From b530432921d7a9709428b0162673e0ab72de1c3d Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Fri, 13 Oct 2023 14:41:16 +0200 Subject: [PATCH 001/128] [Dev/UnderAudit]: Dev<->Master (#3120) --- .github/workflows/linux-ci-rust.yml | 6 + Dockerfile | 3 - .../core/app/blockchains/ethereum/TestBarz.kt | 6 +- .../blockchains/ethereum/TestEthereumAbi.kt | 123 +- .../ethereum/TestEthereumAbiValue.kt | 4 +- .../blockchains/ethereum/TestEthereumRlp.kt | 57 + .../app/blockchains/neo/TestsNEOSigner.kt | 5 + .../ontology/TestOntologySigning.kt | 10 + codegen-v2/manifest/TWCoinType.yaml | 2 +- .../src/codegen/swift/templates/WalletCore.h | 1 + include/TrustWalletCore/TWEthereumAbi.h | 35 +- include/TrustWalletCore/TWEthereumRlp.h | 27 + .../TrustWalletCore/TWTransactionCompiler.h | 1 + rust/Cargo.lock | 736 +++++- rust/Cargo.toml | 20 +- rust/coverage.stats | 2 +- rust/tw_any_coin/Cargo.toml | 22 + rust/tw_any_coin/src/any_address.rs | 68 + rust/tw_any_coin/src/any_signer.rs | 30 + rust/tw_any_coin/src/ffi/mod.rs | 10 + rust/tw_any_coin/src/ffi/tw_any_address.rs | 104 + rust/tw_any_coin/src/ffi/tw_any_signer.rs | 40 + rust/tw_any_coin/src/ffi/tw_message_signer.rs | 53 + .../src/ffi/tw_transaction_compiler.rs | 69 + rust/tw_any_coin/src/lib.rs | 14 + rust/tw_any_coin/src/message_signer.rs | 36 + rust/tw_any_coin/src/test_utils/mod.rs | 16 + rust/tw_any_coin/src/transaction_compiler.rs | 39 + .../tests/tw_any_address_ffi_tests.rs | 152 ++ .../tests/tw_any_signer_ffi_tests.rs | 66 + .../tests/tw_message_signer_ffi_tests.rs | 93 + .../tw_transaction_compiler_ffi_tests.rs | 110 + rust/tw_bitcoin/Cargo.toml | 6 + rust/tw_bitcoin/src/brc20.rs | 211 -- rust/tw_bitcoin/src/claim.rs | 194 -- rust/tw_bitcoin/src/entry.rs | 347 +++ rust/tw_bitcoin/src/ffi/address.rs | 98 - rust/tw_bitcoin/src/ffi/mod.rs | 279 --- rust/tw_bitcoin/src/ffi/scripts.rs | 197 -- rust/tw_bitcoin/src/input/mod.rs | 92 - rust/tw_bitcoin/src/input/p2pkh.rs | 86 - rust/tw_bitcoin/src/input/p2tr_key_path.rs | 90 - rust/tw_bitcoin/src/input/p2tr_script_path.rs | 127 -- rust/tw_bitcoin/src/input/p2wpkh.rs | 81 - rust/tw_bitcoin/src/lib.rs | 84 +- .../src/modules/legacy/build_and_sign.rs | 243 ++ rust/tw_bitcoin/src/modules/legacy/mod.rs | 6 + rust/tw_bitcoin/src/modules/mod.rs | 5 + rust/tw_bitcoin/src/modules/plan_builder.rs | 230 ++ rust/tw_bitcoin/src/modules/signer.rs | 159 ++ .../src/modules/transactions/brc20.rs | 93 + .../src/modules/transactions/input_builder.rs | 320 +++ .../transactions/input_claim_builder.rs | 172 ++ .../src/modules/transactions/mod.rs | 26 + .../{ => modules/transactions}/ordinals.rs | 58 +- .../modules/transactions/output_builder.rs | 371 +++ rust/tw_bitcoin/src/modules/utils.rs | 188 ++ rust/tw_bitcoin/src/nft.rs | 25 - rust/tw_bitcoin/src/output/mod.rs | 75 - rust/tw_bitcoin/src/output/p2pkh.rs | 60 - rust/tw_bitcoin/src/output/p2tr_key_path.rs | 56 - .../tw_bitcoin/src/output/p2tr_script_path.rs | 77 - rust/tw_bitcoin/src/output/p2wpkh.rs | 52 - rust/tw_bitcoin/src/recipient.rs | 260 --- rust/tw_bitcoin/src/tests/address.rs | 24 - rust/tw_bitcoin/src/tests/brc20_transfer.rs | 183 -- rust/tw_bitcoin/src/tests/fee.rs | 104 - .../src/tests/ffi/brc20_transfer.rs | 235 -- rust/tw_bitcoin/src/tests/ffi/fees.rs | 48 - rust/tw_bitcoin/src/tests/ffi/mod.rs | 6 - rust/tw_bitcoin/src/tests/ffi/nft.rs | 146 -- rust/tw_bitcoin/src/tests/ffi/scripts.rs | 77 - rust/tw_bitcoin/src/tests/ffi/transaction.rs | 138 -- rust/tw_bitcoin/src/tests/ffi/utils.rs | 236 -- rust/tw_bitcoin/src/tests/mod.rs | 11 - rust/tw_bitcoin/src/tests/nft.rs | 95 - rust/tw_bitcoin/src/tests/p2pkh.rs | 54 - rust/tw_bitcoin/src/tests/p2tr_key_path.rs | 94 - rust/tw_bitcoin/src/tests/p2wpkh.rs | 92 - rust/tw_bitcoin/src/transaction.rs | 255 --- rust/tw_bitcoin/src/utils.rs | 15 - rust/tw_bitcoin/tests/brc20.rs | 137 ++ .../{src/tests => tests/common}/data.rs | 6 +- rust/tw_bitcoin/tests/common/mod.rs | 12 + rust/tw_bitcoin/tests/free_estimate.rs | 246 ++ rust/tw_bitcoin/tests/legacy_build_sign.rs | 445 ++++ rust/tw_bitcoin/tests/legacy_scripts.rs | 177 ++ rust/tw_bitcoin/tests/ordinal_nft.rs | 130 ++ rust/tw_bitcoin/tests/p2pkh.rs | 75 + rust/tw_bitcoin/tests/p2sh.rs | 147 ++ rust/tw_bitcoin/tests/p2tr_key_path.rs | 98 + rust/tw_bitcoin/tests/p2tr_script_path.rs | 161 ++ rust/tw_bitcoin/tests/p2wpkh.rs | 102 + rust/tw_bitcoin/tests/p2wsh.rs | 142 ++ rust/tw_bitcoin/tests/plan_builder.rs | 192 ++ rust/tw_bitcoin/tests/send_to_address.rs | 316 +++ rust/tw_coin_entry/Cargo.toml | 15 + rust/tw_coin_entry/src/coin_context.rs | 15 + rust/tw_coin_entry/src/coin_entry.rs | 97 + rust/tw_coin_entry/src/coin_entry_ext.rs | 234 ++ .../tw_coin_entry/src/common/compile_input.rs | 46 + rust/tw_coin_entry/src/common/mod.rs | 7 + rust/tw_coin_entry/src/derivation.rs | 22 + rust/tw_coin_entry/src/error.rs | 114 + rust/tw_coin_entry/src/lib.rs | 17 + rust/tw_coin_entry/src/modules/json_signer.rs | 33 + .../src/modules/message_signer.rs | 70 + rust/tw_coin_entry/src/modules/mod.rs | 11 + .../tw_coin_entry/src/modules/plan_builder.rs | 29 + rust/tw_coin_entry/src/prefix.rs | 40 + .../src/test_utils/empty_context.rs | 17 + rust/tw_coin_entry/src/test_utils/mod.rs | 7 + rust/tw_coin_registry/Cargo.toml | 17 + rust/tw_coin_registry/src/blockchain_type.rs | 43 + rust/tw_coin_registry/src/coin_context.rs | 27 + rust/tw_coin_registry/src/coin_type.rs | 8 + rust/tw_coin_registry/src/dispatcher.rs | 51 + rust/tw_coin_registry/src/error.rs | 25 + rust/tw_coin_registry/src/lib.rs | 12 + rust/tw_coin_registry/src/registry.rs | 53 + rust/tw_encoding/Cargo.toml | 1 + rust/tw_encoding/fuzz/.gitignore | 5 + rust/tw_encoding/fuzz/Cargo.toml | 34 + .../fuzz/fuzz_targets/base_decode.rs | 18 + .../fuzz/fuzz_targets/base_encode.rs | 18 + rust/tw_encoding/src/base32.rs | 1 + rust/tw_encoding/src/base58.rs | 3 +- rust/tw_encoding/src/ffi.rs | 11 +- rust/tw_encoding/src/hex.rs | 33 +- rust/tw_ethereum/Cargo.toml | 15 + rust/tw_ethereum/src/entry.rs | 100 + rust/{tw_starknet => tw_ethereum}/src/lib.rs | 3 +- rust/tw_ethereum/tests/compiler.rs | 96 + rust/tw_ethereum/tests/signer.rs | 24 + rust/tw_evm/Cargo.toml | 22 + rust/tw_evm/fuzz/.gitignore | 5 + rust/tw_evm/fuzz/Cargo.toml | 54 + .../fuzz/fuzz_targets/abi_decode_value.rs | 16 + .../fuzz/fuzz_targets/abi_encode_function.rs | 16 + .../fuzz_targets/abi_function_decode_input.rs | 16 + rust/tw_evm/fuzz/fuzz_targets/rlp_encode.rs | 16 + rust/tw_evm/fuzz/fuzz_targets/sign.rs | 16 + rust/tw_evm/src/abi/contract.rs | 62 + rust/tw_evm/src/abi/decode.rs | 873 +++++++ rust/tw_evm/src/abi/encode.rs | 1088 +++++++++ rust/tw_evm/src/abi/function.rs | 67 + rust/tw_evm/src/abi/mod.rs | 49 + rust/tw_evm/src/abi/non_empty_array.rs | 83 + rust/tw_evm/src/abi/param.rs | 124 + rust/tw_evm/src/abi/param_token.rs | 67 + rust/tw_evm/src/abi/param_type/constructor.rs | 107 + rust/tw_evm/src/abi/param_type/mod.rs | 137 ++ rust/tw_evm/src/abi/param_type/reader.rs | 103 + rust/tw_evm/src/abi/param_type/writer.rs | 116 + rust/tw_evm/src/abi/prebuild/erc1155.rs | 42 + rust/tw_evm/src/abi/prebuild/erc20.rs | 35 + rust/tw_evm/src/abi/prebuild/erc4337.rs | 71 + rust/tw_evm/src/abi/prebuild/erc721.rs | 34 + rust/tw_evm/src/abi/prebuild/mod.rs | 10 + .../abi/prebuild/resource/erc1155.abi.json | 295 +++ .../src/abi/prebuild/resource/erc20.abi.json | 185 ++ .../resource/erc4337.simple_account.abi.json | 529 +++++ .../src/abi/prebuild/resource/erc721.abi.json | 287 +++ rust/tw_evm/src/abi/signature.rs | 35 + rust/tw_evm/src/abi/token.rs | 221 ++ rust/tw_evm/src/abi/uint.rs | 55 + rust/tw_evm/src/address.rs | 223 ++ .../tw_evm/src/evm_context.rs | 19 +- rust/tw_evm/src/evm_entry.rs | 123 + rust/tw_evm/src/lib.rs | 15 + rust/tw_evm/src/message/eip191.rs | 44 + .../src/message/eip712/eip712_message.rs | 405 ++++ rust/tw_evm/src/message/eip712/mod.rs | 8 + rust/tw_evm/src/message/eip712/property.rs | 192 ++ rust/tw_evm/src/message/mod.rs | 50 + rust/tw_evm/src/message/signature.rs | 91 + rust/tw_evm/src/modules/abi_encoder.rs | 479 ++++ rust/tw_evm/src/modules/compiler.rs | 89 + rust/tw_evm/src/modules/json_signer.rs | 30 + rust/tw_evm/src/modules/message_signer.rs | 148 ++ rust/tw_evm/src/modules/mod.rs | 13 + rust/tw_evm/src/modules/rlp_encoder.rs | 92 + rust/tw_evm/src/modules/signer.rs | 60 + rust/tw_evm/src/modules/tx_builder.rs | 237 ++ rust/tw_evm/src/rlp/buffer.rs | 64 + rust/tw_evm/src/rlp/impls.rs | 43 + rust/tw_evm/src/rlp/list.rs | 64 + rust/tw_evm/src/rlp/mod.rs | 16 + rust/tw_evm/src/signature.rs | 48 + rust/tw_evm/src/transaction/mod.rs | 113 + rust/tw_evm/src/transaction/signature.rs | 184 ++ .../src/transaction/transaction_eip1559.rs | 138 ++ .../src/transaction/transaction_non_typed.rs | 158 ++ rust/tw_evm/src/transaction/user_operation.rs | 203 ++ rust/tw_evm/tests/abi_encoder.rs | 689 ++++++ rust/tw_evm/tests/barz.rs | 194 ++ .../tests/data/abi_with_negative_int.json | 20 + .../data/decoded_abi_with_negative_int.json | 15 + rust/tw_evm/tests/data/eip712_case_1.json | 64 + rust/tw_evm/tests/data/eip712_case_2.json | 83 + rust/tw_evm/tests/data/eip712_case_3.json | 43 + rust/tw_evm/tests/data/eip712_greenfield.json | 149 ++ .../data/eip712_unequal_array_lengths.json | 66 + .../data/eip712_with_chain_id_string.json | 83 + .../tests/data/eip712_with_custom_array.json | 86 + rust/tw_evm/tests/data/swap_v2.json | 69 + rust/tw_evm/tests/data/swap_v2_decoded.json | 105 + rust/tw_evm/tests/message_signer.rs | 258 +++ rust/tw_evm/tests/rlp.rs | 247 ++ rust/tw_evm/tests/signer.rs | 586 +++++ rust/tw_hash/Cargo.toml | 10 +- rust/tw_hash/fuzz/.gitignore | 5 + rust/tw_hash/fuzz/Cargo.toml | 27 + rust/tw_hash/fuzz/fuzz_targets/hash_fuzz.rs | 33 + rust/tw_hash/src/blake2.rs | 61 +- rust/tw_hash/src/ffi.rs | 42 +- rust/tw_hash/src/hash_array.rs | 312 +++ rust/tw_hash/src/lib.rs | 20 + rust/tw_hash/tests/hash_ffi_tests.rs | 32 +- rust/tw_keypair/Cargo.toml | 27 + rust/tw_keypair/fuzz/.gitignore | 5 + rust/tw_keypair/fuzz/Cargo.toml | 41 + .../fuzz/fuzz_targets/tw_private_sign.rs | 23 + .../fuzz/fuzz_targets/tw_private_to_public.rs | 22 + .../fuzz/fuzz_targets/tw_public_verify.rs | 24 + rust/tw_keypair/src/ecdsa/canonical.rs | 143 ++ rust/tw_keypair/src/ecdsa/mod.rs | 21 + .../tw_keypair/src/ecdsa/nist256p1/keypair.rs | 73 + rust/tw_keypair/src/ecdsa/nist256p1/mod.rs | 109 + .../tw_keypair/src/ecdsa/nist256p1/private.rs | 66 + rust/tw_keypair/src/ecdsa/nist256p1/public.rs | 94 + .../tw_keypair/src/ecdsa/secp256k1/keypair.rs | 72 + rust/tw_keypair/src/ecdsa/secp256k1/mod.rs | 153 ++ .../tw_keypair/src/ecdsa/secp256k1/private.rs | 89 + rust/tw_keypair/src/ecdsa/secp256k1/public.rs | 100 + rust/tw_keypair/src/ecdsa/signature.rs | 174 ++ rust/tw_keypair/src/ed25519/keypair.rs | 68 + rust/tw_keypair/src/ed25519/mangle.rs | 17 + rust/tw_keypair/src/ed25519/mod.rs | 265 +++ .../modifications/cardano/extended_keypair.rs | 71 + .../modifications/cardano/extended_private.rs | 152 ++ .../modifications/cardano/extended_public.rs | 125 + .../src/ed25519/modifications/cardano/mod.rs | 15 + .../src/ed25519/modifications/mod.rs | 8 + .../ed25519/modifications/waves/keypair.rs | 70 + .../src/ed25519/modifications/waves/mod.rs | 18 + .../ed25519/modifications/waves/private.rs | 61 + .../src/ed25519/modifications/waves/public.rs | 93 + .../ed25519/modifications/waves/signature.rs | 82 + rust/tw_keypair/src/ed25519/private.rs | 88 + rust/tw_keypair/src/ed25519/public.rs | 169 ++ rust/tw_keypair/src/ed25519/secret.rs | 129 ++ rust/tw_keypair/src/ed25519/signature.rs | 96 + rust/tw_keypair/src/ffi/mod.rs | 2 + rust/tw_keypair/src/ffi/privkey.rs | 133 ++ rust/tw_keypair/src/ffi/pubkey.rs | 104 + rust/tw_keypair/src/lib.rs | 54 +- rust/tw_keypair/src/starkex/keypair.rs | 69 + rust/tw_keypair/src/starkex/mod.rs | 124 + rust/tw_keypair/src/starkex/private.rs | 124 + rust/tw_keypair/src/starkex/public.rs | 66 + rust/tw_keypair/src/starkex/signature.rs | 83 + rust/tw_keypair/src/test_utils/mod.rs | 8 + .../src/test_utils/tw_private_key_helper.rs | 45 + .../src/test_utils/tw_public_key_helper.rs | 51 + rust/tw_keypair/src/traits.rs | 39 + rust/tw_keypair/src/tw/mod.rs | 131 ++ rust/tw_keypair/src/tw/private.rs | 195 ++ rust/tw_keypair/src/tw/public.rs | 138 ++ .../tests/ed25519_blake2b_sign.json | 1002 +++++++++ .../tw_keypair/tests/ed25519_blake2b_tests.rs | 35 + .../ed25519_extended_cardano_priv_to_pub.json | 2002 +++++++++++++++++ .../tests/ed25519_extended_cardano_sign.json | 1252 +++++++++++ .../tests/ed25519_extended_cardano_tests.rs | 62 + .../tw_keypair/tests/ed25519_priv_to_pub.json | 2002 +++++++++++++++++ rust/tw_keypair/tests/ed25519_sign.json | 1002 +++++++++ rust/tw_keypair/tests/ed25519_tests.rs | 58 + .../tests/ed25519_waves_priv_to_pub.json | 2002 +++++++++++++++++ rust/tw_keypair/tests/ed25519_waves_sign.json | 1002 +++++++++ rust/tw_keypair/tests/ed25519_waves_tests.rs | 62 + .../nist256p1_priv_to_pub_compressed.json | 2002 +++++++++++++++++ rust/tw_keypair/tests/nist256p1_tests.rs | 82 + rust/tw_keypair/tests/nist256p1_verify.json | 1002 +++++++++ .../tw_keypair/tests/private_key_ffi_tests.rs | 210 ++ rust/tw_keypair/tests/public_key_ffi_tests.rs | 124 + rust/tw_keypair/tests/secp256k1_sign.json | 502 +++++ rust/tw_keypair/tests/secp256k1_tests.rs | 33 + .../tests/tw_keypair_starkex_tests.rs | 38 + rust/tw_memory/Cargo.toml | 3 + rust/tw_memory/src/ffi/c_byte_array.rs | 37 +- rust/tw_memory/src/ffi/mod.rs | 12 +- rust/tw_memory/src/ffi/tw_data.rs | 104 + rust/tw_memory/src/ffi/tw_data_vector.rs | 74 + rust/tw_memory/src/ffi/tw_string.rs | 80 + rust/tw_memory/src/lib.rs | 5 + rust/tw_memory/src/test_utils/mod.rs | 10 + .../src/test_utils/tw_data_helper.rs | 54 + .../src/test_utils/tw_data_vector_helper.rs | 44 + .../src/test_utils/tw_string_helper.rs | 52 + rust/tw_memory/src/test_utils/tw_wrapper.rs | 32 + .../tw_memory/tests/c_byte_array_ffi_tests.rs | 6 +- rust/tw_memory/tests/c_result_ffi_tests.rs | 4 +- rust/tw_number/Cargo.toml | 20 + rust/tw_number/src/i256.rs | 458 ++++ rust/tw_number/src/lib.rs | 51 + rust/tw_number/src/sign.rs | 45 + rust/tw_number/src/u256.rs | 264 +++ rust/tw_number/tests/u256.rs | 208 ++ rust/tw_proto/Cargo.toml | 5 + rust/tw_proto/build.rs | 52 + rust/tw_proto/src/lib.rs | 20 +- rust/tw_proto/tests/proto_ffi_tests.rs | 2 +- rust/tw_ronin/Cargo.toml | 16 + rust/tw_ronin/src/address.rs | 67 + rust/tw_ronin/src/entry.rs | 100 + rust/tw_ronin/src/lib.rs | 9 + rust/tw_ronin/src/ronin_context.rs | 15 + rust/tw_ronin/tests/address.rs | 43 + rust/tw_ronin/tests/compiler.rs | 77 + rust/tw_ronin/tests/rlp.rs | 34 + rust/tw_ronin/tests/signer.rs | 68 + rust/tw_starknet/Cargo.toml | 11 - rust/tw_starknet/src/ffi.rs | 97 - rust/tw_starknet/src/key_pair.rs | 107 - rust/tw_starknet/tests/starknet_ffi_tests.rs | 128 -- rust/tw_utxo/Cargo.toml | 18 + rust/tw_utxo/src/compiler.rs | 453 ++++ rust/tw_utxo/src/lib.rs | 26 + rust/tw_utxo/tests/common.rs | 23 + rust/tw_utxo/tests/input_selection.rs | 597 +++++ rust/tw_utxo/tests/p2pkh.rs | 53 + rust/tw_utxo/tests/p2tr.rs | 56 + rust/tw_utxo/tests/p2wpkh.rs | 100 + rust/wallet_core_rs/Cargo.toml | 22 +- rust/wallet_core_rs/cbindgen.toml | 26 +- rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs | 308 +++ rust/wallet_core_rs/src/ffi/bitcoin/mod.rs | 8 + rust/wallet_core_rs/src/ffi/ethereum/abi.rs | 110 + rust/wallet_core_rs/src/ffi/ethereum/mod.rs | 10 + rust/wallet_core_rs/src/ffi/ethereum/rlp.rs | 31 + rust/wallet_core_rs/src/ffi/mod.rs | 8 + rust/wallet_core_rs/src/lib.rs | 6 +- rust/wallet_core_rs/tests/data/custom.json | 20 + .../tests/data/custom_decoded.json | 20 + rust/wallet_core_rs/tests/ethereum_abi.rs | 196 ++ rust/wallet_core_rs/tests/ethereum_rlp.rs | 37 + samples/go/sample/external_signing.go | 30 +- samples/kmp/shared/build.gradle.kts | 2 +- src/Aeternity/Signer.cpp | 23 +- src/Aeternity/Signer.h | 4 +- src/Aeternity/Transaction.cpp | 56 +- src/Aeternity/Transaction.h | 7 - src/Aion/RLP.h | 26 +- src/Aion/Transaction.cpp | 37 +- src/Bitcoin/Script.cpp | 4 +- src/Bitcoin/Signer.cpp | 2 +- src/Bitcoin/Transaction.cpp | 2 +- src/CoinEntry.cpp | 100 + src/CoinEntry.h | 27 + src/Ethereum/ABI.h | 18 - src/Ethereum/ABI/Array.cpp | 186 -- src/Ethereum/ABI/Array.h | 84 - src/Ethereum/ABI/Bytes.cpp | 141 -- src/Ethereum/ABI/Bytes.h | 90 - src/Ethereum/ABI/Function.cpp | 214 +- src/Ethereum/ABI/Function.h | 125 +- src/Ethereum/ABI/ParamAddress.cpp | 27 - src/Ethereum/ABI/ParamAddress.h | 34 - src/Ethereum/ABI/ParamBase.h | 48 - src/Ethereum/ABI/ParamFactory.cpp | 204 -- src/Ethereum/ABI/ParamFactory.h | 36 - src/Ethereum/ABI/ParamNumber.cpp | 118 - src/Ethereum/ABI/ParamNumber.h | 230 -- src/Ethereum/ABI/ParamStruct.cpp | 390 ---- src/Ethereum/ABI/ParamStruct.h | 149 -- src/Ethereum/ABI/Parameters.cpp | 197 -- src/Ethereum/ABI/Parameters.h | 73 - src/Ethereum/ABI/ProtoParam.h | 115 + src/Ethereum/ABI/Tuple.cpp | 23 - src/Ethereum/ABI/Tuple.h | 42 - src/Ethereum/ABI/ValueDecoder.cpp | 39 +- src/Ethereum/ABI/ValueDecoder.h | 2 - src/Ethereum/Barz.cpp | 66 +- src/Ethereum/ContractCall.cpp | 152 +- src/Ethereum/ContractCall.h | 2 +- src/Ethereum/EIP1967.cpp | 19 +- src/Ethereum/ERC4337.cpp | 55 - src/Ethereum/ERC4337.h | 17 - src/Ethereum/Entry.cpp | 89 +- src/Ethereum/Entry.h | 21 +- src/Ethereum/MessageSigner.cpp | 136 +- src/Ethereum/MessageSigner.h | 15 +- src/Ethereum/RLP.cpp | 197 +- src/Ethereum/RLP.h | 86 +- src/Ethereum/Signer.cpp | 429 ---- src/Ethereum/Signer.h | 65 - src/Ethereum/Transaction.cpp | 387 ---- src/Ethereum/Transaction.h | 242 -- src/Filecoin/Signer.cpp | 53 +- src/Greenfield/SignerEip712.cpp | 3 +- src/Harmony/Signer.cpp | 286 ++- src/Harmony/Signer.h | 16 +- src/Hash.cpp | 19 +- src/ImmutableX/StarkKey.cpp | 41 +- src/ImmutableX/StarkKey.h | 6 - src/LiquidStaking/LiquidStaking.cpp | 33 +- src/PrivateKey.cpp | 106 +- src/PublicKey.cpp | 13 +- src/Ronin/Address.cpp | 55 - src/Ronin/Address.h | 31 - src/Ronin/Entry.cpp | 33 +- src/Ronin/Entry.h | 12 +- src/THORChain/Swap.cpp | 24 +- src/Theta/Entry.cpp | 18 + src/Theta/Entry.h | 16 +- src/Theta/Signer.cpp | 34 +- src/Theta/Transaction.cpp | 100 +- src/Theta/Transaction.h | 9 +- src/VeChain/Entry.cpp | 20 +- src/VeChain/Entry.h | 15 +- src/VeChain/Transaction.cpp | 58 +- src/interface/TWEthereumAbi.cpp | 55 +- src/interface/TWEthereumAbiFunction.cpp | 416 ++-- src/interface/TWEthereumRlp.cpp | 22 + src/interface/TWWebAuthn.cpp | 2 +- src/proto/BitcoinV2.proto | 430 ++++ src/proto/Common.proto | 2 + src/proto/Ethereum.proto | 56 + src/proto/EthereumAbi.proto | 318 +++ src/proto/EthereumRlp.proto | 49 + src/proto/Utxo.proto | 221 ++ src/rust/Wrapper.h | 93 +- swift/Tests/BarzTests.swift | 6 +- .../Tests/Blockchains/EthereumAbiTests.swift | 147 +- .../Tests/Blockchains/EthereumRlpTests.swift | 47 + swift/Tests/Blockchains/NEOTests.swift | 5 + swift/Tests/Blockchains/OntologyTests.swift | 8 + tests/chains/Aion/RLPTests.cpp | 25 +- .../chains/BinanceSmartChain/SignerTests.cpp | 55 +- tests/chains/Cosmos/THORChain/SwapTests.cpp | 24 +- tests/chains/Ethereum/AbiStructTests.cpp | 930 -------- tests/chains/Ethereum/AbiTests.cpp | 1652 -------------- tests/chains/Ethereum/BarzTests.cpp | 8 +- tests/chains/Ethereum/ContractCallTests.cpp | 67 +- tests/chains/Ethereum/Data/1inch_decoded.json | 61 + .../chains/Ethereum/Data/swap_v2_decoded.json | 8 +- .../Ethereum/Data/tuple_nested_decoded.json | 47 + .../Ethereum/EthereumMessageSignerTests.cpp | 2 +- tests/chains/Ethereum/RLPTests.cpp | 312 --- tests/chains/Ethereum/SignerTests.cpp | 166 -- tests/chains/Ethereum/TWAnySignerTests.cpp | 36 +- tests/chains/Ethereum/TWEthereumAbiTests.cpp | 30 +- .../TWEthereumAbiValueDecoderTests.cpp | 6 +- tests/chains/Ethereum/TWRlpTests.cpp | 51 + .../Ethereum/TransactionCompilerTests.cpp | 52 +- tests/chains/Ethereum/ValueDecoderTests.cpp | 44 +- tests/chains/ImmutableX/StarkKeyTests.cpp | 36 +- tests/chains/NEO/SignerTests.cpp | 9 +- tests/chains/NEO/TWAnySignerTests.cpp | 10 +- tests/chains/NEO/TransactionCompilerTests.cpp | 12 + tests/chains/Ontology/Oep4Tests.cpp | 13 +- tests/chains/Ontology/OngTests.cpp | 60 +- tests/chains/Ontology/OntTests.cpp | 72 +- tests/chains/Ontology/TWAnySignerTests.cpp | 30 + tests/chains/Ronin/AddressTests.cpp | 17 - tests/chains/Ronin/TWAnyAddressTests.cpp | 1 - tests/chains/Theta/SignerTests.cpp | 2 +- tests/chains/Theta/TransactionTests.cpp | 8 +- tests/chains/ThetaFuel/TWAnySignerTests.cpp | 1 - tests/common/HDWallet/HDWalletTests.cpp | 8 +- tests/common/PrivateKeyTests.cpp | 21 + tests/common/PublicKeyLegacy.h | 20 + tests/common/PublicKeyTests.cpp | 12 + .../interface/TWTransactionCompilerTests.cpp | 25 +- tools/android-build | 2 +- tools/android-release | 2 +- tools/kotlin-release | 2 +- tools/rust-fuzz | 39 + tools/rust-test | 26 + 479 files changed, 45809 insertions(+), 12484 deletions(-) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumRlp.kt create mode 100644 include/TrustWalletCore/TWEthereumRlp.h create mode 100644 rust/tw_any_coin/Cargo.toml create mode 100644 rust/tw_any_coin/src/any_address.rs create mode 100644 rust/tw_any_coin/src/any_signer.rs create mode 100644 rust/tw_any_coin/src/ffi/mod.rs create mode 100644 rust/tw_any_coin/src/ffi/tw_any_address.rs create mode 100644 rust/tw_any_coin/src/ffi/tw_any_signer.rs create mode 100644 rust/tw_any_coin/src/ffi/tw_message_signer.rs create mode 100644 rust/tw_any_coin/src/ffi/tw_transaction_compiler.rs create mode 100644 rust/tw_any_coin/src/lib.rs create mode 100644 rust/tw_any_coin/src/message_signer.rs create mode 100644 rust/tw_any_coin/src/test_utils/mod.rs create mode 100644 rust/tw_any_coin/src/transaction_compiler.rs create mode 100644 rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs create mode 100644 rust/tw_any_coin/tests/tw_any_signer_ffi_tests.rs create mode 100644 rust/tw_any_coin/tests/tw_message_signer_ffi_tests.rs create mode 100644 rust/tw_any_coin/tests/tw_transaction_compiler_ffi_tests.rs delete mode 100644 rust/tw_bitcoin/src/brc20.rs delete mode 100644 rust/tw_bitcoin/src/claim.rs create mode 100644 rust/tw_bitcoin/src/entry.rs delete mode 100644 rust/tw_bitcoin/src/ffi/address.rs delete mode 100644 rust/tw_bitcoin/src/ffi/mod.rs delete mode 100644 rust/tw_bitcoin/src/ffi/scripts.rs delete mode 100644 rust/tw_bitcoin/src/input/mod.rs delete mode 100644 rust/tw_bitcoin/src/input/p2pkh.rs delete mode 100644 rust/tw_bitcoin/src/input/p2tr_key_path.rs delete mode 100644 rust/tw_bitcoin/src/input/p2tr_script_path.rs delete mode 100644 rust/tw_bitcoin/src/input/p2wpkh.rs create mode 100644 rust/tw_bitcoin/src/modules/legacy/build_and_sign.rs create mode 100644 rust/tw_bitcoin/src/modules/legacy/mod.rs create mode 100644 rust/tw_bitcoin/src/modules/mod.rs create mode 100644 rust/tw_bitcoin/src/modules/plan_builder.rs create mode 100644 rust/tw_bitcoin/src/modules/signer.rs create mode 100644 rust/tw_bitcoin/src/modules/transactions/brc20.rs create mode 100644 rust/tw_bitcoin/src/modules/transactions/input_builder.rs create mode 100644 rust/tw_bitcoin/src/modules/transactions/input_claim_builder.rs create mode 100644 rust/tw_bitcoin/src/modules/transactions/mod.rs rename rust/tw_bitcoin/src/{ => modules/transactions}/ordinals.rs (74%) create mode 100644 rust/tw_bitcoin/src/modules/transactions/output_builder.rs create mode 100644 rust/tw_bitcoin/src/modules/utils.rs delete mode 100644 rust/tw_bitcoin/src/nft.rs delete mode 100644 rust/tw_bitcoin/src/output/mod.rs delete mode 100644 rust/tw_bitcoin/src/output/p2pkh.rs delete mode 100644 rust/tw_bitcoin/src/output/p2tr_key_path.rs delete mode 100644 rust/tw_bitcoin/src/output/p2tr_script_path.rs delete mode 100644 rust/tw_bitcoin/src/output/p2wpkh.rs delete mode 100644 rust/tw_bitcoin/src/recipient.rs delete mode 100644 rust/tw_bitcoin/src/tests/address.rs delete mode 100644 rust/tw_bitcoin/src/tests/brc20_transfer.rs delete mode 100644 rust/tw_bitcoin/src/tests/fee.rs delete mode 100644 rust/tw_bitcoin/src/tests/ffi/brc20_transfer.rs delete mode 100644 rust/tw_bitcoin/src/tests/ffi/fees.rs delete mode 100644 rust/tw_bitcoin/src/tests/ffi/mod.rs delete mode 100644 rust/tw_bitcoin/src/tests/ffi/nft.rs delete mode 100644 rust/tw_bitcoin/src/tests/ffi/scripts.rs delete mode 100644 rust/tw_bitcoin/src/tests/ffi/transaction.rs delete mode 100644 rust/tw_bitcoin/src/tests/ffi/utils.rs delete mode 100644 rust/tw_bitcoin/src/tests/mod.rs delete mode 100644 rust/tw_bitcoin/src/tests/nft.rs delete mode 100644 rust/tw_bitcoin/src/tests/p2pkh.rs delete mode 100644 rust/tw_bitcoin/src/tests/p2tr_key_path.rs delete mode 100644 rust/tw_bitcoin/src/tests/p2wpkh.rs delete mode 100644 rust/tw_bitcoin/src/transaction.rs delete mode 100644 rust/tw_bitcoin/src/utils.rs create mode 100644 rust/tw_bitcoin/tests/brc20.rs rename rust/tw_bitcoin/{src/tests => tests/common}/data.rs (99%) create mode 100644 rust/tw_bitcoin/tests/common/mod.rs create mode 100644 rust/tw_bitcoin/tests/free_estimate.rs create mode 100644 rust/tw_bitcoin/tests/legacy_build_sign.rs create mode 100644 rust/tw_bitcoin/tests/legacy_scripts.rs create mode 100644 rust/tw_bitcoin/tests/ordinal_nft.rs create mode 100644 rust/tw_bitcoin/tests/p2pkh.rs create mode 100644 rust/tw_bitcoin/tests/p2sh.rs create mode 100644 rust/tw_bitcoin/tests/p2tr_key_path.rs create mode 100644 rust/tw_bitcoin/tests/p2tr_script_path.rs create mode 100644 rust/tw_bitcoin/tests/p2wpkh.rs create mode 100644 rust/tw_bitcoin/tests/p2wsh.rs create mode 100644 rust/tw_bitcoin/tests/plan_builder.rs create mode 100644 rust/tw_bitcoin/tests/send_to_address.rs create mode 100644 rust/tw_coin_entry/Cargo.toml create mode 100644 rust/tw_coin_entry/src/coin_context.rs create mode 100644 rust/tw_coin_entry/src/coin_entry.rs create mode 100644 rust/tw_coin_entry/src/coin_entry_ext.rs create mode 100644 rust/tw_coin_entry/src/common/compile_input.rs create mode 100644 rust/tw_coin_entry/src/common/mod.rs create mode 100644 rust/tw_coin_entry/src/derivation.rs create mode 100644 rust/tw_coin_entry/src/error.rs create mode 100644 rust/tw_coin_entry/src/lib.rs create mode 100644 rust/tw_coin_entry/src/modules/json_signer.rs create mode 100644 rust/tw_coin_entry/src/modules/message_signer.rs create mode 100644 rust/tw_coin_entry/src/modules/mod.rs create mode 100644 rust/tw_coin_entry/src/modules/plan_builder.rs create mode 100644 rust/tw_coin_entry/src/prefix.rs create mode 100644 rust/tw_coin_entry/src/test_utils/empty_context.rs create mode 100644 rust/tw_coin_entry/src/test_utils/mod.rs create mode 100644 rust/tw_coin_registry/Cargo.toml create mode 100644 rust/tw_coin_registry/src/blockchain_type.rs create mode 100644 rust/tw_coin_registry/src/coin_context.rs create mode 100644 rust/tw_coin_registry/src/coin_type.rs create mode 100644 rust/tw_coin_registry/src/dispatcher.rs create mode 100644 rust/tw_coin_registry/src/error.rs create mode 100644 rust/tw_coin_registry/src/lib.rs create mode 100644 rust/tw_coin_registry/src/registry.rs create mode 100644 rust/tw_encoding/fuzz/.gitignore create mode 100644 rust/tw_encoding/fuzz/Cargo.toml create mode 100644 rust/tw_encoding/fuzz/fuzz_targets/base_decode.rs create mode 100644 rust/tw_encoding/fuzz/fuzz_targets/base_encode.rs create mode 100644 rust/tw_ethereum/Cargo.toml create mode 100644 rust/tw_ethereum/src/entry.rs rename rust/{tw_starknet => tw_ethereum}/src/lib.rs (89%) create mode 100644 rust/tw_ethereum/tests/compiler.rs create mode 100644 rust/tw_ethereum/tests/signer.rs create mode 100644 rust/tw_evm/Cargo.toml create mode 100644 rust/tw_evm/fuzz/.gitignore create mode 100644 rust/tw_evm/fuzz/Cargo.toml create mode 100644 rust/tw_evm/fuzz/fuzz_targets/abi_decode_value.rs create mode 100644 rust/tw_evm/fuzz/fuzz_targets/abi_encode_function.rs create mode 100644 rust/tw_evm/fuzz/fuzz_targets/abi_function_decode_input.rs create mode 100644 rust/tw_evm/fuzz/fuzz_targets/rlp_encode.rs create mode 100644 rust/tw_evm/fuzz/fuzz_targets/sign.rs create mode 100644 rust/tw_evm/src/abi/contract.rs create mode 100644 rust/tw_evm/src/abi/decode.rs create mode 100644 rust/tw_evm/src/abi/encode.rs create mode 100644 rust/tw_evm/src/abi/function.rs create mode 100644 rust/tw_evm/src/abi/mod.rs create mode 100644 rust/tw_evm/src/abi/non_empty_array.rs create mode 100644 rust/tw_evm/src/abi/param.rs create mode 100644 rust/tw_evm/src/abi/param_token.rs create mode 100644 rust/tw_evm/src/abi/param_type/constructor.rs create mode 100644 rust/tw_evm/src/abi/param_type/mod.rs create mode 100644 rust/tw_evm/src/abi/param_type/reader.rs create mode 100644 rust/tw_evm/src/abi/param_type/writer.rs create mode 100644 rust/tw_evm/src/abi/prebuild/erc1155.rs create mode 100644 rust/tw_evm/src/abi/prebuild/erc20.rs create mode 100644 rust/tw_evm/src/abi/prebuild/erc4337.rs create mode 100644 rust/tw_evm/src/abi/prebuild/erc721.rs create mode 100644 rust/tw_evm/src/abi/prebuild/mod.rs create mode 100644 rust/tw_evm/src/abi/prebuild/resource/erc1155.abi.json create mode 100644 rust/tw_evm/src/abi/prebuild/resource/erc20.abi.json create mode 100644 rust/tw_evm/src/abi/prebuild/resource/erc4337.simple_account.abi.json create mode 100644 rust/tw_evm/src/abi/prebuild/resource/erc721.abi.json create mode 100644 rust/tw_evm/src/abi/signature.rs create mode 100644 rust/tw_evm/src/abi/token.rs create mode 100644 rust/tw_evm/src/abi/uint.rs create mode 100644 rust/tw_evm/src/address.rs rename src/Ethereum/ABI/ParamBase.cpp => rust/tw_evm/src/evm_context.rs (50%) create mode 100644 rust/tw_evm/src/evm_entry.rs create mode 100644 rust/tw_evm/src/lib.rs create mode 100644 rust/tw_evm/src/message/eip191.rs create mode 100644 rust/tw_evm/src/message/eip712/eip712_message.rs create mode 100644 rust/tw_evm/src/message/eip712/mod.rs create mode 100644 rust/tw_evm/src/message/eip712/property.rs create mode 100644 rust/tw_evm/src/message/mod.rs create mode 100644 rust/tw_evm/src/message/signature.rs create mode 100644 rust/tw_evm/src/modules/abi_encoder.rs create mode 100644 rust/tw_evm/src/modules/compiler.rs create mode 100644 rust/tw_evm/src/modules/json_signer.rs create mode 100644 rust/tw_evm/src/modules/message_signer.rs create mode 100644 rust/tw_evm/src/modules/mod.rs create mode 100644 rust/tw_evm/src/modules/rlp_encoder.rs create mode 100644 rust/tw_evm/src/modules/signer.rs create mode 100644 rust/tw_evm/src/modules/tx_builder.rs create mode 100644 rust/tw_evm/src/rlp/buffer.rs create mode 100644 rust/tw_evm/src/rlp/impls.rs create mode 100644 rust/tw_evm/src/rlp/list.rs create mode 100644 rust/tw_evm/src/rlp/mod.rs create mode 100644 rust/tw_evm/src/signature.rs create mode 100644 rust/tw_evm/src/transaction/mod.rs create mode 100644 rust/tw_evm/src/transaction/signature.rs create mode 100644 rust/tw_evm/src/transaction/transaction_eip1559.rs create mode 100644 rust/tw_evm/src/transaction/transaction_non_typed.rs create mode 100644 rust/tw_evm/src/transaction/user_operation.rs create mode 100644 rust/tw_evm/tests/abi_encoder.rs create mode 100644 rust/tw_evm/tests/barz.rs create mode 100644 rust/tw_evm/tests/data/abi_with_negative_int.json create mode 100644 rust/tw_evm/tests/data/decoded_abi_with_negative_int.json create mode 100644 rust/tw_evm/tests/data/eip712_case_1.json create mode 100644 rust/tw_evm/tests/data/eip712_case_2.json create mode 100644 rust/tw_evm/tests/data/eip712_case_3.json create mode 100644 rust/tw_evm/tests/data/eip712_greenfield.json create mode 100644 rust/tw_evm/tests/data/eip712_unequal_array_lengths.json create mode 100644 rust/tw_evm/tests/data/eip712_with_chain_id_string.json create mode 100644 rust/tw_evm/tests/data/eip712_with_custom_array.json create mode 100644 rust/tw_evm/tests/data/swap_v2.json create mode 100644 rust/tw_evm/tests/data/swap_v2_decoded.json create mode 100644 rust/tw_evm/tests/message_signer.rs create mode 100644 rust/tw_evm/tests/rlp.rs create mode 100644 rust/tw_evm/tests/signer.rs create mode 100644 rust/tw_hash/fuzz/.gitignore create mode 100644 rust/tw_hash/fuzz/Cargo.toml create mode 100644 rust/tw_hash/fuzz/fuzz_targets/hash_fuzz.rs create mode 100644 rust/tw_hash/src/hash_array.rs create mode 100644 rust/tw_keypair/fuzz/.gitignore create mode 100644 rust/tw_keypair/fuzz/Cargo.toml create mode 100644 rust/tw_keypair/fuzz/fuzz_targets/tw_private_sign.rs create mode 100644 rust/tw_keypair/fuzz/fuzz_targets/tw_private_to_public.rs create mode 100644 rust/tw_keypair/fuzz/fuzz_targets/tw_public_verify.rs create mode 100644 rust/tw_keypair/src/ecdsa/canonical.rs create mode 100644 rust/tw_keypair/src/ecdsa/nist256p1/keypair.rs create mode 100644 rust/tw_keypair/src/ecdsa/nist256p1/mod.rs create mode 100644 rust/tw_keypair/src/ecdsa/nist256p1/private.rs create mode 100644 rust/tw_keypair/src/ecdsa/nist256p1/public.rs create mode 100644 rust/tw_keypair/src/ecdsa/secp256k1/keypair.rs create mode 100644 rust/tw_keypair/src/ecdsa/secp256k1/mod.rs create mode 100644 rust/tw_keypair/src/ecdsa/secp256k1/private.rs create mode 100644 rust/tw_keypair/src/ecdsa/secp256k1/public.rs create mode 100644 rust/tw_keypair/src/ecdsa/signature.rs create mode 100644 rust/tw_keypair/src/ed25519/keypair.rs create mode 100644 rust/tw_keypair/src/ed25519/mangle.rs create mode 100644 rust/tw_keypair/src/ed25519/mod.rs create mode 100644 rust/tw_keypair/src/ed25519/modifications/cardano/extended_keypair.rs create mode 100644 rust/tw_keypair/src/ed25519/modifications/cardano/extended_private.rs create mode 100644 rust/tw_keypair/src/ed25519/modifications/cardano/extended_public.rs create mode 100644 rust/tw_keypair/src/ed25519/modifications/cardano/mod.rs create mode 100644 rust/tw_keypair/src/ed25519/modifications/mod.rs create mode 100644 rust/tw_keypair/src/ed25519/modifications/waves/keypair.rs create mode 100644 rust/tw_keypair/src/ed25519/modifications/waves/mod.rs create mode 100644 rust/tw_keypair/src/ed25519/modifications/waves/private.rs create mode 100644 rust/tw_keypair/src/ed25519/modifications/waves/public.rs create mode 100644 rust/tw_keypair/src/ed25519/modifications/waves/signature.rs create mode 100644 rust/tw_keypair/src/ed25519/private.rs create mode 100644 rust/tw_keypair/src/ed25519/public.rs create mode 100644 rust/tw_keypair/src/ed25519/secret.rs create mode 100644 rust/tw_keypair/src/ed25519/signature.rs create mode 100644 rust/tw_keypair/src/ffi/privkey.rs create mode 100644 rust/tw_keypair/src/ffi/pubkey.rs create mode 100644 rust/tw_keypair/src/starkex/keypair.rs create mode 100644 rust/tw_keypair/src/starkex/mod.rs create mode 100644 rust/tw_keypair/src/starkex/private.rs create mode 100644 rust/tw_keypair/src/starkex/public.rs create mode 100644 rust/tw_keypair/src/starkex/signature.rs create mode 100644 rust/tw_keypair/src/test_utils/mod.rs create mode 100644 rust/tw_keypair/src/test_utils/tw_private_key_helper.rs create mode 100644 rust/tw_keypair/src/test_utils/tw_public_key_helper.rs create mode 100644 rust/tw_keypair/src/traits.rs create mode 100644 rust/tw_keypair/src/tw/mod.rs create mode 100644 rust/tw_keypair/src/tw/private.rs create mode 100644 rust/tw_keypair/src/tw/public.rs create mode 100644 rust/tw_keypair/tests/ed25519_blake2b_sign.json create mode 100644 rust/tw_keypair/tests/ed25519_blake2b_tests.rs create mode 100644 rust/tw_keypair/tests/ed25519_extended_cardano_priv_to_pub.json create mode 100644 rust/tw_keypair/tests/ed25519_extended_cardano_sign.json create mode 100644 rust/tw_keypair/tests/ed25519_extended_cardano_tests.rs create mode 100644 rust/tw_keypair/tests/ed25519_priv_to_pub.json create mode 100644 rust/tw_keypair/tests/ed25519_sign.json create mode 100644 rust/tw_keypair/tests/ed25519_tests.rs create mode 100644 rust/tw_keypair/tests/ed25519_waves_priv_to_pub.json create mode 100644 rust/tw_keypair/tests/ed25519_waves_sign.json create mode 100644 rust/tw_keypair/tests/ed25519_waves_tests.rs create mode 100644 rust/tw_keypair/tests/nist256p1_priv_to_pub_compressed.json create mode 100644 rust/tw_keypair/tests/nist256p1_tests.rs create mode 100644 rust/tw_keypair/tests/nist256p1_verify.json create mode 100644 rust/tw_keypair/tests/private_key_ffi_tests.rs create mode 100644 rust/tw_keypair/tests/public_key_ffi_tests.rs create mode 100644 rust/tw_keypair/tests/secp256k1_sign.json create mode 100644 rust/tw_keypair/tests/secp256k1_tests.rs create mode 100644 rust/tw_keypair/tests/tw_keypair_starkex_tests.rs create mode 100644 rust/tw_memory/src/ffi/tw_data.rs create mode 100644 rust/tw_memory/src/ffi/tw_data_vector.rs create mode 100644 rust/tw_memory/src/ffi/tw_string.rs create mode 100644 rust/tw_memory/src/test_utils/mod.rs create mode 100644 rust/tw_memory/src/test_utils/tw_data_helper.rs create mode 100644 rust/tw_memory/src/test_utils/tw_data_vector_helper.rs create mode 100644 rust/tw_memory/src/test_utils/tw_string_helper.rs create mode 100644 rust/tw_memory/src/test_utils/tw_wrapper.rs create mode 100644 rust/tw_number/Cargo.toml create mode 100644 rust/tw_number/src/i256.rs create mode 100644 rust/tw_number/src/lib.rs create mode 100644 rust/tw_number/src/sign.rs create mode 100644 rust/tw_number/src/u256.rs create mode 100644 rust/tw_number/tests/u256.rs create mode 100644 rust/tw_ronin/Cargo.toml create mode 100644 rust/tw_ronin/src/address.rs create mode 100644 rust/tw_ronin/src/entry.rs create mode 100644 rust/tw_ronin/src/lib.rs create mode 100644 rust/tw_ronin/src/ronin_context.rs create mode 100644 rust/tw_ronin/tests/address.rs create mode 100644 rust/tw_ronin/tests/compiler.rs create mode 100644 rust/tw_ronin/tests/rlp.rs create mode 100644 rust/tw_ronin/tests/signer.rs delete mode 100644 rust/tw_starknet/Cargo.toml delete mode 100644 rust/tw_starknet/src/ffi.rs delete mode 100644 rust/tw_starknet/src/key_pair.rs delete mode 100644 rust/tw_starknet/tests/starknet_ffi_tests.rs create mode 100644 rust/tw_utxo/Cargo.toml create mode 100644 rust/tw_utxo/src/compiler.rs create mode 100644 rust/tw_utxo/src/lib.rs create mode 100644 rust/tw_utxo/tests/common.rs create mode 100644 rust/tw_utxo/tests/input_selection.rs create mode 100644 rust/tw_utxo/tests/p2pkh.rs create mode 100644 rust/tw_utxo/tests/p2tr.rs create mode 100644 rust/tw_utxo/tests/p2wpkh.rs create mode 100644 rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs create mode 100644 rust/wallet_core_rs/src/ffi/bitcoin/mod.rs create mode 100644 rust/wallet_core_rs/src/ffi/ethereum/abi.rs create mode 100644 rust/wallet_core_rs/src/ffi/ethereum/mod.rs create mode 100644 rust/wallet_core_rs/src/ffi/ethereum/rlp.rs create mode 100644 rust/wallet_core_rs/src/ffi/mod.rs create mode 100644 rust/wallet_core_rs/tests/data/custom.json create mode 100644 rust/wallet_core_rs/tests/data/custom_decoded.json create mode 100644 rust/wallet_core_rs/tests/ethereum_abi.rs create mode 100644 rust/wallet_core_rs/tests/ethereum_rlp.rs delete mode 100644 src/Ethereum/ABI.h delete mode 100644 src/Ethereum/ABI/Array.cpp delete mode 100644 src/Ethereum/ABI/Array.h delete mode 100644 src/Ethereum/ABI/Bytes.cpp delete mode 100644 src/Ethereum/ABI/Bytes.h delete mode 100644 src/Ethereum/ABI/ParamAddress.cpp delete mode 100644 src/Ethereum/ABI/ParamAddress.h delete mode 100644 src/Ethereum/ABI/ParamBase.h delete mode 100644 src/Ethereum/ABI/ParamFactory.cpp delete mode 100644 src/Ethereum/ABI/ParamFactory.h delete mode 100644 src/Ethereum/ABI/ParamNumber.cpp delete mode 100644 src/Ethereum/ABI/ParamNumber.h delete mode 100644 src/Ethereum/ABI/ParamStruct.cpp delete mode 100644 src/Ethereum/ABI/ParamStruct.h delete mode 100644 src/Ethereum/ABI/Parameters.cpp delete mode 100644 src/Ethereum/ABI/Parameters.h create mode 100644 src/Ethereum/ABI/ProtoParam.h delete mode 100644 src/Ethereum/ABI/Tuple.cpp delete mode 100644 src/Ethereum/ABI/Tuple.h delete mode 100644 src/Ethereum/ERC4337.cpp delete mode 100644 src/Ethereum/ERC4337.h delete mode 100644 src/Ethereum/Signer.cpp delete mode 100644 src/Ethereum/Signer.h delete mode 100644 src/Ethereum/Transaction.cpp delete mode 100644 src/Ethereum/Transaction.h delete mode 100644 src/Ronin/Address.cpp delete mode 100644 src/Ronin/Address.h create mode 100644 src/interface/TWEthereumRlp.cpp create mode 100644 src/proto/BitcoinV2.proto create mode 100644 src/proto/EthereumAbi.proto create mode 100644 src/proto/EthereumRlp.proto create mode 100644 src/proto/Utxo.proto create mode 100644 swift/Tests/Blockchains/EthereumRlpTests.swift delete mode 100644 tests/chains/Ethereum/AbiStructTests.cpp delete mode 100644 tests/chains/Ethereum/AbiTests.cpp create mode 100644 tests/chains/Ethereum/Data/1inch_decoded.json create mode 100644 tests/chains/Ethereum/Data/tuple_nested_decoded.json delete mode 100644 tests/chains/Ethereum/RLPTests.cpp delete mode 100644 tests/chains/Ethereum/SignerTests.cpp create mode 100644 tests/chains/Ethereum/TWRlpTests.cpp delete mode 100644 tests/chains/Ronin/AddressTests.cpp create mode 100644 tests/common/PublicKeyLegacy.h create mode 100755 tools/rust-fuzz create mode 100755 tools/rust-test diff --git a/.github/workflows/linux-ci-rust.yml b/.github/workflows/linux-ci-rust.yml index d255e25bb7b..9a27424cea2 100644 --- a/.github/workflows/linux-ci-rust.yml +++ b/.github/workflows/linux-ci-rust.yml @@ -40,6 +40,9 @@ jobs: run: | tools/install-rust-dependencies dev + - name: Install emsdk + run: tools/install-wasm-dependencies + - name: Check code formatting run: | cargo fmt --check @@ -55,6 +58,9 @@ jobs: cargo llvm-cov nextest --profile ci --no-fail-fast --lcov --output-path coverage.info working-directory: rust + - name: Run tests in WASM + run: tools/rust-test wasm + - name: Rust Test Report uses: dorny/test-reporter@v1 if: success() || failure() diff --git a/Dockerfile b/Dockerfile index ff8083071cf..7db09d1bd5b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,10 +14,7 @@ RUN apt-get update \ software-properties-common \ && apt-get clean && rm -rf /var/lib/apt/lists/* -# Add latest cmake SHELL ["/bin/bash", "-o", "pipefail", "-c"] -RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc | apt-key add - \ - && apt-add-repository "deb https://apt.kitware.com/ubuntu/ $(lsb_release -sc) main" # Install required packages for dev RUN apt-get update \ diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestBarz.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestBarz.kt index b1e90097602..235cd442b73 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestBarz.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestBarz.kt @@ -127,7 +127,7 @@ class TestBarz { assertEquals(Numeric.toHexString(output.preHash.toByteArray()), "0x2d37191a8688f69090451ed90a0a9ba69d652c2062ee9d023b3ebe964a3ed2ae"); - assertEquals(output.encoded.toStringUtf8(), "{\"callData\":\"0xb61d27f600000000000000000000000061061fcae11fd5461535e134eff67a98cfff44e9000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"100000\",\"initCode\":\"0x\",\"maxFeePerGas\":\"7033440745\",\"maxPriorityFeePerGas\":\"7033440745\",\"nonce\":\"2\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"46856\",\"sender\":\"0xb16db98b365b1f89191996942612b14f1da4bd5f\",\"signature\":\"0x80e84992ebf8d5f71180231163ed150a7557ed0aa4b4bcee23d463a09847e4642d0fbf112df2e5fa067adf4b2fa17fc4a8ac172134ba5b78e3ec9c044e7f28d71c\",\"verificationGasLimit\":\"100000\"}"); + assertEquals(output.encoded.toStringUtf8(), "{\"callData\":\"0xb61d27f600000000000000000000000061061fcae11fd5461535e134eff67a98cfff44e9000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"100000\",\"initCode\":\"0x\",\"maxFeePerGas\":\"7033440745\",\"maxPriorityFeePerGas\":\"7033440745\",\"nonce\":\"2\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"46856\",\"sender\":\"0xb16Db98B365B1f89191996942612B14F1Da4Bd5f\",\"signature\":\"0x80e84992ebf8d5f71180231163ed150a7557ed0aa4b4bcee23d463a09847e4642d0fbf112df2e5fa067adf4b2fa17fc4a8ac172134ba5b78e3ec9c044e7f28d71c\",\"verificationGasLimit\":\"100000\"}"); } // https://testnet.bscscan.com/tx/0xea1f5cddc0653e116327cbcb3bc770360a642891176eff2ec69c227e46791c31 @@ -169,7 +169,7 @@ class TestBarz { assertEquals(Numeric.toHexString(output.preHash.toByteArray()), "0x548c13a0bb87981d04a3a24a78ad5e4ba8d0afbf3cfe9311250e07b54cd38937"); - assertEquals(output.encoded.toStringUtf8(), "{\"callData\":\"0xb61d27f600000000000000000000000061061fcae11fd5461535e134eff67a98cfff44e9000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"2500000\",\"initCode\":\"0x3fc708630d85a3b5ec217e53100ec2b735d4f800296601cd0000000000000000000000005034534efe9902779ed6ea6983f435c00f3bc51000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004104b173a6a812025c40c38bac46343646bd0a8137c807aae6e04aac238cc24d2ad2116ca14d23d357588ff2aabd7db29d5976f4ecc8037775db86f67e873a306b1f00000000000000000000000000000000000000000000000000000000000000\",\"maxFeePerGas\":\"7033440745\",\"maxPriorityFeePerGas\":\"7033440745\",\"nonce\":\"0\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"46856\",\"sender\":\"0x1392ae041bfbdbaa0cff9234a0c8f64df97b7218\",\"signature\":\"0xbf1b68323974e71ad9bd6dfdac07dc062599d150615419bb7876740d2bcf3c8909aa7e627bb0e08a2eab930e2e7313247c9b683c884236dd6ea0b6834fb2cb0a1b\",\"verificationGasLimit\":\"3000000\"}") + assertEquals(output.encoded.toStringUtf8(), "{\"callData\":\"0xb61d27f600000000000000000000000061061fcae11fd5461535e134eff67a98cfff44e9000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"2500000\",\"initCode\":\"0x3fc708630d85a3b5ec217e53100ec2b735d4f800296601cd0000000000000000000000005034534efe9902779ed6ea6983f435c00f3bc51000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004104b173a6a812025c40c38bac46343646bd0a8137c807aae6e04aac238cc24d2ad2116ca14d23d357588ff2aabd7db29d5976f4ecc8037775db86f67e873a306b1f00000000000000000000000000000000000000000000000000000000000000\",\"maxFeePerGas\":\"7033440745\",\"maxPriorityFeePerGas\":\"7033440745\",\"nonce\":\"0\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"46856\",\"sender\":\"0x1392Ae041BfBdBAA0cFF9234a0C8F64df97B7218\",\"signature\":\"0xbf1b68323974e71ad9bd6dfdac07dc062599d150615419bb7876740d2bcf3c8909aa7e627bb0e08a2eab930e2e7313247c9b683c884236dd6ea0b6834fb2cb0a1b\",\"verificationGasLimit\":\"3000000\"}") } // https://testnet.bscscan.com/tx/0x872f709815a9f79623a349f2f16d93b52c4d5136967bab53a586f045edbe9203 @@ -224,6 +224,6 @@ class TestBarz { val output = AnySigner.sign(signingInput.build(), ETHEREUM, SigningOutput.parser()) assertEquals(Numeric.toHexString(output.preHash.toByteArray()), "0x84d0464f5a2b191e06295443970ecdcd2d18f565d0d52b5a79443192153770ab"); - assertEquals(output.encoded.toStringUtf8(), "{\"callData\":\"0x47e1da2a000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000003bbb5660b8687c2aa453a0e42dcb6e0732b126600000000000000000000000003bbb5660b8687c2aa453a0e42dcb6e0732b12660000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27890000000000000000000000000000000000000000000000008ac7230489e80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27890000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"88673\",\"initCode\":\"0x\",\"maxFeePerGas\":\"10000000000\",\"maxPriorityFeePerGas\":\"10000000000\",\"nonce\":\"3\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"56060\",\"sender\":\"0x1e6c542ebc7c960c6a155a9094db838cef842cf5\",\"signature\":\"0x0747b665fe9f3a52407f95a35ac3e76de37c9b89483ae440431244e89a77985f47df712c7364c1a299a5ef62d0b79a2cf4ed63d01772275dd61f72bd1ad5afce1c\",\"verificationGasLimit\":\"522180\"}") + assertEquals(output.encoded.toStringUtf8(), "{\"callData\":\"0x47e1da2a000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000003bbb5660b8687c2aa453a0e42dcb6e0732b126600000000000000000000000003bbb5660b8687c2aa453a0e42dcb6e0732b12660000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27890000000000000000000000000000000000000000000000008ac7230489e80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27890000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"88673\",\"initCode\":\"0x\",\"maxFeePerGas\":\"10000000000\",\"maxPriorityFeePerGas\":\"10000000000\",\"nonce\":\"3\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"56060\",\"sender\":\"0x1E6c542ebC7c960c6A155A9094DB838cEf842cf5\",\"signature\":\"0x0747b665fe9f3a52407f95a35ac3e76de37c9b89483ae440431244e89a77985f47df712c7364c1a299a5ef62d0b79a2cf4ed63d01772275dd61f72bd1ad5afce1c\",\"verificationGasLimit\":\"522180\"}") } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAbi.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAbi.kt index 1edb4127f62..b6ad3654115 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAbi.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAbi.kt @@ -1,10 +1,12 @@ package com.trustwallet.core.app.blockchains.ethereum +import com.google.protobuf.ByteString import com.trustwallet.core.app.utils.toHexByteArray import org.junit.Assert.assertEquals import org.junit.Test import com.trustwallet.core.app.utils.Numeric -import wallet.core.jni.EthereumAbi +import wallet.core.jni.CoinType +import wallet.core.jni.proto.EthereumAbi class TestEthereumAbiDecoder { @@ -35,15 +37,15 @@ class TestEthereumAbiDecoder { } """.trimIndent() - val decoded = EthereumAbi.decodeCall(call, abi) - val expected = """{"function":"approve(address,uint256)","inputs":[{"name":"_spender","type":"address","value":"0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed"},{"name":"_value","type":"uint256","value":"1"}]}""" + val decoded = wallet.core.jni.EthereumAbi.decodeCall(call, abi) + val expected = """{"function":"approve(address,uint256)","inputs":[{"name":"_spender","type":"address","value":"0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"},{"name":"_value","type":"uint256","value":"1"}]}""" assertEquals(decoded, expected) } @Test fun testEthereumAbiEncodeTyped() { - val hash = EthereumAbi.encodeTyped( + val hash = wallet.core.jni.EthereumAbi.encodeTyped( """ { "types": { @@ -94,4 +96,117 @@ class TestEthereumAbiDecoder { """) assertEquals(Numeric.toHexString(hash), "0xa85c2e2b118698e88db68a8105b794a8cc7cec074e89ef991cb4f5f533819cc2") } + + @Test + fun testEthereumAbiEncodeFunction() { + val amountIn = EthereumAbi.NumberNParam.newBuilder().apply { + bits = 256 + value = ByteString.copyFrom(Numeric.hexStringToByteArray("0xde0b6b3a7640000")) // 1000000000000000000 + } + val amountOutMin = EthereumAbi.NumberNParam.newBuilder().apply { + bits = 256 + value = ByteString.copyFrom(Numeric.hexStringToByteArray("0x229f7e501ad62bdb")) // 2494851601099271131 + } + val deadline = EthereumAbi.NumberNParam.newBuilder().apply { + bits = 256 + value = ByteString.copyFrom(Numeric.hexStringToByteArray("0x5f0ed070")) // 1594806384 + } + val addressType = EthereumAbi.AddressType.newBuilder().build() + + val encodingInput = EthereumAbi.FunctionEncodingInput.newBuilder() + .setFunctionName("swapExactTokensForTokens") + .addTokens(EthereumAbi.Token.newBuilder().setNumberUint(amountIn)) + .addTokens(EthereumAbi.Token.newBuilder().setNumberUint(amountOutMin)) + .addTokens(EthereumAbi.Token.newBuilder().apply { + array = EthereumAbi.ArrayParam.newBuilder() + .setElementType(EthereumAbi.ParamType.newBuilder().setAddress(addressType)) + .addElements(EthereumAbi.Token.newBuilder().setAddress("0x6B175474E89094C44Da98b954EedeAC495271d0F")) + .addElements(EthereumAbi.Token.newBuilder().setAddress("0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2")) + .addElements(EthereumAbi.Token.newBuilder().setAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")) + .addElements(EthereumAbi.Token.newBuilder().setAddress("0xE41d2489571d322189246DaFA5ebDe1F4699F498")) + .build() + }) + .addTokens(EthereumAbi.Token.newBuilder().setAddress("0x7d8bf18C7cE84b3E175b339c4Ca93aEd1dD166F1")) + .addTokens(EthereumAbi.Token.newBuilder().setNumberUint(deadline)) + .build() + + val encodingOutputData = wallet.core.jni.EthereumAbi.encodeFunction(CoinType.ETHEREUM, encodingInput.toByteArray()) + val encodingOutput = EthereumAbi.FunctionEncodingOutput.parseFrom(encodingOutputData) + + assertEquals(encodingOutput.error, EthereumAbi.AbiError.OK) + assertEquals(encodingOutput.functionType, "swapExactTokensForTokens(uint256,uint256,address[],address,uint256)") + + val expectedEncoded = ByteString.copyFrom(Numeric.hexStringToByteArray("0x38ed17390000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000229f7e501ad62bdb00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000007d8bf18c7ce84b3e175b339c4ca93aed1dd166f1000000000000000000000000000000000000000000000000000000005f0ed07000000000000000000000000000000000000000000000000000000000000000040000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000009f8f72aa9304c8b593d555f12ef6589cc3a579a2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498")) + assertEquals(encodingOutput.encoded, expectedEncoded) + } + + @Test + fun testEthereumAbiDecodeParams() { + val encoded = ByteString.copyFrom(Numeric.hexStringToByteArray("00000000000000000000000088341d1a8f672d2780c8dc725902aae72f143b0c0000000000000000000000000000000000000000000000000000000000000001")) + + val addressType = EthereumAbi.AddressType.newBuilder().build() + val boolType = EthereumAbi.BoolType.newBuilder().build() + + val params = EthereumAbi.AbiParams.newBuilder() + .addParams(EthereumAbi.Param.newBuilder().apply { + name = "to" + param = EthereumAbi.ParamType.newBuilder().setAddress(addressType).build() + }) + .addParams(EthereumAbi.Param.newBuilder().apply { + name = "approved" + param = EthereumAbi.ParamType.newBuilder().setBoolean(boolType).build() + }) + val decodingInput = EthereumAbi.ParamsDecodingInput.newBuilder() + .setEncoded(encoded) + .setAbiParams(params) + .build() + + val decodingOutputData = wallet.core.jni.EthereumAbi.decodeParams(CoinType.ETHEREUM, decodingInput.toByteArray()) + val decodingOutput = EthereumAbi.ParamsDecodingOutput.parseFrom(decodingOutputData) + + assertEquals(decodingOutput.error, EthereumAbi.AbiError.OK) + + assertEquals(decodingOutput.getTokens(0).name, "to") + assertEquals(decodingOutput.getTokens(0).address, "0x88341d1a8F672D2780C8dC725902AAe72F143B0c") + + assertEquals(decodingOutput.getTokens(1).name, "approved") + assertEquals(decodingOutput.getTokens(1).boolean, true) + } + + @Test + fun testEthereumAbiDecodeValue() { + val encoded = ByteString.copyFrom(Numeric.hexStringToByteArray("000000000000000000000000000000000000000000000000000000000000002c48656c6c6f20576f726c64212020202048656c6c6f20576f726c64212020202048656c6c6f20576f726c64210000000000000000000000000000000000000000")) + + val decodingInput = EthereumAbi.ValueDecodingInput.newBuilder() + .setEncoded(encoded) + .setParamType("string") + .build() + + val decodingOutputData = wallet.core.jni.EthereumAbi.decodeValue(CoinType.ETHEREUM, decodingInput.toByteArray()) + val decodingOutput = EthereumAbi.ValueDecodingOutput.parseFrom(decodingOutputData) + + assertEquals(decodingOutput.error, EthereumAbi.AbiError.OK) + assertEquals(decodingOutput.token.stringValue, "Hello World! Hello World! Hello World!") + } + + @Test + fun testEthereumAbiDecodeContractCall() { + val encoded = ByteString.copyFrom(Numeric.hexStringToByteArray("c47f0027000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000086465616462656566000000000000000000000000000000000000000000000000")) + val abi = """{"c47f0027":{"constant":false,"inputs":[{"name":"name","type":"string"}],"name":"setName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}}""" + val decodingInput = EthereumAbi.ContractCallDecodingInput.newBuilder() + .setEncoded(encoded) + .setSmartContractAbiJson(abi) + .build() + + val decodingOutputData = wallet.core.jni.EthereumAbi.decodeContractCall(CoinType.ETHEREUM, decodingInput.toByteArray()) + val decodingOutput = EthereumAbi.ContractCallDecodingOutput.parseFrom(decodingOutputData) + + assertEquals(decodingOutput.error, EthereumAbi.AbiError.OK) + + val expectedJson = """{"function":"setName(string)","inputs":[{"name":"name","type":"string","value":"deadbeef"}]}""" + assertEquals(decodingOutput.decodedJson, expectedJson) + + assertEquals(decodingOutput.getTokens(0).name, "name") + assertEquals(decodingOutput.getTokens(0).stringValue, "deadbeef") + } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAbiValue.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAbiValue.kt index 02a2680b3e5..5afd77932f9 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAbiValue.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumAbiValue.kt @@ -87,7 +87,7 @@ class TestEthereumAbiValue { fun testValueDecoderValue() { assertEquals("42", EthereumAbiValue.decodeValue(Numeric.hexStringToByteArray("000000000000000000000000000000000000000000000000000000000000002a"), "uint")) assertEquals("24", EthereumAbiValue.decodeValue(Numeric.hexStringToByteArray("0000000000000000000000000000000000000000000000000000000000000018"), "uint8")) - assertEquals("0xf784682c82526e245f50975190ef0fff4e4fc077", EthereumAbiValue.decodeValue(Numeric.hexStringToByteArray("000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077"), "address")) + assertEquals("0xF784682C82526e245F50975190EF0fff4E4fC077", EthereumAbiValue.decodeValue(Numeric.hexStringToByteArray("000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077"), "address")) assertEquals("Hello World! Hello World! Hello World!", EthereumAbiValue.decodeValue( Numeric.hexStringToByteArray("000000000000000000000000000000000000000000000000000000000000002c48656c6c6f20576f726c64212020202048656c6c6f20576f726c64212020202048656c6c6f20576f726c64210000000000000000000000000000000000000000"), "string")) @@ -103,7 +103,7 @@ class TestEthereumAbiValue { @Test fun testValueDecoderArray_address() { val input = Numeric.hexStringToByteArray("0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc0770000000000000000000000002e00cd222cb42b616d86d037cc494e8ab7f5c9a3") - assertEquals("[\"0xf784682c82526e245f50975190ef0fff4e4fc077\",\"0x2e00cd222cb42b616d86d037cc494e8ab7f5c9a3\"]", EthereumAbiValue.decodeArray(input, "address[]")) + assertEquals("[\"0xF784682C82526e245F50975190EF0fff4E4fC077\",\"0x2e00CD222Cb42B616D86D037Cc494e8ab7F5c9a3\"]", EthereumAbiValue.decodeArray(input, "address[]")) } @Test diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumRlp.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumRlp.kt new file mode 100644 index 00000000000..80abb39ff7f --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumRlp.kt @@ -0,0 +1,57 @@ +package com.trustwallet.core.app.blockchains.ethereum + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert.assertEquals +import org.junit.Test +import com.trustwallet.core.app.utils.Numeric +import wallet.core.jni.CoinType +import wallet.core.jni.proto.Common +import wallet.core.jni.proto.EthereumRlp + +class TestEthereumRlp { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testEthereumRlpEncodeEip1559() { + val chainId = ByteString.copyFrom("0x0a".toHexByteArray()) + val nonce = ByteString.copyFrom("0x06".toHexByteArray()) + val maxInclusionFeePerGas = ByteString.copyFrom("0x77359400".toHexByteArray()) // 2000000000 + val maxFeePerGas = ByteString.copyFrom("0xb2d05e00".toHexByteArray()) // 3000000000 + val gasLimit = ByteString.copyFrom("0x526c".toHexByteArray()) // 21100 + val to = "0x6b175474e89094c44da98b954eedeac495271d0f" + val amount = 0.toLong() + val payload = ByteString.copyFrom("a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000001ee0c29f50cb1".toHexByteArray()) + // An empty `accessList`. + val accessList = EthereumRlp.RlpList.newBuilder().build() + + val rlpList = EthereumRlp.RlpList.newBuilder() + .addItems(EthereumRlp.RlpItem.newBuilder().setNumberU256(chainId)) + .addItems(EthereumRlp.RlpItem.newBuilder().setNumberU256(nonce)) + .addItems(EthereumRlp.RlpItem.newBuilder().setNumberU256(maxInclusionFeePerGas)) + .addItems(EthereumRlp.RlpItem.newBuilder().setNumberU256(maxFeePerGas)) + .addItems(EthereumRlp.RlpItem.newBuilder().setNumberU256(gasLimit)) + .addItems(EthereumRlp.RlpItem.newBuilder().setAddress(to)) + .addItems(EthereumRlp.RlpItem.newBuilder().setNumberU64(amount)) + .addItems(EthereumRlp.RlpItem.newBuilder().setData(payload)) + .addItems(EthereumRlp.RlpItem.newBuilder().setList(accessList)) + .build() + + val encodingInput = EthereumRlp.EncodingInput.newBuilder().apply { + item = EthereumRlp.RlpItem.newBuilder().setList(rlpList).build() + }.build() + + val outputData = wallet.core.jni.EthereumRlp.encode(CoinType.ETHEREUM, encodingInput.toByteArray()) + val output = EthereumRlp.EncodingOutput.parseFrom(outputData) + + assertEquals(output.error, Common.SigningError.OK) + assert(output.errorMessage.isEmpty()) + assertEquals( + Numeric.toHexString(output.encoded.toByteArray()), + "0xf86c0a06847735940084b2d05e0082526c946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000001ee0c29f50cb1c0" + ) + } +} \ No newline at end of file diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/neo/TestsNEOSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/neo/TestsNEOSigner.kt index d67f8d11559..eb40bbf1892 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/neo/TestsNEOSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/neo/TestsNEOSigner.kt @@ -99,5 +99,10 @@ class TestNEOSigner { assertEquals( "8000001efb50cb3be3e08917b308a1dbdb2408109394560ec67518af43035d8c260815c601000bd791a26120eef181d8162bd6cb7495dee1299aa67bb796dcd4a03769f9b24e00000bea299e6a243c9379c3e8884c9176b1456b3017611772b2fadc55d10901ee3f000026c413526bbd45cca355683db9f39d6864a7e298f481f2cdeefe8b578ccea96e00002b2647616d4f4143700f8e862aa8427efd7fa9998fe040e23ed877d2cbd35af700003159b899275e2f0e0b1314acddc7e1ec5948598fca40a9733e2b448fe9344705000036509c8a487005aa8e16663613d2d767461ee2f8dc4f678cc22f9148d4420c8b0000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040100385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040200385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040300385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040400385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040500385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040600385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040700385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040800385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f04090040ec871088beb680f5b149767dbb0b8ac7ec1a1c5836e177606b6200e6bc83cf00004e393bd89d886ae116ed9e6b49be427b21f7247d896923265e68dfa82b57d79b00005b99bf2caacf20bfc9cd51b3d3472499383c803c2d781d00f1e2dd970325eeb4000062ac42685ef8b856291bb0264fcb767b00706a15243326775f61a159a29c01e100006f011d435ef43c066689d1222f4eada1d4590ebaaa190960ac26a5acf29d37bd00007dea63ea47a6c9e8318f3b19a0df5ccb3a348f54a176736afa7b9b3b843f4c160000925e50254e8056bfd540f3d45f171dbab504f5b191070ee7af1e16764ac7ce4a00009677a6869128961a1a3b17e609e915d2d9a29ceaab5689dccb841ca729665c8900009692e4e512eb2e04b10042bcc28910140b2229ede40291b0e1a0c3c44381825400009dc6c119d0f4bacb1b1e9faffcba33581729c1915a2f1147ce7a6fc8abe4455300009f6b635afee02b5db0c93a5b1bfcace34a18c78d76c73b7bf90d21d4d0193ec80000b11bbb613e36b2bcc6c3a76c888c6c139957a1b7091dab26ce88b65c3fb056340000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040a00039b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500ba1dd205000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50083064905000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ace72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c605013cf0617000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac014140dc261ac093a87640441bf0c3ad4a55ec727932b9175f600618bb5275f31aacf122956bc88746dc666759a2d67f120fe3ce1659f916d22a91e0b02421d3bddbd1232102a41c2aea8568864b106553729d32b1317ec463aa23e7a3521455d95992e17a7aac", hex) + + // TODO uncomment when nist256p1 Rust implementation is enabled. + // assertEquals( + // "8000001efb50cb3be3e08917b308a1dbdb2408109394560ec67518af43035d8c260815c601000bd791a26120eef181d8162bd6cb7495dee1299aa67bb796dcd4a03769f9b24e00000bea299e6a243c9379c3e8884c9176b1456b3017611772b2fadc55d10901ee3f000026c413526bbd45cca355683db9f39d6864a7e298f481f2cdeefe8b578ccea96e00002b2647616d4f4143700f8e862aa8427efd7fa9998fe040e23ed877d2cbd35af700003159b899275e2f0e0b1314acddc7e1ec5948598fca40a9733e2b448fe9344705000036509c8a487005aa8e16663613d2d767461ee2f8dc4f678cc22f9148d4420c8b0000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040100385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040200385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040300385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040400385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040500385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040600385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040700385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040800385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f04090040ec871088beb680f5b149767dbb0b8ac7ec1a1c5836e177606b6200e6bc83cf00004e393bd89d886ae116ed9e6b49be427b21f7247d896923265e68dfa82b57d79b00005b99bf2caacf20bfc9cd51b3d3472499383c803c2d781d00f1e2dd970325eeb4000062ac42685ef8b856291bb0264fcb767b00706a15243326775f61a159a29c01e100006f011d435ef43c066689d1222f4eada1d4590ebaaa190960ac26a5acf29d37bd00007dea63ea47a6c9e8318f3b19a0df5ccb3a348f54a176736afa7b9b3b843f4c160000925e50254e8056bfd540f3d45f171dbab504f5b191070ee7af1e16764ac7ce4a00009677a6869128961a1a3b17e609e915d2d9a29ceaab5689dccb841ca729665c8900009692e4e512eb2e04b10042bcc28910140b2229ede40291b0e1a0c3c44381825400009dc6c119d0f4bacb1b1e9faffcba33581729c1915a2f1147ce7a6fc8abe4455300009f6b635afee02b5db0c93a5b1bfcace34a18c78d76c73b7bf90d21d4d0193ec80000b11bbb613e36b2bcc6c3a76c888c6c139957a1b7091dab26ce88b65c3fb056340000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040a00039b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500ba1dd205000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50083064905000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ace72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c605013cf0617000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac014140dc261ac093a87640441bf0c3ad4a55ec727932b9175f600618bb5275f31aacf1dd6a943678b9239a98a65d2980edf01beed0a0b4904573f31309a6a128a54980232102a41c2aea8568864b106553729d32b1317ec463aa23e7a3521455d95992e17a7aac", + // hex) } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ontology/TestOntologySigning.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ontology/TestOntologySigning.kt index abff3b06c6c..2bf777966fb 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ontology/TestOntologySigning.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ontology/TestOntologySigning.kt @@ -76,6 +76,11 @@ class TestOntologySigning { assertEquals( "00d102d45c8bf401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df67100c66b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc814feec06b79ed299ea06fcb94abac41aaf3ead76586a7cc8516a7cc86c51c1087472616e736665721400000000000000000000000000000000000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b6500024140301766d925382a6ebb2ebeb18d3741954c9370dcf6d9c45b34ce7b18bc42dcdb7cff28ddaf7f1048822c0ca21a0c4926323a2497875b963f3b8cbd3717aa6e7c2321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac414038466b25ac49a22ba8c301328ef049a61711b257987e85e25d63e0444a14e860305a4cd3bb6ea2fe80fd293abb3c592e679c42c546cbf3baa051a07b28b374a6232103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", hex) + + // TODO uncomment when nist256p1 Rust implementation is enabled. + // assertEquals( + // "00d102d45c8bf401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df67100c66b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc814feec06b79ed299ea06fcb94abac41aaf3ead76586a7cc8516a7cc86c51c1087472616e736665721400000000000000000000000000000000000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b6500024140301766d925382a6ebb2ebeb18d3741954c9370dcf6d9c45b34ce7b18bc42dcdb8300d7215080efb87dd3f35de5f3b6d98aacd6161fbc0845b82d0d8be4b8b6d52321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac414038466b25ac49a22ba8c301328ef049a61711b257987e85e25d63e0444a14e860305a4cd3bb6ea2fe80fd293abb3c592e679c42c546cbf3baa051a07b28b374a6232103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", + // hex) } @Test @@ -100,6 +105,11 @@ class TestOntologySigning { assertEquals( "00d19d3182a8f401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df67100c66b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc814feec06b79ed299ea06fcb94abac41aaf3ead76586a7cc8516a7cc86c51c1087472616e736665721400000000000000000000000000000000000000020068164f6e746f6c6f67792e4e61746976652e496e766f6b6500024140e27e935b87855efad62bb76b21c7b591f445f867eff86f888ca6ee1870ecd80f73b8ab199a4d757b4c7b9ed46c4ff8cfa8aefaa90b7fb6485e358034448cba752321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac4140450047b2efb384129a16ec4c707790e9379b978cc7085170071d8d7c5c037d743b078bd4e21bb4404c0182a32ee05260e22454dffb34dacccf458dfbee6d32db232103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", hex) + + // TODO uncomment when nist256p1 Rust implementation is enabled. + // assertEquals( + // "00d19d3182a8f401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df67100c66b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc814feec06b79ed299ea06fcb94abac41aaf3ead76586a7cc8516a7cc86c51c1087472616e736665721400000000000000000000000000000000000000020068164f6e746f6c6f67792e4e61746976652e496e766f6b6500024140e27e935b87855efad62bb76b21c7b591f445f867eff86f888ca6ee1870ecd80f8c4754e565b28a85b384612b93b00730143800049b97e83c95844a8eb7d66adc2321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac4140450047b2efb384129a16ec4c707790e9379b978cc7085170071d8d7c5c037d74c4f8742a1de44bc0b3fe7d5cd11fad9edac2a5cdabe2c3b824743cc70df5f276232103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", + // hex) } } diff --git a/codegen-v2/manifest/TWCoinType.yaml b/codegen-v2/manifest/TWCoinType.yaml index 5489b74cf98..872c3050766 100644 --- a/codegen-v2/manifest/TWCoinType.yaml +++ b/codegen-v2/manifest/TWCoinType.yaml @@ -179,7 +179,7 @@ enums: - name: boba value: 10000288 - name: metis - value: 1001088 + value: 10001088 - name: aurora value: 1323161554 - name: evmos diff --git a/codegen-v2/src/codegen/swift/templates/WalletCore.h b/codegen-v2/src/codegen/swift/templates/WalletCore.h index 8b5657a9eb4..98e432e9066 100644 --- a/codegen-v2/src/codegen/swift/templates/WalletCore.h +++ b/codegen-v2/src/codegen/swift/templates/WalletCore.h @@ -45,6 +45,7 @@ FOUNDATION_EXPORT const unsigned char WalletCoreVersionString[]; #include "TWEthereumAbiFunction.h" #include "TWEthereumAbiValue.h" #include "TWEthereumChainID.h" +#include "TWEthereumRlp.h" #include "TWEthereumMessageSigner.h" #include "TWFIOAccount.h" #include "TWFilecoinAddressConverter.h" diff --git a/include/TrustWalletCore/TWEthereumAbi.h b/include/TrustWalletCore/TWEthereumAbi.h index 18408f3ebff..33b976dc61a 100644 --- a/include/TrustWalletCore/TWEthereumAbi.h +++ b/include/TrustWalletCore/TWEthereumAbi.h @@ -7,8 +7,9 @@ #pragma once #include "TWBase.h" -#include "TWString.h" +#include "TWCoinType.h" #include "TWData.h" +#include "TWString.h" TW_EXTERN_C_BEGIN @@ -18,6 +19,38 @@ struct TWEthereumAbiFunction; TW_EXPORT_STRUCT struct TWEthereumAbi; +/// Decode a contract call (function input) according to an ABI json. +/// +/// \param coin EVM-compatible coin type. +/// \param input The serialized data of `TW.EthereumAbi.Proto.ContractCallDecodingInput`. +/// \return The serialized data of a `TW.EthereumAbi.Proto.ContractCallDecodingOutput` proto object. +TW_EXPORT_STATIC_METHOD +TWData* _Nonnull TWEthereumAbiDecodeContractCall(enum TWCoinType coin, TWData* _Nonnull input); + +/// Decode a function input or output data according to a given ABI. +/// +/// \param coin EVM-compatible coin type. +/// \param input The serialized data of `TW.EthereumAbi.Proto.ParamsDecodingInput`. +/// \return The serialized data of a `TW.EthereumAbi.Proto.ParamsDecodingOutput` proto object. +TW_EXPORT_STATIC_METHOD +TWData* _Nonnull TWEthereumAbiDecodeParams(enum TWCoinType coin, TWData* _Nonnull input); + +/// /// Decodes an Eth ABI value according to a given type. +/// +/// \param coin EVM-compatible coin type. +/// \param input The serialized data of `TW.EthereumAbi.Proto.ValueDecodingInput`. +/// \return The serialized data of a `TW.EthereumAbi.Proto.ValueDecodingOutput` proto object. +TW_EXPORT_STATIC_METHOD +TWData* _Nonnull TWEthereumAbiDecodeValue(enum TWCoinType coin, TWData* _Nonnull input); + +/// Encode function to Eth ABI binary. +/// +/// \param coin EVM-compatible coin type. +/// \param input The serialized data of `TW.EthereumAbi.Proto.FunctionEncodingInput`. +/// \return The serialized data of a `TW.EthereumAbi.Proto.FunctionEncodingOutput` proto object. +TW_EXPORT_STATIC_METHOD +TWData* _Nonnull TWEthereumAbiEncodeFunction(enum TWCoinType coin, TWData* _Nonnull input); + /// Encode function to Eth ABI binary /// /// \param fn Non-null Eth abi function diff --git a/include/TrustWalletCore/TWEthereumRlp.h b/include/TrustWalletCore/TWEthereumRlp.h new file mode 100644 index 00000000000..1644c86e1e2 --- /dev/null +++ b/include/TrustWalletCore/TWEthereumRlp.h @@ -0,0 +1,27 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "TWBase.h" +#include "TWCoinType.h" +#include "TWData.h" +#include "TWString.h" + +TW_EXTERN_C_BEGIN + +TW_EXPORT_STRUCT +struct TWEthereumRlp; + +/// Encode an item or a list of items as Eth RLP binary format. +/// +/// \param coin EVM-compatible coin type. +/// \param input Non-null serialized `EthereumRlp::Proto::EncodingInput`. +/// \return serialized `EthereumRlp::Proto::EncodingOutput`. +TW_EXPORT_STATIC_METHOD +TWData* _Nonnull TWEthereumRlpEncode(enum TWCoinType coin, TWData* _Nonnull input); + +TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWTransactionCompiler.h b/include/TrustWalletCore/TWTransactionCompiler.h index 529bc12e302..5ec06883c40 100644 --- a/include/TrustWalletCore/TWTransactionCompiler.h +++ b/include/TrustWalletCore/TWTransactionCompiler.h @@ -20,6 +20,7 @@ struct TWTransactionCompiler; /// Builds a coin-specific SigningInput (proto object) from a simple transaction. /// +/// \deprecated `TWTransactionCompilerBuildInput` will be removed soon. /// \param coin coin type. /// \param from sender of the transaction. /// \param to receiver of the transaction. diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 3199454c20e..7ae598db1fd 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -26,6 +26,15 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +[[package]] +name = "arbitrary" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "ark-ff" version = "0.4.2" @@ -90,6 +99,12 @@ dependencies = [ "rand", ] +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "atty" version = "0.2.14" @@ -107,6 +122,18 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bcs" version = "0.1.4" @@ -125,9 +152,9 @@ checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" [[package]] name = "bigdecimal" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" +checksum = "6aaf33151a6429fe9211d1b276eafdf70cdff28b071e76c0b0e1503221ea3744" dependencies = [ "num-bigint", "num-integer", @@ -137,9 +164,9 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.30.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b36f4c848f6bd9ff208128f08751135846cc23ae57d66ab10a22efff1c675f3c" +checksum = "4e99ff7289b20a7385f66a0feda78af2fc119d28fb56aea8886a9cd0a4abdd75" dependencies = [ "bech32", "bitcoin-private", @@ -169,6 +196,18 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake-hash" version = "0.4.1" @@ -182,11 +221,13 @@ dependencies = [ [[package]] name = "blake2" -version = "0.10.6" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +checksum = "0a4e37d16930f5459780f5621038b6382b9bb37c19016f39fb6b5808d831f174" dependencies = [ - "digest 0.10.6", + "crypto-mac", + "digest 0.9.0", + "opaque-debug", ] [[package]] @@ -225,12 +266,24 @@ version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + [[package]] name = "cc" version = "1.0.79" @@ -258,6 +311,12 @@ dependencies = [ "vec_map", ] +[[package]] +name = "const-oid" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" + [[package]] name = "cpufeatures" version = "0.2.5" @@ -267,13 +326,20 @@ dependencies = [ "libc", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-bigint" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" +checksum = "7c2538c4e68e52548bacb3e83ac549f903d44f011ac9d5abb5e132e67d0808f7" dependencies = [ "generic-array", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -288,6 +354,29 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + [[package]] name = "data-encoding" version = "2.3.3" @@ -296,9 +385,13 @@ checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" [[package]] name = "der" -version = "0.7.6" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56acb310e15652100da43d130af8d97b509e95af61aab1c5a7939ef24337ee17" +checksum = "82b10af9f9f9f2134a42d3f8aa74658660f2e0234b0eb81bd171df8aa32779ed" +dependencies = [ + "const-oid", + "zeroize", +] [[package]] name = "derivative" @@ -311,6 +404,17 @@ dependencies = [ "syn 1.0.107", ] +[[package]] +name = "derive_arbitrary" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cdeb9ec472d588e539a818b2dee436825730da08ad0017c4b1a17676bdc8b7" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.107", +] + [[package]] name = "digest" version = "0.9.0" @@ -327,16 +431,50 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer 0.10.3", + "const-oid", "crypto-common", "subtle", ] +[[package]] +name = "ecdsa" +version = "0.16.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a48e5d537b8a30c0b023116d981b16334be1485af7ca68db3a2b7024cbc957fd" +dependencies = [ + "der", + "digest 0.10.6", + "elliptic-curve", + "rfc6979", + "signature", +] + [[package]] name = "either" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +[[package]] +name = "elliptic-curve" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75c71eaa367f2e5d556414a8eea812bc62985c879748d6403edabd9cb03f16e7" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.6", + "ff", + "generic-array", + "group", + "hkdf", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "env_logger" version = "0.7.1" @@ -350,6 +488,41 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "arbitrary", + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "generic-array" version = "0.14.6" @@ -358,6 +531,18 @@ checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] @@ -369,7 +554,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -382,6 +567,23 @@ dependencies = [ "digest 0.10.6", ] +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -403,6 +605,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -421,6 +632,36 @@ dependencies = [ "quick-error", ] +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.107", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "itertools" version = "0.10.5" @@ -445,6 +686,20 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2 0.10.6", + "signature", +] + [[package]] name = "keccak" version = "0.1.3" @@ -454,6 +709,12 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.141" @@ -542,6 +803,50 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.6", +] + +[[package]] +name = "parity-scale-codec" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ddb756ca205bd108aee3c62c6d3c994e1df84a59b9d6d4a5ea42ee1fd5a9a28" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.107", +] + [[package]] name = "paste" version = "1.0.11" @@ -560,17 +865,57 @@ dependencies = [ "nom", ] +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "primeorder" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8d3875361e28f7753baefef104386e7aa47642c93023356d97fdef4003bfb5" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "primitive-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + [[package]] name = "proc-macro2" -version = "1.0.59" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -592,13 +937,19 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.28" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" @@ -607,7 +958,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -617,7 +968,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", ] [[package]] @@ -626,7 +986,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.9", ] [[package]] @@ -676,6 +1036,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + [[package]] name = "ripemd" version = "0.1.3" @@ -685,6 +1060,22 @@ dependencies = [ "digest 0.10.6", ] +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + [[package]] name = "rustc_version" version = "0.4.0" @@ -700,6 +1091,20 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +[[package]] +name = "sec1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48518a2b5775ba8ca5b46596aae011caa431e6ce7e4a67ead66d92f08884220e" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "secp256k1" version = "0.27.0" @@ -728,9 +1133,9 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.163" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" dependencies = [ "serde_derive", ] @@ -746,13 +1151,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.15", ] [[package]] @@ -777,6 +1182,19 @@ dependencies = [ "digest 0.10.6", ] +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "sha2" version = "0.10.6" @@ -798,6 +1216,32 @@ dependencies = [ "keccak", ] +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.6", + "rand_core 0.6.4", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spki" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a5be806ab6f127c3da44b7378837ebf01dadca8510a0e572460216b228bd0e" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "starknet-crypto" version = "0.5.1" @@ -811,7 +1255,7 @@ dependencies = [ "num-integer", "num-traits", "rfc6979", - "sha2", + "sha2 0.10.6", "starknet-crypto-codegen", "starknet-curve", "starknet-ff", @@ -826,7 +1270,7 @@ checksum = "e6dc88f1f470d9de1001ffbb90d2344c9dd1a615f5467daf0574e2975dfd9ebd" dependencies = [ "starknet-curve", "starknet-ff", - "syn 2.0.18", + "syn 2.0.15", ] [[package]] @@ -847,11 +1291,17 @@ dependencies = [ "ark-ff", "bigdecimal", "crypto-bigint", - "getrandom", + "getrandom 0.2.9", "hex", "serde", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.8.0" @@ -877,9 +1327,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.18" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", @@ -898,6 +1348,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "termcolor" version = "1.2.0" @@ -936,6 +1392,38 @@ dependencies = [ "syn 1.0.107", ] +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" + +[[package]] +name = "toml_edit" +version = "0.19.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tw_any_coin" +version = "0.1.0" +dependencies = [ + "tw_any_coin", + "tw_coin_entry", + "tw_coin_registry", + "tw_encoding", + "tw_keypair", + "tw_memory", + "tw_misc", + "tw_number", + "tw_proto", +] + [[package]] name = "tw_bitcoin" version = "0.1.0" @@ -944,47 +1432,133 @@ dependencies = [ "secp256k1", "serde", "serde_json", + "tw_coin_entry", "tw_encoding", + "tw_keypair", + "tw_memory", + "tw_misc", + "tw_proto", + "tw_utxo", + "wallet-core-rs", +] + +[[package]] +name = "tw_coin_entry" +version = "0.1.0" +dependencies = [ + "serde_json", + "tw_keypair", "tw_memory", "tw_misc", + "tw_number", "tw_proto", ] +[[package]] +name = "tw_coin_registry" +version = "0.1.0" +dependencies = [ + "lazy_static", + "serde", + "serde_json", + "tw_bitcoin", + "tw_coin_entry", + "tw_ethereum", + "tw_evm", + "tw_keypair", + "tw_memory", + "tw_misc", + "tw_ronin", +] + [[package]] name = "tw_encoding" version = "0.1.0" dependencies = [ + "arbitrary", "bs58", "data-encoding", "hex", "tw_memory", ] +[[package]] +name = "tw_ethereum" +version = "0.1.0" +dependencies = [ + "tw_coin_entry", + "tw_encoding", + "tw_evm", + "tw_keypair", + "tw_number", + "tw_proto", +] + +[[package]] +name = "tw_evm" +version = "0.1.0" +dependencies = [ + "itertools", + "lazy_static", + "rlp", + "serde", + "serde_json", + "tw_coin_entry", + "tw_encoding", + "tw_hash", + "tw_keypair", + "tw_memory", + "tw_misc", + "tw_number", + "tw_proto", +] + [[package]] name = "tw_hash" version = "0.1.0" dependencies = [ + "arbitrary", "blake-hash", - "blake2", "blake2b-ref", "digest 0.10.6", "groestl", - "hex", "hmac", "ripemd", + "serde", + "serde_json", "sha1", - "sha2", + "sha2 0.10.6", "sha3", + "tw_encoding", "tw_memory", + "zeroize", ] [[package]] name = "tw_keypair" version = "0.1.0" dependencies = [ + "arbitrary", + "blake2", + "curve25519-dalek", "der", + "digest 0.9.0", + "ecdsa", + "k256", + "lazy_static", + "p256", + "rfc6979", + "ring", + "serde", + "serde_json", + "sha2 0.9.9", + "starknet-crypto", + "starknet-ff", "tw_encoding", + "tw_hash", "tw_memory", + "tw_misc", + "zeroize", ] [[package]] @@ -1008,10 +1582,24 @@ dependencies = [ "tw_memory", ] +[[package]] +name = "tw_number" +version = "0.1.0" +dependencies = [ + "arbitrary", + "lazy_static", + "primitive-types", + "serde", + "tw_encoding", + "tw_hash", + "tw_memory", +] + [[package]] name = "tw_proto" version = "0.1.0" dependencies = [ + "arbitrary", "pb-rs", "quick-protobuf", "tw_encoding", @@ -1019,14 +1607,29 @@ dependencies = [ ] [[package]] -name = "tw_starknet" +name = "tw_ronin" version = "0.1.0" dependencies = [ - "hex", - "starknet-crypto", - "starknet-ff", + "tw_coin_entry", + "tw_encoding", + "tw_evm", + "tw_keypair", + "tw_memory", + "tw_number", + "tw_proto", +] + +[[package]] +name = "tw_utxo" +version = "0.1.0" +dependencies = [ + "bitcoin", + "secp256k1", + "tw_coin_entry", "tw_encoding", + "tw_keypair", "tw_memory", + "tw_proto", ] [[package]] @@ -1035,6 +1638,19 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "arbitrary", + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + [[package]] name = "unicode-ident" version = "1.0.6" @@ -1053,6 +1669,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "vec_map" version = "0.8.2" @@ -1069,16 +1691,28 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wallet-core-rs" version = "0.1.0" dependencies = [ + "serde_json", + "tw_any_coin", "tw_bitcoin", + "tw_coin_entry", + "tw_coin_registry", "tw_encoding", + "tw_ethereum", "tw_hash", "tw_keypair", "tw_memory", + "tw_misc", "tw_move_parser", + "tw_number", "tw_proto", - "tw_starknet", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1139,6 +1773,16 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1170,6 +1814,24 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "winnow" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "zeroize" version = "1.6.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 2a3f3dd5dfc..b8fdf347fca 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,13 +1,31 @@ [workspace] members = [ + "tw_any_coin", "tw_bitcoin", + "tw_coin_entry", + "tw_coin_registry", "tw_encoding", + "tw_ethereum", + "tw_evm", "tw_hash", "tw_keypair", "tw_memory", "tw_misc", "tw_move_parser", + "tw_number", "tw_proto", - "tw_starknet", + "tw_ronin", + "tw_utxo", "wallet_core_rs", ] + +[profile.wasm-test] +inherits = "release" +# Fixes an incredibly slow compilation of `curve25519-dalek` package. +opt-level = 1 +debug = true +debug-assertions = true +overflow-checks = true + +[profile.release.package.curve25519-dalek] +opt-level = 2 diff --git a/rust/coverage.stats b/rust/coverage.stats index 2d567ce4c07..7d7ab43dc7c 100644 --- a/rust/coverage.stats +++ b/rust/coverage.stats @@ -1 +1 @@ -86.4 \ No newline at end of file +92.0 \ No newline at end of file diff --git a/rust/tw_any_coin/Cargo.toml b/rust/tw_any_coin/Cargo.toml new file mode 100644 index 00000000000..7ea537e73c2 --- /dev/null +++ b/rust/tw_any_coin/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "tw_any_coin" +version = "0.1.0" +edition = "2021" + +[dependencies] +tw_coin_entry = { path = "../tw_coin_entry" } +tw_coin_registry = { path = "../tw_coin_registry" } +tw_keypair = { path = "../tw_keypair" } +tw_memory = { path = "../tw_memory" } +tw_misc = { path = "../tw_misc" } + +[features] +test-utils = [] + +[dev-dependencies] +tw_any_coin = { path = "./", features = ["test-utils"] } +tw_encoding = { path = "../tw_encoding" } +tw_keypair = { path = "../tw_keypair", features = ["test-utils"] } +tw_memory = { path = "../tw_memory", features = ["test-utils"] } +tw_number = { path = "../tw_number" } +tw_proto = { path = "../tw_proto" } diff --git a/rust/tw_any_coin/src/any_address.rs b/rust/tw_any_coin/src/any_address.rs new file mode 100644 index 00000000000..23fe3fe42e4 --- /dev/null +++ b/rust/tw_any_coin/src/any_address.rs @@ -0,0 +1,68 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::derivation::Derivation; +use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::prefix::AddressPrefix; +use tw_coin_registry::coin_type::CoinType; +use tw_coin_registry::dispatcher::coin_dispatcher; +use tw_keypair::tw::PublicKey; +use tw_memory::Data; +use tw_misc::try_or_false; + +/// Represents an address in Rust for almost any blockchain. +#[derive(Debug, PartialEq)] +pub struct AnyAddress { + coin: CoinType, + address: String, +} + +impl AnyAddress { + /// Determines if the string is a valid Any address. + #[inline] + pub fn is_valid(coin: CoinType, address: &str, prefix: Option) -> bool { + let (ctx, entry) = try_or_false!(coin_dispatcher(coin)); + entry.validate_address(&ctx, address, prefix).is_ok() + } + + /// Creates an address from a string representation and a coin type. + #[inline] + pub fn with_string( + coin: CoinType, + address: &str, + prefix: Option, + ) -> AddressResult { + let (ctx, entry) = coin_dispatcher(coin).map_err(|_| AddressError::UnknownCoinType)?; + let address = entry.normalize_address(&ctx, address, prefix)?; + Ok(AnyAddress { coin, address }) + } + + /// Creates an address from a public key, derivation and prefix option. + #[inline] + pub fn with_public_key( + coin: CoinType, + public_key: PublicKey, + derivation: Derivation, + prefix: Option, + ) -> AddressResult { + let (ctx, entry) = coin_dispatcher(coin).map_err(|_| AddressError::UnknownCoinType)?; + let address = entry.derive_address(&ctx, public_key, derivation, prefix)?; + Ok(AnyAddress { coin, address }) + } + + /// Returns underlying data (public key or key hash). + #[inline] + pub fn get_data(&self) -> AddressResult { + let (ctx, entry) = coin_dispatcher(self.coin).map_err(|_| AddressError::UnknownCoinType)?; + entry.address_to_data(&ctx, &self.address, None) + } + + /// Returns the address string representation. + #[inline] + pub fn description(&self) -> &str { + &self.address + } +} diff --git a/rust/tw_any_coin/src/any_signer.rs b/rust/tw_any_coin/src/any_signer.rs new file mode 100644 index 00000000000..eea6859517b --- /dev/null +++ b/rust/tw_any_coin/src/any_signer.rs @@ -0,0 +1,30 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::error::{SigningError, SigningResult}; +use tw_coin_registry::coin_type::CoinType; +use tw_coin_registry::dispatcher::coin_dispatcher; +use tw_memory::Data; + +/// Represents a signer to sign transactions for any blockchain. +pub struct AnySigner; + +impl AnySigner { + /// Signs a transaction specified by the signing input and coin type. + #[inline] + pub fn sign(input: &[u8], coin: CoinType) -> SigningResult { + let (ctx, entry) = coin_dispatcher(coin)?; + entry.sign(&ctx, input).map_err(SigningError::from) + } + + /// Planning, for UTXO chains, in preparation for signing + /// It is optional, only UTXO chains need it, default impl. leaves empty result. + #[inline] + pub fn plan(input: &[u8], coin: CoinType) -> SigningResult { + let (ctx, entry) = coin_dispatcher(coin)?; + entry.plan(&ctx, input) + } +} diff --git a/rust/tw_any_coin/src/ffi/mod.rs b/rust/tw_any_coin/src/ffi/mod.rs new file mode 100644 index 00000000000..62c144645d8 --- /dev/null +++ b/rust/tw_any_coin/src/ffi/mod.rs @@ -0,0 +1,10 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod tw_any_address; +pub mod tw_any_signer; +pub mod tw_message_signer; +pub mod tw_transaction_compiler; diff --git a/rust/tw_any_coin/src/ffi/tw_any_address.rs b/rust/tw_any_coin/src/ffi/tw_any_address.rs new file mode 100644 index 00000000000..25aa2373a00 --- /dev/null +++ b/rust/tw_any_coin/src/ffi/tw_any_address.rs @@ -0,0 +1,104 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#![allow(clippy::missing_safety_doc)] + +use crate::any_address::AnyAddress; +use tw_coin_entry::derivation::Derivation; +use tw_keypair::ffi::pubkey::TWPublicKey; +use tw_memory::ffi::tw_data::TWData; +use tw_memory::ffi::tw_string::TWString; +use tw_memory::ffi::RawPtrTrait; +use tw_misc::{try_or_else, try_or_false}; + +/// Represents an address in Rust for almost any blockchain. +pub struct TWAnyAddress(AnyAddress); + +impl RawPtrTrait for TWAnyAddress {} + +/// Determines if the string is a valid Any address. +/// +/// \param string address to validate. +/// \param coin coin type of the address. +/// \return bool indicating if the address is valid. +#[no_mangle] +pub unsafe extern "C" fn tw_any_address_is_valid(string: *const TWString, coin: u32) -> bool { + let string = try_or_false!(TWString::from_ptr_as_ref(string)); + let string = try_or_false!(string.as_str()); + + AnyAddress::is_valid(coin, string, None) +} + +/// Creates an address from a string representation and a coin type. Must be deleted with `TWAnyAddressDelete` after use. +/// +/// \param string address to create. +/// \param coin coin type of the address. +/// \return `TWAnyAddress` pointer or nullptr if address and coin are invalid. +#[no_mangle] +pub unsafe extern "C" fn tw_any_address_create_with_string( + string: *const TWString, + coin: u32, +) -> *mut TWAnyAddress { + let string = try_or_else!(TWString::from_ptr_as_ref(string), std::ptr::null_mut); + let string = try_or_else!(string.as_str(), std::ptr::null_mut); + + AnyAddress::with_string(coin, string, None) + .map(|any_address| TWAnyAddress(any_address).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} + +/// Creates an address from a public key and derivation option. +/// +/// \param public_key derivates the address from the public key. +/// \param coin coin type of the address. +/// \param derivation the custom derivation to use. +/// \return `TWAnyAddress` pointer or nullptr if public key is invalid. +#[no_mangle] +pub unsafe extern "C" fn tw_any_address_create_with_public_key_derivation( + public_key: *mut TWPublicKey, + coin: u32, + derivation: u32, +) -> *mut TWAnyAddress { + let public_key = try_or_else!(TWPublicKey::from_ptr_as_ref(public_key), std::ptr::null_mut); + let derivation = try_or_else!(Derivation::from_raw(derivation), std::ptr::null_mut); + + AnyAddress::with_public_key(coin, public_key.as_ref().clone(), derivation, None) + .map(|any_address| TWAnyAddress(any_address).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} + +/// Deletes an address. +/// +/// \param address address to delete. +#[no_mangle] +pub unsafe extern "C" fn tw_any_address_delete(address: *mut TWAnyAddress) { + // Take the ownership back to rust and drop the owner. + let _ = TWAnyAddress::from_ptr(address); +} + +/// Returns the address string representation. +/// +/// \param address address to get the string representation of. +#[no_mangle] +pub unsafe extern "C" fn tw_any_address_description(address: *const TWAnyAddress) -> *mut TWString { + // Take the ownership back to rust and drop the owner. + let address = try_or_else!(TWAnyAddress::from_ptr_as_ref(address), std::ptr::null_mut); + + let description = address.0.description().to_string(); + TWString::from(description).into_ptr() +} + +/// Returns underlying data (public key or key hash) +/// +/// \param address address to get the data of. +#[no_mangle] +pub unsafe extern "C" fn tw_any_address_data(address: *const TWAnyAddress) -> *mut TWData { + // Take the ownership back to rust and drop the owner. + let address = try_or_else!(TWAnyAddress::from_ptr_as_ref(address), std::ptr::null_mut); + + let data = try_or_else!(address.0.get_data(), std::ptr::null_mut); + TWData::from(data).into_ptr() +} diff --git a/rust/tw_any_coin/src/ffi/tw_any_signer.rs b/rust/tw_any_coin/src/ffi/tw_any_signer.rs new file mode 100644 index 00000000000..76e3965d5e9 --- /dev/null +++ b/rust/tw_any_coin/src/ffi/tw_any_signer.rs @@ -0,0 +1,40 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#![allow(clippy::missing_safety_doc)] + +use crate::any_signer::AnySigner; +use tw_memory::ffi::tw_data::TWData; +use tw_memory::ffi::RawPtrTrait; +use tw_misc::try_or_else; + +/// Signs a transaction specified by the signing input and coin type. +/// +/// \param input The serialized data of a signing input (e.g. TW.Bitcoin.Proto.SigningInput). +/// \param coin The given coin type to sign the transaction for. +/// \return The serialized data of a `SigningOutput` proto object. (e.g. TW.Bitcoin.Proto.SigningOutput). +#[no_mangle] +pub unsafe extern "C" fn tw_any_signer_sign(input: *const TWData, coin: u32) -> *mut TWData { + let input = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); + + AnySigner::sign(input.as_slice(), coin) + .map(|output| TWData::from(output).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} + +/// Plans a transaction (for UTXO chains only). +/// +/// \param input The serialized data of a signing input +/// \param coin The given coin type to plan the transaction for. +/// \return The serialized data of a `TransactionPlan` proto object. +#[no_mangle] +pub unsafe extern "C" fn tw_any_signer_plan(input: *const TWData, coin: u32) -> *mut TWData { + let input = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); + + AnySigner::plan(input.as_slice(), coin) + .map(|output| TWData::from(output).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} diff --git a/rust/tw_any_coin/src/ffi/tw_message_signer.rs b/rust/tw_any_coin/src/ffi/tw_message_signer.rs new file mode 100644 index 00000000000..32144ecc204 --- /dev/null +++ b/rust/tw_any_coin/src/ffi/tw_message_signer.rs @@ -0,0 +1,53 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#![allow(clippy::missing_safety_doc)] + +use crate::message_signer::MessageSigner; +use tw_memory::ffi::tw_data::TWData; +use tw_memory::ffi::RawPtrTrait; +use tw_misc::{try_or_else, try_or_false}; + +/// Signs a message for the given blockchain. +/// +/// \param input The serialized data of a signing input (e.g. TW.Ethereum.Proto.MessageSigningInput). +/// \param coin The given coin type to sign the transaction for. +/// \return The serialized data of a `SigningOutput` proto object. (e.g. TW.Ethereum.Proto.MessageSigningOutput). +#[no_mangle] +pub unsafe extern "C" fn tw_message_signer_sign(input: *const TWData, coin: u32) -> *mut TWData { + let input = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); + + MessageSigner::sign_message(input.as_slice(), coin) + .map(|output| TWData::from(output).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} + +/// Verifies a signature for a message. +/// +/// \param input The serialized data of a signing input (e.g. TW.Ethereum.Proto.MessageSigningInput). +/// \param coin The given coin type to sign the transaction for. +/// \return The serialized data of a `SigningOutput` proto object. (e.g. TW.Ethereum.Proto.MessageSigningOutput). +#[no_mangle] +pub unsafe extern "C" fn tw_message_signer_verify(input: *const TWData, coin: u32) -> bool { + let input = try_or_false!(TWData::from_ptr_as_ref(input)); + MessageSigner::verify_message(input.as_slice(), coin).unwrap_or_default() +} + +/// Computes preimage hashes of a message. +/// +/// \param input The serialized data of a signing input (e.g. TW.Ethereum.Proto.MessageSigningInput). +/// \param coin The given coin type to sign the transaction for. +/// \return The serialized data of TW.TxCompiler.PreSigningOutput. +#[no_mangle] +pub unsafe extern "C" fn tw_message_signer_pre_image_hashes( + input: *const TWData, + coin: u32, +) -> *mut TWData { + let input = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); + MessageSigner::message_preimage_hashes(input.as_slice(), coin) + .map(|output| TWData::from(output).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} diff --git a/rust/tw_any_coin/src/ffi/tw_transaction_compiler.rs b/rust/tw_any_coin/src/ffi/tw_transaction_compiler.rs new file mode 100644 index 00000000000..a6f76e19681 --- /dev/null +++ b/rust/tw_any_coin/src/ffi/tw_transaction_compiler.rs @@ -0,0 +1,69 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#![allow(clippy::missing_safety_doc)] + +use crate::transaction_compiler::TransactionCompiler; +use tw_memory::ffi::tw_data::TWData; +use tw_memory::ffi::tw_data_vector::TWDataVector; +use tw_memory::ffi::RawPtrTrait; +use tw_misc::try_or_else; + +/// Obtains pre-signing hashes of a transaction. +/// +/// We provide a default `PreSigningOutput` in TransactionCompiler.proto. +/// For some special coins, such as bitcoin, we will create a custom `PreSigningOutput` object in its proto file. +/// \param coin coin type. +/// \param input The serialized data of a signing input +/// \return serialized data of a proto object `PreSigningOutput` includes hash. +#[no_mangle] +pub unsafe extern "C" fn tw_transaction_compiler_pre_image_hashes( + coin: u32, + input: *const TWData, +) -> *mut TWData { + let input = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); + + TransactionCompiler::preimage_hashes(coin, input.as_slice()) + .map(|output| TWData::from(output).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} + +/// Compiles a complete transaction with one or more external signatures. +/// +/// Puts together from transaction input and provided public keys and signatures. The signatures must match the hashes +/// returned by `TWTransactionCompilerPreImageHashes`, in the same order. The publicKeyHash attached +/// to the hashes enable identifying the private key needed for signing the hash. +/// \param coin coin type. +/// \param input The serialized data of a signing input. +/// \param signatures signatures to compile, using `TWDataVector`. +/// \param public_keys public keys for signers to match private keys, using `TWDataVector`. +/// \return serialized data of a proto object `SigningOutput`. +#[no_mangle] +pub unsafe extern "C" fn tw_transaction_compiler_compile( + coin: u32, + input: *const TWData, + signatures: *const TWDataVector, + public_keys: *const TWDataVector, +) -> *mut TWData { + let input = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); + let signatures_ref = try_or_else!( + TWDataVector::from_ptr_as_ref(signatures), + std::ptr::null_mut + ); + let public_keys_ref = try_or_else!( + TWDataVector::from_ptr_as_ref(public_keys), + std::ptr::null_mut + ); + + TransactionCompiler::compile( + coin, + input.as_slice(), + signatures_ref.to_data_vec(), + public_keys_ref.to_data_vec(), + ) + .map(|output| TWData::from(output).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} diff --git a/rust/tw_any_coin/src/lib.rs b/rust/tw_any_coin/src/lib.rs new file mode 100644 index 00000000000..ebe47631a8e --- /dev/null +++ b/rust/tw_any_coin/src/lib.rs @@ -0,0 +1,14 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod any_address; +pub mod any_signer; +pub mod ffi; +pub mod message_signer; +pub mod transaction_compiler; + +#[cfg(feature = "test-utils")] +pub mod test_utils; diff --git a/rust/tw_any_coin/src/message_signer.rs b/rust/tw_any_coin/src/message_signer.rs new file mode 100644 index 00000000000..0266df3eed0 --- /dev/null +++ b/rust/tw_any_coin/src/message_signer.rs @@ -0,0 +1,36 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::error::SigningResult; +use tw_coin_registry::coin_type::CoinType; +use tw_coin_registry::dispatcher::coin_dispatcher; +use tw_memory::Data; + +/// Represents a message signer to sign regular or typed structured data for any blockchain. +pub struct MessageSigner; + +impl MessageSigner { + /// Signs a message. + #[inline] + pub fn sign_message(input: &[u8], coin: CoinType) -> SigningResult { + let (ctx, entry) = coin_dispatcher(coin)?; + entry.sign_message(&ctx, input) + } + + /// Computes preimage hashes of a message. + #[inline] + pub fn message_preimage_hashes(input: &[u8], coin: CoinType) -> SigningResult { + let (ctx, entry) = coin_dispatcher(coin)?; + entry.message_preimage_hashes(&ctx, input) + } + + /// Verifies a signature for a message. + #[inline] + pub fn verify_message(input: &[u8], coin: CoinType) -> SigningResult { + let (ctx, entry) = coin_dispatcher(coin)?; + entry.verify_message(&ctx, input) + } +} diff --git a/rust/tw_any_coin/src/test_utils/mod.rs b/rust/tw_any_coin/src/test_utils/mod.rs new file mode 100644 index 00000000000..671dbb1a436 --- /dev/null +++ b/rust/tw_any_coin/src/test_utils/mod.rs @@ -0,0 +1,16 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ffi::tw_any_address::{tw_any_address_delete, TWAnyAddress}; +use tw_memory::test_utils::tw_wrapper::{TWWrapper, WithDestructor}; + +pub type TWAnyAddressHelper = TWWrapper; + +impl WithDestructor for TWAnyAddress { + fn destructor() -> unsafe extern "C" fn(*mut Self) { + tw_any_address_delete + } +} diff --git a/rust/tw_any_coin/src/transaction_compiler.rs b/rust/tw_any_coin/src/transaction_compiler.rs new file mode 100644 index 00000000000..85e5013bdb9 --- /dev/null +++ b/rust/tw_any_coin/src/transaction_compiler.rs @@ -0,0 +1,39 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::error::{SigningError, SigningResult}; +use tw_coin_registry::coin_type::CoinType; +use tw_coin_registry::dispatcher::coin_dispatcher; +use tw_memory::Data; + +/// Non-core transaction utility methods, like building a transaction using an external signature. +pub struct TransactionCompiler; + +impl TransactionCompiler { + /// Obtains pre-signing hashes of a transaction. + #[inline] + pub fn preimage_hashes(coin: CoinType, input: &[u8]) -> SigningResult { + let (ctx, entry) = coin_dispatcher(coin)?; + entry + .preimage_hashes(&ctx, input) + .map_err(SigningError::from) + } + + /// Compiles a complete transaction with one or more external signatures. + #[inline] + pub fn compile( + coin: CoinType, + input: &[u8], + signatures: Vec, + public_keys: Vec, + ) -> SigningResult { + let (ctx, entry) = coin_dispatcher(coin)?; + entry + .compile(&ctx, input, signatures, public_keys) + .map_err(SigningError::from) + } +} diff --git a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs new file mode 100644 index 00000000000..829e2b97af0 --- /dev/null +++ b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs @@ -0,0 +1,152 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_any_coin::ffi::tw_any_address::{ + tw_any_address_create_with_public_key_derivation, tw_any_address_create_with_string, + tw_any_address_data, tw_any_address_description, tw_any_address_is_valid, +}; +use tw_any_coin::test_utils::TWAnyAddressHelper; +use tw_coin_entry::derivation::Derivation; +use tw_coin_registry::blockchain_type::BlockchainType; +use tw_coin_registry::registry::supported_coin_items; +use tw_encoding::hex::DecodeHex; +use tw_keypair::ffi::privkey::tw_private_key_get_public_key_by_type; +use tw_keypair::test_utils::tw_private_key_helper::TWPrivateKeyHelper; +use tw_keypair::test_utils::tw_public_key_helper::TWPublicKeyHelper; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_memory::test_utils::tw_string_helper::TWStringHelper; + +const ETHEREUM_COIN_TYPE: u32 = 60; + +#[test] +fn test_any_address_derive() { + let private_key = TWPrivateKeyHelper::with_hex( + "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", + ); + + for coin in supported_coin_items() { + let public_key = TWPublicKeyHelper::wrap(unsafe { + tw_private_key_get_public_key_by_type(private_key.ptr(), coin.public_key_type as u32) + }); + + // TODO match `CoinType` when it's generated. + let expected_address = match coin.blockchain { + // By default, Bitcoin will return a P2PKH address. + BlockchainType::Bitcoin => "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", + BlockchainType::Ethereum => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", + BlockchainType::Ronin => "ronin:Ac1ec44E4f0ca7D172B7803f6836De87Fb72b309", + BlockchainType::Unsupported => unreachable!(), + }; + + let any_address = TWAnyAddressHelper::wrap(unsafe { + tw_any_address_create_with_public_key_derivation( + public_key.ptr(), + coin.coin_id, + Derivation::Default as u32, + ) + }); + + let description = + TWStringHelper::wrap(unsafe { tw_any_address_description(any_address.ptr()) }); + assert_eq!(description.to_string(), Some(expected_address.to_string())); + } +} + +#[test] +fn test_any_address_normalize_eth() { + for coin in supported_coin_items() { + let (denormalized, expected_normalized) = match coin.blockchain { + BlockchainType::Bitcoin => ( + "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", + "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", + ), + BlockchainType::Ethereum => ( + "0xb16db98b365b1f89191996942612b14f1da4bd5f", + "0xb16Db98B365B1f89191996942612B14F1Da4Bd5f", + ), + BlockchainType::Ronin => ( + "0xb16db98b365b1f89191996942612b14f1da4bd5f", + "ronin:b16Db98B365B1f89191996942612B14F1Da4Bd5f", + ), + BlockchainType::Unsupported => unreachable!(), + }; + + let denormalized = TWStringHelper::create(denormalized); + + let any_address = TWAnyAddressHelper::wrap(unsafe { + tw_any_address_create_with_string(denormalized.ptr(), coin.coin_id) + }); + + let normalized = + TWStringHelper::wrap(unsafe { tw_any_address_description(any_address.ptr()) }); + + assert_eq!( + normalized.to_string(), + Some(expected_normalized.to_string()) + ); + } +} + +#[test] +fn test_any_address_is_valid_coin() { + for coin in supported_coin_items() { + let valid = match coin.blockchain { + BlockchainType::Bitcoin => vec![ + "1MrZNGN7mfWZiZNQttrzHjfw72jnJC2JNx", + "bc1qunq74p3h8425hr6wllevlvqqr6sezfxj262rff", + "bc1pwse34zfpvt344rvlt7tw0ngjtfh9xasc4q03avf0lk74jzjpzjuqaz7ks5", + ], + BlockchainType::Ethereum => vec![ + "0xb16db98b365b1f89191996942612b14f1da4bd5f", + "0xb16Db98B365B1f89191996942612B14F1Da4Bd5f", + ], + BlockchainType::Ronin => vec![ + "0xb16db98b365b1f89191996942612b14f1da4bd5f", + "0xb16Db98B365B1f89191996942612B14F1Da4Bd5f", + "ronin:b16db98b365b1f89191996942612b14f1da4bd5f", + "ronin:b16Db98B365B1f89191996942612B14F1Da4Bd5f", + ], + _ => unreachable!(), + }; + + for valid_addr in valid { + let valid = TWStringHelper::create(valid_addr); + assert!(unsafe { tw_any_address_is_valid(valid.ptr(), coin.coin_id) }); + } + } +} + +#[test] +fn test_any_address_is_valid_coin_invalid() { + for coin in supported_coin_items() { + let invalid = match coin.blockchain { + BlockchainType::Bitcoin => { + vec!["0xb16db98b365b1f89191996942612b14f1da4bd5f"] + }, + BlockchainType::Ethereum | BlockchainType::Ronin => { + vec!["b16Db98B365B1f89191996942612B14F1Da4Bd5f"] + }, + BlockchainType::Unsupported => unreachable!(), + }; + + for invalid_addr in invalid { + let valid = TWStringHelper::create(invalid_addr); + assert!(!unsafe { tw_any_address_is_valid(valid.ptr(), coin.coin_id) }); + } + } +} + +#[test] +fn test_any_address_get_data_eth() { + let addr = "0xb16Db98B365B1f89191996942612B14F1Da4Bd5f"; + + let address_str = TWStringHelper::create(addr); + let any_address = TWAnyAddressHelper::wrap(unsafe { + tw_any_address_create_with_string(address_str.ptr(), ETHEREUM_COIN_TYPE) + }); + let data = TWDataHelper::wrap(unsafe { tw_any_address_data(any_address.ptr()) }); + assert_eq!(data.to_vec(), Some(addr.decode_hex().unwrap())); +} diff --git a/rust/tw_any_coin/tests/tw_any_signer_ffi_tests.rs b/rust/tw_any_coin/tests/tw_any_signer_ffi_tests.rs new file mode 100644 index 00000000000..8af80dbc39f --- /dev/null +++ b/rust/tw_any_coin/tests/tw_any_signer_ffi_tests.rs @@ -0,0 +1,66 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::borrow::Cow; +use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; +use tw_coin_entry::error::SigningErrorType; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_number::U256; +use tw_proto::{deserialize, serialize}; + +const ETHEREUM_COIN_TYPE: u32 = 60; + +#[test] +fn test_any_signer_sign_eth() { + use tw_proto::Ethereum::Proto; + + let private = "0x4646464646464646464646464646464646464646464646464646464646464646" + .decode_hex() + .unwrap(); + + let transfer = Proto::mod_Transaction::Transfer { + amount: U256::encode_be_compact(1_000_000_000_000_000_000), + data: Cow::default(), + }; + + let input = Proto::SigningInput { + chain_id: U256::encode_be_compact(1), + nonce: U256::encode_be_compact(9), + gas_price: U256::encode_be_compact(20_000_000_000), + gas_limit: U256::encode_be_compact(21_000), + to_address: "0x3535353535353535353535353535353535353535".into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::transfer(transfer), + }), + private_key: private.into(), + ..Proto::SigningInput::default() + }; + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output = + TWDataHelper::wrap(unsafe { tw_any_signer_sign(input_data.ptr(), ETHEREUM_COIN_TYPE) }) + .to_vec() + .expect("!tw_any_signer_sign returned nullptr"); + + let output: Proto::SigningOutput = deserialize(&output).unwrap(); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected = "f86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83"; + assert_eq!(output.encoded.to_hex(), expected); +} + +#[test] +fn test_any_signer_sign_unknown_coin() { + let unsupported_coin = u32::MAX; + + let input_data = TWDataHelper::create(vec![]); + let output = + TWDataHelper::wrap(unsafe { tw_any_signer_sign(input_data.ptr(), unsupported_coin) }); + assert!(output.is_null()); +} diff --git a/rust/tw_any_coin/tests/tw_message_signer_ffi_tests.rs b/rust/tw_any_coin/tests/tw_message_signer_ffi_tests.rs new file mode 100644 index 00000000000..0a58ddfbec4 --- /dev/null +++ b/rust/tw_any_coin/tests/tw_message_signer_ffi_tests.rs @@ -0,0 +1,93 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_any_coin::ffi::tw_message_signer::{ + tw_message_signer_pre_image_hashes, tw_message_signer_sign, tw_message_signer_verify, +}; +use tw_coin_entry::error::SigningErrorType; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_proto::{deserialize, serialize, Ethereum, TxCompiler}; + +const ETHEREUM_COIN_TYPE: u32 = 60; + +#[test] +fn test_tw_message_signer_sign() { + let input = Ethereum::Proto::MessageSigningInput { + private_key: "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d" + .decode_hex() + .unwrap() + .into(), + message: "Foo".into(), + chain_id: None, + message_type: Ethereum::Proto::MessageType::MessageType_legacy, + }; + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + let output = + TWDataHelper::wrap(unsafe { tw_message_signer_sign(input_data.ptr(), ETHEREUM_COIN_TYPE) }) + .to_vec() + .expect("!tw_message_signer_sign returned nullptr"); + + let output: Ethereum::Proto::MessageSigningOutput = deserialize(&output).unwrap(); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + assert_eq!(output.signature, "21a779d499957e7fd39392d49a079679009e60e492d9654a148829be43d2490736ec72bc4a5644047d979c3cf4ebe2c1c514044cf436b063cb89fc6676be71101b"); +} + +#[test] +fn test_tw_message_signer_verify() { + let input = Ethereum::Proto::MessageVerifyingInput { + message: "Foo".into(), + public_key: "0349d0134ef2c798c02879379a1760baa49c4e25e2324cd128f11e559f073bcc6f".decode_hex().unwrap().into(), + signature: "21a779d499957e7fd39392d49a079679009e60e492d9654a148829be43d2490736ec72bc4a5644047d979c3cf4ebe2c1c514044cf436b063cb89fc6676be71101b".into(), + }; + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + let verified = unsafe { tw_message_signer_verify(input_data.ptr(), ETHEREUM_COIN_TYPE) }; + assert!(verified); +} + +#[test] +fn test_tw_message_signer_verify_invalid() { + let input = Ethereum::Proto::MessageVerifyingInput { + message: "Foo".into(), + public_key: "0349d0134ef2c798c02879379a1760baa49c4e25e2324cd128f11e559f073bcc6f".decode_hex().unwrap().into(), + signature: "21a779d499957e7fd39392d49a079679009e60e492d9654a148829be43d2490736ec72bc4a5644047d979c3cf4ebe2c1c514044cf436b063cb89fc6676be71101c".into(), + }; + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + let verified = unsafe { tw_message_signer_verify(input_data.ptr(), ETHEREUM_COIN_TYPE) }; + assert!(!verified); +} + +#[test] +fn test_tw_message_signer_pre_image_hashes() { + let input = Ethereum::Proto::MessageSigningInput { + private_key: "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d" + .decode_hex() + .unwrap() + .into(), + message: "Foo".into(), + chain_id: None, + message_type: Ethereum::Proto::MessageType::MessageType_legacy, + }; + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + let output = TWDataHelper::wrap(unsafe { + tw_message_signer_pre_image_hashes(input_data.ptr(), ETHEREUM_COIN_TYPE) + }) + .to_vec() + .expect("!tw_message_signer_sign returned nullptr"); + + let output: TxCompiler::Proto::PreSigningOutput = deserialize(&output).unwrap(); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + assert_eq!( + output.data_hash.to_hex(), + "0af844076e792f9685560b2e597967da7403b00a5339b5801ea251ddde375f8a" + ); +} diff --git a/rust/tw_any_coin/tests/tw_transaction_compiler_ffi_tests.rs b/rust/tw_any_coin/tests/tw_transaction_compiler_ffi_tests.rs new file mode 100644 index 00000000000..ea875557d4c --- /dev/null +++ b/rust/tw_any_coin/tests/tw_transaction_compiler_ffi_tests.rs @@ -0,0 +1,110 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::borrow::Cow; +use tw_any_coin::ffi::tw_any_signer::tw_any_signer_plan; +use tw_any_coin::ffi::tw_transaction_compiler::{ + tw_transaction_compiler_compile, tw_transaction_compiler_pre_image_hashes, +}; +use tw_coin_entry::error::SigningErrorType; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_memory::test_utils::tw_data_vector_helper::TWDataVectorHelper; +use tw_number::U256; +use tw_proto::Ethereum::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; +use tw_proto::{deserialize, serialize}; + +const ETHEREUM_COIN_TYPE: u32 = 60; + +#[test] +fn test_transaction_compiler_eth() { + let transfer = Proto::mod_Transaction::Transfer { + amount: U256::encode_be_compact(1_000_000_000_000_000_000), + data: Cow::default(), + }; + let input = Proto::SigningInput { + nonce: U256::encode_be_compact(11), + chain_id: U256::encode_be_compact(1), + gas_price: U256::encode_be_compact(20_000_000_000), + gas_limit: U256::encode_be_compact(21_000), + to_address: "0x3535353535353535353535353535353535353535".into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::transfer(transfer), + }), + ..Proto::SigningInput::default() + }; + + // Step 2: Obtain preimage hash + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + let preimage_data = TWDataHelper::wrap(unsafe { + tw_transaction_compiler_pre_image_hashes(ETHEREUM_COIN_TYPE, input_data.ptr()) + }) + .to_vec() + .expect("!tw_transaction_compiler_pre_image_hashes returned nullptr"); + + let preimage: CompilerProto::PreSigningOutput = + deserialize(&preimage_data).expect("Coin entry returned an invalid output"); + + assert_eq!(preimage.error, SigningErrorType::OK); + assert!(preimage.error_message.is_empty()); + assert_eq!( + preimage.data_hash.to_hex(), + "15e180a6274b2f6a572b9b51823fce25ef39576d10188ecdcd7de44526c47217" + ); + + // Step 3: Compile transaction info + + // Simulate signature, normally obtained from signature server + let signature = "360a84fb41ad07f07c845fedc34cde728421803ebbaae392fc39c116b29fc07b53bd9d1376e15a191d844db458893b928f3efbfee90c9febf51ab84c9796677900".decode_hex().unwrap(); + let public_key = "044bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382ce28cab79ad7119ee1ad3ebcdb98a16805211530ecc6cfefa1b88e6dff99232a".decode_hex().unwrap(); + + let signatures = TWDataVectorHelper::create([signature]); + let public_keys = TWDataVectorHelper::create([public_key]); + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + let output_data = TWDataHelper::wrap(unsafe { + tw_transaction_compiler_compile( + ETHEREUM_COIN_TYPE, + input_data.ptr(), + signatures.ptr(), + public_keys.ptr(), + ) + }) + .to_vec() + .expect("!tw_transaction_compiler_compile returned nullptr"); + + let output: Proto::SigningOutput = + deserialize(&output_data).expect("Coin entry returned an invalid output"); + + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + let expected_encoded = "f86c0b8504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a0360a84fb41ad07f07c845fedc34cde728421803ebbaae392fc39c116b29fc07ba053bd9d1376e15a191d844db458893b928f3efbfee90c9febf51ab84c97966779"; + assert_eq!(output.encoded.to_hex(), expected_encoded); +} + +#[test] +fn test_transaction_compiler_plan_not_supported() { + let transfer = Proto::mod_Transaction::Transfer { + amount: U256::encode_be_compact(1), + data: Cow::default(), + }; + let input = Proto::SigningInput { + to_address: "0x3535353535353535353535353535353535353535".into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::transfer(transfer), + }), + ..Proto::SigningInput::default() + }; + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + let plan = + TWDataHelper::wrap(unsafe { tw_any_signer_plan(input_data.ptr(), ETHEREUM_COIN_TYPE) }); + assert!( + plan.is_null(), + "Transaction plan is expected to be not supported by the {} coin", + ETHEREUM_COIN_TYPE + ); +} diff --git a/rust/tw_bitcoin/Cargo.toml b/rust/tw_bitcoin/Cargo.toml index 82ef70900ab..24771b92ac7 100644 --- a/rust/tw_bitcoin/Cargo.toml +++ b/rust/tw_bitcoin/Cargo.toml @@ -10,7 +10,13 @@ bitcoin = "0.30.0" secp256k1 = { version = "0.27.0", features = [ "global-context", "rand-std" ] } serde = { version = "1.0.163", features = [ "derive" ] } serde_json = "1.0.96" +tw_coin_entry = { path = "../tw_coin_entry", features = ["test-utils"] } +tw_utxo = { path = "../tw_utxo" } tw_encoding = { path = "../tw_encoding" } tw_memory = { path = "../tw_memory" } tw_misc = { path = "../tw_misc" } tw_proto = { path = "../tw_proto" } +tw_keypair = { path = "../tw_keypair" } + +[dev-dependencies] +wallet-core-rs = { path = "../wallet_core_rs" } diff --git a/rust/tw_bitcoin/src/brc20.rs b/rust/tw_bitcoin/src/brc20.rs deleted file mode 100644 index e2070059c2b..00000000000 --- a/rust/tw_bitcoin/src/brc20.rs +++ /dev/null @@ -1,211 +0,0 @@ -use crate::ordinals::OrdinalsInscription; -use crate::{Error, Recipient, Result}; -use bitcoin::PublicKey; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct BRC20Payload { - #[serde(rename = "p")] - protocol: String, - #[serde(rename = "op")] - operation: String, - #[serde(flatten)] - inner: T, -} - -impl BRC20Payload { - const PROTOCOL_ID: &str = "brc-20"; - const MIME: &[u8] = b"text/plain;charset=utf-8"; -} - -// Convenience aliases. -pub type BRC20DeployPayload = BRC20Payload; -pub type BRC20MintPayload = BRC20Payload; -pub type BRC20TransferPayload = BRC20Payload; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Ticker(String); - -impl Ticker { - pub fn new(string: String) -> Result { - // Ticker must be a 4-letter identifier. - if string.len() != 4 { - return Err(Error::Todo); - } - - Ok(Ticker(string)) - } - pub fn to_byte_array(&self) -> [u8; 4] { - self.0 - .as_bytes() - .try_into() - .expect("ticker must be four bytes") - } -} - -impl TryFrom for Ticker { - type Error = Error; - - fn try_from(string: String) -> Result { - Self::new(string) - } -} - -impl BRC20DeployPayload { - const OPERATION: &str = "deploy"; - - pub fn new(ticker: Ticker, max: usize, limit: Option, decimals: Option) -> Self { - BRC20Payload { - protocol: Self::PROTOCOL_ID.to_string(), - operation: Self::OPERATION.to_string(), - inner: DeployPayload { - tick: ticker, - max: max.to_string(), - lim: limit.map(|l| l.to_string()), - dec: decimals.map(|d| d.to_string()), - }, - } - } -} - -impl BRC20TransferPayload { - const OPERATION: &str = "transfer"; - - pub fn new(ticker: Ticker, amount: u64) -> Self { - BRC20Payload { - protocol: Self::PROTOCOL_ID.to_string(), - operation: Self::OPERATION.to_string(), - inner: TransferPayload { - tick: ticker, - amt: amount.to_string(), - }, - } - } -} - -impl BRC20MintPayload { - const OPERATION: &str = "mint"; - - pub fn new(ticker: Ticker, amount: u64) -> Self { - BRC20Payload { - protocol: Self::PROTOCOL_ID.to_string(), - operation: Self::OPERATION.to_string(), - inner: MintPayload { - tick: ticker, - amt: amount.to_string(), - }, - } - } -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct DeployPayload { - pub tick: Ticker, - pub max: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub lim: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub dec: Option, -} - -#[derive(Debug, Clone)] -pub struct BRC20DeployInscription(OrdinalsInscription); - -impl BRC20DeployInscription { - pub fn new( - recipient: Recipient, - ticker: Ticker, - max: usize, - limit: Option, - decimals: Option, - ) -> Result { - let data = BRC20DeployPayload::new(ticker, max, limit, decimals); - - Self::from_payload(data, recipient) - } - pub fn from_payload( - data: BRC20DeployPayload, - recipient: Recipient, - ) -> Result { - let inscription = OrdinalsInscription::new( - BRC20Payload::::MIME, - &serde_json::to_vec(&data).unwrap(), - recipient, - )?; - - Ok(BRC20DeployInscription(inscription)) - } - pub fn inscription(&self) -> &OrdinalsInscription { - &self.0 - } -} - -#[derive(Serialize, Deserialize)] -pub struct TransferPayload { - pub tick: Ticker, - pub amt: String, -} - -pub struct BRC20TransferInscription(OrdinalsInscription); - -impl BRC20TransferInscription { - pub fn new( - recipient: Recipient, - ticker: Ticker, - amount: u64, - ) -> Result { - let data = BRC20TransferPayload::new(ticker, amount); - Self::from_payload(data, recipient) - } - pub fn from_payload( - data: BRC20TransferPayload, - recipient: Recipient, - ) -> Result { - let inscription = OrdinalsInscription::new( - BRC20Payload::::MIME, - &serde_json::to_vec(&data).unwrap(), - recipient, - )?; - - Ok(BRC20TransferInscription(inscription)) - } - pub fn inscription(&self) -> &OrdinalsInscription { - &self.0 - } -} - -/// The structure is the same as `TransferPayload`, but we'll keep it separate -/// for clarity. -#[derive(Serialize, Deserialize)] -pub struct MintPayload { - pub tick: Ticker, - pub amt: String, -} - -pub struct BRC20MintInscription(OrdinalsInscription); - -impl BRC20MintInscription { - pub fn new( - recipient: Recipient, - ticker: Ticker, - amount: u64, - ) -> Result { - let data = BRC20MintPayload::new(ticker, amount); - Self::from_payload(data, recipient) - } - pub fn from_payload( - data: BRC20MintPayload, - recipient: Recipient, - ) -> Result { - let inscription = OrdinalsInscription::new( - BRC20Payload::::MIME, - &serde_json::to_vec(&data).unwrap(), - recipient, - )?; - - Ok(BRC20MintInscription(inscription)) - } - pub fn inscription(&self) -> &OrdinalsInscription { - &self.0 - } -} diff --git a/rust/tw_bitcoin/src/claim.rs b/rust/tw_bitcoin/src/claim.rs deleted file mode 100644 index 02edc331ec5..00000000000 --- a/rust/tw_bitcoin/src/claim.rs +++ /dev/null @@ -1,194 +0,0 @@ -use crate::{ - Error, Recipient, Result, TaprootScript, TxInputP2PKH, TxInputP2TRKeyPath, - TxInputP2TRScriptPath, TxInputP2WPKH, -}; -use bitcoin::key::{KeyPair, PublicKey, TapTweak, TweakedKeyPair, TweakedPublicKey}; -use bitcoin::secp256k1::Secp256k1; -use bitcoin::sighash::{EcdsaSighashType, TapSighashType}; -use bitcoin::taproot::{LeafVersion, Signature}; -use bitcoin::{ScriptBuf, Witness}; - -#[derive(Debug, Clone)] -pub enum ClaimLocation { - Script(ScriptBuf), - Witness(Witness), -} - -pub trait TransactionSigner { - /// Claiming mechanism for (legacy) P2PKH outputs. - fn claim_p2pkh( - &self, - input: &TxInputP2PKH, - sighash: secp256k1::Message, - sighash_type: EcdsaSighashType, - ) -> Result; - /// Claiming mechanism for SegWit P2WPKH outputs. - fn claim_p2wpkh( - &self, - input: &TxInputP2WPKH, - sighash: secp256k1::Message, - sighash_type: EcdsaSighashType, - ) -> Result; - /// Claiming mechanism for Taproot P2TR key-path outputs. - fn claim_p2tr_key_path( - &self, - input: &TxInputP2TRKeyPath, - sighash: secp256k1::Message, - sighash_type: TapSighashType, - ) -> Result; - /// Claiming mechanism for Taproot P2TR script-path outputs. - fn claim_p2tr_script_path( - &self, - input: &TxInputP2TRScriptPath, - sighash: secp256k1::Message, - sighash_type: TapSighashType, - ) -> Result; -} - -// Contains the `scriptBuf` that must be included in the transaction when -// spending the P2PKH input. -pub struct ClaimP2PKH(pub ScriptBuf); - -// Contains the Witness that must be included in the transaction when spending -// the SegWit P2WPKH input. -pub struct ClaimP2WPKH(pub Witness); - -// Contains the Witness that must be included in the transaction when spending -// the Taproot P2TR key-path input. -pub struct ClaimP2TRKeyPath(pub Witness); - -// Contains the Witness that must be included in the transaction when spending -// the Taproot P2TR script-path input. -pub struct ClaimP2TRScriptPath(pub Witness); - -impl TransactionSigner for KeyPair { - fn claim_p2pkh( - &self, - input: &TxInputP2PKH, - sighash: secp256k1::Message, - sighash_type: EcdsaSighashType, - ) -> Result { - let me = Recipient::::from_keypair(self); - - // Check whether we can actually claim the input. - if input.recipient().pubkey_hash() != &me.pubkey_hash() { - return Err(Error::Todo); - } - - // Construct the ECDSA signature. - let sig = bitcoin::ecdsa::Signature { - sig: self.secret_key().sign_ecdsa(sighash), - hash_ty: sighash_type, - }; - - // Construct the Script for claiming. - let script = ScriptBuf::builder() - .push_slice(sig.serialize()) - .push_key(&me.public_key()) - .into_script(); - - Ok(ClaimP2PKH(script)) - } - fn claim_p2wpkh( - &self, - input: &TxInputP2WPKH, - sighash: secp256k1::Message, - sighash_type: EcdsaSighashType, - ) -> Result { - let me = Recipient::::from_keypair(self); - - if input.recipient().wpubkey_hash() != &me.wpubkey_hash()? { - return Err(Error::Todo); - } - - // Construct the ECDSA signature. - let sig = bitcoin::ecdsa::Signature { - sig: self.secret_key().sign_ecdsa(sighash), - hash_ty: sighash_type, - }; - - // Construct the Witness for claiming. - let mut witness = Witness::new(); - witness.push(sig.serialize()); - // Serialize public key. - witness.push(me.public_key().to_bytes()); - - Ok(ClaimP2WPKH(witness)) - } - fn claim_p2tr_key_path( - &self, - input: &TxInputP2TRKeyPath, - sighash: secp256k1::Message, - sighash_type: TapSighashType, - ) -> Result { - let me = Recipient::::from(self); - - // Check whether we can actually claim the input. - if input.recipient() != &me { - return Err(Error::Todo); - } - - let secp = Secp256k1::new(); - - // Tweak keypair for P2TR key-path (ie. zeroed Merkle root). - let tapped: TweakedKeyPair = self.tap_tweak(&secp, None); - let tweaked = KeyPair::from(tapped); - - // Construct the Schnorr signature. - #[cfg(not(test))] - let schnorr = secp.sign_schnorr(&sighash, &tweaked); - #[cfg(test)] - // For tests, we disable the included randomness in order to create - // reproducible signatures. Randomness should ALWAYS be used in - // production. - let schnorr = secp.sign_schnorr_no_aux_rand(&sighash, &tweaked); - - let sig = bitcoin::taproot::Signature { - sig: schnorr, - hash_ty: sighash_type, - }; - - // Construct the witness for claiming. - let mut witness = Witness::new(); - witness.push(sig.to_vec()); - - Ok(ClaimP2TRKeyPath(witness)) - } - fn claim_p2tr_script_path( - &self, - input: &TxInputP2TRScriptPath, - sighash: secp256k1::Message, - sighash_type: TapSighashType, - ) -> Result { - // Tweak our public key with the Merkle root of the Script to be claimed. - let me = Recipient::::from_keypair(self, input.recipient().merkle_root()); - - // Check whether we can actually claim the input. - if input.recipient() != &me { - return Err(Error::Todo); - } - - // The control block contains information on which script of the - // script-path is being executed. - let control_block = input - .spend_info() - .control_block(&(input.witness().clone(), LeafVersion::TapScript)) - .ok_or(Error::Todo)?; - - // Construct the Schnorr signature. We leave the keypair untweaked, - // unlike for key-path. - let sig = Signature { - sig: Secp256k1::new().sign_schnorr(&sighash, self), - hash_ty: sighash_type, - }; - - // Construct the Witness for claiming. - let mut witness = Witness::new(); - // Serialize signature. - witness.push(&sig.to_vec()); - witness.push(input.witness()); - witness.push(control_block.serialize()); - - Ok(ClaimP2TRScriptPath(witness)) - } -} diff --git a/rust/tw_bitcoin/src/entry.rs b/rust/tw_bitcoin/src/entry.rs new file mode 100644 index 00000000000..0809b39f315 --- /dev/null +++ b/rust/tw_bitcoin/src/entry.rs @@ -0,0 +1,347 @@ +use crate::modules::plan_builder::BitcoinPlanBuilder; +use crate::modules::signer::Signer; +use crate::{Error, Result}; +use bitcoin::address::NetworkChecked; +use std::borrow::Cow; +use std::fmt::Display; +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{CoinAddress, CoinEntry, PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::derivation::Derivation; +use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::modules::json_signer::NoJsonSigner; +use tw_coin_entry::modules::message_signer::NoMessageSigner; +use tw_coin_entry::prefix::NoPrefix; +use tw_coin_entry::signing_output_error; +use tw_keypair::tw::PublicKey; +use tw_misc::traits::ToBytesVec; +use tw_proto::BitcoinV2::Proto; +use tw_proto::Utxo::Proto as UtxoProto; + +pub struct Address(pub bitcoin::address::Address); + +impl Display for Address { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl CoinAddress for Address { + fn data(&self) -> tw_memory::Data { + self.0.to_string().into_bytes() + } +} + +pub struct BitcoinEntry; + +impl CoinEntry for BitcoinEntry { + type AddressPrefix = NoPrefix; + type Address = Address; + type SigningInput<'a> = Proto::SigningInput<'a>; + type SigningOutput = Proto::SigningOutput<'static>; + type PreSigningOutput = Proto::PreSigningOutput<'static>; + + // Optional modules: + type JsonSigner = NoJsonSigner; + type PlanBuilder = BitcoinPlanBuilder; + type MessageSigner = NoMessageSigner; + + #[inline] + fn parse_address( + &self, + _coin: &dyn CoinContext, + address: &str, + _prefix: Option, + ) -> AddressResult { + let address = bitcoin::address::Address::from_str(address) + .map_err(|_| AddressError::FromHexError)? + .require_network(bitcoin::Network::Bitcoin) + .map_err(|_| AddressError::InvalidInput)?; + + Ok(Address(address)) + } + + #[inline] + fn derive_address( + &self, + _coin: &dyn CoinContext, + public_key: PublicKey, + _derivation: Derivation, + _prefix: Option, + ) -> AddressResult { + let pubkey = match public_key { + PublicKey::Secp256k1(pubkey) | PublicKey::Secp256k1Extended(pubkey) => pubkey, + _ => return Err(AddressError::InvalidInput), + }; + + let pubkey = bitcoin::PublicKey::from_slice(pubkey.to_vec().as_ref()) + .map_err(|_| AddressError::InvalidInput)?; + + let address: bitcoin::address::Address = bitcoin::address::Address::new( + bitcoin::Network::Bitcoin, + bitcoin::address::Payload::PubkeyHash(pubkey.pubkey_hash()), + ); + + Ok(Address(address)) + } + + #[inline] + fn sign(&self, _coin: &dyn CoinContext, proto: Self::SigningInput<'_>) -> Self::SigningOutput { + Signer::sign_proto(_coin, proto) + .unwrap_or_else(|err| signing_output_error!(Proto::SigningOutput, err)) + } + + #[inline] + fn preimage_hashes( + &self, + _coin: &dyn CoinContext, + proto: Proto::SigningInput<'_>, + ) -> Self::PreSigningOutput { + self.preimage_hashes_impl(_coin, proto) + .unwrap_or_else(|err| signing_output_error!(Proto::PreSigningOutput, err)) + } + + #[inline] + fn compile( + &self, + _coin: &dyn CoinContext, + proto: Proto::SigningInput<'_>, + signatures: Vec, + _public_keys: Vec, + ) -> Self::SigningOutput { + self.compile_impl(_coin, proto, signatures, _public_keys) + .unwrap_or_else(|err| signing_output_error!(Proto::SigningOutput, err)) + } + + #[inline] + fn plan_builder(&self) -> Option { + Some(BitcoinPlanBuilder) + } +} + +impl BitcoinEntry { + pub(crate) fn preimage_hashes_impl( + &self, + _coin: &dyn CoinContext, + proto: Proto::SigningInput<'_>, + ) -> Result> { + let proto = pre_processor(proto); + + // Convert input builders into Utxo inputs. + let utxo_inputs = proto + .inputs + .iter() + .map(crate::modules::transactions::InputBuilder::utxo_from_proto) + .collect::>>()?; + + // Convert output builders into Utxo outputs. + let mut utxo_outputs = proto + .outputs + .iter() + .map(crate::modules::transactions::OutputBuilder::utxo_from_proto) + .collect::>>()?; + + // If automatic change output is enabled, a change script must be provided. + let change_script_pubkey = if proto.disable_change_output { + Cow::default() + } else { + // Convert output builder to Utxo output. + let output = crate::modules::transactions::OutputBuilder::utxo_from_proto( + &proto + .change_output + .ok_or_else(|| Error::from(Proto::Error::Error_invalid_change_output))?, + )?; + + output.script_pubkey + }; + + // Prepare SigningInput for Utxo sighash generation. + let utxo_signing = UtxoProto::SigningInput { + version: proto.version, + lock_time: proto.lock_time, + inputs: utxo_inputs.clone(), + outputs: utxo_outputs + .iter() + .map(|output| UtxoProto::TxOut { + value: output.value, + script_pubkey: Cow::Borrowed(&output.script_pubkey), + }) + .collect(), + input_selector: proto.input_selector, + weight_base: proto.fee_per_vb, + change_script_pubkey, + disable_change_output: proto.disable_change_output, + }; + + // Generate the sighashes to be signed. + let utxo_presigning = tw_utxo::compiler::Compiler::preimage_hashes(utxo_signing); + handle_utxo_error(&utxo_presigning.error)?; + + // If a change output was created by the Utxo compiler, we return it here too. + if utxo_presigning.outputs.len() == utxo_outputs.len() + 1 { + let change_output = utxo_presigning + .outputs + .last() + .expect("expected change output"); + + utxo_outputs.push(Proto::mod_PreSigningOutput::TxOut { + value: change_output.value, + script_pubkey: change_output.script_pubkey.to_vec().into(), + control_block: Default::default(), + taproot_payload: Default::default(), + }) + } + + Ok(Proto::PreSigningOutput { + error: Proto::Error::OK, + error_message: Default::default(), + txid: utxo_presigning.txid, + sighashes: utxo_presigning.sighashes, + // Update selected inputs. + utxo_inputs: utxo_presigning.inputs, + utxo_outputs, + weight_estimate: utxo_presigning.weight_estimate, + fee_estimate: utxo_presigning.fee_estimate, + }) + } + + pub(crate) fn compile_impl( + &self, + _coin: &dyn CoinContext, + proto: Proto::SigningInput<'_>, + signatures: Vec, + _public_keys: Vec, + ) -> Result> { + let proto = pre_processor(proto); + + // There must be a signature for each input. + if proto.inputs.len() != signatures.len() { + return Err(Error::from( + Proto::Error::Error_unmatched_input_signature_count, + )); + } + + // Generate claims for all the inputs. + let mut utxo_input_claims: Vec = vec![]; + for (input, signature) in proto.inputs.iter().zip(signatures.into_iter()) { + let utxo_claim = + crate::modules::transactions::InputClaimBuilder::utxo_claim_from_proto( + input, signature, + )?; + utxo_input_claims.push(utxo_claim); + } + + // Process all the outputs. + let mut utxo_outputs = vec![]; + for output in proto.outputs { + let utxo = crate::modules::transactions::OutputBuilder::utxo_from_proto(&output)?; + + utxo_outputs.push(utxo); + } + + // Prepare PreSerialization input for Utxo compiler. + let utxo_preserializtion = UtxoProto::PreSerialization { + version: proto.version, + lock_time: proto.lock_time.clone(), + inputs: utxo_input_claims.clone(), + outputs: utxo_outputs + .iter() + .map(|out| UtxoProto::TxOut { + value: out.value, + script_pubkey: Cow::Borrowed(&out.script_pubkey), + }) + .collect(), + weight_base: proto.fee_per_vb, + }; + + // Compile the transaction, build the final encoded transaction + // containing the signatures/scriptSigs/witnesses. + let utxo_serialized = tw_utxo::compiler::Compiler::compile(utxo_preserializtion); + handle_utxo_error(&utxo_serialized.error)?; + + // Prepare `Proto::TransactionInput` protobufs for signing output. + let mut proto_inputs = vec![]; + for input in utxo_input_claims { + proto_inputs.push(Proto::TransactionInput { + txid: Cow::Owned(input.txid.to_vec()), + vout: input.vout, + sequence: input.sequence, + script_sig: Cow::Owned(input.script_sig.into_owned()), + witness_items: input + .witness_items + .into_iter() + .map(|item| Cow::Owned(item.into_owned())) + .collect(), + }); + } + + // Prepare `Proto::TransactionOutput` protobufs for output. + let mut proto_outputs = vec![]; + for output in utxo_outputs { + proto_outputs.push(Proto::TransactionOutput { + script_pubkey: output.script_pubkey, + value: output.value, + taproot_payload: output.taproot_payload, + control_block: output.control_block, + }); + } + + // Prepare `Proto::Transaction` protobuf for output. + let transaction = Proto::Transaction { + version: proto.version, + lock_time: proto.lock_time, + inputs: proto_inputs, + outputs: proto_outputs, + }; + + // Return the full protobuf output. + Ok(Proto::SigningOutput { + error: Proto::Error::OK, + error_message: Default::default(), + transaction: Some(transaction), + encoded: utxo_serialized.encoded, + txid: utxo_serialized.txid, + weight: utxo_serialized.weight, + fee: utxo_serialized.fee, + }) + } +} + +// Convenience function for pre-processing of certain fields that must be +// executed on each `CoinEntry` call. +pub(crate) fn pre_processor(mut proto: Proto::SigningInput<'_>) -> Proto::SigningInput<'_> { + // We automatically set the transaction version to 2. + if proto.version == 0 { + proto.version = 2; + } + + // If an input sequence (timelock, replace-by-fee, etc) of zero is not + // expliclity enabled, we interpreted a sequence of zero as the max value + // (default). + proto.inputs.iter_mut().for_each(|txin| { + if !txin.sequence_enable_zero && txin.sequence == 0 { + txin.sequence = u32::MAX + } + }); + + proto +} + +#[rustfmt::skip] +/// Convert `Utxo.proto` error type to `BitcoinV2.proto` error type. +fn handle_utxo_error(utxo_err: &UtxoProto::Error) -> Result<()> { + let bitcoin_err = match utxo_err { + UtxoProto::Error::OK => return Ok(()), + UtxoProto::Error::Error_invalid_leaf_hash => Proto::Error::Error_utxo_invalid_leaf_hash, + UtxoProto::Error::Error_invalid_sighash_type => Proto::Error::Error_utxo_invalid_sighash_type, + UtxoProto::Error::Error_invalid_lock_time => Proto::Error::Error_utxo_invalid_lock_time, + UtxoProto::Error::Error_invalid_txid => Proto::Error::Error_utxo_invalid_txid, + UtxoProto::Error::Error_sighash_failed => Proto::Error::Error_utxo_sighash_failed, + UtxoProto::Error::Error_missing_sighash_method => Proto::Error::Error_utxo_missing_sighash_method, + UtxoProto::Error::Error_failed_encoding => Proto::Error::Error_utxo_failed_encoding, + UtxoProto::Error::Error_insufficient_inputs => Proto::Error::Error_utxo_insufficient_inputs, + UtxoProto::Error::Error_missing_change_script_pubkey => Proto::Error::Error_utxo_missing_change_script_pubkey, + }; + + Err(Error::from(bitcoin_err)) +} diff --git a/rust/tw_bitcoin/src/ffi/address.rs b/rust/tw_bitcoin/src/ffi/address.rs deleted file mode 100644 index 63c34dc31fc..00000000000 --- a/rust/tw_bitcoin/src/ffi/address.rs +++ /dev/null @@ -1,98 +0,0 @@ -use super::CTaprootError; -use crate::Recipient; -use bitcoin::PublicKey; -use std::ffi::CString; -use tw_memory::ffi::c_byte_array_ref::CByteArrayRef; -use tw_memory::ffi::c_result::CStrMutResult; - -#[no_mangle] -pub unsafe extern "C" fn tw_legacy_address_string( - pubkey: *const u8, - pubkey_len: usize, - network: Network, -) -> CStrMutResult { - // Convert Recipient. - let Some(slice) = CByteArrayRef::new(pubkey, pubkey_len).as_slice() else { - return CStrMutResult::error(CTaprootError::InvalidSlice); - }; - - let Ok(recipient) = Recipient::::from_slice(slice) else { - return CStrMutResult::error(CTaprootError::InvalidPubkey); - }; - - let address = recipient.legacy_address_string(network.into()); - let c_string = CString::new(address) - .expect("legacy address contains an internal 0 byte") - .into_raw(); - - CStrMutResult::ok(c_string) -} - -#[no_mangle] -pub unsafe extern "C" fn tw_segwit_address_string( - pubkey: *const u8, - pubkey_len: usize, - network: Network, -) -> CStrMutResult { - // Convert Recipient. - let Some(slice) = CByteArrayRef::new(pubkey, pubkey_len).as_slice() else { - return CStrMutResult::error(CTaprootError::InvalidSlice); - }; - - let Ok(recipient) = Recipient::::from_slice(slice) else { - return CStrMutResult::error(CTaprootError::InvalidPubkey); - }; - - let Ok(address) = recipient.segwit_address_string(network.into()) else { - return CStrMutResult::error(CTaprootError::InvalidSegwitPukey); - }; - - let c_string = CString::new(address) - .expect("legacy address contains an internal 0 byte") - .into_raw(); - - CStrMutResult::ok(c_string) -} - -#[no_mangle] -pub unsafe extern "C" fn tw_taproot_address_string( - pubkey: *const u8, - pubkey_len: usize, - network: Network, -) -> CStrMutResult { - // Convert Recipient. - let Some(slice) = CByteArrayRef::new(pubkey, pubkey_len).as_slice() else { - return CStrMutResult::error(CTaprootError::InvalidSlice); - }; - - let Ok(recipient) = Recipient::::from_slice(slice) else { - return CStrMutResult::error(CTaprootError::InvalidPubkey); - }; - - let address = recipient.taproot_address_string(network.into()); - let c_string = CString::new(address) - .expect("legacy address contains an internal 0 byte") - .into_raw(); - - CStrMutResult::ok(c_string) -} - -// A custom reimplementation of of `bitcoin::Network`. -#[repr(C)] -pub enum Network { - Bitcoin = 0, - Testnet = 1, - Signet = 2, - Regtest = 3, -} - -impl From for bitcoin::Network { - fn from(n: Network) -> Self { - match n { - Network::Bitcoin => bitcoin::Network::Bitcoin, - Network::Testnet => bitcoin::Network::Testnet, - Network::Signet => bitcoin::Network::Signet, - Network::Regtest => bitcoin::Network::Regtest, - } - } -} diff --git a/rust/tw_bitcoin/src/ffi/mod.rs b/rust/tw_bitcoin/src/ffi/mod.rs deleted file mode 100644 index 4b6f8988555..00000000000 --- a/rust/tw_bitcoin/src/ffi/mod.rs +++ /dev/null @@ -1,279 +0,0 @@ -#![allow(clippy::missing_safety_doc)] - -use crate::{ - calculate_fee, Error, Result, TXOutputP2TRScriptPath, TaprootScript, TxInputP2TRScriptPath, -}; -use bitcoin::{ - consensus::Decodable, - taproot::{NodeInfo, TapNodeHash, TaprootSpendInfo}, - PublicKey, ScriptBuf, Transaction, Txid, -}; -use secp256k1::hashes::Hash; -use secp256k1::KeyPair; -use std::borrow::Cow; -use tw_memory::ffi::c_byte_array::CByteArray; -use tw_memory::ffi::c_byte_array_ref::CByteArrayRef; -use tw_memory::ffi::c_result::CUInt64Result; -use tw_memory::ffi::c_result::ErrorCode; -use tw_misc::try_or_else; -use tw_proto::Bitcoin::Proto::{ - OutPoint, SigningInput, SigningOutput, Transaction as ProtoTransaction, TransactionInput, - TransactionOutput, TransactionVariant as TrVariant, -}; - -pub mod address; -pub mod scripts; - -// Re-exports -pub use address::*; -pub use scripts::*; - -use crate::{ - Recipient, TransactionBuilder, TxInput, TxInputP2PKH, TxInputP2TRKeyPath, TxInputP2WPKH, - TxOutput, TxOutputP2PKH, TxOutputP2TRKeyPath, TxOutputP2WPKH, -}; - -#[no_mangle] -pub unsafe extern "C" fn tw_bitcoin_calculate_transaction_fee( - input: *const u8, - input_len: usize, - sat_vb: u64, -) -> CUInt64Result { - let Some(mut encoded) = CByteArrayRef::new(input, input_len).as_slice() else { - return CUInt64Result::error(1); - }; - - // Decode transaction. - let Ok(tx) = Transaction::consensus_decode(&mut encoded) else { - return CUInt64Result::error(1); - }; - - // Calculate fee. - let (_weight, fee) = calculate_fee(&tx, sat_vb); - - CUInt64Result::ok(fee) -} - -#[no_mangle] -pub unsafe extern "C" fn tw_taproot_build_and_sign_transaction( - input: *const u8, - input_len: usize, -) -> CByteArray { - let data = CByteArrayRef::new(input, input_len) - .to_vec() - .unwrap_or_default(); - - let proto: SigningInput = try_or_else!(tw_proto::deserialize(&data), CByteArray::null); - let signing = try_or_else!(taproot_build_and_sign_transaction(proto), CByteArray::null); - - let serialized = tw_proto::serialize(&signing).expect("failed to serialize signed transaction"); - - CByteArray::from(serialized) -} - -/// Note: many of the fields used in the `SigningInput` are currently unused. We -/// can later easily replicate the funcationlity and behavior of the C++ -/// implemenation. -/// -/// Additionally, the `SigningInput` supports two ways of operating (which -/// should probably be separated anyway): one way where the `TransactionPlan` is -/// skipped (and hence automatically constructed) and the other way where the -/// `TransactionPlan` is created manually. As of now, it's expected that the -/// `TransactionPlan` is created manually, meaning that the caller must careful -/// construct the outputs, which must include the return/change transaction and -/// how much goes to the miner as fee ( minus -/// ). -pub(crate) fn taproot_build_and_sign_transaction(proto: SigningInput) -> Result { - let privkey = proto.private_key.get(0).ok_or(Error::Todo)?; - - // Prepare keypair and derive corresponding public key. - let keypair = KeyPair::from_seckey_slice(&secp256k1::Secp256k1::new(), privkey.as_ref()) - .map_err(|_| crate::Error::Todo)?; - - let my_pubkey = Recipient::::from(keypair); - - let mut builder = TransactionBuilder::new(); - - // Process inputs. - for input in proto.utxo { - let my_pubkey = my_pubkey.clone(); - - let out_point = input.out_point.ok_or(Error::Todo)?; - let txid = Txid::from_slice(&out_point.hash).map_err(|_| crate::Error::Todo)?; - let vout = out_point.index; - let satoshis = input.amount as u64; - - let script_buf = ScriptBuf::from_bytes(input.script.to_vec()); - - let tx: TxInput = match input.variant { - TrVariant::P2PKH => { - TxInputP2PKH::new_with_script(txid, vout, my_pubkey.into(), satoshis, script_buf) - .into() - }, - TrVariant::P2WPKH => TxInputP2WPKH::new_with_script( - txid, - vout, - my_pubkey.try_into()?, - satoshis, - script_buf, - ) - .into(), - TrVariant::P2TRKEYPATH => TxInputP2TRKeyPath::new_with_script( - txid, - vout, - my_pubkey.into(), - satoshis, - script_buf, - ) - .into(), - TrVariant::BRC20TRANSFER | TrVariant::NFTINSCRIPTION => { - // We construct the merkle root for the given spending script. - let spending_script = ScriptBuf::from_bytes(input.spendingScript.to_vec()); - let merkle_root = TapNodeHash::from_script( - spending_script.as_script(), - bitcoin::taproot::LeafVersion::TapScript, - ); - - // Convert to tapscript recipient with the given merkle root. - let recipient = - Recipient::::from_pubkey_recipient(my_pubkey, merkle_root); - - // Derive the spending information for the taproot recipient. - let spend_info = TaprootSpendInfo::from_node_info( - &secp256k1::Secp256k1::new(), - recipient.untweaked_pubkey(), - NodeInfo::new_leaf_with_ver( - spending_script.clone(), - bitcoin::taproot::LeafVersion::TapScript, - ), - ); - - TxInputP2TRScriptPath::new_with_script( - txid, - vout, - recipient, - satoshis, - script_buf, - spending_script, - spend_info, - ) - .into() - }, - }; - - builder = builder.add_input(tx); - } - - // Process outputs. - for output in proto.plan.ok_or(Error::Todo)?.utxos { - let script_buf = ScriptBuf::from_bytes(output.script.to_vec()); - let satoshis = output.amount as u64; - - #[rustfmt::skip] - let tx: TxOutput = match output.variant { - TrVariant::P2PKH => { - TxOutputP2PKH::new_with_script(satoshis, script_buf).into() - }, - TrVariant::P2WPKH => { - TxOutputP2WPKH::new_with_script(satoshis, script_buf).into() - }, - TrVariant::P2TRKEYPATH => { - TxOutputP2TRKeyPath::new_with_script(satoshis, script_buf).into() - }, - // We're keeping those two variants separate for now, we're planning - // on writing a new interface as part of a larger task anyway. - TrVariant::BRC20TRANSFER => { - TXOutputP2TRScriptPath::new_with_script(satoshis, script_buf).into() - }, - TrVariant::NFTINSCRIPTION => { - TXOutputP2TRScriptPath::new_with_script(satoshis, script_buf).into() - } - }; - - builder = builder.add_output(tx); - } - - // Copy those values before `builder` gets consumed. - let version = builder.version; - let lock_time = builder.lock_time.to_consensus_u32(); - - // Sign transaction and create protobuf structures. - let tx = builder.sign_inputs(keypair)?; - - // Create Protobuf structures of inputs. - let mut proto_inputs = vec![]; - for input in &tx.inner.input { - let txid: Vec = input - .previous_output - .txid - .as_byte_array() - .iter() - .cloned() - .rev() - .collect(); - - proto_inputs.push(TransactionInput { - previousOutput: Some(OutPoint { - hash: Cow::from(txid), - index: input.previous_output.vout, - sequence: input.sequence.to_consensus_u32(), - // Unused. - tree: 0, - }), - sequence: input.sequence.to_consensus_u32(), - script: { - // If `scriptSig` is empty, then the Witness is being used. - if input.script_sig.is_empty() { - let witness: Vec = input.witness.to_vec().into_iter().flatten().collect(); - Cow::from(witness) - } else { - Cow::from(input.script_sig.to_bytes()) - } - }, - }); - } - - // Create Protobuf structures of outputs. - let mut proto_outputs = vec![]; - for output in &tx.inner.output { - proto_outputs.push(TransactionOutput { - value: output.value as i64, - script: Cow::from(output.script_pubkey.to_bytes()), - spendingScript: Cow::default(), - }) - } - - // Create Protobuf structure of the full transaction. - let mut signing = SigningOutput { - transaction: Some(ProtoTransaction { - version, - lockTime: lock_time, - inputs: proto_inputs, - outputs: proto_outputs, - }), - encoded: Cow::default(), - transaction_id: Cow::from(tx.inner.txid().to_string()), - error: tw_proto::Common::Proto::SigningError::OK, - error_message: Cow::default(), - }; - - // Sign transaction and update Protobuf structure. - let signed = tx.serialize()?; - signing.encoded = Cow::from(signed); - - Ok(signing) -} - -#[repr(C)] -pub enum CTaprootError { - Ok = 0, - InvalidSlice = 1, - InvalidPubkey = 2, - InvalidSegwitPukey = 3, -} - -impl From for ErrorCode { - fn from(error: CTaprootError) -> Self { - error as ErrorCode - } -} diff --git a/rust/tw_bitcoin/src/ffi/scripts.rs b/rust/tw_bitcoin/src/ffi/scripts.rs deleted file mode 100644 index f230cbd0cc0..00000000000 --- a/rust/tw_bitcoin/src/ffi/scripts.rs +++ /dev/null @@ -1,197 +0,0 @@ -use crate::brc20::{BRC20TransferInscription, Ticker}; -use crate::nft::OrdinalNftInscription; -use crate::{ - Recipient, TXOutputP2TRScriptPath, TxOutputP2PKH, TxOutputP2TRKeyPath, TxOutputP2WPKH, -}; -use bitcoin::{PublicKey, WPubkeyHash}; -use std::borrow::Cow; -use std::ffi::{c_char, CStr}; -use tw_memory::ffi::c_byte_array::CByteArray; -use tw_memory::ffi::c_byte_array_ref::CByteArrayRef; -use tw_misc::try_or_else; -use tw_proto::Bitcoin::Proto::TransactionOutput; - -#[no_mangle] -// Builds the P2PKH scriptPubkey. -pub unsafe extern "C" fn tw_build_p2pkh_script( - satoshis: i64, - pubkey: *const u8, - pubkey_len: usize, -) -> CByteArray { - // Convert Recipient - let slice = try_or_else!( - CByteArrayRef::new(pubkey, pubkey_len).as_slice(), - CByteArray::null - ); - let recipient = try_or_else!(Recipient::::from_slice(slice), CByteArray::null); - - let tx_out = TxOutputP2PKH::new(satoshis as u64, recipient); - - // Prepare and serialize protobuf structure. - let proto = TransactionOutput { - value: satoshis, - script: Cow::from(tx_out.script_pubkey.as_bytes()), - spendingScript: Cow::default(), - }; - - let serialized = tw_proto::serialize(&proto).expect("failed to serialized transaction output"); - - CByteArray::from(serialized) -} - -#[no_mangle] -// Builds the P2WPKH scriptPubkey. -pub unsafe extern "C" fn tw_build_p2wpkh_script( - satoshis: i64, - pubkey: *const u8, - pubkey_len: usize, -) -> CByteArray { - // Convert Recipient - let slice = try_or_else!( - CByteArrayRef::new(pubkey, pubkey_len).as_slice(), - CByteArray::null - ); - let recipient = try_or_else!( - Recipient::::from_slice(slice), - CByteArray::null - ); - - let tx_out = TxOutputP2WPKH::new(satoshis as u64, recipient); - - // Prepare and serialize protobuf structure. - let proto = TransactionOutput { - value: satoshis, - script: Cow::from(tx_out.script_pubkey.as_bytes()), - spendingScript: Cow::default(), - }; - - let serialized = tw_proto::serialize(&proto).expect("failed to serialized transaction output"); - - CByteArray::from(serialized) -} - -#[no_mangle] -// Builds the P2TR key-path scriptPubkey. -pub unsafe extern "C" fn tw_build_p2tr_key_path_script( - satoshis: i64, - pubkey: *const u8, - pubkey_len: usize, -) -> CByteArray { - // Convert Recipient - let slice = try_or_else!( - CByteArrayRef::new(pubkey, pubkey_len).as_slice(), - CByteArray::null - ); - let recipient = try_or_else!(Recipient::::from_slice(slice), CByteArray::null); - - let tx_out = TxOutputP2TRKeyPath::new(satoshis as u64, recipient.into()); - - // Prepare and serialize protobuf structure. - let proto = TransactionOutput { - value: satoshis, - script: Cow::from(tx_out.script_pubkey.as_bytes()), - spendingScript: Cow::default(), - }; - - let serialized = tw_proto::serialize(&proto).expect("failed to serialized transaction output"); - - CByteArray::from(serialized) -} - -#[no_mangle] -// Builds the Ordinals inscripton for BRC20 transfer. -pub unsafe extern "C" fn tw_build_brc20_transfer_inscription( - // The 4-byte ticker. - ticker: *const c_char, - amount: u64, - satoshis: i64, - pubkey: *const u8, - pubkey_len: usize, -) -> CByteArray { - // Convert ticket. - let ticker = match CStr::from_ptr(ticker).to_str() { - Ok(input) => input, - Err(_) => return CByteArray::null(), - }; - - if ticker.len() != 4 { - return CByteArray::null(); - } - - let ticker = Ticker::new(ticker.to_string()).expect("ticker must be 4 bytes"); - - // Convert Recipient - let slice = try_or_else!( - CByteArrayRef::new(pubkey, pubkey_len).as_slice(), - CByteArray::null - ); - - let recipient = try_or_else!(Recipient::::from_slice(slice), CByteArray::null); - - // Build transfer inscription. - let transfer = BRC20TransferInscription::new(recipient, ticker, amount) - .expect("transfer inscription implemented wrongly"); - - let tx_out = TXOutputP2TRScriptPath::new(satoshis as u64, transfer.inscription().recipient()); - let spending_script = transfer.inscription().taproot_program(); - - // Prepare and serialize protobuf structure. - let proto = TransactionOutput { - value: satoshis, - script: Cow::from(tx_out.script_pubkey.as_bytes()), - spendingScript: Cow::from(spending_script.as_bytes()), - }; - - let serialized = tw_proto::serialize(&proto).expect("failed to serialized transaction output"); - - CByteArray::from(serialized) -} - -#[no_mangle] -// Builds the Ordinals inscripton for BRC20 transfer. -pub unsafe extern "C" fn tw_bitcoin_build_nft_inscription( - mime_type: *const c_char, - data: *const u8, - data_len: usize, - satoshis: i64, - pubkey: *const u8, - pubkey_len: usize, -) -> CByteArray { - // Convert mimeType. - let mime_type = match CStr::from_ptr(mime_type).to_str() { - Ok(input) => input, - Err(_) => return CByteArray::null(), - }; - - // Convert data to inscribe. - let data = try_or_else!( - CByteArrayRef::new(data, data_len).as_slice(), - CByteArray::null - ); - - // Convert Recipient. - let slice = try_or_else!( - CByteArrayRef::new(pubkey, pubkey_len).as_slice(), - CByteArray::null - ); - - let recipient = try_or_else!(Recipient::::from_slice(slice), CByteArray::null); - - // Inscribe NFT data. - let nft = OrdinalNftInscription::new(mime_type.as_bytes(), data, recipient) - .expect("Ordinal NFT inscription incorrectly constructed"); - - let tx_out = TXOutputP2TRScriptPath::new(satoshis as u64, nft.inscription().recipient()); - let spending_script = nft.inscription().taproot_program(); - - // Prepare and serialize protobuf structure. - let proto = TransactionOutput { - value: satoshis, - script: Cow::from(tx_out.script_pubkey.as_bytes()), - spendingScript: Cow::from(spending_script.as_bytes()), - }; - - let serialized = tw_proto::serialize(&proto).expect("failed to serialized transaction output"); - - CByteArray::from(serialized) -} diff --git a/rust/tw_bitcoin/src/input/mod.rs b/rust/tw_bitcoin/src/input/mod.rs deleted file mode 100644 index ebe68369c2d..00000000000 --- a/rust/tw_bitcoin/src/input/mod.rs +++ /dev/null @@ -1,92 +0,0 @@ -use bitcoin::{OutPoint, ScriptBuf, Sequence, TxIn, TxOut, Witness}; - -mod p2pkh; -mod p2tr_key_path; -mod p2tr_script_path; -mod p2wpkh; - -pub use p2pkh::*; -pub use p2tr_key_path::*; -pub use p2tr_script_path::*; -pub use p2wpkh::*; - -#[derive(Debug, Clone)] -pub struct InputContext { - pub previous_output: OutPoint, - pub value: u64, - // The condition for claiming the output. - pub script_pubkey: ScriptBuf, - pub sequence: Sequence, - // Witness data for Segwit/Taproot transactions. -} - -impl InputContext { - pub fn new(utxo: TxOut, point: OutPoint) -> Self { - InputContext { - previous_output: point, - value: utxo.value, - script_pubkey: utxo.script_pubkey, - // Default value of `0xFFFFFFFF = 4294967295`. - sequence: Sequence::default(), - } - } -} - -#[derive(Debug, Clone)] -pub enum TxInput { - P2PKH(TxInputP2PKH), - P2WPKH(TxInputP2WPKH), - P2TRKeyPath(TxInputP2TRKeyPath), - P2TRScriptPath(TxInputP2TRScriptPath), -} - -impl From for TxInput { - fn from(input: TxInputP2PKH) -> Self { - TxInput::P2PKH(input) - } -} - -impl From for TxInput { - fn from(input: TxInputP2WPKH) -> Self { - TxInput::P2WPKH(input) - } -} - -impl From for TxInput { - fn from(input: TxInputP2TRKeyPath) -> Self { - TxInput::P2TRKeyPath(input) - } -} - -impl From for TxInput { - fn from(input: TxInputP2TRScriptPath) -> Self { - TxInput::P2TRScriptPath(input) - } -} - -impl From for TxIn { - fn from(input: TxInput) -> Self { - let ctx = input.ctx(); - - TxIn { - previous_output: ctx.previous_output, - script_sig: ScriptBuf::new(), - sequence: ctx.sequence, - witness: Witness::default(), - } - } -} - -impl TxInput { - pub fn ctx(&self) -> &InputContext { - match self { - TxInput::P2PKH(t) => t.ctx(), - TxInput::P2WPKH(t) => t.ctx(), - TxInput::P2TRKeyPath(t) => t.ctx(), - TxInput::P2TRScriptPath(t) => t.ctx(), - } - } - pub fn satoshis(&self) -> u64 { - self.ctx().value - } -} diff --git a/rust/tw_bitcoin/src/input/p2pkh.rs b/rust/tw_bitcoin/src/input/p2pkh.rs deleted file mode 100644 index 66bfe3de73b..00000000000 --- a/rust/tw_bitcoin/src/input/p2pkh.rs +++ /dev/null @@ -1,86 +0,0 @@ -use crate::{Error, InputContext, Recipient, Result}; -use bitcoin::{OutPoint, PubkeyHash, ScriptBuf, Sequence, Txid}; - -#[derive(Debug, Clone)] -pub struct TxInputP2PKH { - ctx: InputContext, - recipient: Recipient, -} - -impl TxInputP2PKH { - pub fn new(txid: Txid, vout: u32, recipient: Recipient, satoshis: u64) -> Self { - let script = ScriptBuf::new_p2pkh(recipient.pubkey_hash()); - Self::new_with_script(txid, vout, recipient, satoshis, script) - } - pub fn new_with_script( - txid: Txid, - vout: u32, - recipient: Recipient, - satoshis: u64, - script: ScriptBuf, - ) -> Self { - TxInputP2PKH { - ctx: InputContext { - previous_output: OutPoint { txid, vout }, - value: satoshis, - script_pubkey: script, - sequence: Sequence::default(), - }, - recipient, - } - } - pub fn builder() -> TxInputP2PKHBuilder { - TxInputP2PKHBuilder::new() - } - /// Read-only exposure to the context. - pub fn ctx(&self) -> &InputContext { - &self.ctx - } - /// Read-only exposure to the recipient. - pub fn recipient(&self) -> &Recipient { - &self.recipient - } -} - -#[derive(Debug, Clone, Default)] -pub struct TxInputP2PKHBuilder { - txid: Option, - vout: Option, - recipient: Option>, - satoshis: Option, -} - -impl TxInputP2PKHBuilder { - pub fn new() -> TxInputP2PKHBuilder { - TxInputP2PKHBuilder { - txid: None, - vout: None, - recipient: None, - satoshis: None, - } - } - pub fn txid(mut self, txid: Txid) -> TxInputP2PKHBuilder { - self.txid = Some(txid); - self - } - pub fn vout(mut self, vout: u32) -> TxInputP2PKHBuilder { - self.vout = Some(vout); - self - } - pub fn recipient(mut self, recipient: impl Into>) -> TxInputP2PKHBuilder { - self.recipient = Some(recipient.into()); - self - } - pub fn satoshis(mut self, satoshis: u64) -> TxInputP2PKHBuilder { - self.satoshis = Some(satoshis); - self - } - pub fn build(self) -> Result { - Ok(TxInputP2PKH::new( - self.txid.ok_or(Error::Todo)?, - self.vout.ok_or(Error::Todo)?, - self.recipient.ok_or(Error::Todo)?, - self.satoshis.ok_or(Error::Todo)?, - )) - } -} diff --git a/rust/tw_bitcoin/src/input/p2tr_key_path.rs b/rust/tw_bitcoin/src/input/p2tr_key_path.rs deleted file mode 100644 index 29bf0ff07f5..00000000000 --- a/rust/tw_bitcoin/src/input/p2tr_key_path.rs +++ /dev/null @@ -1,90 +0,0 @@ -use crate::{Error, InputContext, Recipient, Result}; -use bitcoin::key::TweakedPublicKey; -use bitcoin::{OutPoint, ScriptBuf, Sequence, Txid}; - -#[derive(Debug, Clone)] -pub struct TxInputP2TRKeyPath { - ctx: InputContext, - recipient: Recipient, -} - -impl TxInputP2TRKeyPath { - pub fn new( - txid: Txid, - vout: u32, - recipient: Recipient, - satoshis: u64, - ) -> Self { - let script = ScriptBuf::new_v1_p2tr_tweaked(recipient.tweaked_pubkey()); - Self::new_with_script(txid, vout, recipient, satoshis, script) - } - pub fn new_with_script( - txid: Txid, - vout: u32, - recipient: Recipient, - satoshis: u64, - script: ScriptBuf, - ) -> Self { - TxInputP2TRKeyPath { - ctx: InputContext { - previous_output: OutPoint { txid, vout }, - value: satoshis, - script_pubkey: script, - sequence: Sequence::default(), - }, - recipient, - } - } - pub fn builder() -> TxInputP2TRKeyPathBuilder { - TxInputP2TRKeyPathBuilder::new() - } - /// Read-only exposure to the context. - pub fn ctx(&self) -> &InputContext { - &self.ctx - } - /// Read-only exposure to the recipient. - pub fn recipient(&self) -> &Recipient { - &self.recipient - } -} - -#[derive(Debug, Clone, Default)] -pub struct TxInputP2TRKeyPathBuilder { - txid: Option, - vout: Option, - recipient: Option>, - satoshis: Option, -} - -impl TxInputP2TRKeyPathBuilder { - pub fn new() -> TxInputP2TRKeyPathBuilder { - Self::default() - } - pub fn txid(mut self, txid: Txid) -> TxInputP2TRKeyPathBuilder { - self.txid = Some(txid); - self - } - pub fn vout(mut self, vout: u32) -> TxInputP2TRKeyPathBuilder { - self.vout = Some(vout); - self - } - pub fn recipient( - mut self, - recipient: impl Into>, - ) -> TxInputP2TRKeyPathBuilder { - self.recipient = Some(recipient.into()); - self - } - pub fn satoshis(mut self, satoshis: u64) -> TxInputP2TRKeyPathBuilder { - self.satoshis = Some(satoshis); - self - } - pub fn build(self) -> Result { - Ok(TxInputP2TRKeyPath::new( - self.txid.ok_or(Error::Todo)?, - self.vout.ok_or(Error::Todo)?, - self.recipient.ok_or(Error::Todo)?, - self.satoshis.ok_or(Error::Todo)?, - )) - } -} diff --git a/rust/tw_bitcoin/src/input/p2tr_script_path.rs b/rust/tw_bitcoin/src/input/p2tr_script_path.rs deleted file mode 100644 index af02c107533..00000000000 --- a/rust/tw_bitcoin/src/input/p2tr_script_path.rs +++ /dev/null @@ -1,127 +0,0 @@ -use crate::{Error, InputContext, Recipient, Result, TaprootScript}; -use bitcoin::script::ScriptBuf; -use bitcoin::taproot::TaprootSpendInfo; -use bitcoin::{OutPoint, Sequence, Txid}; - -#[derive(Debug, Clone)] -pub struct TxInputP2TRScriptPath { - ctx: InputContext, - recipient: Recipient, - witness: ScriptBuf, - spend_info: TaprootSpendInfo, -} - -impl TxInputP2TRScriptPath { - pub fn new( - txid: Txid, - vout: u32, - recipient: Recipient, - satoshis: u64, - witness: ScriptBuf, - spend_info: TaprootSpendInfo, - ) -> Self { - let script = ScriptBuf::new_v1_p2tr( - &secp256k1::Secp256k1::new(), - recipient.untweaked_pubkey(), - Some(recipient.merkle_root()), - ); - - Self::new_with_script(txid, vout, recipient, satoshis, script, witness, spend_info) - } - pub fn new_with_script( - txid: Txid, - vout: u32, - recipient: Recipient, - satoshis: u64, - script: ScriptBuf, - witness: ScriptBuf, - spend_info: TaprootSpendInfo, - ) -> Self { - TxInputP2TRScriptPath { - ctx: InputContext { - previous_output: OutPoint { txid, vout }, - value: satoshis, - script_pubkey: script, - sequence: Sequence::default(), - }, - recipient, - witness, - spend_info, - } - } - pub fn builder() -> TxInputP2TRScriptPathBuilder { - TxInputP2TRScriptPathBuilder::new() - } - pub fn ctx(&self) -> &InputContext { - &self.ctx - } - pub fn recipient(&self) -> &Recipient { - &self.recipient - } - pub fn witness(&self) -> &ScriptBuf { - &self.witness - } - pub fn spend_info(&self) -> &TaprootSpendInfo { - &self.spend_info - } -} - -#[derive(Debug, Clone, Default)] -pub struct TxInputP2TRScriptPathBuilder { - txid: Option, - vout: Option, - recipient: Option>, - satoshis: Option, - script: Option, - spend_info: Option, -} - -impl TxInputP2TRScriptPathBuilder { - pub fn new() -> TxInputP2TRScriptPathBuilder { - TxInputP2TRScriptPathBuilder { - txid: None, - vout: None, - recipient: None, - satoshis: None, - script: None, - spend_info: None, - } - } - pub fn txid(mut self, txid: Txid) -> TxInputP2TRScriptPathBuilder { - self.txid = Some(txid); - self - } - pub fn vout(mut self, vout: u32) -> TxInputP2TRScriptPathBuilder { - self.vout = Some(vout); - self - } - pub fn recipient( - mut self, - recipient: Recipient, - ) -> TxInputP2TRScriptPathBuilder { - self.recipient = Some(recipient); - self - } - pub fn satoshis(mut self, satoshis: u64) -> TxInputP2TRScriptPathBuilder { - self.satoshis = Some(satoshis); - self - } - pub fn script(mut self, script: ScriptBuf) -> TxInputP2TRScriptPathBuilder { - self.script = Some(script); - self - } - pub fn spend_info(mut self, spend_info: TaprootSpendInfo) -> TxInputP2TRScriptPathBuilder { - self.spend_info = Some(spend_info); - self - } - pub fn build(self) -> Result { - Ok(TxInputP2TRScriptPath::new( - self.txid.ok_or(Error::Todo)?, - self.vout.ok_or(Error::Todo)?, - self.recipient.ok_or(Error::Todo)?, - self.satoshis.ok_or(Error::Todo)?, - self.script.ok_or(Error::Todo)?, - self.spend_info.ok_or(Error::Todo)?, - )) - } -} diff --git a/rust/tw_bitcoin/src/input/p2wpkh.rs b/rust/tw_bitcoin/src/input/p2wpkh.rs deleted file mode 100644 index 4270fe73688..00000000000 --- a/rust/tw_bitcoin/src/input/p2wpkh.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::{Error, InputContext, Recipient, Result}; -use bitcoin::{OutPoint, ScriptBuf, Sequence, Txid, WPubkeyHash}; - -#[derive(Debug, Clone)] -pub struct TxInputP2WPKH { - ctx: InputContext, - recipient: Recipient, -} - -impl TxInputP2WPKH { - pub fn new(txid: Txid, vout: u32, recipient: Recipient, satoshis: u64) -> Self { - let script = ScriptBuf::new_v0_p2wpkh(recipient.wpubkey_hash()); - Self::new_with_script(txid, vout, recipient, satoshis, script) - } - pub fn new_with_script( - txid: Txid, - vout: u32, - recipient: Recipient, - satoshis: u64, - script: ScriptBuf, - ) -> Self { - TxInputP2WPKH { - ctx: InputContext { - previous_output: OutPoint { txid, vout }, - value: satoshis, - script_pubkey: script, - sequence: Sequence::default(), - }, - recipient, - } - } - pub fn builder() -> TxInputP2WPKHBuilder { - TxInputP2WPKHBuilder::new() - } - /// Read-only exposure to the context. - pub fn ctx(&self) -> &InputContext { - &self.ctx - } - /// Read-only exposure to the recipient. - pub fn recipient(&self) -> &Recipient { - &self.recipient - } -} - -#[derive(Debug, Clone, Default)] -pub struct TxInputP2WPKHBuilder { - txid: Option, - vout: Option, - recipient: Option>, - satoshis: Option, -} - -impl TxInputP2WPKHBuilder { - pub fn new() -> TxInputP2WPKHBuilder { - Self::default() - } - pub fn txid(mut self, txid: Txid) -> TxInputP2WPKHBuilder { - self.txid = Some(txid); - self - } - pub fn vout(mut self, vout: u32) -> TxInputP2WPKHBuilder { - self.vout = Some(vout); - self - } - pub fn recipient(mut self, recipient: Recipient) -> TxInputP2WPKHBuilder { - self.recipient = Some(recipient); - self - } - pub fn satoshis(mut self, satoshis: u64) -> TxInputP2WPKHBuilder { - self.satoshis = Some(satoshis); - self - } - pub fn build(self) -> Result { - Ok(TxInputP2WPKH::new( - self.txid.ok_or(Error::Todo)?, - self.vout.ok_or(Error::Todo)?, - self.recipient.ok_or(Error::Todo)?, - self.satoshis.ok_or(Error::Todo)?, - )) - } -} diff --git a/rust/tw_bitcoin/src/lib.rs b/rust/tw_bitcoin/src/lib.rs index 7ff788a5dc6..a3bb3d0ac69 100644 --- a/rust/tw_bitcoin/src/lib.rs +++ b/rust/tw_bitcoin/src/lib.rs @@ -1,28 +1,68 @@ extern crate serde; -pub mod brc20; -pub mod claim; -pub mod ffi; -pub mod input; -pub mod nft; -pub mod ordinals; -pub mod output; -pub mod recipient; -#[cfg(test)] -mod tests; -pub mod transaction; -pub mod utils; - -// Reexports -pub use input::*; -pub use output::*; -pub use recipient::Recipient; -pub use transaction::*; -pub use utils::*; +pub mod entry; +pub mod modules; + +use std::fmt::Display; + +pub use bitcoin as native; +pub use entry::*; +pub use secp256k1; + +use tw_proto::BitcoinV2::Proto; pub type Result = std::result::Result; -#[derive(Debug, Clone)] -pub enum Error { - Todo, +#[derive(Debug)] +pub struct Error(Proto::Error); + +// TODO: We can improve this. +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } +} + +impl From for Error { + fn from(value: Proto::Error) -> Self { + Error(value) + } +} + +impl From for Proto::Error { + fn from(value: Error) -> Self { + value.0 + } +} + +impl From for Error { + fn from(_: bitcoin::key::Error) -> Self { + Error::from(Proto::Error::Error_invalid_public_key) + } +} + +impl From for Error { + fn from(_: bitcoin::ecdsa::Error) -> Self { + Error::from(Proto::Error::Error_invalid_ecdsa_signature) + } +} + +impl From for Error { + fn from(_: bitcoin::taproot::Error) -> Self { + Error::from(Proto::Error::Error_invalid_schnorr_signature) + } +} + +// Convenience aliases. +#[rustfmt::skip] +pub mod aliases { + use super::Proto; + + pub type ProtoOutputRecipient<'a> = Proto::mod_Output::OneOfto_recipient<'a>; + pub type ProtoOutputBuilder<'a> = Proto::mod_Output::mod_OutputBuilder::OneOfvariant<'a>; + pub type ProtoOutputRedeemScriptOrHashBuilder<'a> = Proto::mod_Output::mod_OutputRedeemScriptOrHash::OneOfvariant<'a>; + pub type ProtoPubkeyOrHash<'a> = Proto::mod_ToPublicKeyOrHash::OneOfto_address<'a>; + pub type ProtoRedeemScriptOrHash<'a> = Proto::mod_Output::mod_OutputRedeemScriptOrHash::OneOfvariant<'a>; + pub type ProtoInputRecipient<'a> = Proto::mod_Input::OneOfto_recipient<'a>; + pub type ProtoInputBuilder<'a> = Proto::mod_Input::mod_InputBuilder::OneOfvariant<'a>; } diff --git a/rust/tw_bitcoin/src/modules/legacy/build_and_sign.rs b/rust/tw_bitcoin/src/modules/legacy/build_and_sign.rs new file mode 100644 index 00000000000..04cbe0d8d92 --- /dev/null +++ b/rust/tw_bitcoin/src/modules/legacy/build_and_sign.rs @@ -0,0 +1,243 @@ +use crate::aliases::*; +use crate::{Error, Result}; +use bitcoin::absolute::LockTime; +use bitcoin::taproot::{LeafVersion, NodeInfo, TaprootSpendInfo}; +use bitcoin::{Network, PrivateKey, PublicKey, ScriptBuf}; +use secp256k1::XOnlyPublicKey; +use tw_coin_entry::coin_entry::CoinEntry; +use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_encoding::hex; +use tw_misc::traits::ToBytesVec; +use tw_proto::Bitcoin::Proto as LegacyProto; +use tw_proto::BitcoinV2::Proto; +use tw_proto::Common::Proto as CommonProto; +use tw_proto::Utxo::Proto as UtxoProto; + +// Builds a Taproot transaction for the legacy protobuf structure, as used by +// `tw_bitcoin_legacy_taproot_build_and_sign_transaction` in the +// `wallet-core-rs` crate. +pub fn taproot_build_and_sign_transaction( + legacy: LegacyProto::SigningInput, +) -> Result { + // Convert the appropriate lock time. + let native_lock_time = LockTime::from_consensus(legacy.lock_time); + let lock_time = match native_lock_time { + LockTime::Blocks(blocks) => UtxoProto::LockTime { + variant: UtxoProto::mod_LockTime::OneOfvariant::blocks(blocks.to_consensus_u32()), + }, + LockTime::Seconds(seconds) => UtxoProto::LockTime { + variant: UtxoProto::mod_LockTime::OneOfvariant::seconds(seconds.to_consensus_u32()), + }, + }; + + // Prepare the inputs. + let mut inputs = vec![]; + for (index, utxo) in legacy.utxo.iter().enumerate() { + // We try to fetch the private key in the `private_key` fields by the + // corresponding index. If there is none, we default to the first + // provided key (implying that one single private key is used for all + // inputs). + let private_key = if let Some(private_key) = legacy.private_key.get(index) { + private_key + } else { + legacy + .private_key + .get(0) + .ok_or_else(|| Error::from(Proto::Error::Error_legacy_no_private_key))? + }; + + let private_key = PrivateKey::from_slice(private_key, Network::Bitcoin)?; + let my_pubkey = private_key.public_key(&secp256k1::Secp256k1::new()); + + let mut input = input_from_legacy_utxo(my_pubkey, utxo, legacy.hash_type)?; + input.private_key = private_key.to_bytes().into(); + inputs.push(input); + } + + // We skip any sort of builders and use the provided scripts directly. + let mut outputs = vec![]; + for output in legacy + .plan + .ok_or_else(|| Error::from(Proto::Error::Error_legacy_no_plan_provided))? + .utxos + { + outputs.push(Proto::Output { + value: output.amount as u64, + to_recipient: ProtoOutputRecipient::custom_script_pubkey(output.script), + }) + } + + // We only select enough inputs to cover the output balance. However, since + // some transaction types require precise input ordering (such as BRC20), we + // do not sort the inputs and use the ordering as provided by the caller. + let input_selector = UtxoProto::InputSelector::SelectInOrder; + + // The primary payload. + let signing_input = Proto::SigningInput { + version: 2, + private_key: legacy + .private_key + .get(0) + .map(|pk| pk.to_vec().into()) + .unwrap_or_default(), + lock_time: Some(lock_time), + inputs, + outputs, + input_selector, + fee_per_vb: legacy.byte_fee as u64, + change_output: None, + disable_change_output: true, + dangerous_use_fixed_schnorr_rng: false, + }; + + // Build and sign the Bitcoin transaction. + let signed = crate::entry::BitcoinEntry.sign(&EmptyCoinContext, signing_input); + + // Check for error. + if signed.error != Proto::Error::OK { + return Err(Error::from(signed.error)); + } + + let transaction = signed + .transaction + .expect("transaction not returned from signer"); + + // Convert the returned transaction data into the (legacy) `Transaction` + // protobuf from `Bitcoin.proto`. + let legacy_transaction = LegacyProto::Transaction { + version: 2, + lockTime: native_lock_time.to_consensus_u32(), + inputs: transaction + .inputs + .iter() + .map(|input| LegacyProto::TransactionInput { + previousOutput: Some(LegacyProto::OutPoint { + hash: input.txid.clone(), + index: input.vout, + sequence: input.sequence, + // Unused for Bitcoin + tree: Default::default(), + }), + // Notr: Not sure why this exists twice? + sequence: input.sequence, + script: input.script_sig.clone(), + }) + .collect(), + outputs: transaction + .outputs + .iter() + .map(|output| LegacyProto::TransactionOutput { + value: output.value as i64, + script: output.script_pubkey.clone(), + spendingScript: output.taproot_payload.clone(), + }) + .collect(), + }; + + let txid_hex = hex::encode(signed.txid.as_ref(), false); + + // Put the `Transaction` into the `SigningOutput`, return. + let legacy_output = LegacyProto::SigningOutput { + transaction: Some(legacy_transaction), + encoded: signed.encoded, + transaction_id: txid_hex.into(), + error: CommonProto::SigningError::OK, + error_message: Default::default(), + }; + + Ok(legacy_output) +} + +/// Convenience function. +fn input_from_legacy_utxo( + my_pubkey: PublicKey, + utxo: &LegacyProto::UnspentTransaction, + hash_type: u32, +) -> Result> { + // We identify the provided `Variant` and invoke the builder function. We + // explicitly skip/ignore any provided script in the input. + let input_builder = match utxo.variant { + LegacyProto::TransactionVariant::P2PKH => Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2pkh(my_pubkey.to_bytes().into()), + }, + LegacyProto::TransactionVariant::P2WPKH => Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(my_pubkey.to_bytes().into()), + }, + LegacyProto::TransactionVariant::P2TRKEYPATH => Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2tr_key_path(Proto::mod_Input::InputTaprootKeyPath { + one_prevout: false, + public_key: my_pubkey.to_bytes().into(), + }), + }, + LegacyProto::TransactionVariant::BRC20TRANSFER + | LegacyProto::TransactionVariant::NFTINSCRIPTION => { + // The spending script must to be empty. + if utxo.spendingScript.is_empty() { + return Err(Error::from( + Proto::Error::Error_legacy_no_spending_script_provided, + )); + } + + let spending_script = ScriptBuf::from_bytes(utxo.spendingScript.to_vec()); + + let xonly = XOnlyPublicKey::from(my_pubkey.inner); + let spend_info = TaprootSpendInfo::from_node_info( + &secp256k1::Secp256k1::new(), + xonly, + NodeInfo::new_leaf_with_ver(spending_script.clone(), LeafVersion::TapScript), + ); + + let control_block = spend_info + .control_block(&(spending_script, LeafVersion::TapScript)) + .expect("failed to construct control block"); + + Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2tr_script_path( + Proto::mod_Input::InputTaprootScriptPath { + one_prevout: false, + payload: utxo.spendingScript.to_vec().into(), + control_block: control_block.serialize().into(), + }, + ), + } + }, + }; + + // Convert the integer indicating the sighash type into the corresponding + // Utxo variant. + let sighash_type = match hash_type { + 0 => UtxoProto::SighashType::UseDefault, + 1 => UtxoProto::SighashType::All, + 2 => UtxoProto::SighashType::None_pb, + 3 => UtxoProto::SighashType::Single, + 129 => UtxoProto::SighashType::AllPlusAnyoneCanPay, + 130 => UtxoProto::SighashType::NonePlusAnyoneCanPay, + 131 => UtxoProto::SighashType::SinglePlusAnyoneCanPay, + _ => return Err(Error::from(Proto::Error::Error_utxo_invalid_sighash_type)), + }; + + // Construct Input and return. + let out_point = utxo + .out_point + .as_ref() + .ok_or_else(|| Error::from(Proto::Error::Error_legacy_outpoint_not_set))?; + + // We explicitly disable zero-valued sequences for legacy and default to + // `0xFFFFFFFF`' + let sequence = if out_point.sequence == 0 { + u32::MAX + } else { + out_point.sequence + }; + + Ok(Proto::Input { + private_key: Default::default(), + txid: out_point.hash.to_vec().into(), + vout: out_point.index, + value: utxo.amount as u64, + sequence, + sequence_enable_zero: false, + sighash_type, + to_recipient: ProtoInputRecipient::builder(input_builder), + }) +} diff --git a/rust/tw_bitcoin/src/modules/legacy/mod.rs b/rust/tw_bitcoin/src/modules/legacy/mod.rs new file mode 100644 index 00000000000..b488c44f4a6 --- /dev/null +++ b/rust/tw_bitcoin/src/modules/legacy/mod.rs @@ -0,0 +1,6 @@ +#![allow(clippy::missing_safety_doc)] + +pub mod build_and_sign; + +// Re-exports +pub use build_and_sign::*; diff --git a/rust/tw_bitcoin/src/modules/mod.rs b/rust/tw_bitcoin/src/modules/mod.rs new file mode 100644 index 00000000000..ff6024a4895 --- /dev/null +++ b/rust/tw_bitcoin/src/modules/mod.rs @@ -0,0 +1,5 @@ +pub mod legacy; +pub mod plan_builder; +pub mod signer; +pub mod transactions; +mod utils; diff --git a/rust/tw_bitcoin/src/modules/plan_builder.rs b/rust/tw_bitcoin/src/modules/plan_builder.rs new file mode 100644 index 00000000000..fd8eb880cad --- /dev/null +++ b/rust/tw_bitcoin/src/modules/plan_builder.rs @@ -0,0 +1,230 @@ +use crate::modules::utils::hard_clone_proto_output; +use crate::{aliases::*, pre_processor, BitcoinEntry}; +use crate::{Error, Result}; +use tw_coin_entry::coin_entry::CoinEntry; +use tw_coin_entry::modules::plan_builder::PlanBuilder; +use tw_coin_entry::signing_output_error; +use tw_proto::BitcoinV2::Proto; +use tw_proto::BitcoinV2::Proto::mod_Input::InputBrc20Inscription; +use tw_proto::Utxo::Proto as UtxoProto; + +pub struct BitcoinPlanBuilder; + +impl PlanBuilder for BitcoinPlanBuilder { + type SigningInput<'a> = Proto::ComposePlan<'a>; + type Plan = Proto::TransactionPlan<'static>; + + #[inline] + fn plan( + &self, + _coin: &dyn tw_coin_entry::coin_context::CoinContext, + proto: Self::SigningInput<'_>, + ) -> Self::Plan { + self.plan_impl(_coin, proto) + .unwrap_or_else(|err| signing_output_error!(Proto::TransactionPlan, err)) + } +} + +impl BitcoinPlanBuilder { + fn plan_impl( + &self, + _coin: &dyn tw_coin_entry::coin_context::CoinContext, + proto: Proto::ComposePlan<'_>, + ) -> Result> { + let plan = match proto.compose { + Proto::mod_ComposePlan::OneOfcompose::brc20(plan) => { + let built_plan = self.plan_brc20(_coin, plan)?; + + Proto::TransactionPlan { + error: Proto::Error::OK, + error_message: Default::default(), + plan: Proto::mod_TransactionPlan::OneOfplan::brc20(built_plan), + } + }, + _ => panic!(), + }; + + Ok(plan) + } + fn plan_brc20( + &self, + _coin: &dyn tw_coin_entry::coin_context::CoinContext, + proto: Proto::mod_ComposePlan::ComposeBrc20Plan<'_>, + ) -> Result> { + // Hard-clones + let inscription = proto + .inscription + .ok_or_else(|| Error::from(Proto::Error::Error_missing_inscription))?; + + let brc20_info = InputBrc20Inscription { + one_prevout: inscription.one_prevout, + inscribe_to: inscription.inscribe_to.to_vec().into(), + ticker: inscription.ticker.to_string().into(), + transfer_amount: inscription.transfer_amount, + }; + + let tagged_output = super::utils::hard_clone_proto_output( + proto + .tagged_output + .ok_or_else(|| Error::from(Proto::Error::Error_missing_tagged_output))?, + )?; + + // First, we create the reveal transaction in order to calculate its input requirement (fee + dust limit). + + // We can use a zeroed Txid here. + let txid = vec![0; 32]; + let dummy_brc20_input = Proto::Input { + txid: txid.into(), + // The value is not relevant here, but we raise it above the output + // or we get an error. + value: u64::MAX, + sighash_type: UtxoProto::SighashType::UseDefault, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::brc20_inscribe(brc20_info.clone()), + }), + ..Default::default() + }; + + let dummy_reveal = Proto::SigningInput { + inputs: vec![dummy_brc20_input], + outputs: vec![tagged_output.clone()], + input_selector: UtxoProto::InputSelector::UseAll, + // Disable change output creation. + fee_per_vb: proto.fee_per_vb, + disable_change_output: true, + ..Default::default() + }; + + // We can now determine the fee of the reveal transaction. + let dummy_presigned = BitcoinEntry.preimage_hashes(_coin, dummy_reveal); + if dummy_presigned.error != Proto::Error::OK { + return Err(Error::from(dummy_presigned.error)); + } + + assert_eq!(dummy_presigned.error, Proto::Error::OK); + let reveal_fee_estimate = dummy_presigned.fee_estimate; + + // Create the BRC20 output for the COMMIT transaction; we set the + // amount to the estimated fee (REVEAL) plus the dust limit (`tagged_output.value`). + let brc20_output = Proto::Output { + value: reveal_fee_estimate + tagged_output.value, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::brc20_inscribe( + Proto::mod_Output::OutputBrc20Inscription { + inscribe_to: brc20_info.inscribe_to.to_vec().into(), + ticker: brc20_info.ticker.to_string().into(), + transfer_amount: brc20_info.transfer_amount, + }, + ), + }), + }; + + let brc20_output_value = brc20_output.value; + + // Clone the change output, if provided. + let change_output = if let Some(change) = proto.change_output { + Some(super::utils::hard_clone_proto_output(change)?) + } else { + None + }; + + // Create the full COMMIT transaction with the appropriately selected inputs. + let commit_signing = Proto::SigningInput { + private_key: proto.private_key.to_vec().into(), + inputs: proto + .inputs + .iter() + .cloned() + .map(super::utils::hard_clone_proto_input) + .collect::>()?, + outputs: vec![brc20_output], + input_selector: proto.input_selector, + change_output: change_output.clone(), + fee_per_vb: proto.fee_per_vb, + disable_change_output: proto.disable_change_output, + ..Default::default() + }; + + let mut commit_signing = pre_processor(commit_signing); + + // We now determine the Txid of the COMMIT transaction, which we will have + // to use in the REVEAL transaction. + let presigned = BitcoinEntry.preimage_hashes(_coin, commit_signing.clone()); + if presigned.error != Proto::Error::OK { + return Err(Error::from(presigned.error)); + } + + assert_eq!(presigned.error, Proto::Error::OK); + let commit_txid: Vec = presigned.txid.to_vec().iter().copied().rev().collect(); + + // Create a list of the selected input Txids, as indicated by the + // `InputSelector`. + let selected_txids: Vec<_> = presigned + .utxo_inputs + .iter() + .map(|utxo| utxo.txid.clone()) + .collect(); + + // Create the list of selected inputs and update the COMMIT transaction. + let selected_inputs: Vec<_> = proto + .inputs + .into_iter() + .filter(|input| selected_txids.contains(&input.txid)) + .map(super::utils::hard_clone_proto_input) + .collect::>()?; + + commit_signing.inputs = selected_inputs; + + // Update the change amount to calculated amount. + if !proto.disable_change_output && presigned.utxo_outputs.len() == 2 { + let change_amount = presigned + .utxo_outputs + .last() + .expect("No Utxo outputs generated") + .value; + + let mut change_output = change_output.expect("change output expected"); + change_output.value = change_amount; + + commit_signing + .outputs + .push(hard_clone_proto_output(change_output)?); + } + + commit_signing.input_selector = UtxoProto::InputSelector::UseAll; + commit_signing.disable_change_output = true; + commit_signing.fee_per_vb = 0; + commit_signing.change_output = Default::default(); + + // Now we construct the *actual* REVEAL transaction. + + let brc20_input = Proto::Input { + value: brc20_output_value, + txid: commit_txid.into(), // Reference COMMIT transaction. + sighash_type: UtxoProto::SighashType::UseDefault, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::brc20_inscribe(brc20_info.clone()), + }), + ..Default::default() + }; + + // Build the REVEAL transaction. + let reveal_signing = Proto::SigningInput { + private_key: proto.private_key.to_vec().into(), + inputs: vec![brc20_input], + outputs: vec![tagged_output], + input_selector: UtxoProto::InputSelector::UseAll, + change_output: Default::default(), + fee_per_vb: 0, + disable_change_output: true, + ..Default::default() + }; + + let reveal_signing = pre_processor(reveal_signing); + + Ok(Proto::mod_TransactionPlan::Brc20Plan { + commit: Some(commit_signing), + reveal: Some(reveal_signing), + }) + } +} diff --git a/rust/tw_bitcoin/src/modules/signer.rs b/rust/tw_bitcoin/src/modules/signer.rs new file mode 100644 index 00000000000..cdf17b68eef --- /dev/null +++ b/rust/tw_bitcoin/src/modules/signer.rs @@ -0,0 +1,159 @@ +use crate::{BitcoinEntry, Error, Result}; +use bitcoin::key::{TapTweak, TweakedKeyPair}; +use bitcoin::sighash::{EcdsaSighashType, TapSighashType}; +use secp256k1::{KeyPair, Message, Secp256k1}; +use std::collections::HashMap; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{PrivateKeyBytes, SignatureBytes}; +use tw_misc::traits::ToBytesVec; +use tw_proto::BitcoinV2::Proto; +use tw_proto::Utxo::Proto as UtxoProto; + +pub struct Signer; + +impl Signer { + pub fn sign_proto( + _coin: &dyn CoinContext, + proto: Proto::SigningInput<'_>, + ) -> Result> { + // Technically not required here, since this gets called by + // `preimage_hashes_impl` and `compile_impl`. But we're leaving this + // here in case this methods gets extended and the pre-processing does + // not get accidentally forgotten. + let proto = crate::entry::pre_processor(proto); + + // Collect individual private keys per input, if there are any. + let mut individual_keys = HashMap::new(); + for (index, txin) in proto.inputs.iter().enumerate() { + if !txin.private_key.is_empty() { + individual_keys.insert(index, txin.private_key.to_vec()); + } + } + + // Generate the sighashes. + let pre_signed = BitcoinEntry.preimage_hashes_impl(_coin, proto.clone())?; + + // Check for error. + if pre_signed.error != Proto::Error::OK { + return Err(Error::from(pre_signed.error)); + } + + // Sign the sighashes. + let signatures = crate::modules::signer::Signer::signatures_from_proto( + &pre_signed, + proto.private_key.to_vec(), + individual_keys, + proto.dangerous_use_fixed_schnorr_rng, + )?; + + // Construct the final transaction. + BitcoinEntry.compile_impl(_coin, proto, signatures, vec![]) + } + pub fn signatures_from_proto( + input: &Proto::PreSigningOutput<'_>, + private_key: PrivateKeyBytes, + individual_keys: HashMap, + dangerous_use_fixed_schnorr_rng: bool, + ) -> Result> { + let secp = Secp256k1::new(); + + let mut signatures = vec![]; + + for (index, (entry, utxo)) in input + .sighashes + .iter() + .zip(input.utxo_inputs.iter()) + .enumerate() + { + // Check if there's an individual private key for the given input. If not, use the primary one. + let keypair = if let Some(slice) = individual_keys.get(&index) { + KeyPair::from_seckey_slice(&secp, slice) + .map_err(|_| Error::from(Proto::Error::Error_invalid_private_key))? + } else { + KeyPair::from_seckey_slice(&secp, private_key.as_ref()) + .map_err(|_| Error::from(Proto::Error::Error_invalid_private_key))? + }; + + // Create signable message from sighash. + let sighash = Message::from_slice(entry.sighash.as_ref()) + .map_err(|_| Error::from(Proto::Error::Error_invalid_sighash))?; + + // Sign the sighash depending on signing method. + match entry.signing_method { + // Create a ECDSA signature for legacy and segwit transaction. + UtxoProto::SigningMethod::Legacy | UtxoProto::SigningMethod::Segwit => { + let sighash_type = + if let UtxoProto::SighashType::UseDefault = entry.sighash_type { + EcdsaSighashType::All + } else { + EcdsaSighashType::from_consensus(entry.sighash_type as u32) + }; + + let sig = bitcoin::ecdsa::Signature { + sig: keypair.secret_key().sign_ecdsa(sighash), + hash_ty: sighash_type, + }; + + signatures.push(sig.serialize().to_vec()); + }, + // Create a Schnorr signature for taproot transactions. + UtxoProto::SigningMethod::TaprootAll + | UtxoProto::SigningMethod::TaprootOnePrevout => { + // Note that `input.sighash_type = 0` is handled by the underlying library. + let sighash_type = TapSighashType::from_consensus_u8(entry.sighash_type as u8) + .map_err(|_| Error::from(Proto::Error::Error_utxo_invalid_sighash_type))?; + + // Any empty leaf hash implies P2TR key-path (balance transfer) + if utxo.leaf_hash.is_empty() { + // Tweak keypair for P2TR key-path (ie. zeroed Merkle root). + let tapped: TweakedKeyPair = keypair.tap_tweak(&secp, None); + let tweaked = KeyPair::from(tapped); + + // Construct the Schnorr signature. + let schnorr = if dangerous_use_fixed_schnorr_rng { + // For tests, we disable the included randomness in order to create + // reproducible signatures. Randomness should ALWAYS be used in + // production. + secp.sign_schnorr_no_aux_rand(&sighash, &tweaked) + } else { + secp.sign_schnorr(&sighash, &tweaked) + }; + + let sig = bitcoin::taproot::Signature { + sig: schnorr, + hash_ty: sighash_type, + }; + + signatures.push(sig.to_vec()); + } + // If it has a leaf hash, then it's a P2TR script-path (complex transaction) + else { + // NOTE: We do not tweak the key here since the complex + // spending condition(s) must take into account on who + // is allowed to spend the input, hence this signing + // process is simpler than for P2TR key-path. + + // Construct the Schnorr signature. + let schnorr = if dangerous_use_fixed_schnorr_rng { + // For tests, we disable the included randomness in order to create + // reproducible signatures. Randomness should ALWAYS be used in + // production. + secp.sign_schnorr_no_aux_rand(&sighash, &keypair) + } else { + secp.sign_schnorr(&sighash, &keypair) + }; + + let sig = bitcoin::taproot::Signature { + sig: schnorr, + hash_ty: sighash_type, + }; + + signatures.push(sig.to_vec()); + } + }, + } + } + + Ok(signatures) + } +} diff --git a/rust/tw_bitcoin/src/modules/transactions/brc20.rs b/rust/tw_bitcoin/src/modules/transactions/brc20.rs new file mode 100644 index 00000000000..51afda68777 --- /dev/null +++ b/rust/tw_bitcoin/src/modules/transactions/brc20.rs @@ -0,0 +1,93 @@ +use super::ordinals::OrdinalsInscription; +use crate::{Error, Result}; +use bitcoin::PublicKey; +use serde::Serialize; +use tw_proto::BitcoinV2::Proto; + +#[derive(Debug, Clone, Serialize)] +pub struct Brc20Ticker(String); + +impl Brc20Ticker { + pub fn new(string: String) -> Result { + // Brc20Ticker must be a 4-letter identifier. + if string.len() != 4 { + return Err(Error::from(Proto::Error::Error_invalid_brc20_ticker)); + } + + Ok(Brc20Ticker(string)) + } +} + +#[derive(Serialize)] +struct BRC20TransferPayload { + #[serde(rename = "p")] + protocol: String, + #[serde(rename = "op")] + operation: String, + #[serde(rename = "tick")] + ticker: Brc20Ticker, + #[serde(rename = "amt")] + amount: String, +} + +impl BRC20TransferPayload { + const PROTOCOL_ID: &str = "brc-20"; + const MIME: &[u8] = b"text/plain;charset=utf-8"; +} + +impl BRC20TransferPayload { + const OPERATION: &str = "transfer"; + + fn new(ticker: Brc20Ticker, value: u64) -> Self { + BRC20TransferPayload { + protocol: Self::PROTOCOL_ID.to_string(), + operation: Self::OPERATION.to_string(), + ticker, + amount: value.to_string(), + } + } +} + +pub struct BRC20TransferInscription(OrdinalsInscription); + +impl BRC20TransferInscription { + pub fn new( + recipient: PublicKey, + ticker: Brc20Ticker, + value: u64, + ) -> Result { + let data: BRC20TransferPayload = BRC20TransferPayload::new(ticker, value); + + let inscription = OrdinalsInscription::new( + BRC20TransferPayload::MIME, + &serde_json::to_vec(&data).expect("badly constructed BRC20 payload"), + recipient, + )?; + + Ok(BRC20TransferInscription(inscription)) + } + pub fn inscription(&self) -> &OrdinalsInscription { + &self.0 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn brc20_ticker_validity() { + // Must be four characters. + let ticker = Brc20Ticker::new("invalid".to_string()); + assert!(ticker.is_err()); + + let ticker = Brc20Ticker::new("asdf".to_string()); + assert!(ticker.is_ok()); + + // Cover clone implemenation. + let ticker = ticker.unwrap(); + + let _cloned = ticker.clone(); + let _ticker = ticker; + } +} diff --git a/rust/tw_bitcoin/src/modules/transactions/input_builder.rs b/rust/tw_bitcoin/src/modules/transactions/input_builder.rs new file mode 100644 index 00000000000..4d1660cb61f --- /dev/null +++ b/rust/tw_bitcoin/src/modules/transactions/input_builder.rs @@ -0,0 +1,320 @@ +use super::brc20::{BRC20TransferInscription, Brc20Ticker}; +use crate::aliases::*; +use crate::modules::transactions::OrdinalNftInscription; +use crate::{Error, Result}; +use bitcoin::taproot::{LeafVersion, TapLeafHash}; +use bitcoin::ScriptBuf; +use secp256k1::XOnlyPublicKey; +use tw_misc::traits::ToBytesVec; +use tw_proto::BitcoinV2::Proto; +use tw_proto::Utxo::Proto as UtxoProto; + +// Convenience varibles used solely for readability. +const NO_LEAF_HASH: Option = None; + +pub struct InputBuilder; + +impl InputBuilder { + pub fn utxo_from_proto(input: &Proto::Input<'_>) -> Result> { + let (signing_method, script_pubkey, leaf_hash, weight) = match &input.to_recipient { + ProtoInputRecipient::builder(builder) => match &builder.variant { + ProtoInputBuilder::p2sh(redeem_script) => { + // The scriptPubkey is the redeem script directly. + let script_pubkey = ScriptBuf::from_bytes(redeem_script.to_vec()); + + ( + UtxoProto::SigningMethod::Legacy, + script_pubkey, + NO_LEAF_HASH, + // scale factor applied to non-witness bytes + 4 * ( + // length + redeem script. + 1 + redeem_script.len() as u64 + ), + ) + }, + ProtoInputBuilder::p2pkh(pubkey) => { + let pubkey = bitcoin::PublicKey::from_slice(pubkey.as_ref())?; + let script_pubkey = ScriptBuf::new_p2pkh(&pubkey.pubkey_hash()); + + ( + UtxoProto::SigningMethod::Legacy, + script_pubkey, + NO_LEAF_HASH, + // scale factor applied to non-witness bytes + 4 * ( + // length + ECDSA signature + 1 + 72 + + // length + public key + 1 + { + if pubkey.compressed { + 33 + } else { + 65 + } + } + ), + ) + }, + ProtoInputBuilder::p2wsh(redeem_script) => { + // The scriptPubkey is the redeem script directly. + let script_pubkey = ScriptBuf::from_bytes(redeem_script.to_vec()); + + ( + UtxoProto::SigningMethod::Segwit, + script_pubkey, + NO_LEAF_HASH, + // witness bytes, scale factor NOT applied. + ( + // length + redeem script. + 1 + redeem_script.len() as u64 + ), + ) + }, + ProtoInputBuilder::p2wpkh(pubkey) => { + let pubkey = bitcoin::PublicKey::from_slice(pubkey.as_ref())?; + + let script_pubkey = + ScriptBuf::new_v0_p2wpkh(&pubkey.wpubkey_hash().ok_or_else(|| { + Error::from(Proto::Error::Error_invalid_witness_pubkey_hash) + })?) + // Special script code requirement for claiming P2WPKH outputs. + .p2wpkh_script_code() + .ok_or_else(|| Error::from(Proto::Error::Error_invalid_wpkh_script_code))?; + + ( + UtxoProto::SigningMethod::Segwit, + script_pubkey, + NO_LEAF_HASH, + // witness bytes, scale factor NOT applied. + ( + // indicator of witness item (2) + 1 + + // length + ECDSA signature (can be 71 or 72) + 1 + 72 + + // length + public key + 1 + { + if pubkey.compressed { + 33 + } else { + 65 + } + } + ), + ) + }, + ProtoInputBuilder::p2tr_key_path(key_path) => { + let pubkey = bitcoin::PublicKey::from_slice(key_path.public_key.as_ref())?; + let xonly = XOnlyPublicKey::from(pubkey.inner); + + let signing_method = if key_path.one_prevout { + UtxoProto::SigningMethod::TaprootOnePrevout + } else { + UtxoProto::SigningMethod::TaprootAll + }; + + let script_pubkey = + ScriptBuf::new_v1_p2tr(&secp256k1::Secp256k1::new(), xonly, None); + + ( + signing_method, + script_pubkey, + NO_LEAF_HASH, + // witness bytes, scale factor NOT applied. + ( + // indicator of witness item (1) + 1 + + // length + Schnorr signature (can be 71 or 72) + 1 + 72 + // NO public key + ), + ) + }, + ProtoInputBuilder::p2tr_script_path(complex) => { + let script_pubkey = ScriptBuf::from_bytes(complex.payload.to_vec()); + let leaf_hash = Some(TapLeafHash::from_script( + script_pubkey.as_script(), + bitcoin::taproot::LeafVersion::TapScript, + )); + + let signing_method = if complex.one_prevout { + UtxoProto::SigningMethod::TaprootOnePrevout + } else { + UtxoProto::SigningMethod::TaprootAll + }; + + ( + signing_method, + script_pubkey, + leaf_hash, + // witness bytes, scale factor NOT applied. + ( + // indicator of witness item + 1 + + // the payload/witness + complex.payload.len() as u64 + ), + ) + }, + ProtoInputBuilder::ordinal_inscribe(ordinal) => { + let pubkey = bitcoin::PublicKey::from_slice(ordinal.inscribe_to.as_ref())?; + let mime_type = ordinal.mime_type.as_ref(); + let data = ordinal.payload.as_ref(); + + let nft = OrdinalNftInscription::new(mime_type.as_bytes(), data, pubkey) + .expect("badly constructed Ordinal inscription"); + + // We construct a control block to estimate the fee, + // otherwise we do not need it here. + let control_block = nft + .inscription() + .spend_info() + .control_block(&( + nft.inscription().taproot_program().to_owned(), + LeafVersion::TapScript, + )) + .expect("badly constructed control block"); + + let leaf_hash = Some(TapLeafHash::from_script( + nft.inscription().taproot_program(), + bitcoin::taproot::LeafVersion::TapScript, + )); + + let signing_method = if ordinal.one_prevout { + UtxoProto::SigningMethod::TaprootOnePrevout + } else { + UtxoProto::SigningMethod::TaprootAll + }; + + let script_pubkey = ScriptBuf::from(nft.inscription().taproot_program()); + + ( + signing_method, + // TODO: This is technically not needed here, since + // Sighash only includes the leaf hash, not the actual + // payload. Remove this (same for other complex scripts). + script_pubkey, + leaf_hash, + // witness bytes, scale factor NOT applied. + ( + // indicator of witness item (1) + 1 + + // length + Schnorr signature (can be 71 or 72) + 1 + 72 + + // the payload/witness + nft.inscription().taproot_program().len() as u64 + + // length + control block + 1 + control_block.size() as u64 + ), + ) + }, + // TODO: Unify this and `ordinal_inscribe` somehow + ProtoInputBuilder::brc20_inscribe(brc20) => { + let pubkey = bitcoin::PublicKey::from_slice(brc20.inscribe_to.as_ref())?; + let ticker = Brc20Ticker::new(brc20.ticker.to_string())?; + + let transfer = + BRC20TransferInscription::new(pubkey, ticker, brc20.transfer_amount) + .expect("invalid BRC20 transfer construction"); + + // We construct a control block to estimate the fee, + // otherwise we do not need it here. + let control_block = transfer + .inscription() + .spend_info() + .control_block(&( + transfer.inscription().taproot_program().to_owned(), + LeafVersion::TapScript, + )) + .expect("badly constructed control block"); + + let leaf_hash = Some(TapLeafHash::from_script( + transfer.inscription().taproot_program(), + bitcoin::taproot::LeafVersion::TapScript, + )); + + let signing_method = if brc20.one_prevout { + UtxoProto::SigningMethod::TaprootOnePrevout + } else { + UtxoProto::SigningMethod::TaprootAll + }; + + let script_pubkey = ScriptBuf::from(transfer.inscription().taproot_program()); + + ( + signing_method, + script_pubkey, + leaf_hash, + // witness bytes, scale factor NOT applied. + ( + // indicator of witness item (1) + 1 + + // length + Schnorr signature (can be 71 or 72) + 1 + 72 + + // the payload/witness + transfer.inscription().taproot_program().len() as u64 + + // length + control block + 1 + control_block.size() as u64 + ), + ) + }, + ProtoInputBuilder::None => { + return Err(Error::from(Proto::Error::Error_missing_input_builder)) + }, + }, + ProtoInputRecipient::custom_script(custom) => { + let script_pubkey = ScriptBuf::from_bytes(custom.script_pubkey.to_vec()); + + #[rustfmt::skip] + let leaf_hash = if let UtxoProto::SigningMethod::TaprootAll + | UtxoProto::SigningMethod::TaprootOnePrevout = custom.signing_method + { + Some(TapLeafHash::from_script( + &script_pubkey, + bitcoin::taproot::LeafVersion::TapScript, + )) + } else { + None + }; + + ( + custom.signing_method, + script_pubkey, + leaf_hash, + ( + // scale factor applied to non-witness bytes + 4 * custom.script_sig.len() as u64 + // indicator of witness item count. + + 1 + // witness bytes, scale factor NOT applied. + + custom + .witness_items + .iter() + .map(|item| item.len() as u64) + .sum::() + ), + ) + }, + ProtoInputRecipient::None => { + return Err(Error::from(Proto::Error::Error_missing_input_builder)) + }, + }; + + // Create Utxo.proto structure. + let utxo = UtxoProto::TxIn { + txid: input.txid.to_vec().into(), + vout: input.vout, + value: input.value, + sequence: input.sequence, + script_pubkey: script_pubkey.to_vec().into(), + signing_method, + sighash_type: input.sighash_type, + weight_estimate: weight, + leaf_hash: leaf_hash + .map(|hash| hash.to_vec().into()) + .unwrap_or_default(), + }; + + Ok(utxo) + } +} diff --git a/rust/tw_bitcoin/src/modules/transactions/input_claim_builder.rs b/rust/tw_bitcoin/src/modules/transactions/input_claim_builder.rs new file mode 100644 index 00000000000..b93269031fc --- /dev/null +++ b/rust/tw_bitcoin/src/modules/transactions/input_claim_builder.rs @@ -0,0 +1,172 @@ +use super::brc20::{BRC20TransferInscription, Brc20Ticker}; +use super::OrdinalNftInscription; +use crate::aliases::*; +use crate::{Error, Result}; +use bitcoin::consensus::Decodable; +use bitcoin::taproot::{ControlBlock, LeafVersion}; +use bitcoin::{ScriptBuf, Witness}; +use std::borrow::Cow; +use tw_coin_entry::coin_entry::SignatureBytes; +use tw_misc::traits::ToBytesVec; +use tw_proto::BitcoinV2::Proto; +use tw_proto::Utxo::Proto as UtxoProto; + +pub struct InputClaimBuilder; + +impl InputClaimBuilder { + /// Creates the claim script (_scriptSig_ or _Witness_) to be revealed + /// on-chain for a given input. + pub fn utxo_claim_from_proto( + input: &Proto::Input<'_>, + signature: SignatureBytes, + ) -> Result> { + let (script_sig, witness) = match &input.to_recipient { + ProtoInputRecipient::builder(variant) => match &variant.variant { + ProtoInputBuilder::p2sh(redeem_script) => ( + ScriptBuf::from_bytes(redeem_script.to_vec()), + Witness::new(), + ), + ProtoInputBuilder::p2pkh(pubkey) => { + let sig = bitcoin::ecdsa::Signature::from_slice(signature.as_ref())?; + let pubkey = bitcoin::PublicKey::from_slice(pubkey.as_ref())?; + + // The spending script itself. + ( + ScriptBuf::builder() + .push_slice(sig.serialize()) + .push_key(&pubkey) + .into_script(), + Witness::new(), + ) + }, + ProtoInputBuilder::p2wsh(redeem_script) => { + let witness = Witness::consensus_decode(&mut redeem_script.as_ref()) + .map_err(|_| Error::from(Proto::Error::Error_invalid_witness_encoding))?; + + (ScriptBuf::new(), witness) + }, + ProtoInputBuilder::p2wpkh(pubkey) => { + let sig = bitcoin::ecdsa::Signature::from_slice(signature.as_ref())?; + let pubkey = bitcoin::PublicKey::from_slice(pubkey.as_ref())?; + + // The spending script itself. + (ScriptBuf::new(), { + let mut w = Witness::new(); + w.push(sig.serialize()); + w.push(pubkey.to_bytes()); + w + }) + }, + ProtoInputBuilder::p2tr_key_path(_) => { + let sig = bitcoin::taproot::Signature::from_slice(signature.as_ref())?; + + // The spending script itself. + (ScriptBuf::new(), { + let mut w = Witness::new(); + w.push(sig.to_vec()); + w + }) + }, + ProtoInputBuilder::p2tr_script_path(taproot) => { + let control_block = ControlBlock::decode(taproot.control_block.as_ref()) + .map_err(|_| Error::from(Proto::Error::Error_invalid_control_block))?; + + let sig = bitcoin::taproot::Signature::from_slice(signature.as_ref())?; + + // The spending script itself. + (ScriptBuf::new(), { + let mut w = Witness::new(); + w.push(sig.to_vec()); + w.push(taproot.payload.as_ref()); + w.push(control_block.serialize()); + w + }) + }, + ProtoInputBuilder::ordinal_inscribe(ordinal) => { + let pubkey = bitcoin::PublicKey::from_slice(ordinal.inscribe_to.as_ref())?; + let mime_type = ordinal.mime_type.as_ref(); + let data = ordinal.payload.as_ref(); + + let nft = OrdinalNftInscription::new(mime_type.as_bytes(), data, pubkey) + .expect("badly constructed Ordinal inscription"); + + // Create a control block for that inscription. + let control_block = nft + .inscription() + .spend_info() + .control_block(&( + nft.inscription().taproot_program().to_owned(), + LeafVersion::TapScript, + )) + .expect("badly constructed control block"); + + let sig = bitcoin::taproot::Signature::from_slice(signature.as_ref())?; + + // The spending script itself. + (ScriptBuf::new(), { + let mut w = Witness::new(); + w.push(sig.to_vec()); + w.push(nft.inscription().taproot_program()); + w.push(control_block.serialize()); + w + }) + }, + ProtoInputBuilder::brc20_inscribe(brc20) => { + let pubkey = bitcoin::PublicKey::from_slice(brc20.inscribe_to.as_ref())?; + let ticker = Brc20Ticker::new(brc20.ticker.to_string())?; + + // Construct the BRC20 transfer inscription. + let transfer = + BRC20TransferInscription::new(pubkey, ticker, brc20.transfer_amount) + .expect("invalid BRC20 transfer construction"); + + // Create a control block for that inscription. + let control_block = transfer + .inscription() + .spend_info() + .control_block(&( + transfer.inscription().taproot_program().to_owned(), + LeafVersion::TapScript, + )) + .expect("badly constructed control block"); + + let sig = bitcoin::taproot::Signature::from_slice(signature.as_ref())?; + + // The spending script itself. + (ScriptBuf::new(), { + let mut w = Witness::new(); + w.push(sig.to_vec()); + w.push(transfer.inscription().taproot_program()); + w.push(control_block.serialize()); + w + }) + }, + ProtoInputBuilder::None => { + return Err(Error::from(Proto::Error::Error_missing_input_builder)) + }, + }, + ProtoInputRecipient::custom_script(custom) => ( + ScriptBuf::from_bytes(custom.script_sig.to_vec()), + Witness::from_slice(&custom.witness_items), + ), + ProtoInputRecipient::None => { + return Err(Error::from(Proto::Error::Error_missing_recipient)) + }, + }; + + // Create Utxo.proto structure. + let claim = UtxoProto::TxInClaim { + txid: input.txid.to_vec().into(), + vout: input.vout, + sequence: input.sequence, + script_sig: script_sig.to_vec().into(), + witness_items: witness + .to_vec() + .into_iter() + .map(Cow::Owned) + .collect::>>(), + }; + + Ok(claim) + } +} diff --git a/rust/tw_bitcoin/src/modules/transactions/mod.rs b/rust/tw_bitcoin/src/modules/transactions/mod.rs new file mode 100644 index 00000000000..03172284046 --- /dev/null +++ b/rust/tw_bitcoin/src/modules/transactions/mod.rs @@ -0,0 +1,26 @@ +use bitcoin::key::PublicKey; +use bitcoin::script::ScriptBuf; +use bitcoin::taproot::{TapNodeHash, TaprootSpendInfo}; + +mod brc20; +mod input_builder; +mod input_claim_builder; +mod ordinals; +mod output_builder; + +// Re-exports +pub use brc20::{BRC20TransferInscription, Brc20Ticker}; +pub use input_builder::InputBuilder; +pub use input_claim_builder::InputClaimBuilder; +pub use ordinals::{OrdinalNftInscription, OrdinalsInscription}; +pub use output_builder::OutputBuilder; + +pub struct TaprootScript { + pub pubkey: PublicKey, + pub merkle_root: TapNodeHash, +} + +pub struct TaprootProgram { + pub script: ScriptBuf, + pub spend_info: TaprootSpendInfo, +} diff --git a/rust/tw_bitcoin/src/ordinals.rs b/rust/tw_bitcoin/src/modules/transactions/ordinals.rs similarity index 74% rename from rust/tw_bitcoin/src/ordinals.rs rename to rust/tw_bitcoin/src/modules/transactions/ordinals.rs index 35c48a02a0c..76b4a221631 100644 --- a/rust/tw_bitcoin/src/ordinals.rs +++ b/rust/tw_bitcoin/src/modules/transactions/ordinals.rs @@ -1,35 +1,22 @@ -use crate::{Error, Recipient, Result, TaprootProgram, TaprootScript}; +use super::TaprootProgram; +use crate::{Error, Result}; use bitcoin::script::{PushBytesBuf, ScriptBuf}; use bitcoin::secp256k1::XOnlyPublicKey; use bitcoin::taproot::{TaprootBuilder, TaprootSpendInfo}; use bitcoin::{PublicKey, Script}; +use tw_proto::BitcoinV2::Proto; -#[derive(Debug, Clone)] pub struct OrdinalsInscription { envelope: TaprootProgram, - recipient: Recipient, } impl OrdinalsInscription { /// Creates a new Ordinals Inscription ("commit stage"). - pub fn new( - mime: &[u8], - data: &[u8], - recipient: Recipient, - ) -> Result { + pub fn new(mime: &[u8], data: &[u8], recipient: PublicKey) -> Result { // Create the envelope, containing the inscription content. - let envelope = create_envelope(mime, data, recipient.public_key())?; + let envelope = create_envelope(mime, data, recipient)?; - // Compute the merkle root of the inscription. - let merkle_root = envelope - .spend_info - .merkle_root() - .expect("Ordinals envelope not constructed correctly"); - - Ok(OrdinalsInscription { - envelope, - recipient: Recipient::::from_pubkey_recipient(recipient, merkle_root), - }) + Ok(OrdinalsInscription { envelope }) } pub fn taproot_program(&self) -> &Script { self.envelope.script.as_script() @@ -37,9 +24,6 @@ impl OrdinalsInscription { pub fn spend_info(&self) -> &TaprootSpendInfo { &self.envelope.spend_info } - pub fn recipient(&self) -> &Recipient { - &self.recipient - } } /// Creates an [Ordinals Inscription](https://docs.ordinals.com/inscriptions.html). @@ -63,7 +47,9 @@ fn create_envelope(mime: &[u8], data: &[u8], internal_key: PublicKey) -> Result< // Create MIME buffer. let mut mime_buf = PushBytesBuf::new(); - mime_buf.extend_from_slice(mime).map_err(|_| Error::Todo)?; + mime_buf + .extend_from_slice(mime) + .map_err(|_| Error::from(Proto::Error::Error_ordinal_mime_type_too_large))?; // Create an Ordinals Inscription. let mut builder = ScriptBuf::builder() @@ -89,7 +75,9 @@ fn create_envelope(mime: &[u8], data: &[u8], internal_key: PublicKey) -> Result< for chunk in data.chunks(520) { // Create data buffer. let mut data_buf = PushBytesBuf::new(); - data_buf.extend_from_slice(chunk).map_err(|_| Error::Todo)?; + data_buf + .extend_from_slice(chunk) + .map_err(|_| Error::from(Proto::Error::Error_ordinal_payload_too_large))?; // Push buffer builder = builder.push_slice(data_buf); @@ -113,3 +101,25 @@ fn create_envelope(mime: &[u8], data: &[u8], internal_key: PublicKey) -> Result< Ok(TaprootProgram { script, spend_info }) } + +pub struct OrdinalNftInscription(OrdinalsInscription); + +impl OrdinalNftInscription { + // Constructs an [Ordinal inscription] with a given MIME type. Common MIME + // types are: + // * "application/json", + // * "application/pdf", + // * "image/gif", + // * "image/jpeg", + // * "image/png", + // * "text/plain;charset=utf-8" + // * ... + // + // [Ordinal inscription]: https://docs.ordinals.com/inscriptions.html + pub fn new(mime_type: &[u8], data: &[u8], recipient: PublicKey) -> Result { + OrdinalsInscription::new(mime_type, data, recipient).map(OrdinalNftInscription) + } + pub fn inscription(&self) -> &OrdinalsInscription { + &self.0 + } +} diff --git a/rust/tw_bitcoin/src/modules/transactions/output_builder.rs b/rust/tw_bitcoin/src/modules/transactions/output_builder.rs new file mode 100644 index 00000000000..72fef4c0c39 --- /dev/null +++ b/rust/tw_bitcoin/src/modules/transactions/output_builder.rs @@ -0,0 +1,371 @@ +use std::str::FromStr; + +use super::brc20::{BRC20TransferInscription, Brc20Ticker}; +use super::OrdinalNftInscription; +use crate::aliases::*; +use crate::{Error, Result}; +use bitcoin::address::{Payload, WitnessVersion}; +use bitcoin::key::TweakedPublicKey; +use bitcoin::taproot::{LeafVersion, TapNodeHash}; +use bitcoin::{Address, PubkeyHash, ScriptBuf, ScriptHash, WPubkeyHash, WScriptHash}; +use secp256k1::hashes::Hash; +use secp256k1::XOnlyPublicKey; +use tw_misc::traits::ToBytesVec; +use tw_proto::BitcoinV2::Proto; + +pub struct OutputBuilder; + +// Convenience varibles used solely for readability. +const NO_CONTROL_BLOCK: Option> = None; +const NO_TAPROOT_PAYLOAD: Option> = None; + +impl OutputBuilder { + /// Creates the spending condition (_scriptPubkey_) for a given output. + pub fn utxo_from_proto( + output: &Proto::Output<'_>, + ) -> Result> { + let secp = secp256k1::Secp256k1::new(); + + let (script_pubkey, control_block, taproot_payload) = match &output.to_recipient { + // Script spending condition was passed on directly. + ProtoOutputRecipient::custom_script_pubkey(script) => ( + ScriptBuf::from_bytes(script.to_vec()), + NO_CONTROL_BLOCK, + NO_TAPROOT_PAYLOAD, + ), + // Process builder methods. We construct the Script spending + // conditions by using the specified parameters. + ProtoOutputRecipient::builder(builder) => match &builder.variant { + ProtoOutputBuilder::p2sh(script_or_hash) => { + let script_hash = redeem_script_or_hash(script_or_hash)?; + ( + ScriptBuf::new_p2sh(&script_hash), + NO_CONTROL_BLOCK, + NO_TAPROOT_PAYLOAD, + ) + }, + ProtoOutputBuilder::p2pkh(pubkey_or_hash) => { + let pubkey_hash = pubkey_hash_from_proto(pubkey_or_hash)?; + ( + ScriptBuf::new_p2pkh(&pubkey_hash), + NO_CONTROL_BLOCK, + NO_TAPROOT_PAYLOAD, + ) + }, + ProtoOutputBuilder::p2wsh(script_or_hash) => { + let wscript_hash = witness_redeem_script_or_hash(script_or_hash)?; + ( + ScriptBuf::new_v0_p2wsh(&wscript_hash), + NO_CONTROL_BLOCK, + NO_TAPROOT_PAYLOAD, + ) + }, + ProtoOutputBuilder::p2wpkh(pubkey_or_hash) => { + let wpubkey_hash = witness_pubkey_hash_from_proto(pubkey_or_hash)?; + ( + ScriptBuf::new_v0_p2wpkh(&wpubkey_hash), + NO_CONTROL_BLOCK, + NO_TAPROOT_PAYLOAD, + ) + }, + ProtoOutputBuilder::p2tr_key_path(pubkey) => { + let pubkey = bitcoin::PublicKey::from_slice(pubkey.as_ref())?; + let xonly = XOnlyPublicKey::from(pubkey.inner); + ( + ScriptBuf::new_v1_p2tr(&secp, xonly, None), + NO_CONTROL_BLOCK, + NO_TAPROOT_PAYLOAD, + ) + }, + ProtoOutputBuilder::p2tr_script_path(complex) => { + let node_hash = TapNodeHash::from_slice(complex.merkle_root.as_ref()) + .map_err(|_| Error::from(Proto::Error::Error_invalid_taproot_root))?; + + let pubkey = bitcoin::PublicKey::from_slice(complex.internal_key.as_ref())?; + let xonly = XOnlyPublicKey::from(pubkey.inner); + + ( + ScriptBuf::new_v1_p2tr(&secp, xonly, Some(node_hash)), + NO_CONTROL_BLOCK, + NO_TAPROOT_PAYLOAD, + ) + }, + ProtoOutputBuilder::p2tr_dangerous_assume_tweaked(tweaked_pubkey) => { + let xonly = XOnlyPublicKey::from_slice(tweaked_pubkey).map_err(|_| { + Error::from(Proto::Error::Error_invalid_taproot_tweaked_pubkey) + })?; + + let tweaked = TweakedPublicKey::dangerous_assume_tweaked(xonly); + + ( + ScriptBuf::new_v1_p2tr_tweaked(tweaked), + NO_CONTROL_BLOCK, + NO_TAPROOT_PAYLOAD, + ) + }, + ProtoOutputBuilder::ordinal_inscribe(ordinal) => { + let pubkey = bitcoin::PublicKey::from_slice(ordinal.inscribe_to.as_ref())?; + let xonly = XOnlyPublicKey::from(pubkey.inner); + let mime_type = ordinal.mime_type.as_ref(); + let data = ordinal.payload.as_ref(); + + let nft = OrdinalNftInscription::new(mime_type.as_bytes(), data, pubkey) + .expect("badly constructed Ordinal inscription"); + + // Construct the control block. + let control_block = nft + .inscription() + .spend_info() + .control_block(&( + nft.inscription().taproot_program().to_owned(), + LeafVersion::TapScript, + )) + .expect("badly constructed control block"); + + // Construct the merkle root. + let merkle_root = nft + .inscription() + .spend_info() + .merkle_root() + .expect("badly constructed Taproot merkle root"); + + ( + ScriptBuf::new_v1_p2tr(&secp, xonly, Some(merkle_root)), + Some(control_block.serialize()), + Some(nft.inscription().taproot_program().to_vec()), + ) + }, + ProtoOutputBuilder::brc20_inscribe(brc20) => { + let pubkey = bitcoin::PublicKey::from_slice(brc20.inscribe_to.as_ref())?; + let xonly = XOnlyPublicKey::from(pubkey.inner); + + let ticker = Brc20Ticker::new(brc20.ticker.to_string())?; + let transfer = + BRC20TransferInscription::new(pubkey, ticker, brc20.transfer_amount) + .expect("invalid BRC20 transfer construction"); + + // Construct the control block. + let control_block = transfer + .inscription() + .spend_info() + .control_block(&( + transfer.inscription().taproot_program().to_owned(), + LeafVersion::TapScript, + )) + .expect("badly constructed control block"); + + // Construct the merkle root. + let merkle_root = transfer + .inscription() + .spend_info() + .merkle_root() + .expect("badly constructed Taproot merkle root"); + + ( + ScriptBuf::new_v1_p2tr(&secp, xonly, Some(merkle_root)), + Some(control_block.serialize()), + Some(transfer.inscription().taproot_program().to_vec()), + ) + }, + ProtoOutputBuilder::None => { + return Err(Error::from(Proto::Error::Error_missing_output_builder)) + }, + }, + // We derive the transaction type from the address. + ProtoOutputRecipient::from_address(addr) => { + let proto = output_from_address(output.value, addr.as_ref())?; + + // Recursive call, will initiate the appropraite builder. + return Self::utxo_from_proto(&proto); + }, + ProtoOutputRecipient::None => { + return Err(Error::from(Proto::Error::Error_missing_recipient)) + }, + }; + + let utxo = Proto::mod_PreSigningOutput::TxOut { + value: output.value, + script_pubkey: script_pubkey.to_vec().into(), + control_block: control_block.map(|cb| cb.into()).unwrap_or_default(), + taproot_payload: taproot_payload.map(|cb| cb.into()).unwrap_or_default(), + }; + + Ok(utxo) + } +} + +// Convenience helper function. +fn redeem_script_or_hash( + script_or_hash: &Proto::mod_Output::OutputRedeemScriptOrHash, +) -> Result { + let pubkey_hash = match &script_or_hash.variant { + ProtoRedeemScriptOrHash::hash(hash) => ScriptHash::from_slice(hash.as_ref()) + .map_err(|_| Error::from(Proto::Error::Error_invalid_redeem_script))?, + ProtoRedeemScriptOrHash::redeem_script(script) => { + ScriptBuf::from_bytes(script.to_vec()).script_hash() + }, + ProtoRedeemScriptOrHash::None => { + return Err(Error::from(Proto::Error::Error_missing_recipient)) + }, + }; + + Ok(pubkey_hash) +} + +// Convenience helper function. +fn witness_redeem_script_or_hash( + script_or_hash: &Proto::mod_Output::OutputRedeemScriptOrHash, +) -> Result { + let pubkey_hash = match &script_or_hash.variant { + ProtoRedeemScriptOrHash::hash(hash) => WScriptHash::from_slice(hash) + .map_err(|_| Error::from(Proto::Error::Error_invalid_witness_redeem_script_hash))?, + ProtoRedeemScriptOrHash::redeem_script(script) => { + ScriptBuf::from_bytes(script.to_vec()).wscript_hash() + }, + ProtoRedeemScriptOrHash::None => { + return Err(Error::from(Proto::Error::Error_missing_recipient)) + }, + }; + + Ok(pubkey_hash) +} + +// Convenience helper function. +fn pubkey_hash_from_proto(pubkey_or_hash: &Proto::ToPublicKeyOrHash) -> Result { + let pubkey_hash = match &pubkey_or_hash.to_address { + ProtoPubkeyOrHash::hash(hash) => PubkeyHash::from_slice(hash.as_ref()) + .map_err(|_| Error::from(Proto::Error::Error_invalid_pubkey_hash))?, + ProtoPubkeyOrHash::pubkey(pubkey) => { + bitcoin::PublicKey::from_slice(pubkey.as_ref())?.pubkey_hash() + }, + ProtoPubkeyOrHash::None => return Err(Error::from(Proto::Error::Error_missing_recipient)), + }; + + Ok(pubkey_hash) +} + +// Convenience helper function. +fn witness_pubkey_hash_from_proto( + pubkey_or_hash: &Proto::ToPublicKeyOrHash, +) -> Result { + let wpubkey_hash = match &pubkey_or_hash.to_address { + ProtoPubkeyOrHash::hash(hash) => WPubkeyHash::from_slice(hash.as_ref()) + .map_err(|_| Error::from(Proto::Error::Error_invalid_witness_pubkey_hash))?, + ProtoPubkeyOrHash::pubkey(pubkey) => bitcoin::PublicKey::from_slice(pubkey.as_ref())? + .wpubkey_hash() + .ok_or_else(|| Error::from(Proto::Error::Error_invalid_witness_pubkey_hash))?, + ProtoPubkeyOrHash::None => return Err(Error::from(Proto::Error::Error_missing_recipient)), + }; + + Ok(wpubkey_hash) +} + +// Derives the P2* output from the given address. +fn output_from_address(value: u64, addr: &str) -> Result> { + let string = String::from_utf8(addr.to_vec()) + .map_err(|_| Error::from(Proto::Error::Error_bad_address_recipient))?; + + let addr = Address::from_str(&string) + .map_err(|_| Error::from(Proto::Error::Error_bad_address_recipient))? + .require_network(bitcoin::Network::Bitcoin) + .map_err(|_| Error::from(Proto::Error::Error_bad_address_recipient))?; + + let proto = match addr.payload { + // Identified a "PubkeyHash" address (i.e. P2PKH). + Payload::PubkeyHash(pubkey_hash) => Proto::Output { + value, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2pkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::hash(pubkey_hash.to_vec().into()), + }), + }), + }, + // Identified a witness program (i.e. Segwit or Taproot). + Payload::WitnessProgram(progam) => { + match progam.version() { + // Identified version 0, i.e. Segwit + WitnessVersion::V0 => { + let payload = progam.program().as_bytes().to_vec(); + + // Check for P2WPKH. + if payload.len() == 20 { + return Ok(Proto::Output { + value, + to_recipient: ProtoOutputRecipient::builder( + Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::hash( + // Payload is the WPubkey hash. + payload.into(), + ), + }), + }, + ), + }); + } + + // Check for P2WSH. + if payload.len() == 32 { + return Ok(Proto::Output { + value, + to_recipient: ProtoOutputRecipient::builder( + Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wsh( + Proto::mod_Output::OutputRedeemScriptOrHash { + variant: ProtoOutputRedeemScriptOrHashBuilder::hash( + // Payload is the WScript hash. + payload.to_vec().into(), + ), + }, + ), + }, + ), + }); + } + + return Err(Error::from(Proto::Error::Error_bad_address_recipient)); + }, + // Identified version 1, i.e P2TR key-path (Taproot) + WitnessVersion::V1 => { + let pubkey = progam.program().as_bytes().to_vec(); + if pubkey.len() != 32 { + return Err(Error::from(Proto::Error::Error_bad_address_recipient)); + } + + Proto::Output { + value, + to_recipient: ProtoOutputRecipient::builder( + Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2tr_dangerous_assume_tweaked( + pubkey.into(), + ), + }, + ), + } + }, + _ => { + return Err(Error::from( + Proto::Error::Error_unsupported_address_recipient, + )) + }, + } + }, + Payload::ScriptHash(script_hash) => Proto::Output { + value, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2sh(Proto::mod_Output::OutputRedeemScriptOrHash { + variant: ProtoOutputRedeemScriptOrHashBuilder::hash( + script_hash.to_vec().into(), + ), + }), + }), + }, + _ => { + return Err(Error::from( + Proto::Error::Error_unsupported_address_recipient, + )) + }, + }; + + Ok(proto) +} diff --git a/rust/tw_bitcoin/src/modules/utils.rs b/rust/tw_bitcoin/src/modules/utils.rs new file mode 100644 index 00000000000..f64f9bfb29e --- /dev/null +++ b/rust/tw_bitcoin/src/modules/utils.rs @@ -0,0 +1,188 @@ +use crate::aliases::*; +use crate::{Error, Result}; +use tw_proto::BitcoinV2::Proto; + +// Convenience function: our protobuf library wraps certain types (such as +// `bytes`) in `Cow`, but given that calling `clone()` on a `Cow::Borrowed(T)` +// does not actually clone the underlying data (but the smart pointer instead), +// we must hard-clone individual fields manually. This is unfortunately required +// due to how protobuf library works and our use of the 'static constraints. +pub fn hard_clone_proto_input(proto: Proto::Input<'_>) -> Result> { + fn new_builder(variant: ProtoInputBuilder<'static>) -> ProtoInputRecipient<'static> { + ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { variant }) + } + + let to_recipient = match proto.to_recipient { + ProtoInputRecipient::builder(builder) => match builder.variant { + ProtoInputBuilder::p2sh(script) => { + new_builder(ProtoInputBuilder::p2sh(script.to_vec().into())) + }, + ProtoInputBuilder::p2pkh(script) => { + new_builder(ProtoInputBuilder::p2pkh(script.to_vec().into())) + }, + ProtoInputBuilder::p2wsh(script) => { + new_builder(ProtoInputBuilder::p2wsh(script.to_vec().into())) + }, + ProtoInputBuilder::p2wpkh(script) => { + new_builder(ProtoInputBuilder::p2wpkh(script.to_vec().into())) + }, + ProtoInputBuilder::p2tr_key_path(key_path) => new_builder( + ProtoInputBuilder::p2tr_key_path(Proto::mod_Input::InputTaprootKeyPath { + one_prevout: key_path.one_prevout, + public_key: key_path.public_key.to_vec().into(), + }), + ), + ProtoInputBuilder::p2tr_script_path(script) => new_builder( + ProtoInputBuilder::p2tr_script_path(Proto::mod_Input::InputTaprootScriptPath { + one_prevout: script.one_prevout, + payload: script.payload.to_vec().into(), + control_block: script.control_block.to_vec().into(), + }), + ), + ProtoInputBuilder::brc20_inscribe(brc20) => new_builder( + ProtoInputBuilder::brc20_inscribe(Proto::mod_Input::InputBrc20Inscription { + one_prevout: brc20.one_prevout, + inscribe_to: brc20.inscribe_to.to_vec().into(), + ticker: brc20.ticker.to_string().into(), + transfer_amount: brc20.transfer_amount, + }), + ), + ProtoInputBuilder::ordinal_inscribe(ord) => new_builder( + ProtoInputBuilder::ordinal_inscribe(Proto::mod_Input::InputOrdinalInscription { + one_prevout: ord.one_prevout, + inscribe_to: ord.inscribe_to.to_vec().into(), + mime_type: ord.mime_type.to_string().into(), + payload: ord.payload.to_vec().into(), + }), + ), + ProtoInputBuilder::None => { + return Err(Error::from(Proto::Error::Error_missing_input_builder)) + }, + }, + ProtoInputRecipient::custom_script(custom) => { + ProtoInputRecipient::custom_script(Proto::mod_Input::InputScriptWitness { + script_pubkey: custom.script_pubkey.to_vec().into(), + script_sig: custom.script_sig.to_vec().into(), + witness_items: custom + .witness_items + .iter() + .map(|item| item.to_vec().into()) + .collect(), + signing_method: custom.signing_method, + }) + }, + ProtoInputRecipient::None => { + return Err(Error::from(Proto::Error::Error_missing_recipient)) + }, + }; + + Ok(Proto::Input { + private_key: proto.private_key.to_vec().into(), + txid: proto.txid.to_vec().into(), + to_recipient, + ..proto + }) +} + +// Convenience function: our protobuf library wraps certain types (such as +// `bytes`) in `Cow`, but given that calling `clone()` on a `Cow::Borrowed(T)` +// does not actually clone the underlying data (but the smart pointer instead), +// we must hard-clone individual fields manually. This is unfortunately required +// due to how protobuf library works and our use of the 'static constraints. +pub fn hard_clone_proto_output(proto: Proto::Output<'_>) -> Result> { + fn new_builder(variant: ProtoOutputBuilder<'static>) -> ProtoOutputRecipient<'static> { + ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { variant }) + } + + fn new_script_or_hash( + proto: Proto::mod_Output::OutputRedeemScriptOrHash<'_>, + ) -> Result> { + let variant = match proto.variant { + ProtoOutputRedeemScriptOrHashBuilder::redeem_script(script) => { + ProtoOutputRedeemScriptOrHashBuilder::redeem_script(script.to_vec().into()) + }, + ProtoOutputRedeemScriptOrHashBuilder::hash(hash) => { + ProtoOutputRedeemScriptOrHashBuilder::hash(hash.to_vec().into()) + }, + ProtoOutputRedeemScriptOrHashBuilder::None => { + return Err(Error::from(Proto::Error::Error_missing_recipient)) + }, + }; + + Ok(Proto::mod_Output::OutputRedeemScriptOrHash { variant }) + } + + fn new_pubkey_or_hash( + proto: Proto::ToPublicKeyOrHash<'_>, + ) -> Result> { + let to_address = match proto.to_address { + ProtoPubkeyOrHash::pubkey(pubkey) => ProtoPubkeyOrHash::pubkey(pubkey.to_vec().into()), + ProtoPubkeyOrHash::hash(hash) => ProtoPubkeyOrHash::hash(hash.to_vec().into()), + ProtoPubkeyOrHash::None => { + return Err(Error::from(Proto::Error::Error_missing_recipient)) + }, + }; + + Ok(Proto::ToPublicKeyOrHash { to_address }) + } + + let to_recipient = match proto.to_recipient { + ProtoOutputRecipient::builder(builder) => match builder.variant { + ProtoOutputBuilder::p2sh(script_or_hash) => new_builder(ProtoOutputBuilder::p2sh( + new_script_or_hash(script_or_hash)?, + )), + ProtoOutputBuilder::p2pkh(pubkey_or_hash) => new_builder(ProtoOutputBuilder::p2pkh( + new_pubkey_or_hash(pubkey_or_hash)?, + )), + ProtoOutputBuilder::p2wsh(script_or_hash) => new_builder(ProtoOutputBuilder::p2wsh( + new_script_or_hash(script_or_hash)?, + )), + ProtoOutputBuilder::p2wpkh(pubkey_or_hash) => new_builder(ProtoOutputBuilder::p2wpkh( + new_pubkey_or_hash(pubkey_or_hash)?, + )), + ProtoOutputBuilder::p2tr_key_path(pubkey) => { + new_builder(ProtoOutputBuilder::p2tr_key_path(pubkey.to_vec().into())) + }, + ProtoOutputBuilder::p2tr_script_path(script_path) => new_builder( + ProtoOutputBuilder::p2tr_script_path(Proto::mod_Output::OutputTaprootScriptPath { + internal_key: script_path.internal_key.to_vec().into(), + merkle_root: script_path.merkle_root.to_vec().into(), + }), + ), + ProtoOutputBuilder::p2tr_dangerous_assume_tweaked(tweaked) => new_builder( + ProtoOutputBuilder::p2tr_dangerous_assume_tweaked(tweaked.to_vec().into()), + ), + ProtoOutputBuilder::brc20_inscribe(brc20) => new_builder( + ProtoOutputBuilder::brc20_inscribe(Proto::mod_Output::OutputBrc20Inscription { + inscribe_to: brc20.inscribe_to.to_vec().into(), + ticker: brc20.ticker.to_string().into(), + transfer_amount: brc20.transfer_amount, + }), + ), + ProtoOutputBuilder::ordinal_inscribe(ord) => new_builder( + ProtoOutputBuilder::ordinal_inscribe(Proto::mod_Output::OutputOrdinalInscription { + inscribe_to: ord.inscribe_to.to_vec().into(), + mime_type: ord.mime_type.to_string().into(), + payload: ord.payload.to_vec().into(), + }), + ), + ProtoOutputBuilder::None => { + return Err(Error::from(Proto::Error::Error_missing_output_builder)) + }, + }, + ProtoOutputRecipient::custom_script_pubkey(custom) => { + ProtoOutputRecipient::custom_script_pubkey(custom.to_vec().into()) + }, + ProtoOutputRecipient::from_address(address) => { + ProtoOutputRecipient::from_address(address.to_string().into()) + }, + ProtoOutputRecipient::None => { + return Err(Error::from(Proto::Error::Error_missing_output_builder)) + }, + }; + + Ok(Proto::Output { + value: proto.value, + to_recipient, + }) +} diff --git a/rust/tw_bitcoin/src/nft.rs b/rust/tw_bitcoin/src/nft.rs deleted file mode 100644 index 8d25b55fd2a..00000000000 --- a/rust/tw_bitcoin/src/nft.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::ordinals::OrdinalsInscription; -use crate::{Recipient, Result}; -use bitcoin::PublicKey; - -pub struct OrdinalNftInscription(OrdinalsInscription); - -impl OrdinalNftInscription { - // Constructs an [Ordinal inscription] with a given MIME type. Common MIME - // types are: - // * "application/json", - // * "application/pdf", - // * "image/gif", - // * "image/jpeg", - // * "image/png", - // * "text/plain;charset=utf-8" - // * ... - // - // [Ordinal inscription]: https://docs.ordinals.com/inscriptions.html - pub fn new(mime_type: &[u8], data: &[u8], recipient: Recipient) -> Result { - OrdinalsInscription::new(mime_type, data, recipient).map(OrdinalNftInscription) - } - pub fn inscription(&self) -> &OrdinalsInscription { - &self.0 - } -} diff --git a/rust/tw_bitcoin/src/output/mod.rs b/rust/tw_bitcoin/src/output/mod.rs deleted file mode 100644 index a93ccda8c87..00000000000 --- a/rust/tw_bitcoin/src/output/mod.rs +++ /dev/null @@ -1,75 +0,0 @@ -mod p2pkh; -mod p2tr_key_path; -mod p2tr_script_path; -mod p2wpkh; - -pub use p2pkh::*; -pub use p2tr_key_path::*; -pub use p2tr_script_path::*; -pub use p2wpkh::*; - -#[derive(Debug, Clone)] -pub enum TxOutput { - P2PKH(TxOutputP2PKH), - P2WPKH(TxOutputP2WPKH), - P2TRKeyPath(TxOutputP2TRKeyPath), - P2TRScriptPath(TXOutputP2TRScriptPath), -} - -impl TxOutput { - pub fn satoshis(&self) -> u64 { - match self { - TxOutput::P2PKH(p) => p.satoshis, - TxOutput::P2WPKH(p) => p.satoshis, - TxOutput::P2TRKeyPath(p) => p.satoshis, - TxOutput::P2TRScriptPath(p) => p.satoshis, - } - } -} - -impl From for TxOutput { - fn from(output: TxOutputP2PKH) -> Self { - TxOutput::P2PKH(output) - } -} - -impl From for TxOutput { - fn from(output: TxOutputP2TRKeyPath) -> Self { - TxOutput::P2TRKeyPath(output) - } -} - -impl From for TxOutput { - fn from(output: TxOutputP2WPKH) -> Self { - TxOutput::P2WPKH(output) - } -} - -impl From for TxOutput { - fn from(output: TXOutputP2TRScriptPath) -> Self { - TxOutput::P2TRScriptPath(output) - } -} - -impl From for bitcoin::TxOut { - fn from(out: TxOutput) -> Self { - match out { - TxOutput::P2PKH(p) => Self { - value: p.satoshis, - script_pubkey: p.script_pubkey, - }, - TxOutput::P2WPKH(p) => Self { - value: p.satoshis, - script_pubkey: p.script_pubkey, - }, - TxOutput::P2TRKeyPath(p) => Self { - value: p.satoshis, - script_pubkey: p.script_pubkey, - }, - TxOutput::P2TRScriptPath(p) => Self { - value: p.satoshis, - script_pubkey: p.script_pubkey, - }, - } - } -} diff --git a/rust/tw_bitcoin/src/output/p2pkh.rs b/rust/tw_bitcoin/src/output/p2pkh.rs deleted file mode 100644 index 168d260339d..00000000000 --- a/rust/tw_bitcoin/src/output/p2pkh.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::{Error, Recipient, Result}; -use bitcoin::{PubkeyHash, ScriptBuf}; - -#[derive(Debug, Clone)] -pub struct TxOutputP2PKH { - pub(crate) satoshis: u64, - pub(crate) script_pubkey: ScriptBuf, -} - -impl TxOutputP2PKH { - pub fn new(satoshis: u64, recipient: impl Into>) -> Self { - let recipient: Recipient = recipient.into(); - - TxOutputP2PKH { - satoshis, - script_pubkey: ScriptBuf::new_p2pkh(recipient.pubkey_hash()), - } - } - pub fn new_with_script(satoshis: u64, script_pubkey: ScriptBuf) -> Self { - TxOutputP2PKH { - satoshis, - script_pubkey, - } - } - pub fn builder() -> TxOutputP2PKHBuilder { - TxOutputP2PKHBuilder::new() - } -} - -#[derive(Debug, Clone, Default)] -pub struct TxOutputP2PKHBuilder { - satoshis: Option, - recipient: Option>, -} - -impl TxOutputP2PKHBuilder { - pub fn new() -> TxOutputP2PKHBuilder { - TxOutputP2PKHBuilder { - satoshis: None, - recipient: None, - } - } - pub fn satoshis(mut self, satoshis: u64) -> TxOutputP2PKHBuilder { - self.satoshis = Some(satoshis); - self - } - pub fn recipient( - mut self, - recipient: impl Into>, - ) -> TxOutputP2PKHBuilder { - self.recipient = Some(recipient.into()); - self - } - pub fn build(self) -> Result { - Ok(TxOutputP2PKH::new( - self.satoshis.ok_or(Error::Todo)?, - self.recipient.ok_or(Error::Todo)?, - )) - } -} diff --git a/rust/tw_bitcoin/src/output/p2tr_key_path.rs b/rust/tw_bitcoin/src/output/p2tr_key_path.rs deleted file mode 100644 index 94e166fd4ff..00000000000 --- a/rust/tw_bitcoin/src/output/p2tr_key_path.rs +++ /dev/null @@ -1,56 +0,0 @@ -use crate::{Error, Recipient, Result}; -use bitcoin::key::TweakedPublicKey; -use bitcoin::script::ScriptBuf; - -#[derive(Debug, Clone)] -pub struct TxOutputP2TRKeyPath { - pub(crate) satoshis: u64, - pub(crate) script_pubkey: ScriptBuf, -} - -impl TxOutputP2TRKeyPath { - pub fn new(satoshis: u64, recipient: Recipient) -> Self { - TxOutputP2TRKeyPath { - satoshis, - script_pubkey: ScriptBuf::new_v1_p2tr_tweaked(recipient.tweaked_pubkey()), - } - } - pub fn new_with_script(satoshis: u64, script_pubkey: ScriptBuf) -> Self { - TxOutputP2TRKeyPath { - satoshis, - script_pubkey, - } - } - pub fn builder() -> TxOutputP2TRKeyPathBuilder { - TxOutputP2TRKeyPathBuilder::new() - } -} - -#[derive(Debug, Clone, Default)] -pub struct TxOutputP2TRKeyPathBuilder { - satoshis: Option, - recipient: Option>, -} - -impl TxOutputP2TRKeyPathBuilder { - pub fn new() -> Self { - Self::default() - } - pub fn satoshis(mut self, satoshis: u64) -> TxOutputP2TRKeyPathBuilder { - self.satoshis = Some(satoshis); - self - } - pub fn recipient( - mut self, - recipient: impl Into>, - ) -> TxOutputP2TRKeyPathBuilder { - self.recipient = Some(recipient.into()); - self - } - pub fn build(self) -> Result { - Ok(TxOutputP2TRKeyPath::new( - self.satoshis.ok_or(Error::Todo)?, - self.recipient.ok_or(Error::Todo)?, - )) - } -} diff --git a/rust/tw_bitcoin/src/output/p2tr_script_path.rs b/rust/tw_bitcoin/src/output/p2tr_script_path.rs deleted file mode 100644 index cf7e4f55a8c..00000000000 --- a/rust/tw_bitcoin/src/output/p2tr_script_path.rs +++ /dev/null @@ -1,77 +0,0 @@ -use crate::{Error, Recipient, Result}; -use bitcoin::key::PublicKey; -use bitcoin::script::ScriptBuf; -use bitcoin::secp256k1; -use bitcoin::taproot::{TapNodeHash, TaprootSpendInfo}; - -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct TaprootScript { - pub pubkey: PublicKey, - pub merkle_root: TapNodeHash, -} - -#[derive(Debug, Clone)] -pub struct TaprootProgram { - pub script: ScriptBuf, - pub spend_info: TaprootSpendInfo, -} - -#[derive(Debug, Clone)] -pub struct TXOutputP2TRScriptPath { - pub(crate) satoshis: u64, - pub(crate) script_pubkey: ScriptBuf, -} - -impl TXOutputP2TRScriptPath { - pub fn new(satoshis: u64, recipient: &Recipient) -> Self { - let script_pubkey = ScriptBuf::new_v1_p2tr( - &secp256k1::Secp256k1::new(), - recipient.untweaked_pubkey(), - Some(recipient.merkle_root()), - ); - - TXOutputP2TRScriptPath { - satoshis, - script_pubkey, - } - } - pub fn new_with_script(satoshis: u64, script_pubkey: ScriptBuf) -> Self { - TXOutputP2TRScriptPath { - satoshis, - script_pubkey, - } - } - pub fn builder() -> TxOutputP2TRScriptPathBuilder { - TxOutputP2TRScriptPathBuilder::new() - } -} - -#[derive(Debug, Clone, Default)] -pub struct TxOutputP2TRScriptPathBuilder { - satoshis: Option, - recipient: Option>, -} - -impl TxOutputP2TRScriptPathBuilder { - pub fn new() -> Self { - Self::default() - } - pub fn satoshis(mut self, satoshis: u64) -> TxOutputP2TRScriptPathBuilder { - self.satoshis = Some(satoshis); - self - } - pub fn recipient( - mut self, - recipient: Recipient, - ) -> TxOutputP2TRScriptPathBuilder { - self.recipient = Some(recipient); - self - } - pub fn build(self) -> Result { - let recipient = self.recipient.ok_or(Error::Todo)?; - Ok(TXOutputP2TRScriptPath::new( - self.satoshis.ok_or(Error::Todo)?, - &recipient, - )) - } -} diff --git a/rust/tw_bitcoin/src/output/p2wpkh.rs b/rust/tw_bitcoin/src/output/p2wpkh.rs deleted file mode 100644 index 82877517874..00000000000 --- a/rust/tw_bitcoin/src/output/p2wpkh.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::{Error, Recipient, Result}; -use bitcoin::{ScriptBuf, WPubkeyHash}; - -#[derive(Debug, Clone)] -pub struct TxOutputP2WPKH { - pub(crate) satoshis: u64, - pub(crate) script_pubkey: ScriptBuf, -} - -impl TxOutputP2WPKH { - pub fn new(satoshis: u64, recipient: Recipient) -> Self { - TxOutputP2WPKH { - satoshis, - script_pubkey: ScriptBuf::new_v0_p2wpkh(recipient.wpubkey_hash()), - } - } - pub fn new_with_script(satoshis: u64, script_pubkey: ScriptBuf) -> Self { - TxOutputP2WPKH { - satoshis, - script_pubkey, - } - } - pub fn builder() -> TxOutputP2WPKHBuilder { - TxOutputP2WPKHBuilder::new() - } -} - -#[derive(Debug, Clone, Default)] -pub struct TxOutputP2WPKHBuilder { - satoshis: Option, - recipient: Option>, -} - -impl TxOutputP2WPKHBuilder { - pub fn new() -> TxOutputP2WPKHBuilder { - Self::default() - } - pub fn satoshis(mut self, satoshis: u64) -> TxOutputP2WPKHBuilder { - self.satoshis = Some(satoshis); - self - } - pub fn recipient(mut self, recipient: Recipient) -> TxOutputP2WPKHBuilder { - self.recipient = Some(recipient); - self - } - pub fn build(self) -> Result { - Ok(TxOutputP2WPKH::new( - self.satoshis.ok_or(Error::Todo)?, - self.recipient.ok_or(Error::Todo)?, - )) - } -} diff --git a/rust/tw_bitcoin/src/recipient.rs b/rust/tw_bitcoin/src/recipient.rs deleted file mode 100644 index f284ac84ab3..00000000000 --- a/rust/tw_bitcoin/src/recipient.rs +++ /dev/null @@ -1,260 +0,0 @@ -use std::str::FromStr; - -use crate::output::TaprootScript; -use crate::{tweak_pubkey, Error, Result}; -use bitcoin::key::{KeyPair, PublicKey, TweakedPublicKey, UntweakedPublicKey}; -use bitcoin::taproot::TapNodeHash; -use bitcoin::{ - secp256k1::{self, XOnlyPublicKey}, - Address, Network, PubkeyHash, WPubkeyHash, -}; - -/// This type is used to specify the recipient of a Bitcoin transaction, -/// depending on the required information that's required. For example, P2PKH -/// only requires the public key hash (`Recipient`), while P2TR -/// key-path requires the actual (tweaked) public key (`Recipient`). -/// -/// The recipient can easily downgrade, such as -/// ```rust,ignore -/// let pubkey = Recipient::::from_keypair(keypair); -/// let hash: Recipient = pubkey.into(); -/// ``` -/// -/// But it cannot, for example, derive a public key from just the hash. -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct Recipient { - inner: T, -} - -impl Recipient { - pub fn from_keypair(keypair: &KeyPair) -> Self { - Recipient { - inner: PublicKey::new(keypair.public_key()), - } - } - pub fn public_key(&self) -> PublicKey { - self.inner - } - pub fn pubkey_hash(&self) -> PubkeyHash { - PubkeyHash::from(self.inner) - } - pub fn wpubkey_hash(&self) -> Result { - self.inner.wpubkey_hash().ok_or(Error::Todo) - } - pub fn tweaked_pubkey(&self) -> TweakedPublicKey { - tweak_pubkey(self.inner) - } - pub fn untweaked_pubkey(&self) -> UntweakedPublicKey { - XOnlyPublicKey::from(self.inner.inner) - } - pub fn legacy_address(&self, network: Network) -> Address { - Address::p2pkh(&self.inner, network) - } - pub fn segwit_address(&self, network: Network) -> Result
{ - Address::p2wpkh(&self.inner, network).map_err(|_| Error::Todo) - } - pub fn taproot_address(&self, network: Network) -> Address { - let untweaked = UntweakedPublicKey::from(self.inner.inner); - Address::p2tr(&secp256k1::Secp256k1::new(), untweaked, None, network) - } - pub fn legacy_address_string(&self, network: Network) -> String { - self.legacy_address(network).to_string() - } - pub fn segwit_address_string(&self, network: Network) -> Result { - self.segwit_address(network).map(|addr| addr.to_string()) - } - pub fn taproot_address_string(&self, network: Network) -> String { - self.taproot_address(network).to_string() - } -} - -impl Recipient { - pub fn pubkey_hash(&self) -> &PubkeyHash { - &self.inner - } -} - -impl Recipient { - pub fn from_slice(slice: &[u8]) -> Result { - Ok(Recipient { - inner: PublicKey::from_slice(slice) - .map_err(|_| Error::Todo)? - .wpubkey_hash() - .ok_or(Error::Todo)?, - }) - } - pub fn wpubkey_hash(&self) -> &WPubkeyHash { - &self.inner - } -} - -impl Recipient { - pub fn from_slice(slice: &[u8]) -> Result { - Ok(Recipient { - inner: PublicKey::from_slice(slice).map_err(|_| Error::Todo)?, - }) - } -} - -impl FromStr for Recipient { - type Err = Error; - - fn from_str(string: &str) -> Result { - Self::from_slice(string.as_bytes()) - } -} - -impl From for Recipient { - fn from(inner: PublicKey) -> Self { - Recipient { inner } - } -} - -impl From for Recipient { - fn from(keypair: KeyPair) -> Self { - Self::from(&keypair) - } -} - -impl From<&KeyPair> for Recipient { - fn from(keypair: &KeyPair) -> Self { - Recipient { - inner: PublicKey::new(keypair.public_key()), - } - } -} - -impl From for Recipient { - fn from(pubkey: PublicKey) -> Self { - let tweaked = tweak_pubkey(pubkey); - Recipient { inner: tweaked } - } -} - -impl From> for Recipient { - fn from(recipient: Recipient) -> Self { - Recipient { - inner: Self::from(recipient.inner).inner, - } - } -} - -impl From for Recipient { - fn from(keypair: KeyPair) -> Self { - Self::from(&keypair) - } -} - -impl From<&KeyPair> for Recipient { - fn from(keypair: &KeyPair) -> Self { - let pk = Recipient::::from(keypair); - let tweaked = Recipient::::from(pk); - - Recipient { - inner: tweaked.inner, - } - } -} - -impl TryFrom for Recipient { - type Error = Error; - - fn try_from(pubkey: PublicKey) -> Result { - Ok(Recipient { - inner: pubkey.wpubkey_hash().unwrap(), - }) - } -} - -impl TryFrom> for Recipient { - type Error = Error; - - fn try_from(recipient: Recipient) -> Result { - Ok(Recipient { - inner: Self::try_from(recipient.inner)?.inner, - }) - } -} - -impl TryFrom<&KeyPair> for Recipient { - type Error = Error; - - fn try_from(keypair: &KeyPair) -> Result { - let pubkey = Recipient::::from(keypair); - Self::try_from(pubkey.inner) - } -} - -impl TryFrom for Recipient { - type Error = Error; - - fn try_from(keypair: KeyPair) -> Result { - Self::try_from(&keypair) - } -} - -impl From for Recipient { - fn from(pubkey: PublicKey) -> Self { - Recipient { - inner: pubkey.into(), - } - } -} - -impl From> for Recipient { - fn from(recipient: Recipient) -> Self { - Recipient { - inner: Self::from(recipient.inner).inner, - } - } -} - -impl From for Recipient { - fn from(keypair: KeyPair) -> Self { - Self::from(&keypair) - } -} - -impl From<&KeyPair> for Recipient { - fn from(keypair: &KeyPair) -> Self { - let pk = Recipient::::from(keypair); - - Recipient { - inner: pk.inner.into(), - } - } -} - -impl Recipient { - pub fn tweaked_pubkey(&self) -> TweakedPublicKey { - self.inner - } -} - -impl Recipient { - pub fn from_keypair(keypair: &KeyPair, merkle_root: TapNodeHash) -> Self { - Recipient { - inner: TaprootScript { - pubkey: PublicKey::new(keypair.public_key()), - merkle_root, - }, - } - } - pub fn from_pubkey_recipient( - recipient: Recipient, - merkle_root: TapNodeHash, - ) -> Self { - Recipient { - inner: TaprootScript { - pubkey: recipient.inner, - merkle_root, - }, - } - } - pub fn untweaked_pubkey(&self) -> UntweakedPublicKey { - XOnlyPublicKey::from(self.inner.pubkey.inner) - } - pub fn merkle_root(&self) -> TapNodeHash { - self.inner.merkle_root - } -} diff --git a/rust/tw_bitcoin/src/tests/address.rs b/rust/tw_bitcoin/src/tests/address.rs deleted file mode 100644 index 032771bb1b7..00000000000 --- a/rust/tw_bitcoin/src/tests/address.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::{keypair_from_wif, Recipient}; -use bitcoin::{Network, PublicKey}; - -// This private key was used in a Bitcoin regtest environment. -pub const ALICE_WIF: &str = "cQUNzeMnF9xPPLqZhH7hMVYGwSuu3b78zznuc5UrxgXnYQBq6Bx1"; - -#[test] -fn addresses() { - let alice = keypair_from_wif(ALICE_WIF).unwrap(); - let recipient = Recipient::::from(&alice); - - assert_eq!( - recipient.legacy_address_string(Network::Bitcoin), - "1MrZNGN7mfWZiZNQttrzHjfw72jnJC2JNx" - ); - assert_eq!( - recipient.segwit_address_string(Network::Bitcoin).unwrap(), - "bc1qunq74p3h8425hr6wllevlvqqr6sezfxj262rff" - ); - assert_eq!( - recipient.taproot_address_string(Network::Bitcoin), - "bc1pwse34zfpvt344rvlt7tw0ngjtfh9xasc4q03avf0lk74jzjpzjuqaz7ks5" - ); -} diff --git a/rust/tw_bitcoin/src/tests/brc20_transfer.rs b/rust/tw_bitcoin/src/tests/brc20_transfer.rs deleted file mode 100644 index 40b8ff40837..00000000000 --- a/rust/tw_bitcoin/src/tests/brc20_transfer.rs +++ /dev/null @@ -1,183 +0,0 @@ -use crate::{ - brc20::{BRC20TransferInscription, Ticker}, - keypair_from_wif, TXOutputP2TRScriptPath, TransactionBuilder, TxInputP2TRScriptPath, - TxInputP2WPKH, TxOutputP2WPKH, -}; -use bitcoin::Txid; -use std::str::FromStr; -use tw_encoding::hex; - -// Those private keys were used for Bitcoin mainnet tests and have a transaction -// history. BTC holdings have been emptied. -pub const ALICE_WIF: &str = "L4of5AJ6aKmvChg7gQ7m2RzHFgpWe5Uirmuey1fXJ1FtfmXj59LW"; -pub const BOB_WIF: &str = "L59WHi2hj1HnMAYaFyMqR4Z36HrUDTZQCixzTHachAxbUU9VUCjp"; - -pub const FULL_SATOSHIS: u64 = 26_400; -pub const MINER_FEE: u64 = 3_000; - -pub const BRC20_TICKER: &str = "oadf"; -pub const BRC20_AMOUNT: u64 = 20; -pub const BRC20_INSCRIBE_SATOSHIS: u64 = 7_000; -pub const BRC20_DUST_SATOSHIS: u64 = 546; - -pub const FOR_FEE_SATOSHIS: u64 = FULL_SATOSHIS - BRC20_INSCRIBE_SATOSHIS - MINER_FEE; - -// Used for the committing the Inscription. -// https://www.blockchain.com/explorer/transactions/btc/797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1 -pub const COMMIT_TXID: &str = "8ec895b4d30adb01e38471ca1019bfc8c3e5fbd1f28d9e7b5653260d89989008"; -pub const COMMIT_TX_RAW: &str = "02000000000101089098890d2653567b9e8df2d1fbe5c3c8bf1910ca7184e301db0ad3b495c88e0100000000ffffffff02581b000000000000225120e8b706a97732e705e22ae7710703e7f589ed13c636324461afa443016134cc051040000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d02483045022100a44aa28446a9a886b378a4a65e32ad9a3108870bd725dc6105160bed4f317097022069e9de36422e4ce2e42b39884aa5f626f8f94194d1013007d5a1ea9220a06dce0121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000"; - -// Used for revealing the Inscription. -// https://www.blockchain.com/explorer/transactions/btc/7046dc2689a27e143ea2ad1039710885147e9485ab6453fa7e87464aa7dd3eca -pub const REVEAL_TXID: &str = "797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1"; -pub const REVEAL_TX_RAW: &str = "02000000000101b11f1782607a1fe5f033ccf9dc17404db020a0dedff94183596ee67ad4177d790000000000ffffffff012202000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d0340de6fd13e43700f59876d305e5a4a5c41ad7ada10bc5a4e4bdd779eb0060c0a78ebae9c33daf77bb3725172edb5bd12e26f00c08f9263e480d53b93818138ad0b5b0063036f7264010118746578742f706c61696e3b636861727365743d7574662d3800377b2270223a226272632d3230222c226f70223a227472616e73666572222c227469636b223a226f616466222c22616d74223a223230227d6821c00f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000"; - -// Used for transfering the Inscription ("BRC20 transfer"). -// https://www.blockchain.com/explorer/transactions/btc/3e3576eb02667fac284a5ecfcb25768969680cc4c597784602d0a33ba7c654b7 -pub use skip::*; -// We skip formatting for the `skip` module, re-exporting everything. -#[rustfmt::skip] -mod skip { -pub const TRANSFER_TXID_INSCRIPTION: &str = "7046dc2689a27e143ea2ad1039710885147e9485ab6453fa7e87464aa7dd3eca"; -pub const TRANSFER_TXID_FOR_FEES: &str = "797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1"; -pub const TRANSFER_RAW: &str = "02000000000102ca3edda74a46877efa5364ab85947e148508713910ada23e147ea28926dc46700000000000ffffffffb11f1782607a1fe5f033ccf9dc17404db020a0dedff94183596ee67ad4177d790100000000ffffffff022202000000000000160014e891850afc55b64aa8247b2076f8894ebdf889015834000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d024830450221008798393eb0b7390217591a8c33abe18dd2f7ea7009766e0d833edeaec63f2ec302200cf876ff52e68dbaf108a3f6da250713a9b04949a8f1dcd1fb867b24052236950121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb0248304502210096bbb9d1f0596d69875646689e46f29485e8ceccacde9d0025db87fd96d3066902206d6de2dd69d965d28df3441b94c76e812384ab9297e69afe3480ee4031e1b2060121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000"; -} - -#[test] -fn brc20_transfer() { - let ticker = Ticker::new(BRC20_TICKER.to_string()).unwrap(); - - let alice = keypair_from_wif(ALICE_WIF).unwrap(); - let bob = keypair_from_wif(BOB_WIF).unwrap(); - - let txid = Txid::from_str(COMMIT_TXID).unwrap(); - - // # Make "available" tokens "transferable". - // Based on Bitcoin transaction: - // https://www.blockchain.com/explorer/transactions/btc/797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1 - - // Commit transfer. - let input = TxInputP2WPKH::builder() - .txid(txid) - .vout(1) - .recipient(alice.try_into().unwrap()) - .satoshis(FULL_SATOSHIS) - .build() - .unwrap(); - - let transfer = BRC20TransferInscription::new(alice.into(), ticker, BRC20_AMOUNT).unwrap(); - - let output = TXOutputP2TRScriptPath::builder() - .recipient(transfer.inscription().recipient().clone()) - .satoshis(BRC20_INSCRIBE_SATOSHIS) - .build() - .unwrap(); - - let output_change = TxOutputP2WPKH::builder() - .recipient(alice.try_into().unwrap()) - .satoshis(FOR_FEE_SATOSHIS) - .build() - .unwrap(); - - let transaction = TransactionBuilder::new() - .add_input(input.into()) - .add_output(output.into()) - .add_output(output_change.into()) - .sign_inputs(alice) - .unwrap() - .serialize() - .unwrap(); - - // Encode the signed transaction. - let hex = hex::encode(&transaction, false); - assert_eq!(hex, COMMIT_TX_RAW); - - // # Reveal transfer. - // Based on Bitcoin transaction: - // https://www.blockchain.com/explorer/transactions/btc/7046dc2689a27e143ea2ad1039710885147e9485ab6453fa7e87464aa7dd3eca - - let txid = - Txid::from_str("797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1").unwrap(); - - let input = TxInputP2TRScriptPath::builder() - .txid(txid) - .vout(0) - .recipient(transfer.inscription().recipient().clone()) - .satoshis(BRC20_INSCRIBE_SATOSHIS) - .script(transfer.inscription().taproot_program().to_owned()) - .spend_info(transfer.inscription().spend_info().clone()) - .build() - .unwrap(); - - let output = TxOutputP2WPKH::builder() - .recipient(alice.try_into().unwrap()) - .satoshis(BRC20_DUST_SATOSHIS) - .build() - .unwrap(); - - let transaction = TransactionBuilder::new() - .add_input(input.into()) - .add_output(output.into()) - .sign_inputs(alice) - .unwrap() - .serialize() - .unwrap(); - - // Encode the signed transaction. - let hex = hex::encode(&transaction, false); - - assert_eq!(hex[..164], REVEAL_TX_RAW[..164]); - // We ignore the 64-byte Schnorr signature, since it uses random data for - // signing on each construction and is therefore not reproducible. - assert_ne!(hex[164..292], REVEAL_TX_RAW[164..292]); - assert_eq!(hex[292..], REVEAL_TX_RAW[292..]); - - // # Actually transfer the "transferable" tokens. - // Based on Bitcoin transaction: - // https://www.blockchain.com/explorer/transactions/btc/3e3576eb02667fac284a5ecfcb25768969680cc4c597784602d0a33ba7c654b7 - - // We use a normal P2WPKH output for this. - let input_for_brc20_transfer = TxInputP2WPKH::builder() - .txid(Txid::from_str(TRANSFER_TXID_INSCRIPTION).unwrap()) - .vout(0) - .recipient(alice.try_into().unwrap()) - .satoshis(BRC20_DUST_SATOSHIS) - .build() - .unwrap(); - - let input_for_fee = TxInputP2WPKH::builder() - .txid(Txid::from_str(TRANSFER_TXID_FOR_FEES).unwrap()) - .vout(1) - .recipient(alice.try_into().unwrap()) - .satoshis(FOR_FEE_SATOSHIS) - .build() - .unwrap(); - - // We transfer the tokens to Bob. - let output_brc20_transfer = TxOutputP2WPKH::builder() - .recipient(bob.try_into().unwrap()) - .satoshis(BRC20_DUST_SATOSHIS) - .build() - .unwrap(); - - let output_change = TxOutputP2WPKH::builder() - .recipient(alice.try_into().unwrap()) - .satoshis(FOR_FEE_SATOSHIS - MINER_FEE) - .build() - .unwrap(); - - // We carefully add the BRC20 transfer in the first position for both input and output. - let transaction = TransactionBuilder::new() - .add_input(input_for_brc20_transfer.into()) - .add_input(input_for_fee.into()) - .add_output(output_brc20_transfer.into()) - .add_output(output_change.into()) - .sign_inputs(alice) - .unwrap() - .serialize() - .unwrap(); - - // Encode the signed transaction. - let hex = hex::encode(&transaction, false); - assert_eq!(hex, TRANSFER_RAW); -} diff --git a/rust/tw_bitcoin/src/tests/fee.rs b/rust/tw_bitcoin/src/tests/fee.rs deleted file mode 100644 index f7cfa85d8f9..00000000000 --- a/rust/tw_bitcoin/src/tests/fee.rs +++ /dev/null @@ -1,104 +0,0 @@ -use crate::calculate_fee; -use bitcoin::{consensus::Decodable, Transaction}; - -// 10 satoshis per virtual byte. -const SAT_VB: u64 = 12; - -fn decode_tx(raw: &str) -> Transaction { - let hex = tw_encoding::hex::decode(raw).unwrap(); - Transaction::consensus_decode(&mut hex.as_slice()).unwrap() -} - -#[test] -fn p2pkh_fee() { - let tx = decode_tx(super::p2pkh::TX_RAW); - - let (weight, fee) = calculate_fee(&tx, SAT_VB); - assert_eq!(weight.to_vbytes_ceil(), 191); - assert_eq!(fee, 191 * SAT_VB); -} - -#[test] -fn p2wpkh_fee() { - let tx = decode_tx(super::p2wpkh::TX_RAW); - - let (weight, fee) = calculate_fee(&tx, SAT_VB); - assert_eq!(weight.to_vbytes_ceil(), 189); - assert_eq!(fee, 189 * SAT_VB); -} - -#[test] -fn brc20_commit_fee() { - // Metadata can be observed live on: - // https://www.blockchain.com/explorer/transactions/btc/797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1 - // - // Fee/VB 19.608 sat/vByte - // Size 235 Bytes - // Weight 610 - - // 19 satoshis per vbyte. - const SAT_19_VB: u64 = 19; - - let tx = decode_tx(super::brc20_transfer::COMMIT_TX_RAW); - - let (weight, fee) = calculate_fee(&tx, SAT_19_VB); - assert_eq!(weight.to_vbytes_ceil(), 153); // 153 = ceil(610/4) - assert_eq!(fee, 153 * SAT_19_VB); -} - -#[test] -fn brc20_reveal_fee() { - // Metadata can be observed live on: - // https://www.blockchain.com/explorer/transactions/btc/7046dc2689a27e143ea2ad1039710885147e9485ab6453fa7e87464aa7dd3eca - // - // Fee/VB 49.267 sat/vByte - // Size 276 Bytes - // Weight 522 - - // 49 satoshis per vbyte (slightly overpaid here...) - const SAT_49_VB: u64 = 49; - - let tx = decode_tx(super::brc20_transfer::REVEAL_TX_RAW); - - let (weight, fee) = calculate_fee(&tx, SAT_49_VB); - assert_eq!(weight.to_vbytes_ceil(), 131); // 131 = ceil(522/4) - assert_eq!(fee, 131 * SAT_49_VB); -} - -#[test] -fn ordinal_nft_commit_fee() { - // Metadata can be observed live on: - // https://www.blockchain.com/explorer/transactions/btc/f1e708e5c5847339e16accf8716c14b33717c14d6fe68f9db36627cecbde7117 - // - // Fee/VB 10.656 sat/vByte - // Size 203 Bytes - // Weight 485 - - // 19 satoshis per vbyte. - const SAT_10_VB: u64 = 10; - - let tx = decode_tx(super::nft::COMMIT_RAW_TX); - - let (weight, fee) = calculate_fee(&tx, SAT_10_VB); - assert_eq!(weight.to_vbytes_ceil(), 122); // 122 = ceil(485/4) - assert_eq!(fee, 122 * SAT_10_VB); -} - -#[test] -fn ordinal_nft_reveal_fee() { - // Metadata can be observed live on: - // https://www.blockchain.com/explorer/transactions/btc/173f8350b722243d44cc8db5584de76b432eb6d0888d9e66e662db51584f44ac - // - // Fee/VB 15.133 sat/vByte - // Size 7'829 Bytes - // Weight 8'075 - - // 19 satoshis per vbyte. - const SAT_15_VB: u64 = 15; - - let tx = decode_tx(super::nft::REVEAL_RAW_TX); - - let (weight, fee) = calculate_fee(&tx, SAT_15_VB); - assert_eq!(weight.to_vbytes_ceil(), 2019); // 2019 = ceil(8_075/4) - assert_eq!(fee, 2019 * SAT_15_VB); -} diff --git a/rust/tw_bitcoin/src/tests/ffi/brc20_transfer.rs b/rust/tw_bitcoin/src/tests/ffi/brc20_transfer.rs deleted file mode 100644 index 4c210a85c6a..00000000000 --- a/rust/tw_bitcoin/src/tests/ffi/brc20_transfer.rs +++ /dev/null @@ -1,235 +0,0 @@ -use crate::brc20::{BRC20TransferInscription, Ticker}; -use crate::ffi::taproot_build_and_sign_transaction; -use crate::tests::ffi::utils::{ - call_ffi_build_brc20_transfer_script, call_ffi_build_p2wpkh_script, reverse_txid, - ProtoSigningInputBuilder, ProtoTransactionBuilder, -}; -use crate::tests::p2pkh::ALICE_WIF; -use crate::{keypair_from_wif, Recipient, TXOutputP2TRScriptPath}; -use bitcoin::PublicKey; -use std::borrow::Cow; -use tw_encoding::hex; -use tw_proto::Bitcoin::Proto::{TransactionOutput, TransactionVariant}; - -#[test] -fn proto_brc20_transfer_script() { - let keypair: secp256k1::KeyPair = keypair_from_wif(ALICE_WIF).unwrap(); - let recipient = Recipient::::from(keypair); - - let satoshis: u64 = 1_000; - let brc20_amount = 20; - let ticker = "oadf"; - - // Call FFI function. - let ffi_out = call_ffi_build_brc20_transfer_script(ticker, brc20_amount, satoshis, &recipient); - - // Compare with native call. - let transfer = BRC20TransferInscription::new( - recipient, - Ticker::new(ticker.to_string()).unwrap(), - brc20_amount, - ) - .unwrap(); - - let tapscript = transfer.inscription().recipient().clone(); - let spending_script = transfer.inscription().taproot_program(); - - let tx_out = TXOutputP2TRScriptPath::new(satoshis, &tapscript); - // Wrap in Protobuf structure. - let proto = TransactionOutput { - value: satoshis as i64, - script: Cow::from(tx_out.script_pubkey.as_bytes()), - spendingScript: Cow::from(spending_script.as_bytes()), - }; - - assert_eq!(ffi_out, proto); -} - -/// Commit the Inscription. -#[test] -fn proto_sign_brc20_transfer_inscription_commit() { - use crate::tests::brc20_transfer::*; - - // Prepare keys. - let alice = keypair_from_wif(ALICE_WIF).unwrap(); - let alice_privkey = alice.secret_bytes(); - let alice_recipient = Recipient::::from(&alice); - - // Note that the Txid must be reversed. - let txid = reverse_txid(COMMIT_TXID); - - // Build input script. - let input = call_ffi_build_p2wpkh_script(FULL_SATOSHIS, &alice_recipient); - - // Build inscription output. - let output_inscribe = call_ffi_build_brc20_transfer_script( - BRC20_TICKER, - BRC20_AMOUNT, - BRC20_INSCRIBE_SATOSHIS, - &alice_recipient, - ); - - // Build change output. - let output_change = call_ffi_build_p2wpkh_script(FOR_FEE_SATOSHIS, &alice_recipient); - - // Construct Protobuf payload. - let signing = ProtoSigningInputBuilder::new() - .private_key(&alice_privkey) - .input( - ProtoTransactionBuilder::new() - .txid(&txid) - .vout(1) - .script_pubkey(&input.script) - .satoshis(FULL_SATOSHIS) - .variant(TransactionVariant::P2WPKH) - .build(), - ) - .output( - ProtoTransactionBuilder::new() - .script_pubkey(&output_inscribe.script) - .satoshis(BRC20_INSCRIBE_SATOSHIS) - .variant(TransactionVariant::BRC20TRANSFER) - .build(), - ) - .output( - ProtoTransactionBuilder::new() - .script_pubkey(&output_change.script) - .satoshis(FOR_FEE_SATOSHIS) - .variant(TransactionVariant::P2WPKH) - .build(), - ) - .build(); - - let signed = taproot_build_and_sign_transaction(signing).unwrap(); - assert_eq!(hex::encode(&signed.encoded, false), COMMIT_TX_RAW); -} - -/// Reveal the Inscription. -#[test] -fn proto_sign_brc20_transfer_inscription_reveal() { - use crate::tests::brc20_transfer::*; - - // Prepare keys. - let alice = keypair_from_wif(ALICE_WIF).unwrap(); - let alice_privkey = alice.secret_bytes(); - let alice_recipient = Recipient::::from(&alice); - - // Note that the Txid must be reversed. - let txid = reverse_txid(REVEAL_TXID); - - // Build input script. - let input = call_ffi_build_brc20_transfer_script( - BRC20_TICKER, - BRC20_AMOUNT, - BRC20_INSCRIBE_SATOSHIS, - &alice_recipient, - ); - - // Build inscription output. - let output_p2wpkh = call_ffi_build_p2wpkh_script(BRC20_DUST_SATOSHIS, &alice_recipient); - - // Construct Protobuf payload. - let signing = ProtoSigningInputBuilder::new() - .private_key(&alice_privkey) - .input( - ProtoTransactionBuilder::new() - .txid(&txid) - .vout(0) - .script_pubkey(&input.script) - .satoshis(BRC20_INSCRIBE_SATOSHIS) - .variant(TransactionVariant::BRC20TRANSFER) - // IMPORANT: include the witness containing the actual inscription. - .spending_script(&input.spendingScript) - .build(), - ) - .output( - ProtoTransactionBuilder::new() - .script_pubkey(&output_p2wpkh.script) - .satoshis(BRC20_DUST_SATOSHIS) - .variant(TransactionVariant::P2WPKH) - .build(), - ) - .build(); - - let signed = taproot_build_and_sign_transaction(signing).unwrap(); - let hex = hex::encode(&signed.encoded, false); - - assert_eq!(hex[..164], REVEAL_TX_RAW[..164]); - // We ignore the 64-byte Schnorr signature, since it uses random data for - // signing on each construction and is therefore not reproducible. - assert_ne!(hex[164..292], REVEAL_TX_RAW[164..292]); - assert_eq!(hex[292..], REVEAL_TX_RAW[292..]); -} - -/// Transfer the Inscription with P2WPKH. -#[test] -fn proto_sign_brc20_transfer_inscription_p2wpkh_transfer() { - use crate::tests::brc20_transfer::*; - - // Prepare keys. - let alice = keypair_from_wif(ALICE_WIF).unwrap(); - let alice_privkey = alice.secret_bytes(); - let alice_recipient = Recipient::::from(&alice); - - let bob = keypair_from_wif(BOB_WIF).unwrap(); - let bob_recipient = Recipient::::from(&bob); - - // The Txid to reference the Inscription. - let txid_inscription = reverse_txid(TRANSFER_TXID_INSCRIPTION); - - // The Txid for paying fees. - let txid_for_fees = reverse_txid(TRANSFER_TXID_FOR_FEES); - - // Build input script for Inscription transfer. - let input_transfer = call_ffi_build_p2wpkh_script(BRC20_DUST_SATOSHIS, &alice_recipient); - - // Build input for paying fees. - let input_fees = call_ffi_build_p2wpkh_script(FOR_FEE_SATOSHIS, &alice_recipient); - - // Build Inscription transfer output with Bob as recipient. - let output_transfer = call_ffi_build_p2wpkh_script(BRC20_DUST_SATOSHIS, &bob_recipient); - - // Build change output. - let output_change = - call_ffi_build_p2wpkh_script(FOR_FEE_SATOSHIS - MINER_FEE, &alice_recipient); - - // Construct Protobuf payload. - let signing = ProtoSigningInputBuilder::new() - .private_key(&alice_privkey) - .input( - ProtoTransactionBuilder::new() - .txid(&txid_inscription) - .vout(0) - .script_pubkey(&input_transfer.script) - .satoshis(BRC20_DUST_SATOSHIS) - .variant(TransactionVariant::P2WPKH) - .build(), - ) - .input( - ProtoTransactionBuilder::new() - .txid(&txid_for_fees) - .vout(1) - .script_pubkey(&input_fees.script) - .satoshis(FOR_FEE_SATOSHIS) - .variant(TransactionVariant::P2WPKH) - .build(), - ) - .output( - ProtoTransactionBuilder::new() - .script_pubkey(&output_transfer.script) - .satoshis(BRC20_DUST_SATOSHIS) - .variant(TransactionVariant::P2WPKH) - .build(), - ) - .output( - ProtoTransactionBuilder::new() - .script_pubkey(&output_change.script) - .satoshis(FOR_FEE_SATOSHIS - MINER_FEE) - .variant(TransactionVariant::P2WPKH) - .build(), - ) - .build(); - - let signed = taproot_build_and_sign_transaction(signing).unwrap(); - assert_eq!(hex::encode(&signed.encoded, false), TRANSFER_RAW); -} diff --git a/rust/tw_bitcoin/src/tests/ffi/fees.rs b/rust/tw_bitcoin/src/tests/ffi/fees.rs deleted file mode 100644 index a5cdea4f7b3..00000000000 --- a/rust/tw_bitcoin/src/tests/ffi/fees.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::ffi::tw_bitcoin_calculate_transaction_fee; -use tw_memory::ffi::c_result::CUInt64Result; - -/// Convenience wrapper. -fn call_ffi_calculate_fee(hex: &str, sat_vb: u64) -> u64 { - let hex = tw_encoding::hex::decode(hex).unwrap(); - - let res: CUInt64Result = - unsafe { tw_bitcoin_calculate_transaction_fee(hex.as_ptr(), hex.len(), sat_vb) }; - - res.unwrap() -} - -#[test] -fn ffi_calculate_p2pkh_fee() { - let fee = call_ffi_calculate_fee(crate::tests::p2pkh::TX_RAW, 10); - assert_eq!(fee, 191 * 10); -} - -#[test] -fn ffi_calculate_p2wpkh_fee() { - let fee = call_ffi_calculate_fee(crate::tests::p2wpkh::TX_RAW, 10); - assert_eq!(fee, 189 * 10); -} - -#[test] -fn ffi_calculate_brc20_commit_fee() { - let fee = call_ffi_calculate_fee(crate::tests::brc20_transfer::COMMIT_TX_RAW, 19); - assert_eq!(fee, 153 * 19); -} - -#[test] -fn ffi_calculate_brc20_reveal_fee() { - let fee = call_ffi_calculate_fee(crate::tests::brc20_transfer::REVEAL_TX_RAW, 49); - assert_eq!(fee, 131 * 49); -} - -#[test] -fn ffi_calculate_ordinal_nft_commit_fee() { - let fee = call_ffi_calculate_fee(crate::tests::nft::COMMIT_RAW_TX, 10); - assert_eq!(fee, 122 * 10); -} - -#[test] -fn ffi_calculate_ordinal_nft_reveal_fee() { - let fee = call_ffi_calculate_fee(crate::tests::nft::REVEAL_RAW_TX, 15); - assert_eq!(fee, 2019 * 15); -} diff --git a/rust/tw_bitcoin/src/tests/ffi/mod.rs b/rust/tw_bitcoin/src/tests/ffi/mod.rs deleted file mode 100644 index fcb183bbdf0..00000000000 --- a/rust/tw_bitcoin/src/tests/ffi/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod brc20_transfer; -mod fees; -mod nft; -mod scripts; -mod transaction; -mod utils; diff --git a/rust/tw_bitcoin/src/tests/ffi/nft.rs b/rust/tw_bitcoin/src/tests/ffi/nft.rs deleted file mode 100644 index 9a16acd7e31..00000000000 --- a/rust/tw_bitcoin/src/tests/ffi/nft.rs +++ /dev/null @@ -1,146 +0,0 @@ -use crate::ffi::taproot_build_and_sign_transaction; -use crate::nft::OrdinalNftInscription; -use crate::tests::ffi::utils::{ - call_ffi_build_p2wpkh_script, reverse_txid, ProtoSigningInputBuilder, ProtoTransactionBuilder, -}; -use crate::tests::nft::ALICE_WIF; -use crate::{keypair_from_wif, Recipient, TXOutputP2TRScriptPath}; -use bitcoin::PublicKey; -use std::borrow::Cow; -use tw_encoding::hex; -use tw_proto::Bitcoin::Proto::{TransactionOutput, TransactionVariant}; - -use super::utils::call_ffi_build_nft_inscription; - -#[test] -fn proto_nft_inscription_script() { - let keypair: secp256k1::KeyPair = keypair_from_wif(ALICE_WIF).unwrap(); - let recipient = Recipient::::from(keypair); - - let mime_type = b"image/png"; - let payload = hex::decode(crate::tests::data::NFT_INSCRIPTION_IMAGE_DATA).unwrap(); - let satoshis: u64 = 1_000; - - // Call FFI function. - let ffi_out = call_ffi_build_nft_inscription(mime_type, &payload, satoshis, &recipient); - - // Compare with native call. - let nft = OrdinalNftInscription::new(mime_type, &payload, recipient).unwrap(); - - let tapscript = nft.inscription().recipient().clone(); - let spending_script = nft.inscription().taproot_program(); - - let tx_out = TXOutputP2TRScriptPath::new(satoshis, &tapscript); - // Wrap in Protobuf structure. - let proto = TransactionOutput { - value: satoshis as i64, - script: Cow::from(tx_out.script_pubkey.as_bytes()), - spendingScript: Cow::from(spending_script.as_bytes()), - }; - - assert_eq!(ffi_out, proto); -} - -/// Commit the Inscription. -#[test] -fn proto_sign_nft_inscription_commit() { - use crate::tests::nft::*; - - // Prepare keys. - let alice = keypair_from_wif(ALICE_WIF).unwrap(); - let privkey = alice.secret_bytes(); - let recipient = Recipient::::from(&alice); - - let mime_type = b"image/png"; - let payload = hex::decode(crate::tests::data::NFT_INSCRIPTION_IMAGE_DATA).unwrap(); - let satoshis: u64 = 1_000; - - // Note that the Txid must be reversed. - let txid = reverse_txid(COMMIT_TXID); - - // Build input script. - let input = call_ffi_build_p2wpkh_script(FULL_SATOSHIS, &recipient); - - // Build inscription output. - let output = call_ffi_build_nft_inscription(mime_type, &payload, satoshis, &recipient); - - // Construct Protobuf payload. - let signing = ProtoSigningInputBuilder::new() - .private_key(&privkey) - .input( - ProtoTransactionBuilder::new() - .txid(&txid) - .vout(0) - .script_pubkey(&input.script) - .satoshis(FULL_SATOSHIS) - .variant(TransactionVariant::P2WPKH) - .build(), - ) - .output( - ProtoTransactionBuilder::new() - .script_pubkey(&output.script) - .satoshis(INSCRIBE_SATOSHIS) - .variant(TransactionVariant::NFTINSCRIPTION) - .build(), - ) - .build(); - - let signed = taproot_build_and_sign_transaction(signing).unwrap(); - assert_eq!(hex::encode(&signed.encoded, false), COMMIT_RAW_TX); -} - -/// Reveal the Inscription. -#[test] -fn proto_sign_nft_inscription_reveal() { - use crate::tests::nft::*; - - // Prepare keys. - let alice = keypair_from_wif(ALICE_WIF).unwrap(); - let privkey = alice.secret_bytes(); - let recipient = Recipient::::from(&alice); - - let mime_type = b"image/png"; - let payload = hex::decode(crate::tests::data::NFT_INSCRIPTION_IMAGE_DATA).unwrap(); - let satoshis: u64 = 1_000; - - // Note that the Txid must be reversed. - let txid = reverse_txid(REVEAL_TXID); - - // Build inscription input. - let input = call_ffi_build_nft_inscription(mime_type, &payload, satoshis, &recipient); - - // Build inscription output. - let output_p2wpkh = call_ffi_build_p2wpkh_script(DUST_SATOSHIS, &recipient); - - // Construct Protobuf payload. - let signing = ProtoSigningInputBuilder::new() - .private_key(&privkey) - .input( - ProtoTransactionBuilder::new() - .txid(&txid) - .vout(0) - .script_pubkey(&input.script) - .satoshis(INSCRIBE_SATOSHIS) - .variant(TransactionVariant::NFTINSCRIPTION) - // IMPORANT: include the witness containing the actual inscription. - .spending_script(&input.spendingScript) - .build(), - ) - .output( - ProtoTransactionBuilder::new() - .script_pubkey(&output_p2wpkh.script) - .satoshis(DUST_SATOSHIS) - .variant(TransactionVariant::P2WPKH) - .build(), - ) - .build(); - - let signed = taproot_build_and_sign_transaction(signing).unwrap(); - let hex = hex::encode(&signed.encoded, false); - - assert_eq!(hex[..164], REVEAL_RAW_TX[..164]); - // We ignore the 64-byte Schnorr signature, since it uses random data for - // signing on each construction and is therefore not reproducible. - assert_ne!(hex[164..292], REVEAL_RAW_TX[164..292]); - assert_eq!(hex[292..], REVEAL_RAW_TX[292..]); -} diff --git a/rust/tw_bitcoin/src/tests/ffi/scripts.rs b/rust/tw_bitcoin/src/tests/ffi/scripts.rs deleted file mode 100644 index b0cc8853f7e..00000000000 --- a/rust/tw_bitcoin/src/tests/ffi/scripts.rs +++ /dev/null @@ -1,77 +0,0 @@ -use crate::tests::ffi::utils::{ - call_ffi_build_p2pkh_script, call_ffi_build_p2tr_key_path_script, call_ffi_build_p2wpkh_script, -}; -use crate::tests::p2pkh::ALICE_WIF; -use crate::{keypair_from_wif, Recipient, TxOutputP2PKH, TxOutputP2TRKeyPath, TxOutputP2WPKH}; -use bitcoin::PublicKey; -use std::borrow::Cow; -use tw_proto::Bitcoin::Proto::TransactionOutput; - -#[test] -fn proto_build_p2pkh_script() { - // Prepare keys. - let keypair: secp256k1::KeyPair = keypair_from_wif(ALICE_WIF).unwrap(); - let recipient = Recipient::::from(keypair); - - let satoshis: u64 = 1_000; - - // Call FFI function. - let ffi_out = call_ffi_build_p2pkh_script(satoshis, &recipient); - - // Compare with native call. - let tx_out = TxOutputP2PKH::new(satoshis, recipient); - // Wrap in Protobuf structure. - let proto = TransactionOutput { - value: satoshis as i64, - script: Cow::from(tx_out.script_pubkey.as_bytes()), - spendingScript: Cow::default(), - }; - - assert_eq!(ffi_out, proto); -} - -#[test] -fn proto_build_p2wpkh_script() { - // Prepare keys. - let keypair: secp256k1::KeyPair = keypair_from_wif(ALICE_WIF).unwrap(); - let recipient = Recipient::::from(keypair); - - let satoshis: u64 = 1_000; - - // Call FFI function. - let ffi_out = call_ffi_build_p2wpkh_script(satoshis, &recipient); - - // Compare with native call. - let tx_out = TxOutputP2WPKH::new(satoshis, recipient.try_into().unwrap()); - // Wrap in Protobuf structure. - let proto = TransactionOutput { - value: satoshis as i64, - script: Cow::from(tx_out.script_pubkey.as_bytes()), - spendingScript: Cow::default(), - }; - - assert_eq!(ffi_out, proto); -} - -#[test] -fn proto_build_p2tr_key_path_script() { - // Prepare keys. - let keypair: secp256k1::KeyPair = keypair_from_wif(ALICE_WIF).unwrap(); - let recipient = Recipient::::from(keypair); - - let satoshis: u64 = 1_000; - - // Call FFI function. - let ffi_out = call_ffi_build_p2tr_key_path_script(satoshis, &recipient); - - // Compare with native call. - let tx_out = TxOutputP2TRKeyPath::new(satoshis, recipient.try_into().unwrap()); - // Wrap in Protobuf structure. - let proto = TransactionOutput { - value: satoshis as i64, - script: Cow::from(tx_out.script_pubkey.as_bytes()), - spendingScript: Cow::default(), - }; - - assert_eq!(ffi_out, proto); -} diff --git a/rust/tw_bitcoin/src/tests/ffi/transaction.rs b/rust/tw_bitcoin/src/tests/ffi/transaction.rs deleted file mode 100644 index 10d8035d92b..00000000000 --- a/rust/tw_bitcoin/src/tests/ffi/transaction.rs +++ /dev/null @@ -1,138 +0,0 @@ -use crate::ffi::taproot_build_and_sign_transaction; -use crate::tests::ffi::utils::{ - call_ffi_build_p2pkh_script, call_ffi_build_p2tr_key_path_script, call_ffi_build_p2wpkh_script, - reverse_txid, ProtoSigningInputBuilder, ProtoTransactionBuilder, -}; -use crate::{keypair_from_wif, Recipient}; -use bitcoin::PublicKey; -use tw_encoding::hex; -use tw_proto::Bitcoin::Proto::TransactionVariant; - -#[test] -pub fn proto_sign_input_p2pkh_output_p2pkh() { - use crate::tests::p2pkh::*; - - // Prepare keys. - let alice: secp256k1::KeyPair = keypair_from_wif(ALICE_WIF).unwrap(); - let alice_privkey = alice.secret_bytes(); - let alice_recipient = Recipient::::from_keypair(&alice); - - let bob = keypair_from_wif(BOB_WIF).unwrap(); - let bob_recipient = Recipient::::from_keypair(&bob); - - let txid = reverse_txid(TXID); - - // Prepare the scripts. - let input = call_ffi_build_p2pkh_script(FULL_SATOSHIS, &alice_recipient); - let output = call_ffi_build_p2pkh_script(SEND_SATOSHIS, &bob_recipient); - - // Construct Protobuf payload. - let signing = ProtoSigningInputBuilder::new() - .private_key(&alice_privkey) - .input( - ProtoTransactionBuilder::new() - .txid(&txid) - .vout(0) - .script_pubkey(&input.script) - .satoshis(FULL_SATOSHIS) - .variant(TransactionVariant::P2PKH) - .build(), - ) - .output( - ProtoTransactionBuilder::new() - .script_pubkey(&output.script) - .satoshis(SEND_SATOSHIS) - .variant(TransactionVariant::P2PKH) - .build(), - ) - .build(); - - let signed = taproot_build_and_sign_transaction(signing).unwrap(); - assert_eq!(hex::encode(&signed.encoded, false), TX_RAW); -} - -#[test] -pub fn proto_sign_input_p2pkh_output_p2wpkh() { - use crate::tests::p2wpkh::*; - - // Prepare keys. - let alice: secp256k1::KeyPair = keypair_from_wif(ALICE_WIF).unwrap(); - let alice_privkey = alice.secret_bytes(); - let alice_recipient = Recipient::::from_keypair(&alice); - - let bob = keypair_from_wif(BOB_WIF).unwrap(); - let bob_recipient = Recipient::::from_keypair(&bob); - - let txid = reverse_txid(TXID); - - // Prepare the scripts. - let input = call_ffi_build_p2pkh_script(FULL_SATOSHIS, &alice_recipient); - let output = call_ffi_build_p2wpkh_script(SEND_SATOSHIS, &bob_recipient); - - // Construct Protobuf payload. - let signing = ProtoSigningInputBuilder::new() - .private_key(&alice_privkey) - .input( - ProtoTransactionBuilder::new() - .txid(&txid) - .vout(0) - .script_pubkey(&input.script) - .satoshis(FULL_SATOSHIS) - .variant(TransactionVariant::P2PKH) - .build(), - ) - .output( - ProtoTransactionBuilder::new() - .script_pubkey(&output.script) - .satoshis(SEND_SATOSHIS) - .variant(TransactionVariant::P2WPKH) - .build(), - ) - .build(); - - let signed = taproot_build_and_sign_transaction(signing).unwrap(); - assert_eq!(hex::encode(&signed.encoded, false), TX_RAW); -} - -#[test] -pub fn proto_sign_input_p2pkh_output_p2tr_key_path() { - use crate::tests::p2tr_key_path::*; - - // Prepare keys. - let alice: secp256k1::KeyPair = keypair_from_wif(ALICE_WIF).unwrap(); - let alice_privkey = alice.secret_bytes(); - let alice_recipient = Recipient::::from_keypair(&alice); - - let bob = keypair_from_wif(BOB_WIF).unwrap(); - let bob_recipient = Recipient::::from_keypair(&bob); - - let txid = reverse_txid(FIRST_TXID); - - // Prepare the scripts. - let input = call_ffi_build_p2pkh_script(FULL_SATOSHIS, &alice_recipient); - let output = call_ffi_build_p2tr_key_path_script(SEND_SATOSHIS_TO_BOB, &bob_recipient); - - // Construct Protobuf payload. - let signing = ProtoSigningInputBuilder::new() - .private_key(&alice_privkey) - .input( - ProtoTransactionBuilder::new() - .txid(&txid) - .vout(0) - .script_pubkey(&input.script) - .satoshis(FULL_SATOSHIS) - .variant(TransactionVariant::P2PKH) - .build(), - ) - .output( - ProtoTransactionBuilder::new() - .script_pubkey(&output.script) - .satoshis(SEND_SATOSHIS_TO_BOB) - .variant(TransactionVariant::P2TRKEYPATH) - .build(), - ) - .build(); - - let signed = taproot_build_and_sign_transaction(signing).unwrap(); - assert_eq!(hex::encode(&signed.encoded, false), FIRST_TX_RAW); -} diff --git a/rust/tw_bitcoin/src/tests/ffi/utils.rs b/rust/tw_bitcoin/src/tests/ffi/utils.rs deleted file mode 100644 index 67e041c3938..00000000000 --- a/rust/tw_bitcoin/src/tests/ffi/utils.rs +++ /dev/null @@ -1,236 +0,0 @@ -use crate::ffi::{ - tw_bitcoin_build_nft_inscription, tw_build_brc20_transfer_inscription, tw_build_p2pkh_script, - tw_build_p2tr_key_path_script, tw_build_p2wpkh_script, -}; -use crate::Recipient; -use bitcoin::PublicKey; -use std::borrow::Cow; -use std::ffi::CString; -use tw_proto::Bitcoin::Proto::{ - OutPoint, SigningInput, TransactionOutput, TransactionPlan, TransactionVariant, - UnspentTransaction, -}; - -/// Convenience function for reversing the Txid before it's being passed on to -/// the FFI. -pub fn reverse_txid(txid: &str) -> Vec { - tw_encoding::hex::decode(txid) - .unwrap() - .into_iter() - .rev() - .collect() -} - -/// Convenience wrapper over `tw_build_p2pkh_script` with Protobuf -/// deserialization support. -pub fn call_ffi_build_p2pkh_script<'a, 'b>( - satoshis: u64, - // We use 'b to clarify that `recipient` is not tied to the return value. - recipient: &'b Recipient, -) -> TransactionOutput<'a> { - let pubkey = recipient.public_key().to_bytes(); - - let raw = - unsafe { tw_build_p2pkh_script(satoshis as i64, pubkey.as_ptr(), pubkey.len()).into_vec() }; - - let des: TransactionOutput = tw_proto::deserialize(&raw).unwrap(); - - // We convert the referenced data into owned data since `raw` goes out of - // scope at the end of the function. - TransactionOutput { - value: des.value, - script: des.script.into_owned().into(), - spendingScript: des.spendingScript.into_owned().into(), - } -} - -/// Convenience wrapper over `tw_build_p2wpkh_script` with Protobuf -/// deserialization support. -pub fn call_ffi_build_p2wpkh_script<'a, 'b>( - satoshis: u64, - // We use 'b to clarify that `recipient` is not tied to the return value. - recipient: &'b Recipient, -) -> TransactionOutput<'a> { - let pubkey = recipient.public_key().to_bytes(); - - let raw = unsafe { - tw_build_p2wpkh_script(satoshis as i64, pubkey.as_ptr(), pubkey.len()).into_vec() - }; - - let des: TransactionOutput = tw_proto::deserialize(&raw).unwrap(); - - // We convert the referenced data into owned data since `raw` goes out of - // scope at the end of the function. - TransactionOutput { - value: des.value, - script: des.script.into_owned().into(), - spendingScript: des.spendingScript.into_owned().into(), - } -} - -/// Convenience wrapper over `tw_build_p2tr_key_path_script` with Protobuf -/// deserialization support. -pub fn call_ffi_build_p2tr_key_path_script<'a, 'b>( - satoshis: u64, - // We use 'b to clarify that `recipient` is not tied to the return value. - recipient: &'b Recipient, -) -> TransactionOutput<'a> { - let pubkey = recipient.public_key().to_bytes(); - - let raw = unsafe { - tw_build_p2tr_key_path_script(satoshis as i64, pubkey.as_ptr(), pubkey.len()).into_vec() - }; - - let des: TransactionOutput = tw_proto::deserialize(&raw).unwrap(); - - // We convert the referenced data into owned data since `raw` goes out of - // scope at the end of the function. - TransactionOutput { - value: des.value, - script: des.script.into_owned().into(), - spendingScript: des.spendingScript.into_owned().into(), - } -} - -/// Convenience wrapper over `tw_build_brc20_inscribe_transfer` with Protobuf -/// deserialization support. -pub fn call_ffi_build_brc20_transfer_script<'a, 'b>( - ticker: &str, - brc20_amount: u64, - satoshis: u64, - // We use 'b to clarify that `recipient` is not tied to the return value. - recipient: &'b Recipient, -) -> TransactionOutput<'a> { - let pubkey = recipient.public_key().to_bytes(); - let c_ticker = CString::new(ticker).unwrap(); - - let raw = unsafe { - tw_build_brc20_transfer_inscription( - c_ticker.as_ptr(), - brc20_amount, - satoshis as i64, - pubkey.as_ptr(), - pubkey.len(), - ) - .into_vec() - }; - - let des: TransactionOutput = tw_proto::deserialize(&raw).unwrap(); - - // We convert the referenced data into owned data since `raw` goes out of - // scope at the end of the function. - TransactionOutput { - value: des.value, - script: des.script.into_owned().into(), - spendingScript: des.spendingScript.into_owned().into(), - } -} - -/// Convenience wrapper over `tw_bitcoin_build_nft_inscription` with Protobuf -/// deserialization support. -pub fn call_ffi_build_nft_inscription<'a, 'b>( - mime_type: &[u8], - data: &[u8], - satoshis: u64, - // We use 'b to clarify that `recipient` is not tied to the return value. - recipient: &'b Recipient, -) -> TransactionOutput<'a> { - let pubkey = recipient.public_key().to_bytes(); - let c_mime_type = CString::new(mime_type).unwrap(); - - let raw = unsafe { - tw_bitcoin_build_nft_inscription( - c_mime_type.as_ptr(), - data.as_ptr(), - data.len(), - satoshis as i64, - pubkey.as_ptr(), - pubkey.len(), - ) - .into_vec() - }; - - let des: TransactionOutput = tw_proto::deserialize(&raw).unwrap(); - - // We convert the referenced data into owned data since `raw` goes out of - // scope at the end of the function. - TransactionOutput { - value: des.value, - script: des.script.into_owned().into(), - spendingScript: des.spendingScript.into_owned().into(), - } -} - -/// Builder for creating the `SigningInput` Protobuf structure. -pub struct ProtoSigningInputBuilder<'a> { - inner: SigningInput<'a>, -} - -impl<'a> ProtoSigningInputBuilder<'a> { - pub fn new() -> Self { - let signing = SigningInput { - plan: Some(TransactionPlan::default()), - ..Default::default() - }; - - ProtoSigningInputBuilder { inner: signing } - } - pub fn private_key(mut self, privkey: &'a [u8]) -> Self { - self.inner.private_key = vec![Cow::from(privkey)]; - self - } - pub fn input(mut self, tx: UnspentTransaction<'a>) -> Self { - self.inner.utxo.push(tx); - self - } - pub fn output(mut self, tx: UnspentTransaction<'a>) -> Self { - self.inner.plan.as_mut().unwrap().utxos.push(tx); - self - } - pub fn build(self) -> SigningInput<'a> { - self.inner - } -} - -/// Builder for creating the `UnspentTransaction` Protobuf structure. -pub struct ProtoTransactionBuilder<'a> { - inner: UnspentTransaction<'a>, -} - -impl<'a> ProtoTransactionBuilder<'a> { - pub fn new() -> Self { - let unspent = UnspentTransaction { - out_point: Some(OutPoint::default()), - ..Default::default() - }; - - ProtoTransactionBuilder { inner: unspent } - } - pub fn txid(mut self, slice: &'a [u8]) -> Self { - self.inner.out_point.as_mut().unwrap().hash = slice.into(); - self - } - pub fn vout(mut self, vout: u32) -> Self { - self.inner.out_point.as_mut().unwrap().index = vout; - self - } - pub fn variant(mut self, variant: TransactionVariant) -> Self { - self.inner.variant = variant; - self - } - pub fn satoshis(mut self, satoshis: u64) -> Self { - self.inner.amount = satoshis as i64; - self - } - pub fn script_pubkey(mut self, script: &'a [u8]) -> Self { - self.inner.script = script.into(); - self - } - pub fn spending_script(mut self, script: &'a [u8]) -> Self { - self.inner.spendingScript = script.into(); - self - } - pub fn build(self) -> UnspentTransaction<'a> { - self.inner - } -} diff --git a/rust/tw_bitcoin/src/tests/mod.rs b/rust/tw_bitcoin/src/tests/mod.rs deleted file mode 100644 index d95d0a493c7..00000000000 --- a/rust/tw_bitcoin/src/tests/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -mod address; -mod brc20_transfer; -mod data; -mod fee; -mod ffi; -mod nft; -mod p2pkh; -mod p2tr_key_path; -mod p2wpkh; - -pub const ONE_BTC: u64 = 100_000_000; diff --git a/rust/tw_bitcoin/src/tests/nft.rs b/rust/tw_bitcoin/src/tests/nft.rs deleted file mode 100644 index 20e262307cb..00000000000 --- a/rust/tw_bitcoin/src/tests/nft.rs +++ /dev/null @@ -1,95 +0,0 @@ -use crate::nft::OrdinalNftInscription; -use crate::{ - keypair_from_wif, TXOutputP2TRScriptPath, TransactionBuilder, TxInputP2TRScriptPath, - TxInputP2WPKH, TxOutputP2WPKH, -}; -use bitcoin::Txid; -use std::str::FromStr; -use tw_encoding::hex; - -pub const ALICE_WIF: &str = "L4of5AJ6aKmvChg7gQ7m2RzHFgpWe5Uirmuey1fXJ1FtfmXj59LW"; -pub const FULL_SATOSHIS: u64 = 32_400; -pub const INSCRIBE_SATOSHIS: u64 = FULL_SATOSHIS - MINER_FEE; -pub const DUST_SATOSHIS: u64 = 546; - -pub const MINER_FEE: u64 = 1_300; - -pub const COMMIT_TXID: &str = "579590c3227253ad423b1e7e3c5b073b8a280d307c68aecd779df2600daa2f99"; -pub const COMMIT_RAW_TX: &str = "02000000000101992faa0d60f29d77cdae687c300d288a3b075b3c7e1e3b42ad537222c39095570000000000ffffffff017c790000000000002251202ac69a7e9dba801e9fcba826055917b84ca6fba4d51a29e47d478de603eedab602473044022054212984443ed4c66fc103d825bfd2da7baf2ab65d286e3c629b36b98cd7debd022050214cfe5d3b12a17aaaf1a196bfeb2f0ad15ffb320c4717eb7614162453e4fe0121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000"; - -pub const REVEAL_TXID: &str = "f1e708e5c5847339e16accf8716c14b33717c14d6fe68f9db36627cecbde7117"; -pub const REVEAL_RAW_TX: &str = super::data::NFT_INSCRIPTION_RAW_HEX; - -#[test] -fn inscribe_nft() { - let alice = keypair_from_wif(ALICE_WIF).unwrap(); - - let payload = hex::decode(super::data::NFT_INSCRIPTION_IMAGE_DATA).unwrap(); - let nft_inscription = OrdinalNftInscription::new(b"image/png", &payload, alice.into()).unwrap(); - - let txid = Txid::from_str(COMMIT_TXID).unwrap(); - - // Commit NFT. - let input = TxInputP2WPKH::builder() - .txid(txid) - .vout(0) - .recipient(alice.try_into().unwrap()) - .satoshis(FULL_SATOSHIS) - .build() - .unwrap(); - - let output = TXOutputP2TRScriptPath::builder() - .recipient(nft_inscription.inscription().recipient().clone()) - .satoshis(INSCRIBE_SATOSHIS) - .build() - .unwrap(); - - let transaction = TransactionBuilder::new() - .add_input(input.into()) - .add_output(output.into()) - .sign_inputs(alice) - .unwrap() - .serialize() - .unwrap(); - - let hex = hex::encode(&transaction, false); - assert_eq!(hex, COMMIT_RAW_TX); - - // Successfully broadcasted: https://www.blockchain.com/explorer/transactions/btc/f1e708e5c5847339e16accf8716c14b33717c14d6fe68f9db36627cecbde7117 - - let txid = Txid::from_str(REVEAL_TXID).unwrap(); - - // Reveal NFT. - let input = TxInputP2TRScriptPath::builder() - .txid(txid) - .vout(0) - .recipient(nft_inscription.inscription().recipient().clone()) - .satoshis(INSCRIBE_SATOSHIS) - .script(nft_inscription.inscription().taproot_program().to_owned()) - .spend_info(nft_inscription.inscription().spend_info().clone()) - .build() - .unwrap(); - - let output = TxOutputP2WPKH::builder() - .recipient(alice.try_into().unwrap()) - .satoshis(DUST_SATOSHIS) - .build() - .unwrap(); - - let transaction = TransactionBuilder::new() - .add_input(input.into()) - .add_output(output.into()) - .sign_inputs(alice) - .unwrap() - .serialize() - .unwrap(); - - let hex = hex::encode(&transaction, false); - assert_eq!(hex[..164], REVEAL_RAW_TX[..164]); - // We ignore the 64-byte Schnorr signature, since it uses random data for - // signing on each construction and is therefore not reproducible. - assert_ne!(hex[164..292], REVEAL_RAW_TX[164..292]); - assert_eq!(hex[292..], REVEAL_RAW_TX[292..]); - - // Successfully broadcasted: https://www.blockchain.com/explorer/transactions/btc/173f8350b722243d44cc8db5584de76b432eb6d0888d9e66e662db51584f44ac -} diff --git a/rust/tw_bitcoin/src/tests/p2pkh.rs b/rust/tw_bitcoin/src/tests/p2pkh.rs deleted file mode 100644 index 238ba909abf..00000000000 --- a/rust/tw_bitcoin/src/tests/p2pkh.rs +++ /dev/null @@ -1,54 +0,0 @@ -use super::*; -use crate::{keypair_from_wif, TransactionBuilder, TxInputP2PKH, TxOutputP2PKH}; -use bitcoin::Txid; -use std::str::FromStr; -use tw_encoding::hex; - -// Those private keys were used in a Bitcoin regtest environment. -pub const ALICE_WIF: &str = "cQUNzeMnF9xPPLqZhH7hMVYGwSuu3b78zznuc5UrxgXnYQBq6Bx1"; -pub const BOB_WIF: &str = "cTk5wSci88FPka7JwHpNEA82dUMjAysdDbCiuYB2fegfgGESAZVn"; -pub const TXID: &str = "1e1cdc48aa990d7e154a161d5b5f1cad737742e97d2712ab188027bb42e6e47b"; - -pub const FULL_SATOSHIS: u64 = ONE_BTC * 50; -pub const MINER_FEE: u64 = ONE_BTC / 100; -pub const SEND_SATOSHIS: u64 = FULL_SATOSHIS - MINER_FEE; - -// This passed the `bitcoin-cli -retest testmempoolaccept` command. -pub const TX_RAW: &str = "02000000017be4e642bb278018ab12277de9427773ad1c5f5b1d164a157e0d99aa48dc1c1e000000006a473044022078eda020d4b86fcb3af78ef919912e6d79b81164dbbb0b0b96da6ac58a2de4b102201a5fd8d48734d5a02371c4b5ee551a69dca3842edbf577d863cf8ae9fdbbd4590121036666dd712e05a487916384bfcd5973eb53e8038eccbbf97f7eed775b87389536ffffffff01c0aff629010000001976a9145eaaa4f458f9158f86afcba08dd7448d27045e3d88ac00000000"; - -#[test] -fn sign_input_p2pkh_output_p2pkh() { - // This passed the `bitcoin-cli -retest testmempoolaccept` command. - - let alice = keypair_from_wif(ALICE_WIF).unwrap(); - let bob = keypair_from_wif(BOB_WIF).unwrap(); - - // Prepare inputs for Alice. - let input = TxInputP2PKH::builder() - .txid(Txid::from_str(TXID).unwrap()) - .vout(0) - .recipient(alice) - .satoshis(FULL_SATOSHIS) - .build() - .unwrap(); - - // Prepare outputs for Bob. - let output = TxOutputP2PKH::builder() - .satoshis(SEND_SATOSHIS) - .recipient(bob) - .build() - .unwrap(); - - // Alice signs the transaction. - let signed_transaction = TransactionBuilder::new() - .miner_fee(MINER_FEE) - .add_input(input.into()) - .add_output(output.into()) - .sign_inputs(alice) - .unwrap() - .serialize() - .unwrap(); - - let hex = hex::encode(&signed_transaction, false); - assert_eq!(&hex, TX_RAW); -} diff --git a/rust/tw_bitcoin/src/tests/p2tr_key_path.rs b/rust/tw_bitcoin/src/tests/p2tr_key_path.rs deleted file mode 100644 index cf876fb8975..00000000000 --- a/rust/tw_bitcoin/src/tests/p2tr_key_path.rs +++ /dev/null @@ -1,94 +0,0 @@ -use super::ONE_BTC; -use crate::{ - keypair_from_wif, TransactionBuilder, TxInputP2PKH, TxInputP2TRKeyPath, TxOutputP2TRKeyPath, -}; -use bitcoin::Txid; -use std::str::FromStr; -use tw_encoding::hex; - -// Those private keys were used in a Bitcoin regtest environment. -pub const ALICE_WIF: &str = "cNDFvH3TXCjxgWeVc7vbu4Jw5m2Lu8FkQ69Z2XvFUD9D9rGjofN1"; -pub const BOB_WIF: &str = "cNt3XNHiJdJpoX5zt3CXY8ncgrCted8bxmFBzcGeTZbBw6jkByWB"; - -pub const FULL_SATOSHIS: u64 = ONE_BTC * 50; -pub const MINER_FEE: u64 = ONE_BTC / 100; -pub const SEND_SATOSHIS_TO_BOB: u64 = FULL_SATOSHIS - MINER_FEE; - -// The raw transactions passed the `bitcoin-cli -retest testmempoolaccept` command. -pub const FIRST_TXID: &str = "c50563913e5a838f937c94232f5a8fc74e58b629fae41dfdffcc9a70f833b53a"; -pub const FIRST_TX_RAW: &str = "02000000013ab533f8709accfffd1de4fa29b6584ec78f5a2f23947c938f835a3e916305c5000000006b48304502210086ab2c2192e2738529d6cd9604d8ee75c5b09b0c2f4066a5c5fa3f87a26c0af602202afc7096aaa992235c43e712146057b5ed6a776d82b9129620bc5a21991c0a5301210351e003fdc48e7f31c9bc94996c91f6c3273b7ef4208a1686021bedf7673bb058ffffffff01c0aff62901000000225120e01cfdd05da8fa1d71f987373f3790d45dea9861acb0525c86656fe50f4397a600000000"; - -pub const SEND_SATOSHIS_TO_ALICE: u64 = SEND_SATOSHIS_TO_BOB - MINER_FEE; -pub const SECOND_TXID: &str = "9a582032f6a50cedaff77d3d5604b33adf8bc31bdaef8de977c2187e395860ac"; -pub const SECOND_TX_RAW: &str = "02000000000101ac6058397e18c277e98defda1bc38bdf3ab304563d7df7afed0ca5f63220589a0000000000ffffffff01806de72901000000225120a5c027857e359d19f625e52a106b8ac6ca2d6a8728f6cf2107cd7958ee0787c20140ec2d3910d41506b60aaa20520bb72f15e2d2cbd97e3a8e26ee7bad5f4c56b0f2fb0ceaddac33cb2813a33ba017ba6b1d011bab74a0426f12a2bcf47b4ed5bc8600000000"; - -#[test] -fn sign_input_p2pkh_output_p2tr_key_path() { - let alice = keypair_from_wif(ALICE_WIF).unwrap(); - let bob = keypair_from_wif(BOB_WIF).unwrap(); - - // # First transaction: Alice spends the P2PKH coinbase input and creates - // # a P2WPKH output for Bob. - - // Prepare inputs for Alice. - let input = TxInputP2PKH::builder() - .txid(Txid::from_str(FIRST_TXID).unwrap()) - .vout(0) - .recipient(alice) - .satoshis(FULL_SATOSHIS) - .build() - .unwrap(); - - // Prepare outputs for Bob. - let output = TxOutputP2TRKeyPath::builder() - .recipient(bob) - .satoshis(SEND_SATOSHIS_TO_BOB) - .build() - .unwrap(); - - // Alice signs the transaction. - let signed_transaction = TransactionBuilder::new() - .miner_fee(MINER_FEE) - .add_input(input.into()) - .add_output(output.into()) - .sign_inputs(alice) - .unwrap() - .serialize() - .unwrap(); - - let hex = hex::encode(&signed_transaction, false); - assert_eq!(&hex, FIRST_TX_RAW); - - // # Second transaction: Bob spends the P2WPKH input and creates - // # a P2WPKH output for Alice. - - // Transaction was submitted in regtest env via `sendrawtransaction` and - // mined with `-generate 1` - let input = TxInputP2TRKeyPath::builder() - .txid(Txid::from_str(SECOND_TXID).unwrap()) - .vout(0) - .recipient(bob) - .satoshis(SEND_SATOSHIS_TO_BOB) - .build() - .unwrap(); - - // Prepare outputs for Bob. - let output = TxOutputP2TRKeyPath::builder() - .recipient(alice) - .satoshis(SEND_SATOSHIS_TO_ALICE) - .build() - .unwrap(); - - // Alice signs the transaction. - let signed_transaction = TransactionBuilder::new() - .miner_fee(MINER_FEE) - .add_input(input.into()) - .add_output(output.into()) - .sign_inputs(bob) - .unwrap() - .serialize() - .unwrap(); - - let hex = hex::encode(&signed_transaction, false); - assert_eq!(hex, SECOND_TX_RAW); -} diff --git a/rust/tw_bitcoin/src/tests/p2wpkh.rs b/rust/tw_bitcoin/src/tests/p2wpkh.rs deleted file mode 100644 index 1422e825896..00000000000 --- a/rust/tw_bitcoin/src/tests/p2wpkh.rs +++ /dev/null @@ -1,92 +0,0 @@ -use super::*; -use crate::{keypair_from_wif, TransactionBuilder, TxInputP2PKH, TxInputP2WPKH, TxOutputP2WPKH}; -use bitcoin::Txid; -use std::str::FromStr; -use tw_encoding::hex; - -// Those private keys were used in a Bitcoin regtest environment. -pub const ALICE_WIF: &str = "cQX5ePcXjTx7C5p6xV8zkp2NN9unhZx4a8RQVPiHd52WxoApV6yK"; -pub const BOB_WIF: &str = "cMn7SSCtE5yt2PS97P4NCMvxpCVvT4cBuHiCzKFW5XMvio4fQbD1"; -pub const TXID: &str = "181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911"; - -pub const FULL_SATOSHIS: u64 = ONE_BTC * 50; -pub const MINER_FEE: u64 = ONE_BTC / 100; -pub const SEND_SATOSHIS: u64 = FULL_SATOSHIS - MINER_FEE; - -// This passed the `bitcoin-cli -retest testmempoolaccept` command. -pub const TX_RAW: &str = "020000000111b9f62923af73e297abb69f749e7a1aa2735fbdfd32ac5f6aa89e5c96841c18000000006b483045022100df9ed0b662b759e68b89a42e7144cddf787782a7129d4df05642dd825930e6e6022051a08f577f11cc7390684bbad2951a6374072253ffcf2468d14035ed0d8cd6490121028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28fffffffff01c0aff629010000001600140d0e1cec6c2babe8badde5e9b3dea667da90036d00000000"; - -#[test] -fn sign_input_p2pkh_and_p2wpkh_output_p2wpkh() { - let alice = keypair_from_wif(ALICE_WIF).unwrap(); - let bob = keypair_from_wif(BOB_WIF).unwrap(); - - // # First transaction: Alice spends the P2PKH coinbase input and creates - // # a P2WPKH output for Bob. - - // Prepare inputs for Alice. - let input = TxInputP2PKH::builder() - .txid(Txid::from_str(TXID).unwrap()) - .vout(0) - .recipient(alice) - .satoshis(FULL_SATOSHIS) - .build() - .unwrap(); - - // Prepare outputs for Bob. - let output = TxOutputP2WPKH::builder() - .recipient(bob.try_into().unwrap()) - .satoshis(SEND_SATOSHIS) - .build() - .unwrap(); - - // Alice signs the transaction. - let signed_transaction = TransactionBuilder::new() - .miner_fee(MINER_FEE) - .add_input(input.into()) - .add_output(output.into()) - .sign_inputs(alice) - .unwrap() - .serialize() - .unwrap(); - - let hex = hex::encode(&signed_transaction, false); - assert_eq!(&hex, TX_RAW); - - // # Second transaction: Bob spends the P2WPKH input and creates - // # a P2WPKH output for Alice. - - // Transaction was submitted in regtest env via `sendrawtransaction` and - // mined with `-generate 1` - const TX_RAW_SECOND: &str = "020000000001016e1f16dcfafbb3a83697f6c23c624cd71085a7f8a25ce0bd9743a41d0a458e850000000000ffffffff01806de7290100000016001460cda7b50f14c152d7401c28ae773c698db9237302483045022100a9b517de5a5e036d7133df499b5b751db6f9a01576a6c5dc38229ec08b6c45cd02200e42c9f8c707c9bf0ceab4f739ec8d683dc1f1f29e195a8da9bc183584d624a60121025a0af1510f0f24d40dd00d7c0e51605ca504bbc177c3e19b065f373a1efdd22f00000000"; - const LATEST_TXID: &str = "858e450a1da44397bde05ca2f8a78510d74c623cc2f69736a8b3fbfadc161f6e"; - const SEND_TO_ALICE: u64 = SEND_SATOSHIS - MINER_FEE; - - let input = TxInputP2WPKH::builder() - .txid(Txid::from_str(LATEST_TXID).unwrap()) - .vout(0) - .recipient(bob.try_into().unwrap()) - .satoshis(SEND_SATOSHIS) - .build() - .unwrap(); - - // Prepare outputs for Bob. - let output = TxOutputP2WPKH::builder() - .recipient(alice.try_into().unwrap()) - .satoshis(SEND_TO_ALICE) - .build() - .unwrap(); - - // Alice signs the transaction. - let signed_transaction = TransactionBuilder::new() - .miner_fee(MINER_FEE) - .add_input(input.into()) - .add_output(output.into()) - .sign_inputs(bob) - .unwrap() - .serialize() - .unwrap(); - - let hex = hex::encode(&signed_transaction, false); - assert_eq!(&hex, TX_RAW_SECOND); -} diff --git a/rust/tw_bitcoin/src/transaction.rs b/rust/tw_bitcoin/src/transaction.rs deleted file mode 100644 index 7706f85eca8..00000000000 --- a/rust/tw_bitcoin/src/transaction.rs +++ /dev/null @@ -1,255 +0,0 @@ -use crate::claim::{ClaimLocation, TransactionSigner}; -use crate::input::*; -use crate::output::*; -use crate::{Error, Result}; -use bitcoin::blockdata::locktime::absolute::{Height, LockTime}; -use bitcoin::consensus::Encodable; -use bitcoin::sighash::{EcdsaSighashType, SighashCache, TapSighashType}; -use bitcoin::taproot::{LeafVersion, TapLeafHash}; -use bitcoin::{secp256k1, Address, TxIn, TxOut}; -use bitcoin::{Transaction, Weight}; - -/// Determines the weight of the transaction and calculates the fee with the -/// given satoshis per vbyte. -pub fn calculate_fee(tx: &Transaction, sat_vb: u64) -> (Weight, u64) { - let weight = tx.weight(); - (weight, weight.to_vbytes_ceil() * sat_vb) -} - -#[derive(Debug, Clone)] -pub struct TransactionBuilder { - pub version: i32, - pub lock_time: LockTime, - inputs: Vec, - outputs: Vec, - miner_fee: Option, - return_address: Option
, - contains_taproot: bool, -} - -impl Default for TransactionBuilder { - fn default() -> Self { - TransactionBuilder { - version: 2, - // No lock time, transaction is immediately spendable. - lock_time: LockTime::Blocks(Height::ZERO), - inputs: vec![], - outputs: vec![], - miner_fee: None, - return_address: None, - contains_taproot: false, - } - } -} - -impl TransactionBuilder { - pub fn new() -> Self { - Self::default() - } - pub fn version(mut self, version: i32) -> Self { - self.version = version; - self - } - pub fn lock_time_height(mut self, height: u32) -> Result { - self.lock_time = LockTime::Blocks(Height::from_consensus(height).map_err(|_| Error::Todo)?); - Ok(self) - } - pub fn return_address(mut self, address: Address) -> Self { - self.return_address = Some(address); - self - } - pub fn miner_fee(mut self, satoshis: u64) -> Self { - self.miner_fee = Some(satoshis); - self - } - pub fn add_input(mut self, input: TxInput) -> Self { - match input { - TxInput::P2TRKeyPath(_) | TxInput::P2TRScriptPath(_) => self.contains_taproot = true, - _ => {}, - } - - self.inputs.push(input); - self - } - pub fn add_output(mut self, output: TxOutput) -> Self { - self.outputs.push(output); - self - } - pub fn sign_inputs(self, signer: S) -> Result - where - S: TransactionSigner, - { - self.sign_inputs_fn(|input, sighash| match input { - TxInput::P2PKH(p) => signer - .claim_p2pkh(p, sighash, EcdsaSighashType::All) - .map(|claim| ClaimLocation::Script(claim.0)), - TxInput::P2WPKH(p) => signer - .claim_p2wpkh(p, sighash, EcdsaSighashType::All) - .map(|claim| ClaimLocation::Witness(claim.0)), - TxInput::P2TRKeyPath(p) => signer - .claim_p2tr_key_path(p, sighash, TapSighashType::Default) - .map(|claim| ClaimLocation::Witness(claim.0)), - TxInput::P2TRScriptPath(p) => signer - .claim_p2tr_script_path(p, sighash, TapSighashType::Default) - .map(|claim| ClaimLocation::Witness(claim.0)), - }) - } - pub fn sign_inputs_fn(self, signer: F) -> Result - where - F: Fn(&TxInput, secp256k1::Message) -> Result, - { - // Prepare boilerplate transaction for `bitcoin` crate. - let mut tx = Transaction { - version: self.version, - lock_time: self.lock_time, - input: vec![], - output: vec![], - }; - - // Prepare the inputs for `bitcoin` crate. - for input in self.inputs.iter().cloned() { - let btxin = TxIn::from(input); - tx.input.push(btxin); - } - - // Prepare the outputs for `bitcoin` crate. - for output in self.outputs.iter().cloned() { - let btc_txout = TxOut::from(output); - tx.output.push(btc_txout); - } - - // Satoshi output check - /* - // TODO: This should be enabled, eventually. - let miner_fee = self.miner_fee.ok_or(Error::Todo)?; - if total_satoshis_outputs + miner_fee > total_satoshi_inputs { - return Err(Error::Todo); - } - */ - - // If Taproot is enabled, we prepare the full `TxOuts` (value and - // scriptPubKey) for hashing, which will then be signed. What - // distinguishes this from legacy signing is that the output value in - // satoshis is actually part of the signature. - let mut prevouts = vec![]; - if self.contains_taproot { - for input in &self.inputs { - prevouts.push(TxOut { - value: input.ctx().value, - script_pubkey: input.ctx().script_pubkey.clone(), - }); - } - } - - let mut cache = SighashCache::new(tx); - - let mut claims = vec![]; - - // For each input (index), we create a hash which is to be signed. - for (index, input) in self.inputs.iter().enumerate() { - match input { - TxInput::P2PKH(p2pkh) => { - let hash = cache - .legacy_signature_hash( - index, - &p2pkh.ctx().script_pubkey, - EcdsaSighashType::All.to_u32(), - ) - .map_err(|_| Error::Todo)?; - - let message = secp256k1::Message::from_slice(hash.as_ref()) - .expect("Sighash must always convert to secp256k1::Message"); - let updated = signer(input, message)?; - - claims.push((index, updated)); - }, - TxInput::P2WPKH(p2wpkh) => { - let hash = cache - .segwit_signature_hash( - index, - p2wpkh - .ctx() - .script_pubkey - .p2wpkh_script_code() - .as_ref() - .expect("P2WPKH builder must set the script code correctly"), - p2wpkh.ctx().value, - EcdsaSighashType::All, - ) - .map_err(|_| Error::Todo)?; - - let message = secp256k1::Message::from_slice(hash.as_ref()) - .expect("Sighash must always convert to secp256k1::Message"); - let updated = signer(input, message)?; - - claims.push((index, updated)); - }, - TxInput::P2TRKeyPath(_) => { - let hash = cache - .taproot_key_spend_signature_hash( - index, - &bitcoin::sighash::Prevouts::All(&prevouts), - TapSighashType::Default, - ) - .map_err(|_| Error::Todo)?; - - let message = secp256k1::Message::from_slice(hash.as_ref()) - .expect("Sighash must always convert to secp256k1::Message"); - let updated = signer(input, message)?; - - claims.push((index, updated)); - }, - TxInput::P2TRScriptPath(p2trsp) => { - let leaf_hash = - TapLeafHash::from_script(p2trsp.witness(), LeafVersion::TapScript); - - let hash = cache - .taproot_script_spend_signature_hash( - index, - &bitcoin::sighash::Prevouts::All(&prevouts), - leaf_hash, - TapSighashType::Default, - ) - .map_err(|_| Error::Todo)?; - - let message = secp256k1::Message::from_slice(hash.as_ref()) - .expect("Sighash must always convert to secp256k1::Message"); - let updated = signer(input, message)?; - - claims.push((index, updated)); - }, - }; - } - - let mut tx = cache.into_transaction(); - - // Update the transaction with the updated scriptSig/Witness. - for (index, claim_loc) in claims { - match claim_loc { - ClaimLocation::Script(script) => { - tx.input[index].script_sig = script; - }, - ClaimLocation::Witness(witness) => { - tx.input[index].witness = witness; - }, - } - } - - Ok(TransactionSigned { inner: tx }) - } -} - -pub struct TransactionSigned { - pub inner: Transaction, -} - -impl TransactionSigned { - pub fn serialize(&self) -> Result> { - let mut buffer = vec![]; - self.inner - .consensus_encode(&mut buffer) - .map_err(|_| Error::Todo)?; - - Ok(buffer) - } -} diff --git a/rust/tw_bitcoin/src/utils.rs b/rust/tw_bitcoin/src/utils.rs deleted file mode 100644 index 399a84526e5..00000000000 --- a/rust/tw_bitcoin/src/utils.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::{Error, Result}; -use bitcoin::key::{KeyPair, PrivateKey, PublicKey, TapTweak, TweakedPublicKey}; -use bitcoin::secp256k1::{self, XOnlyPublicKey}; - -pub fn keypair_from_wif(string: &str) -> Result { - let pk = PrivateKey::from_wif(string).map_err(|_| Error::Todo)?; - let keypair = KeyPair::from_secret_key(&secp256k1::Secp256k1::new(), &pk.inner); - Ok(keypair) -} - -pub(crate) fn tweak_pubkey(pubkey: PublicKey) -> TweakedPublicKey { - let xonly = XOnlyPublicKey::from(pubkey.inner); - let (tweaked, _) = xonly.tap_tweak(&secp256k1::Secp256k1::new(), None); - tweaked -} diff --git a/rust/tw_bitcoin/tests/brc20.rs b/rust/tw_bitcoin/tests/brc20.rs new file mode 100644 index 00000000000..055452b5942 --- /dev/null +++ b/rust/tw_bitcoin/tests/brc20.rs @@ -0,0 +1,137 @@ +mod common; + +use common::hex; +use tw_bitcoin::aliases::*; +use tw_bitcoin::BitcoinEntry; +use tw_coin_entry::coin_entry::CoinEntry; +use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_proto::BitcoinV2::Proto; +use tw_proto::Utxo::Proto as UtxoProto; + +#[test] +fn coin_entry_sign_brc20_commit_reveal_transfer() { + let coin = EmptyCoinContext; + + let alice_private_key = hex("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129"); + let alice_pubkey = hex("030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb"); + + let txid: Vec = hex("8ec895b4d30adb01e38471ca1019bfc8c3e5fbd1f28d9e7b5653260d89989008") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 1, + value: 26_400, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: 7_000, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::brc20_inscribe( + Proto::mod_Output::OutputBrc20Inscription { + inscribe_to: alice_pubkey.as_slice().into(), + ticker: "oadf".into(), + transfer_amount: 20, + }, + ), + }), + }; + + // Change/return transaction. + let out2 = Proto::Output { + value: 16_400, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(alice_pubkey.as_slice().into()), + }), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1, out2], + input_selector: UtxoProto::InputSelector::UseAll, + disable_change_output: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + assert_eq!(signed.error, Proto::Error::OK); + assert_eq!( + signed.txid, + hex("797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1") + ); + + let encoded = tw_encoding::hex::encode(signed.encoded, false); + let transaction = signed.transaction.unwrap(); + + assert_eq!(transaction.inputs.len(), 1); + assert_eq!(transaction.outputs.len(), 2); + assert_eq!(&encoded, "02000000000101089098890d2653567b9e8df2d1fbe5c3c8bf1910ca7184e301db0ad3b495c88e0100000000ffffffff02581b000000000000225120e8b706a97732e705e22ae7710703e7f589ed13c636324461afa443016134cc051040000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d02483045022100a44aa28446a9a886b378a4a65e32ad9a3108870bd725dc6105160bed4f317097022069e9de36422e4ce2e42b39884aa5f626f8f94194d1013007d5a1ea9220a06dce0121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000"); + + // https://www.blockchain.com/explorer/transactions/btc/797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1 + let txid: Vec = hex("797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: 7_000, + sighash_type: UtxoProto::SighashType::UseDefault, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::brc20_inscribe(Proto::mod_Input::InputBrc20Inscription { + one_prevout: false, + inscribe_to: alice_pubkey.as_slice().into(), + ticker: "oadf".into(), + transfer_amount: 20, + }), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: 546, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(alice_pubkey.as_slice().into()), + }), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + disable_change_output: true, + // We enable deterministic Schnorr signatures here + dangerous_use_fixed_schnorr_rng: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + assert_eq!(signed.error, Proto::Error::OK); + + // https://www.blockchain.com/explorer/transactions/btc/7046dc2689a27e143ea2ad1039710885147e9485ab6453fa7e87464aa7dd3eca + assert_eq!( + signed.txid, + hex("7046dc2689a27e143ea2ad1039710885147e9485ab6453fa7e87464aa7dd3eca") + ); + + let encoded = tw_encoding::hex::encode(signed.encoded, false); + let transaction = signed.transaction.unwrap(); + + assert_eq!(encoded, "02000000000101b11f1782607a1fe5f033ccf9dc17404db020a0dedff94183596ee67ad4177d790000000000ffffffff012202000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d03406a35548b8fa4620028e021a944c1d3dc6e947243a7bfc901bf63fefae0d2460efa149a6440cab51966aa4f09faef2d1e5efcba23ab4ca6e669da598022dbcfe35b0063036f7264010118746578742f706c61696e3b636861727365743d7574662d3800377b2270223a226272632d3230222c226f70223a227472616e73666572222c227469636b223a226f616466222c22616d74223a223230227d6821c00f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000"); + assert_eq!(transaction.inputs.len(), 1); + assert_eq!(transaction.outputs.len(), 1); +} diff --git a/rust/tw_bitcoin/src/tests/data.rs b/rust/tw_bitcoin/tests/common/data.rs similarity index 99% rename from rust/tw_bitcoin/src/tests/data.rs rename to rust/tw_bitcoin/tests/common/data.rs index 4ea13a2b2dd..c17d4f0113b 100644 --- a/rust/tw_bitcoin/src/tests/data.rs +++ b/rust/tw_bitcoin/tests/common/data.rs @@ -256,9 +256,9 @@ d1d75f32de1ddd2b7e64faa36f9bd0caa3fa7df3b7fb9b3e92bb7f6d48a9\ pub const NFT_INSCRIPTION_RAW_HEX: &str = "\ 020000000001011771decbce2766b39d8fe66f4dc11737b3146c71f8cc6a\ e1397384c5e508e7f10000000000ffffffff012202000000000000160014\ -e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d0340cc1e7b0b5fa18b28\ -dce702e4e8ed2e91069d682b8daa3a773774bfc7d0e6f737d403016a9016\ -b58a92592ad0b41682e6209167444eb56605532b28e9be922d3afdda1d00\ +e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d0340e9bb1aaf8cb98c60\ +f793f08fc4258a9ad5a7b430fc5906fe3b522b67431fec087cbaf54767d4\ +3061be51d8f49fa179cb9b4f0f9e00ee23101962ac64f9d71a76fdda1d00\ 63036f7264010109696d6167652f706e67004d080289504e470d0a1a0a00\ 00000d4948445200000360000002be0803000000f30f8d7d000000d8504c\ 54450000003070bf3070af3173bd3078b73870b73070b73575ba3075ba30\ diff --git a/rust/tw_bitcoin/tests/common/mod.rs b/rust/tw_bitcoin/tests/common/mod.rs new file mode 100644 index 00000000000..9afe54f6d4f --- /dev/null +++ b/rust/tw_bitcoin/tests/common/mod.rs @@ -0,0 +1,12 @@ +// This seems to be required, even if the tests in `tests/` actually use +// functions/constants. +#![allow(dead_code)] + +pub mod data; + +pub const ONE_BTC: u64 = 100_000_000; +pub const MINER_FEE: u64 = 1_000_000; + +pub fn hex(string: &str) -> Vec { + tw_encoding::hex::decode(string).unwrap() +} diff --git a/rust/tw_bitcoin/tests/free_estimate.rs b/rust/tw_bitcoin/tests/free_estimate.rs new file mode 100644 index 00000000000..3e86653ce6d --- /dev/null +++ b/rust/tw_bitcoin/tests/free_estimate.rs @@ -0,0 +1,246 @@ +mod common; + +use common::{hex, ONE_BTC}; +use tw_bitcoin::aliases::*; +use tw_bitcoin::entry::BitcoinEntry; +use tw_coin_entry::coin_entry::CoinEntry; +use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_proto::BitcoinV2::Proto; +use tw_proto::Utxo::Proto as UtxoProto; + +const SAT_VB: u64 = 20; + +#[test] +fn p2pkh_fee_estimate() { + let coin = EmptyCoinContext; + + let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); + let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); + + let bob_pubkey = hex("025a0af1510f0f24d40dd00d7c0e51605ca504bbc177c3e19b065f373a1efdd22f"); + let txid: Vec = hex("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911") + .into_iter() + .rev() + .collect(); + + let mut signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![], + outputs: vec![], + input_selector: UtxoProto::InputSelector::UseAll, + fee_per_vb: SAT_VB, + disable_change_output: true, + ..Default::default() + }; + + signing.inputs.push(Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: 2 * ONE_BTC, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2pkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }); + + signing.outputs.push(Proto::Output { + value: ONE_BTC, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2pkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(bob_pubkey.as_slice().into()), + }), + }), + }); + + let prehashes = BitcoinEntry.preimage_hashes(&coin, signing.clone()); + assert_eq!(prehashes.error, Proto::Error::OK); + assert_eq!(prehashes.weight_estimate, 768); + assert_eq!(prehashes.fee_estimate, (768 + 3) / 4 * SAT_VB); + + let signed = BitcoinEntry.sign(&coin, signing); + assert_eq!(signed.error, Proto::Error::OK); + assert_eq!(signed.weight, 768); + assert_eq!(signed.fee, (768 + 3) / 4 * SAT_VB); +} + +#[test] +fn p2wpkh_fee_estimate() { + let coin = EmptyCoinContext; + + let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); + let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); + + let bob_pubkey = hex("025a0af1510f0f24d40dd00d7c0e51605ca504bbc177c3e19b065f373a1efdd22f"); + let txid: Vec = hex("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911") + .into_iter() + .rev() + .collect(); + + let mut signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![], + outputs: vec![], + input_selector: UtxoProto::InputSelector::UseAll, + fee_per_vb: SAT_VB, + disable_change_output: true, + ..Default::default() + }; + + signing.inputs.push(Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: 2 * ONE_BTC, + sequence: u32::MAX, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }); + + signing.outputs.push(Proto::Output { + value: ONE_BTC, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(bob_pubkey.as_slice().into()), + }), + }), + }); + + let prehashes = BitcoinEntry.preimage_hashes(&coin, signing.clone()); + assert_eq!(prehashes.error, Proto::Error::OK); + // TODO: The estimated weight/fee is slightly off from the finalized + // weight/fee. This is probably good enough, but we can probably improve + // this. + assert_eq!(prehashes.weight_estimate, 436); + assert_eq!(prehashes.fee_estimate, (436 + 3) / 4 * SAT_VB); + + let signed = BitcoinEntry.sign(&coin, signing); + assert_eq!(signed.error, Proto::Error::OK); + assert_eq!(signed.weight, 438); + assert_eq!(signed.fee, (438 + 3) / 4 * SAT_VB); +} + +#[test] +fn p2tr_key_path_fee_estimate() { + let coin = EmptyCoinContext; + + let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); + let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); + + let bob_pubkey = hex("025a0af1510f0f24d40dd00d7c0e51605ca504bbc177c3e19b065f373a1efdd22f"); + let txid: Vec = hex("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: 2 * ONE_BTC, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2tr_key_path(Proto::mod_Input::InputTaprootKeyPath { + one_prevout: false, + public_key: alice_pubkey.as_slice().into(), + }), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: ONE_BTC, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2tr_key_path(bob_pubkey.as_slice().into()), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + fee_per_vb: SAT_VB, + disable_change_output: true, + ..Default::default() + }; + + let prehashes = BitcoinEntry.preimage_hashes(&coin, signing.clone()); + assert_eq!(prehashes.error, Proto::Error::OK); + // TODO: The estimated weight/fee is slightly off from the finalized + // weight/fee. This is probably good enough, but we can probably improve + // this. + assert_eq!(prehashes.weight_estimate, 450); + assert_eq!(prehashes.fee_estimate, (450 + 3) / 4 * SAT_VB); + + let signed = BitcoinEntry.sign(&coin, signing); + assert_eq!(signed.error, Proto::Error::OK); + assert_eq!(signed.weight, 445); + assert_eq!(signed.fee, (445 + 3) / 4 * SAT_VB); +} + +#[test] +fn brc20_inscribe_fee_estimate() { + let coin = EmptyCoinContext; + + let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); + let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); + + let txid: Vec = hex("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: 2 * ONE_BTC, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::brc20_inscribe(Proto::mod_Input::InputBrc20Inscription { + one_prevout: false, + inscribe_to: alice_pubkey.as_slice().into(), + ticker: "oadf".into(), + transfer_amount: 20, + }), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: ONE_BTC, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::brc20_inscribe( + Proto::mod_Output::OutputBrc20Inscription { + inscribe_to: alice_pubkey.as_slice().into(), + ticker: "oadf".into(), + transfer_amount: 20, + }, + ), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + fee_per_vb: SAT_VB, + disable_change_output: true, + ..Default::default() + }; + + let prehashes = BitcoinEntry.preimage_hashes(&coin, signing.clone()); + assert_eq!(prehashes.error, Proto::Error::OK); + // TODO: The estimated weight/fee is slightly off from the finalized + // weight/fee. This is probably good enough, but we can probably improve + // this. + assert_eq!(prehashes.weight_estimate, 575); + assert_eq!(prehashes.fee_estimate, (575 + 3) / 4 * SAT_VB); + + let signed = BitcoinEntry.sign(&coin, signing); + assert_eq!(signed.error, Proto::Error::OK); + assert_eq!(signed.weight, 571); + assert_eq!(signed.fee, (571 + 3) / 4 * SAT_VB); +} diff --git a/rust/tw_bitcoin/tests/legacy_build_sign.rs b/rust/tw_bitcoin/tests/legacy_build_sign.rs new file mode 100644 index 00000000000..4c871fb0dc9 --- /dev/null +++ b/rust/tw_bitcoin/tests/legacy_build_sign.rs @@ -0,0 +1,445 @@ +#![allow(deprecated)] + +mod common; + +use common::{hex, MINER_FEE, ONE_BTC}; +use secp256k1::ffi::CPtr; +use std::ffi::CString; +use tw_proto::Bitcoin::Proto as LegacyProto; +use tw_proto::Common::Proto as CommonProto; +use wallet_core_rs::ffi::bitcoin::legacy as legacy_ffi; + +const ONE_BTC_I64: i64 = ONE_BTC as i64; +const MINER_FEE_I64: i64 = MINER_FEE as i64; + +#[test] +fn ffi_proto_sign_input_p2pkh_output_p2pkh() { + let alice_private_key = hex("56429688a1a6b00b90ccd22a0de0a376b6569d8684022ae92229a28478bfb657"); + let bob_pubkey = hex("037ed9a436e11ec4947ac4b7823787e24ba73180f1edd2857bff19c9f4d62b65bf"); + + let txid = hex("1e1cdc48aa990d7e154a161d5b5f1cad737742e97d2712ab188027bb42e6e47b") + .into_iter() + .rev() + .collect(); + + // Output. + let output = unsafe { + legacy_ffi::tw_bitcoin_legacy_build_p2pkh_script( + ONE_BTC_I64 * 50 - MINER_FEE_I64, + bob_pubkey.as_c_ptr(), + bob_pubkey.len(), + ) + .into_vec() + }; + let output: LegacyProto::TransactionOutput = tw_proto::deserialize(&output).unwrap(); + + // Prepare SigningInput. + let signing = LegacyProto::SigningInput { + private_key: vec![alice_private_key.into()], + utxo: vec![LegacyProto::UnspentTransaction { + out_point: Some(LegacyProto::OutPoint { + hash: txid, + index: 0, + sequence: u32::MAX, + ..Default::default() + }), + // For inputs, script is not needed (derived from variant). + script: Default::default(), + amount: ONE_BTC_I64 * 50, + variant: LegacyProto::TransactionVariant::P2PKH, + spendingScript: Default::default(), + }], + plan: Some(LegacyProto::TransactionPlan { + utxos: vec![LegacyProto::UnspentTransaction { + out_point: Default::default(), + script: output.script, + amount: output.value, + variant: LegacyProto::TransactionVariant::P2PKH, + spendingScript: Default::default(), + }], + ..Default::default() + }), + ..Default::default() + }; + let serialized = tw_proto::serialize(&signing).unwrap(); + + // Sign and build the transaction. + let signed = unsafe { + legacy_ffi::tw_bitcoin_legacy_taproot_build_and_sign_transaction( + serialized.as_c_ptr(), + serialized.len(), + ) + .into_vec() + }; + let signed: LegacyProto::SigningOutput = tw_proto::deserialize(&signed).unwrap(); + + // Check result. + assert_eq!(signed.error, CommonProto::SigningError::OK); + let encoded_hex = tw_encoding::hex::encode(signed.encoded, false); + assert_eq!(encoded_hex, "02000000017be4e642bb278018ab12277de9427773ad1c5f5b1d164a157e0d99aa48dc1c1e000000006a473044022078eda020d4b86fcb3af78ef919912e6d79b81164dbbb0b0b96da6ac58a2de4b102201a5fd8d48734d5a02371c4b5ee551a69dca3842edbf577d863cf8ae9fdbbd4590121036666dd712e05a487916384bfcd5973eb53e8038eccbbf97f7eed775b87389536ffffffff01c0aff629010000001976a9145eaaa4f458f9158f86afcba08dd7448d27045e3d88ac00000000"); +} + +#[test] +fn ffi_proto_sign_input_p2pkh_output_p2wpkh() { + let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); + let bob_pubkey = hex("025a0af1510f0f24d40dd00d7c0e51605ca504bbc177c3e19b065f373a1efdd22f"); + + let txid = hex("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911") + .into_iter() + .rev() + .collect(); + + // Output. + let output = unsafe { + legacy_ffi::tw_bitcoin_legacy_build_p2wpkh_script( + ONE_BTC_I64 * 50 - MINER_FEE_I64, + bob_pubkey.as_c_ptr(), + bob_pubkey.len(), + ) + .into_vec() + }; + let output: LegacyProto::TransactionOutput = tw_proto::deserialize(&output).unwrap(); + + // Prepare SigningInput. + let signing = LegacyProto::SigningInput { + private_key: vec![alice_private_key.into()], + utxo: vec![LegacyProto::UnspentTransaction { + out_point: Some(LegacyProto::OutPoint { + hash: txid, + index: 0, + sequence: u32::MAX, + ..Default::default() + }), + // For inputs, script is not needed (derived from variant). + script: Default::default(), + amount: ONE_BTC_I64 * 50, + variant: LegacyProto::TransactionVariant::P2PKH, + spendingScript: Default::default(), + }], + plan: Some(LegacyProto::TransactionPlan { + utxos: vec![LegacyProto::UnspentTransaction { + out_point: Default::default(), + script: output.script, + amount: output.value, + variant: LegacyProto::TransactionVariant::P2WPKH, + spendingScript: Default::default(), + }], + ..Default::default() + }), + ..Default::default() + }; + let serialized = tw_proto::serialize(&signing).unwrap(); + + // Sign and build the transaction. + let signed = unsafe { + legacy_ffi::tw_bitcoin_legacy_taproot_build_and_sign_transaction( + serialized.as_c_ptr(), + serialized.len(), + ) + .into_vec() + }; + let signed: LegacyProto::SigningOutput = tw_proto::deserialize(&signed).unwrap(); + + // Check result. + assert_eq!(signed.error, CommonProto::SigningError::OK); + let encoded_hex = tw_encoding::hex::encode(signed.encoded, false); + assert_eq!(encoded_hex, "020000000111b9f62923af73e297abb69f749e7a1aa2735fbdfd32ac5f6aa89e5c96841c18000000006b483045022100df9ed0b662b759e68b89a42e7144cddf787782a7129d4df05642dd825930e6e6022051a08f577f11cc7390684bbad2951a6374072253ffcf2468d14035ed0d8cd6490121028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28fffffffff01c0aff629010000001600140d0e1cec6c2babe8badde5e9b3dea667da90036d00000000"); +} + +#[test] +fn ffi_proto_sign_input_p2pkh_output_p2tr_key_path() { + let alice_private_key = hex("12ce558df23528f1aa86f1f51ac7e13a197a06bda27610fa89e13b04c40ee999"); + let alice_pubkey = hex("0351e003fdc48e7f31c9bc94996c91f6c3273b7ef4208a1686021bedf7673bb058"); + let bob_private_key = hex("26c2566adcc030a1799213bfd546e615f6ab06f72085ec6806ff1761da48d227"); + let bob_pubkey = hex("02c0938cf377023dfde55e9c96b3cff4ca8894fb6b5d2009006bd43c0bff69cac9"); + + let txid = hex("c50563913e5a838f937c94232f5a8fc74e58b629fae41dfdffcc9a70f833b53a") + .into_iter() + .rev() + .collect(); + + // Output. + let output = unsafe { + legacy_ffi::tw_bitcoin_legacy_build_p2tr_key_path_script( + ONE_BTC_I64 * 50 - MINER_FEE_I64, + bob_pubkey.as_c_ptr(), + bob_pubkey.len(), + ) + .into_vec() + }; + let output: LegacyProto::TransactionOutput = tw_proto::deserialize(&output).unwrap(); + + // Prepare SigningInput. + let signing = LegacyProto::SigningInput { + private_key: vec![alice_private_key.into()], + utxo: vec![LegacyProto::UnspentTransaction { + out_point: Some(LegacyProto::OutPoint { + hash: txid, + index: 0, + sequence: u32::MAX, + ..Default::default() + }), + // For inputs, script is not needed (derived from variant). + script: Default::default(), + amount: ONE_BTC_I64 * 50, + variant: LegacyProto::TransactionVariant::P2PKH, + spendingScript: Default::default(), + }], + plan: Some(LegacyProto::TransactionPlan { + utxos: vec![LegacyProto::UnspentTransaction { + out_point: Default::default(), + script: output.script, + amount: output.value, + variant: LegacyProto::TransactionVariant::P2TRKEYPATH, + spendingScript: Default::default(), + }], + ..Default::default() + }), + ..Default::default() + }; + let serialized = tw_proto::serialize(&signing).unwrap(); + + // Sign and build the transaction. + let signed = unsafe { + legacy_ffi::tw_bitcoin_legacy_taproot_build_and_sign_transaction( + serialized.as_c_ptr(), + serialized.len(), + ) + .into_vec() + }; + let signed: LegacyProto::SigningOutput = tw_proto::deserialize(&signed).unwrap(); + + // Check result. + assert_eq!(signed.error, CommonProto::SigningError::OK); + let encoded_hex = tw_encoding::hex::encode(signed.encoded, false); + assert_eq!(encoded_hex, "02000000013ab533f8709accfffd1de4fa29b6584ec78f5a2f23947c938f835a3e916305c5000000006b48304502210086ab2c2192e2738529d6cd9604d8ee75c5b09b0c2f4066a5c5fa3f87a26c0af602202afc7096aaa992235c43e712146057b5ed6a776d82b9129620bc5a21991c0a5301210351e003fdc48e7f31c9bc94996c91f6c3273b7ef4208a1686021bedf7673bb058ffffffff01c0aff62901000000225120e01cfdd05da8fa1d71f987373f3790d45dea9861acb0525c86656fe50f4397a600000000"); + + // Next transaction; try to spend the P2TR key-path. + + let txid: Vec = hex("9a582032f6a50cedaff77d3d5604b33adf8bc31bdaef8de977c2187e395860ac") + .into_iter() + .rev() + .collect(); + + // Output. + let output = unsafe { + legacy_ffi::tw_bitcoin_legacy_build_p2tr_key_path_script( + ONE_BTC_I64 * 50 - MINER_FEE_I64 - MINER_FEE_I64, + alice_pubkey.as_c_ptr(), + alice_pubkey.len(), + ) + .into_vec() + }; + let output: LegacyProto::TransactionOutput = tw_proto::deserialize(&output).unwrap(); + + // Prepare SigningInput. + let signing = LegacyProto::SigningInput { + private_key: vec![bob_private_key.into()], + utxo: vec![LegacyProto::UnspentTransaction { + out_point: Some(LegacyProto::OutPoint { + hash: txid.into(), + index: 0, + sequence: u32::MAX, + ..Default::default() + }), + // For inputs, script is not needed (derived from variant). + script: Default::default(), + amount: ONE_BTC_I64 * 50 - MINER_FEE_I64, + variant: LegacyProto::TransactionVariant::P2TRKEYPATH, + spendingScript: Default::default(), + }], + plan: Some(LegacyProto::TransactionPlan { + utxos: vec![LegacyProto::UnspentTransaction { + out_point: Default::default(), + script: output.script, + amount: output.value, + variant: LegacyProto::TransactionVariant::P2TRKEYPATH, + spendingScript: Default::default(), + }], + ..Default::default() + }), + ..Default::default() + }; + let serialized = tw_proto::serialize(&signing).unwrap(); + + // Sign and build the transaction. + let signed = unsafe { + legacy_ffi::tw_bitcoin_legacy_taproot_build_and_sign_transaction( + serialized.as_c_ptr(), + serialized.len(), + ) + .into_vec() + }; + let signed: LegacyProto::SigningOutput = tw_proto::deserialize(&signed).unwrap(); + + // Check result. + const REVEAL_RAW: &str = "02000000000101ac6058397e18c277e98defda1bc38bdf3ab304563d7df7afed0ca5f63220589a0000000000ffffffff01806de72901000000225120a5c027857e359d19f625e52a106b8ac6ca2d6a8728f6cf2107cd7958ee0787c20140ec2d3910d41506b60aaa20520bb72f15e2d2cbd97e3a8e26ee7bad5f4c56b0f2fb0ceaddac33cb2813a33ba017ba6b1d011bab74a0426f12a2bcf47b4ed5bc8600000000"; + + assert_eq!(signed.error, CommonProto::SigningError::OK); + let encoded_hex = tw_encoding::hex::encode(signed.encoded, false); + assert_eq!(encoded_hex[..188], REVEAL_RAW[..188]); + // Schnorr signature does not match (non-deterministic). + assert_ne!(encoded_hex[188..316], REVEAL_RAW[188..316]); + assert_eq!(encoded_hex[316..], REVEAL_RAW[316..]); +} + +#[test] +fn ffi_proto_sign_input_p2wpkh_output_brc20() { + let alice_private_key = hex("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129"); + let alice_pubkey = hex("030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb"); + + let txid = hex("8ec895b4d30adb01e38471ca1019bfc8c3e5fbd1f28d9e7b5653260d89989008") + .into_iter() + .rev() + .collect(); + + // Output. + let c_ticker = CString::new("oadf").unwrap(); + let brc20_output = unsafe { + legacy_ffi::tw_bitcoin_legacy_build_brc20_transfer_inscription( + c_ticker.as_ptr(), + 20, + 7_000, + alice_pubkey.as_c_ptr(), + alice_pubkey.len(), + ) + .into_vec() + }; + let brc20_output: LegacyProto::TransactionOutput = + tw_proto::deserialize(&brc20_output).unwrap(); + + // Change output. + let change_output = unsafe { + legacy_ffi::tw_bitcoin_legacy_build_p2wpkh_script( + 16_400, + alice_pubkey.as_c_ptr(), + alice_pubkey.len(), + ) + .into_vec() + }; + let change_output: LegacyProto::TransactionOutput = + tw_proto::deserialize(&change_output).unwrap(); + + // Prepare SigningInput. + let signing = LegacyProto::SigningInput { + private_key: vec![alice_private_key.clone().into()], + utxo: vec![LegacyProto::UnspentTransaction { + out_point: Some(LegacyProto::OutPoint { + hash: txid, + index: 1, + sequence: u32::MAX, + ..Default::default() + }), + // For inputs, script is not needed (derived from variant). + script: Default::default(), + amount: 26_400, + variant: LegacyProto::TransactionVariant::P2WPKH, + spendingScript: Default::default(), + }], + plan: Some(LegacyProto::TransactionPlan { + utxos: vec![ + LegacyProto::UnspentTransaction { + out_point: Default::default(), + script: brc20_output.script.clone(), + amount: brc20_output.value, + variant: LegacyProto::TransactionVariant::BRC20TRANSFER, + spendingScript: Default::default(), + }, + // Change output. + LegacyProto::UnspentTransaction { + out_point: Default::default(), + script: change_output.script, + amount: change_output.value, + variant: LegacyProto::TransactionVariant::P2WPKH, + spendingScript: Default::default(), + }, + ], + ..Default::default() + }), + ..Default::default() + }; + let serialized = tw_proto::serialize(&signing).unwrap(); + + // Sign and build the transaction. + let signed = unsafe { + legacy_ffi::tw_bitcoin_legacy_taproot_build_and_sign_transaction( + serialized.as_c_ptr(), + serialized.len(), + ) + .into_vec() + }; + let signed: LegacyProto::SigningOutput = tw_proto::deserialize(&signed).unwrap(); + + // Check result. + assert_eq!(signed.error, CommonProto::SigningError::OK); + let encoded_hex = tw_encoding::hex::encode(signed.encoded, false); + assert_eq!(encoded_hex, "02000000000101089098890d2653567b9e8df2d1fbe5c3c8bf1910ca7184e301db0ad3b495c88e0100000000ffffffff02581b000000000000225120e8b706a97732e705e22ae7710703e7f589ed13c636324461afa443016134cc051040000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d02483045022100a44aa28446a9a886b378a4a65e32ad9a3108870bd725dc6105160bed4f317097022069e9de36422e4ce2e42b39884aa5f626f8f94194d1013007d5a1ea9220a06dce0121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000"); + + // Next transaction; try to spend the BRC20 transfer inscription. + + let txid: Vec = hex("797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1") + .into_iter() + .rev() + .collect(); + + // Tagged output. + let output = unsafe { + legacy_ffi::tw_bitcoin_legacy_build_p2wpkh_script( + 546, + alice_pubkey.as_c_ptr(), + alice_pubkey.len(), + ) + .into_vec() + }; + let output: LegacyProto::TransactionOutput = tw_proto::deserialize(&output).unwrap(); + + // Prepare SigningInput. + let signing = LegacyProto::SigningInput { + private_key: vec![alice_private_key.into()], + utxo: vec![LegacyProto::UnspentTransaction { + out_point: Some(LegacyProto::OutPoint { + hash: txid.into(), + index: 0, + sequence: u32::MAX, + ..Default::default() + }), + script: Default::default(), + amount: brc20_output.value, + variant: LegacyProto::TransactionVariant::BRC20TRANSFER, + // IMPORTANT: spending script is specified. + spendingScript: brc20_output.spendingScript, + }], + plan: Some(LegacyProto::TransactionPlan { + utxos: vec![LegacyProto::UnspentTransaction { + out_point: Default::default(), + script: output.script, + amount: 546, + variant: LegacyProto::TransactionVariant::P2WPKH, + spendingScript: Default::default(), + }], + ..Default::default() + }), + ..Default::default() + }; + let serialized = tw_proto::serialize(&signing).unwrap(); + + // Sign and build the transaction. + let signed = unsafe { + legacy_ffi::tw_bitcoin_legacy_taproot_build_and_sign_transaction( + serialized.as_c_ptr(), + serialized.len(), + ) + .into_vec() + }; + let signed: LegacyProto::SigningOutput = tw_proto::deserialize(&signed).unwrap(); + + // Check result. + const REVEAL_RAW: &str = "02000000000101b11f1782607a1fe5f033ccf9dc17404db020a0dedff94183596ee67ad4177d790000000000ffffffff012202000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d03406a35548b8fa4620028e021a944c1d3dc6e947243a7bfc901bf63fefae0d2460efa149a6440cab51966aa4f09faef2d1e5efcba23ab4ca6e669da598022dbcfe35b0063036f7264010118746578742f706c61696e3b636861727365743d7574662d3800377b2270223a226272632d3230222c226f70223a227472616e73666572222c227469636b223a226f616466222c22616d74223a223230227d6821c00f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000"; + + assert_eq!(signed.error, CommonProto::SigningError::OK); + let encoded_hex = tw_encoding::hex::encode(signed.encoded, false); + assert_eq!(encoded_hex[..164], REVEAL_RAW[..164]); + // Schnorr signature does not match (non-deterministic). + assert_ne!(encoded_hex[164..292], REVEAL_RAW[164..292]); + assert_eq!(encoded_hex[292..], REVEAL_RAW[292..]); +} diff --git a/rust/tw_bitcoin/tests/legacy_scripts.rs b/rust/tw_bitcoin/tests/legacy_scripts.rs new file mode 100644 index 00000000000..77d5fadf82d --- /dev/null +++ b/rust/tw_bitcoin/tests/legacy_scripts.rs @@ -0,0 +1,177 @@ +#![allow(deprecated)] + +mod common; + +use bitcoin::{PublicKey, ScriptBuf}; +use secp256k1::XOnlyPublicKey; +use std::ffi::CString; +use tw_bitcoin::modules::transactions::{ + BRC20TransferInscription, Brc20Ticker, OrdinalNftInscription, +}; +use tw_encoding::hex; +use tw_proto::Bitcoin::Proto as LegacyProto; +use wallet_core_rs::ffi::bitcoin::legacy as legacy_ffi; + +// When building the spending conditions of inputs (scriptPubkey), then the +// actual value is not important. We can just use 0 here. +const SATOSHIS: i64 = 0; +const PUBKEY: &str = "028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"; + +#[test] +fn ffi_tw_bitcoin_legacy_build_p2pkh_script() { + let pubkey_slice = hex::decode(PUBKEY).unwrap(); + let pubkey = PublicKey::from_slice(&pubkey_slice).unwrap(); + + let raw = unsafe { + legacy_ffi::tw_bitcoin_legacy_build_p2pkh_script( + SATOSHIS, + pubkey_slice.as_ptr(), + pubkey_slice.len(), + ) + .into_vec() + }; + + // The expected script. + let expected = ScriptBuf::new_p2pkh(&pubkey.pubkey_hash()); + + let proto: LegacyProto::TransactionOutput = tw_proto::deserialize(&raw).unwrap(); + assert_eq!(proto.value, SATOSHIS); + assert_eq!(proto.script, expected.as_bytes()); + assert!(proto.spendingScript.is_empty()); +} + +#[test] +fn ffi_tw_bitcoin_legacy_build_p2wpkh_script() { + let pubkey_slice = hex::decode(PUBKEY).unwrap(); + let pubkey = PublicKey::from_slice(&pubkey_slice).unwrap(); + + let raw = unsafe { + legacy_ffi::tw_bitcoin_legacy_build_p2wpkh_script( + SATOSHIS, + pubkey_slice.as_ptr(), + pubkey_slice.len(), + ) + .into_vec() + }; + + // The expected script. + let expected = ScriptBuf::new_v0_p2wpkh(&pubkey.wpubkey_hash().unwrap()); + + let proto: LegacyProto::TransactionOutput = tw_proto::deserialize(&raw).unwrap(); + assert_eq!(proto.value, SATOSHIS); + assert_eq!(proto.script, expected.as_bytes()); + assert!(proto.spendingScript.is_empty()); +} + +#[test] +fn ffi_tw_bitcoin_legacy_build_p2tr_key_path_script() { + let pubkey_slice = hex::decode(PUBKEY).unwrap(); + let pubkey = PublicKey::from_slice(&pubkey_slice).unwrap(); + + let raw = unsafe { + legacy_ffi::tw_bitcoin_legacy_build_p2tr_key_path_script( + SATOSHIS, + pubkey_slice.as_ptr(), + pubkey_slice.len(), + ) + .into_vec() + }; + + // The expected script. + let xonly = XOnlyPublicKey::from(pubkey.inner); + let expected = ScriptBuf::new_v1_p2tr(&secp256k1::Secp256k1::new(), xonly, None); + + let proto: LegacyProto::TransactionOutput = tw_proto::deserialize(&raw).unwrap(); + assert_eq!(proto.value, SATOSHIS); + assert_eq!(proto.script, expected.as_bytes()); + assert!(proto.spendingScript.is_empty()); +} + +#[test] +fn ffi_tw_bitcoin_legacy_build_brc20_transfer_inscription() { + let pubkey_slice = hex::decode(PUBKEY).unwrap(); + let pubkey = PublicKey::from_slice(&pubkey_slice).unwrap(); + + let ticker_str = "oadf"; + let c_ticker = CString::new(ticker_str).unwrap(); + let brc20_amount = 100; + + // Call the FFI function. + let raw = unsafe { + legacy_ffi::tw_bitcoin_legacy_build_brc20_transfer_inscription( + c_ticker.as_ptr(), + brc20_amount, + SATOSHIS, + pubkey_slice.as_ptr(), + pubkey_slice.len(), + ) + .into_vec() + }; + + // Prepare the BRC20 payload + merkle root. + let ticker = Brc20Ticker::new(ticker_str.to_string()).unwrap(); + let transfer = BRC20TransferInscription::new(pubkey, ticker, brc20_amount).unwrap(); + + let merkle_root = transfer + .inscription() + .spend_info() + .merkle_root() + .expect("incorrectly constructed Taproot merkle root"); + + // The expected script. + let xonly = XOnlyPublicKey::from(pubkey.inner); + let expected = ScriptBuf::new_v1_p2tr(&secp256k1::Secp256k1::new(), xonly, Some(merkle_root)); + + let proto: LegacyProto::TransactionOutput = tw_proto::deserialize(&raw).unwrap(); + assert_eq!(proto.value, SATOSHIS); + assert_eq!(proto.script, expected.as_bytes()); + assert_eq!( + proto.spendingScript, + transfer.inscription().taproot_program().as_bytes() + ); +} + +#[test] +fn ffi_tw_bitcoin_legacy_build_nft_inscription() { + let pubkey_slice = hex::decode(PUBKEY).unwrap(); + let pubkey = PublicKey::from_slice(&pubkey_slice).unwrap(); + + let mime_type = "image/png"; + let c_mime_type = CString::new(mime_type).unwrap(); + let payload_hex = common::data::NFT_INSCRIPTION_IMAGE_DATA; + let payload = tw_encoding::hex::decode(payload_hex).unwrap(); + + // Call the FFI function. + let raw = unsafe { + legacy_ffi::tw_bitcoin_legacy_build_nft_inscription( + c_mime_type.as_ptr(), + payload.as_ptr(), + payload.len(), + SATOSHIS, + pubkey_slice.as_ptr(), + pubkey_slice.len(), + ) + .into_vec() + }; + + // Prepare the NFT inscription + merkle root. + let nft = OrdinalNftInscription::new(mime_type.as_bytes(), &payload, pubkey).unwrap(); + + let merkle_root = nft + .inscription() + .spend_info() + .merkle_root() + .expect("incorrectly constructed Taproot merkle root"); + + // The expected script. + let xonly = XOnlyPublicKey::from(pubkey.inner); + let expected = ScriptBuf::new_v1_p2tr(&secp256k1::Secp256k1::new(), xonly, Some(merkle_root)); + + let proto: LegacyProto::TransactionOutput = tw_proto::deserialize(&raw).unwrap(); + assert_eq!(proto.value, SATOSHIS); + assert_eq!(proto.script, expected.as_bytes()); + assert_eq!( + proto.spendingScript, + nft.inscription().taproot_program().as_bytes() + ); +} diff --git a/rust/tw_bitcoin/tests/ordinal_nft.rs b/rust/tw_bitcoin/tests/ordinal_nft.rs new file mode 100644 index 00000000000..23ff1694a04 --- /dev/null +++ b/rust/tw_bitcoin/tests/ordinal_nft.rs @@ -0,0 +1,130 @@ +mod common; + +use common::hex; +use tw_bitcoin::aliases::*; +use tw_bitcoin::entry::BitcoinEntry; +use tw_coin_entry::coin_entry::CoinEntry; +use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_proto::BitcoinV2::Proto; +use tw_proto::Utxo::Proto as UtxoProto; + +#[test] +fn coin_entry_sign_ordinal_nft_commit_reveal_transfer() { + let coin = EmptyCoinContext; + + let alice_private_key = hex("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129"); + let alice_pubkey = hex("030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb"); + + let txid: Vec = hex("579590c3227253ad423b1e7e3c5b073b8a280d307c68aecd779df2600daa2f99") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: 32_400, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: 31_100, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::ordinal_inscribe( + Proto::mod_Output::OutputOrdinalInscription { + inscribe_to: alice_pubkey.as_slice().into(), + mime_type: "image/png".into(), + payload: hex(common::data::NFT_INSCRIPTION_IMAGE_DATA).into(), + }, + ), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + disable_change_output: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + assert_eq!(signed.error, Proto::Error::OK); + + // https://www.blockchain.com/explorer/transactions/btc/f1e708e5c5847339e16accf8716c14b33717c14d6fe68f9db36627cecbde7117 + assert_eq!( + signed.txid, + hex("f1e708e5c5847339e16accf8716c14b33717c14d6fe68f9db36627cecbde7117") + ); + + let encoded = tw_encoding::hex::encode(signed.encoded, false); + let transaction = signed.transaction.unwrap(); + + assert_eq!(transaction.inputs.len(), 1); + assert_eq!(transaction.outputs.len(), 1); + assert_eq!(&encoded, "02000000000101992faa0d60f29d77cdae687c300d288a3b075b3c7e1e3b42ad537222c39095570000000000ffffffff017c790000000000002251202ac69a7e9dba801e9fcba826055917b84ca6fba4d51a29e47d478de603eedab602473044022054212984443ed4c66fc103d825bfd2da7baf2ab65d286e3c629b36b98cd7debd022050214cfe5d3b12a17aaaf1a196bfeb2f0ad15ffb320c4717eb7614162453e4fe0121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000"); + + let txid: Vec = hex("f1e708e5c5847339e16accf8716c14b33717c14d6fe68f9db36627cecbde7117") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: 31_100, + sighash_type: UtxoProto::SighashType::UseDefault, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::ordinal_inscribe( + Proto::mod_Input::InputOrdinalInscription { + one_prevout: false, + inscribe_to: alice_pubkey.as_slice().into(), + mime_type: "image/png".into(), + payload: hex(common::data::NFT_INSCRIPTION_IMAGE_DATA).into(), + }, + ), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: 546, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(alice_pubkey.as_slice().into()), + }), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + disable_change_output: true, + // We enable deterministic Schnorr signatures here + dangerous_use_fixed_schnorr_rng: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + assert_eq!(signed.error, Proto::Error::OK); + + // https://www.blockchain.com/explorer/transactions/btc/173f8350b722243d44cc8db5584de76b432eb6d0888d9e66e662db51584f44ac + assert_eq!( + signed.txid, + hex("173f8350b722243d44cc8db5584de76b432eb6d0888d9e66e662db51584f44ac") + ); + + let encoded = tw_encoding::hex::encode(signed.encoded, false); + let transaction = signed.transaction.unwrap(); + + assert_eq!(encoded, common::data::NFT_INSCRIPTION_RAW_HEX); + assert_eq!(transaction.inputs.len(), 1); + assert_eq!(transaction.outputs.len(), 1); +} diff --git a/rust/tw_bitcoin/tests/p2pkh.rs b/rust/tw_bitcoin/tests/p2pkh.rs new file mode 100644 index 00000000000..05f99c8d438 --- /dev/null +++ b/rust/tw_bitcoin/tests/p2pkh.rs @@ -0,0 +1,75 @@ +mod common; + +use common::{hex, MINER_FEE, ONE_BTC}; +use tw_bitcoin::aliases::*; +use tw_bitcoin::entry::BitcoinEntry; +use tw_coin_entry::coin_entry::CoinEntry; +use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_proto::BitcoinV2::Proto; +use tw_proto::Utxo::Proto as UtxoProto; + +#[test] +fn coin_entry_emtpy() { + let _coin = EmptyCoinContext; + let alice_private_key = hex("56429688a1a6b00b90ccd22a0de0a376b6569d8684022ae92229a28478bfb657"); + + let signing = Proto::SigningInput { + private_key: alice_private_key.into(), + disable_change_output: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&_coin, signing); + assert_eq!(signed.error, Proto::Error::OK); +} + +#[test] +fn coin_entry_sign_input_p2pkh_output_p2pkh() { + let coin = EmptyCoinContext; + + let alice_private_key = hex("56429688a1a6b00b90ccd22a0de0a376b6569d8684022ae92229a28478bfb657"); + let alice_pubkey = hex("036666dd712e05a487916384bfcd5973eb53e8038eccbbf97f7eed775b87389536"); + let _bob_private_key = hex("b7da1ec42b19085fe09fec54b9d9eacd998ae4e6d2ad472be38d8393391b9ead"); + let bob_pubkey = hex("037ed9a436e11ec4947ac4b7823787e24ba73180f1edd2857bff19c9f4d62b65bf"); + + // Create transaction with P2PKH as input and output. + let txid: Vec = hex("1e1cdc48aa990d7e154a161d5b5f1cad737742e97d2712ab188027bb42e6e47b") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC * 50, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2pkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: ONE_BTC * 50 - MINER_FEE, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2pkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(bob_pubkey.as_slice().into()), + }), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + disable_change_output: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + let encoded = tw_encoding::hex::encode(signed.encoded, false); + + assert_eq!(signed.error, Proto::Error::OK); + assert_eq!(&encoded, "02000000017be4e642bb278018ab12277de9427773ad1c5f5b1d164a157e0d99aa48dc1c1e000000006a473044022078eda020d4b86fcb3af78ef919912e6d79b81164dbbb0b0b96da6ac58a2de4b102201a5fd8d48734d5a02371c4b5ee551a69dca3842edbf577d863cf8ae9fdbbd4590121036666dd712e05a487916384bfcd5973eb53e8038eccbbf97f7eed775b87389536ffffffff01c0aff629010000001976a9145eaaa4f458f9158f86afcba08dd7448d27045e3d88ac00000000"); +} diff --git a/rust/tw_bitcoin/tests/p2sh.rs b/rust/tw_bitcoin/tests/p2sh.rs new file mode 100644 index 00000000000..d09a15165ad --- /dev/null +++ b/rust/tw_bitcoin/tests/p2sh.rs @@ -0,0 +1,147 @@ +mod common; + +use bitcoin::script::PushBytesBuf; +use bitcoin::{PublicKey, ScriptBuf}; +use common::{hex, MINER_FEE, ONE_BTC}; +use tw_bitcoin::aliases::*; +use tw_bitcoin::entry::BitcoinEntry; +use tw_bitcoin::modules::signer::Signer; +use tw_coin_entry::coin_entry::CoinEntry; +use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_proto::BitcoinV2::Proto; +use tw_proto::Utxo::Proto as UtxoProto; + +#[test] +fn coin_entry_sign_input_p2pkh_output_p2sh() { + let coin = EmptyCoinContext; + + let alice_private_key = hex("56429688a1a6b00b90ccd22a0de0a376b6569d8684022ae92229a28478bfb657"); + let alice_pubkey = hex("036666dd712e05a487916384bfcd5973eb53e8038eccbbf97f7eed775b87389536"); + let bob_private_key = hex("b7da1ec42b19085fe09fec54b9d9eacd998ae4e6d2ad472be38d8393391b9ead"); + let bob_pubkey = hex("037ed9a436e11ec4947ac4b7823787e24ba73180f1edd2857bff19c9f4d62b65bf"); + + // Create transaction with P2SH as output (spend). + + let txid: Vec = hex("e7503721268d0547b3b009dab56e5ebd8bcadbfc7dfae3468a56b5cb0c07a2f7") + .into_iter() + .rev() + .collect(); + + // We use a simple P2PKH as the redeem script (ie. P2PKH embedded inside P2SH). + let bob_native_pubkey = PublicKey::from_slice(&bob_pubkey).unwrap(); + let redeem_script = ScriptBuf::new_p2pkh(&bob_native_pubkey.pubkey_hash()); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: 50 * ONE_BTC, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2pkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: 50 * ONE_BTC - MINER_FEE, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2sh(Proto::mod_Output::OutputRedeemScriptOrHash { + variant: ProtoOutputRedeemScriptOrHashBuilder::redeem_script( + redeem_script.as_bytes().into(), + ), + }), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + disable_change_output: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + let encoded = tw_encoding::hex::encode(signed.encoded, false); + assert_eq!(signed.error, Proto::Error::OK); + assert_eq!(&encoded, "0200000001f7a2070ccbb5568a46e3fa7dfcdbca8bbd5e6eb5da09b0b347058d26213750e7000000006a473044022007c88caf624c0a130fc79d2835ed5b6db49f2dea0d5e685f06138aaa4a904d690220243fe7744c8b48759e74a87075de3f548988252a770871fc1444652bb32ec46e0121036666dd712e05a487916384bfcd5973eb53e8038eccbbf97f7eed775b87389536ffffffff01c0aff6290100000017a914a519b524d55ae8972e8e0e6b9d645ab20eb2635e8700000000"); + + // Create transaction with P2SH as input (claim). + + let txid: Vec = hex("5d99b77a411a879fb6fa5b442f0d121965346d8e5ab61e0d189967fd5f49bd82") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: 50 * ONE_BTC - MINER_FEE, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + // The way P2SH is signed in Bitcoin, we first place the redeem script directly here. + variant: ProtoInputBuilder::p2sh(redeem_script.as_bytes().into()), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: 50 * ONE_BTC - MINER_FEE - MINER_FEE, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2pkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(alice_pubkey.as_slice().into()), + }), + }), + }; + + let mut signing = Proto::SigningInput { + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + disable_change_output: true, + ..Default::default() + }; + + // Generate the sighashes. + let sighashes = BitcoinEntry.preimage_hashes(&coin, signing.clone()); + assert_eq!(sighashes.error, Proto::Error::OK); + + // Sign the sighashes. + let signatures = Signer::signatures_from_proto( + &sighashes, + bob_private_key.to_vec(), + Default::default(), + false, + ) + .unwrap(); + + let sig = &signatures[0]; + + // Construc the final redeem scrip with the necessary stack items (signature + pubkey). + let mut sig_buf = PushBytesBuf::new(); + sig_buf.extend_from_slice(sig).unwrap(); + + let mut redeem_buf = PushBytesBuf::new(); + redeem_buf + .extend_from_slice(redeem_script.as_bytes()) + .unwrap(); + + let finalized = ScriptBuf::builder() + .push_slice(sig_buf) + .push_key(&bob_native_pubkey) + .push_slice(redeem_buf.as_push_bytes()) + .into_script(); + + // Now that we've signed the input, we update the input with the complete, + // finalized redeem script. + signing.inputs[0].to_recipient = ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2sh(finalized.as_bytes().into()), + }); + + // Compile the final transaction. + let signed = BitcoinEntry.compile(&coin, signing, signatures, vec![]); + let encoded = tw_encoding::hex::encode(signed.encoded, false); + assert_eq!(signed.error, Proto::Error::OK); + assert_eq!(&encoded, "020000000182bd495ffd6799180d1eb65a8e6d346519120d2f445bfab69f871a417ab7995d000000008447304402207aad4b72c6d78c81a1e795325bd5ddb449f0a1363205903f5e37950e6b89054102202aaf4dd919700d21fe2431352df99c434378bd0d46b778b445079579300effdf0121037ed9a436e11ec4947ac4b7823787e24ba73180f1edd2857bff19c9f4d62b65bf1976a9145eaaa4f458f9158f86afcba08dd7448d27045e3d88acffffffff01806de729010000001976a914e4c1ea86373d554b8f4efff2cfb0001ea19124d288ac00000000"); +} diff --git a/rust/tw_bitcoin/tests/p2tr_key_path.rs b/rust/tw_bitcoin/tests/p2tr_key_path.rs new file mode 100644 index 00000000000..c32c230c396 --- /dev/null +++ b/rust/tw_bitcoin/tests/p2tr_key_path.rs @@ -0,0 +1,98 @@ +mod common; + +use common::{hex, MINER_FEE, ONE_BTC}; +use tw_bitcoin::aliases::*; +use tw_bitcoin::entry::BitcoinEntry; +use tw_coin_entry::coin_entry::CoinEntry; +use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_proto::BitcoinV2::Proto; +use tw_proto::Utxo::Proto as UtxoProto; + +#[test] +fn coin_entry_sign_input_p2pkh_output_p2tr_key_path() { + let coin = EmptyCoinContext; + + let alice_private_key = hex("12ce558df23528f1aa86f1f51ac7e13a197a06bda27610fa89e13b04c40ee999"); + let alice_pubkey = hex("0351e003fdc48e7f31c9bc94996c91f6c3273b7ef4208a1686021bedf7673bb058"); + let bob_private_key = hex("26c2566adcc030a1799213bfd546e615f6ab06f72085ec6806ff1761da48d227"); + let bob_pubkey = hex("02c0938cf377023dfde55e9c96b3cff4ca8894fb6b5d2009006bd43c0bff69cac9"); + + let txid: Vec = hex("c50563913e5a838f937c94232f5a8fc74e58b629fae41dfdffcc9a70f833b53a") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC * 50, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2pkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: ONE_BTC * 50 - MINER_FEE, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2tr_key_path(bob_pubkey.as_slice().into()), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + disable_change_output: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + let encoded = tw_encoding::hex::encode(signed.encoded, false); + assert_eq!(signed.error, Proto::Error::OK); + assert_eq!(&encoded, "02000000013ab533f8709accfffd1de4fa29b6584ec78f5a2f23947c938f835a3e916305c5000000006b48304502210086ab2c2192e2738529d6cd9604d8ee75c5b09b0c2f4066a5c5fa3f87a26c0af602202afc7096aaa992235c43e712146057b5ed6a776d82b9129620bc5a21991c0a5301210351e003fdc48e7f31c9bc94996c91f6c3273b7ef4208a1686021bedf7673bb058ffffffff01c0aff62901000000225120e01cfdd05da8fa1d71f987373f3790d45dea9861acb0525c86656fe50f4397a600000000"); + + let txid: Vec = hex("9a582032f6a50cedaff77d3d5604b33adf8bc31bdaef8de977c2187e395860ac") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC * 50 - MINER_FEE, + sighash_type: UtxoProto::SighashType::UseDefault, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2tr_key_path(Proto::mod_Input::InputTaprootKeyPath { + public_key: bob_pubkey.as_slice().into(), + one_prevout: false, + }), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: ONE_BTC * 50 - MINER_FEE - MINER_FEE, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2tr_key_path(alice_pubkey.as_slice().into()), + }), + }; + + let signing = Proto::SigningInput { + private_key: bob_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + disable_change_output: true, + // We enable deterministic Schnorr signatures here + dangerous_use_fixed_schnorr_rng: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + let encoded = tw_encoding::hex::encode(signed.encoded, false); + assert_eq!(signed.error, Proto::Error::OK); + assert_eq!(&encoded, "02000000000101ac6058397e18c277e98defda1bc38bdf3ab304563d7df7afed0ca5f63220589a0000000000ffffffff01806de72901000000225120a5c027857e359d19f625e52a106b8ac6ca2d6a8728f6cf2107cd7958ee0787c20140ec2d3910d41506b60aaa20520bb72f15e2d2cbd97e3a8e26ee7bad5f4c56b0f2fb0ceaddac33cb2813a33ba017ba6b1d011bab74a0426f12a2bcf47b4ed5bc8600000000"); +} diff --git a/rust/tw_bitcoin/tests/p2tr_script_path.rs b/rust/tw_bitcoin/tests/p2tr_script_path.rs new file mode 100644 index 00000000000..b2f01b8f606 --- /dev/null +++ b/rust/tw_bitcoin/tests/p2tr_script_path.rs @@ -0,0 +1,161 @@ +mod common; + +use bitcoin::taproot::LeafVersion; +use bitcoin::PublicKey; +use common::hex; +use tw_bitcoin::aliases::*; +use tw_bitcoin::entry::BitcoinEntry; +use tw_bitcoin::modules::transactions::{BRC20TransferInscription, Brc20Ticker}; +use tw_coin_entry::coin_entry::CoinEntry; +use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_misc::traits::ToBytesVec; +use tw_proto::BitcoinV2::Proto; +use tw_proto::Utxo::Proto as UtxoProto; + +#[test] +/// A test for the custom P2TR script-path builders. This test essentially +/// reconstruct the BRC20 transfer tests, but without using the convenience +/// builders. +fn coin_entry_custom_script_path() { + let coin = EmptyCoinContext; + + let alice_private_key = hex("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129"); + let alice_pubkey = hex("030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb"); + + let txid: Vec = hex("8ec895b4d30adb01e38471ca1019bfc8c3e5fbd1f28d9e7b5653260d89989008") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 1, + value: 26_400, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + // Build the BRC20 transfer outside the library, only provide essential + // information to the builder. + let ticker = Brc20Ticker::new("oadf".to_string()).unwrap(); + let inscribe_to = PublicKey::from_slice(&alice_pubkey).unwrap(); + let transfer = BRC20TransferInscription::new(inscribe_to, ticker, 20).unwrap(); + let merkle_root = transfer.inscription().spend_info().merkle_root().unwrap(); + + // Provide the public key ("internal key") and the merkle root directly to the builder. + let out1 = Proto::Output { + value: 7_000, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2tr_script_path( + Proto::mod_Output::OutputTaprootScriptPath { + internal_key: alice_pubkey.to_vec().into(), + merkle_root: merkle_root.to_vec().into(), + }, + ), + }), + }; + + // Change/return transaction. + let out2 = Proto::Output { + value: 16_400, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(alice_pubkey.as_slice().into()), + }), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1, out2], + input_selector: UtxoProto::InputSelector::UseAll, + disable_change_output: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + assert_eq!(signed.error, Proto::Error::OK); + assert_eq!( + signed.txid, + hex("797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1") + ); + + let encoded = tw_encoding::hex::encode(signed.encoded, false); + let transaction = signed.transaction.unwrap(); + + assert_eq!(transaction.inputs.len(), 1); + assert_eq!(transaction.outputs.len(), 2); + assert_eq!(&encoded, "02000000000101089098890d2653567b9e8df2d1fbe5c3c8bf1910ca7184e301db0ad3b495c88e0100000000ffffffff02581b000000000000225120e8b706a97732e705e22ae7710703e7f589ed13c636324461afa443016134cc051040000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d02483045022100a44aa28446a9a886b378a4a65e32ad9a3108870bd725dc6105160bed4f317097022069e9de36422e4ce2e42b39884aa5f626f8f94194d1013007d5a1ea9220a06dce0121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000"); + + // https://www.blockchain.com/explorer/transactions/btc/797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1 + let txid: Vec = hex("797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1") + .into_iter() + .rev() + .collect(); + + // Prepare the BRC20 payload and control block. + let payload = transfer.inscription().taproot_program().to_owned(); + let control_block = transfer + .inscription() + .spend_info() + .control_block(&(payload.to_owned(), LeafVersion::TapScript)) + .unwrap(); + + // Provide the the payload and control block directly to the builder. + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: 7_000, + sighash_type: UtxoProto::SighashType::UseDefault, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2tr_script_path( + Proto::mod_Input::InputTaprootScriptPath { + one_prevout: false, + payload: payload.to_vec().into(), + control_block: control_block.serialize().into(), + }, + ), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: 546, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(alice_pubkey.as_slice().into()), + }), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + disable_change_output: true, + // We enable deterministic Schnorr signatures here + dangerous_use_fixed_schnorr_rng: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + assert_eq!(signed.error, Proto::Error::OK); + + // https://www.blockchain.com/explorer/transactions/btc/7046dc2689a27e143ea2ad1039710885147e9485ab6453fa7e87464aa7dd3eca + assert_eq!( + signed.txid, + hex("7046dc2689a27e143ea2ad1039710885147e9485ab6453fa7e87464aa7dd3eca") + ); + + let encoded = tw_encoding::hex::encode(signed.encoded, false); + let transaction = signed.transaction.unwrap(); + + assert_eq!(encoded, "02000000000101b11f1782607a1fe5f033ccf9dc17404db020a0dedff94183596ee67ad4177d790000000000ffffffff012202000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d03406a35548b8fa4620028e021a944c1d3dc6e947243a7bfc901bf63fefae0d2460efa149a6440cab51966aa4f09faef2d1e5efcba23ab4ca6e669da598022dbcfe35b0063036f7264010118746578742f706c61696e3b636861727365743d7574662d3800377b2270223a226272632d3230222c226f70223a227472616e73666572222c227469636b223a226f616466222c22616d74223a223230227d6821c00f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000"); + assert_eq!(transaction.inputs.len(), 1); + assert_eq!(transaction.outputs.len(), 1); +} diff --git a/rust/tw_bitcoin/tests/p2wpkh.rs b/rust/tw_bitcoin/tests/p2wpkh.rs new file mode 100644 index 00000000000..5dfedc04959 --- /dev/null +++ b/rust/tw_bitcoin/tests/p2wpkh.rs @@ -0,0 +1,102 @@ +mod common; + +use common::{hex, MINER_FEE, ONE_BTC}; +use tw_bitcoin::aliases::*; +use tw_bitcoin::entry::BitcoinEntry; +use tw_coin_entry::coin_entry::CoinEntry; +use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_proto::BitcoinV2::Proto; +use tw_proto::Utxo::Proto as UtxoProto; + +#[test] +fn coin_entry_sign_input_p2pkh_output_p2wpkh() { + let coin = EmptyCoinContext; + + let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); + let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); + let bob_private_key = hex("05dead4689ec7d55de654771120866be83bf1b8e25c9a1b77fc58a336e1cd1a3"); + let bob_pubkey = hex("025a0af1510f0f24d40dd00d7c0e51605ca504bbc177c3e19b065f373a1efdd22f"); + + // Create transaction with P2WPKH as output. + let txid: Vec = hex("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC * 50, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2pkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: ONE_BTC * 50 - MINER_FEE, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(bob_pubkey.as_slice().into()), + }), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + disable_change_output: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + let encoded = tw_encoding::hex::encode(signed.encoded, false); + + assert_eq!(signed.error, Proto::Error::OK); + assert_eq!(&encoded, "020000000111b9f62923af73e297abb69f749e7a1aa2735fbdfd32ac5f6aa89e5c96841c18000000006b483045022100df9ed0b662b759e68b89a42e7144cddf787782a7129d4df05642dd825930e6e6022051a08f577f11cc7390684bbad2951a6374072253ffcf2468d14035ed0d8cd6490121028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28fffffffff01c0aff629010000001600140d0e1cec6c2babe8badde5e9b3dea667da90036d00000000"); + + // Create transaction with P2WPKH as input (claim). + + let txid: Vec = hex("858e450a1da44397bde05ca2f8a78510d74c623cc2f69736a8b3fbfadc161f6e") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC * 50 - MINER_FEE, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(bob_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: ONE_BTC * 50 - MINER_FEE - MINER_FEE, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(alice_pubkey.as_slice().into()), + }), + }), + }; + + let signing = Proto::SigningInput { + private_key: bob_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + disable_change_output: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + let encoded = tw_encoding::hex::encode(signed.encoded, false); + + assert_eq!(signed.error, Proto::Error::OK); + assert_eq!(&encoded, "020000000001016e1f16dcfafbb3a83697f6c23c624cd71085a7f8a25ce0bd9743a41d0a458e850000000000ffffffff01806de7290100000016001460cda7b50f14c152d7401c28ae773c698db9237302483045022100a9b517de5a5e036d7133df499b5b751db6f9a01576a6c5dc38229ec08b6c45cd02200e42c9f8c707c9bf0ceab4f739ec8d683dc1f1f29e195a8da9bc183584d624a60121025a0af1510f0f24d40dd00d7c0e51605ca504bbc177c3e19b065f373a1efdd22f00000000"); +} diff --git a/rust/tw_bitcoin/tests/p2wsh.rs b/rust/tw_bitcoin/tests/p2wsh.rs new file mode 100644 index 00000000000..fd50fd831d5 --- /dev/null +++ b/rust/tw_bitcoin/tests/p2wsh.rs @@ -0,0 +1,142 @@ +mod common; + +use bitcoin::consensus::Encodable; +use bitcoin::{PublicKey, ScriptBuf, Witness}; +use common::{hex, MINER_FEE, ONE_BTC}; +use tw_bitcoin::aliases::*; +use tw_bitcoin::entry::BitcoinEntry; +use tw_bitcoin::modules::signer::Signer; +use tw_coin_entry::coin_entry::CoinEntry; +use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_proto::BitcoinV2::Proto; +use tw_proto::Utxo::Proto as UtxoProto; + +#[test] +fn coin_entry_sign_input_p2pkh_output_p2wsh() { + let coin = EmptyCoinContext; + + let alice_private_key = hex("56429688a1a6b00b90ccd22a0de0a376b6569d8684022ae92229a28478bfb657"); + let alice_pubkey = hex("036666dd712e05a487916384bfcd5973eb53e8038eccbbf97f7eed775b87389536"); + let bob_private_key = hex("b7da1ec42b19085fe09fec54b9d9eacd998ae4e6d2ad472be38d8393391b9ead"); + let bob_pubkey = hex("037ed9a436e11ec4947ac4b7823787e24ba73180f1edd2857bff19c9f4d62b65bf"); + + // Create transaction with P2WSH as output (spend). + + let txid: Vec = hex("c01007bb55bde4e70278e1154c34db72f34a833687d3f37443bd5c49137ee5fe") + .into_iter() + .rev() + .collect(); + + // We use a simple P2PKH as the redeem script (ie. P2PKH embedded inside P2WSH). + let bob_native_pubkey = PublicKey::from_slice(&bob_pubkey).unwrap(); + let redeem_script = ScriptBuf::new_p2pkh(&bob_native_pubkey.pubkey_hash()); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: 50 * ONE_BTC - 2 * MINER_FEE, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2pkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: 50 * ONE_BTC - 3 * MINER_FEE, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wsh(Proto::mod_Output::OutputRedeemScriptOrHash { + variant: ProtoOutputRedeemScriptOrHashBuilder::redeem_script( + redeem_script.as_bytes().into(), + ), + }), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + change_output: Default::default(), + disable_change_output: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + let encoded = tw_encoding::hex::encode(signed.encoded, false); + assert_eq!(signed.error, Proto::Error::OK); + assert_eq!(&encoded, "0200000001fee57e13495cbd4374f3d38736834af372db344c15e17802e7e4bd55bb0710c0000000006b483045022100edd19b379131b9f7a05ff2a79313ccec181f2e3fd06901e27ffff4fa500dbf95022060ae976cd70b8af956fdf4582bb1289bdf233a5bace82e69ea9b7cfb55c583370121036666dd712e05a487916384bfcd5973eb53e8038eccbbf97f7eed775b87389536ffffffff01402bd82901000000220020883a539555e537e0498732376a3d4d282e304bce7bfda6876a2b63b08a04f54400000000"); + + // Create transaction with P2WSH as input (claim). + + let txid: Vec = hex("dd9d4ca23532f5c89d016e1aacef1210ab5b9d00527c633969841daca7dd17c7") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: 50 * ONE_BTC - 3 * MINER_FEE, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + // The way P2WSH is signed in Bitcoin, we first place the redeem script directly here. + variant: ProtoInputBuilder::p2wsh(redeem_script.to_bytes().into()), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: 50 * ONE_BTC - 4 * MINER_FEE, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2pkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(alice_pubkey.as_slice().into()), + }), + }), + }; + + let mut signing = Proto::SigningInput { + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + disable_change_output: true, + ..Default::default() + }; + + // Generate the sighashes. + let sighashes = BitcoinEntry.preimage_hashes(&coin, signing.clone()); + + // Sign the sighashes. + let signatures = Signer::signatures_from_proto( + &sighashes, + bob_private_key.to_vec(), + Default::default(), + false, + ) + .unwrap(); + + let sig = &signatures[0]; + + // Construc the final redeem witness stack with the necessary witness stack items (signature + pubkey). + let mut finalized = Witness::new(); + finalized.push(sig); + finalized.push(bob_native_pubkey.to_bytes()); + finalized.push(redeem_script); + + // The witness stack must be encoded correctly. + let mut encoded = vec![]; + let _ = finalized.consensus_encode(&mut encoded).unwrap(); + + // Now that we've signed the input, we update the input with the complete, + // finalized redeem witness stack.. + signing.inputs[0].to_recipient = ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wsh(encoded.into()), + }); + + // Compile the final transaction. + let signed = BitcoinEntry.compile(&coin, signing, signatures, vec![]); + let encoded = tw_encoding::hex::encode(signed.encoded, false); + assert_eq!(signed.error, Proto::Error::OK); + assert_eq!(&encoded, "02000000000101c717dda7ac1d846939637c52009d5bab1012efac1a6e019dc8f53235a24c9ddd0000000000ffffffff0100e9c829010000001976a914e4c1ea86373d554b8f4efff2cfb0001ea19124d288ac0347304402201d22810b5580a49a2e73d7c4ea90754b5d70d36adb9a8f0c9cb7393da1d1d28f02207683b2e3d31a5c7e74126681f1f2a7249b7a3a918d5890ef69b94bd3bb4fb9300121037ed9a436e11ec4947ac4b7823787e24ba73180f1edd2857bff19c9f4d62b65bf1976a9145eaaa4f458f9158f86afcba08dd7448d27045e3d88ac00000000"); +} diff --git a/rust/tw_bitcoin/tests/plan_builder.rs b/rust/tw_bitcoin/tests/plan_builder.rs new file mode 100644 index 00000000000..935c5ada511 --- /dev/null +++ b/rust/tw_bitcoin/tests/plan_builder.rs @@ -0,0 +1,192 @@ +mod common; + +use common::{hex, ONE_BTC}; +use tw_bitcoin::aliases::*; +use tw_bitcoin::BitcoinEntry; +use tw_coin_entry::coin_entry::CoinEntry; +use tw_coin_entry::modules::plan_builder::PlanBuilder; +use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_proto::BitcoinV2::Proto; +use tw_proto::Utxo::Proto as UtxoProto; + +#[test] +fn transaction_plan_compose_brc20() { + let _coin = EmptyCoinContext; + + let alice_private_key = hex("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129"); + let alice_pubkey = hex("030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb"); + + let txid1: Vec = hex("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid1.as_slice().into(), + vout: 0, + value: ONE_BTC, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let txid2: Vec = hex("858e450a1da44397bde05ca2f8a78510d74c623cc2f69736a8b3fbfadc161f6e") + .into_iter() + .rev() + .collect(); + + let tx2 = Proto::Input { + txid: txid2.as_slice().into(), + vout: 0, + value: ONE_BTC * 2, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let tagged_output = Proto::Output { + value: 546, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(alice_pubkey.as_slice().into()), + }), + }), + }; + + let change_output = Proto::Output { + // Will be set by the library. + value: 0, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(alice_pubkey.as_slice().into()), + }), + }), + }; + + let brc20_inscription = Proto::mod_Input::InputBrc20Inscription { + one_prevout: false, + inscribe_to: alice_pubkey.as_slice().into(), + ticker: "oadf".into(), + transfer_amount: 20, + }; + + let compose = Proto::ComposePlan { + compose: Proto::mod_ComposePlan::OneOfcompose::brc20( + Proto::mod_ComposePlan::ComposeBrc20Plan { + private_key: alice_private_key.clone().into(), + inputs: vec![tx1.clone(), tx2.clone()], + input_selector: UtxoProto::InputSelector::SelectAscending, + tagged_output: Some(tagged_output.clone()), + inscription: Some(brc20_inscription), + fee_per_vb: 25, + change_output: Some(change_output.clone()), + disable_change_output: false, + }, + ), + }; + + // Compute plan + let builder = BitcoinEntry.plan_builder().unwrap(); + let built = builder.plan(&_coin, compose); + assert_eq!(built.error, Proto::Error::OK); + + let Proto::mod_TransactionPlan::OneOfplan::brc20(plan) = built.plan else { panic!() }; + + // Check basics of the COMMIT transaction. + let commit_signing = { + let mut commit = plan.commit.unwrap(); + // One input covers all outputs. + assert_eq!(commit.version, 2); + assert_eq!(commit.private_key, alice_private_key); + assert_eq!(commit.inputs.len(), 1); + // BRC20 inscription output + change. + assert_eq!(commit.outputs.len(), 2); + // Use inputs as provided (already selected by TransactionPlan). + assert_eq!(commit.input_selector, UtxoProto::InputSelector::UseAll); + assert_eq!(commit.fee_per_vb, 0); + // Change output generation is disabled, inclulded in `commit.outputs`. + assert_eq!(commit.change_output, Default::default()); + assert!(commit.disable_change_output); + + // Check first input. + assert_eq!(commit.inputs[0], tx1); + + // Check first output. + let res_out_brc20 = &commit.outputs[0]; + assert_eq!(res_out_brc20.value, 3846); + let Proto::mod_Output::OneOfto_recipient::builder(builder) = &res_out_brc20.to_recipient else { panic!() }; + let Proto::mod_Output::mod_OutputBuilder::OneOfvariant::brc20_inscribe(brc20) = &builder.variant else { panic!() }; + assert_eq!(brc20.inscribe_to, alice_pubkey); + assert_eq!(brc20.ticker, "oadf"); + assert_eq!(brc20.transfer_amount, 20); + + // Check second output (ie. change output). + let res_out_change = &commit.outputs[1]; + assert_eq!(res_out_change.value, ONE_BTC - 3846 - 3175); // Change: tx1 value - out1 value + assert_eq!(res_out_change.to_recipient, change_output.to_recipient); + + commit.private_key = alice_private_key.clone().into(); + commit + }; + + // Check basics of the REVEAL transaction. + let reveal_signing = { + let mut reveal = plan.reveal.unwrap(); + assert_eq!(reveal.version, 2); + assert_eq!(reveal.private_key, alice_private_key); + // One inputs covers all outputs. + assert_eq!(reveal.inputs.len(), 1); + assert_eq!(reveal.outputs.len(), 1); + // Use inputs as provided. + assert_eq!(reveal.input_selector, UtxoProto::InputSelector::UseAll); + assert_eq!(reveal.fee_per_vb, 0); + // Change output generation is disabled. + assert_eq!(reveal.change_output, Default::default()); + assert!(reveal.disable_change_output); + + // Check first and only input. + let res_in_brc20 = &reveal.inputs[0]; + //assert_eq!(plan_input.txid, ) + assert_eq!(res_in_brc20.sequence, u32::MAX); + assert_eq!(res_in_brc20.value, 3846); + assert_eq!( + res_in_brc20.sighash_type, + UtxoProto::SighashType::UseDefault + ); + let Proto::mod_Input::OneOfto_recipient::builder(builder) = &res_in_brc20.to_recipient else { panic!() }; + let Proto::mod_Input::mod_InputBuilder::OneOfvariant::brc20_inscribe(brc20) = &builder.variant else { panic!() }; + assert_eq!(brc20.inscribe_to, alice_pubkey); + assert_eq!(brc20.ticker, "oadf"); + assert_eq!(brc20.transfer_amount, 20); + + // Check first and only output. + assert_eq!(reveal.outputs[0], tagged_output); + + reveal.private_key = alice_private_key.into(); + reveal + }; + + // Signed both transactions from the returned plan. + let commit_signed = BitcoinEntry.sign(&_coin, commit_signing); + assert_eq!(commit_signed.error, Proto::Error::OK); + let reveal_signed = BitcoinEntry.sign(&_coin, reveal_signing); + assert_eq!(reveal_signed.error, Proto::Error::OK); + + // Note that the API returns this in a non-reversed manner, so we need to reverse it first. + let commit_txid = commit_signed.txid.iter().copied().rev().collect::>(); + + // IMPORTANT: The input of the REVEAL transaction must reference the COMMIT transaction (Id). + assert_eq!( + commit_txid, + reveal_signed.transaction.as_ref().unwrap().inputs[0] + .txid + .as_ref() + ); + + //dbg!(&commit_signed); + //dbg!(&reveal_signed); +} diff --git a/rust/tw_bitcoin/tests/send_to_address.rs b/rust/tw_bitcoin/tests/send_to_address.rs new file mode 100644 index 00000000000..030148ea415 --- /dev/null +++ b/rust/tw_bitcoin/tests/send_to_address.rs @@ -0,0 +1,316 @@ +mod common; + +use bitcoin::{Address, PublicKey, ScriptBuf}; +use common::hex; +use secp256k1::XOnlyPublicKey; +use tw_bitcoin::aliases::*; +use tw_bitcoin::entry::BitcoinEntry; +use tw_coin_entry::coin_entry::CoinEntry; +use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_proto::BitcoinV2::Proto; +use tw_proto::Utxo::Proto as UtxoProto; + +#[test] +fn send_to_p2sh_address() { + let coin = EmptyCoinContext; + + let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); + let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); + let bob_pubkey = hex("025a0af1510f0f24d40dd00d7c0e51605ca504bbc177c3e19b065f373a1efdd22f"); + + let txid: Vec = hex("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: 10_000, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2pkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + // Create the P2SH address. + let recipient = PublicKey::from_slice(&bob_pubkey).unwrap(); + // We use a simple P2PKH as the redeem script. + let redeem_script = ScriptBuf::new_p2pkh(&recipient.pubkey_hash()); + let address = Address::p2sh(&redeem_script, bitcoin::Network::Bitcoin).unwrap(); + + // The output variant is derived from the specified address. + let out1 = Proto::Output { + value: 1_000, + to_recipient: ProtoOutputRecipient::from_address(address.to_string().into()), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + disable_change_output: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + assert_eq!(signed.error, Proto::Error::OK); + + let tx = signed.transaction.as_ref().unwrap(); + assert_eq!(tx.inputs.len(), 1); + assert_eq!(tx.outputs.len(), 1); + + // The expected P2SH scriptPubkey + let expected = ScriptBuf::new_p2sh(&redeem_script.script_hash()); + + assert_eq!(tx.outputs[0].value, 1_000); + assert_eq!(tx.outputs[0].script_pubkey, expected.as_bytes()); + assert!(tx.outputs[0].taproot_payload.is_empty()); + assert!(tx.outputs[0].control_block.is_empty()); +} + +#[test] +fn send_to_p2pkh_address() { + let coin = EmptyCoinContext; + + let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); + let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); + let bob_pubkey = hex("025a0af1510f0f24d40dd00d7c0e51605ca504bbc177c3e19b065f373a1efdd22f"); + + let txid: Vec = hex("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: 10_000, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2pkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + // Create the P2PKH address. + let recipient = PublicKey::from_slice(&bob_pubkey).unwrap(); + let address = Address::p2pkh(&recipient, bitcoin::Network::Bitcoin); + let address_string = address.to_string(); + + // The output variant is derived from the specified address. + let out1 = Proto::Output { + value: 1_000, + to_recipient: ProtoOutputRecipient::from_address(address_string.as_str().into()), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + disable_change_output: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + assert_eq!(signed.error, Proto::Error::OK); + + let tx = signed.transaction.as_ref().unwrap(); + assert_eq!(tx.inputs.len(), 1); + assert_eq!(tx.outputs.len(), 1); + + // The expected P2PKH scriptPubkey + let expected = ScriptBuf::new_p2pkh(&recipient.pubkey_hash()); + + assert_eq!(tx.outputs[0].value, 1_000); + assert_eq!(tx.outputs[0].script_pubkey, expected.as_bytes()); + assert!(tx.outputs[0].taproot_payload.is_empty()); + assert!(tx.outputs[0].control_block.is_empty()); +} + +#[test] +fn send_to_p2wsh_address() { + let coin = EmptyCoinContext; + + let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); + let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); + let bob_pubkey = hex("025a0af1510f0f24d40dd00d7c0e51605ca504bbc177c3e19b065f373a1efdd22f"); + + let txid: Vec = hex("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: 10_000, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2pkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + // Create the P2WSH address. + let recipient = PublicKey::from_slice(&bob_pubkey).unwrap(); + // We use a simple P2PKH as the redeem script. + let redeem_script = ScriptBuf::new_p2pkh(&recipient.pubkey_hash()); + let address = Address::p2wsh(&redeem_script, bitcoin::Network::Bitcoin); + + // The output variant is derived from the specified address. + let out1 = Proto::Output { + value: 1_000, + to_recipient: ProtoOutputRecipient::from_address(address.to_string().into()), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + disable_change_output: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + assert_eq!(signed.error, Proto::Error::OK); + + let tx = signed.transaction.as_ref().unwrap(); + assert_eq!(tx.inputs.len(), 1); + assert_eq!(tx.outputs.len(), 1); + + // The expected Pw2SH scriptPubkey + let expected = ScriptBuf::new_v0_p2wsh(&redeem_script.wscript_hash()); + + assert_eq!(tx.outputs[0].value, 1_000); + assert_eq!(tx.outputs[0].script_pubkey, expected.as_bytes()); + assert!(tx.outputs[0].taproot_payload.is_empty()); + assert!(tx.outputs[0].control_block.is_empty()); +} + +#[test] +fn send_to_p2wpkh_address() { + let coin = EmptyCoinContext; + + let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); + let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); + let bob_pubkey = hex("025a0af1510f0f24d40dd00d7c0e51605ca504bbc177c3e19b065f373a1efdd22f"); + + let txid: Vec = hex("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: 10_000, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2pkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + // Create the P2WPKH address. + let recipient = PublicKey::from_slice(&bob_pubkey).unwrap(); + let address = Address::p2wpkh(&recipient, bitcoin::Network::Bitcoin).unwrap(); + let address_string = address.to_string(); + + // The output variant is derived from the specified address. + let out1 = Proto::Output { + value: 1_000, + to_recipient: ProtoOutputRecipient::from_address(address_string.as_str().into()), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + disable_change_output: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + assert_eq!(signed.error, Proto::Error::OK); + + let tx = signed.transaction.as_ref().unwrap(); + assert_eq!(tx.inputs.len(), 1); + assert_eq!(tx.outputs.len(), 1); + + // The expected P2WPKH scriptPubkey + let expected = ScriptBuf::new_v0_p2wpkh(&recipient.wpubkey_hash().unwrap()); + + assert_eq!(tx.outputs[0].value, 1_000); + assert_eq!(tx.outputs[0].script_pubkey, expected.as_bytes()); + assert!(tx.outputs[0].taproot_payload.is_empty()); + assert!(tx.outputs[0].control_block.is_empty()); +} + +#[test] +fn send_to_p2tr_key_path_address() { + let coin = EmptyCoinContext; + + let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); + let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); + let bob_pubkey = hex("025a0af1510f0f24d40dd00d7c0e51605ca504bbc177c3e19b065f373a1efdd22f"); + + let txid: Vec = hex("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911") + .into_iter() + .rev() + .collect(); + + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: 10_000, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2pkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + // Create the P2TR key-path address. + let secp = secp256k1::Secp256k1::new(); + + let recipient = PublicKey::from_slice(&bob_pubkey).unwrap(); + let xonly = XOnlyPublicKey::from(recipient.inner); + let address = Address::p2tr(&secp, xonly, None, bitcoin::Network::Bitcoin); + let address_string = address.to_string(); + + // The output variant is derived from the specified address. + let out1 = Proto::Output { + value: 1_000, + to_recipient: ProtoOutputRecipient::from_address(address_string.as_str().into()), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1], + input_selector: UtxoProto::InputSelector::UseAll, + disable_change_output: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + assert_eq!(signed.error, Proto::Error::OK); + + let tx = signed.transaction.as_ref().unwrap(); + assert_eq!(tx.inputs.len(), 1); + assert_eq!(tx.outputs.len(), 1); + + // The expected P2TR key-path scriptPubkey + let expected = ScriptBuf::new_v1_p2tr(&secp, xonly, None); + + assert_eq!(tx.outputs[0].value, 1_000); + assert_eq!(tx.outputs[0].script_pubkey, expected.as_bytes()); + assert!(tx.outputs[0].taproot_payload.is_empty()); + assert!(tx.outputs[0].control_block.is_empty()); +} diff --git a/rust/tw_coin_entry/Cargo.toml b/rust/tw_coin_entry/Cargo.toml new file mode 100644 index 00000000000..8b55efc5c0e --- /dev/null +++ b/rust/tw_coin_entry/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "tw_coin_entry" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde_json = "1.0.95" +tw_keypair = { path = "../tw_keypair" } +tw_memory = { path = "../tw_memory" } +tw_misc = { path = "../tw_misc" } +tw_number = { path = "../tw_number" } +tw_proto = { path = "../tw_proto" } + +[features] +test-utils = [] diff --git a/rust/tw_coin_entry/src/coin_context.rs b/rust/tw_coin_entry/src/coin_context.rs new file mode 100644 index 00000000000..8d6ccc92e35 --- /dev/null +++ b/rust/tw_coin_entry/src/coin_context.rs @@ -0,0 +1,15 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_keypair::tw::PublicKeyType; + +/// Extend the trait with methods required for blockchain additions. +pub trait CoinContext { + fn public_key_type(&self) -> PublicKeyType; + + // Example: + // fn ss58_prefix(&self) -> Option; +} diff --git a/rust/tw_coin_entry/src/coin_entry.rs b/rust/tw_coin_entry/src/coin_entry.rs new file mode 100644 index 00000000000..195efe7263b --- /dev/null +++ b/rust/tw_coin_entry/src/coin_entry.rs @@ -0,0 +1,97 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::coin_context::CoinContext; +use crate::derivation::Derivation; +use crate::error::AddressResult; +use crate::modules::json_signer::JsonSigner; +use crate::modules::plan_builder::PlanBuilder; +use crate::prefix::Prefix; +use std::fmt; +use tw_keypair::tw::PublicKey; +use tw_memory::Data; +use tw_proto::{MessageRead, MessageWrite}; + +use crate::modules::message_signer::MessageSigner; +pub use tw_proto::{ProtoError, ProtoResult}; + +pub type PrivateKeyBytes = Data; +pub type SignatureBytes = Data; +pub type PublicKeyBytes = Data; + +pub trait CoinAddress: fmt::Display { + fn data(&self) -> Data; +} + +pub trait CoinEntry { + type AddressPrefix: Prefix; + type Address: CoinAddress; + type SigningInput<'a>: MessageRead<'a> + MessageWrite; + type SigningOutput: MessageWrite; + type PreSigningOutput: MessageWrite; + + // Optional modules: + type JsonSigner: JsonSigner; + type PlanBuilder: PlanBuilder; + type MessageSigner: MessageSigner; + + /// Tries to parse `Self::Address` from the given `address` string by `coin` type and address `prefix`. + fn parse_address( + &self, + coin: &dyn CoinContext, + address: &str, + prefix: Option, + ) -> AddressResult; + + /// Derives an address associated with the given `public_key` by `coin` context, `derivation` and address `prefix`. + fn derive_address( + &self, + coin: &dyn CoinContext, + public_key: PublicKey, + derivation: Derivation, + prefix: Option, + ) -> AddressResult; + + /// Signs a transaction declared as the given `input`. + fn sign(&self, coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput; + + /// Returns hash(es) for signing, needed for external signing. + fn preimage_hashes( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + ) -> Self::PreSigningOutput; + + /// Compiles a transaction with externally-supplied `signatures` and `public_keys`. + fn compile( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Self::SigningOutput; + + /// It is optional, Signing JSON input with private key. + /// Returns `Ok(None)` if the chain doesn't support signing JSON. + #[inline] + fn json_signer(&self) -> Option { + None + } + + /// Planning, for UTXO chains, in preparation for signing. + /// Returns an optional `Plan` builder. Only UTXO chains need it. + #[inline] + fn plan_builder(&self) -> Option { + None + } + + /// It is optional, Signing regular messages. + /// Returns `Ok(None)` if the chain doesn't support regular message signing. + #[inline] + fn message_signer(&self) -> Option { + None + } +} diff --git a/rust/tw_coin_entry/src/coin_entry_ext.rs b/rust/tw_coin_entry/src/coin_entry_ext.rs new file mode 100644 index 00000000000..68caa9fe6e7 --- /dev/null +++ b/rust/tw_coin_entry/src/coin_entry_ext.rs @@ -0,0 +1,234 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::coin_context::CoinContext; +use crate::coin_entry::{CoinAddress, CoinEntry, PublicKeyBytes, SignatureBytes}; +use crate::derivation::Derivation; +use crate::error::SigningResult; +use crate::error::{AddressResult, SigningError, SigningErrorType}; +use crate::modules::json_signer::JsonSigner; +use crate::modules::message_signer::MessageSigner; +use crate::modules::plan_builder::PlanBuilder; +use crate::prefix::AddressPrefix; +use tw_keypair::tw::{PrivateKey, PublicKey}; +use tw_memory::Data; +use tw_proto::{deserialize, serialize, ProtoResult}; + +pub type PrivateKeyBytes = Data; + +/// The [`CoinEntry`] trait extension. +pub trait CoinEntryExt { + fn validate_address( + &self, + coin: &dyn CoinContext, + address: &str, + prefix: Option, + ) -> AddressResult<()>; + + /// Validates and normalizes the given `address`. + fn normalize_address( + &self, + coin: &dyn CoinContext, + address: &str, + prefix: Option, + ) -> AddressResult; + + /// Derives an address associated with the given `public_key` by `coin` context, `derivation` and address `prefix`. + fn derive_address( + &self, + coin: &dyn CoinContext, + public_key: PublicKey, + derivation: Derivation, + prefix: Option, + ) -> AddressResult; + + /// Returns underlying data (public key or key hash). + fn address_to_data( + &self, + coin: &dyn CoinContext, + address: &str, + prefix: Option, + ) -> AddressResult; + + /// Signs a transaction declared as the given `input`. + fn sign(&self, coin: &dyn CoinContext, input: &[u8]) -> ProtoResult; + + /// Returns `true` if the chain supports JSON signing. + fn supports_json_signing(&self) -> bool; + + /// Signs a transaction specified by the JSON representation of signing input and a private key. + /// Returns the JSON representation of the signing output if the blockchain supports JSON signing. + /// Checks [`CoinEntryExt::supports_json_signing`]. + fn sign_json( + &self, + coin: &dyn CoinContext, + input_json: &str, + private_key: PrivateKeyBytes, + ) -> SigningResult; + + /// Returns hash(es) for signing, needed for external signing. + /// It will return a proto object named `PreSigningOutput` which will include hash. + /// We provide a default `PreSigningOutput` in TransactionCompiler.proto. + fn preimage_hashes(&self, coin: &dyn CoinContext, input: &[u8]) -> ProtoResult; + + /// Compiles a transaction with externally-supplied `signatures` and `public_keys`. + fn compile( + &self, + coin: &dyn CoinContext, + input: &[u8], + signatures: Vec, + public_keys: Vec, + ) -> ProtoResult; + + /// Plans a transaction (for UTXO chains only). + fn plan(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult; + + /// Signs a message. + fn sign_message(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult; + + /// Computes preimage hashes of a message. + fn message_preimage_hashes(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult; + + /// Verifies a signature for a message. + fn verify_message(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult; +} + +impl CoinEntryExt for T +where + T: CoinEntry, +{ + fn validate_address( + &self, + coin: &dyn CoinContext, + address: &str, + prefix: Option, + ) -> AddressResult<()> { + let prefix = prefix.map(T::AddressPrefix::try_from).transpose()?; + self.parse_address(coin, address, prefix).map(|_| ()) + } + + fn normalize_address( + &self, + coin: &dyn CoinContext, + address: &str, + prefix: Option, + ) -> AddressResult { + let prefix = prefix.map(T::AddressPrefix::try_from).transpose()?; + // Parse the address and display it. + // Please note that `Self::Address::to_string()` returns a normalize address. + ::parse_address(self, coin, address, prefix).map(|addr| addr.to_string()) + } + + fn derive_address( + &self, + coin: &dyn CoinContext, + public_key: PublicKey, + derivation: Derivation, + prefix: Option, + ) -> AddressResult { + let prefix = prefix + .map(::AddressPrefix::try_from) + .transpose()?; + ::derive_address(self, coin, public_key, derivation, prefix) + .map(|addr| addr.to_string()) + } + + fn address_to_data( + &self, + coin: &dyn CoinContext, + address: &str, + prefix: Option, + ) -> AddressResult { + let prefix = prefix.map(T::AddressPrefix::try_from).transpose()?; + + self.parse_address(coin, address, prefix) + .map(|addr| addr.data()) + } + + fn sign(&self, coin: &dyn CoinContext, input: &[u8]) -> ProtoResult { + let input: T::SigningInput<'_> = deserialize(input)?; + let output = ::sign(self, coin, input); + serialize(&output) + } + + fn supports_json_signing(&self) -> bool { + self.json_signer().is_some() + } + + fn sign_json( + &self, + coin: &dyn CoinContext, + input_json: &str, + private_key: PrivateKeyBytes, + ) -> SigningResult { + let Some(json_signer) = self.json_signer() else { + return Err(SigningError(SigningErrorType::Error_not_supported)); + }; + + let private_key = PrivateKey::new(private_key)?; + json_signer.sign_json(coin, input_json, &private_key) + } + + fn preimage_hashes(&self, coin: &dyn CoinContext, input: &[u8]) -> ProtoResult { + let input: T::SigningInput<'_> = deserialize(input)?; + let output = ::preimage_hashes(self, coin, input); + serialize(&output) + } + + fn compile( + &self, + coin: &dyn CoinContext, + input: &[u8], + signatures: Vec, + public_keys: Vec, + ) -> ProtoResult { + let input: T::SigningInput<'_> = deserialize(input)?; + let output = self.compile(coin, input, signatures, public_keys); + serialize(&output) + } + + fn plan(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult { + let Some(plan_builder) = self.plan_builder() else { + return Err(SigningError(SigningErrorType::Error_not_supported)); + }; + + let input: ::SigningInput<'_> = deserialize(input)?; + let output = plan_builder.plan(coin, input); + serialize(&output).map_err(SigningError::from) + } + + fn sign_message(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult { + let Some(message_signer) = self.message_signer() else { + return Err(SigningError(SigningErrorType::Error_not_supported)); + }; + + let input: ::MessageSigningInput<'_> = + deserialize(input)?; + let output = message_signer.sign_message(coin, input); + serialize(&output).map_err(SigningError::from) + } + + fn message_preimage_hashes(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult { + let Some(message_signer) = self.message_signer() else { + return Err(SigningError(SigningErrorType::Error_not_supported)); + }; + + let input: ::MessageSigningInput<'_> = + deserialize(input)?; + let output = message_signer.message_preimage_hashes(coin, input); + serialize(&output).map_err(SigningError::from) + } + + fn verify_message(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult { + let Some(message_signer) = self.message_signer() else { + return Err(SigningError(SigningErrorType::Error_not_supported)); + }; + + let input: ::MessageVerifyingInput<'_> = + deserialize(input)?; + Ok(message_signer.verify_message(coin, input)) + } +} diff --git a/rust/tw_coin_entry/src/common/compile_input.rs b/rust/tw_coin_entry/src/common/compile_input.rs new file mode 100644 index 00000000000..317c1f3b5f7 --- /dev/null +++ b/rust/tw_coin_entry/src/common/compile_input.rs @@ -0,0 +1,46 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::coin_entry::{PublicKeyBytes, SignatureBytes}; +use crate::error::{SigningError, SigningErrorType, SigningResult}; +use tw_keypair::KeyPairError; + +pub struct SingleSignaturePubkey { + pub signature: Signature, + pub public_key: PublicKey, +} + +impl SingleSignaturePubkey +where + Signature: for<'a> TryFrom<&'a [u8], Error = KeyPairError>, + PublicKey: for<'a> TryFrom<&'a [u8], Error = KeyPairError>, +{ + pub fn from_sign_pubkey_list( + signatures: Vec, + public_keys: Vec, + ) -> SigningResult { + if signatures.len() > 1 || public_keys.len() > 1 { + return Err(SigningError(SigningErrorType::Error_no_support_n2n)); + } + + let signature_data = signatures + .into_iter() + .next() + .ok_or(SigningError(SigningErrorType::Error_signatures_count))?; + let public_key_data = public_keys + .into_iter() + .next() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + + let signature = Signature::try_from(signature_data.as_slice())?; + let public_key = PublicKey::try_from(public_key_data.as_slice())?; + + Ok(SingleSignaturePubkey { + signature, + public_key, + }) + } +} diff --git a/rust/tw_coin_entry/src/common/mod.rs b/rust/tw_coin_entry/src/common/mod.rs new file mode 100644 index 00000000000..59492cc4690 --- /dev/null +++ b/rust/tw_coin_entry/src/common/mod.rs @@ -0,0 +1,7 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod compile_input; diff --git a/rust/tw_coin_entry/src/derivation.rs b/rust/tw_coin_entry/src/derivation.rs new file mode 100644 index 00000000000..3eb0f6c0b70 --- /dev/null +++ b/rust/tw_coin_entry/src/derivation.rs @@ -0,0 +1,22 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +/// Extend this enum. +#[repr(u32)] +pub enum Derivation { + /// Default derivation. + Default = 0, +} + +impl Derivation { + #[inline] + pub fn from_raw(derivation: u32) -> Option { + match derivation { + 0 => Some(Derivation::Default), + _ => None, + } + } +} diff --git a/rust/tw_coin_entry/src/error.rs b/rust/tw_coin_entry/src/error.rs new file mode 100644 index 00000000000..c31b495576b --- /dev/null +++ b/rust/tw_coin_entry/src/error.rs @@ -0,0 +1,114 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::fmt; +use std::fmt::Formatter; +use tw_keypair::KeyPairError; +use tw_number::NumberError; +use tw_proto::Common::Proto; +use tw_proto::ProtoError; + +#[macro_export] +macro_rules! signing_output_error { + ($output:ty, $error:expr) => {{ + let err = $error; + + let mut output = <$output>::default(); + output.error = err.0; + output.error_message = std::borrow::Cow::from(err.to_string()); + + output + }}; +} + +pub type AddressResult = Result; + +#[derive(Debug)] +pub enum AddressError { + UnknownCoinType, + MissingPrefix, + FromHexError, + PublicKeyTypeMismatch, + UnexpectedAddressPrefix, + InvalidInput, +} + +pub type SigningResult = Result; +pub type SigningErrorType = Proto::SigningError; + +/// The wrapper over the `Common::SigningErrorType` error for convenient use. +#[derive(Debug)] +pub struct SigningError(pub SigningErrorType); + +impl From for SigningError { + #[inline] + fn from(_err: NumberError) -> Self { + SigningError(SigningErrorType::Error_invalid_params) + } +} + +impl From for SigningError { + #[inline] + fn from(_err: AddressError) -> Self { + SigningError(SigningErrorType::Error_invalid_address) + } +} + +impl From for SigningError { + fn from(err: KeyPairError) -> Self { + match err { + KeyPairError::InvalidSecretKey => { + SigningError(SigningErrorType::Error_invalid_private_key) + }, + KeyPairError::InvalidPublicKey + | KeyPairError::InvalidSignature + | KeyPairError::InvalidSignMessage + | KeyPairError::SignatureVerifyError => { + SigningError(SigningErrorType::Error_invalid_params) + }, + KeyPairError::SigningError => SigningError(SigningErrorType::Error_signing), + } + } +} + +impl From for SigningError { + fn from(_e: ProtoError) -> Self { + SigningError(SigningErrorType::Error_input_parse) + } +} + +impl fmt::Display for SigningError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let str = match self.0 { + SigningErrorType::OK => "", + SigningErrorType::Error_general => "Unknown error", + SigningErrorType::Error_internal => "Internal error", + SigningErrorType::Error_low_balance => "Low balance: the sender balance is not enough to cover the send and other auxiliary amount such as fee, deposit, or minimal balance", + SigningErrorType::Error_zero_amount_requested => "Requested amount is zero, send of 0 makes no sense", + SigningErrorType::Error_missing_private_key => "One required key is missing (too few or wrong keys are provided)", + SigningErrorType::Error_invalid_private_key => "A private key provided is invalid (e.g. wrong size, usually should be 32 bytes)", + SigningErrorType::Error_invalid_address => "A provided address (e.g. destination address) is invalid", + SigningErrorType::Error_invalid_utxo => "A provided input UTXO is invalid", + SigningErrorType::Error_invalid_utxo_amount => "The amount of an input UTXO is invalid", + SigningErrorType::Error_wrong_fee => "Wrong fee is given, probably it is too low to cover minimal fee for the transaction", + SigningErrorType::Error_signing => "General signing error", + SigningErrorType::Error_tx_too_big => "Resulting transaction is too large", + SigningErrorType::Error_missing_input_utxos => "No input UTXOs provided", + SigningErrorType::Error_not_enough_utxos => "Not enough non-dust input UTXOs to cover requested amount (dust UTXOs are filtered out)", + SigningErrorType::Error_script_redeem => "Missing required redeem script", + SigningErrorType::Error_script_output => "Invalid required output script", + SigningErrorType::Error_script_witness_program => "Unrecognized witness program", + SigningErrorType::Error_invalid_memo => "Invalid memo", + SigningErrorType::Error_input_parse => "Some input field cannot be parsed", + SigningErrorType::Error_no_support_n2n => "Multi-input and multi-output transaction not supported", + SigningErrorType::Error_signatures_count => "Incorrect count of signatures passed to compile", + SigningErrorType::Error_invalid_params => "Incorrect input parameter", + SigningErrorType::Error_invalid_requested_token_amount => "Invalid input token amount", + SigningErrorType::Error_not_supported => "Operation not supported for the chain", + }; + write!(f, "{str}") + } +} diff --git a/rust/tw_coin_entry/src/lib.rs b/rust/tw_coin_entry/src/lib.rs new file mode 100644 index 00000000000..fd45ec0f96b --- /dev/null +++ b/rust/tw_coin_entry/src/lib.rs @@ -0,0 +1,17 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod coin_context; +pub mod coin_entry; +pub mod coin_entry_ext; +pub mod common; +pub mod derivation; +pub mod error; +pub mod modules; +pub mod prefix; + +#[cfg(feature = "test-utils")] +pub mod test_utils; diff --git a/rust/tw_coin_entry/src/modules/json_signer.rs b/rust/tw_coin_entry/src/modules/json_signer.rs new file mode 100644 index 00000000000..8757c5ed40d --- /dev/null +++ b/rust/tw_coin_entry/src/modules/json_signer.rs @@ -0,0 +1,33 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::coin_context::CoinContext; +use crate::error::SigningResult; +use tw_keypair::tw::PrivateKey; + +pub trait JsonSigner { + /// Signs the given JSON input with the private key. + fn sign_json( + &self, + coin: &dyn CoinContext, + input_json: &str, + key: &PrivateKey, + ) -> SigningResult; +} + +/// `NoJsonSigner` can't be created since there are no enum variants. +pub enum NoJsonSigner {} + +impl JsonSigner for NoJsonSigner { + fn sign_json( + &self, + _coin: &dyn CoinContext, + _input_json: &str, + _key: &PrivateKey, + ) -> SigningResult { + panic!("`NoJsonSigner` should never be constructed and used") + } +} diff --git a/rust/tw_coin_entry/src/modules/message_signer.rs b/rust/tw_coin_entry/src/modules/message_signer.rs new file mode 100644 index 00000000000..141c30352f1 --- /dev/null +++ b/rust/tw_coin_entry/src/modules/message_signer.rs @@ -0,0 +1,70 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::coin_context::CoinContext; +use tw_proto::{DummyMessage, MessageRead, MessageWrite, NoMessage}; + +pub trait MessageSigner { + type MessageSigningInput<'a>: MessageRead<'a>; + type MessagePreSigningOutput: MessageWrite; + type MessageSigningOutput: MessageWrite; + type MessageVerifyingInput<'a>: MessageRead<'a>; + + /// Returns hash(es) for signing, needed for external signing. + fn message_preimage_hashes( + &self, + coin: &dyn CoinContext, + input: Self::MessageSigningInput<'_>, + ) -> Self::MessagePreSigningOutput; + + /// Signs a message. + fn sign_message( + &self, + coin: &dyn CoinContext, + input: Self::MessageSigningInput<'_>, + ) -> Self::MessageSigningOutput; + + /// Verifies signature. + fn verify_message( + &self, + _coin: &dyn CoinContext, + input: Self::MessageVerifyingInput<'_>, + ) -> bool; +} + +/// `NoMessageSigner` can't be created since there are no enum variants. +pub enum NoMessageSigner {} + +impl MessageSigner for NoMessageSigner { + type MessageSigningInput<'a> = DummyMessage; + type MessagePreSigningOutput = NoMessage; + type MessageSigningOutput = NoMessage; + type MessageVerifyingInput<'a> = DummyMessage; + + fn message_preimage_hashes( + &self, + _coin: &dyn CoinContext, + _input: Self::MessageSigningInput<'_>, + ) -> Self::MessagePreSigningOutput { + panic!("`NoMessageSigner` should never be constructed and used") + } + + fn sign_message( + &self, + _coin: &dyn CoinContext, + _input: Self::MessageSigningInput<'_>, + ) -> Self::MessageSigningOutput { + panic!("`NoMessageSigner` should never be constructed and used") + } + + fn verify_message( + &self, + _coin: &dyn CoinContext, + _input: Self::MessageVerifyingInput<'_>, + ) -> bool { + panic!("`NoMessageSigner` should never be constructed and used") + } +} diff --git a/rust/tw_coin_entry/src/modules/mod.rs b/rust/tw_coin_entry/src/modules/mod.rs new file mode 100644 index 00000000000..6f8b8b891bf --- /dev/null +++ b/rust/tw_coin_entry/src/modules/mod.rs @@ -0,0 +1,11 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +//! This directory contains optional modules that some chain may or may not implement. + +pub mod json_signer; +pub mod message_signer; +pub mod plan_builder; diff --git a/rust/tw_coin_entry/src/modules/plan_builder.rs b/rust/tw_coin_entry/src/modules/plan_builder.rs new file mode 100644 index 00000000000..fd5b9f1b828 --- /dev/null +++ b/rust/tw_coin_entry/src/modules/plan_builder.rs @@ -0,0 +1,29 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::coin_context::CoinContext; +use tw_proto::{DummyMessage, MessageRead, MessageWrite, NoMessage}; + +pub trait PlanBuilder { + type SigningInput<'a>: MessageRead<'a>; + type Plan: MessageWrite; + + /// Planning, for UTXO chains, in preparation for signing. + fn plan(&self, coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::Plan; +} + +/// `NoInputBuilder` can't be created since there are no enum variants. +pub enum NoPlanBuilder {} + +impl PlanBuilder for NoPlanBuilder { + type SigningInput<'a> = DummyMessage; + type Plan = NoMessage; + + /// [`PlanBuilder::plan`] should never be called. + fn plan(&self, _coin: &dyn CoinContext, _input: Self::SigningInput<'_>) -> Self::Plan { + panic!("`NoPlanBuilder` should never be constructed and used") + } +} diff --git a/rust/tw_coin_entry/src/prefix.rs b/rust/tw_coin_entry/src/prefix.rs new file mode 100644 index 00000000000..52898e19ee5 --- /dev/null +++ b/rust/tw_coin_entry/src/prefix.rs @@ -0,0 +1,40 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::error::AddressError; + +/// Extend when adding new blockchains. +#[derive(Clone)] +pub enum AddressPrefix { + Hrp(String), +} + +pub trait Prefix: TryFrom {} + +impl Prefix for T where T: TryFrom {} + +/// There is no way to create an instance of the `NoPrefix` enum as it doesn't has variants. +#[derive(Debug)] +pub enum NoPrefix {} + +impl TryFrom for NoPrefix { + type Error = AddressError; + + #[inline] + fn try_from(_: AddressPrefix) -> Result { + Err(AddressError::UnexpectedAddressPrefix) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_no_prefix() { + NoPrefix::try_from(AddressPrefix::Hrp("".to_string())).unwrap_err(); + } +} diff --git a/rust/tw_coin_entry/src/test_utils/empty_context.rs b/rust/tw_coin_entry/src/test_utils/empty_context.rs new file mode 100644 index 00000000000..423081ba7e1 --- /dev/null +++ b/rust/tw_coin_entry/src/test_utils/empty_context.rs @@ -0,0 +1,17 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::coin_context::CoinContext; +use tw_keypair::tw::PublicKeyType; + +/// Test coin context that panics on any `CoinContext` method call. +pub struct EmptyCoinContext; + +impl CoinContext for EmptyCoinContext { + fn public_key_type(&self) -> PublicKeyType { + panic!() + } +} diff --git a/rust/tw_coin_entry/src/test_utils/mod.rs b/rust/tw_coin_entry/src/test_utils/mod.rs new file mode 100644 index 00000000000..805b39f4b3e --- /dev/null +++ b/rust/tw_coin_entry/src/test_utils/mod.rs @@ -0,0 +1,7 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod empty_context; diff --git a/rust/tw_coin_registry/Cargo.toml b/rust/tw_coin_registry/Cargo.toml new file mode 100644 index 00000000000..a66a537a37d --- /dev/null +++ b/rust/tw_coin_registry/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "tw_coin_registry" +version = "0.1.0" +edition = "2021" + +[dependencies] +lazy_static = "1.4.0" +serde = { version = "1.0.163", features = ["derive"] } +serde_json = "1.0.96" +tw_bitcoin = { path = "../tw_bitcoin" } +tw_coin_entry = { path = "../tw_coin_entry" } +tw_ethereum = { path = "../tw_ethereum" } +tw_evm = { path = "../tw_evm" } +tw_keypair = { path = "../tw_keypair" } +tw_memory = { path = "../tw_memory" } +tw_misc = { path = "../tw_misc" } +tw_ronin = { path = "../tw_ronin" } diff --git a/rust/tw_coin_registry/src/blockchain_type.rs b/rust/tw_coin_registry/src/blockchain_type.rs new file mode 100644 index 00000000000..47a19ff54ce --- /dev/null +++ b/rust/tw_coin_registry/src/blockchain_type.rs @@ -0,0 +1,43 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::error::RegistryError; +use serde::de::Error; +use serde::{Deserialize, Deserializer}; +use std::str::FromStr; + +/// Blockchain implementation type. +/// Extend this enum when adding new blockchains. +#[derive(Copy, Clone, Debug)] +pub enum BlockchainType { + Bitcoin, + Ethereum, + Ronin, + Unsupported, +} + +impl<'de> Deserialize<'de> for BlockchainType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + BlockchainType::from_str(&s).map_err(|e| Error::custom(format!("{e:?}"))) + } +} + +impl FromStr for BlockchainType { + type Err = RegistryError; + + fn from_str(s: &str) -> Result { + match s { + "Bitcoin" => Ok(BlockchainType::Bitcoin), + "Ethereum" => Ok(BlockchainType::Ethereum), + "Ronin" => Ok(BlockchainType::Ronin), + _ => Ok(BlockchainType::Unsupported), + } + } +} diff --git a/rust/tw_coin_registry/src/coin_context.rs b/rust/tw_coin_registry/src/coin_context.rs new file mode 100644 index 00000000000..41c090a7194 --- /dev/null +++ b/rust/tw_coin_registry/src/coin_context.rs @@ -0,0 +1,27 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::registry::CoinItem; +use tw_coin_entry::coin_context::CoinContext; +use tw_keypair::tw::PublicKeyType; + +pub struct CoinRegistryContext { + item: &'static CoinItem, +} + +impl CoinRegistryContext { + #[inline] + pub fn with_coin_item(item: &'static CoinItem) -> CoinRegistryContext { + CoinRegistryContext { item } + } +} + +impl CoinContext for CoinRegistryContext { + #[inline] + fn public_key_type(&self) -> PublicKeyType { + self.item.public_key_type + } +} diff --git a/rust/tw_coin_registry/src/coin_type.rs b/rust/tw_coin_registry/src/coin_type.rs new file mode 100644 index 00000000000..d03c00f87ca --- /dev/null +++ b/rust/tw_coin_registry/src/coin_type.rs @@ -0,0 +1,8 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +/// TODO make this enum generated from `registry.json`. +pub type CoinType = u32; diff --git a/rust/tw_coin_registry/src/dispatcher.rs b/rust/tw_coin_registry/src/dispatcher.rs new file mode 100644 index 00000000000..9c47e279a02 --- /dev/null +++ b/rust/tw_coin_registry/src/dispatcher.rs @@ -0,0 +1,51 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::blockchain_type::BlockchainType; +use crate::coin_context::CoinRegistryContext; +use crate::coin_type::CoinType; +use crate::error::{RegistryError, RegistryResult}; +use crate::registry::get_coin_item; +use tw_bitcoin::entry::BitcoinEntry; +use tw_coin_entry::coin_entry_ext::CoinEntryExt; +use tw_ethereum::entry::EthereumEntry; +use tw_evm::evm_entry::EvmEntryExt; +use tw_ronin::entry::RoninEntry; + +pub type CoinEntryExtStaticRef = &'static dyn CoinEntryExt; +pub type EvmEntryExtStaticRef = &'static dyn EvmEntryExt; + +const BITCOIN: BitcoinEntry = BitcoinEntry; +const ETHEREUM: EthereumEntry = EthereumEntry; +const RONIN: RoninEntry = RoninEntry; + +pub fn blockchain_dispatcher(blockchain: BlockchainType) -> RegistryResult { + match blockchain { + BlockchainType::Bitcoin => Ok(&BITCOIN), + BlockchainType::Ethereum => Ok(ÐEREUM), + BlockchainType::Ronin => Ok(&RONIN), + BlockchainType::Unsupported => Err(RegistryError::Unsupported), + } +} + +pub fn coin_dispatcher( + coin: CoinType, +) -> RegistryResult<(CoinRegistryContext, CoinEntryExtStaticRef)> { + let item = get_coin_item(coin)?; + let coin_entry = blockchain_dispatcher(item.blockchain)?; + let coin_context = CoinRegistryContext::with_coin_item(item); + Ok((coin_context, coin_entry)) +} + +pub fn evm_dispatcher(coin: CoinType) -> RegistryResult { + let item = get_coin_item(coin)?; + match item.blockchain { + BlockchainType::Bitcoin => Err(RegistryError::Unsupported), + BlockchainType::Ethereum => Ok(ÐEREUM), + BlockchainType::Ronin => Ok(&RONIN), + BlockchainType::Unsupported => Err(RegistryError::Unsupported), + } +} diff --git a/rust/tw_coin_registry/src/error.rs b/rust/tw_coin_registry/src/error.rs new file mode 100644 index 00000000000..52857b83564 --- /dev/null +++ b/rust/tw_coin_registry/src/error.rs @@ -0,0 +1,25 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::error::{SigningError, SigningErrorType}; + +pub type RegistryResult = Result; + +#[derive(Debug)] +pub enum RegistryError { + UnknownCoinType, + Unsupported, +} + +impl From for SigningError { + #[inline] + fn from(e: RegistryError) -> Self { + match e { + RegistryError::UnknownCoinType => SigningError(SigningErrorType::Error_invalid_params), + RegistryError::Unsupported => SigningError(SigningErrorType::Error_internal), + } + } +} diff --git a/rust/tw_coin_registry/src/lib.rs b/rust/tw_coin_registry/src/lib.rs new file mode 100644 index 00000000000..cbad9faaa44 --- /dev/null +++ b/rust/tw_coin_registry/src/lib.rs @@ -0,0 +1,12 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod blockchain_type; +pub mod coin_context; +pub mod coin_type; +pub mod dispatcher; +pub mod error; +pub mod registry; diff --git a/rust/tw_coin_registry/src/registry.rs b/rust/tw_coin_registry/src/registry.rs new file mode 100644 index 00000000000..422dd38ae6f --- /dev/null +++ b/rust/tw_coin_registry/src/registry.rs @@ -0,0 +1,53 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::blockchain_type::BlockchainType; +use crate::coin_type::CoinType; +use crate::error::{RegistryError, RegistryResult}; +use lazy_static::lazy_static; +use serde::Deserialize; +use std::collections::HashMap; +use tw_keypair::tw::PublicKeyType; + +type RegistryMap = HashMap; + +/// cbindgen:ignore +const REGISTRY_JSON: &str = + include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../registry.json")); + +lazy_static! { + static ref REGISTRY: RegistryMap = parse_registry_json(); +} + +/// Extend this structure according to `registry.json`. +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CoinItem { + pub coin_id: CoinType, + pub blockchain: BlockchainType, + pub public_key_type: PublicKeyType, +} + +#[inline] +pub fn get_coin_item(coin: CoinType) -> RegistryResult<&'static CoinItem> { + REGISTRY.get(&coin).ok_or(RegistryError::UnknownCoinType) +} + +#[inline] +pub fn registry_iter() -> impl Iterator { + REGISTRY.iter().map(|(_coin_type, item)| item) +} + +#[inline] +pub fn supported_coin_items() -> impl Iterator { + registry_iter().filter(|item| !matches!(item.blockchain, BlockchainType::Unsupported)) +} + +fn parse_registry_json() -> RegistryMap { + let items: Vec = + serde_json::from_str(REGISTRY_JSON).expect("registry.json expected to be valid"); + items.into_iter().map(|item| (item.coin_id, item)).collect() +} diff --git a/rust/tw_encoding/Cargo.toml b/rust/tw_encoding/Cargo.toml index 43798b3ae89..2d32de903f4 100644 --- a/rust/tw_encoding/Cargo.toml +++ b/rust/tw_encoding/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] +arbitrary = { version = "1", features = ["derive"], optional = true } bs58 = "0.4.0" data-encoding = "2.3.3" hex = "0.4.3" diff --git a/rust/tw_encoding/fuzz/.gitignore b/rust/tw_encoding/fuzz/.gitignore new file mode 100644 index 00000000000..5c404b9583f --- /dev/null +++ b/rust/tw_encoding/fuzz/.gitignore @@ -0,0 +1,5 @@ +target +corpus +artifacts +coverage +Cargo.lock diff --git a/rust/tw_encoding/fuzz/Cargo.toml b/rust/tw_encoding/fuzz/Cargo.toml new file mode 100644 index 00000000000..eaf660bc073 --- /dev/null +++ b/rust/tw_encoding/fuzz/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "tw_encoding-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = { version = "0.4", features = ["arbitrary-derive"] } + +[dependencies.tw_encoding] +path = ".." +features = ["arbitrary"] + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[profile.release] +debug = 1 + +[[bin]] +name = "base_encode" +path = "fuzz_targets/base_encode.rs" +test = false +doc = false + +[[bin]] +name = "base_decode" +path = "fuzz_targets/base_decode.rs" +test = false +doc = false diff --git a/rust/tw_encoding/fuzz/fuzz_targets/base_decode.rs b/rust/tw_encoding/fuzz/fuzz_targets/base_decode.rs new file mode 100644 index 00000000000..cb98cd89186 --- /dev/null +++ b/rust/tw_encoding/fuzz/fuzz_targets/base_decode.rs @@ -0,0 +1,18 @@ +#![no_main] + +use libfuzzer_sys::{arbitrary, fuzz_target}; +use tw_encoding::{base32, base58, base64, ffi::Base58Alphabet}; + +#[derive(arbitrary::Arbitrary, Debug)] +struct BaseDecodeInput<'a> { + data: &'a str, + alphabet_base58: Base58Alphabet, + padding: bool, + is_url: bool, +} + +fuzz_target!(|input: BaseDecodeInput<'_>| { + base32::decode(input.data, None, input.padding).ok(); + base58::decode(input.data, input.alphabet_base58.into()).ok(); + base64::decode(input.data, input.is_url).ok(); +}); diff --git a/rust/tw_encoding/fuzz/fuzz_targets/base_encode.rs b/rust/tw_encoding/fuzz/fuzz_targets/base_encode.rs new file mode 100644 index 00000000000..aba1f7d1356 --- /dev/null +++ b/rust/tw_encoding/fuzz/fuzz_targets/base_encode.rs @@ -0,0 +1,18 @@ +#![no_main] + +use libfuzzer_sys::{arbitrary, fuzz_target}; +use tw_encoding::{base32, base58, base64, ffi::Base58Alphabet}; + +#[derive(arbitrary::Arbitrary, Debug)] +struct BaseEncodeInput<'a> { + data: &'a [u8], + alphabet_base58: Base58Alphabet, + padding: bool, + is_url: bool, +} + +fuzz_target!(|input: BaseEncodeInput<'_>| { + base32::encode(input.data, None, input.padding).ok(); + base58::encode(input.data, input.alphabet_base58.into()); + base64::encode(input.data, input.is_url); +}); diff --git a/rust/tw_encoding/src/base32.rs b/rust/tw_encoding/src/base32.rs index 9831437144e..d43c10acaf5 100644 --- a/rust/tw_encoding/src/base32.rs +++ b/rust/tw_encoding/src/base32.rs @@ -10,6 +10,7 @@ use std::cell::RefCell; use std::collections::hash_map::Entry; use std::collections::HashMap; +/// cbindgen:ignore const ALPHABET_RFC4648: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; type EncodingMap = HashMap; diff --git a/rust/tw_encoding/src/base58.rs b/rust/tw_encoding/src/base58.rs index 7e047b51ed7..2f0a7cb8694 100644 --- a/rust/tw_encoding/src/base58.rs +++ b/rust/tw_encoding/src/base58.rs @@ -5,7 +5,8 @@ // file LICENSE at the root of the source code distribution tree. use crate::{EncodingError, EncodingResult}; -use bs58::{decode::Error, Alphabet}; +use bs58::decode::Error; +pub use bs58::Alphabet; impl From for EncodingError { fn from(_: Error) -> Self { diff --git a/rust/tw_encoding/src/ffi.rs b/rust/tw_encoding/src/ffi.rs index 1acde6ebcc7..83f830793bd 100644 --- a/rust/tw_encoding/src/ffi.rs +++ b/rust/tw_encoding/src/ffi.rs @@ -41,7 +41,8 @@ impl From for ErrorCode { } #[repr(C)] -#[derive(PartialEq, Debug)] +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] pub enum Base58Alphabet { Bitcoin = 1, Ripple = 2, @@ -104,7 +105,7 @@ pub unsafe extern "C" fn decode_base32( }; base32::decode(input, alphabet, padding) - .map(CByteArray::new) + .map(CByteArray::from) .map_err(CEncodingCode::from) .into() } @@ -141,7 +142,7 @@ pub unsafe extern "C" fn decode_base58( }; base58::decode(input, alphabet.into()) - .map(CByteArray::new) + .map(CByteArray::from) .map_err(CEncodingCode::from) .into() } @@ -172,7 +173,7 @@ pub unsafe extern "C" fn decode_base64(data: *const c_char, is_url: bool) -> CBy Err(_) => return CByteArrayResult::error(CEncodingCode::InvalidInput), }; base64::decode(str_slice, is_url) - .map(CByteArray::new) + .map(CByteArray::from) .map_err(CEncodingCode::from) .into() } @@ -191,7 +192,7 @@ pub unsafe extern "C" fn decode_hex(data: *const c_char) -> CByteArrayResult { }; hex::decode(hex_string) - .map(CByteArray::new) + .map(CByteArray::from) .map_err(CEncodingCode::from) .into() } diff --git a/rust/tw_encoding/src/hex.rs b/rust/tw_encoding/src/hex.rs index ec0b1d60eaf..d7cb5a5da95 100644 --- a/rust/tw_encoding/src/hex.rs +++ b/rust/tw_encoding/src/hex.rs @@ -6,13 +6,42 @@ pub use hex::FromHexError; +pub trait ToHex { + fn to_hex(&self) -> String; + + fn to_hex_prefixed(&self) -> String; +} + +impl ToHex for T +where + T: AsRef<[u8]>, +{ + fn to_hex(&self) -> String { + encode(self, false) + } + + fn to_hex_prefixed(&self) -> String { + encode(self, true) + } +} + +pub trait DecodeHex { + fn decode_hex(&self) -> Result, FromHexError>; +} + +impl<'a> DecodeHex for &'a str { + fn decode_hex(&self) -> Result, FromHexError> { + decode(self) + } +} + pub fn decode(data: &str) -> Result, FromHexError> { let hex_string = data.trim_start_matches("0x"); hex::decode(hex_string) } -pub fn encode(data: &[u8], prefixed: bool) -> String { - let encoded = hex::encode(data); +pub fn encode>(data: T, prefixed: bool) -> String { + let encoded = hex::encode(data.as_ref()); if prefixed { return format!("0x{encoded}"); } diff --git a/rust/tw_ethereum/Cargo.toml b/rust/tw_ethereum/Cargo.toml new file mode 100644 index 00000000000..2b8f6554a62 --- /dev/null +++ b/rust/tw_ethereum/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "tw_ethereum" +version = "0.1.0" +edition = "2021" + +[dependencies] +tw_coin_entry = { path = "../tw_coin_entry" } +tw_evm = { path = "../tw_evm" } +tw_keypair = { path = "../tw_keypair" } +tw_proto = { path = "../tw_proto" } + +[dev-dependencies] +tw_coin_entry = { path = "../tw_coin_entry", features = ["test-utils"] } +tw_encoding = { path = "../tw_encoding" } +tw_number = { path = "../tw_number", features = ["helpers"] } diff --git a/rust/tw_ethereum/src/entry.rs b/rust/tw_ethereum/src/entry.rs new file mode 100644 index 00000000000..cb9a1b4fb48 --- /dev/null +++ b/rust/tw_ethereum/src/entry.rs @@ -0,0 +1,100 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::derivation::Derivation; +use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::prefix::NoPrefix; +use tw_evm::address::Address; +use tw_evm::evm_context::StandardEvmContext; +use tw_evm::evm_entry::EvmEntry; +use tw_evm::modules::compiler::Compiler; +use tw_evm::modules::json_signer::EthJsonSigner; +use tw_evm::modules::message_signer::EthMessageSigner; +use tw_evm::modules::signer::Signer; +use tw_keypair::tw::PublicKey; +use tw_proto::Ethereum::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct EthereumEntry; + +impl CoinEntry for EthereumEntry { + type AddressPrefix = NoPrefix; + type Address = Address; + type SigningInput<'a> = Proto::SigningInput<'a>; + type SigningOutput = Proto::SigningOutput<'static>; + type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + + // Optional modules: + type JsonSigner = EthJsonSigner; + type PlanBuilder = NoPlanBuilder; + type MessageSigner = EthMessageSigner; + + #[inline] + fn parse_address( + &self, + _coin: &dyn CoinContext, + address: &str, + _prefix: Option, + ) -> AddressResult { + Address::from_str(address) + } + + fn derive_address( + &self, + _coin: &dyn CoinContext, + public_key: PublicKey, + _derivation: Derivation, + _prefix: Option, + ) -> AddressResult { + let public_key = public_key + .to_secp256k1() + .ok_or(AddressError::PublicKeyTypeMismatch)?; + Ok(Address::with_secp256k1_pubkey(public_key)) + } + + #[inline] + fn sign(&self, _coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput { + Signer::::sign_proto(input) + } + + #[inline] + fn preimage_hashes( + &self, + _coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + ) -> Self::PreSigningOutput { + Compiler::::preimage_hashes(input) + } + + #[inline] + fn compile( + &self, + _coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Self::SigningOutput { + Compiler::::compile(input, signatures, public_keys) + } + + #[inline] + fn json_signer(&self) -> Option { + Some(EthJsonSigner::default()) + } + + #[inline] + fn message_signer(&self) -> Option { + Some(EthMessageSigner) + } +} + +impl EvmEntry for EthereumEntry { + type Context = StandardEvmContext; +} diff --git a/rust/tw_starknet/src/lib.rs b/rust/tw_ethereum/src/lib.rs similarity index 89% rename from rust/tw_starknet/src/lib.rs rename to rust/tw_ethereum/src/lib.rs index fc5cac0c0ae..1afc00798db 100644 --- a/rust/tw_starknet/src/lib.rs +++ b/rust/tw_ethereum/src/lib.rs @@ -4,5 +4,4 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -pub mod ffi; -pub mod key_pair; +pub mod entry; diff --git a/rust/tw_ethereum/tests/compiler.rs b/rust/tw_ethereum/tests/compiler.rs new file mode 100644 index 00000000000..48b0df2b256 --- /dev/null +++ b/rust/tw_ethereum/tests/compiler.rs @@ -0,0 +1,96 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::borrow::Cow; +use tw_coin_entry::coin_entry_ext::CoinEntryExt; +use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_encoding::hex; +use tw_ethereum::entry::EthereumEntry; +use tw_keypair::ecdsa::secp256k1; +use tw_keypair::tw; +use tw_number::U256; +use tw_proto::Ethereum::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; +use tw_proto::{deserialize, serialize}; + +#[test] +fn test_external_signature_sign() { + let transfer = Proto::mod_Transaction::Transfer { + amount: U256::encode_be_compact(1_000_000_000_000_000_000), + data: Cow::default(), + }; + let input = Proto::SigningInput { + nonce: U256::encode_be_compact(11), + chain_id: U256::encode_be_compact(1), + gas_price: U256::encode_be_compact(20_000_000_000), + gas_limit: U256::encode_be_compact(21_000), + to_address: "0x3535353535353535353535353535353535353535".into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::transfer(transfer), + }), + ..Proto::SigningInput::default() + }; + + // Step 1: Obtain preimage hash + let input_data = serialize(&input).unwrap(); + let preimage_data = EthereumEntry + .preimage_hashes(&EmptyCoinContext, &input_data) + .expect("!preimage_hashes"); + let preimage: CompilerProto::PreSigningOutput = + deserialize(&preimage_data).expect("Coin entry returned an invalid output"); + + assert_eq!(preimage.error, SigningErrorType::OK); + assert!(preimage.error_message.is_empty()); + assert_eq!( + hex::encode(&preimage.data_hash, false), + "15e180a6274b2f6a572b9b51823fce25ef39576d10188ecdcd7de44526c47217" + ); + + // Simulate signature, normally obtained from signature server + let public_key = secp256k1::PublicKey::try_from("044bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382ce28cab79ad7119ee1ad3ebcdb98a16805211530ecc6cfefa1b88e6dff99232a").unwrap(); + let public_key = tw::PublicKey::Secp256k1Extended(public_key); + let signature = hex::decode("360a84fb41ad07f07c845fedc34cde728421803ebbaae392fc39c116b29fc07b53bd9d1376e15a191d844db458893b928f3efbfee90c9febf51ab84c9796677900").unwrap(); + + // Verify signature (pubkey & hash & signature) + assert!(public_key.verify(&signature, &preimage.data_hash)); + + // Step 2: Compile transaction info + let input_data = serialize(&input).unwrap(); + let output_data = EthereumEntry + .compile( + &EmptyCoinContext, + &input_data, + vec![signature], + vec![public_key.to_bytes()], + ) + .expect("!compile"); + let output: Proto::SigningOutput = + deserialize(&output_data).expect("Coin entry returned an invalid output"); + + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + let expected_encoded = "f86c0b8504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a0360a84fb41ad07f07c845fedc34cde728421803ebbaae392fc39c116b29fc07ba053bd9d1376e15a191d844db458893b928f3efbfee90c9febf51ab84c97966779"; + assert_eq!(hex::encode(output.encoded, false), expected_encoded); + + // Double check: check if simple signature process gives the same result. Note that private + // keys were not used anywhere up to this point. + let input = Proto::SigningInput { + private_key: Cow::from( + hex::decode("4646464646464646464646464646464646464646464646464646464646464646") + .unwrap(), + ), + ..input + }; + + let input_data = serialize(&input).unwrap(); + let output_data = EthereumEntry + .sign(&EmptyCoinContext, &input_data) + .expect("!output_data"); + let output: Proto::SigningOutput = + deserialize(&output_data).expect("Coin entry returned an invalid output"); + assert_eq!(hex::encode(output.encoded, false), expected_encoded); +} diff --git a/rust/tw_ethereum/tests/signer.rs b/rust/tw_ethereum/tests/signer.rs new file mode 100644 index 00000000000..d880744d540 --- /dev/null +++ b/rust/tw_ethereum/tests/signer.rs @@ -0,0 +1,24 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::coin_entry_ext::CoinEntryExt; +use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_encoding::hex::DecodeHex; +use tw_ethereum::entry::EthereumEntry; + +#[test] +fn test_sign_json() { + let input_json = r#"{"chainId":"AQ==","gasPrice":"1pOkAA==","gasLimit":"Ugg=","toAddress":"0x7d8bf18C7cE84b3E175b339c4Ca93aEd1dD166F1","transaction":{"transfer":{"amount":"A0i8paFgAA=="}}})"#; + let private_key = "17209af590a86462395d5881e60d11c7fa7d482cfb02b5a01b93c2eeef243543" + .decode_hex() + .unwrap(); + + EthereumEntry + .sign_json(&EmptyCoinContext, input_json, private_key) + .expect_err("'EthEntry::sign_json' is not supported yet"); + + // Expected result - "f86a8084d693a400825208947d8bf18c7ce84b3e175b339c4ca93aed1dd166f1870348bca5a160008025a0fe5802b49e04c6b1705088310e133605ed8b549811a18968ad409ea02ad79f21a05bf845646fb1e1b9365f63a7fd5eb5e984094e3ed35c3bed7361aebbcbf41f10" +} diff --git a/rust/tw_evm/Cargo.toml b/rust/tw_evm/Cargo.toml new file mode 100644 index 00000000000..43c3f334c72 --- /dev/null +++ b/rust/tw_evm/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "tw_evm" +version = "0.1.0" +edition = "2021" + +[dependencies] +itertools = "0.10.5" +lazy_static = "1.4.0" +rlp = "0.5.2" +serde = { version = "1.0.159", features = ["derive"] } +serde_json = "1.0.95" +tw_coin_entry = { path = "../tw_coin_entry" } +tw_encoding = { path = "../tw_encoding" } +tw_hash = { path = "../tw_hash" } +tw_keypair = { path = "../tw_keypair" } +tw_memory = { path = "../tw_memory" } +tw_misc = { path = "../tw_misc" } +tw_number = { path = "../tw_number" } +tw_proto = { path = "../tw_proto" } + +[dev-dependencies] +tw_coin_entry = { path = "../tw_coin_entry", features = ["test-utils"] } diff --git a/rust/tw_evm/fuzz/.gitignore b/rust/tw_evm/fuzz/.gitignore new file mode 100644 index 00000000000..5c404b9583f --- /dev/null +++ b/rust/tw_evm/fuzz/.gitignore @@ -0,0 +1,5 @@ +target +corpus +artifacts +coverage +Cargo.lock diff --git a/rust/tw_evm/fuzz/Cargo.toml b/rust/tw_evm/fuzz/Cargo.toml new file mode 100644 index 00000000000..6fad417a7d8 --- /dev/null +++ b/rust/tw_evm/fuzz/Cargo.toml @@ -0,0 +1,54 @@ +[package] +name = "tw_evm-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = { version = "0.4", features = ["arbitrary-derive"] } +serde_json = "1.0.95" +tw_number = { path = "../../tw_number" } +tw_proto = { path = "../../tw_proto", features = ["fuzz"] } + +[dependencies.tw_evm] +path = ".." + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[profile.release] +debug = 1 + +[[bin]] +name = "abi_encode_function" +path = "fuzz_targets/abi_encode_function.rs" +test = false +doc = false + +[[bin]] +name = "abi_decode_value" +path = "fuzz_targets/abi_decode_value.rs" +test = false +doc = false + +[[bin]] +name = "abi_function_decode_input" +path = "fuzz_targets/abi_function_decode_input.rs" +test = false +doc = false + +[[bin]] +name = "rlp_encode" +path = "fuzz_targets/rlp_encode.rs" +test = false +doc = false + +[[bin]] +name = "sign" +path = "fuzz_targets/sign.rs" +test = false +doc = false diff --git a/rust/tw_evm/fuzz/fuzz_targets/abi_decode_value.rs b/rust/tw_evm/fuzz/fuzz_targets/abi_decode_value.rs new file mode 100644 index 00000000000..4da7cf941e9 --- /dev/null +++ b/rust/tw_evm/fuzz/fuzz_targets/abi_decode_value.rs @@ -0,0 +1,16 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#![no_main] + +use libfuzzer_sys::fuzz_target; +use tw_evm::evm_context::StandardEvmContext; +use tw_evm::modules::abi_encoder::AbiEncoder; +use tw_proto::EthereumAbi::Proto; + +fuzz_target!(|input: Proto::ValueDecodingInput<'_>| { + let _ = AbiEncoder::::decode_value(input); +}); diff --git a/rust/tw_evm/fuzz/fuzz_targets/abi_encode_function.rs b/rust/tw_evm/fuzz/fuzz_targets/abi_encode_function.rs new file mode 100644 index 00000000000..a8652534169 --- /dev/null +++ b/rust/tw_evm/fuzz/fuzz_targets/abi_encode_function.rs @@ -0,0 +1,16 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#![no_main] + +use libfuzzer_sys::fuzz_target; +use tw_evm::evm_context::StandardEvmContext; +use tw_evm::modules::abi_encoder::AbiEncoder; +use tw_proto::EthereumAbi::Proto; + +fuzz_target!(|input: Proto::FunctionEncodingInput<'_>| { + let _ = AbiEncoder::::encode_contract_call(input); +}); diff --git a/rust/tw_evm/fuzz/fuzz_targets/abi_function_decode_input.rs b/rust/tw_evm/fuzz/fuzz_targets/abi_function_decode_input.rs new file mode 100644 index 00000000000..cfba6120348 --- /dev/null +++ b/rust/tw_evm/fuzz/fuzz_targets/abi_function_decode_input.rs @@ -0,0 +1,16 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#![no_main] + +use libfuzzer_sys::fuzz_target; +use tw_evm::evm_context::StandardEvmContext; +use tw_evm::modules::abi_encoder::AbiEncoder; +use tw_proto::EthereumAbi::Proto; + +fuzz_target!(|input: Proto::ParamsDecodingInput<'_>| { + let _ = AbiEncoder::::decode_params(input); +}); diff --git a/rust/tw_evm/fuzz/fuzz_targets/rlp_encode.rs b/rust/tw_evm/fuzz/fuzz_targets/rlp_encode.rs new file mode 100644 index 00000000000..2a686c6c553 --- /dev/null +++ b/rust/tw_evm/fuzz/fuzz_targets/rlp_encode.rs @@ -0,0 +1,16 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#![no_main] + +use libfuzzer_sys::fuzz_target; +use tw_evm::evm_context::StandardEvmContext; +use tw_evm::modules::rlp_encoder::RlpEncoder; +use tw_proto::EthereumRlp::Proto; + +fuzz_target!(|input: Proto::EncodingInput<'_>| { + let _ = RlpEncoder::::encode_with_proto(input); +}); diff --git a/rust/tw_evm/fuzz/fuzz_targets/sign.rs b/rust/tw_evm/fuzz/fuzz_targets/sign.rs new file mode 100644 index 00000000000..dfa541a2d4c --- /dev/null +++ b/rust/tw_evm/fuzz/fuzz_targets/sign.rs @@ -0,0 +1,16 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#![no_main] + +use libfuzzer_sys::fuzz_target; +use tw_evm::evm_context::StandardEvmContext; +use tw_evm::modules::signer::Signer; +use tw_proto::Ethereum::Proto; + +fuzz_target!(|input: Proto::SigningInput<'_>| { + let _ = Signer::::sign_proto(input); +}); diff --git a/rust/tw_evm/src/abi/contract.rs b/rust/tw_evm/src/abi/contract.rs new file mode 100644 index 00000000000..117de4c3a87 --- /dev/null +++ b/rust/tw_evm/src/abi/contract.rs @@ -0,0 +1,62 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::function::Function; +use crate::abi::{AbiError, AbiErrorKind, AbiResult}; +use serde::{Deserialize, Deserializer}; +use std::collections::BTreeMap; + +/// API building calls to contracts ABI. +/// Consider adding missing field such as `errors`, `events` etc. +#[derive(Clone, Debug, Default)] +pub struct Contract { + pub functions: BTreeMap>, +} + +impl Contract { + /// Get the function named `name`, the first if there are overloaded versions of the same function. + pub fn function(&self, name: &str) -> AbiResult<&Function> { + self.functions + .get(name) + .into_iter() + .flatten() + .next() + .ok_or(AbiError(AbiErrorKind::Error_abi_mismatch)) + } +} + +impl<'de> Deserialize<'de> for Contract { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + /// Consider adding missing field such as `errors`, `events` etc. + #[derive(Deserialize)] + #[serde(tag = "type", rename_all = "snake_case")] + enum Operation { + Function(Function), + #[serde(other)] + Unsupported, + } + + let operations: Vec = Vec::deserialize(deserializer)?; + + let mut result = Contract { + functions: BTreeMap::default(), + }; + for operation in operations { + match operation { + Operation::Function(fun) => result + .functions + .entry(fun.name.clone()) + .or_default() + .push(fun), + Operation::Unsupported => (), + } + } + Ok(result) + } +} diff --git a/rust/tw_evm/src/abi/decode.rs b/rust/tw_evm/src/abi/decode.rs new file mode 100644 index 00000000000..76de9399a89 --- /dev/null +++ b/rust/tw_evm/src/abi/decode.rs @@ -0,0 +1,873 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::non_empty_array::{NonEmptyArray, NonEmptyBytes}; +use crate::abi::param::Param; +use crate::abi::param_token::NamedToken; +use crate::abi::param_type::ParamType; +use crate::abi::token::Token; +use crate::abi::{AbiError, AbiErrorKind, AbiResult}; +use crate::address::Address; +use lazy_static::lazy_static; +use tw_hash::{H160, H256}; +use tw_number::{I256, U256}; + +const WORD_LEN: usize = 32; + +lazy_static! { + // 0x0000000000000000000000000000000000000000000000000000000000000020 + static ref DEFAULT_DYNAMIC_OFFSET: [u8; U256::BYTES] = U256::from(WORD_LEN).to_big_endian().take(); +} + +pub fn decode_params(params: &[Param], data: &[u8]) -> AbiResult> { + let param_types: Vec<_> = params.iter().map(|param| param.kind.clone()).collect(); + let decoded_tokens = decode_params_impl(¶m_types, data) + .map_err(|_| AbiError(AbiErrorKind::Error_decoding_data))?; + + let named_tokens: Vec<_> = params + .iter() + .zip(decoded_tokens.into_iter()) + .map(|(param, value)| NamedToken::with_param_and_token(param, value)) + .collect(); + Ok(named_tokens) +} + +pub fn decode_value(param_type: &ParamType, data: &[u8]) -> AbiResult { + let offset = 0; + + let decoded = if param_type.is_dynamic() { + // If the token is dynamic, we need to append a dynamic offset that points to the `data`. + let mut encoded = Vec::with_capacity(DEFAULT_DYNAMIC_OFFSET.len() + data.len()); + encoded.extend_from_slice(&DEFAULT_DYNAMIC_OFFSET[..]); + encoded.extend_from_slice(data); + + decode_param(param_type, &encoded, offset)? + } else { + decode_param(param_type, data, offset)? + }; + + Ok(decoded.token) +} + +#[derive(Debug)] +struct DecodeResult { + token: Token, + new_offset: usize, +} + +/// Decodes ABI compliant vector of bytes into vector of tokens described by types param. +/// Returns ok, even if some data left to decode +fn decode_params_impl(types: &[ParamType], data: &[u8]) -> AbiResult> { + decode_offset(types, data).map(|(tokens, _)| tokens) +} + +fn decode_offset(types: &[ParamType], data: &[u8]) -> AbiResult<(Vec, usize)> { + // We don't support empty `FixedBytes` or `FixedArray` collections. + if data.is_empty() { + return Err(AbiError(AbiErrorKind::Error_decoding_data)); + } + + let mut tokens = vec![]; + let mut offset = 0; + + for param in types { + let res = decode_param(param, data, offset)?; + offset = res.new_offset; + tokens.push(res.token); + } + + Ok((tokens, offset)) +} + +fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult { + match param { + ParamType::Address => { + let slice = peek_32_bytes(data, offset)?; + let mut address = H160::default(); + address.copy_from_slice(&slice[12..]); + let result = DecodeResult { + token: Token::Address(Address::from_bytes(address)), + new_offset: offset + WORD_LEN, + }; + Ok(result) + }, + ParamType::Int { bits } => { + let slice = peek_32_bytes(data, offset)?; + let result = DecodeResult { + token: Token::Int { + int: I256::from_big_endian(slice), + bits: *bits, + }, + new_offset: offset + WORD_LEN, + }; + Ok(result) + }, + ParamType::Uint { bits } => { + let slice = peek_32_bytes(data, offset)?; + let result = DecodeResult { + token: Token::Uint { + uint: U256::from_big_endian(slice), + bits: *bits, + }, + new_offset: offset + WORD_LEN, + }; + Ok(result) + }, + ParamType::Bool => { + let b = as_bool(&peek_32_bytes(data, offset)?)?; + let result = DecodeResult { + token: Token::Bool(b), + new_offset: offset + WORD_LEN, + }; + Ok(result) + }, + ParamType::FixedBytes { len } => { + // FixedBytes is anything from bytes1 to bytes32. These values + // are padded with trailing zeros to fill 32 bytes. + let bytes = take_bytes(data, offset, len.get())?; + let checked_bytes = NonEmptyBytes::new(bytes)?; + let result = DecodeResult { + token: Token::FixedBytes(checked_bytes), + new_offset: offset + WORD_LEN, + }; + Ok(result) + }, + ParamType::Bytes => { + let dynamic_offset = as_usize(&peek_32_bytes(data, offset)?)?; + let bytes_offset = add_checked(dynamic_offset, WORD_LEN)?; + + let len = as_usize(&peek_32_bytes(data, dynamic_offset)?)?; + let bytes = take_bytes(data, bytes_offset, len)?; + let result = DecodeResult { + token: Token::Bytes(bytes), + new_offset: offset + WORD_LEN, + }; + Ok(result) + }, + ParamType::String => { + let dynamic_offset = as_usize(&peek_32_bytes(data, offset)?)?; + let bytes_offset = add_checked(dynamic_offset, WORD_LEN)?; + + let len = as_usize(&peek_32_bytes(data, dynamic_offset)?)?; + let bytes = take_bytes(data, bytes_offset, len)?; + let result = DecodeResult { + // NOTE: We're decoding strings using lossy UTF-8 decoding to + // prevent invalid strings written into contracts by either users or + // Solidity bugs from causing graph-node to fail decoding event + // data. + token: Token::String(String::from_utf8_lossy(&bytes).into()), + new_offset: offset + WORD_LEN, + }; + Ok(result) + }, + ParamType::Array { kind } => { + let len_offset = as_usize(&peek_32_bytes(data, offset)?)?; + let len = as_usize(&peek_32_bytes(data, len_offset)?)?; + + let tail_offset = add_checked(len_offset, WORD_LEN)?; + let tail = &data[tail_offset..]; + + let mut tokens = vec![]; + let mut new_offset = 0; + + for _ in 0..len { + let res = decode_param(kind, tail, new_offset)?; + new_offset = res.new_offset; + tokens.push(res.token); + } + + let result = DecodeResult { + token: Token::Array { + kind: kind.as_ref().clone(), + arr: tokens, + }, + new_offset: offset + WORD_LEN, + }; + + Ok(result) + }, + ParamType::FixedArray { kind, len } => { + let is_dynamic = param.is_dynamic(); + + let (tail, mut new_offset) = if is_dynamic { + let offset = as_usize(&peek_32_bytes(data, offset)?)?; + if offset > data.len() { + return Err(AbiError(AbiErrorKind::Error_decoding_data)); + } + (&data[offset..], 0) + } else { + (data, offset) + }; + + let mut tokens = vec![]; + + for _ in 0..len.get() { + let res = decode_param(kind, tail, new_offset)?; + new_offset = res.new_offset; + tokens.push(res.token); + } + + let checked_tokens = NonEmptyArray::new(tokens)?; + let result = DecodeResult { + token: Token::FixedArray { + arr: checked_tokens, + kind: kind.as_ref().clone(), + }, + new_offset: if is_dynamic { + offset + WORD_LEN + } else { + new_offset + }, + }; + + Ok(result) + }, + ParamType::Tuple { params } => { + let is_dynamic = param.is_dynamic(); + + // The first element in a dynamic Tuple is an offset to the Tuple's data + // For a static Tuple the data begins right away + let (tail, mut new_offset) = if is_dynamic { + let offset = as_usize(&peek_32_bytes(data, offset)?)?; + if offset > data.len() { + return Err(AbiError(AbiErrorKind::Error_decoding_data)); + } + (&data[offset..], 0) + } else { + (data, offset) + }; + + let mut named_tokens = Vec::with_capacity(params.len()); + for param in params.iter() { + let res = decode_param(¶m.kind, tail, new_offset)?; + new_offset = res.new_offset; + named_tokens.push(NamedToken { + name: param.name.clone(), + value: res.token, + internal_type: param.internal_type.clone(), + }); + } + + // The returned new_offset depends on whether the Tuple is dynamic + // dynamic Tuple -> follows the prefixed Tuple data offset element + // static Tuple -> follows the last data element + let result = DecodeResult { + token: Token::Tuple { + params: named_tokens, + }, + new_offset: if is_dynamic { + offset + WORD_LEN + } else { + new_offset + }, + }; + + Ok(result) + }, + } +} + +fn as_usize(slice: &H256) -> AbiResult { + if !slice[..28].iter().all(|x| *x == 0) { + return Err(AbiError(AbiErrorKind::Error_decoding_data)); + } + + let result = ((slice[28] as usize) << 24) + + ((slice[29] as usize) << 16) + + ((slice[30] as usize) << 8) + + (slice[31] as usize); + + Ok(result) +} + +fn as_bool(slice: &H256) -> AbiResult { + if !slice[..31].iter().all(|x| *x == 0) { + return Err(AbiError(AbiErrorKind::Error_decoding_data)); + } + + Ok(slice[31] == 1) +} + +fn peek(data: &[u8], offset: usize, len: usize) -> AbiResult<&[u8]> { + let end = add_checked(offset, len)?; + if end > data.len() { + Err(AbiError(AbiErrorKind::Error_decoding_data)) + } else { + Ok(&data[offset..end]) + } +} + +fn peek_32_bytes(data: &[u8], offset: usize) -> AbiResult { + peek(data, offset, WORD_LEN).map(|x| { + let mut out = H256::default(); + out.copy_from_slice(&x[0..WORD_LEN]); + out + }) +} + +fn take_bytes(data: &[u8], offset: usize, len: usize) -> AbiResult> { + let end = add_checked(offset, len)?; + if end > data.len() { + Err(AbiError(AbiErrorKind::Error_decoding_data)) + } else { + Ok(data[offset..end].to_vec()) + } +} + +fn add_checked(left: usize, right: usize) -> AbiResult { + left.checked_add(right) + .ok_or(AbiError(AbiErrorKind::Error_decoding_data)) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::abi::non_empty_array::NonZeroLen; + use tw_encoding::hex::DecodeHex; + + fn address(byte: u8) -> Token { + let data = H160::from([byte; 20]); + Token::Address(Address::from_bytes(data)) + } + + fn u256(byte: u8) -> Token { + let data = H256::from([byte; WORD_LEN]); + Token::u256(U256::from_big_endian(data)) + } + + #[test] + fn decode_from_empty_byte_slice() { + // these can NOT be decoded from empty byte slice + assert!(decode_params_impl(&[ParamType::Address], &[]).is_err()); + assert!(decode_params_impl(&[ParamType::Bytes], &[]).is_err()); + assert!(decode_params_impl(&[ParamType::i256()], &[]).is_err()); + assert!(decode_params_impl(&[ParamType::Bool], &[]).is_err()); + assert!(decode_params_impl(&[ParamType::String], &[]).is_err()); + let kind = Box::new(ParamType::Bool); + assert!(decode_params_impl(&[ParamType::Array { kind }], &[]).is_err()); + let len = NonZeroLen::new(1).unwrap(); + assert!(decode_params_impl(&[ParamType::FixedBytes { len }], &[]).is_err()); + let kind = Box::new(ParamType::Bool); + let len = NonZeroLen::new(1).unwrap(); + assert!(decode_params_impl(&[ParamType::FixedArray { kind, len }], &[]).is_err()); + } + + #[test] + fn decode_static_tuple_of_addresses_and_uints() { + let encoded = concat!( + "0000000000000000000000001111111111111111111111111111111111111111", + "0000000000000000000000002222222222222222222222222222222222222222", + "1111111111111111111111111111111111111111111111111111111111111111" + ) + .decode_hex() + .unwrap(); + let tuple = Token::Tuple { + params: vec![ + NamedToken::with_token(address(0x11_u8)), + NamedToken::with_token(address(0x22_u8)), + NamedToken::with_token(u256(0x11_u8)), + ], + }; + let expected = vec![tuple]; + + let tuple_type = ParamType::Tuple { + params: vec![ + Param::with_type(ParamType::Address), + Param::with_type(ParamType::Address), + Param::with_type(ParamType::u256()), + ], + }; + let decoded = decode_params_impl(&[tuple_type], &encoded).unwrap(); + assert_eq!(decoded, expected); + } + + #[test] + fn decode_dynamic_tuple() { + let encoded = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000040", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000009", + "6761766f66796f726b0000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000009", + "6761766f66796f726b0000000000000000000000000000000000000000000000" + ) + .decode_hex() + .unwrap(); + let string1 = Token::String("gavofyork".to_owned()); + let string2 = Token::String("gavofyork".to_owned()); + let tuple = Token::Tuple { + params: vec![ + NamedToken::with_token(string1), + NamedToken::with_token(string2), + ], + }; + let decoded = decode_params_impl( + &[ParamType::Tuple { + params: vec![ + Param::with_type(ParamType::String), + Param::with_type(ParamType::String), + ], + }], + &encoded, + ) + .unwrap(); + let expected = vec![tuple]; + assert_eq!(decoded, expected); + } + + #[test] + fn decode_nested_tuple() { + let encoded = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000001", + "00000000000000000000000000000000000000000000000000000000000000c0", + "0000000000000000000000000000000000000000000000000000000000000100", + "0000000000000000000000000000000000000000000000000000000000000004", + "7465737400000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000006", + "6379626f72670000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000060", + "00000000000000000000000000000000000000000000000000000000000000a0", + "00000000000000000000000000000000000000000000000000000000000000e0", + "0000000000000000000000000000000000000000000000000000000000000005", + "6e69676874000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000003", + "6461790000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000040", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000004", + "7765656500000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000008", + "66756e7465737473000000000000000000000000000000000000000000000000" + ) + .decode_hex() + .unwrap(); + let string1 = Token::String("test".to_owned()); + let string2 = Token::String("cyborg".to_owned()); + let string3 = Token::String("night".to_owned()); + let string4 = Token::String("day".to_owned()); + let string5 = Token::String("weee".to_owned()); + let string6 = Token::String("funtests".to_owned()); + let bool = Token::Bool(true); + let deep_tuple = Token::Tuple { + params: vec![ + NamedToken::with_token(string5), + NamedToken::with_token(string6), + ], + }; + let inner_tuple = Token::Tuple { + params: vec![ + NamedToken::with_token(string3), + NamedToken::with_token(string4), + NamedToken::with_token(deep_tuple), + ], + }; + let outer_tuple = Token::Tuple { + params: vec![ + NamedToken::with_token(string1), + NamedToken::with_token(bool), + NamedToken::with_token(string2), + NamedToken::with_token(inner_tuple), + ], + }; + let expected = vec![outer_tuple]; + + let inner_tuple_type = ParamType::Tuple { + params: vec![ + Param::with_type(ParamType::String), + Param::with_type(ParamType::String), + Param::with_type(ParamType::Tuple { + params: vec![ + Param::with_type(ParamType::String), + Param::with_type(ParamType::String), + ], + }), + ], + }; + let decoded = decode_params_impl( + &[ParamType::Tuple { + params: vec![ + Param::with_type(ParamType::String), + Param::with_type(ParamType::Bool), + Param::with_type(ParamType::String), + Param::with_type(inner_tuple_type), + ], + }], + &encoded, + ) + .unwrap(); + assert_eq!(decoded, expected); + } + + #[test] + fn decode_complex_tuple_of_dynamic_and_static_types() { + let encoded = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "1111111111111111111111111111111111111111111111111111111111111111", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000001111111111111111111111111111111111111111", + "0000000000000000000000002222222222222222222222222222222222222222", + "0000000000000000000000000000000000000000000000000000000000000009", + "6761766f66796f726b0000000000000000000000000000000000000000000000", + ) + .decode_hex() + .unwrap(); + let string = Token::String("gavofyork".to_owned()); + let tuple = Token::Tuple { + params: vec![ + NamedToken::with_token(u256(0x11_u8)), + NamedToken::with_token(string), + NamedToken::with_token(address(0x11_u8)), + NamedToken::with_token(address(0x22_u8)), + ], + }; + let expected = vec![tuple]; + + let tuple_type = ParamType::Tuple { + params: vec![ + Param::with_type(ParamType::u256()), + Param::with_type(ParamType::String), + Param::with_type(ParamType::Address), + Param::with_type(ParamType::Address), + ], + }; + let decoded = decode_params_impl(&[tuple_type], &encoded).unwrap(); + assert_eq!(decoded, expected); + } + + #[test] + fn decode_params_containing_dynamic_tuple() { + let encoded = concat!( + "0000000000000000000000002222222222222222222222222222222222222222", + "00000000000000000000000000000000000000000000000000000000000000a0", + "0000000000000000000000003333333333333333333333333333333333333333", + "0000000000000000000000004444444444444444444444444444444444444444", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001", + "0000000000000000000000000000000000000000000000000000000000000060", + "00000000000000000000000000000000000000000000000000000000000000a0", + "0000000000000000000000000000000000000000000000000000000000000009", + "7370616365736869700000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000006", + "6379626f72670000000000000000000000000000000000000000000000000000", + ) + .decode_hex() + .unwrap(); + let bool1 = Token::Bool(true); + let string1 = Token::String("spaceship".to_owned()); + let string2 = Token::String("cyborg".to_owned()); + let tuple = Token::Tuple { + params: vec![ + NamedToken::with_token(bool1), + NamedToken::with_token(string1), + NamedToken::with_token(string2), + ], + }; + let bool2 = Token::Bool(false); + let expected = vec![ + address(0x22_u8), + tuple, + address(0x33_u8), + address(0x44_u8), + bool2, + ]; + let decoded = decode_params_impl( + &[ + ParamType::Address, + ParamType::Tuple { + params: vec![ + Param::with_type(ParamType::Bool), + Param::with_type(ParamType::String), + Param::with_type(ParamType::String), + ], + }, + ParamType::Address, + ParamType::Address, + ParamType::Bool, + ], + &encoded, + ) + .unwrap(); + assert_eq!(decoded, expected); + } + + #[test] + fn decode_params_containing_static_tuple() { + let encoded = concat!( + "0000000000000000000000001111111111111111111111111111111111111111", + "0000000000000000000000002222222222222222222222222222222222222222", + "0000000000000000000000000000000000000000000000000000000000000001", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000003333333333333333333333333333333333333333", + "0000000000000000000000004444444444444444444444444444444444444444", + ) + .decode_hex() + .unwrap(); + let bool1 = Token::Bool(true); + let bool2 = Token::Bool(false); + let tuple = Token::Tuple { + params: vec![ + NamedToken::with_token(address(0x22_u8)), + NamedToken::with_token(bool1), + NamedToken::with_token(bool2), + ], + }; + + let expected = vec![address(0x11_u8), tuple, address(0x33_u8), address(0x44_u8)]; + let decoded = decode_params_impl( + &[ + ParamType::Address, + ParamType::Tuple { + params: vec![ + Param::with_type(ParamType::Address), + Param::with_type(ParamType::Bool), + Param::with_type(ParamType::Bool), + ], + }, + ParamType::Address, + ParamType::Address, + ], + &encoded, + ) + .unwrap(); + assert_eq!(decoded, expected); + } + + #[test] + fn decode_data_with_size_that_is_not_a_multiple_of_32() { + let encoded = concat!( + "0000000000000000000000000000000000000000000000000000000000000000", + "00000000000000000000000000000000000000000000000000000000000000a0", + "0000000000000000000000000000000000000000000000000000000000000152", + "0000000000000000000000000000000000000000000000000000000000000001", + "000000000000000000000000000000000000000000000000000000000054840d", + "0000000000000000000000000000000000000000000000000000000000000092", + "3132323033393637623533326130633134633938306235616566666231373034", + "3862646661656632633239336139353039663038656233633662306635663866", + "3039343265376239636337366361353163636132366365353436393230343438", + "6533303866646136383730623565326165313261323430396439343264653432", + "3831313350373230703330667073313678390000000000000000000000000000", + "0000000000000000000000000000000000103933633731376537633061363531", + "3761", + ) + .decode_hex() + .unwrap(); + + let types = [ + ParamType::u256(), + ParamType::String, + ParamType::String, + ParamType::u256(), + ParamType::u256(), + ]; + let expected = [ + Token::u256(U256::default()), + Token::String(String::from("12203967b532a0c14c980b5aeffb17048bdfaef2c293a9509f08eb3c6b0f5f8f0942e7b9cc76ca51cca26ce546920448e308fda6870b5e2ae12a2409d942de428113P720p30fps16x9")), + Token::String(String::from("93c717e7c0a6517a")), + Token::u256(U256::from(1_u64)), + Token::u256(U256::from(5538829_u64)), + ]; + assert_eq!(decode_params_impl(&types, &encoded,).unwrap(), expected); + } + + #[test] + fn decode_after_fixed_bytes_with_less_than_32_bytes() { + let encoded = concat!( + "0000000000000000000000008497afefdc5ac170a664a231f6efb25526ef813f", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080", + "000000000000000000000000000000000000000000000000000000000000000a", + "3078303030303030314600000000000000000000000000000000000000000000", + ) + .decode_hex() + .unwrap(); + + let types = [ + ParamType::Address, + ParamType::FixedBytes { + len: NonZeroLen::new(32).unwrap(), + }, + ParamType::FixedBytes { + len: NonZeroLen::new(4).unwrap(), + }, + ParamType::String, + ]; + + let expected = [ + Token::Address(Address::from("0x8497afefdc5ac170a664a231f6efb25526ef813f")), + Token::FixedBytes(NonEmptyBytes::new(vec![0u8; 32]).unwrap()), + Token::FixedBytes(NonEmptyBytes::new(vec![0u8; 4]).unwrap()), + Token::String("0x0000001F".into()), + ]; + assert_eq!(decode_params_impl(&types, &encoded,).unwrap(), &expected); + } + + #[test] + fn decode_broken_utf8() { + let encoded = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000004", + "e4b88de500000000000000000000000000000000000000000000000000000000", + ) + .decode_hex() + .unwrap(); + + assert_eq!( + decode_params_impl(&[ParamType::String,], &encoded).unwrap(), + &[Token::String("不�".into())] + ); + } + + #[test] + fn decode_corrupted_dynamic_array() { + // line 1 at 0x00 = 0: tail offset of array + // line 2 at 0x20 = 32: length of array + // line 3 at 0x40 = 64: first word + // line 4 at 0x60 = 96: second word + let encoded = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "00000000000000000000000000000000000000000000000000000000ffffffff", + "0000000000000000000000000000000000000000000000000000000000000001", + "0000000000000000000000000000000000000000000000000000000000000002", + ) + .decode_hex() + .unwrap(); + + let types = [ParamType::Array { + kind: Box::new(ParamType::u256()), + }]; + assert!(decode_params_impl(&types, &encoded).is_err()); + } + + #[test] + fn decode_corrupted_nested_array_tuple() { + let input = concat!( + "0000000000000000000000000000000000000000000000000000000000000040", + // + "00000000000000000000000000000000000000000000000000000000000002a0", + "0000000000000000000000000000000000000000000000000000000000000009", + // + "00000000000000000000000000000000fffffffffffffffffffffffffffffffe", + "0000000000000000000000000000000000000000000000000000000000000000", + // + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + // + "0000000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000ffffffffffffffff", + // + "0008000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000020000000000000000", + // + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000001000000000000000000000000000000000000", + // + "000000000000000000000000000000000000000000000000000000000000053a", + "0100000000000000000000000000000000000000000000000000000000000000", + // + "0000000000000010000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + // + "0000000000000000000000000000000000000000000000000000000002000000", + "0000000000000000000000000000000000000000000000000000000000100000", + // + "0000000000000000000000000000000000000000000000000000000000000000", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + // + "0000000000000000000000000000000000000000000000000000000000000006", + "00000000000000000000000000000000000000000000000000000000000000c0", + // + "0000000000000000000000000000000000000000000000000000000000002ce0", + "0000000000000000000000000000000000000000000000000000000000005880", + // + "0000000000000000000000000000000000000000000000000000000000008280", + "000000000000000000000000000000000000000000000000000000000000acc0", + // + "000000000000000000000000000000000000000000000000000000000000d6e0", + "0000000000000000000000000000000000000000020000000000000000000000", + // + "0000000000000000000000000000000000000000000000000000000000000040", + "0000000000000000000000000000000000000000000000000000000000000009", + // + "0000000000000000000000000000000000000000000000000000000000000120", + "0000000000000000000000000000000000000000000000000000000000000720", + // + "0000000000000000000000000000000000000000000000000000000000000b80", + "0000000000000000000000000000000000000000000000000000000000000fe0", + ) + .decode_hex() + .unwrap(); + + let types = [ + ParamType::Array { + kind: Box::new(ParamType::Tuple { + params: vec![ + Param::with_type(ParamType::u256()), + Param::with_type(ParamType::u256()), + ], + }), + }, + ParamType::Array { + kind: Box::new(ParamType::Tuple { + params: vec![ + Param::with_type(ParamType::u256()), + Param::with_type(ParamType::Array { + kind: Box::new(ParamType::Tuple { + params: vec![ + Param::with_type(ParamType::u256()), + Param::with_type(ParamType::Array { + kind: Box::new(ParamType::String), + }), + ], + }), + }), + ], + }), + }, + ]; + + assert!(decode_params_impl(&types, &input).is_err()); + } + + #[test] + fn decode_corrupted_fixed_array_of_strings() { + let input = concat!( + "0000000000000000000000000000000000000000000000000000000000000001", + "0000000000000000000000000000000000000000000000000000000001000040", + "0000000000000000000000000000000000000000000000000000000000000040", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000008", + "5445535454455354000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000008", + "5445535454455354000000000000000000000000000000000000000000000000", + ) + .decode_hex() + .unwrap(); + + let types = [ + ParamType::u256(), + ParamType::FixedArray { + len: NonZeroLen::new(2).unwrap(), + kind: Box::new(ParamType::String), + }, + ]; + assert!(decode_params_impl(&types, &input).is_err()); + } + + #[test] + fn decode_whole_addresses() { + let input = concat!( + "0000000000000000000000000000000000000000000000000000000000012345", + "0000000000000000000000000000000000000000000000000000000000054321", + ) + .decode_hex() + .unwrap(); + assert!(decode_params_impl(&[ParamType::Address], &input).is_ok()); + } +} diff --git a/rust/tw_evm/src/abi/encode.rs b/rust/tw_evm/src/abi/encode.rs new file mode 100644 index 00000000000..17c2c273c38 --- /dev/null +++ b/rust/tw_evm/src/abi/encode.rs @@ -0,0 +1,1088 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::token::Token; +use tw_hash::H256; +use tw_memory::Data; + +pub fn encode_tokens(tokens: &[Token]) -> Data { + let mediates = tokens.iter().map(mediate_token).collect::>(); + + encode_head_tail(&mediates) + .into_iter() + .flat_map(H256::take) + .collect() +} + +#[derive(Debug)] +enum Mediate<'a> { + // head + Raw(u32, &'a Token), + RawArray(Vec>), + + // head + tail + Prefixed(u32, &'a Token), + PrefixedArray(Vec>), + PrefixedArrayWithLength(Vec>), +} + +impl Mediate<'_> { + fn head_len(&self) -> u32 { + match self { + Mediate::Raw(len, _) => 32 * len, + Mediate::RawArray(ref mediates) => { + mediates.iter().map(|mediate| mediate.head_len()).sum() + }, + Mediate::Prefixed(_, _) + | Mediate::PrefixedArray(_) + | Mediate::PrefixedArrayWithLength(_) => 32, + } + } + + fn tail_len(&self) -> u32 { + match self { + Mediate::Raw(_, _) | Mediate::RawArray(_) => 0, + Mediate::Prefixed(len, _) => 32 * len, + Mediate::PrefixedArray(ref mediates) => mediates + .iter() + .fold(0, |acc, m| acc + m.head_len() + m.tail_len()), + Mediate::PrefixedArrayWithLength(ref mediates) => mediates + .iter() + .fold(32, |acc, m| acc + m.head_len() + m.tail_len()), + } + } + + fn head_append(&self, acc: &mut Vec, suffix_offset: u32) { + match *self { + Mediate::Raw(_, raw) => encode_token_append(acc, raw), + Mediate::RawArray(ref raw) => { + raw.iter().for_each(|mediate| mediate.head_append(acc, 0)) + }, + Mediate::Prefixed(_, _) + | Mediate::PrefixedArray(_) + | Mediate::PrefixedArrayWithLength(_) => acc.push(pad_u32(suffix_offset)), + } + } + + fn tail_append(&self, acc: &mut Vec) { + match *self { + Mediate::Raw(_, _) | Mediate::RawArray(_) => {}, + Mediate::Prefixed(_, raw) => encode_token_append(acc, raw), + Mediate::PrefixedArray(ref mediates) => encode_head_tail_append(acc, mediates), + Mediate::PrefixedArrayWithLength(ref mediates) => { + // + 32 added to offset represents len of the array prepended to tail + acc.push(pad_u32(mediates.len() as u32)); + encode_head_tail_append(acc, mediates); + }, + }; + } +} + +fn encode_head_tail(mediates: &[Mediate]) -> Vec { + let (heads_len, tails_len) = mediates.iter().fold((0, 0), |(head_acc, tail_acc), m| { + (head_acc + m.head_len(), tail_acc + m.tail_len()) + }); + + let mut result = Vec::with_capacity((heads_len + tails_len) as usize); + encode_head_tail_append(&mut result, mediates); + + result +} + +fn encode_head_tail_append(acc: &mut Vec, mediates: &[Mediate]) { + let heads_len = mediates + .iter() + .fold(0, |head_acc, m| head_acc + m.head_len()); + + let mut offset = heads_len; + for mediate in mediates { + mediate.head_append(acc, offset); + offset += mediate.tail_len(); + } + + mediates.iter().for_each(|m| m.tail_append(acc)); +} + +fn mediate_token(token: &Token) -> Mediate { + match token { + Token::Address(_) => Mediate::Raw(1, token), + Token::Bytes(bytes) => Mediate::Prefixed(pad_bytes_len(bytes), token), + Token::String(s) => Mediate::Prefixed(pad_bytes_len(s.as_bytes()), token), + Token::FixedBytes(bytes) => Mediate::Raw(fixed_bytes_len(bytes), token), + Token::Int { .. } | Token::Uint { .. } | Token::Bool(_) => Mediate::Raw(1, token), + Token::Array { arr, .. } => { + let mediates = arr.iter().map(mediate_token).collect(); + Mediate::PrefixedArrayWithLength(mediates) + }, + Token::FixedArray { arr, .. } => { + let mediates = arr.iter().map(mediate_token).collect(); + if token.is_dynamic() { + Mediate::PrefixedArray(mediates) + } else { + Mediate::RawArray(mediates) + } + }, + Token::Tuple { params } => { + let mediates = params + .iter() + .map(|param| mediate_token(¶m.value)) + .collect(); + if token.is_dynamic() { + Mediate::PrefixedArray(mediates) + } else { + Mediate::RawArray(mediates) + } + }, + } +} + +fn encode_token_append(data: &mut Vec, token: &Token) { + match token { + Token::Address(address) => { + let mut padded = H256::default(); + padded[12..].copy_from_slice(address.as_slice()); + data.push(padded); + }, + Token::Bytes(bytes) => pad_bytes_append(data, bytes), + Token::String(s) => pad_bytes_append(data, s.as_bytes()), + Token::FixedBytes(bytes) => fixed_bytes_append(data, bytes), + Token::Int { int, .. } => data.push(int.to_big_endian()), + Token::Uint { uint, .. } => data.push(uint.to_big_endian()), + Token::Bool(b) => { + let mut value = H256::default(); + if *b { + value[31] = 1; + } + data.push(value); + }, + Token::FixedArray { .. } | Token::Array { .. } | Token::Tuple { .. } => { + debug_assert!(false, "Unhandled nested token: {:?}", token) + }, + } +} + +/// Converts a u32 to a right aligned array of 32 bytes. +pub fn pad_u32(value: u32) -> H256 { + let mut padded = H256::default(); + padded[28..32].copy_from_slice(&value.to_be_bytes()); + padded +} + +fn pad_bytes_len(bytes: &[u8]) -> u32 { + // "+ 1" because len is also appended + ((bytes.len() + 31) / 32) as u32 + 1 +} + +fn pad_bytes_append(data: &mut Vec, bytes: &[u8]) { + data.push(pad_u32(bytes.len() as u32)); + fixed_bytes_append(data, bytes); +} + +fn fixed_bytes_len(bytes: &[u8]) -> u32 { + ((bytes.len() + 31) / 32) as u32 +} + +fn fixed_bytes_append(result: &mut Vec, bytes: &[u8]) { + let len = (bytes.len() + 31) / 32; + for i in 0..len { + let mut padded = H256::default(); + + let to_copy = match i == len - 1 { + false => 32, + true => match bytes.len() % 32 { + 0 => 32, + x => x, + }, + }; + + let offset = 32 * i; + padded[..to_copy].copy_from_slice(&bytes[offset..offset + to_copy]); + result.push(padded); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::abi::non_empty_array::{NonEmptyArray, NonEmptyBytes}; + use crate::abi::param::Param; + use crate::abi::param_token::NamedToken; + use crate::abi::param_type::constructor::TypeConstructor; + use crate::abi::param_type::ParamType; + use tw_encoding::hex::DecodeHex; + use tw_number::{I256, U256}; + + #[test] + fn encode_address() { + let address = Token::Address("0x1111111111111111111111111111111111111111".into()); + let encoded = encode_tokens(&[address]); + let expected = "0000000000000000000000001111111111111111111111111111111111111111" + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_dynamic_array_of_addresses() { + let address1 = Token::Address("0x1111111111111111111111111111111111111111".into()); + let address2 = Token::Address("0x2222222222222222222222222222222222222222".into()); + let addresses = Token::Array { + arr: vec![address1, address2], + kind: ParamType::Address, + }; + let encoded = encode_tokens(&[addresses]); + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000002", + "0000000000000000000000001111111111111111111111111111111111111111", + "0000000000000000000000002222222222222222222222222222222222222222", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_fixed_array_of_addresses() { + let address1 = Token::Address("0x1111111111111111111111111111111111111111".into()); + let address2 = Token::Address("0x2222222222222222222222222222222222222222".into()); + let addresses = { + let arr = NonEmptyArray::new(vec![address1, address2]).unwrap(); + let kind = ParamType::Address; + Token::FixedArray { arr, kind } + }; + + let encoded = encode_tokens(&[addresses]); + let expected = concat!( + "0000000000000000000000001111111111111111111111111111111111111111", + "0000000000000000000000002222222222222222222222222222222222222222", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_two_addresses() { + let address1 = Token::Address("0x1111111111111111111111111111111111111111".into()); + let address2 = Token::Address("0x2222222222222222222222222222222222222222".into()); + let encoded = encode_tokens(&[address1, address2]); + let expected = concat!( + "0000000000000000000000001111111111111111111111111111111111111111", + "0000000000000000000000002222222222222222222222222222222222222222", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_fixed_array_of_dynamic_array_of_addresses() { + let address1 = Token::Address("0x1111111111111111111111111111111111111111".into()); + let address2 = Token::Address("0x2222222222222222222222222222222222222222".into()); + let address3 = Token::Address("0x3333333333333333333333333333333333333333".into()); + let address4 = Token::Address("0x4444444444444444444444444444444444444444".into()); + let array0 = Token::Array { + arr: vec![address1, address2], + kind: ParamType::Address, + }; + let array1 = Token::Array { + arr: vec![address3, address4], + kind: ParamType::Address, + }; + let fixed = Token::FixedArray { + arr: NonEmptyArray::new(vec![array0, array1]).unwrap(), + kind: ParamType::array(ParamType::Address), + }; + let encoded = encode_tokens(&[fixed]); + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000040", + "00000000000000000000000000000000000000000000000000000000000000a0", + "0000000000000000000000000000000000000000000000000000000000000002", + "0000000000000000000000001111111111111111111111111111111111111111", + "0000000000000000000000002222222222222222222222222222222222222222", + "0000000000000000000000000000000000000000000000000000000000000002", + "0000000000000000000000003333333333333333333333333333333333333333", + "0000000000000000000000004444444444444444444444444444444444444444", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_dynamic_array_of_fixed_array_of_addresses() { + let address1 = Token::Address("0x1111111111111111111111111111111111111111".into()); + let address2 = Token::Address("0x2222222222222222222222222222222222222222".into()); + let address3 = Token::Address("0x3333333333333333333333333333333333333333".into()); + let address4 = Token::Address("0x4444444444444444444444444444444444444444".into()); + let array0 = Token::FixedArray { + arr: NonEmptyArray::new(vec![address1, address2]).unwrap(), + kind: ParamType::Address, + }; + let array1 = Token::FixedArray { + arr: NonEmptyArray::new(vec![address3, address4]).unwrap(), + kind: ParamType::Address, + }; + let dynamic = Token::Array { + arr: vec![array0, array1], + kind: ParamType::fixed_array(2, ParamType::Address).unwrap(), + }; + let encoded = encode_tokens(&[dynamic]); + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000002", + "0000000000000000000000001111111111111111111111111111111111111111", + "0000000000000000000000002222222222222222222222222222222222222222", + "0000000000000000000000003333333333333333333333333333333333333333", + "0000000000000000000000004444444444444444444444444444444444444444", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_dynamic_array_of_dynamic_arrays() { + let address1 = Token::Address("0x1111111111111111111111111111111111111111".into()); + let address2 = Token::Address("0x2222222222222222222222222222222222222222".into()); + let array0 = Token::Array { + arr: vec![address1], + kind: ParamType::Address, + }; + let array1 = Token::Array { + arr: vec![address2], + kind: ParamType::Address, + }; + let dynamic = Token::Array { + arr: vec![array0, array1], + kind: ParamType::array(ParamType::Address), + }; + let encoded = encode_tokens(&[dynamic]); + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000002", + "0000000000000000000000000000000000000000000000000000000000000040", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000001", + "0000000000000000000000001111111111111111111111111111111111111111", + "0000000000000000000000000000000000000000000000000000000000000001", + "0000000000000000000000002222222222222222222222222222222222222222", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_dynamic_array_of_dynamic_arrays2() { + let address1 = Token::Address("0x1111111111111111111111111111111111111111".into()); + let address2 = Token::Address("0x2222222222222222222222222222222222222222".into()); + let address3 = Token::Address("0x3333333333333333333333333333333333333333".into()); + let address4 = Token::Address("0x4444444444444444444444444444444444444444".into()); + let array0 = Token::Array { + arr: vec![address1, address2], + kind: ParamType::Address, + }; + let array1 = Token::Array { + arr: vec![address3, address4], + kind: ParamType::Address, + }; + let dynamic = Token::Array { + arr: vec![array0, array1], + kind: ParamType::array(ParamType::Address), + }; + let encoded = encode_tokens(&[dynamic]); + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000002", + "0000000000000000000000000000000000000000000000000000000000000040", + "00000000000000000000000000000000000000000000000000000000000000a0", + "0000000000000000000000000000000000000000000000000000000000000002", + "0000000000000000000000001111111111111111111111111111111111111111", + "0000000000000000000000002222222222222222222222222222222222222222", + "0000000000000000000000000000000000000000000000000000000000000002", + "0000000000000000000000003333333333333333333333333333333333333333", + "0000000000000000000000004444444444444444444444444444444444444444", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_fixed_array_of_fixed_arrays() { + let address1 = Token::Address("0x1111111111111111111111111111111111111111".into()); + let address2 = Token::Address("0x2222222222222222222222222222222222222222".into()); + let address3 = Token::Address("0x3333333333333333333333333333333333333333".into()); + let address4 = Token::Address("0x4444444444444444444444444444444444444444".into()); + let array0 = Token::FixedArray { + arr: NonEmptyArray::new(vec![address1, address2]).unwrap(), + kind: ParamType::Address, + }; + let array1 = Token::FixedArray { + arr: NonEmptyArray::new(vec![address3, address4]).unwrap(), + kind: ParamType::Address, + }; + let fixed = Token::FixedArray { + arr: NonEmptyArray::new(vec![array0, array1]).unwrap(), + kind: ParamType::fixed_array(2, ParamType::fixed_array(2, ParamType::Address).unwrap()) + .unwrap(), + }; + let encoded = encode_tokens(&[fixed]); + let expected = concat!( + "0000000000000000000000001111111111111111111111111111111111111111", + "0000000000000000000000002222222222222222222222222222222222222222", + "0000000000000000000000003333333333333333333333333333333333333333", + "0000000000000000000000004444444444444444444444444444444444444444", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_fixed_array_of_static_tuples_followed_by_dynamic_type() { + let tuple1 = Token::Tuple { + params: vec![ + NamedToken::with_token(Token::u256(93523141_u64.into())), + NamedToken::with_token(Token::u256(352332135_u64.into())), + NamedToken::with_token(Token::Address( + "0x4444444444444444444444444444444444444444".into(), + )), + ], + }; + let tuple2 = Token::Tuple { + params: vec![ + NamedToken::with_token(Token::u256(12411_u64.into())), + NamedToken::with_token(Token::u256(451_u64.into())), + NamedToken::with_token(Token::Address( + "0x2222222222222222222222222222222222222222".into(), + )), + ], + }; + let fixed = Token::FixedArray { + arr: NonEmptyArray::new(vec![tuple1, tuple2]).unwrap(), + kind: ParamType::Tuple { + params: vec![ + Param::with_type(ParamType::u256()), + Param::with_type(ParamType::u256()), + Param::with_type(ParamType::Address), + ], + }, + }; + let s = Token::String("gavofyork".to_owned()); + let encoded = encode_tokens(&[fixed, s]); + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000005930cc5", + "0000000000000000000000000000000000000000000000000000000015002967", + "0000000000000000000000004444444444444444444444444444444444444444", + "000000000000000000000000000000000000000000000000000000000000307b", + "00000000000000000000000000000000000000000000000000000000000001c3", + "0000000000000000000000002222222222222222222222222222222222222222", + "00000000000000000000000000000000000000000000000000000000000000e0", + "0000000000000000000000000000000000000000000000000000000000000009", + "6761766f66796f726b0000000000000000000000000000000000000000000000", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_empty_array() { + // Empty arrays + let encoded = encode_tokens(&[ + Token::Array { + arr: vec![], + kind: ParamType::Bool, + }, + Token::Array { + arr: vec![], + kind: ParamType::Address, + }, + ]); + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000040", + "0000000000000000000000000000000000000000000000000000000000000060", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + + // Nested empty arrays + let encoded = encode_tokens(&[ + Token::Array { + arr: vec![Token::Array { + arr: vec![], + kind: ParamType::i256(), + }], + kind: ParamType::array(ParamType::i256()), + }, + Token::Array { + arr: vec![Token::Array { + arr: vec![], + kind: ParamType::String, + }], + kind: ParamType::array(ParamType::String), + }, + ]); + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000040", + "00000000000000000000000000000000000000000000000000000000000000a0", + "0000000000000000000000000000000000000000000000000000000000000001", + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001", + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000000", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_bytes() { + let bytes = Token::Bytes(vec![0x12, 0x34]); + let encoded = encode_tokens(&[bytes]); + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000002", + "1234000000000000000000000000000000000000000000000000000000000000", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_fixed_bytes() { + let bytes = Token::FixedBytes(NonEmptyBytes::new(vec![0x12, 0x34]).unwrap()); + let encoded = encode_tokens(&[bytes]); + let expected = "1234000000000000000000000000000000000000000000000000000000000000" + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_string() { + let s = Token::String("gavofyork".to_owned()); + let encoded = encode_tokens(&[s]); + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000009", + "6761766f66796f726b0000000000000000000000000000000000000000000000", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_bytes2() { + let bytes = Token::Bytes( + "10000000000000000000000000000000000000000000000000000000000002" + .decode_hex() + .unwrap(), + ); + let encoded = encode_tokens(&[bytes]); + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "000000000000000000000000000000000000000000000000000000000000001f", + "1000000000000000000000000000000000000000000000000000000000000200", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_bytes3() { + let bytes = Token::Bytes( + "10000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000".decode_hex().unwrap() + ); + let encoded = encode_tokens(&[bytes]); + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000040", + "1000000000000000000000000000000000000000000000000000000000000000", + "1000000000000000000000000000000000000000000000000000000000000000", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_two_bytes() { + let bytes1 = Token::Bytes( + "10000000000000000000000000000000000000000000000000000000000002" + .decode_hex() + .unwrap(), + ); + let bytes2 = Token::Bytes( + "0010000000000000000000000000000000000000000000000000000000000002" + .decode_hex() + .unwrap(), + ); + let encoded = encode_tokens(&[bytes1, bytes2]); + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000040", + "0000000000000000000000000000000000000000000000000000000000000080", + "000000000000000000000000000000000000000000000000000000000000001f", + "1000000000000000000000000000000000000000000000000000000000000200", + "0000000000000000000000000000000000000000000000000000000000000020", + "0010000000000000000000000000000000000000000000000000000000000002", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_uint() { + let mut uint = H256::default(); + uint[31] = 4; + let encoded = encode_tokens(&[Token::u256(U256::from_big_endian(uint))]); + let expected = "0000000000000000000000000000000000000000000000000000000000000004" + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_int() { + let mut int = H256::default(); + int[31] = 4; + let encoded = encode_tokens(&[Token::i256(I256::from_big_endian(int))]); + let expected = "0000000000000000000000000000000000000000000000000000000000000004" + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_bool() { + let encoded = encode_tokens(&[Token::Bool(true)]); + let expected = "0000000000000000000000000000000000000000000000000000000000000001" + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_bool2() { + let encoded = encode_tokens(&[Token::Bool(false)]); + let expected = "0000000000000000000000000000000000000000000000000000000000000000" + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn comprehensive_test() { + let bytes = concat!( + "131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b", + "131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b", + ) + .decode_hex() + .unwrap(); + let encoded = encode_tokens(&[ + Token::i256(5_u64.into()), + Token::Bytes(bytes.clone()), + Token::i256(3_u64.into()), + Token::Bytes(bytes), + ]); + + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000005", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000003", + "00000000000000000000000000000000000000000000000000000000000000e0", + "0000000000000000000000000000000000000000000000000000000000000040", + "131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b", + "131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b", + "0000000000000000000000000000000000000000000000000000000000000040", + "131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b", + "131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn test_pad_u32() { + // this will fail if endianess is not supported + assert_eq!(pad_u32(0x1)[31], 1); + assert_eq!(pad_u32(0x100)[30], 1); + } + + #[test] + fn comprehensive_test2() { + let encoded = encode_tokens(&vec![ + Token::i256(1_u64.into()), + Token::String("gavofyork".to_owned()), + Token::i256(2_u64.into()), + Token::i256(3_u64.into()), + Token::i256(4_u64.into()), + Token::Array { + arr: vec![ + Token::i256(5_u64.into()), + Token::i256(6_u64.into()), + Token::i256(7_u64.into()), + ], + kind: ParamType::i256(), + }, + ]); + + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000001", + "00000000000000000000000000000000000000000000000000000000000000c0", + "0000000000000000000000000000000000000000000000000000000000000002", + "0000000000000000000000000000000000000000000000000000000000000003", + "0000000000000000000000000000000000000000000000000000000000000004", + "0000000000000000000000000000000000000000000000000000000000000100", + "0000000000000000000000000000000000000000000000000000000000000009", + "6761766f66796f726b0000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000003", + "0000000000000000000000000000000000000000000000000000000000000005", + "0000000000000000000000000000000000000000000000000000000000000006", + "0000000000000000000000000000000000000000000000000000000000000007", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_dynamic_array_of_bytes() { + let bytes = "019c80031b20d5e69c8093a571162299032018d913930d93ab320ae5ea44a4218a274f00d607" + .decode_hex() + .unwrap(); + let encoded = encode_tokens(&[Token::Array { + arr: vec![Token::Bytes(bytes.to_vec())], + kind: ParamType::Bytes, + }]); + + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000001", + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000026", + "019c80031b20d5e69c8093a571162299032018d913930d93ab320ae5ea44a421", + "8a274f00d6070000000000000000000000000000000000000000000000000000", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_dynamic_array_of_bytes2() { + let bytes = "4444444444444444444444444444444444444444444444444444444444444444444444444444" + .decode_hex() + .unwrap(); + let bytes2 = "6666666666666666666666666666666666666666666666666666666666666666666666666666" + .decode_hex() + .unwrap(); + let encoded = encode_tokens(&[Token::Array { + arr: vec![Token::Bytes(bytes.to_vec()), Token::Bytes(bytes2.to_vec())], + kind: ParamType::Bytes, + }]); + + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000002", + "0000000000000000000000000000000000000000000000000000000000000040", + "00000000000000000000000000000000000000000000000000000000000000a0", + "0000000000000000000000000000000000000000000000000000000000000026", + "4444444444444444444444444444444444444444444444444444444444444444", + "4444444444440000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000026", + "6666666666666666666666666666666666666666666666666666666666666666", + "6666666666660000000000000000000000000000000000000000000000000000", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_static_tuple_of_addresses() { + let address1 = Token::Address("0x1111111111111111111111111111111111111111".into()); + let address2 = Token::Address("0x2222222222222222222222222222222222222222".into()); + let encoded = encode_tokens(&[Token::Tuple { + params: vec![ + NamedToken::with_token(address1), + NamedToken::with_token(address2), + ], + }]); + + let expected = concat!( + "0000000000000000000000001111111111111111111111111111111111111111", + "0000000000000000000000002222222222222222222222222222222222222222", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_dynamic_tuple() { + let string1 = Token::String("gavofyork".to_owned()); + let string2 = Token::String("gavofyork".to_owned()); + let tuple = Token::Tuple { + params: vec![ + NamedToken::with_token(string1), + NamedToken::with_token(string2), + ], + }; + let encoded = encode_tokens(&[tuple]); + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000040", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000009", + "6761766f66796f726b0000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000009", + "6761766f66796f726b0000000000000000000000000000000000000000000000", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_dynamic_tuple_of_bytes2() { + let bytes = "4444444444444444444444444444444444444444444444444444444444444444444444444444" + .decode_hex() + .unwrap(); + let bytes2 = "6666666666666666666666666666666666666666666666666666666666666666666666666666" + .decode_hex() + .unwrap(); + let encoded = encode_tokens(&[Token::Tuple { + params: vec![ + NamedToken::with_token(Token::Bytes(bytes.to_vec())), + NamedToken::with_token(Token::Bytes(bytes2.to_vec())), + ], + }]); + + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000040", + "00000000000000000000000000000000000000000000000000000000000000a0", + "0000000000000000000000000000000000000000000000000000000000000026", + "4444444444444444444444444444444444444444444444444444444444444444", + "4444444444440000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000026", + "6666666666666666666666666666666666666666666666666666666666666666", + "6666666666660000000000000000000000000000000000000000000000000000", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_complex_tuple() { + let uint_data = + H256::from("1111111111111111111111111111111111111111111111111111111111111111"); + let uint = Token::u256(U256::from_big_endian(uint_data)); + let string = Token::String("gavofyork".to_owned()); + let address1 = Token::Address("0x1111111111111111111111111111111111111111".into()); + let address2 = Token::Address("0x2222222222222222222222222222222222222222".into()); + let tuple = Token::Tuple { + params: vec![ + NamedToken::with_token(uint), + NamedToken::with_token(string), + NamedToken::with_token(address1), + NamedToken::with_token(address2), + ], + }; + let encoded = encode_tokens(&[tuple]); + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "1111111111111111111111111111111111111111111111111111111111111111", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000001111111111111111111111111111111111111111", + "0000000000000000000000002222222222222222222222222222222222222222", + "0000000000000000000000000000000000000000000000000000000000000009", + "6761766f66796f726b0000000000000000000000000000000000000000000000", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_nested_tuple() { + let string1 = Token::String("test".to_owned()); + let string2 = Token::String("cyborg".to_owned()); + let string3 = Token::String("night".to_owned()); + let string4 = Token::String("day".to_owned()); + let string5 = Token::String("weee".to_owned()); + let string6 = Token::String("funtests".to_owned()); + let bool = Token::Bool(true); + let deep_tuple = Token::Tuple { + params: vec![ + NamedToken::with_token(string5), + NamedToken::with_token(string6), + ], + }; + let inner_tuple = Token::Tuple { + params: vec![ + NamedToken::with_token(string3), + NamedToken::with_token(string4), + NamedToken::with_token(deep_tuple), + ], + }; + let outer_tuple = Token::Tuple { + params: vec![ + NamedToken::with_token(string1), + NamedToken::with_token(bool), + NamedToken::with_token(string2), + NamedToken::with_token(inner_tuple), + ], + }; + let encoded = encode_tokens(&[outer_tuple]); + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000001", + "00000000000000000000000000000000000000000000000000000000000000c0", + "0000000000000000000000000000000000000000000000000000000000000100", + "0000000000000000000000000000000000000000000000000000000000000004", + "7465737400000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000006", + "6379626f72670000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000060", + "00000000000000000000000000000000000000000000000000000000000000a0", + "00000000000000000000000000000000000000000000000000000000000000e0", + "0000000000000000000000000000000000000000000000000000000000000005", + "6e69676874000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000003", + "6461790000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000040", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000004", + "7765656500000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000008", + "66756e7465737473000000000000000000000000000000000000000000000000", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_params_containing_dynamic_tuple() { + let address2 = Token::Address("0x2222222222222222222222222222222222222222".into()); + let address3 = Token::Address("0x3333333333333333333333333333333333333333".into()); + let address4 = Token::Address("0x4444444444444444444444444444444444444444".into()); + let bool1 = Token::Bool(true); + let string1 = Token::String("spaceship".to_owned()); + let string2 = Token::String("cyborg".to_owned()); + let tuple = Token::Tuple { + params: vec![ + NamedToken::with_token(bool1), + NamedToken::with_token(string1), + NamedToken::with_token(string2), + ], + }; + let bool2 = Token::Bool(false); + let encoded = encode_tokens(&[address2, tuple, address3, address4, bool2]); + let expected = concat!( + "0000000000000000000000002222222222222222222222222222222222222222", + "00000000000000000000000000000000000000000000000000000000000000a0", + "0000000000000000000000003333333333333333333333333333333333333333", + "0000000000000000000000004444444444444444444444444444444444444444", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001", + "0000000000000000000000000000000000000000000000000000000000000060", + "00000000000000000000000000000000000000000000000000000000000000a0", + "0000000000000000000000000000000000000000000000000000000000000009", + "7370616365736869700000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000006", + "6379626f72670000000000000000000000000000000000000000000000000000", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_params_containing_static_tuple() { + let address1 = Token::Address("0x1111111111111111111111111111111111111111".into()); + let address2 = Token::Address("0x2222222222222222222222222222222222222222".into()); + let address3 = Token::Address("0x3333333333333333333333333333333333333333".into()); + let address4 = Token::Address("0x4444444444444444444444444444444444444444".into()); + let bool1 = Token::Bool(true); + let bool2 = Token::Bool(false); + let tuple = Token::Tuple { + params: vec![ + NamedToken::with_token(address2), + NamedToken::with_token(bool1), + NamedToken::with_token(bool2), + ], + }; + let encoded = encode_tokens(&[address1, tuple, address3, address4]); + let expected = concat!( + "0000000000000000000000001111111111111111111111111111111111111111", + "0000000000000000000000002222222222222222222222222222222222222222", + "0000000000000000000000000000000000000000000000000000000000000001", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000003333333333333333333333333333333333333333", + "0000000000000000000000004444444444444444444444444444444444444444", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } + + #[test] + fn encode_dynamic_tuple_with_nested_static_tuples() { + let uint1 = Token::u256(0x777_u64.into()); + let uint2 = Token::u256(0x42_u64.into()); + let uint3 = Token::u256(0x1337_u64.into()); + let token = { + Token::Tuple { + params: vec![ + NamedToken::with_token(Token::Tuple { + params: vec![NamedToken::with_token(Token::Tuple { + params: vec![ + NamedToken::with_token(Token::Bool(false)), + NamedToken::with_token(uint1), + ], + })], + }), + NamedToken::with_token(Token::Array { + arr: vec![uint2, uint3], + kind: ParamType::u256(), + }), + ], + } + }; + let encoded = encode_tokens(&[token]); + let expected = concat!( + "0000000000000000000000000000000000000000000000000000000000000020", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000777", + "0000000000000000000000000000000000000000000000000000000000000060", + "0000000000000000000000000000000000000000000000000000000000000002", + "0000000000000000000000000000000000000000000000000000000000000042", + "0000000000000000000000000000000000000000000000000000000000001337", + ) + .decode_hex() + .unwrap(); + assert_eq!(encoded, expected); + } +} diff --git a/rust/tw_evm/src/abi/function.rs b/rust/tw_evm/src/abi/function.rs new file mode 100644 index 00000000000..cae72467a02 --- /dev/null +++ b/rust/tw_evm/src/abi/function.rs @@ -0,0 +1,67 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::decode::decode_params; +use crate::abi::encode::encode_tokens; +use crate::abi::param::Param; +use crate::abi::param_token::NamedToken; +use crate::abi::signature::short_signature; +use crate::abi::token::Token; +use crate::abi::{AbiError, AbiErrorKind, AbiResult}; +use itertools::Itertools; +use serde::Deserialize; +use tw_memory::Data; + +#[derive(Clone, Debug, Default, Deserialize)] +pub struct Function { + /// Function name. + pub name: String, + /// Function input. + pub inputs: Vec, + /// Function output. + pub outputs: Vec, +} + +impl Function { + /// Returns a signature that uniquely identifies this function. + /// + /// Examples: + /// - `functionName()` + /// - `functionName():(uint256)` + /// - `functionName(bool):(uint256,string)` + /// - `functionName(uint256,bytes32):(string,uint256)` + pub fn signature(&self) -> String { + let inputs = self.inputs.iter().map(|p| p.kind.to_type_long()).join(","); + + let outputs = self.outputs.iter().map(|p| p.kind.to_type_long()).join(","); + + match (inputs.len(), outputs.len()) { + (_, 0) => format!("{}({inputs})", self.name), + (_, _) => format!("{}({inputs}):({outputs})", self.name), + } + } + + /// Parses the ABI function input to a list of tokens. + pub fn decode_input(&self, data: &[u8]) -> AbiResult> { + decode_params(&self.inputs, data) + } + + /// Encodes function input to Eth ABI binary. + pub fn encode_input(&self, tokens: &[Token]) -> AbiResult { + // Check if the given tokens match `Self::inputs` ABI. + let input_param_types: Vec<_> = + self.inputs.iter().map(|param| param.kind.clone()).collect(); + for (token, kind) in tokens.iter().zip(input_param_types.iter()) { + if token.to_param_type() != *kind { + return Err(AbiError(AbiErrorKind::Error_abi_mismatch)); + } + } + + let signed = short_signature(&self.name, &input_param_types); + let encoded = encode_tokens(tokens); + Ok(signed.into_iter().chain(encoded.into_iter()).collect()) + } +} diff --git a/rust/tw_evm/src/abi/mod.rs b/rust/tw_evm/src/abi/mod.rs new file mode 100644 index 00000000000..25b36b9f936 --- /dev/null +++ b/rust/tw_evm/src/abi/mod.rs @@ -0,0 +1,49 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::error::{SigningError, SigningErrorType}; + +pub mod contract; +pub mod decode; +pub mod encode; +pub mod function; +pub mod non_empty_array; +pub mod param; +pub mod param_token; +pub mod param_type; +pub mod prebuild; +pub mod signature; +pub mod token; +pub mod uint; + +#[macro_export] +macro_rules! abi_output_error { + ($output:ty, $error:expr) => {{ + let err = $error; + + let mut output = <$output>::default(); + output.error = err.0; + output.error_message = std::borrow::Cow::from(format!("{err:?}")); + + output + }}; +} + +pub type AbiResult = Result; +pub type AbiErrorKind = tw_proto::EthereumAbi::Proto::AbiError; + +#[derive(Debug)] +pub struct AbiError(pub AbiErrorKind); + +impl From for SigningError { + fn from(err: AbiError) -> Self { + match err.0 { + AbiErrorKind::OK => SigningError(SigningErrorType::OK), + AbiErrorKind::Error_internal => SigningError(SigningErrorType::Error_internal), + _ => SigningError(SigningErrorType::Error_invalid_params), + } + } +} diff --git a/rust/tw_evm/src/abi/non_empty_array.rs b/rust/tw_evm/src/abi/non_empty_array.rs new file mode 100644 index 00000000000..b6a7a535c28 --- /dev/null +++ b/rust/tw_evm/src/abi/non_empty_array.rs @@ -0,0 +1,83 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::{AbiError, AbiErrorKind, AbiResult}; +use core::fmt; +use std::num::NonZeroUsize; +use std::ops::Deref; + +pub type NonEmptyBytes = NonEmptyArray; + +/// A convenient wrapper over `NonZeroUsize`. +#[derive(Copy, Clone, PartialEq)] +pub struct NonZeroLen(NonZeroUsize); + +impl NonZeroLen { + pub fn new(len: usize) -> AbiResult { + NonZeroUsize::new(len) + .ok_or(AbiError(AbiErrorKind::Error_invalid_param_type)) + .map(NonZeroLen) + } + + pub fn get(&self) -> usize { + self.0.get() + } +} + +impl fmt::Debug for NonZeroLen { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{self}") + } +} + +impl fmt::Display for NonZeroLen { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0.get()) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct NonEmptyArray(Vec); + +impl NonEmptyArray { + pub fn new(elements: Vec) -> AbiResult> { + if elements.is_empty() { + return Err(AbiError(AbiErrorKind::Error_empty_type)); + } + Ok(NonEmptyArray(elements)) + } + + pub fn len(&self) -> NonZeroLen { + NonZeroLen::new(self.0.len()).expect("`NonEmptyArray` must have at least one element") + } + + pub fn into_vec(self) -> Vec { + self.0 + } +} + +impl IntoIterator for NonEmptyArray { + type Item = T; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl Deref for NonEmptyArray { + type Target = [T]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl AsRef<[u8]> for NonEmptyArray { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} diff --git a/rust/tw_evm/src/abi/param.rs b/rust/tw_evm/src/abi/param.rs new file mode 100644 index 00000000000..93ca5dcb3eb --- /dev/null +++ b/rust/tw_evm/src/abi/param.rs @@ -0,0 +1,124 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::param_type::ParamType; +use serde::de::{MapAccess, Visitor}; +use serde::{de::Error as DeError, Deserialize, Deserializer}; +use std::fmt::Formatter; + +#[derive(Clone, Debug, PartialEq)] +pub struct Param { + /// Param name. + pub name: Option, + /// Param type. + pub kind: ParamType, + /// Additional Internal type. + pub internal_type: Option, +} + +impl Param { + /// Should be used in tests only. + #[cfg(test)] + pub(crate) fn with_type(kind: ParamType) -> Param { + Param { + name: None, + kind, + internal_type: None, + } + } +} + +impl<'de> Deserialize<'de> for Param { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_any(ParamVisitor) + } +} + +struct ParamVisitor; + +impl<'a> Visitor<'a> for ParamVisitor { + type Value = Param; + + fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { + write!(formatter, "a valid event parameter spec") + } + + // The method implementation is inspired by + // https://github.com/rust-ethereum/ethabi/blob/b1710adc18f5b771d2d2519c87248b1ba9430778/ethabi/src/param.rs#L59-L103 + // + // The only difference is that tuple parameters have names. + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'a>, + { + macro_rules! handle_map_key { + ($key_name:literal, $value:expr) => {{ + if $value.is_some() { + return Err(DeError::duplicate_field($key_name)); + } + $value = Some(map.next_value()?); + }}; + } + + let mut name = None; + let mut kind = None; + let mut internal_type = None; + let mut components = None; + + while let Some(ref key) = map.next_key::()? { + match key.as_ref() { + "name" => handle_map_key!("name", name), + "type" => handle_map_key!("kind", kind), + "internalType" => handle_map_key!("internalType", internal_type), + "components" => handle_map_key!("components", components), + // Skip unknown field. + _ => {}, + } + } + + let mut kind = kind.ok_or_else(|| DeError::missing_field("kind"))?; + set_tuple_components::(&mut kind, components)?; + + Ok(Param { + name, + kind, + internal_type, + }) + } +} + +/// Set tuple components if: +/// 1) `kind` is `tuple` or `tuple[]` or `tuple[][]`, etc. +/// 2) `components` is `Some`. +fn set_tuple_components( + kind: &mut ParamType, + components: Option>, +) -> Result<(), E> { + if let Some(tuple_components) = inner_tuple_mut(kind) { + *tuple_components = components.ok_or_else(|| E::missing_field("components"))?; + if tuple_components.is_empty() { + return Err(DeError::custom("'components' cannot be empty")); + } + } + Ok(()) +} + +/// Returns a mutable reference to the tuple components if `param` is `tuple` or `tuple[]` or `tuple[][]`, etc. +fn inner_tuple_mut(mut param: &mut ParamType) -> Option<&mut Vec> { + loop { + match param { + ParamType::Array { kind: elem_kind } => param = elem_kind.as_mut(), + ParamType::FixedArray { + kind: elem_kind, .. + } => param = elem_kind.as_mut(), + ParamType::Tuple { params } => return Some(params), + _ => return None, + } + } +} diff --git a/rust/tw_evm/src/abi/param_token.rs b/rust/tw_evm/src/abi/param_token.rs new file mode 100644 index 00000000000..420b8e6f217 --- /dev/null +++ b/rust/tw_evm/src/abi/param_token.rs @@ -0,0 +1,67 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::param::Param; +use crate::abi::token::Token; +use serde::ser::SerializeMap; +use serde::{Serialize, Serializer}; + +#[derive(Clone, Debug, PartialEq)] +pub struct NamedToken { + /// Optional param name. + pub name: Option, + /// Parameter value. + pub value: Token, + /// Additional Internal type. + pub internal_type: Option, +} + +impl NamedToken { + pub fn with_param_and_token(param: &Param, value: Token) -> NamedToken { + NamedToken { + name: param.name.clone(), + value, + internal_type: param.internal_type.clone(), + } + } + + pub fn to_param(&self) -> Param { + Param { + name: self.name.clone(), + kind: self.value.to_param_type(), + internal_type: self.internal_type.clone(), + } + } + + #[cfg(test)] + pub fn with_token(value: Token) -> NamedToken { + NamedToken { + name: None, + value, + internal_type: None, + } + } +} + +impl Serialize for NamedToken { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + const NUMBER_OF_ENTRIES: usize = 3; + + let mut serde_struct = serializer.serialize_map(Some(NUMBER_OF_ENTRIES))?; + serde_struct.serialize_entry("name", &self.name)?; + serde_struct.serialize_entry("type", &self.value.type_short())?; + + if self.value.to_param_type().has_tuple_components() { + serde_struct.serialize_entry("components", &self.value)?; + } else { + serde_struct.serialize_entry("value", &self.value)?; + } + serde_struct.end() + } +} diff --git a/rust/tw_evm/src/abi/param_type/constructor.rs b/rust/tw_evm/src/abi/param_type/constructor.rs new file mode 100644 index 00000000000..37eb5d2d39a --- /dev/null +++ b/rust/tw_evm/src/abi/param_type/constructor.rs @@ -0,0 +1,107 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::non_empty_array::NonZeroLen; +use crate::abi::param_type::ParamType; +use crate::abi::uint::UintBits; +use crate::abi::{AbiError, AbiErrorKind, AbiResult}; + +pub trait TypeConstructor: Sized { + fn address() -> Self; + + fn bytes() -> Self; + + fn fixed_bytes(len: usize) -> AbiResult { + let checked_len = NonZeroLen::new(len)?; + Ok(Self::fixed_bytes_checked(checked_len)) + } + + fn fixed_bytes_checked(len: NonZeroLen) -> Self; + + fn int(bits: usize) -> AbiResult { + let checked_bits = UintBits::new(bits)?; + Ok(Self::int_checked(checked_bits)) + } + + fn int_checked(bits: UintBits) -> Self; + + fn uint(bits: usize) -> AbiResult { + let checked_bits = UintBits::new(bits)?; + Ok(Self::uint_checked(checked_bits)) + } + + fn uint_checked(bits: UintBits) -> Self; + + fn bool() -> Self; + + fn string() -> Self; + + fn array(element_type: Self) -> Self; + + fn fixed_array(len: usize, element_type: Self) -> AbiResult { + let checked_len = NonZeroLen::new(len)?; + Ok(Self::fixed_array_checked(checked_len, element_type)) + } + + fn fixed_array_checked(len: NonZeroLen, element_type: Self) -> Self; + + fn empty_tuple() -> AbiResult; + + fn custom(s: &str) -> AbiResult; +} + +impl TypeConstructor for ParamType { + fn address() -> Self { + ParamType::Address + } + + fn bytes() -> Self { + ParamType::Bytes + } + + fn fixed_bytes_checked(len: NonZeroLen) -> Self { + ParamType::FixedBytes { len } + } + + fn int_checked(bits: UintBits) -> Self { + ParamType::Int { bits } + } + + fn uint_checked(bits: UintBits) -> Self { + ParamType::Uint { bits } + } + + fn bool() -> Self { + ParamType::Bool + } + + fn string() -> Self { + ParamType::String + } + + fn array(element_type: Self) -> Self { + ParamType::Array { + kind: Box::new(element_type), + } + } + + fn fixed_array_checked(len: NonZeroLen, element_type: Self) -> Self { + ParamType::FixedArray { + kind: Box::new(element_type), + len, + } + } + + fn empty_tuple() -> AbiResult { + Ok(ParamType::Tuple { + params: Vec::default(), + }) + } + + fn custom(_s: &str) -> AbiResult { + Err(AbiError(AbiErrorKind::Error_invalid_param_type)) + } +} diff --git a/rust/tw_evm/src/abi/param_type/mod.rs b/rust/tw_evm/src/abi/param_type/mod.rs new file mode 100644 index 00000000000..1ca70655574 --- /dev/null +++ b/rust/tw_evm/src/abi/param_type/mod.rs @@ -0,0 +1,137 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::non_empty_array::NonZeroLen; +use crate::abi::param::Param; +use crate::abi::uint::UintBits; +use crate::abi::AbiResult; +use serde::{de::Error as DeError, Deserialize, Deserializer}; + +pub mod constructor; +pub mod reader; +pub mod writer; + +use reader::Reader; +use writer::Writer; + +#[derive(Clone, Debug, PartialEq)] +pub enum ParamType { + /// Address. + /// + /// solidity name: address + /// Encoded to left padded [0u8; 32]. + Address, + /// Vector of bytes with known size. + /// + /// solidity name eg.: bytes8, bytes32, bytes64, bytes1024 + /// Encoded to right padded [0u8; ((N + 31) / 32) * 32]. + FixedBytes { len: NonZeroLen }, + /// Vector of bytes of unknown size. + /// + /// solidity name: bytes + /// Encoded in two parts. + /// Init part: offset of 'closing part`. + /// Closing part: encoded length followed by encoded right padded bytes. + Bytes, + /// Signed integer. + /// + /// solidity name: int + Int { bits: UintBits }, + /// Unsigned integer. + /// + /// solidity name: uint + Uint { bits: UintBits }, + /// Boolean value. + /// + /// solidity name: bool + /// Encoded as left padded [0u8; 32], where last bit represents boolean value. + Bool, + /// String. + /// + /// solidity name: string + /// Encoded in the same way as bytes. Must be utf8 compliant. + String, + /// Array with known size. + /// + /// solidity name eg.: int[3], bool[3], address[][8] + /// Encoding of array is equal to encoding of consecutive elements of array. + FixedArray { + kind: Box, + len: NonZeroLen, + }, + /// Array of params with unknown size. + /// + /// solidity name eg. int[], bool[], address[5][] + Array { kind: Box }, + /// Tuple of params of variable types. + /// + /// solidity name: tuple + Tuple { params: Vec }, +} + +impl ParamType { + pub fn i256() -> ParamType { + ParamType::Int { + bits: UintBits::default(), + } + } + + pub fn u256() -> ParamType { + ParamType::Uint { + bits: UintBits::default(), + } + } + + /// Tuples will be represented as list of inner types in parens, for example `(int256,bool)`. + pub fn to_type_long(&self) -> String { + let serialize_tuple_contents = true; + Writer::write_for_abi(self, serialize_tuple_contents) + } + + /// Tuples will be represented as keyword `tuple`. + pub fn to_type_short(&self) -> String { + let serialize_tuple_contents = false; + Writer::write_for_abi(self, serialize_tuple_contents) + } + + /// returns whether a ParamType is dynamic + /// used to decide how the ParamType should be encoded + pub fn is_dynamic(&self) -> bool { + match self { + ParamType::Bytes | ParamType::String | ParamType::Array { .. } => true, + ParamType::FixedArray { kind, .. } => kind.is_dynamic(), + ParamType::Tuple { params } => params.iter().any(|param| param.kind.is_dynamic()), + _ => false, + } + } +} + +impl ParamType { + pub fn try_from_type_short(type_short: &str) -> AbiResult { + Reader::parse_type(type_short) + } + + pub(crate) fn has_tuple_components(&self) -> bool { + let mut inner_type = self; + loop { + match inner_type { + ParamType::Array { kind } | ParamType::FixedArray { kind, .. } => inner_type = kind, + ParamType::Tuple { .. } => return true, + _ => return false, + } + } + } +} + +impl<'de> Deserialize<'de> for ParamType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let identifier = String::deserialize(deserializer)?; + ParamType::try_from_type_short(&identifier).map_err(|e| DeError::custom(format!("{e:?}"))) + } +} diff --git a/rust/tw_evm/src/abi/param_type/reader.rs b/rust/tw_evm/src/abi/param_type/reader.rs new file mode 100644 index 00000000000..1e084fbec6c --- /dev/null +++ b/rust/tw_evm/src/abi/param_type/reader.rs @@ -0,0 +1,103 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::non_empty_array::NonZeroLen; +use crate::abi::param_type::constructor::TypeConstructor; +use crate::abi::uint::UintBits; +use crate::abi::{AbiError, AbiErrorKind, AbiResult}; +use std::str::FromStr; + +pub struct Reader; + +impl Reader { + /// Doesn't accept tuple types with specified parameters, e.g `(uint32, address)`. + pub fn parse_type(s: &str) -> AbiResult { + // Array + if let Some(remaining) = s.strip_suffix(']') { + let Some((element_type_str, len_str)) = remaining.rsplit_once('[') else { + return Err(AbiError(AbiErrorKind::Error_invalid_param_type)); + }; + + let element_type = Reader::parse_type::(element_type_str)?; + if let Some(len) = parse_len(len_str)? { + return Ok(T::fixed_array_checked(len, element_type)); + } + return Ok(T::array(element_type)); + } + + let all_alphanumeric = s.chars().all(|ch| ch.is_ascii_alphanumeric()); + if s.is_empty() || !all_alphanumeric { + return Err(AbiError(AbiErrorKind::Error_invalid_param_type)); + } + + if s.contains(['[', ']']) { + return Err(AbiError(AbiErrorKind::Error_invalid_param_type)); + } + + // uint, uint32, ... + if let Some(len_str) = s.strip_prefix("uint") { + let bits = parse_uint_bits(len_str)?.unwrap_or_default(); + return Ok(T::uint_checked(bits)); + } + + // int, int32, ... + if let Some(len_str) = s.strip_prefix("int") { + let bits = parse_uint_bits(len_str)?.unwrap_or_default(); + return Ok(T::int_checked(bits)); + } + + // bytes, bytes32, ... + if let Some(len_str) = s.strip_prefix("bytes") { + if let Some(len) = parse_len(len_str)? { + // Fixed-len bytes. + return Ok(T::fixed_bytes_checked(len)); + } + // Otherwise, dynamic-len bytes. + return Ok(T::bytes()); + } + + // Handle other types. + match s { + "address" => Ok(T::address()), + "bool" => Ok(T::bool()), + "string" => Ok(T::string()), + "tuple" => T::empty_tuple(), + custom => T::custom(custom), + } + } +} + +fn parse_len(len_str: &str) -> AbiResult> { + match parse_usize(len_str)? { + Some(u) => { + let len = NonZeroLen::new(u)?; + Ok(Some(len)) + }, + None => Ok(None), + } +} + +fn parse_uint_bits(bits_str: &str) -> AbiResult> { + match parse_usize(bits_str)? { + Some(u) => { + let bits = UintBits::new(u)?; + Ok(Some(bits)) + }, + None => Ok(None), + } +} + +fn parse_usize(usize_str: &str) -> AbiResult> { + if usize_str.is_empty() { + return Ok(None); + } + if usize_str.starts_with('0') { + return Err(AbiError(AbiErrorKind::Error_invalid_param_type)); + } + usize::from_str(usize_str) + .map(Some) + .map_err(|_| AbiError(AbiErrorKind::Error_invalid_param_type)) +} diff --git a/rust/tw_evm/src/abi/param_type/writer.rs b/rust/tw_evm/src/abi/param_type/writer.rs new file mode 100644 index 00000000000..724a5ed9abe --- /dev/null +++ b/rust/tw_evm/src/abi/param_type/writer.rs @@ -0,0 +1,116 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::param_type::ParamType; + +/// Output formatter for param type. +pub struct Writer; + +impl Writer { + /// Returns string which is a formatted represenation of param. + pub fn write(param: &ParamType) -> String { + Writer::write_for_abi(param, true) + } + + /// If `serialize_tuple_contents` is `true`, tuples will be represented + /// as list of inner types in parens, for example `(int256,bool)`. + /// If it is `false`, tuples will be represented as keyword `tuple`. + pub fn write_for_abi(param: &ParamType, serialize_tuple_contents: bool) -> String { + match param { + ParamType::Address => "address".to_owned(), + ParamType::Bytes => "bytes".to_owned(), + ParamType::FixedBytes { len } => format!("bytes{len}"), + ParamType::Int { bits } => format!("int{bits}"), + ParamType::Uint { bits } => format!("uint{bits}"), + ParamType::Bool => "bool".to_owned(), + ParamType::String => "string".to_owned(), + ParamType::FixedArray { kind, len } => { + format!( + "{}[{len}]", + Writer::write_for_abi(kind, serialize_tuple_contents) + ) + }, + ParamType::Array { ref kind } => { + format!( + "{}[]", + Writer::write_for_abi(kind, serialize_tuple_contents) + ) + }, + ParamType::Tuple { ref params } => { + if serialize_tuple_contents { + let formatted = params + .iter() + .map(|t| Writer::write_for_abi(&t.kind, serialize_tuple_contents)) + .collect::>() + .join(","); + format!("({formatted})") + } else { + "tuple".to_owned() + } + }, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::abi::non_empty_array::NonZeroLen; + use crate::abi::param::Param; + use crate::abi::param_type::constructor::TypeConstructor; + + #[test] + fn test_write_param() { + assert_eq!(Writer::write(&ParamType::Address), "address"); + assert_eq!(Writer::write(&ParamType::Bytes), "bytes"); + assert_eq!( + Writer::write(&ParamType::FixedBytes { + len: NonZeroLen::new(32).unwrap() + }), + "bytes32" + ); + assert_eq!(Writer::write(&ParamType::u256()), "uint256"); + assert_eq!(Writer::write(&ParamType::int(64).unwrap()), "int64"); + assert_eq!(Writer::write(&ParamType::Bool), "bool"); + assert_eq!(Writer::write(&ParamType::String), "string"); + assert_eq!(Writer::write(&ParamType::array(ParamType::Bool)), "bool[]"); + assert_eq!( + Writer::write(&ParamType::fixed_array(2, ParamType::String).unwrap()), + "string[2]" + ); + assert_eq!( + Writer::write(&ParamType::fixed_array(2, ParamType::array(ParamType::Bool)).unwrap()), + "bool[][2]" + ); + assert_eq!( + Writer::write(&ParamType::array(ParamType::Tuple { + params: vec![ + Param::with_type(ParamType::array(ParamType::Tuple { + params: vec![ + Param::with_type(ParamType::int(256).unwrap()), + Param::with_type(ParamType::uint(256).unwrap()) + ] + })), + Param::with_type(ParamType::fixed_bytes(32).unwrap()), + ] + })), + "((int256,uint256)[],bytes32)[]" + ); + + assert_eq!( + Writer::write_for_abi( + &ParamType::array(ParamType::Tuple { + params: vec![ + Param::with_type(ParamType::array(ParamType::i256())), + Param::with_type(ParamType::fixed_bytes(32).unwrap()), + ] + }), + false + ), + "tuple[]" + ); + } +} diff --git a/rust/tw_evm/src/abi/prebuild/erc1155.rs b/rust/tw_evm/src/abi/prebuild/erc1155.rs new file mode 100644 index 00000000000..1a9bf391f98 --- /dev/null +++ b/rust/tw_evm/src/abi/prebuild/erc1155.rs @@ -0,0 +1,42 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::contract::Contract; +use crate::abi::token::Token; +use crate::abi::AbiResult; +use crate::address::Address; +use lazy_static::lazy_static; +use tw_memory::Data; +use tw_number::U256; + +/// Generated via https://remix.ethereum.org +/// Solidity: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.9.2/contracts/token/ERC1155/IERC1155.sol +const ERC1155_ABI: &str = include_str!("resource/erc1155.abi.json"); + +lazy_static! { + static ref ERC1155: Contract = serde_json::from_str(ERC1155_ABI).unwrap(); +} + +pub struct Erc1155; + +impl Erc1155 { + pub fn encode_safe_transfer_from( + from: Address, + to: Address, + token_id: U256, + value: U256, + data: Data, + ) -> AbiResult { + let func = ERC1155.function("safeTransferFrom")?; + func.encode_input(&[ + Token::Address(from), + Token::Address(to), + Token::u256(token_id), + Token::u256(value), + Token::Bytes(data), + ]) + } +} diff --git a/rust/tw_evm/src/abi/prebuild/erc20.rs b/rust/tw_evm/src/abi/prebuild/erc20.rs new file mode 100644 index 00000000000..4d3d042d663 --- /dev/null +++ b/rust/tw_evm/src/abi/prebuild/erc20.rs @@ -0,0 +1,35 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::contract::Contract; +use crate::abi::token::Token; +use crate::abi::AbiResult; +use crate::address::Address; +use lazy_static::lazy_static; +use tw_memory::Data; +use tw_number::U256; + +/// Generated via https://remix.ethereum.org +/// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.9.2/contracts/token/ERC20/IERC20.sol +const ERC20_ABI: &str = include_str!("resource/erc20.abi.json"); + +lazy_static! { + static ref ERC20: Contract = serde_json::from_str(ERC20_ABI).unwrap(); +} + +pub struct Erc20; + +impl Erc20 { + pub fn transfer(recipient: Address, amount: U256) -> AbiResult { + let func = ERC20.function("transfer")?; + func.encode_input(&[Token::Address(recipient), Token::u256(amount)]) + } + + pub fn approve(spender: Address, amount: U256) -> AbiResult { + let func = ERC20.function("approve")?; + func.encode_input(&[Token::Address(spender), Token::u256(amount)]) + } +} diff --git a/rust/tw_evm/src/abi/prebuild/erc4337.rs b/rust/tw_evm/src/abi/prebuild/erc4337.rs new file mode 100644 index 00000000000..bd3a3977204 --- /dev/null +++ b/rust/tw_evm/src/abi/prebuild/erc4337.rs @@ -0,0 +1,71 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::contract::Contract; +use crate::abi::param_type::ParamType; +use crate::abi::token::Token; +use crate::abi::AbiResult; +use crate::address::Address; +use lazy_static::lazy_static; +use tw_memory::Data; +use tw_number::U256; + +/// Generated via https://remix.ethereum.org +/// https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/samples/SimpleAccount.sol +const ERC4337_SIMPLE_ACCOUNT_ABI: &str = include_str!("resource/erc4337.simple_account.abi.json"); + +lazy_static! { + static ref ERC4337_SIMPLE_ACCOUNT: Contract = + serde_json::from_str(ERC4337_SIMPLE_ACCOUNT_ABI).unwrap(); +} + +pub struct ExecuteArgs { + pub to: Address, + pub value: U256, + pub data: Data, +} + +pub struct Erc4337SimpleAccount; + +impl Erc4337SimpleAccount { + pub fn encode_execute(args: ExecuteArgs) -> AbiResult { + let func = ERC4337_SIMPLE_ACCOUNT.function("execute")?; + func.encode_input(&[ + Token::Address(args.to), + Token::u256(args.value), + Token::Bytes(args.data), + ]) + } + + pub fn encode_execute_batch(args: I) -> AbiResult + where + I: IntoIterator, + { + let func = ERC4337_SIMPLE_ACCOUNT.function("executeBatch")?; + + let args = args.into_iter(); + let capacity = { + let (lower, upper) = args.size_hint(); + upper.unwrap_or(lower) + }; + + let mut addresses = Vec::with_capacity(capacity); + let mut values = Vec::with_capacity(capacity); + let mut datas = Vec::with_capacity(capacity); + + for arg in args { + addresses.push(Token::Address(arg.to)); + values.push(Token::u256(arg.value)); + datas.push(Token::Bytes(arg.data)); + } + + func.encode_input(&[ + Token::array(ParamType::Address, addresses), + Token::array(ParamType::u256(), values), + Token::array(ParamType::Bytes, datas), + ]) + } +} diff --git a/rust/tw_evm/src/abi/prebuild/erc721.rs b/rust/tw_evm/src/abi/prebuild/erc721.rs new file mode 100644 index 00000000000..e58ac7d0c1b --- /dev/null +++ b/rust/tw_evm/src/abi/prebuild/erc721.rs @@ -0,0 +1,34 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::contract::Contract; +use crate::abi::token::Token; +use crate::abi::AbiResult; +use crate::address::Address; +use lazy_static::lazy_static; +use tw_memory::Data; +use tw_number::U256; + +/// Generated via https://remix.ethereum.org +/// Solidity: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.9.2/contracts/token/ERC721/IERC721.sol +const ERC721_ABI: &str = include_str!("resource/erc721.abi.json"); + +lazy_static! { + static ref ERC721: Contract = serde_json::from_str(ERC721_ABI).unwrap(); +} + +pub struct Erc721; + +impl Erc721 { + pub fn encode_transfer_from(from: Address, to: Address, token_id: U256) -> AbiResult { + let func = ERC721.function("transferFrom")?; + func.encode_input(&[ + Token::Address(from), + Token::Address(to), + Token::u256(token_id), + ]) + } +} diff --git a/rust/tw_evm/src/abi/prebuild/mod.rs b/rust/tw_evm/src/abi/prebuild/mod.rs new file mode 100644 index 00000000000..4bbcf59535b --- /dev/null +++ b/rust/tw_evm/src/abi/prebuild/mod.rs @@ -0,0 +1,10 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod erc1155; +pub mod erc20; +pub mod erc4337; +pub mod erc721; diff --git a/rust/tw_evm/src/abi/prebuild/resource/erc1155.abi.json b/rust/tw_evm/src/abi/prebuild/resource/erc1155.abi.json new file mode 100644 index 00000000000..db16ea84248 --- /dev/null +++ b/rust/tw_evm/src/abi/prebuild/resource/erc1155.abi.json @@ -0,0 +1,295 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + } + ], + "name": "TransferBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "TransferSingle", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "value", + "type": "string" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "URI", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "accounts", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + } + ], + "name": "balanceOfBatch", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeBatchTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/rust/tw_evm/src/abi/prebuild/resource/erc20.abi.json b/rust/tw_evm/src/abi/prebuild/resource/erc20.abi.json new file mode 100644 index 00000000000..06e0d938333 --- /dev/null +++ b/rust/tw_evm/src/abi/prebuild/resource/erc20.abi.json @@ -0,0 +1,185 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/rust/tw_evm/src/abi/prebuild/resource/erc4337.simple_account.abi.json b/rust/tw_evm/src/abi/prebuild/resource/erc4337.simple_account.abi.json new file mode 100644 index 00000000000..c965a66a789 --- /dev/null +++ b/rust/tw_evm/src/abi/prebuild/resource/erc4337.simple_account.abi.json @@ -0,0 +1,529 @@ +[ + { + "inputs": [ + { + "internalType": "contract IEntryPoint", + "name": "anEntryPoint", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beacon", + "type": "address" + } + ], + "name": "BeaconUpgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IEntryPoint", + "name": "entryPoint", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "SimpleAccountInitialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [], + "name": "addDeposit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "entryPoint", + "outputs": [ + { + "internalType": "contract IEntryPoint", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "dest", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "func", + "type": "bytes" + } + ], + "name": "execute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "dest", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "value", + "type": "uint256[]" + }, + { + "internalType": "bytes[]", + "name": "func", + "type": "bytes[]" + } + ], + "name": "executeBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getDeposit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "anOwner", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155BatchReceived", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC721Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proxiableUUID", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "tokensReceived", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "initCode", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "callData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "callGasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "verificationGasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "preVerificationGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxPriorityFeePerGas", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "paymasterAndData", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct UserOperation", + "name": "userOp", + "type": "tuple" + }, + { + "internalType": "bytes32", + "name": "userOpHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "missingAccountFunds", + "type": "uint256" + } + ], + "name": "validateUserOp", + "outputs": [ + { + "internalType": "uint256", + "name": "validationData", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "withdrawAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdrawDepositTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] \ No newline at end of file diff --git a/rust/tw_evm/src/abi/prebuild/resource/erc721.abi.json b/rust/tw_evm/src/abi/prebuild/resource/erc721.abi.json new file mode 100644 index 00000000000..d0526f5a9d5 --- /dev/null +++ b/rust/tw_evm/src/abi/prebuild/resource/erc721.abi.json @@ -0,0 +1,287 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/rust/tw_evm/src/abi/signature.rs b/rust/tw_evm/src/abi/signature.rs new file mode 100644 index 00000000000..1cc9da4750f --- /dev/null +++ b/rust/tw_evm/src/abi/signature.rs @@ -0,0 +1,35 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::param_type::ParamType; +use tw_hash::sha3::keccak256; +use tw_hash::{H256, H32}; + +/// Returns the first four bytes of the Keccak-256 hash of the signature of the given params. +pub fn short_signature(name: &str, params: &[ParamType]) -> H32 { + let mut result = H32::default(); + fill_signature(name, params, result.as_mut()); + result +} + +/// Returns the full Keccak-256 hash of the signature of the given params. +pub fn long_signature(name: &str, params: &[ParamType]) -> H256 { + let mut result = H256::default(); + fill_signature(name, params, result.as_mut()); + result +} + +fn fill_signature(name: &str, params: &[ParamType], result: &mut [u8]) { + let types = params + .iter() + .map(ParamType::to_type_long) + .collect::>() + .join(","); + + let data: Vec = From::from(format!("{name}({types})").as_str()); + + result.copy_from_slice(&keccak256(&data)[..result.len()]) +} diff --git a/rust/tw_evm/src/abi/token.rs b/rust/tw_evm/src/abi/token.rs new file mode 100644 index 00000000000..3d52d080d98 --- /dev/null +++ b/rust/tw_evm/src/abi/token.rs @@ -0,0 +1,221 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::non_empty_array::{NonEmptyArray, NonEmptyBytes}; +use crate::abi::param_token::NamedToken; +use crate::abi::param_type::ParamType; +use crate::abi::uint::UintBits; +use crate::abi::AbiResult; +use crate::address::Address; +use serde::{Serialize, Serializer}; +use std::fmt; +use tw_encoding::hex::ToHex; +use tw_memory::Data; +use tw_number::{I256, U256}; + +#[derive(Debug, Clone, PartialEq)] +pub enum Token { + /// Address. + /// + /// solidity name: address + /// Encoded to left padded [0u8; 32]. + Address(Address), + /// Vector of bytes with known size. + /// + /// solidity name eg.: bytes8, bytes32, bytes64, bytes1024 + /// Encoded to right padded [0u8; ((N + 31) / 32) * 32]. + FixedBytes(NonEmptyBytes), + /// Vector of bytes of unknown size. + /// + /// solidity name: bytes + /// Encoded in two parts. + /// Init part: offset of 'closing part`. + /// Closing part: encoded length followed by encoded right padded bytes. + Bytes(Data), + /// Signed integer. + /// + /// solidity name: int + Int { int: I256, bits: UintBits }, + /// Unsigned integer. + /// + /// solidity name: uint + Uint { uint: U256, bits: UintBits }, + /// Boolean value. + /// + /// solidity name: bool + /// Encoded as left padded [0u8; 32], where last bit represents boolean value. + Bool(bool), + /// String. + /// + /// solidity name: string + /// Encoded in the same way as bytes. Must be utf8 compliant. + String(String), + /// Array with known size. + /// + /// solidity name eg.: int[3], bool[3], address[][8] + /// Encoding of array is equal to encoding of consecutive elements of array. + FixedArray { + arr: NonEmptyArray, + kind: ParamType, + }, + /// Array of params with unknown size. + /// + /// solidity name eg. int[], bool[], address[5][] + Array { arr: Vec, kind: ParamType }, + /// Tuple of params of variable types. + /// + /// solidity name: tuple + Tuple { params: Vec }, +} + +impl fmt::Display for Token { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// Formats the given element of a tuple or array. + fn format_sequence_token(token: &Token) -> String { + // Check if the parameter value should be quoted. + match token { + Token::Address(_) | Token::FixedBytes(_) | Token::Bytes(_) | Token::String(_) => { + format!(r#""{token}""#) + }, + _ => format!("{token}"), + } + } + + match self { + Token::Address(addr) => write!(f, "{addr}"), + Token::Bytes(bytes) => { + write!(f, "{}", bytes.to_hex_prefixed()) + }, + Token::FixedBytes(bytes) => { + write!(f, "{}", bytes.to_hex_prefixed()) + }, + Token::Int { int, .. } => write!(f, "{int}"), + Token::Uint { uint, .. } => write!(f, "{uint}"), + Token::Bool(bool) => write!(f, "{bool}"), + Token::String(str) => write!(f, "{str}"), + Token::Array { arr, .. } => { + let s = arr + .iter() + .map(format_sequence_token) + .collect::>() + .join(","); + write!(f, "[{s}]") + }, + Token::FixedArray { arr, .. } => { + let s = arr + .iter() + .map(format_sequence_token) + .collect::>() + .join(","); + write!(f, "[{s}]") + }, + Token::Tuple { params } => { + let s = params + .iter() + .map(|param| format_sequence_token(¶m.value)) + .collect::>() + .join(","); + write!(f, "({s})") + }, + } + } +} + +impl Serialize for Token { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Token::Address(addr) => addr.serialize(serializer), + Token::FixedBytes(bytes) => bytes.to_hex_prefixed().serialize(serializer), + Token::Bytes(bytes) => bytes.to_hex_prefixed().serialize(serializer), + Token::Int { int, .. } => int.as_decimal_str(serializer), + Token::Uint { uint, .. } => uint.as_decimal_str(serializer), + Token::Bool(bool) => bool.serialize(serializer), + Token::String(str) => str.serialize(serializer), + Token::FixedArray { arr, .. } => arr.serialize(serializer), + Token::Array { arr, .. } => arr.serialize(serializer), + Token::Tuple { params } => params.serialize(serializer), + } + } +} + +impl Token { + pub fn u256(uint: U256) -> Token { + Token::Uint { + bits: UintBits::default(), + uint, + } + } + + pub fn i256(int: I256) -> Token { + Token::Int { + bits: UintBits::default(), + int, + } + } + + pub fn uint>(bits: usize, uint: UInt) -> AbiResult { + let bits = UintBits::new(bits)?; + Ok(Token::Uint { + uint: uint.into(), + bits, + }) + } + + pub fn int>(bits: usize, int: Int) -> AbiResult { + let bits = UintBits::new(bits)?; + Ok(Token::Int { + int: int.into(), + bits, + }) + } + + pub fn array(element_kind: ParamType, elements: Vec) -> Token { + Token::Array { + kind: element_kind, + arr: elements, + } + } + + pub fn type_short(&self) -> String { + self.to_param_type().to_type_short() + } + + /// Check if the token is a dynamic type resulting in prefixed encoding. + pub fn is_dynamic(&self) -> bool { + match self { + Token::Bytes(_) | Token::String(_) | Token::Array { .. } => true, + Token::FixedArray { arr, .. } => arr.iter().any(|token| token.is_dynamic()), + Token::Tuple { params } => params.iter().any(|token| token.value.is_dynamic()), + _ => false, + } + } + + pub(crate) fn to_param_type(&self) -> ParamType { + match self { + Token::Address(_) => ParamType::Address, + Token::Bytes(_) => ParamType::Bytes, + Token::Int { bits, .. } => ParamType::Int { bits: *bits }, + Token::Uint { bits, .. } => ParamType::Uint { bits: *bits }, + Token::Bool(_) => ParamType::Bool, + Token::String(_) => ParamType::String, + Token::Array { kind, .. } => ParamType::Array { + kind: Box::new(kind.clone()), + }, + Token::FixedBytes(bytes) => ParamType::FixedBytes { len: bytes.len() }, + Token::FixedArray { arr, kind } => ParamType::FixedArray { + kind: Box::new(kind.clone()), + len: arr.len(), + }, + Token::Tuple { params } => { + let params = params.iter().map(|param| param.to_param()).collect(); + ParamType::Tuple { params } + }, + } + } +} diff --git a/rust/tw_evm/src/abi/uint.rs b/rust/tw_evm/src/abi/uint.rs new file mode 100644 index 00000000000..3b5f6021432 --- /dev/null +++ b/rust/tw_evm/src/abi/uint.rs @@ -0,0 +1,55 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::{AbiError, AbiErrorKind, AbiResult}; +use std::fmt; +use tw_number::U256; + +#[derive(Clone, Copy, PartialEq)] +pub struct UintBits(usize); + +impl Default for UintBits { + fn default() -> Self { + UintBits::new(U256::BITS).expect("U256::BITS must be a valid number of bits") + } +} + +impl UintBits { + pub fn new(bits: usize) -> AbiResult { + check_uint_bits(bits)?; + Ok(UintBits(bits)) + } + + pub fn get(&self) -> usize { + self.0 + } +} + +impl fmt::Display for UintBits { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl fmt::Debug for UintBits { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl From for usize { + fn from(value: UintBits) -> Self { + value.0 + } +} + +// https://docs.soliditylang.org/en/latest/abi-spec.html#types +pub fn check_uint_bits(bits: usize) -> AbiResult<()> { + if bits % 8 != 0 || bits == 0 || bits > 256 { + return Err(AbiError(AbiErrorKind::Error_invalid_uint_value)); + } + Ok(()) +} diff --git a/rust/tw_evm/src/address.rs b/rust/tw_evm/src/address.rs new file mode 100644 index 00000000000..c53887b5662 --- /dev/null +++ b/rust/tw_evm/src/address.rs @@ -0,0 +1,223 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde::de::Error as SerdeError; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt::{Display, Formatter}; +use std::ops::{RangeFrom, RangeInclusive}; +use std::str::FromStr; +use tw_coin_entry::coin_entry::CoinAddress; +use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_encoding::hex; +use tw_hash::{sha3::keccak256, H160, H256}; +use tw_keypair::ecdsa::secp256k1; +use tw_memory::Data; + +pub trait EvmAddress: FromStr + Into
{ + /// Tries to parse an address from the string representation. + /// Returns `Ok(None)` if the given `s` string is empty. + #[inline] + fn from_str_optional(s: &str) -> AddressResult> { + if s.is_empty() { + return Ok(None); + } + + Self::from_str(s).map(Some) + } +} + +/// Represents an Ethereum address. +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct Address { + bytes: H160, +} + +/// cbindgen:ignore +impl Address { + pub const LEN: usize = 20; + + /// Initializes an address with a `secp256k1` public key. + pub fn with_secp256k1_pubkey(pubkey: &secp256k1::PublicKey) -> Address { + /// `keccak256` returns 32 bytes, but Ethereum address is the last 20 bytes of the hash. + const ADDRESS_HASH_STARTS_AT: usize = H256::len() - H160::len(); + const ADDRESS_HASH_RANGE: RangeFrom = ADDRESS_HASH_STARTS_AT..; + + let pubkey_bytes = pubkey.uncompressed_without_prefix(); + let hash = keccak256(pubkey_bytes.as_slice()); + assert_eq!(hash.len(), H256::len()); + + let bytes = H160::try_from(&hash[ADDRESS_HASH_RANGE]).expect("Expected 20 byte array"); + + Address { bytes } + } + + /// Constructs an address from the 20-length byte array. + pub fn from_bytes(bytes: H160) -> Address { + Address { bytes } + } + + /// Displays the address in mixed-case checksum form + /// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md + fn into_checksum_address(self) -> String { + const UPPER_RANGE_1: RangeInclusive = '8'..='9'; + const UPPER_RANGE_2: RangeInclusive = 'a'..='f'; + + let prefixed = false; + let addr_hex = hex::encode(self.bytes, prefixed); + let addr_hash = hex::encode(keccak256(addr_hex.as_bytes()), prefixed); + + let payload_chars = addr_hex.chars().zip(addr_hash.chars()).map(|(a, h)| { + if a.is_ascii_digit() { + a + } else if UPPER_RANGE_1.contains(&h) || UPPER_RANGE_2.contains(&h) { + a.to_ascii_uppercase() + } else { + a.to_ascii_lowercase() + } + }); + + "0x".chars().chain(payload_chars).collect() + } + + /// Returns bytes of the address. + #[inline] + pub fn bytes(&self) -> H160 { + self.bytes + } + + /// Returns bytes as a slice of the address. + #[inline] + pub fn as_slice(&self) -> &[u8] { + self.bytes.as_slice() + } +} + +impl CoinAddress for Address { + #[inline] + fn data(&self) -> Data { + self.bytes.to_vec() + } +} + +impl FromStr for Address { + type Err = AddressError; + + fn from_str(s: &str) -> Result { + let addr_hex = s.strip_prefix("0x").ok_or(AddressError::MissingPrefix)?; + let addr_hash = H160::from_str(addr_hex).map_err(|_| AddressError::FromHexError)?; + Ok(Address { bytes: addr_hash }) + } +} + +impl EvmAddress for Address {} + +/// Implement `str` -> `PrivateKey` conversion for test purposes. +impl From<&'static str> for Address { + #[inline] + fn from(addr: &'static str) -> Self { + Address::from_str(addr).expect("Expected a valid Ethereum address") + } +} + +impl Display for Address { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.into_checksum_address()) + } +} + +impl<'de> Deserialize<'de> for Address { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s: &str = Deserialize::deserialize(deserializer)?; + Address::from_str(s).map_err(|e| SerdeError::custom(format!("{e:?}"))) + } +} + +impl Serialize for Address { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tw_keypair::ecdsa::secp256k1::PrivateKey; + + struct Eip55Test { + test: &'static str, + mixedcase: &'static str, + } + + #[test] + fn test_to_from_string() { + let tests = [ + Eip55Test { + test: "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", + mixedcase: "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", + }, + Eip55Test { + test: "0x5AAEB6053F3E94C9b9A09f33669435E7Ef1BEAED", + mixedcase: "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", + }, + Eip55Test { + test: "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", + mixedcase: "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", + }, + Eip55Test { + test: "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", + mixedcase: "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", + }, + Eip55Test { + test: "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", + mixedcase: "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", + }, + ]; + + for test in tests { + let addr: Address = test.test.parse().unwrap(); + + let expected_payload = H160::from(test.test); + assert_eq!(addr.bytes, expected_payload); + + let mixed = addr.to_string(); + assert_eq!(mixed, test.mixedcase); + } + } + + #[test] + fn test_from_string_invalid() { + let tests = [ + "abc", + "0xabc", + "0xaaeb60f3e94c9b9a09f33669435e7ef1beaed", + "fB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", + ]; + for test in tests { + Address::from_str(test).unwrap_err(); + } + } + + #[test] + fn test_from_public_key() { + let private = PrivateKey::try_from( + "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", + ) + .unwrap(); + let public = private.public(); + let addr = Address::with_secp256k1_pubkey(&public); + assert_eq!( + addr.to_string(), + "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309" + ); + } +} diff --git a/src/Ethereum/ABI/ParamBase.cpp b/rust/tw_evm/src/evm_context.rs similarity index 50% rename from src/Ethereum/ABI/ParamBase.cpp rename to rust/tw_evm/src/evm_context.rs index 294bb09a452..83113770bb6 100644 --- a/src/Ethereum/ABI/ParamBase.cpp +++ b/rust/tw_evm/src/evm_context.rs @@ -4,15 +4,16 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "ParamBase.h" +use crate::address::{Address, EvmAddress}; -namespace TW::Ethereum::ABI { - -// Default implementation for simple types: return encoded value (32 bytes) -Data ParamBase::hashStruct() const { - Data encoded; - encode(encoded); - return encoded; +/// EVM compatible chain specific. +pub trait EvmContext { + type Address: EvmAddress; } -} // namespace TW::Ethereum::ABI +#[derive(Default)] +pub struct StandardEvmContext; + +impl EvmContext for StandardEvmContext { + type Address = Address; +} diff --git a/rust/tw_evm/src/evm_entry.rs b/rust/tw_evm/src/evm_entry.rs new file mode 100644 index 00000000000..7ecbba2a885 --- /dev/null +++ b/rust/tw_evm/src/evm_entry.rs @@ -0,0 +1,123 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::evm_context::EvmContext; +use crate::modules::abi_encoder::AbiEncoder; +use crate::modules::rlp_encoder::RlpEncoder; +use tw_memory::Data; +use tw_proto::EthereumAbi::Proto as AbiProto; +use tw_proto::EthereumRlp::Proto as RlpProto; +use tw_proto::{deserialize, serialize, ProtoResult}; + +/// An EVM-compatible chain entry. +pub trait EvmEntry { + type Context: EvmContext; + + /// Encodes an item or a list of items as Eth RLP binary format. + #[inline] + fn encode_rlp(input: RlpProto::EncodingInput<'_>) -> RlpProto::EncodingOutput<'static> { + RlpEncoder::::encode_with_proto(input) + } + + /// Decodes function call data to human readable json format, according to input abi json. + #[inline] + fn decode_abi_contract_call( + input: AbiProto::ContractCallDecodingInput<'_>, + ) -> AbiProto::ContractCallDecodingOutput<'static> { + AbiEncoder::::decode_contract_call(input) + } + + /// Decodes a function input or output data according to a given ABI. + #[inline] + fn decode_abi_params( + input: AbiProto::ParamsDecodingInput<'_>, + ) -> AbiProto::ParamsDecodingOutput { + AbiEncoder::::decode_params(input) + } + + /// Decodes an Eth ABI value according to a given type. + #[inline] + fn decode_abi_value( + input: AbiProto::ValueDecodingInput<'_>, + ) -> AbiProto::ValueDecodingOutput<'static> { + AbiEncoder::::decode_value(input) + } + + /// Returns the function type signature, of the form "baz(int32,uint256)". + #[inline] + fn get_abi_function_signature(input: AbiProto::FunctionGetTypeInput<'_>) -> String { + AbiEncoder::::get_function_signature(input) + } + + // Encodes function inputs to Eth ABI binary. + #[inline] + fn encode_abi_function( + input: AbiProto::FunctionEncodingInput<'_>, + ) -> AbiProto::FunctionEncodingOutput<'static> { + AbiEncoder::::encode_contract_call(input) + } +} + +/// The [`EvmEntry`] trait extension. +pub trait EvmEntryExt { + /// Encodes an item or a list of items as Eth RLP binary format. + fn encode_rlp(&self, input: &[u8]) -> ProtoResult; + + /// Decodes function call data to human readable json format, according to input abi json. + fn decode_abi_contract_call(&self, input: &[u8]) -> ProtoResult; + + /// Decodes a function input or output data according to a given ABI. + fn decode_abi_params(&self, input: &[u8]) -> ProtoResult; + + /// Returns the function type signature, of the form "baz(int32,uint256)". + fn get_abi_function_signature(&self, input: &[u8]) -> ProtoResult; + + /// Encodes function inputs to Eth ABI binary. + fn encode_abi_function(&self, input: &[u8]) -> ProtoResult; + + /// Decodes an Eth ABI value according to a given type. + fn decode_abi_value(&self, input: &[u8]) -> ProtoResult; +} + +impl EvmEntryExt for T +where + T: EvmEntry, +{ + fn encode_rlp(&self, input: &[u8]) -> ProtoResult { + let input = deserialize(input)?; + let output = ::encode_rlp(input); + serialize(&output) + } + + fn decode_abi_contract_call(&self, input: &[u8]) -> ProtoResult { + let input = deserialize(input)?; + let output = ::decode_abi_contract_call(input); + serialize(&output) + } + + fn decode_abi_params(&self, input: &[u8]) -> ProtoResult { + let input = deserialize(input)?; + let output = ::decode_abi_params(input); + serialize(&output) + } + + fn get_abi_function_signature(&self, input: &[u8]) -> ProtoResult { + let input = deserialize(input)?; + Ok(::get_abi_function_signature(input)) + } + + fn encode_abi_function(&self, input: &[u8]) -> ProtoResult { + let input = deserialize(input)?; + let output = ::encode_abi_function(input); + serialize(&output) + } + + fn decode_abi_value(&self, input: &[u8]) -> ProtoResult { + let input = deserialize(input)?; + let output = ::decode_abi_value(input); + serialize(&output) + } +} diff --git a/rust/tw_evm/src/lib.rs b/rust/tw_evm/src/lib.rs new file mode 100644 index 00000000000..cacd3c67e82 --- /dev/null +++ b/rust/tw_evm/src/lib.rs @@ -0,0 +1,15 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod abi; +pub mod address; +pub mod evm_context; +pub mod evm_entry; +pub mod message; +pub mod modules; +pub mod rlp; +pub mod signature; +pub mod transaction; diff --git a/rust/tw_evm/src/message/eip191.rs b/rust/tw_evm/src/message/eip191.rs new file mode 100644 index 00000000000..4ff12895222 --- /dev/null +++ b/rust/tw_evm/src/message/eip191.rs @@ -0,0 +1,44 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::message::{EthMessage, MessageSigningResult}; +use tw_hash::sha3::keccak256; +use tw_hash::H256; + +/// cbindgen:ignore +pub const ETHEREUM_PREFIX: u8 = 0x19; +/// cbindgen:ignore +pub const ETHEREUM_MESSAGE_PREFIX: &str = "Ethereum Signed Message:\n"; + +pub struct Eip191Message { + user_message: String, +} + +impl Eip191Message { + pub fn new>(user_message: S) -> Eip191Message { + Eip191Message { + user_message: user_message.into(), + } + } + + fn data_to_sign(&self) -> Vec { + let mut data = Vec::with_capacity(self.user_message.len() * 2); + + data.push(ETHEREUM_PREFIX); + data.extend_from_slice(ETHEREUM_MESSAGE_PREFIX.as_bytes()); + data.extend_from_slice(self.user_message.len().to_string().as_bytes()); + data.extend_from_slice(self.user_message.as_bytes()); + + data + } +} + +impl EthMessage for Eip191Message { + fn hash(&self) -> MessageSigningResult { + let hash = keccak256(&self.data_to_sign()); + Ok(H256::try_from(hash.as_slice()).expect("Expected 32 byte hash")) + } +} diff --git a/rust/tw_evm/src/message/eip712/eip712_message.rs b/rust/tw_evm/src/message/eip712/eip712_message.rs new file mode 100644 index 00000000000..7be57299689 --- /dev/null +++ b/rust/tw_evm/src/message/eip712/eip712_message.rs @@ -0,0 +1,405 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::encode::encode_tokens; +use crate::abi::non_empty_array::NonEmptyBytes; +use crate::abi::token::Token; +use crate::address::Address; +use crate::message::eip712::property::{Property, PropertyType}; +use crate::message::{EthMessage, MessageSigningError, MessageSigningResult}; +use itertools::Itertools; +use serde::Deserialize; +use serde_json::Value as Json; +use std::collections::HashMap; +use std::str::FromStr; +use tw_encoding::hex::DecodeHex; +use tw_hash::sha3::keccak256; +use tw_hash::{H160, H256}; +use tw_memory::Data; +use tw_number::{I256, U256}; + +/// EIP-191 compliant. +/// cbindgen:ignore +const PREFIX: &[u8; 2] = b"\x19\x01"; +/// cbindgen:ignore +const EIP712_DOMAIN: &str = "EIP712Domain"; + +type CustomTypes = HashMap>; + +#[derive(Debug, Deserialize)] +pub struct Eip712Message { + types: CustomTypes, + domain: Json, + #[serde(rename = "primaryType")] + primary_type: String, + message: Json, +} + +impl Eip712Message { + /// Tries to construct an EIP712 message from the given string. + pub fn new>(message_to_sign: S) -> MessageSigningResult { + let eip712_msg: Eip712Message = serde_json::from_str(message_to_sign.as_ref()) + .map_err(|_| MessageSigningError::TypeValueMismatch)?; + + // Check if the given message is actually EIP712. + if !eip712_msg.types.contains_key(EIP712_DOMAIN) { + return Err(MessageSigningError::TypeValueMismatch); + } + Ok(eip712_msg) + } + + pub fn new_checked>( + message_to_sign: S, + expected_chain_id: U256, + ) -> MessageSigningResult { + let msg = Eip712Message::new(message_to_sign)?; + + // Check if `domain.chainId` is expected. + let chain_id_value = msg.domain["chainId"].clone(); + let chain_id = U256::from_u64_or_decimal_str(chain_id_value) + .map_err(|_| MessageSigningError::TypeValueMismatch)?; + if chain_id != expected_chain_id { + return Err(MessageSigningError::InvalidChainId); + } + + Ok(msg) + } +} + +impl EthMessage for Eip712Message { + fn hash(&self) -> MessageSigningResult { + let domain_hash = encode_data( + &self.types, + PropertyType::Custom(EIP712_DOMAIN.to_string()), + &self.domain, + )?; + let primary_data_hash = encode_data( + &self.types, + PropertyType::Custom(self.primary_type.clone()), + &self.message, + )?; + + let concat = [ + PREFIX.as_slice(), + domain_hash.as_slice(), + primary_data_hash.as_slice(), + ] + .concat(); + + let hash_data = keccak256(&concat); + Ok(H256::try_from(hash_data.as_slice()).expect("Expected 32-byte hash")) + } +} + +fn encode_data( + custom_types: &CustomTypes, + data_type: PropertyType, + data: &Json, +) -> MessageSigningResult> { + match data_type { + PropertyType::Bool => encode_bool(data), + PropertyType::String => encode_string(data), + PropertyType::Int => encode_i256(data), + PropertyType::Uint => encode_u256(data), + PropertyType::Address => encode_address(data), + PropertyType::FixBytes { len } => encode_fix_bytes(data, len.get()), + PropertyType::Bytes => encode_bytes(data), + PropertyType::Custom(custom) => encode_custom(custom_types, &custom, data), + PropertyType::Array(element_type) => encode_array(custom_types, *element_type, data, None), + PropertyType::FixArray { len, element_type } => { + encode_array(custom_types, *element_type, data, Some(len.get())) + }, + } +} + +fn encode_bool(value: &Json) -> MessageSigningResult { + let bin = value + .as_bool() + .ok_or(MessageSigningError::InvalidParameterValue)?; + Ok(encode_tokens(&[Token::Bool(bin)])) +} + +fn encode_string(value: &Json) -> MessageSigningResult { + let string = value + .as_str() + .ok_or(MessageSigningError::InvalidParameterValue)?; + let hash = keccak256(string.as_bytes()); + let checked_bytes = NonEmptyBytes::new(hash).expect("`hash` must not be empty"); + Ok(encode_tokens(&[Token::FixedBytes(checked_bytes)])) +} + +fn encode_u256(value: &Json) -> MessageSigningResult { + let uint = U256::from_u64_or_decimal_str(value.clone()) + .map_err(|_| MessageSigningError::InvalidParameterValue)?; + Ok(encode_tokens(&[Token::u256(uint)])) +} + +fn encode_i256(value: &Json) -> MessageSigningResult { + let int = I256::from_i64_or_decimal_str(value.clone()) + .map_err(|_| MessageSigningError::InvalidParameterValue)?; + Ok(encode_tokens(&[Token::i256(int)])) +} + +fn encode_address(value: &Json) -> MessageSigningResult { + let addr_str = value + .as_str() + .ok_or(MessageSigningError::InvalidParameterValue)?; + // H160 doesn't require the string to be `0x` prefixed. + let addr_data = + H160::from_str(addr_str).map_err(|_| MessageSigningError::InvalidParameterValue)?; + let addr = Address::from_bytes(addr_data); + Ok(encode_tokens(&[Token::Address(addr)])) +} + +fn encode_fix_bytes(value: &Json, expected_len: usize) -> MessageSigningResult { + let str = value + .as_str() + .ok_or(MessageSigningError::InvalidParameterValue)?; + let fix_bytes = str + .decode_hex() + .map_err(|_| MessageSigningError::InvalidParameterValue)?; + if fix_bytes.len() != expected_len { + return Err(MessageSigningError::TypeValueMismatch); + } + let checked_bytes = + NonEmptyBytes::new(fix_bytes).map_err(|_| MessageSigningError::InvalidParameterValue)?; + Ok(encode_tokens(&[Token::FixedBytes(checked_bytes)])) +} + +fn encode_bytes(value: &Json) -> MessageSigningResult { + let str = value + .as_str() + .ok_or(MessageSigningError::InvalidParameterValue)?; + let bytes = str + .decode_hex() + .map_err(|_| MessageSigningError::InvalidParameterValue)?; + let hash = keccak256(&bytes); + Ok(encode_tokens(&[Token::Bytes(hash)])) +} + +fn encode_array( + custom_types: &CustomTypes, + element_type: PropertyType, + data: &Json, + expected_len: Option, +) -> MessageSigningResult { + let elements = data + .as_array() + .ok_or(MessageSigningError::InvalidParameterValue)?; + + // Check if the type definition actually matches the length of items to be encoded. + if expected_len.is_some() && Some(elements.len()) != expected_len { + return Err(MessageSigningError::TypeValueMismatch)?; + } + + let mut encoded_items = vec![]; + for item in elements { + let mut encoded = encode_data(custom_types, element_type.clone(), item)?; + encoded_items.append(&mut encoded); + } + + Ok(keccak256(&encoded_items)) +} + +fn encode_custom( + custom_types: &CustomTypes, + data_ident: &str, + data: &Json, +) -> MessageSigningResult { + let data_properties = custom_types + .get(data_ident) + .ok_or(MessageSigningError::TypeValueMismatch)?; + + let type_hash = encode_custom_type::type_hash(data_ident, custom_types)?; + let checked_bytes = + NonEmptyBytes::new(type_hash).map_err(|_| MessageSigningError::InvalidParameterValue)?; + let mut encoded_tokens = encode_tokens(&[Token::FixedBytes(checked_bytes)]); + + for field in data_properties.iter() { + let field_value = &data[&field.name]; + let field_property = PropertyType::from_str(&field.property_type)?; + let mut encoded = encode_data(custom_types, field_property, field_value)?; + encoded_tokens.append(&mut encoded); + } + + Ok(keccak256(&encoded_tokens)) +} + +mod encode_custom_type { + use super::*; + use std::collections::HashSet; + + pub(super) fn type_hash( + data_type: &str, + custom_types: &CustomTypes, + ) -> MessageSigningResult { + let encoded_type = encode_type(custom_types, data_type)?; + Ok(keccak256(encoded_type.as_bytes())) + } + + pub(super) fn encode_type( + custom_types: &CustomTypes, + data_type: &str, + ) -> MessageSigningResult { + let deps = { + let mut temp = build_dependencies(data_type, custom_types) + .ok_or(MessageSigningError::TypeValueMismatch)?; + temp.remove(data_type); + let mut temp = temp.into_iter().collect::>(); + temp.sort_unstable(); + temp.insert(0, data_type); + temp + }; + + let encoded = deps + .into_iter() + .filter_map(|dep| { + custom_types.get(dep).map(|field_types| { + let types = field_types + .iter() + .map(|value| format!("{} {}", value.property_type, value.name)) + .join(","); + format!("{}({})", dep, types) + }) + }) + .collect::>() + .concat(); + Ok(encoded) + } + + /// Given a type and the set of custom types. + /// Returns a `HashSet` of dependent types of the given type. + pub(super) fn build_dependencies<'a>( + data_type: &'a str, + custom_types: &'a CustomTypes, + ) -> Option> { + custom_types.get(data_type)?; + + let mut types_stack = Vec::new(); + types_stack.push(data_type); + let mut deps = HashSet::new(); + + while let Some(item) = types_stack.pop() { + if let Some(fields) = custom_types.get(item) { + deps.insert(item); + + for field in fields.iter() { + // check if this field is an array type + let field_type = if let Some(index) = field.property_type.find('[') { + &field.property_type[..index] + } else { + &field.property_type + }; + // Seen this type before? or not a custom type - skip + if !deps.contains(field_type) || custom_types.contains_key(field_type) { + types_stack.push(field_type); + } + } + } + } + + Some(deps) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::collections::HashSet; + use tw_encoding::hex::ToHex; + + #[test] + fn test_build_dependencies() { + let custom_types = r#"{ + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" } + ], + "Person": [ + { "name": "name", "type": "string" }, + { "name": "wallet", "type": "address" } + ], + "Mail": [ + { "name": "from", "type": "Person" }, + { "name": "to", "type": "Person" }, + { "name": "contents", "type": "string" } + ] + }"#; + + let custom_types: CustomTypes = serde_json::from_str(custom_types).unwrap(); + + let mail = "Mail"; + let person = "Person"; + + let expected = { + let mut temp = HashSet::new(); + temp.insert(mail); + temp.insert(person); + temp + }; + assert_eq!( + encode_custom_type::build_dependencies(mail, &custom_types), + Some(expected) + ); + } + + #[test] + fn test_encode_type() { + let custom_types = r#"{ + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" } + ], + "Person": [ + { "name": "name", "type": "string" }, + { "name": "wallet", "type": "address" } + ], + "Mail": [ + { "name": "from", "type": "Person" }, + { "name": "to", "type": "Person" }, + { "name": "contents", "type": "string" } + ] + }"#; + + let custom_types: CustomTypes = serde_json::from_str(custom_types).expect("alas error!"); + assert_eq!( + "Mail(Person from,Person to,string contents)Person(string name,address wallet)", + encode_custom_type::encode_type(&custom_types, "Mail").expect("alas error!") + ) + } + + #[test] + fn test_encode_type_hash() { + let custom_types = r#"{ + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" } + ], + "Person": [ + { "name": "name", "type": "string" }, + { "name": "wallet", "type": "address" } + ], + "Mail": [ + { "name": "from", "type": "Person" }, + { "name": "to", "type": "Person" }, + { "name": "contents", "type": "string" } + ] + }"#; + + let custom_types = serde_json::from_str::(custom_types).expect("alas error!"); + let hash = encode_custom_type::type_hash("Mail", &custom_types).expect("alas error!"); + assert_eq!( + hash.to_hex(), + "a0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2" + ); + } +} diff --git a/rust/tw_evm/src/message/eip712/mod.rs b/rust/tw_evm/src/message/eip712/mod.rs new file mode 100644 index 00000000000..78ee85335ad --- /dev/null +++ b/rust/tw_evm/src/message/eip712/mod.rs @@ -0,0 +1,8 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod eip712_message; +pub mod property; diff --git a/rust/tw_evm/src/message/eip712/property.rs b/rust/tw_evm/src/message/eip712/property.rs new file mode 100644 index 00000000000..d61340873ad --- /dev/null +++ b/rust/tw_evm/src/message/eip712/property.rs @@ -0,0 +1,192 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::non_empty_array::NonZeroLen; +use crate::abi::param_type::constructor::TypeConstructor; +use crate::abi::param_type::reader::Reader; +use crate::abi::uint::UintBits; +use crate::abi::{AbiError, AbiErrorKind, AbiResult}; +use crate::message::MessageSigningError; +use serde::Deserialize; +use std::str::FromStr; + +#[derive(Clone, Debug, Deserialize)] +pub struct Property { + pub name: String, + #[serde(rename = "type")] + pub property_type: String, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum PropertyType { + Bool, + String, + Int, + Uint, + Address, + FixBytes { + len: NonZeroLen, + }, + Bytes, + Custom(String), + Array(Box), + FixArray { + len: NonZeroLen, + element_type: Box, + }, +} + +impl TypeConstructor for PropertyType { + fn address() -> Self { + PropertyType::Address + } + + fn bytes() -> Self { + PropertyType::Bytes + } + + fn fixed_bytes_checked(len: NonZeroLen) -> Self { + PropertyType::FixBytes { len } + } + + fn int_checked(_bits: UintBits) -> Self { + PropertyType::Int + } + + fn uint_checked(_bits: UintBits) -> Self { + PropertyType::Uint + } + + fn bool() -> Self { + PropertyType::Bool + } + + fn string() -> Self { + PropertyType::String + } + + fn array(element_type: Self) -> Self { + PropertyType::Array(Box::new(element_type)) + } + + fn fixed_array_checked(len: NonZeroLen, element_type: Self) -> Self { + PropertyType::FixArray { + len, + element_type: Box::new(element_type), + } + } + + fn empty_tuple() -> AbiResult { + Err(AbiError(AbiErrorKind::Error_invalid_param_type)) + } + + fn custom(s: &str) -> AbiResult { + Ok(PropertyType::Custom(s.to_string())) + } +} + +impl FromStr for PropertyType { + type Err = MessageSigningError; + + fn from_str(s: &str) -> Result { + Reader::parse_type(s).map_err(|_| MessageSigningError::InvalidParameterType) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_int() { + let ints = ["int", "int8", "int256"]; + for int in ints { + assert_eq!(PropertyType::from_str(int).unwrap(), PropertyType::Int); + } + } + + #[test] + fn test_parse_uint() { + let ints = ["uint", "uint8", "uint256"]; + for int in ints { + assert_eq!(PropertyType::from_str(int).unwrap(), PropertyType::Uint); + } + } + + #[test] + fn test_parse_bytes() { + assert_eq!( + PropertyType::from_str("bytes").unwrap(), + PropertyType::Bytes + ); + assert_eq!( + PropertyType::from_str("bytes8").unwrap(), + PropertyType::FixBytes { + len: NonZeroLen::new(8).unwrap() + } + ); + assert_eq!( + PropertyType::from_str("bytes31").unwrap(), + PropertyType::FixBytes { + len: NonZeroLen::new(31).unwrap() + } + ); + } + + #[test] + fn test_parse_types() { + assert_eq!(PropertyType::from_str("bool").unwrap(), PropertyType::Bool); + assert_eq!( + PropertyType::from_str("string").unwrap(), + PropertyType::String + ); + assert_eq!( + PropertyType::from_str("address").unwrap(), + PropertyType::Address + ); + assert_eq!( + PropertyType::from_str("Unknown").unwrap(), + PropertyType::Custom("Unknown".to_string()) + ); + } + + #[test] + fn test_parse_nested_arrays() { + fn array(kind: PropertyType) -> PropertyType { + PropertyType::Array(Box::new(kind)) + } + + fn fix_array(kind: PropertyType) -> PropertyType { + PropertyType::FixArray { + len: NonZeroLen::new(N).unwrap(), + element_type: Box::new(kind), + } + } + + let source = "bytes[][][7][]"; + let actual = PropertyType::from_str(source).unwrap(); + let expected = array(fix_array::<7>(array(array(PropertyType::Bytes)))); + assert_eq!(actual, expected); + } + + #[test] + fn test_malformed_array_type() { + let inputs = [ + "string[[]][]", + "string[7[]][]", + "string[7[]uint][]", + "string][", + "[]string", + "[string]", + "[]", + "string[]uint", + ]; + + for input in inputs { + assert_eq!(PropertyType::from_str(input).is_err(), true, "{}", input); + } + } +} diff --git a/rust/tw_evm/src/message/mod.rs b/rust/tw_evm/src/message/mod.rs new file mode 100644 index 00000000000..a962df3519b --- /dev/null +++ b/rust/tw_evm/src/message/mod.rs @@ -0,0 +1,50 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::error::{SigningError, SigningErrorType}; +use tw_hash::H256; + +pub mod eip191; +pub mod eip712; +pub mod signature; + +pub type EthMessageBoxed = Box; +pub type MessageSigningResult = Result; + +#[derive(Debug)] +pub enum MessageSigningError { + InvalidParameterType, + InvalidParameterValue, + TypeValueMismatch, + InvalidChainId, + Internal, +} + +impl From for SigningError { + fn from(err: MessageSigningError) -> Self { + match err { + MessageSigningError::InvalidParameterType + | MessageSigningError::InvalidParameterValue + | MessageSigningError::TypeValueMismatch + | MessageSigningError::InvalidChainId => { + SigningError(SigningErrorType::Error_invalid_params) + }, + MessageSigningError::Internal => SigningError(SigningErrorType::Error_internal), + } + } +} + +pub trait EthMessage { + fn into_boxed(self) -> EthMessageBoxed + where + Self: 'static + Sized, + { + Box::new(self) + } + + /// Returns hash of the message. + fn hash(&self) -> MessageSigningResult; +} diff --git a/rust/tw_evm/src/message/signature.rs b/rust/tw_evm/src/message/signature.rs new file mode 100644 index 00000000000..847df7f4b4a --- /dev/null +++ b/rust/tw_evm/src/message/signature.rs @@ -0,0 +1,91 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::signature::{ + eip155_replay_protection, legacy_replay_protection, remove_replay_protection, +}; +use std::str::FromStr; +use tw_encoding::hex::DecodeHex; +use tw_hash::{H256, H520}; +use tw_keypair::ecdsa::secp256k1; +use tw_keypair::{KeyPairError, KeyPairResult}; +use tw_number::U256; + +pub enum SignatureType { + Standard, + Legacy, + Eip155 { chain_id: u64 }, +} + +pub struct MessageSignature { + r: H256, + s: H256, + v: u8, +} + +impl MessageSignature { + /// The length of the binary representation. + /// cbindgen:ignore + pub const LEN: usize = secp256k1::Signature::LEN; + + pub fn prepared(sign: secp256k1::Signature, sign_type: SignatureType) -> KeyPairResult { + let v = match sign_type { + SignatureType::Standard => U256::from(sign.v()), + SignatureType::Legacy => { + legacy_replay_protection(sign.v()).map_err(|_| KeyPairError::InvalidSignature)? + }, + SignatureType::Eip155 { chain_id } => { + eip155_replay_protection(U256::from(chain_id), sign.v()) + .map_err(|_| KeyPairError::InvalidSignature)? + }, + }; + Ok(MessageSignature { + r: sign.r(), + s: sign.s(), + v: v.low_u8(), + }) + } + + pub fn to_bytes(&self) -> H520 { + let mut bytes = Vec::with_capacity(H520::LEN); + bytes.extend_from_slice(self.r.as_slice()); + bytes.extend_from_slice(self.s.as_slice()); + bytes.push(self.v); + + H520::try_from(bytes.as_slice()).expect("'bytes' is 130 byte array") + } + + pub fn to_secp256k1_signature(&self) -> KeyPairResult { + let v = remove_replay_protection(self.v); + secp256k1::Signature::try_from_parts(self.r, self.s, v) + } +} + +impl FromStr for MessageSignature { + type Err = KeyPairError; + + fn from_str(s: &str) -> Result { + let bytes = s.decode_hex().map_err(|_| KeyPairError::InvalidSignature)?; + Self::try_from(bytes.as_slice()) + } +} + +impl<'a> TryFrom<&'a [u8]> for MessageSignature { + type Error = KeyPairError; + + fn try_from(data: &'a [u8]) -> Result { + if data.len() != Self::LEN { + return Err(KeyPairError::InvalidSignature); + } + + let r = H256::try_from(&data[secp256k1::Signature::R_RANGE]) + .expect("Expected 'r' 32 byte length array"); + let s = H256::try_from(&data[secp256k1::Signature::S_RANGE]) + .expect("Expected 's' 32 byte length array"); + let v = data[secp256k1::Signature::RECOVERY_LAST]; + Ok(MessageSignature { r, s, v }) + } +} diff --git a/rust/tw_evm/src/modules/abi_encoder.rs b/rust/tw_evm/src/modules/abi_encoder.rs new file mode 100644 index 00000000000..6879a1ab2e4 --- /dev/null +++ b/rust/tw_evm/src/modules/abi_encoder.rs @@ -0,0 +1,479 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::decode::{decode_params, decode_value}; +use crate::abi::function::Function; +use crate::abi::param::Param; +use crate::abi::param_token::NamedToken; +use crate::abi::param_type::ParamType; +use crate::abi::token::Token; +use crate::abi::{AbiError, AbiErrorKind, AbiResult}; +use crate::abi_output_error; +use crate::address::Address; +use crate::evm_context::EvmContext; +use serde::Deserialize; +use serde::Serialize; +use std::borrow::Cow; +use std::collections::HashMap; +use std::marker::PhantomData; +use std::str::FromStr; +use tw_hash::H32; +use tw_misc::traits::ToBytesVec; +use tw_number::{I256, U256}; +use tw_proto::EthereumAbi::Proto; + +use crate::abi::non_empty_array::{NonEmptyArray, NonEmptyBytes, NonZeroLen}; +use crate::abi::uint::UintBits; +use Proto::mod_ParamType::OneOfparam as ProtoParamType; +use Proto::mod_ParamsDecodingInput::OneOfabi as AbiEnum; +use Proto::mod_Token::OneOftoken as TokenEnum; + +pub struct AbiEncoder { + _phantom: PhantomData, +} + +impl AbiEncoder { + #[inline] + pub fn decode_contract_call( + input: Proto::ContractCallDecodingInput, + ) -> Proto::ContractCallDecodingOutput<'static> { + Self::decode_contract_call_impl(input) + .unwrap_or_else(|err| abi_output_error!(Proto::ContractCallDecodingOutput, err)) + } + + #[inline] + pub fn decode_params( + input: Proto::ParamsDecodingInput<'_>, + ) -> Proto::ParamsDecodingOutput<'static> { + Self::decode_params_impl(input) + .unwrap_or_else(|err| abi_output_error!(Proto::ParamsDecodingOutput, err)) + } + + #[inline] + pub fn decode_value( + input: Proto::ValueDecodingInput<'_>, + ) -> Proto::ValueDecodingOutput<'static> { + Self::decode_value_impl(input) + .unwrap_or_else(|err| abi_output_error!(Proto::ValueDecodingOutput, err)) + } + + #[inline] + pub fn get_function_signature(input: Proto::FunctionGetTypeInput<'_>) -> String { + Self::get_function_signature_impl(input) + } + + #[inline] + pub fn encode_contract_call( + input: Proto::FunctionEncodingInput<'_>, + ) -> Proto::FunctionEncodingOutput<'static> { + Self::encode_contract_call_impl(input) + .unwrap_or_else(|err| abi_output_error!(Proto::FunctionEncodingOutput, err)) + } + + fn decode_contract_call_impl( + input: Proto::ContractCallDecodingInput, + ) -> AbiResult> { + if input.encoded.len() < H32::len() { + return Err(AbiError(AbiErrorKind::Error_decoding_data)); + } + let short_signature = &input.encoded[0..H32::len()]; + let short_signature = + H32::try_from(short_signature).expect("The length expected to be checked above"); + let encoded_data = &input.encoded[H32::len()..]; + + let mut abi_json: SmartContractCallAbiJson = + serde_json::from_str(&input.smart_contract_abi_json) + .map_err(|_| AbiError(AbiErrorKind::Error_invalid_abi))?; + + let function = abi_json + .map + .get_mut(&short_signature) + .ok_or(AbiError(AbiErrorKind::Error_abi_mismatch))?; + + let decoded_tokens = function.decode_input(encoded_data)?; + + // Clear the `outputs` to avoid adding them to the signature. + // This is a requirement that comes from legacy ABI implementation. + function.outputs.clear(); + let function_signature = function.signature(); + + // Serialize the `decoded_json` result. + let decoded_res = SmartContractCallDecodedInputJson { + function: function_signature, + inputs: &decoded_tokens, + }; + let decoded_json = serde_json::to_string(&decoded_res) + .map_err(|_| AbiError(AbiErrorKind::Error_internal))?; + + // Serialize the Proto parameters. + let decoded_protos = decoded_tokens + .into_iter() + .map(Self::named_token_to_proto) + .collect(); + + Ok(Proto::ContractCallDecodingOutput { + decoded_json: Cow::Owned(decoded_json), + tokens: decoded_protos, + ..Proto::ContractCallDecodingOutput::default() + }) + } + + fn decode_params_impl( + input: Proto::ParamsDecodingInput<'_>, + ) -> AbiResult> { + let abi = match input.abi { + AbiEnum::abi_json(abi_json) => serde_json::from_str(&abi_json) + .map_err(|_| AbiError(AbiErrorKind::Error_invalid_abi))?, + AbiEnum::abi_params(abi_params) => abi_params + .params + .into_iter() + .map(Self::param_from_proto) + .collect::>>()?, + AbiEnum::None => return Err(AbiError(AbiErrorKind::Error_invalid_abi)), + }; + + let decoded_tokens = decode_params(&abi, &input.encoded)?; + // Serialize the Proto parameters. + let decoded_protos = decoded_tokens + .into_iter() + .map(Self::named_token_to_proto) + .collect(); + + Ok(Proto::ParamsDecodingOutput { + tokens: decoded_protos, + ..Proto::ParamsDecodingOutput::default() + }) + } + + fn decode_value_impl( + input: Proto::ValueDecodingInput<'_>, + ) -> AbiResult> { + let param_type = DecodingValueType::from_str(&input.param_type)?.0; + let token = decode_value(¶m_type, &input.encoded)?; + let token_str = token.to_string(); + Ok(Proto::ValueDecodingOutput { + token: Some(Self::token_to_proto(token)), + param_str: token_str.into(), + ..Proto::ValueDecodingOutput::default() + }) + } + + fn get_function_signature_impl(input: Proto::FunctionGetTypeInput<'_>) -> String { + let function_inputs = input + .inputs + .into_iter() + .map(Self::param_from_proto) + .collect::>>() + .unwrap_or_default(); + + let fun = Function { + name: input.function_name.to_string(), + inputs: function_inputs, + ..Function::default() + }; + fun.signature() + } + + fn encode_contract_call_impl( + input: Proto::FunctionEncodingInput<'_>, + ) -> AbiResult> { + let mut tokens = Vec::with_capacity(input.tokens.len()); + let mut input_types = Vec::with_capacity(input.tokens.len()); + + for token in input.tokens { + let named_token = Self::named_token_from_proto(token)?; + input_types.push(named_token.to_param()); + tokens.push(named_token.value); + } + + let fun = Function { + name: input.function_name.to_string(), + inputs: input_types, + ..Function::default() + }; + + let encoded = fun.encode_input(&tokens)?; + Ok(Proto::FunctionEncodingOutput { + function_type: fun.signature().into(), + encoded: encoded.into(), + ..Proto::FunctionEncodingOutput::default() + }) + } + + pub fn param_to_proto(param: Param) -> Proto::Param<'static> { + Proto::Param { + name: Cow::Owned(param.name.unwrap_or_default()), + param: Some(Self::param_type_to_proto(param.kind)), + } + } + + fn param_from_proto(param: Proto::Param<'_>) -> AbiResult { + let name = if param.name.is_empty() { + None + } else { + Some(param.name.to_string()) + }; + + let proto_param_type = param + .param + .ok_or(AbiError(AbiErrorKind::Error_missing_param_type))?; + let kind = Self::param_type_from_proto(proto_param_type)?; + + Ok(Param { + name, + kind, + internal_type: None, + }) + } + + fn named_token_from_proto(named_token: Proto::Token<'_>) -> AbiResult { + Ok(NamedToken { + name: Some(named_token.name.clone().into()), + value: Self::token_from_proto(named_token)?, + internal_type: None, + }) + } + + fn token_from_proto(token: Proto::Token<'_>) -> AbiResult { + match token.token { + TokenEnum::boolean(bool) => Ok(Token::Bool(bool)), + TokenEnum::number_uint(u) => { + let uint = Self::u_number_n_from_proto(&u.value)?; + Token::uint(u.bits as usize, uint) + }, + TokenEnum::number_int(i) => { + let int = Self::s_number_n_from_proto(&i.value)?; + Token::int(i.bits as usize, int) + }, + TokenEnum::string_value(str) => Ok(Token::String(str.to_string())), + TokenEnum::address(addr) => { + let addr = Address::from_str(&addr) + .map_err(|_| AbiError(AbiErrorKind::Error_invalid_address_value))?; + Ok(Token::Address(addr)) + }, + TokenEnum::byte_array(bytes) => Ok(Token::Bytes(bytes.to_vec())), + TokenEnum::byte_array_fix(bytes) => { + let checked_bytes = NonEmptyBytes::new(bytes.to_vec())?; + Ok(Token::FixedBytes(checked_bytes)) + }, + TokenEnum::array(arr) => { + let (arr, kind) = Self::array_from_proto(arr)?; + Ok(Token::Array { arr, kind }) + }, + TokenEnum::fixed_array(arr) => { + let (arr, kind) = Self::array_from_proto(arr)?; + let arr = NonEmptyArray::new(arr)?; + Ok(Token::FixedArray { arr, kind }) + }, + TokenEnum::tuple(Proto::TupleParam { params }) => { + let params = params + .into_iter() + .map(Self::named_token_from_proto) + .collect::>>()?; + Ok(Token::Tuple { params }) + }, + TokenEnum::None => Err(AbiError(AbiErrorKind::Error_missing_param_value)), + } + } + + fn array_from_proto(array: Proto::ArrayParam<'_>) -> AbiResult<(Vec, ParamType)> { + let element_type = array + .element_type + .ok_or(AbiError(AbiErrorKind::Error_missing_param_type))?; + let element_type = Self::param_type_from_proto(element_type)?; + + let mut array_tokens = Vec::with_capacity(array.elements.len()); + for proto_token in array.elements { + let token = Self::token_from_proto(proto_token)?; + let token_type = token.to_param_type(); + + // Check if all tokens are the same as declared in `ArrayParam::element_type`. + if token_type != element_type { + return Err(AbiError(AbiErrorKind::Error_invalid_param_type)); + } + array_tokens.push(token); + } + + Ok((array_tokens, element_type)) + } + + fn param_type_to_proto(param_type: ParamType) -> Proto::ParamType<'static> { + let param = match param_type { + ParamType::Address => ProtoParamType::address(Proto::AddressType {}), + ParamType::FixedBytes { len } => { + let size = len.get() as u64; + ProtoParamType::byte_array_fix(Proto::ByteArrayFixType { size }) + }, + ParamType::Bytes => ProtoParamType::byte_array(Proto::ByteArrayType {}), + ParamType::Int { bits } => ProtoParamType::number_int(Proto::NumberNType { + bits: bits.get() as u32, + }), + ParamType::Uint { bits } => ProtoParamType::number_uint(Proto::NumberNType { + bits: bits.get() as u32, + }), + ParamType::Bool => ProtoParamType::boolean(Proto::BoolType {}), + ParamType::String => ProtoParamType::string_param(Proto::StringType {}), + ParamType::FixedArray { kind, len } => { + let size = len.get() as u64; + let element_type = Some(Box::new(Self::param_type_to_proto(*kind))); + ProtoParamType::fixed_array(Box::new(Proto::FixedArrayType { size, element_type })) + }, + ParamType::Array { kind } => { + let element_type = Some(Box::new(Self::param_type_to_proto(*kind))); + ProtoParamType::array(Box::new(Proto::ArrayType { element_type })) + }, + ParamType::Tuple { params } => { + let params: Vec<_> = params.into_iter().map(Self::param_to_proto).collect(); + ProtoParamType::tuple(Proto::TupleType { params }) + }, + }; + Proto::ParamType { param } + } + + fn param_type_from_proto(param_type: Proto::ParamType<'_>) -> AbiResult { + match param_type.param { + ProtoParamType::boolean(_) => Ok(ParamType::Bool), + ProtoParamType::number_int(i) => { + let bits = UintBits::new(i.bits as usize)?; + Ok(ParamType::Int { bits }) + }, + ProtoParamType::number_uint(u) => { + let bits = UintBits::new(u.bits as usize)?; + Ok(ParamType::Uint { bits }) + }, + ProtoParamType::string_param(_) => Ok(ParamType::String), + ProtoParamType::address(_) => Ok(ParamType::Address), + ProtoParamType::byte_array(_) => Ok(ParamType::Bytes), + ProtoParamType::byte_array_fix(bytes) => { + let len = NonZeroLen::new(bytes.size as usize)?; + Ok(ParamType::FixedBytes { len }) + }, + ProtoParamType::array(arr) => { + let element_type = arr + .element_type + .ok_or(AbiError(AbiErrorKind::Error_missing_param_type))?; + let kind = Self::param_type_from_proto(*element_type)?; + Ok(ParamType::Array { + kind: Box::new(kind), + }) + }, + ProtoParamType::fixed_array(arr) => { + let element_type = arr + .element_type + .ok_or(AbiError(AbiErrorKind::Error_missing_param_type))?; + let kind = Box::new(Self::param_type_from_proto(*element_type)?); + let len = NonZeroLen::new(arr.size as usize)?; + Ok(ParamType::FixedArray { kind, len }) + }, + ProtoParamType::tuple(tuple) => { + let params = tuple + .params + .into_iter() + .map(Self::param_from_proto) + .collect::>>()?; + if params.is_empty() { + return Err(AbiError(AbiErrorKind::Error_invalid_abi)); + } + Ok(ParamType::Tuple { params }) + }, + ProtoParamType::None => Err(AbiError(AbiErrorKind::Error_missing_param_type)), + } + } + + fn named_token_to_proto(token: NamedToken) -> Proto::Token<'static> { + Proto::Token { + name: Cow::Owned(token.name.unwrap_or_default()), + ..Self::token_to_proto(token.value) + } + } + + pub fn token_to_proto(token: Token) -> Proto::Token<'static> { + let value = match token { + Token::Address(addr) => TokenEnum::address(Cow::Owned(addr.to_string())), + Token::FixedBytes(bytes) => TokenEnum::byte_array_fix(Cow::Owned(bytes.into_vec())), + Token::Bytes(bytes) => TokenEnum::byte_array(Cow::Owned(bytes)), + Token::Int { int, bits } => TokenEnum::number_int(Self::s_number_n_proto(int, bits)), + Token::Uint { uint, bits } => { + TokenEnum::number_uint(Self::u_number_n_proto(uint, bits)) + }, + Token::Bool(bool) => TokenEnum::boolean(bool), + Token::String(str) => TokenEnum::string_value(Cow::Owned(str)), + Token::FixedArray { kind, arr } => { + TokenEnum::fixed_array(Self::array_to_proto(kind, arr.into_vec())) + }, + Token::Array { kind, arr, .. } => TokenEnum::array(Self::array_to_proto(kind, arr)), + Token::Tuple { params } => TokenEnum::tuple(Self::tuple_to_proto(params)), + }; + Proto::Token { + name: "".into(), + token: value, + } + } + + fn s_number_n_from_proto(encoded: &[u8]) -> AbiResult { + I256::from_big_endian_slice(encoded) + .map_err(|_| AbiError(AbiErrorKind::Error_invalid_uint_value)) + } + + fn u_number_n_from_proto(encoded: &[u8]) -> AbiResult { + U256::from_big_endian_slice(encoded) + .map_err(|_| AbiError(AbiErrorKind::Error_invalid_uint_value)) + } + + fn s_number_n_proto(i: I256, bits: UintBits) -> Proto::NumberNParam<'static> { + Proto::NumberNParam { + bits: bits.get() as u32, + value: Cow::Owned(i.to_big_endian_compact()), + } + } + + fn u_number_n_proto(u: U256, bits: UintBits) -> Proto::NumberNParam<'static> { + Proto::NumberNParam { + bits: bits.get() as u32, + value: Cow::Owned(u.to_big_endian_compact()), + } + } + + fn array_to_proto(kind: ParamType, arr: Vec) -> Proto::ArrayParam<'static> { + Proto::ArrayParam { + elements: arr.into_iter().map(Self::token_to_proto).collect(), + element_type: Some(Self::param_type_to_proto(kind)), + } + } + + fn tuple_to_proto(params: Vec) -> Proto::TupleParam<'static> { + let params = params.into_iter().map(Self::named_token_to_proto).collect(); + Proto::TupleParam { params } + } +} + +#[derive(Deserialize)] +struct SmartContractCallAbiJson { + #[serde(flatten)] + map: HashMap, +} + +#[derive(Serialize)] +struct SmartContractCallDecodedInputJson<'a> { + function: String, + inputs: &'a [NamedToken], +} + +/// A value type used on [`AbiEncoder::decode_value`]. +/// Please note [`AbiEncoder::decode_value`] doesn't support `ParamType::Tuple` for decoding. +struct DecodingValueType(ParamType); + +impl FromStr for DecodingValueType { + type Err = AbiError; + + fn from_str(s: &str) -> Result { + let param_type = ParamType::try_from_type_short(s)?; + if param_type.has_tuple_components() { + return Err(AbiError(AbiErrorKind::Error_invalid_param_type)); + } + Ok(DecodingValueType(param_type)) + } +} diff --git a/rust/tw_evm/src/modules/compiler.rs b/rust/tw_evm/src/modules/compiler.rs new file mode 100644 index 00000000000..4d5066874b4 --- /dev/null +++ b/rust/tw_evm/src/modules/compiler.rs @@ -0,0 +1,89 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::evm_context::EvmContext; +use crate::modules::tx_builder::TxBuilder; +use std::borrow::Cow; +use std::marker::PhantomData; +use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::common::compile_input::SingleSignaturePubkey; +use tw_coin_entry::error::SigningResult; +use tw_coin_entry::signing_output_error; +use tw_keypair::ecdsa::secp256k1; +use tw_number::U256; +use tw_proto::Ethereum::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct Compiler { + _phantom: PhantomData, +} + +impl Compiler { + #[inline] + pub fn preimage_hashes( + input: Proto::SigningInput<'_>, + ) -> CompilerProto::PreSigningOutput<'static> { + Self::preimage_hashes_impl(input) + .unwrap_or_else(|e| signing_output_error!(CompilerProto::PreSigningOutput, e)) + } + + #[inline] + pub fn compile( + input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Proto::SigningOutput<'static> { + Self::compile_impl(input, signatures, public_keys) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn preimage_hashes_impl( + input: Proto::SigningInput<'_>, + ) -> SigningResult> { + let chain_id = U256::from_big_endian_slice(&input.chain_id)?; + + let unsigned = TxBuilder::::tx_from_proto(&input)?; + let prehash = unsigned.pre_hash(chain_id); + let preimage_data = unsigned.encode(chain_id); + + Ok(CompilerProto::PreSigningOutput { + data: Cow::from(preimage_data), + data_hash: Cow::from(prehash.to_vec()), + ..CompilerProto::PreSigningOutput::default() + }) + } + + fn compile_impl( + input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> SigningResult> { + let SingleSignaturePubkey { + signature, + public_key: _, + } = SingleSignaturePubkey::::from_sign_pubkey_list(signatures, public_keys)?; + + let chain_id = U256::from_big_endian_slice(&input.chain_id)?; + + let unsigned = TxBuilder::::tx_from_proto(&input)?; + + let pre_hash = unsigned.pre_hash(chain_id); + + let signed = unsigned.try_into_signed(signature, chain_id)?; + + let eth_signature = signed.signature(); + + Ok(Proto::SigningOutput { + encoded: signed.encode().into(), + v: eth_signature.v().to_big_endian_compact().into(), + r: eth_signature.r().to_big_endian_compact().into(), + s: eth_signature.s().to_big_endian_compact().into(), + data: signed.payload().into(), + pre_hash: pre_hash.to_vec().into(), + ..Proto::SigningOutput::default() + }) + } +} diff --git a/rust/tw_evm/src/modules/json_signer.rs b/rust/tw_evm/src/modules/json_signer.rs new file mode 100644 index 00000000000..9bacea446c0 --- /dev/null +++ b/rust/tw_evm/src/modules/json_signer.rs @@ -0,0 +1,30 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::evm_context::EvmContext; +use std::marker::PhantomData; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::modules::json_signer::JsonSigner; +use tw_keypair::tw::PrivateKey; + +#[derive(Default)] +pub struct EthJsonSigner { + _phantom: PhantomData, +} + +impl JsonSigner for EthJsonSigner { + #[inline] + fn sign_json( + &self, + _coin: &dyn CoinContext, + _input_json: &str, + _key: &PrivateKey, + ) -> SigningResult { + // TODO implement when `quick_protobuf` is replaced with `rust-protobuf`. + Err(SigningError(SigningErrorType::Error_internal)) + } +} diff --git a/rust/tw_evm/src/modules/message_signer.rs b/rust/tw_evm/src/modules/message_signer.rs new file mode 100644 index 00000000000..e0f76f9e116 --- /dev/null +++ b/rust/tw_evm/src/modules/message_signer.rs @@ -0,0 +1,148 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::message::eip191::Eip191Message; +use crate::message::eip712::eip712_message::Eip712Message; +use crate::message::signature::{MessageSignature, SignatureType}; +use crate::message::{EthMessage, EthMessageBoxed}; +use std::borrow::Cow; +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::SigningResult; +use tw_coin_entry::modules::message_signer::MessageSigner; +use tw_coin_entry::signing_output_error; +use tw_encoding::hex::ToHex; +use tw_keypair::ecdsa::secp256k1; +use tw_keypair::ecdsa::signature::VerifySignature; +use tw_keypair::traits::{SigningKeyTrait, VerifyingKeyTrait}; +use tw_number::U256; +use tw_proto::Ethereum::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +#[derive(Default)] +pub struct EthMessageSigner; + +impl MessageSigner for EthMessageSigner { + type MessageSigningInput<'a> = Proto::MessageSigningInput<'a>; + type MessagePreSigningOutput = CompilerProto::PreSigningOutput<'static>; + type MessageSigningOutput = Proto::MessageSigningOutput<'static>; + type MessageVerifyingInput<'a> = Proto::MessageVerifyingInput<'a>; + + fn message_preimage_hashes( + &self, + _coin: &dyn CoinContext, + input: Self::MessageSigningInput<'_>, + ) -> Self::MessagePreSigningOutput { + Self::message_preimage_hashes_impl(input) + .unwrap_or_else(|e| signing_output_error!(CompilerProto::PreSigningOutput, e)) + } + + fn sign_message( + &self, + _coin: &dyn CoinContext, + input: Self::MessageSigningInput<'_>, + ) -> Self::MessageSigningOutput { + Self::sign_message_impl(input) + .unwrap_or_else(|e| signing_output_error!(Proto::MessageSigningOutput, e)) + } + + fn verify_message( + &self, + _coin: &dyn CoinContext, + input: Self::MessageVerifyingInput<'_>, + ) -> bool { + Self::verify_message_impl(input).unwrap_or_default() + } +} + +impl EthMessageSigner { + fn message_preimage_hashes_impl( + input: Proto::MessageSigningInput<'_>, + ) -> SigningResult> { + let msg = Self::message_from_proto(input)?; + let hash = msg.hash()?.to_vec(); + Ok(CompilerProto::PreSigningOutput { + data: Cow::Owned(hash.clone()), + data_hash: Cow::Owned(hash), + ..CompilerProto::PreSigningOutput::default() + }) + } + + fn sign_message_impl( + input: Proto::MessageSigningInput<'_>, + ) -> SigningResult> { + let private_key = secp256k1::PrivateKey::try_from(input.private_key.as_ref())?; + let signature_type = + Self::signature_type_from_proto(input.message_type, input.chain_id.clone()); + + let msg = Self::message_from_proto(input)?; + + let hash_to_sign = msg.hash()?; + let secp_sign = private_key.sign(hash_to_sign)?; + let prepared_sign = MessageSignature::prepared(secp_sign, signature_type)?; + + Ok(Proto::MessageSigningOutput { + signature: Cow::Owned(prepared_sign.to_bytes().to_hex()), + ..Proto::MessageSigningOutput::default() + }) + } + + fn verify_message_impl(input: Proto::MessageVerifyingInput<'_>) -> SigningResult { + let public_key = secp256k1::PublicKey::try_from(input.public_key.as_ref())?; + + let msg_hash = Self::message_from_str(&input.message)?.hash()?; + let secp_signature = + MessageSignature::from_str(&input.signature)?.to_secp256k1_signature()?; + + let actual_public_key = secp256k1::PublicKey::recover(secp_signature.clone(), msg_hash)?; + let valid = actual_public_key == public_key + && public_key.verify(VerifySignature::from(secp_signature), msg_hash); + + Ok(valid) + } + + fn message_from_proto(input: Proto::MessageSigningInput<'_>) -> SigningResult { + match input.message_type { + Proto::MessageType::MessageType_legacy + | Proto::MessageType::MessageType_eip155 + | Proto::MessageType::MessageType_immutable_x => { + Ok(Eip191Message::new(input.message).into_boxed()) + }, + Proto::MessageType::MessageType_typed + | Proto::MessageType::MessageType_typed_eip155 => match input.chain_id { + Some(expected_chain_id) => { + let expected_chain_id = U256::from(expected_chain_id.chain_id); + Ok(Eip712Message::new_checked(input.message, expected_chain_id)?.into_boxed()) + }, + None => Ok(Eip712Message::new(input.message)?.into_boxed()), + }, + } + } + + fn message_from_str(user_message: &str) -> SigningResult { + match Eip712Message::new(user_message) { + Ok(typed_data) => Ok(typed_data.into_boxed()), + Err(_) => Ok(Eip191Message::new(user_message).into_boxed()), + } + } + + fn signature_type_from_proto( + msg_type: Proto::MessageType, + maybe_chain_id: Option, + ) -> SignatureType { + match msg_type { + Proto::MessageType::MessageType_immutable_x => SignatureType::Standard, + Proto::MessageType::MessageType_legacy | Proto::MessageType::MessageType_typed => { + SignatureType::Legacy + }, + Proto::MessageType::MessageType_eip155 + | Proto::MessageType::MessageType_typed_eip155 => { + let chain_id = maybe_chain_id.unwrap_or_default().chain_id; + SignatureType::Eip155 { chain_id } + }, + } + } +} diff --git a/rust/tw_evm/src/modules/mod.rs b/rust/tw_evm/src/modules/mod.rs new file mode 100644 index 00000000000..ba8e26862b4 --- /dev/null +++ b/rust/tw_evm/src/modules/mod.rs @@ -0,0 +1,13 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod abi_encoder; +pub mod compiler; +pub mod json_signer; +pub mod message_signer; +pub mod rlp_encoder; +pub mod signer; +pub mod tx_builder; diff --git a/rust/tw_evm/src/modules/rlp_encoder.rs b/rust/tw_evm/src/modules/rlp_encoder.rs new file mode 100644 index 00000000000..e45e2ef0d2d --- /dev/null +++ b/rust/tw_evm/src/modules/rlp_encoder.rs @@ -0,0 +1,92 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::evm_context::EvmContext; +use crate::rlp::buffer::RlpBuffer; +use crate::rlp::list::RlpList; +use crate::rlp::RlpEncode; +use std::borrow::Cow; +use std::marker::PhantomData; +use std::str::FromStr; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::signing_output_error; +use tw_memory::Data; +use tw_number::U256; +use tw_proto::EthereumRlp::Proto; + +/// cbindgen:ignore +pub const RECURSION_LIMIT: usize = 10; + +pub struct RlpEncoder { + _phantom: PhantomData, +} + +impl RlpEncoder { + pub fn encode(val: T) -> Data + where + T: RlpEncode, + { + let mut buf = RlpBuffer::new(); + val.rlp_append(&mut buf); + buf.finish() + } + + pub fn encode_with_proto(input: Proto::EncodingInput<'_>) -> Proto::EncodingOutput<'static> { + Self::encode_with_proto_impl(input) + .unwrap_or_else(|err| signing_output_error!(Proto::EncodingOutput, err)) + } + + fn encode_with_proto_impl( + input: Proto::EncodingInput<'_>, + ) -> SigningResult> { + let Some(rlp_item) = input.item else { + return Err(SigningError(SigningErrorType::Error_invalid_params)); + }; + + let initial_depth = 0; + let encoded = Self::encode_proto_item(initial_depth, rlp_item)?; + Ok(Proto::EncodingOutput { + encoded: Cow::from(encoded.as_slice().to_vec()), + ..Proto::EncodingOutput::default() + }) + } + + fn encode_proto_item(depth: usize, rlp_item: Proto::RlpItem) -> SigningResult { + use Proto::mod_RlpItem::OneOfitem as Item; + + if depth >= RECURSION_LIMIT { + return Err(SigningError(SigningErrorType::Error_invalid_params)); + } + + let encoded_item = match rlp_item.item { + Item::string_item(str) => RlpEncoder::::encode(str.as_ref()), + Item::number_u64(num) => RlpEncoder::::encode(U256::from(num)), + Item::number_u256(num_be) => { + let num = U256::from_big_endian_slice(num_be.as_ref())?; + RlpEncoder::::encode(num) + }, + Item::address(addr_s) => { + let addr = Context::Address::from_str(addr_s.as_ref())?; + RlpEncoder::::encode(addr.into()) + }, + Item::data(data) => RlpEncoder::::encode(data.as_ref()), + Item::list(proto_nested_list) => { + let mut rlp_nested_list = RlpList::new(); + let new_depth = depth + 1; + + for proto_nested_list in proto_nested_list.items { + let encoded_item = Self::encode_proto_item(new_depth, proto_nested_list)?; + rlp_nested_list.append_raw_encoded(encoded_item.as_slice()); + } + rlp_nested_list.finish() + }, + // Pass the `raw_encoded` item as it is. + Item::raw_encoded(encoded) => encoded.to_vec(), + Item::None => return Err(SigningError(SigningErrorType::Error_invalid_params)), + }; + Ok(encoded_item) + } +} diff --git a/rust/tw_evm/src/modules/signer.rs b/rust/tw_evm/src/modules/signer.rs new file mode 100644 index 00000000000..18eb01b3374 --- /dev/null +++ b/rust/tw_evm/src/modules/signer.rs @@ -0,0 +1,60 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::evm_context::EvmContext; +use crate::modules::tx_builder::TxBuilder; +use std::borrow::Cow; +use std::marker::PhantomData; +use tw_coin_entry::error::SigningResult; +use tw_coin_entry::signing_output_error; +use tw_keypair::ecdsa::secp256k1; +use tw_keypair::traits::SigningKeyTrait; +use tw_number::U256; +use tw_proto::Ethereum::Proto; + +const SIGNATURE_V_MIN_LEN: usize = 1; + +pub struct Signer { + _phantom: PhantomData, +} + +impl Signer { + #[inline] + pub fn sign_proto(input: Proto::SigningInput<'_>) -> Proto::SigningOutput<'static> { + Self::sign_proto_impl(input) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn sign_proto_impl( + input: Proto::SigningInput<'_>, + ) -> SigningResult> { + let chain_id = U256::from_big_endian_slice(&input.chain_id)?; + let private_key = secp256k1::PrivateKey::try_from(input.private_key.as_ref())?; + + let unsigned = TxBuilder::::tx_from_proto(&input)?; + + let pre_hash = unsigned.pre_hash(chain_id); + let signature = private_key.sign(pre_hash)?; + + let signed = unsigned.try_into_signed(signature, chain_id)?; + + let eth_signature = signed.signature(); + + let v = eth_signature + .v() + .to_big_endian_compact_min_len(SIGNATURE_V_MIN_LEN); + + Ok(Proto::SigningOutput { + encoded: Cow::from(signed.encode()), + v: Cow::from(v), + r: Cow::from(eth_signature.r().to_big_endian().to_vec()), + s: Cow::from(eth_signature.s().to_big_endian().to_vec()), + data: Cow::from(signed.payload()), + pre_hash: Cow::from(pre_hash.to_vec()), + ..Proto::SigningOutput::default() + }) + } +} diff --git a/rust/tw_evm/src/modules/tx_builder.rs b/rust/tw_evm/src/modules/tx_builder.rs new file mode 100644 index 00000000000..395fd81489a --- /dev/null +++ b/rust/tw_evm/src/modules/tx_builder.rs @@ -0,0 +1,237 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::prebuild::erc1155::Erc1155; +use crate::abi::prebuild::erc20::Erc20; +use crate::abi::prebuild::erc4337::{Erc4337SimpleAccount, ExecuteArgs}; +use crate::abi::prebuild::erc721::Erc721; +use crate::address::{Address, EvmAddress}; +use crate::evm_context::EvmContext; +use crate::transaction::transaction_eip1559::TransactionEip1559; +use crate::transaction::transaction_non_typed::TransactionNonTyped; +use crate::transaction::user_operation::UserOperation; +use crate::transaction::UnsignedTransactionBox; +use std::marker::PhantomData; +use std::str::FromStr; +use tw_coin_entry::error::{AddressResult, SigningError, SigningErrorType, SigningResult}; +use tw_memory::Data; +use tw_number::U256; +use tw_proto::Common::Proto::SigningError as CommonError; +use tw_proto::Ethereum::Proto; + +pub struct TxBuilder { + _phantom: PhantomData, +} + +impl TxBuilder { + pub fn tx_from_proto( + input: &Proto::SigningInput<'_>, + ) -> SigningResult> { + use Proto::mod_Transaction::OneOftransaction_oneof as Tx; + use Proto::TransactionMode as TxMode; + + let Some(ref transaction) = input.transaction else { + return Err(SigningError(CommonError::Error_invalid_params)); + }; + + let (eth_amount, payload, to) = match transaction.transaction_oneof { + Tx::transfer(ref transfer) => { + let amount = U256::from_big_endian_slice(&transfer.amount)?; + let to_address = Self::parse_address(&input.to_address)?; + (amount, transfer.data.to_vec(), Some(to_address)) + }, + Tx::erc20_transfer(ref erc20_transfer) => { + let token_to_address = Self::parse_address(&erc20_transfer.to)?; + let token_amount = U256::from_big_endian_slice(&erc20_transfer.amount)?; + let contract_address = Self::parse_address(&input.to_address)?; + + let payload = Erc20::transfer(token_to_address, token_amount)?; + (U256::zero(), payload, Some(contract_address)) + }, + Tx::erc20_approve(ref erc20_approve) => { + let spender = Self::parse_address(&erc20_approve.spender)?; + let token_amount = U256::from_big_endian_slice(&erc20_approve.amount)?; + let contract_address = Self::parse_address(&input.to_address)?; + + let payload = Erc20::approve(spender, token_amount)?; + (U256::zero(), payload, Some(contract_address)) + }, + Tx::erc721_transfer(ref erc721_transfer) => { + let from = Self::parse_address(&erc721_transfer.from)?; + let token_to_address = Self::parse_address(&erc721_transfer.to)?; + let token_id = U256::from_big_endian_slice(&erc721_transfer.token_id)?; + let contract_address = Self::parse_address(&input.to_address)?; + + let payload = Erc721::encode_transfer_from(from, token_to_address, token_id)?; + (U256::zero(), payload, Some(contract_address)) + }, + Tx::erc1155_transfer(ref erc1155_transfer) => { + let from = Self::parse_address(&erc1155_transfer.from)?; + let to = Self::parse_address(&erc1155_transfer.to)?; + let token_id = U256::from_big_endian_slice(&erc1155_transfer.token_id)?; + let value = U256::from_big_endian_slice(&erc1155_transfer.value)?; + let data = erc1155_transfer.data.to_vec(); + let contract_address = Self::parse_address(&input.to_address)?; + + let payload = Erc1155::encode_safe_transfer_from(from, to, token_id, value, data)?; + (U256::zero(), payload, Some(contract_address)) + }, + Tx::contract_generic(ref contract_generic) => { + let amount = U256::from_big_endian_slice(&contract_generic.amount)?; + let payload = contract_generic.data.to_vec(); + // `to_address` can be omitted for the generic contract call. + // For example, on creating a new smart contract. + let to_address = Self::parse_address_optional(&input.to_address)?; + + (amount, payload, to_address) + }, + Tx::batch(ref batch) => { + if input.tx_mode != TxMode::UserOp { + return Err(SigningError(SigningErrorType::Error_invalid_params)); + } + + // Payload should match ERC4337 standard. + let calls: Vec<_> = batch + .calls + .iter() + .map(Self::erc4337_execute_call_from_proto) + .collect::, _>>()?; + let payload = Erc4337SimpleAccount::encode_execute_batch(calls)?; + + return Self::user_operation_from_proto(input, payload) + .map(UserOperation::into_boxed); + }, + Tx::None => return Err(SigningError(SigningErrorType::Error_invalid_params)), + }; + + let tx = match input.tx_mode { + TxMode::Legacy => { + Self::transaction_non_typed_from_proto(input, eth_amount, payload, to)?.into_boxed() + }, + TxMode::Enveloped => { + Self::transaction_eip1559_from_proto(input, eth_amount, payload, to)?.into_boxed() + }, + TxMode::UserOp => { + let to = to.ok_or(SigningError(SigningErrorType::Error_invalid_address))?; + // Payload should match the ERC4337 standard. + let payload = Erc4337SimpleAccount::encode_execute(ExecuteArgs { + to, + value: eth_amount, + data: payload, + })?; + + Self::user_operation_from_proto(input, payload)?.into_boxed() + }, + }; + Ok(tx) + } + + #[inline] + fn erc4337_execute_call_from_proto( + call: &Proto::mod_Transaction::mod_Batch::BatchedCall, + ) -> SigningResult { + let to = Self::parse_address(&call.address)?; + let value = U256::from_big_endian_slice(&call.amount)?; + Ok(ExecuteArgs { + to, + value, + data: call.payload.to_vec(), + }) + } + + #[inline] + fn transaction_non_typed_from_proto( + input: &Proto::SigningInput, + eth_amount: U256, + payload: Data, + to_address: Option
, + ) -> SigningResult { + let nonce = U256::from_big_endian_slice(&input.nonce)?; + let gas_price = U256::from_big_endian_slice(&input.gas_price)?; + let gas_limit = U256::from_big_endian_slice(&input.gas_limit)?; + + Ok(TransactionNonTyped { + nonce, + gas_price, + gas_limit, + to: to_address, + amount: eth_amount, + payload, + }) + } + + #[inline] + fn transaction_eip1559_from_proto( + input: &Proto::SigningInput, + eth_amount: U256, + payload: Data, + to_address: Option
, + ) -> SigningResult { + let nonce = U256::from_big_endian_slice(&input.nonce)?; + let gas_limit = U256::from_big_endian_slice(&input.gas_limit)?; + let max_inclusion_fee_per_gas = + U256::from_big_endian_slice(&input.max_inclusion_fee_per_gas)?; + let max_fee_per_gas = U256::from_big_endian_slice(&input.max_fee_per_gas)?; + + Ok(TransactionEip1559 { + nonce, + max_inclusion_fee_per_gas, + max_fee_per_gas, + gas_limit, + to: to_address, + amount: eth_amount, + payload, + }) + } + + fn user_operation_from_proto( + input: &Proto::SigningInput, + erc4337_payload: Data, + ) -> SigningResult { + let Some(ref user_op) = input.user_operation else { + return Err(SigningError(CommonError::Error_invalid_params)) + }; + + let nonce = U256::from_big_endian_slice(&input.nonce)?; + let gas_limit = U256::from_big_endian_slice(&input.gas_limit)?; + let max_inclusion_fee_per_gas = + U256::from_big_endian_slice(&input.max_inclusion_fee_per_gas)?; + let max_fee_per_gas = U256::from_big_endian_slice(&input.max_fee_per_gas)?; + + let entry_point = Self::parse_address(user_op.entry_point.as_ref())?; + let sender = Self::parse_address(user_op.sender.as_ref())?; + let verification_gas_limit = U256::from_big_endian_slice(&user_op.verification_gas_limit)?; + let pre_verification_gas = U256::from_big_endian_slice(&user_op.pre_verification_gas)?; + + Ok(UserOperation { + nonce, + entry_point, + sender, + init_code: user_op.init_code.to_vec(), + gas_limit, + verification_gas_limit, + max_fee_per_gas, + max_inclusion_fee_per_gas, + pre_verification_gas, + paymaster_and_data: user_op.paymaster_and_data.to_vec(), + payload: erc4337_payload, + }) + } + + #[inline] + fn parse_address(addr: &str) -> AddressResult
{ + Context::Address::from_str(addr).map(Context::Address::into) + } + + #[inline] + fn parse_address_optional(addr: &str) -> AddressResult> { + match Context::Address::from_str_optional(addr) { + Ok(Some(addr)) => Ok(Some(addr.into())), + Ok(None) => Ok(None), + Err(e) => Err(e), + } + } +} diff --git a/rust/tw_evm/src/rlp/buffer.rs b/rust/tw_evm/src/rlp/buffer.rs new file mode 100644 index 00000000000..eea9d9e750e --- /dev/null +++ b/rust/tw_evm/src/rlp/buffer.rs @@ -0,0 +1,64 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_memory::Data; + +#[derive(Default)] +pub struct RlpBuffer { + stream: rlp::RlpStream, +} + +impl RlpBuffer { + /// cbindgen:ignore + const NO_ITEMS: usize = 0; + /// cbindgen:ignore + const ONE_ITEM: usize = 1; + + /// Creates `RlpBuffer` with a default capacity. + pub fn new() -> RlpBuffer { + RlpBuffer::default() + } + + /// Begins an RLP list. + /// Please note that it should be finalized using [`RlpBuffer::finalize_list`]. + pub(crate) fn begin_list(&mut self) { + self.stream.begin_unbounded_list(); + } + + /// Appends an item by its `bytes` representation. + /// This method is usually called from [`RlpEncode::rlp_append`]. + pub fn append_data(&mut self, bytes: &[u8]) { + self.stream.encoder().encode_value(bytes) + } + + /// Appends an empty list. + pub(crate) fn append_empty_list(&mut self) { + self.stream.begin_list(Self::NO_ITEMS); + } + + /// Appends an already encoded with all required headers value. + pub(crate) fn append_raw_encoded(&mut self, bytes: &[u8]) { + self.stream.append_raw(bytes, Self::ONE_ITEM); + } + + /// Finalize the list. + /// + /// # Panic + /// + /// The method may panic if [`RlpBuffer::begin_list`] hasn't been called before. + pub(crate) fn finalize_list(&mut self) { + self.stream.finalize_unbounded_list(); + } + + /// Streams out encoded bytes. + /// + /// # Panic + /// + /// Tte method panic if [`RlpBuffer::begin_list`] has been called without [`RlpBuffer::finalize_list`]. + pub fn finish(self) -> Data { + self.stream.out().into() + } +} diff --git a/rust/tw_evm/src/rlp/impls.rs b/rust/tw_evm/src/rlp/impls.rs new file mode 100644 index 00000000000..1927c01cff8 --- /dev/null +++ b/rust/tw_evm/src/rlp/impls.rs @@ -0,0 +1,43 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::Address; +use crate::rlp::buffer::RlpBuffer; +use crate::rlp::RlpEncode; +use tw_number::U256; + +impl RlpEncode for U256 { + fn rlp_append(&self, buf: &mut RlpBuffer) { + buf.append_data(&self.to_big_endian_compact()) + } +} + +impl RlpEncode for Address { + fn rlp_append(&self, buf: &mut RlpBuffer) { + buf.append_data(self.as_slice()) + } +} + +impl RlpEncode for Option
{ + fn rlp_append(&self, buf: &mut RlpBuffer) { + match self { + Some(ref addr) => addr.rlp_append(buf), + None => buf.append_data(&[]), + } + } +} + +impl<'a> RlpEncode for &'a [u8] { + fn rlp_append(&self, buf: &mut RlpBuffer) { + buf.append_data(self) + } +} + +impl<'a> RlpEncode for &'a str { + fn rlp_append(&self, buf: &mut RlpBuffer) { + buf.append_data(self.as_bytes()) + } +} diff --git a/rust/tw_evm/src/rlp/list.rs b/rust/tw_evm/src/rlp/list.rs new file mode 100644 index 00000000000..4bf58634d1f --- /dev/null +++ b/rust/tw_evm/src/rlp/list.rs @@ -0,0 +1,64 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::rlp::buffer::RlpBuffer; +use crate::rlp::RlpEncode; +use tw_memory::Data; + +/// An RLP list of items. +pub struct RlpList { + buf: RlpBuffer, +} + +impl Default for RlpList { + fn default() -> Self { + RlpList::new() + } +} + +impl RlpList { + /// Creates a default `RlpList`. + pub fn new() -> RlpList { + let mut buf = RlpBuffer::new(); + buf.begin_list(); + RlpList { buf } + } + + /// Appends an item. + pub fn append(&mut self, item: T) -> &mut Self + where + T: RlpEncode, + { + item.rlp_append(&mut self.buf); + self + } + + /// Appends a sublist. + pub fn append_list(&mut self, list: RlpList) -> &mut Self { + let encoded_list = list.finish(); + self.buf.append_data(encoded_list.as_ref()); + self + } + + /// Appends an empty sublist. + pub fn append_empty_list(&mut self) -> &mut Self { + self.buf.append_empty_list(); + self + } + + /// Appends an already encoded with all required headers value. + pub fn append_raw_encoded(&mut self, encoded: &[u8]) -> &mut Self { + self.buf.append_raw_encoded(encoded); + self + } + + /// Finalizes the RLP list buffer. Returns encoded RLP list with a header. + #[must_use] + pub fn finish(mut self) -> Data { + self.buf.finalize_list(); + self.buf.finish() + } +} diff --git a/rust/tw_evm/src/rlp/mod.rs b/rust/tw_evm/src/rlp/mod.rs new file mode 100644 index 00000000000..88bbe1ff550 --- /dev/null +++ b/rust/tw_evm/src/rlp/mod.rs @@ -0,0 +1,16 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::rlp::buffer::RlpBuffer; + +pub mod buffer; +pub mod impls; +pub mod list; + +/// The trait should be implemented for all types that need to be encoded in RLP. +pub trait RlpEncode { + fn rlp_append(&self, buf: &mut RlpBuffer); +} diff --git a/rust/tw_evm/src/signature.rs b/rust/tw_evm/src/signature.rs new file mode 100644 index 00000000000..0949cb8027e --- /dev/null +++ b/rust/tw_evm/src/signature.rs @@ -0,0 +1,48 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::ops::BitXor; +use tw_number::{NumberResult, U256}; + +/// EIP155 Eth encoding of V, of the form 27+v, or 35+chainID*2+v. +/// cbindgin:ignore +pub const ETHEREUM_SIGNATURE_V_OFFSET: u8 = 27; + +/// Embeds `chain_id` in `v` param, for replay protection, legacy or EIP155. +#[inline] +pub fn replay_protection(chain_id: U256, v: u8) -> NumberResult { + if chain_id.is_zero() { + legacy_replay_protection(v) + } else { + eip155_replay_protection(chain_id, v) + } +} + +/// Embeds `chain_id` in `v` param, for replay protection, legacy. +#[inline] +pub fn legacy_replay_protection(v: u8) -> NumberResult { + U256::from(v).checked_add(ETHEREUM_SIGNATURE_V_OFFSET) +} + +/// Embeds `chain_id` in `v` param, for replay protection, EIP155. +#[inline] +pub fn eip155_replay_protection(chain_id: U256, v: u8) -> NumberResult { + // chain_id + chain_id + 35u8 + v + chain_id + .checked_add(chain_id)? + .checked_add(35_u64)? + .checked_add(v) +} + +/// Removes EIP155 or legacy replay protection. +#[inline] +pub fn remove_replay_protection(v: u8) -> u8 { + const BIT_MASK: u8 = 0x01; + if v >= ETHEREUM_SIGNATURE_V_OFFSET { + return (v & BIT_MASK).bitxor(BIT_MASK); + } + v +} diff --git a/rust/tw_evm/src/transaction/mod.rs b/rust/tw_evm/src/transaction/mod.rs new file mode 100644 index 00000000000..a80521be11c --- /dev/null +++ b/rust/tw_evm/src/transaction/mod.rs @@ -0,0 +1,113 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +//! Transactions can be: +//! - Non-typed (legacy, pre-EIP2718) transactions: +//! -- simple ETH transfer +//! -- others with payload, function call, e.g. ERC20 transfer +//! - Typed transactions (enveloped, EIP2718), with specific type and transaction payload +//! - User operations (EIP4337) + +use crate::transaction::signature::EthSignature; +use tw_coin_entry::error::SigningResult; +use tw_hash::{sha3::keccak256, H256}; +use tw_keypair::ecdsa::secp256k1; +use tw_memory::Data; +use tw_number::U256; + +pub mod signature; +pub mod transaction_eip1559; +pub mod transaction_non_typed; +pub mod user_operation; + +pub trait TransactionCommon { + fn payload(&self) -> Data; +} + +pub trait UnsignedTransaction: TransactionCommon { + type SignedTransaction: SignedTransaction + 'static; + + fn pre_hash(&self, chain_id: U256) -> H256 { + let hash = keccak256(&self.encode(chain_id)); + H256::try_from(hash.as_slice()).expect("keccak256 returns 32 bytes") + } + + fn encode(&self, chain_id: U256) -> Data; + + fn try_into_signed( + self, + signature: secp256k1::Signature, + chain_id: U256, + ) -> SigningResult; +} + +pub trait SignedTransaction: TransactionCommon { + type Signature: EthSignature; + + fn encode(&self) -> Data; + + fn signature(&self) -> &Self::Signature; +} + +pub trait UnsignedTransactionBox: TransactionCommon { + fn into_boxed(self) -> Box + where + Self: Sized + 'static, + { + Box::new(self) + } + + fn pre_hash(&self, chain_id: U256) -> H256; + + fn encode(&self, chain_id: U256) -> Data; + + fn try_into_signed( + self: Box, + signature: secp256k1::Signature, + chain_id: U256, + ) -> SigningResult>; +} + +impl UnsignedTransactionBox for T +where + T: UnsignedTransaction, +{ + fn pre_hash(&self, chain_id: U256) -> H256 { + ::pre_hash(self, chain_id) + } + + fn encode(&self, chain_id: U256) -> Data { + ::encode(self, chain_id) + } + + fn try_into_signed( + self: Box, + signature: secp256k1::Signature, + chain_id: U256, + ) -> SigningResult> { + let signed = ::try_into_signed(*self, signature, chain_id)?; + Ok(Box::new(signed)) + } +} + +pub trait SignedTransactionBox: TransactionCommon { + fn encode(&self) -> Data; + + fn signature(&self) -> &dyn EthSignature; +} + +impl SignedTransactionBox for T +where + T: SignedTransaction, +{ + fn encode(&self) -> Data { + ::encode(self) + } + + fn signature(&self) -> &dyn EthSignature { + ::signature(self) + } +} diff --git a/rust/tw_evm/src/transaction/signature.rs b/rust/tw_evm/src/transaction/signature.rs new file mode 100644 index 00000000000..198ca2e91bb --- /dev/null +++ b/rust/tw_evm/src/transaction/signature.rs @@ -0,0 +1,184 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::signature::replay_protection; +use tw_hash::H520; +use tw_keypair::ecdsa::secp256k1; +use tw_keypair::{KeyPairError, KeyPairResult}; +use tw_number::U256; + +pub trait EthSignature { + fn v(&self) -> U256; + + fn r(&self) -> U256; + + fn s(&self) -> U256; +} + +/// R-S-V Signature values. +pub struct Signature { + v: U256, + r: U256, + s: U256, + rsv: H520, +} + +impl Signature { + #[inline] + pub fn new(sign: secp256k1::Signature) -> Self { + Signature { + v: U256::from(sign.v()), + r: U256::from_big_endian(sign.r()), + s: U256::from_big_endian(sign.s()), + rsv: sign.to_bytes(), + } + } + + #[inline] + pub fn to_rsv_bytes(&self) -> H520 { + self.rsv + } +} + +impl EthSignature for Signature { + #[inline] + fn v(&self) -> U256 { + self.v + } + + #[inline] + fn r(&self) -> U256 { + self.r + } + + #[inline] + fn s(&self) -> U256 { + self.s + } +} + +/// R-S-V Signature values EIP115. +pub struct SignatureEip155 { + v: U256, + r: U256, + s: U256, +} + +impl SignatureEip155 { + #[inline] + pub fn new(sign: secp256k1::Signature, chain_id: U256) -> KeyPairResult { + let v = + replay_protection(chain_id, sign.v()).map_err(|_| KeyPairError::InvalidSignature)?; + Ok(SignatureEip155 { + v, + r: U256::from_big_endian(sign.r()), + s: U256::from_big_endian(sign.s()), + }) + } +} + +impl EthSignature for SignatureEip155 { + #[inline] + fn v(&self) -> U256 { + self.v + } + + #[inline] + fn r(&self) -> U256 { + self.r + } + + #[inline] + fn s(&self) -> U256 { + self.s + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tw_encoding::hex; + use tw_hash::H256; + + #[test] + fn test_signature_no_eip155() { + let secp_bytes = hex::decode("d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a00").unwrap(); + let secp_sign = secp256k1::Signature::from_bytes(secp_bytes.as_slice()).unwrap(); + + let eth_sign = Signature::new(secp_sign); + + let r = H256::from("d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47"); + let s = H256::from("786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a"); + let v = hex::decode("00").unwrap(); + + assert_eq!(eth_sign.r(), U256::from_big_endian(r)); + assert_eq!(eth_sign.s(), U256::from_big_endian(s)); + assert_eq!( + eth_sign.v(), + U256::from_big_endian_slice(v.as_slice()).unwrap() + ); + } + + #[test] + fn test_signature_eip155_legacy() { + let secp_bytes = hex::decode("d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a00").unwrap(); + let secp_sign = secp256k1::Signature::from_bytes(secp_bytes.as_slice()).unwrap(); + + let chain_id = U256::zero(); + let eth_sign = SignatureEip155::new(secp_sign, chain_id).unwrap(); + + let r = H256::from("d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47"); + let s = H256::from("786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a"); + let v = hex::decode("1b").unwrap(); + + assert_eq!(eth_sign.r(), U256::from_big_endian(r)); + assert_eq!(eth_sign.s(), U256::from_big_endian(s)); + assert_eq!( + eth_sign.v(), + U256::from_big_endian_slice(v.as_slice()).unwrap() + ); + } + + #[test] + fn test_signature_eip155_v0() { + let secp_bytes = hex::decode("d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a00").unwrap(); + let secp_sign = secp256k1::Signature::from_bytes(secp_bytes.as_slice()).unwrap(); + + let chain_id = U256::from(1_u64); + let eth_sign = SignatureEip155::new(secp_sign, chain_id).unwrap(); + + let r = H256::from("d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47"); + let s = H256::from("786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a"); + let v = hex::decode("25").unwrap(); + + assert_eq!(eth_sign.r(), U256::from_big_endian(r)); + assert_eq!(eth_sign.s(), U256::from_big_endian(s)); + assert_eq!( + eth_sign.v(), + U256::from_big_endian_slice(v.as_slice()).unwrap() + ); + } + + #[test] + fn test_signature_eip155_v1() { + let secp_bytes = hex::decode("42556c4f2a3f4e4e639cca524d1da70e60881417d4643e5382ed110a52719eaf172f591a2a763d0bd6b13d042d8c5eb66e87f129c9dc77ada66b6041012db2b301").unwrap(); + let secp_sign = secp256k1::Signature::from_bytes(secp_bytes.as_slice()).unwrap(); + + let chain_id = U256::from(1_u64); + let eth_sign = SignatureEip155::new(secp_sign, chain_id).unwrap(); + + let r = H256::from("42556c4f2a3f4e4e639cca524d1da70e60881417d4643e5382ed110a52719eaf"); + let s = H256::from("172f591a2a763d0bd6b13d042d8c5eb66e87f129c9dc77ada66b6041012db2b3"); + let v = hex::decode("26").unwrap(); + + assert_eq!(eth_sign.r(), U256::from_big_endian(r)); + assert_eq!(eth_sign.s(), U256::from_big_endian(s)); + assert_eq!( + eth_sign.v(), + U256::from_big_endian_slice(v.as_slice()).unwrap() + ); + } +} diff --git a/rust/tw_evm/src/transaction/transaction_eip1559.rs b/rust/tw_evm/src/transaction/transaction_eip1559.rs new file mode 100644 index 00000000000..352a5b53a8e --- /dev/null +++ b/rust/tw_evm/src/transaction/transaction_eip1559.rs @@ -0,0 +1,138 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::Address; +use crate::rlp::list::RlpList; +use crate::transaction::signature::{EthSignature, Signature}; +use crate::transaction::{SignedTransaction, TransactionCommon, UnsignedTransaction}; +use tw_coin_entry::error::SigningResult; +use tw_keypair::ecdsa::secp256k1; +use tw_memory::Data; +use tw_number::U256; + +const EIP1559_TX_TYPE: u8 = 0x02; + +/// EIP1559 transaction. +pub struct TransactionEip1559 { + pub nonce: U256, + pub max_inclusion_fee_per_gas: U256, + pub max_fee_per_gas: U256, + pub gas_limit: U256, + pub to: Option
, + pub amount: U256, + pub payload: Data, +} + +impl TransactionCommon for TransactionEip1559 { + #[inline] + fn payload(&self) -> Data { + self.payload.clone() + } +} + +impl UnsignedTransaction for TransactionEip1559 { + type SignedTransaction = SignedTransactionEip1559; + + #[inline] + fn encode(&self, chain_id: U256) -> Data { + encode_transaction(self, chain_id, None) + } + + #[inline] + fn try_into_signed( + self, + signature: secp256k1::Signature, + chain_id: U256, + ) -> SigningResult { + Ok(SignedTransactionEip1559 { + unsigned: self, + signature: Signature::new(signature), + chain_id, + }) + } +} + +pub struct SignedTransactionEip1559 { + unsigned: TransactionEip1559, + signature: Signature, + chain_id: U256, +} + +impl TransactionCommon for SignedTransactionEip1559 { + #[inline] + fn payload(&self) -> Data { + self.unsigned.payload.clone() + } +} + +impl SignedTransaction for SignedTransactionEip1559 { + type Signature = Signature; + + #[inline] + fn encode(&self) -> Data { + encode_transaction(&self.unsigned, self.chain_id, Some(&self.signature)) + } + + #[inline] + fn signature(&self) -> &Self::Signature { + &self.signature + } +} + +fn encode_transaction( + tx: &TransactionEip1559, + chain_id: U256, + signature: Option<&Signature>, +) -> Data { + let mut list = RlpList::new(); + list.append(chain_id) + .append(tx.nonce) + .append(tx.max_inclusion_fee_per_gas) + .append(tx.max_fee_per_gas) + .append(tx.gas_limit) + .append(tx.to) + .append(tx.amount) + .append(tx.payload.as_slice()) + // empty `access_list`. + .append_empty_list(); + + if let Some(signature) = signature { + list.append(signature.v()); + list.append(signature.r()); + list.append(signature.s()); + } + + let tx_encoded = list.finish(); + + let mut envelope = Vec::with_capacity(tx_encoded.len() + 1); + envelope.push(EIP1559_TX_TYPE); + envelope.extend_from_slice(tx_encoded.as_slice()); + envelope +} + +#[cfg(test)] +mod tests { + use super::*; + use tw_encoding::hex; + + #[test] + fn test_encode_transaction_eip1559() { + let tx = TransactionEip1559 { + nonce: U256::from(6u64), + max_inclusion_fee_per_gas: U256::from(2_000_000_000u64), + max_fee_per_gas: U256::from(3_000_000_000u64), + gas_limit: U256::from(21100u32), + to: Some(Address::from("0x6b175474e89094c44da98b954eedeac495271d0f")), + amount: U256::zero(), + payload: hex::decode("a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000001ee0c29f50cb1").unwrap(), + }; + let chain_id = U256::from(10u64); + let actual = tx.encode(chain_id); + + let expected = "02f86c0a06847735940084b2d05e0082526c946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000001ee0c29f50cb1c0"; + assert_eq!(hex::encode(actual, false), expected); + } +} diff --git a/rust/tw_evm/src/transaction/transaction_non_typed.rs b/rust/tw_evm/src/transaction/transaction_non_typed.rs new file mode 100644 index 00000000000..67d386def60 --- /dev/null +++ b/rust/tw_evm/src/transaction/transaction_non_typed.rs @@ -0,0 +1,158 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::Address; +use crate::rlp::list::RlpList; +use crate::transaction::signature::{EthSignature, SignatureEip155}; +use crate::transaction::{SignedTransaction, TransactionCommon, UnsignedTransaction}; +use tw_coin_entry::error::SigningResult; +use tw_keypair::ecdsa::secp256k1; +use tw_memory::Data; +use tw_number::U256; + +/// Original transaction format, with no explicit type, legacy as pre-EIP2718. +pub struct TransactionNonTyped { + pub nonce: U256, + pub gas_price: U256, + pub gas_limit: U256, + pub to: Option
, + pub amount: U256, + pub payload: Data, +} + +impl TransactionCommon for TransactionNonTyped { + #[inline] + fn payload(&self) -> Data { + self.payload.to_vec() + } +} + +impl UnsignedTransaction for TransactionNonTyped { + type SignedTransaction = SignedTransactionNonTyped; + + #[inline] + fn encode(&self, chain_id: U256) -> Data { + encode_transaction(self, chain_id, None) + } + + #[inline] + fn try_into_signed( + self, + signature: secp256k1::Signature, + chain_id: U256, + ) -> SigningResult { + Ok(SignedTransactionNonTyped { + unsigned: self, + signature: SignatureEip155::new(signature, chain_id)?, + chain_id, + }) + } +} + +pub struct SignedTransactionNonTyped { + unsigned: TransactionNonTyped, + signature: SignatureEip155, + chain_id: U256, +} + +impl TransactionCommon for SignedTransactionNonTyped { + #[inline] + fn payload(&self) -> Data { + self.unsigned.payload.clone() + } +} + +impl SignedTransaction for SignedTransactionNonTyped { + type Signature = SignatureEip155; + + #[inline] + fn encode(&self) -> Data { + encode_transaction(&self.unsigned, self.chain_id, Some(&self.signature)) + } + + #[inline] + fn signature(&self) -> &Self::Signature { + &self.signature + } +} + +fn encode_transaction( + tx: &TransactionNonTyped, + chain_id: U256, + signature: Option<&SignatureEip155>, +) -> Data { + let mut list = RlpList::new(); + list.append(tx.nonce) + .append(tx.gas_price) + .append(tx.gas_limit) + .append(tx.to) + .append(tx.amount) + .append(tx.payload.as_slice()); + + let (v, r, s) = match signature { + Some(sign) => (sign.v(), sign.r(), sign.s()), + None => (chain_id, U256::zero(), U256::zero()), + }; + list.append(v).append(r).append(s); + list.finish() +} + +#[cfg(test)] +mod tests { + use super::*; + use tw_encoding::hex; + + #[test] + fn test_encode_unsigned_non_typed_zero() { + let tx = TransactionNonTyped { + nonce: U256::zero(), + gas_price: U256::zero(), + gas_limit: U256::zero(), + to: Some(Address::default()), + amount: U256::zero(), + payload: Vec::new(), + }; + let chain_id = U256::zero(); + let actual = tx.encode(chain_id); + + let expected = "dd8080809400000000000000000000000000000000000000008080808080"; + assert_eq!(hex::encode(actual, false), expected); + } + + #[test] + fn test_encode_unsigned_non_typed() { + let tx = TransactionNonTyped { + nonce: U256::zero(), + gas_price: U256::from(42_000_000_000_u64), + gas_limit: U256::from(78009_u32), + to: Some(Address::from("0x6b175474e89094c44da98b954eedeac495271d0f")), + amount: U256::zero(), + payload: hex::decode("a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000").unwrap(), + }; + let chain_id = U256::from(10_u8); + let actual = tx.encode(chain_id); + + let expected = "f86a808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec800000a8080"; + assert_eq!(hex::encode(actual, false), expected); + } + + #[test] + fn test_encode_unsigned_non_typed_pre_hash() { + let tx = TransactionNonTyped { + nonce: U256::from(9_u64), + gas_price: U256::from(20_000_000_000_u64), + gas_limit: U256::from(21_000_u64), + to: Some(Address::from("0x3535353535353535353535353535353535353535")), + amount: U256::from(1_000_000_000_000_000_000_u64), + payload: Vec::default(), + }; + let chain_id = U256::from(1_u64); + let actual = tx.pre_hash(chain_id); + + let expected = "daf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53"; + assert_eq!(hex::encode(actual, false), expected); + } +} diff --git a/rust/tw_evm/src/transaction/user_operation.rs b/rust/tw_evm/src/transaction/user_operation.rs new file mode 100644 index 00000000000..6abe6bd048b --- /dev/null +++ b/rust/tw_evm/src/transaction/user_operation.rs @@ -0,0 +1,203 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::abi::encode::encode_tokens; +use crate::abi::non_empty_array::NonEmptyBytes; +use crate::abi::token::Token; +use crate::address::Address; +use crate::transaction::signature::Signature; +use crate::transaction::{SignedTransaction, TransactionCommon, UnsignedTransaction}; +use serde::Serialize; +use tw_coin_entry::error::SigningResult; +use tw_encoding::hex; +use tw_hash::sha3::keccak256; +use tw_hash::H256; +use tw_memory::Data; +use tw_number::U256; + +/// EIP4337 UserOperation +/// https://github.com/ethereum/EIPs/blob/3fd65b1a782912bfc18cb975c62c55f733c7c96e/EIPS/eip-4337.md#specification +pub struct UserOperation { + pub nonce: U256, + pub entry_point: Address, + pub sender: Address, + pub init_code: Data, + pub gas_limit: U256, + pub verification_gas_limit: U256, + pub max_fee_per_gas: U256, + pub max_inclusion_fee_per_gas: U256, + pub pre_verification_gas: U256, + pub paymaster_and_data: Data, + pub payload: Data, +} + +impl TransactionCommon for UserOperation { + #[inline] + fn payload(&self) -> Data { + self.payload.clone() + } +} + +impl UnsignedTransaction for UserOperation { + type SignedTransaction = SignedUserOperation; + + fn pre_hash(&self, chain_id: U256) -> H256 { + let encode_hash = keccak256(&self.encode(chain_id)); + let encode_hash = + NonEmptyBytes::new(encode_hash).expect("keccak256 must not return an empty hash"); + + let tokens = [ + Token::FixedBytes(encode_hash), + Token::Address(self.entry_point), + Token::u256(chain_id), + ]; + let encoded = encode_tokens(&tokens); + let pre_hash = keccak256(&encoded); + H256::try_from(pre_hash.as_slice()).expect("keccak256 returns 32 bytes") + } + + fn encode(&self, _chain_id: U256) -> Data { + let init_code_hash = keccak256(&self.init_code); + let init_code_hash = + NonEmptyBytes::new(init_code_hash).expect("keccak256 must not return an empty hash"); + + let payload_hash = keccak256(&self.payload); + let payload_hash = + NonEmptyBytes::new(payload_hash).expect("keccak256 must not return an empty hash"); + + let paymaster_and_data_hash = keccak256(&self.paymaster_and_data); + let paymaster_and_data_hash = NonEmptyBytes::new(paymaster_and_data_hash) + .expect("keccak256 must not return an empty hash"); + + let tokens = [ + Token::Address(self.sender), + Token::u256(self.nonce), + Token::FixedBytes(init_code_hash), + Token::FixedBytes(payload_hash), + Token::u256(self.gas_limit), + Token::u256(self.verification_gas_limit), + Token::u256(self.pre_verification_gas), + Token::u256(self.max_fee_per_gas), + Token::u256(self.max_inclusion_fee_per_gas), + Token::FixedBytes(paymaster_and_data_hash), + ]; + + encode_tokens(&tokens) + } + + #[inline] + fn try_into_signed( + self, + signature: tw_keypair::ecdsa::secp256k1::Signature, + _chain_id: U256, + ) -> SigningResult { + Ok(SignedUserOperation { + unsigned: self, + signature: Signature::new(signature), + }) + } +} + +pub struct SignedUserOperation { + unsigned: UserOperation, + signature: Signature, +} + +impl TransactionCommon for SignedUserOperation { + #[inline] + fn payload(&self) -> Data { + self.unsigned.payload.clone() + } +} + +impl SignedTransaction for SignedUserOperation { + type Signature = Signature; + + fn encode(&self) -> Data { + let mut signature = self.signature.to_rsv_bytes(); + signature[64] += 27; + + let prefix = true; + let tx = SignedUserOperationSerde { + call_data: hex::encode(&self.unsigned.payload, prefix), + call_gas_limit: self.unsigned.gas_limit.to_string(), + init_code: hex::encode(&self.unsigned.init_code, prefix), + max_fee_per_gas: self.unsigned.max_fee_per_gas.to_string(), + max_priority_fee_per_gas: self.unsigned.max_inclusion_fee_per_gas.to_string(), + nonce: self.unsigned.nonce.to_string(), + paymaster_and_data: hex::encode(&self.unsigned.paymaster_and_data, prefix), + pre_verification_gas: self.unsigned.pre_verification_gas.to_string(), + sender: self.unsigned.sender.to_string(), + signature: hex::encode(signature.as_slice(), prefix), + verification_gas_limit: self.unsigned.verification_gas_limit.to_string(), + }; + serde_json::to_string(&tx) + .expect("Simple structure should never fail on serialization") + .into_bytes() + } + + #[inline] + fn signature(&self) -> &Self::Signature { + &self.signature + } +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct SignedUserOperationSerde { + call_data: String, + call_gas_limit: String, + init_code: String, + max_fee_per_gas: String, + max_priority_fee_per_gas: String, + nonce: String, + paymaster_and_data: String, + pre_verification_gas: String, + sender: String, + signature: String, + verification_gas_limit: String, +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::abi::prebuild::erc4337::{Erc4337SimpleAccount, ExecuteArgs}; + + #[test] + fn test_encode_user_operation() { + let chain_id = U256::from(97u64); + + let execute_args = ExecuteArgs { + to: Address::from("0x61061fCAE11fD5461535e134EfF67A98CFFF44E9"), + value: U256::from(0x2_386f_26fc_10000u64), + data: Vec::default(), + }; + let payload = Erc4337SimpleAccount::encode_execute(execute_args).unwrap(); + + let user_op = UserOperation { + nonce: U256::from(2u64), + entry_point: Address::from("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"), + sender: Address::from("0xb16Db98B365B1f89191996942612B14F1Da4Bd5f"), + init_code: Vec::default(), + gas_limit: U256::from(0x186a0u64), + verification_gas_limit: U256::from(0x186a0u64), + max_fee_per_gas: U256::from(0x1_a339_c9e9u64), + max_inclusion_fee_per_gas: U256::from(0x1_a339_c9e9u64), + pre_verification_gas: U256::from(0xb708u64), + paymaster_and_data: Vec::default(), + payload, + }; + + let encoded = hex::encode(user_op.encode(chain_id), false); + let expected = "000000000000000000000000b16db98b365b1f89191996942612b14f1da4bd5f0000000000000000000000000000000000000000000000000000000000000002c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470fbec3c1db0378685d954edd265aa6eb11e8474d828e6bda151810263838e457000000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000186a0000000000000000000000000000000000000000000000000000000000000b70800000000000000000000000000000000000000000000000000000001a339c9e900000000000000000000000000000000000000000000000000000001a339c9e9c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"; + assert_eq!(encoded, expected); + + let pre_hash = user_op.pre_hash(chain_id); + let expected_pre_hash = + H256::from("2d37191a8688f69090451ed90a0a9ba69d652c2062ee9d023b3ebe964a3ed2ae"); + assert_eq!(pre_hash, expected_pre_hash); + } +} diff --git a/rust/tw_evm/tests/abi_encoder.rs b/rust/tw_evm/tests/abi_encoder.rs new file mode 100644 index 00000000000..b94533bbb27 --- /dev/null +++ b/rust/tw_evm/tests/abi_encoder.rs @@ -0,0 +1,689 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde_json::{json, Value as Json}; +use std::borrow::Cow; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_evm::abi::AbiErrorKind; +use tw_evm::evm_context::StandardEvmContext; +use tw_evm::modules::abi_encoder::AbiEncoder; +use tw_number::{I256, U256}; +use tw_proto::EthereumAbi::Proto; + +use Proto::mod_ParamType::OneOfparam as ParamTypeEnum; +use Proto::mod_ParamsDecodingInput::OneOfabi as AbiEnum; +use Proto::mod_Token::OneOftoken as TokenEnum; + +const SWAP_V2_ABI: &str = include_str!("data/swap_v2.json"); +const SWAP_V2_DECODED: &str = include_str!("data/swap_v2_decoded.json"); + +fn param(name: &str, kind: ParamTypeEnum<'static>) -> Proto::Param<'static> { + Proto::Param { + name: name.to_string().into(), + param: Some(Proto::ParamType { param: kind }), + } +} + +fn named_token(name: &str, token: TokenEnum<'static>) -> Proto::Token<'static> { + Proto::Token { + name: Cow::Owned(name.to_string()), + token, + } +} + +fn u_number_n(value: u64) -> TokenEnum<'static> { + TokenEnum::number_uint(Proto::NumberNParam { + bits: BITS, + value: U256::encode_be_compact(value), + }) +} + +fn s_number_n(value: i64) -> TokenEnum<'static> { + TokenEnum::number_int(Proto::NumberNParam { + bits: BITS, + value: I256::encode_be_compact(value), + }) +} + +fn number_type_n() -> Proto::NumberNType { + Proto::NumberNType { bits: BITS } +} + +fn array( + element_type: ParamTypeEnum<'static>, + values: Vec>, +) -> Proto::ArrayParam<'static> { + let elements = values + .into_iter() + .map(|token| Proto::Token { + name: "".into(), + token, + }) + .collect(); + let element_type = Some(Proto::ParamType { + param: element_type, + }); + Proto::ArrayParam { + element_type, + elements, + } +} + +fn array_type(element_type: ParamTypeEnum<'static>) -> Box> { + Box::new(Proto::ArrayType { + element_type: Some(Box::new(Proto::ParamType { + param: element_type, + })), + }) +} + +fn tuple(params: I) -> TokenEnum<'static> +where + I: IntoIterator>, +{ + TokenEnum::tuple(Proto::TupleParam { + params: params.into_iter().collect(), + }) +} + +fn test_encode_contract_call_impl( + function_name: &str, + tokens: Vec>, + expected_call: &str, +) { + let input = Proto::FunctionEncodingInput { + function_name: function_name.into(), + tokens, + }; + + let output = AbiEncoder::::encode_contract_call(input); + assert_eq!(output.error, AbiErrorKind::OK); + assert!(output.error_message.is_empty()); + assert_eq!(output.encoded.to_hex(), expected_call); +} + +fn test_decode_params_impl( + encoded: &str, + expected_tokens: Vec>, + abi: AbiEnum<'_>, +) { + let input = Proto::ParamsDecodingInput { + encoded: encoded.decode_hex().unwrap().into(), + abi, + }; + + let output = AbiEncoder::::decode_params(input); + assert_eq!(output.error, AbiErrorKind::OK); + assert!(output.error_message.is_empty()); + assert_eq!(output.tokens, expected_tokens); +} + +fn test_decode_contract_call_impl( + encoded: &str, + abi_json: &str, + decoded_json: &str, + expected_tokens: Vec>, +) { + let input = Proto::ContractCallDecodingInput { + encoded: encoded.decode_hex().unwrap().into(), + smart_contract_abi_json: Cow::Borrowed(abi_json), + }; + + let output = AbiEncoder::::decode_contract_call(input); + assert_eq!(output.error, AbiErrorKind::OK); + assert!(output.error_message.is_empty()); + + let actual_json: Json = serde_json::from_str(&output.decoded_json).unwrap(); + let expected_json: Json = serde_json::from_str(decoded_json).unwrap(); + assert_eq!(actual_json, expected_json); + assert_eq!(output.tokens, expected_tokens); +} + +mod swap_v2 { + use super::*; + + const FUNCTION_NAME: &str = "callBridgeCall"; + const ENCODED_CALL: &str = "846a1bc6000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000470de4df82000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000740000000000000000000000000000000000000000000000000000000000000078000000000000000000000000000000000000000000000000000000000000007c00000000000000000000000000000000000000000000000000000000000000820000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000099a58482bd75cbab83b27ec03ca68ff489b5788f00000000000000000000000000000000000000000000000000470de4df820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099a58482bd75cbab83b27ec03ca68ff489b5788f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000000003840651cb35000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000bebc44782c7db0a1a60cb6fe97d0b483032ff1c7000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000470de4df8200000000000000000000000000000000000000000000000000000000298ce42936ed0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d66600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045553444300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000762696e616e636500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863653136463639333735353230616230313337376365374238386635424138433438463844363636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000072000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000ac000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf300000000000000000000000000000000000000000000000000000000000000010000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb1400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf300000000000000000000000055d398326f99059ff775485246999027b319795500000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd300000000000000000000000000000000000000000000000000000189c04a7044000000000000000000000000000000000000000000000000000029a23529cf68000000000000000000000000000000000000000000005af4f3f913bd553d03b900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf30000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000100000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000104414bf38900000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c00000000000000000000000000000000000000000000000000000000000001f40000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd300000000000000000000000000000000000000000000000000000189c04a7045000000000000000000000000000000000000000000005b527785e694f805bdd300000000000000000000000000000000000000000000005f935a1fa5c4a6ec61000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000055d398326f99059ff775485246999027b319795500000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + fn calls_array_type() -> ParamTypeEnum<'static> { + ParamTypeEnum::tuple(Proto::TupleType { + params: vec![ + param("callType", ParamTypeEnum::number_uint(number_type_n::<8>())), + param("target", ParamTypeEnum::address(Proto::AddressType {})), + param("value", ParamTypeEnum::number_uint(number_type_n::<256>())), + param( + "callData", + ParamTypeEnum::byte_array(Proto::ByteArrayType {}), + ), + param( + "payload", + ParamTypeEnum::byte_array(Proto::ByteArrayType {}), + ), + ], + }) + } + + fn token_values() -> Vec> { + let call_data_1 = "0x095ea7b300000000000000000000000099a58482bd75cbab83b27ec03ca68ff489b5788f00000000000000000000000000000000000000000000000000470de4df820000".decode_hex().unwrap(); + let call_data_2 = "0x0651cb35000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000bebc44782c7db0a1a60cb6fe97d0b483032ff1c7000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000470de4df8200000000000000000000000000000000000000000000000000000000298ce42936ed0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666".decode_hex().unwrap(); + + let calls = vec![ + tuple(vec![ + named_token("callType", u_number_n::<8>(0)), + named_token( + "target", + TokenEnum::address("0xdAC17F958D2ee523a2206206994597C13D831ec7".into()), + ), + named_token("value", u_number_n::<256>(0)), + named_token("callData", TokenEnum::byte_array(call_data_1.into())), + named_token("payload", TokenEnum::byte_array(Cow::default())), + ]), + tuple(vec![ + named_token("callType", u_number_n::<8>(0)), + named_token( + "target", + TokenEnum::address("0x99a58482BD75cbab83b27EC03CA68fF489b5788f".into()), + ), + named_token("value", u_number_n::<256>(0)), + named_token("callData", TokenEnum::byte_array(call_data_2.into())), + named_token("payload", TokenEnum::byte_array(Cow::default())), + ]), + ]; + + let payload = "0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000072000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000ac000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf300000000000000000000000000000000000000000000000000000000000000010000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb1400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf300000000000000000000000055d398326f99059ff775485246999027b319795500000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd300000000000000000000000000000000000000000000000000000189c04a7044000000000000000000000000000000000000000000000000000029a23529cf68000000000000000000000000000000000000000000005af4f3f913bd553d03b900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf30000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000100000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000104414bf38900000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c00000000000000000000000000000000000000000000000000000000000001f40000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd300000000000000000000000000000000000000000000000000000189c04a7045000000000000000000000000000000000000000000005b527785e694f805bdd300000000000000000000000000000000000000000000005f935a1fa5c4a6ec61000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000055d398326f99059ff775485246999027b319795500000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".decode_hex().unwrap(); + vec![ + named_token( + "token", + TokenEnum::address("0xdAC17F958D2ee523a2206206994597C13D831ec7".into()), + ), + named_token("amount", u_number_n::<256>(20000000000000000)), + named_token("calls", TokenEnum::array(array(calls_array_type(), calls))), + named_token("bridgedTokenSymbol", TokenEnum::string_value("USDC".into())), + named_token( + "destinationChain", + TokenEnum::string_value("binance".into()), + ), + named_token( + "destinationAddress", + TokenEnum::string_value("0xce16F69375520ab01377ce7B88f5BA8C48F8D666".into()), + ), + named_token("payload", TokenEnum::byte_array(payload.into())), + named_token( + "gasRefundRecipient", + TokenEnum::address("0xa140F413C63FBDA84E9008607E678258ffFbC76b".into()), + ), + named_token("enableExpress", TokenEnum::boolean(true)), + ] + } + + #[test] + fn test_decode_contract_call_swap_v2() { + test_decode_contract_call_impl(ENCODED_CALL, SWAP_V2_ABI, SWAP_V2_DECODED, token_values()); + } + + #[test] + fn test_encode_contract_call_swap_v2() { + test_encode_contract_call_impl(FUNCTION_NAME, token_values(), ENCODED_CALL); + } +} + +#[test] +fn test_decode_params_with_abi_json() { + let abi_json = json!([ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "resolver", + "type": "address" + } + ]); + let abi_json = serde_json::to_string(&abi_json).unwrap(); + let encoded = "e71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f0000000000000000000000004976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41"; + + let node_bytes = "0xe71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f" + .decode_hex() + .unwrap(); + let expected_tokens = vec![ + named_token("node", TokenEnum::byte_array_fix(node_bytes.into())), + named_token( + "resolver", + TokenEnum::address("0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41".into()), + ), + ]; + + test_decode_params_impl(encoded, expected_tokens, AbiEnum::abi_json(abi_json.into())); +} + +#[test] +fn test_decode_params_with_abi_params() { + let encoded = "e71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f0000000000000000000000004976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41"; + + let node_bytes = "0xe71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f" + .decode_hex() + .unwrap(); + let expected_tokens = vec![ + named_token("node", TokenEnum::byte_array_fix(node_bytes.into())), + named_token( + "resolver", + TokenEnum::address("0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41".into()), + ), + ]; + + let abi_params = vec![ + param( + "node", + ParamTypeEnum::byte_array_fix(Proto::ByteArrayFixType { size: 32 }), + ), + param("resolver", ParamTypeEnum::address(Proto::AddressType {})), + ]; + + test_decode_params_impl( + encoded, + expected_tokens, + AbiEnum::abi_params(Proto::AbiParams { params: abi_params }), + ); +} + +mod dynamic_arguments { + use super::*; + + const FUNCTION_NAME: &str = "f"; + const ENCODED_CALL_WITH_SIGNATURE: &str = "47b941bf00000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000080313233343536373839300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000789000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000"; + const ENCODED_CALL: &str = "00000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000080313233343536373839300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000789000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000"; + + fn token_values() -> Vec> { + let dynamic_array = array( + ParamTypeEnum::number_uint(number_type_n::<32>()), + vec![u_number_n::<32>(0x456), u_number_n::<32>(0x789)], + ); + let byte_array = vec![0x31u8, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30]; + vec![ + named_token("", u_number_n::<256>(0x123)), + named_token("", TokenEnum::array(dynamic_array)), + named_token("", TokenEnum::byte_array_fix(byte_array.into())), + named_token("", TokenEnum::string_value("Hello, world!".into())), + ] + } + + fn param_types() -> Vec> { + vec![ + param("", ParamTypeEnum::number_uint(number_type_n::<256>())), + param( + "", + ParamTypeEnum::array(array_type( + ParamTypeEnum::number_uint(number_type_n::<32>()), + )), + ), + param( + "", + ParamTypeEnum::byte_array_fix(Proto::ByteArrayFixType { size: 10 }), + ), + param("", ParamTypeEnum::string_param(Proto::StringType {})), + ] + } + + #[test] + fn test_encode_contract_call_with_dynamic_arguments() { + test_encode_contract_call_impl(FUNCTION_NAME, token_values(), ENCODED_CALL_WITH_SIGNATURE); + } + + #[test] + fn test_decode_params_with_dynamic_arguments_case2() { + test_decode_params_impl( + ENCODED_CALL, + token_values(), + AbiEnum::abi_params(Proto::AbiParams { + params: param_types(), + }), + ); + } +} + +mod negative_integers { + use super::*; + + const FUNCTION_NAME: &str = "approve"; + const ENCODED_CALL_WITH_SIGNATURE: &str = "d4e12f2e0000000000000000000000005aaeb6053f3e94c9b9a09f33669435e7ef1beaedfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd"; + const ENCODED_CALL: &str = "0000000000000000000000005aaeb6053f3e94c9b9a09f33669435e7ef1beaedfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd"; + + const ABI_JSON: &str = include_str!("data/abi_with_negative_int.json"); + const DECODED_JSON: &str = include_str!("data/decoded_abi_with_negative_int.json"); + + fn token_values() -> Vec> { + vec![ + named_token( + "_spender", + TokenEnum::address("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed".into()), + ), + named_token("_value", s_number_n::<256>(-3)), + ] + } + + fn param_types() -> Vec> { + vec![ + param("_spender", ParamTypeEnum::address(Proto::AddressType {})), + param("_value", ParamTypeEnum::number_int(number_type_n::<256>())), + ] + } + + #[test] + fn test_encode_contract_call_with_negative_integer() { + test_encode_contract_call_impl(FUNCTION_NAME, token_values(), ENCODED_CALL_WITH_SIGNATURE); + } + + #[test] + fn test_decode_params_with_negative_integer() { + test_decode_params_impl( + ENCODED_CALL, + token_values(), + AbiEnum::abi_params(Proto::AbiParams { + params: param_types(), + }), + ); + } + + #[test] + fn test_decode_contract_call_with_negative_integer() { + test_decode_contract_call_impl( + ENCODED_CALL_WITH_SIGNATURE, + ABI_JSON, + DECODED_JSON, + token_values(), + ); + } +} + +#[test] +fn test_encode_params_monster() { + let byte_array = "3132333435".decode_hex().unwrap(); + + let u1 = u_number_n::<8>(1); + let u2 = u_number_n::<16>(2); + let u3 = u_number_n::<32>(3); + let u4 = u_number_n::<64>(4); + let u5 = u_number_n::<168>(0x123); + let u6 = u_number_n::<256>(0x123); + + let u1t = ParamTypeEnum::number_uint(number_type_n::<8>()); + let u2t = ParamTypeEnum::number_uint(number_type_n::<16>()); + let u3t = ParamTypeEnum::number_uint(number_type_n::<32>()); + let u4t = ParamTypeEnum::number_uint(number_type_n::<64>()); + let u5t = ParamTypeEnum::number_uint(number_type_n::<168>()); + let u6t = ParamTypeEnum::number_uint(number_type_n::<256>()); + + let i1 = s_number_n::<8>(1); + let i2 = s_number_n::<16>(2); + let i3 = s_number_n::<32>(3); + let i4 = s_number_n::<64>(4); + let i5 = s_number_n::<168>(0x123); + let i6 = s_number_n::<256>(0x123); + + let i1t = ParamTypeEnum::number_int(number_type_n::<8>()); + let i2t = ParamTypeEnum::number_int(number_type_n::<16>()); + let i3t = ParamTypeEnum::number_int(number_type_n::<32>()); + let i4t = ParamTypeEnum::number_int(number_type_n::<64>()); + let i5t = ParamTypeEnum::number_int(number_type_n::<168>()); + let i6t = ParamTypeEnum::number_int(number_type_n::<256>()); + + let b = TokenEnum::boolean(true); + let bt = ParamTypeEnum::boolean(Proto::BoolType {}); + let s = TokenEnum::string_value("Hello, world!".into()); + let st = ParamTypeEnum::string_param(Proto::StringType {}); + let a = TokenEnum::address("0xf784682c82526e245f50975190ef0fff4e4fc077".into()); + let at = ParamTypeEnum::address(Proto::AddressType {}); + let bytes = TokenEnum::byte_array(byte_array.clone().into()); + let bytes_t = ParamTypeEnum::byte_array(Proto::ByteArrayType {}); + let fbytes = TokenEnum::byte_array_fix(byte_array.clone().into()); + let fbytes_t = ParamTypeEnum::byte_array_fix(Proto::ByteArrayFixType { + size: byte_array.len() as u64, + }); + + let tokens = vec![ + // Uint + named_token("u1", u1.clone()), + named_token("u2", u2.clone()), + named_token("u3", u3.clone()), + named_token("u4", u4.clone()), + named_token("u5", u5.clone()), + named_token("u6", u6.clone()), + // Int + named_token("i1", i1.clone()), + named_token("i2", i2.clone()), + named_token("i3", i3.clone()), + named_token("i4", i4.clone()), + named_token("i5", i5.clone()), + named_token("i6", i6.clone()), + // Single params + named_token("b", b.clone()), + named_token("s", s.clone()), + named_token("a", a.clone()), + named_token("bytes", bytes.clone()), + named_token("fbytes", fbytes.clone()), + // Array + named_token("a_u1", TokenEnum::array(array(u1t, vec![u1]))), + named_token("a_u2", TokenEnum::array(array(u2t, vec![u2]))), + named_token("a_u3", TokenEnum::array(array(u3t, vec![u3]))), + named_token("a_u4", TokenEnum::array(array(u4t, vec![u4]))), + named_token("a_u5", TokenEnum::array(array(u5t, vec![u5]))), + named_token("a_u6", TokenEnum::array(array(u6t, vec![u6]))), + // Array + named_token("a_i1", TokenEnum::array(array(i1t, vec![i1]))), + named_token("a_i2", TokenEnum::array(array(i2t, vec![i2]))), + named_token("a_i3", TokenEnum::array(array(i3t, vec![i3]))), + named_token("a_i4", TokenEnum::array(array(i4t, vec![i4]))), + named_token("a_i5", TokenEnum::array(array(i5t, vec![i5]))), + named_token("a_i6", TokenEnum::array(array(i6t, vec![i6]))), + // Arrays with single params + named_token("a_b", TokenEnum::array(array(bt, vec![b]))), + named_token("a_s", TokenEnum::array(array(st, vec![s]))), + named_token("a_a", TokenEnum::array(array(at, vec![a]))), + named_token("a_bytes", TokenEnum::array(array(bytes_t, vec![bytes]))), + named_token("a_fbytes", TokenEnum::array(array(fbytes_t, vec![fbytes]))), + ]; + let encoding_input = Proto::FunctionEncodingInput { + function_name: "monster".into(), + tokens, + }; + let output = AbiEncoder::::encode_contract_call(encoding_input); + + assert_eq!(output.error, AbiErrorKind::OK); + assert!(output.error_message.is_empty()); + + // let expected_encoded = "4061f075000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077000000000000000000000000000000000000000000000000000000000000030031323334350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004c00000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000058000000000000000000000000000000000000000000000000000000000000005c00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000531323334350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005313233343500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013132333435000000000000000000000000000000000000000000000000000000"; + let expected_encoded = "70efb5a500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000012300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000012300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000440000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc0770000000000000000000000000000000000000000000000000000000000000480313233343500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000540000000000000000000000000000000000000000000000000000000000000058000000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000068000000000000000000000000000000000000000000000000000000000000006c000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000740000000000000000000000000000000000000000000000000000000000000078000000000000000000000000000000000000000000000000000000000000007c00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000008c00000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000531323334350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005313233343500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013132333435000000000000000000000000000000000000000000000000000000"; + assert_eq!(output.encoded.to_hex(), expected_encoded); +} + +#[test] +fn test_decode_value() { + struct TestInput { + encoded: &'static str, + kind: &'static str, + expected_value_str: &'static str, + expected_value_proto: TokenEnum<'static>, + } + + let num1 = u_number_n::<8>(49); + let num2 = u_number_n::<8>(50); + let num3 = u_number_n::<8>(51); + let address1 = TokenEnum::address("0xF784682C82526e245F50975190EF0fff4E4fC077".into()); + let address2 = TokenEnum::address("0x2e00CD222Cb42B616D86D037Cc494e8ab7F5c9a3".into()); + let bytes1 = TokenEnum::byte_array("0x1011".decode_hex().unwrap().into()); + let bytes2 = TokenEnum::byte_array("0x102222".decode_hex().unwrap().into()); + + let test_values = [ + TestInput { + encoded: "0000000000000000000000000000000000000000000000000000091d0eb3e2af", + kind: "uint256", + expected_value_str: "10020405371567", + expected_value_proto: u_number_n::<256>(10020405371567), + }, + TestInput { + encoded: "0000000000000000000000000000000000000000000000000000091d0eb3e2af0000000000000000000000000000000000000000000000000000000000000000", + kind: "int256", + expected_value_str: "10020405371567", + expected_value_proto: s_number_n::<256>(10020405371567), + }, + TestInput { + encoded: "000000000000000000000000000000000000000000000000000000000000002a", + kind: "uint", + expected_value_str: "42", + expected_value_proto: u_number_n::<256>(42), + }, + TestInput { + encoded: "0000000000000000000000000000000000000000000000000000000000000018", + kind: "uint8", + expected_value_str: "24", + expected_value_proto: u_number_n::<8>(24), + }, + TestInput { + encoded: "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000003100000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000033", + kind: "uint8[]", + expected_value_str: "[49,50,51]", + expected_value_proto: TokenEnum::array(array(ParamTypeEnum::number_uint(number_type_n::<8>()), vec![num1, num2, num3])), + }, + TestInput { + encoded: "000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077", + kind: "address", + expected_value_str: "0xF784682C82526e245F50975190EF0fff4E4fC077", + expected_value_proto: TokenEnum::address( + "0xF784682C82526e245F50975190EF0fff4E4fC077".into(), + ), + }, + TestInput { + encoded: "000000000000000000000000000000000000000000000000000000000000002c48656c6c6f20576f726c64212020202048656c6c6f20576f726c64212020202048656c6c6f20576f726c64210000000000000000000000000000000000000000", + kind: "string", + expected_value_str: "Hello World! Hello World! Hello World!", + expected_value_proto: TokenEnum::string_value( + "Hello World! Hello World! Hello World!".into(), + ), + }, + TestInput { + encoded: "0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc0770000000000000000000000002e00cd222cb42b616d86d037cc494e8ab7f5c9a3", + kind: "address[]", + expected_value_str: r#"["0xF784682C82526e245F50975190EF0fff4E4fC077","0x2e00CD222Cb42B616D86D037Cc494e8ab7F5c9a3"]"#, + expected_value_proto: TokenEnum::array( + array(ParamTypeEnum::address(Proto::AddressType {}), vec![address1, address2])), + }, + TestInput { + encoded: "3132333435363738393000000000000000000000000000000000000000000000", + kind: "bytes10", + expected_value_str: "0x31323334353637383930", + expected_value_proto: TokenEnum::byte_array_fix( + "0x31323334353637383930".decode_hex().unwrap().into(), + ), + }, + TestInput { + encoded: "0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002101100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000031022220000000000000000000000000000000000000000000000000000000000", + kind: "bytes[]", + expected_value_str: r#"["0x1011","0x102222"]"#, + expected_value_proto: TokenEnum::array( + array(ParamTypeEnum::byte_array(Proto::ByteArrayType {}), vec![bytes1, bytes2]) + ), + }, + ]; + + for test in test_values { + let input = Proto::ValueDecodingInput { + encoded: test.encoded.decode_hex().unwrap().into(), + param_type: test.kind.into(), + }; + let output = AbiEncoder::::decode_value(input); + assert_eq!(output.error, AbiErrorKind::OK); + assert!(output.error_message.is_empty()); + + assert_eq!(output.token.unwrap().token, test.expected_value_proto); + assert_eq!(output.param_str, test.expected_value_str); + } +} + +#[test] +fn test_decode_value_error() { + #[track_caller] + fn test_decode_value_error_impl(kind: &str, encoded: &str) { + let input = Proto::ValueDecodingInput { + encoded: encoded.decode_hex().unwrap().into(), + param_type: kind.into(), + }; + let output = AbiEncoder::::decode_value(input); + assert_ne!(output.error, AbiErrorKind::OK); + assert!(!output.error_message.is_empty()); + } + + test_decode_value_error_impl("tuple[7][][2][2]", "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000375696e742a6c"); + test_decode_value_error_impl("bytes0[0][][2][6]", "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000375696e742a6c"); +} + +#[test] +fn test_encode_params_invalid_address() { + struct TestInput { + token: TokenEnum<'static>, + error: AbiErrorKind, + } + + let test_inputs = [ + // No address '0x' prefix. + TestInput { + token: TokenEnum::address("f784682c82526e245f50975190ef0fff4e4fc077".into()), + error: AbiErrorKind::Error_invalid_address_value, + }, + // There is no uint0. + TestInput { + token: u_number_n::<0>(0), + error: AbiErrorKind::Error_invalid_uint_value, + }, + // There is no int7. + TestInput { + token: s_number_n::<7>(1), + error: AbiErrorKind::Error_invalid_uint_value, + }, + // There is no uint512. + TestInput { + token: u_number_n::<512>(123), + error: AbiErrorKind::Error_invalid_uint_value, + }, + // ArrayType::element_type and token mismatch. + TestInput { + token: TokenEnum::array(array( + ParamTypeEnum::number_uint(number_type_n::<8>()), + vec![u_number_n::<256>(1000)], + )), + error: AbiErrorKind::Error_invalid_param_type, + }, + ]; + + for TestInput { token, error } in test_inputs { + let encoding_input = Proto::FunctionEncodingInput { + function_name: "NonExisting".into(), + tokens: vec![named_token("", token.clone())], + }; + + let output = AbiEncoder::::encode_contract_call(encoding_input); + assert_eq!( + output.error, error, + "Expected error on encoding {:?}", + token + ); + assert!(!output.error_message.is_empty()); + } +} + +#[test] +fn test_decode_contract_call_error() { + let encoded_input = "11223344000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000470de4df82000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000740000000000000000000000000000000000000000000000000000000000000078000000000000000000000000000000000000000000000000000000000000007c00000000000000000000000000000000000000000000000000000000000000820000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000099a58482bd75cbab83b27ec03ca68ff489b5788f00000000000000000000000000000000000000000000000000470de4df820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099a58482bd75cbab83b27ec03ca68ff489b5788f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000000003840651cb35000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000bebc44782c7db0a1a60cb6fe97d0b483032ff1c7000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000470de4df8200000000000000000000000000000000000000000000000000000000298ce42936ed0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d66600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045553444300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000762696e616e636500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863653136463639333735353230616230313337376365374238386635424138433438463844363636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000072000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000ac000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf300000000000000000000000000000000000000000000000000000000000000010000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb1400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf300000000000000000000000055d398326f99059ff775485246999027b319795500000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd300000000000000000000000000000000000000000000000000000189c04a7044000000000000000000000000000000000000000000000000000029a23529cf68000000000000000000000000000000000000000000005af4f3f913bd553d03b900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf30000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000100000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000104414bf38900000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c00000000000000000000000000000000000000000000000000000000000001f40000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd300000000000000000000000000000000000000000000000000000189c04a7045000000000000000000000000000000000000000000005b527785e694f805bdd300000000000000000000000000000000000000000000005f935a1fa5c4a6ec61000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000055d398326f99059ff775485246999027b319795500000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".decode_hex().unwrap(); + let input = Proto::ContractCallDecodingInput { + encoded: Cow::Owned(encoded_input), + smart_contract_abi_json: Cow::Borrowed(SWAP_V2_ABI), + }; + + let output = AbiEncoder::::decode_contract_call(input); + assert_eq!(output.error, AbiErrorKind::Error_abi_mismatch); + assert!(!output.error_message.is_empty()); +} diff --git a/rust/tw_evm/tests/barz.rs b/rust/tw_evm/tests/barz.rs new file mode 100644 index 00000000000..270bd958ae2 --- /dev/null +++ b/rust/tw_evm/tests/barz.rs @@ -0,0 +1,194 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::borrow::Cow; +use tw_coin_entry::error::SigningErrorType; +use tw_encoding::hex; +use tw_evm::abi::prebuild::erc20::Erc20; +use tw_evm::address::Address; +use tw_evm::evm_context::StandardEvmContext; +use tw_evm::modules::signer::Signer; +use tw_number::U256; +use tw_proto::Ethereum::Proto; + +// https://testnet.bscscan.com/tx/0x43fc13dfdf06bbb09da8ce070953753764f1e43782d0c8b621946d8b45749419 +#[test] +fn test_barz_transfer_account_deployed() { + let private_key = + hex::decode("0x3c90badc15c4d35733769093d3733501e92e7f16e101df284cee9a310d36c483").unwrap(); + + let transfer = Proto::mod_Transaction::Transfer { + amount: U256::encode_be_compact(0x23_86f2_6fc1_0000), + data: Cow::default(), + }; + let user_op = Proto::UserOperation { + entry_point: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789".into(), + init_code: Cow::default(), + sender: "0xb16Db98B365B1f89191996942612B14F1Da4Bd5f".into(), + pre_verification_gas: U256::encode_be_compact(0xb708), + verification_gas_limit: U256::encode_be_compact(0x186a0), + paymaster_and_data: Cow::default(), + }; + + let input = Proto::SigningInput { + chain_id: U256::encode_be_compact(97), + nonce: U256::encode_be_compact(2), + tx_mode: Proto::TransactionMode::UserOp, + gas_price: Cow::default(), + gas_limit: U256::encode_be_compact(0x186A0), + max_fee_per_gas: U256::encode_be_compact(0x1_a339_c9e9), + max_inclusion_fee_per_gas: U256::encode_be_compact(0x1_a339_c9e9), + to_address: "0x61061fCAE11fD5461535e134EfF67A98CFFF44E9".into(), + private_key: private_key.into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::transfer(transfer), + }), + user_operation: Some(user_op), + }; + + let output = Signer::::sign_proto(input); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected = r#"{"callData":"0xb61d27f600000000000000000000000061061fcae11fd5461535e134eff67a98cfff44e9000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000","callGasLimit":"100000","initCode":"0x","maxFeePerGas":"7033440745","maxPriorityFeePerGas":"7033440745","nonce":"2","paymasterAndData":"0x","preVerificationGas":"46856","sender":"0xb16Db98B365B1f89191996942612B14F1Da4Bd5f","signature":"0x80e84992ebf8d5f71180231163ed150a7557ed0aa4b4bcee23d463a09847e4642d0fbf112df2e5fa067adf4b2fa17fc4a8ac172134ba5b78e3ec9c044e7f28d71c","verificationGasLimit":"100000"}"#; + let actual = String::from_utf8(output.encoded.to_vec()).unwrap(); + assert_eq!(actual, expected); + + assert_eq!( + hex::encode(output.pre_hash, false), + "2d37191a8688f69090451ed90a0a9ba69d652c2062ee9d023b3ebe964a3ed2ae" + ); +} + +// // https://testnet.bscscan.com/tx/0xea1f5cddc0653e116327cbcb3bc770360a642891176eff2ec69c227e46791c31 +#[test] +fn test_barz_transfer_account_not_deployed() { + let private_key = + hex::decode("0x3c90badc15c4d35733769093d3733501e92e7f16e101df284cee9a310d36c483").unwrap(); + + // TODO generate `init_code` with Barz when it's moved to Rust. + // See: https://github.com/trustwallet/wallet-core/blob/f266567e913a40bfee80b8ecdd4b74785cea2b32/tests/chains/Ethereum/BarzTests.cpp#L160 + let init_code = hex::decode("3fc708630d85a3b5ec217e53100ec2b735d4f800296601cd0000000000000000000000005034534efe9902779ed6ea6983f435c00f3bc51000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004104b173a6a812025c40c38bac46343646bd0a8137c807aae6e04aac238cc24d2ad2116ca14d23d357588ff2aabd7db29d5976f4ecc8037775db86f67e873a306b1f00000000000000000000000000000000000000000000000000000000000000").unwrap(); + + let transfer = Proto::mod_Transaction::Transfer { + amount: U256::encode_be_compact(0x23_86f2_6fc1_0000), + data: Cow::default(), + }; + let user_op = Proto::UserOperation { + entry_point: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789".into(), + init_code: init_code.into(), + sender: "0x1392Ae041BfBdBAA0cFF9234a0C8F64df97B7218".into(), + pre_verification_gas: U256::encode_be_compact(0xb708), + verification_gas_limit: U256::encode_be_compact(0x2D_C6C0), + paymaster_and_data: Cow::default(), + }; + + let input = Proto::SigningInput { + chain_id: U256::encode_be_compact(97), + nonce: U256::encode_be_compact(0), + tx_mode: Proto::TransactionMode::UserOp, + gas_price: Cow::default(), + gas_limit: U256::encode_be_compact(0x26_25A0), + max_fee_per_gas: U256::encode_be_compact(0x1_a339_c9e9), + max_inclusion_fee_per_gas: U256::encode_be_compact(0x1_a339_c9e9), + to_address: "0x61061fCAE11fD5461535e134EfF67A98CFFF44E9".into(), + private_key: private_key.into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::transfer(transfer), + }), + user_operation: Some(user_op), + }; + + let output = Signer::::sign_proto(input); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected = r#"{"callData":"0xb61d27f600000000000000000000000061061fcae11fd5461535e134eff67a98cfff44e9000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000","callGasLimit":"2500000","initCode":"0x3fc708630d85a3b5ec217e53100ec2b735d4f800296601cd0000000000000000000000005034534efe9902779ed6ea6983f435c00f3bc51000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004104b173a6a812025c40c38bac46343646bd0a8137c807aae6e04aac238cc24d2ad2116ca14d23d357588ff2aabd7db29d5976f4ecc8037775db86f67e873a306b1f00000000000000000000000000000000000000000000000000000000000000","maxFeePerGas":"7033440745","maxPriorityFeePerGas":"7033440745","nonce":"0","paymasterAndData":"0x","preVerificationGas":"46856","sender":"0x1392Ae041BfBdBAA0cFF9234a0C8F64df97B7218","signature":"0xbf1b68323974e71ad9bd6dfdac07dc062599d150615419bb7876740d2bcf3c8909aa7e627bb0e08a2eab930e2e7313247c9b683c884236dd6ea0b6834fb2cb0a1b","verificationGasLimit":"3000000"}"#; + let actual = String::from_utf8(output.encoded.to_vec()).unwrap(); + assert_eq!(actual, expected); + + assert_eq!( + hex::encode(output.pre_hash, false), + "548c13a0bb87981d04a3a24a78ad5e4ba8d0afbf3cfe9311250e07b54cd38937" + ); +} + +// // https://testnet.bscscan.com/tx/0xea1f5cddc0653e116327cbcb3bc770360a642891176eff2ec69c227e46791c31 +#[test] +fn test_barz_batched_account_deployed() { + let private_key = + hex::decode("0x3c90badc15c4d35733769093d3733501e92e7f16e101df284cee9a310d36c483").unwrap(); + + let contract_address = "0x03bBb5660B8687C2aa453A0e42dCb6e0732b1266"; + + let mut calls = Vec::with_capacity(2); + + // ERC20 approve + { + let spender = Address::from("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"); + let amount = U256::from(0x8AC7_2304_89E8_0000_u64); + let payload = Erc20::approve(spender, amount).unwrap(); + + calls.push(Proto::mod_Transaction::mod_Batch::BatchedCall { + address: contract_address.into(), + amount: Cow::default(), + payload: payload.into(), + }); + }; + + // ERC20 transfer + { + let recipient = Address::from("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"); + let amount = U256::from(0x8AC7_2304_89E8_0000_u64); + let payload = Erc20::transfer(recipient, amount).unwrap(); + + calls.push(Proto::mod_Transaction::mod_Batch::BatchedCall { + address: contract_address.into(), + amount: Cow::default(), + payload: payload.into(), + }); + } + + let user_op = Proto::UserOperation { + entry_point: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789".into(), + init_code: Cow::default(), + sender: "0x1e6c542ebc7c960c6a155a9094db838cef842cf5".into(), + pre_verification_gas: U256::encode_be_compact(0xDAFC), + verification_gas_limit: U256::encode_be_compact(0x07_F7C4), + paymaster_and_data: Cow::default(), + }; + + let input = Proto::SigningInput { + chain_id: U256::encode_be_compact(97), + nonce: U256::encode_be_compact(3), + tx_mode: Proto::TransactionMode::UserOp, + gas_price: Cow::default(), + gas_limit: U256::encode_be_compact(0x01_5A61), + max_fee_per_gas: U256::encode_be_compact(0x02_540B_E400), + max_inclusion_fee_per_gas: U256::encode_be_compact(0x02_540B_E400), + to_address: contract_address.into(), + private_key: private_key.into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::batch( + Proto::mod_Transaction::Batch { calls }, + ), + }), + user_operation: Some(user_op), + }; + + let output = Signer::::sign_proto(input); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected = r#"{"callData":"0x47e1da2a000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000003bbb5660b8687c2aa453a0e42dcb6e0732b126600000000000000000000000003bbb5660b8687c2aa453a0e42dcb6e0732b12660000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27890000000000000000000000000000000000000000000000008ac7230489e80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27890000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000000000000000","callGasLimit":"88673","initCode":"0x","maxFeePerGas":"10000000000","maxPriorityFeePerGas":"10000000000","nonce":"3","paymasterAndData":"0x","preVerificationGas":"56060","sender":"0x1E6c542ebC7c960c6A155A9094DB838cEf842cf5","signature":"0x0747b665fe9f3a52407f95a35ac3e76de37c9b89483ae440431244e89a77985f47df712c7364c1a299a5ef62d0b79a2cf4ed63d01772275dd61f72bd1ad5afce1c","verificationGasLimit":"522180"}"#; + let actual = String::from_utf8(output.encoded.to_vec()).unwrap(); + assert_eq!(actual, expected); + + assert_eq!( + hex::encode(output.pre_hash, false), + "84d0464f5a2b191e06295443970ecdcd2d18f565d0d52b5a79443192153770ab" + ); +} diff --git a/rust/tw_evm/tests/data/abi_with_negative_int.json b/rust/tw_evm/tests/data/abi_with_negative_int.json new file mode 100644 index 00000000000..c5b59d3e84d --- /dev/null +++ b/rust/tw_evm/tests/data/abi_with_negative_int.json @@ -0,0 +1,20 @@ +{ + "d4e12f2e": { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_value", + "type": "int256" + } + ], + "name": "approve", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +} \ No newline at end of file diff --git a/rust/tw_evm/tests/data/decoded_abi_with_negative_int.json b/rust/tw_evm/tests/data/decoded_abi_with_negative_int.json new file mode 100644 index 00000000000..0165692cb82 --- /dev/null +++ b/rust/tw_evm/tests/data/decoded_abi_with_negative_int.json @@ -0,0 +1,15 @@ +{ + "function": "approve(address,int256)", + "inputs": [ + { + "name": "_spender", + "value": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", + "type": "address" + }, + { + "name": "_value", + "value": "-3", + "type": "int256" + } + ] +} \ No newline at end of file diff --git a/rust/tw_evm/tests/data/eip712_case_1.json b/rust/tw_evm/tests/data/eip712_case_1.json new file mode 100644 index 00000000000..22e6b14317f --- /dev/null +++ b/rust/tw_evm/tests/data/eip712_case_1.json @@ -0,0 +1,64 @@ +{ + "primaryType": "Mail", + "domain": { + "name": "Ether Mail", + "version": "1", + "chainId": "0x1", + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" + }, + "contents": "Hello, Bob!" + }, + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Person": [ + { + "name": "name", + "type": "string" + }, + { + "name": "wallet", + "type": "address" + } + ], + "Mail": [ + { + "name": "from", + "type": "Person" + }, + { + "name": "to", + "type": "Person" + }, + { + "name": "contents", + "type": "string" + } + ] + } +} \ No newline at end of file diff --git a/rust/tw_evm/tests/data/eip712_case_2.json b/rust/tw_evm/tests/data/eip712_case_2.json new file mode 100644 index 00000000000..3155577ea67 --- /dev/null +++ b/rust/tw_evm/tests/data/eip712_case_2.json @@ -0,0 +1,83 @@ +{ + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Person": [ + { + "name": "name", + "type": "string" + }, + { + "name": "wallets", + "type": "address[]" + } + ], + "Mail": [ + { + "name": "from", + "type": "Person" + }, + { + "name": "to", + "type": "Person[]" + }, + { + "name": "contents", + "type": "string" + } + ], + "Group": [ + { + "name": "name", + "type": "string" + }, + { + "name": "members", + "type": "Person[]" + } + ] + }, + "domain": { + "name": "Ether Mail", + "version": "1", + "chainId": "0x1", + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "primaryType": "Mail", + "message": { + "from": { + "name": "Cow", + "wallets": [ + "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF" + ] + }, + "to": [ + { + "name": "Bob", + "wallets": [ + "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + "0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57", + "0xB0B0b0b0b0b0B000000000000000000000000000" + ] + } + ], + "contents": "Hello, Bob!" + } +} \ No newline at end of file diff --git a/rust/tw_evm/tests/data/eip712_case_3.json b/rust/tw_evm/tests/data/eip712_case_3.json new file mode 100644 index 00000000000..1c1da8c9d85 --- /dev/null +++ b/rust/tw_evm/tests/data/eip712_case_3.json @@ -0,0 +1,43 @@ +{ + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Person": [ + { + "name": "name", + "type": "string" + }, + { + "name": "wallet", + "type": "address" + } + ] + }, + "primaryType": "Person", + "domain": { + "name": "Ether Person", + "version": "1", + "chainId": 0, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "name": "Cow", + "wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + } +} \ No newline at end of file diff --git a/rust/tw_evm/tests/data/eip712_greenfield.json b/rust/tw_evm/tests/data/eip712_greenfield.json new file mode 100644 index 00000000000..31382ae06ab --- /dev/null +++ b/rust/tw_evm/tests/data/eip712_greenfield.json @@ -0,0 +1,149 @@ +{ + "types": { + "Coin": [ + { + "name": "amount", + "type": "uint256" + }, + { + "name": "denom", + "type": "string" + } + ], + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "name", + "type": "string" + }, + { + "name": "salt", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "string" + }, + { + "name": "version", + "type": "string" + } + ], + "Fee": [ + { + "name": "amount", + "type": "Coin[]" + }, + { + "name": "gas_limit", + "type": "uint256" + }, + { + "name": "granter", + "type": "string" + }, + { + "name": "payer", + "type": "string" + } + ], + "Msg1": [ + { + "name": "amount", + "type": "TypeMsg1Amount[]" + }, + { + "name": "from_address", + "type": "string" + }, + { + "name": "to_address", + "type": "string" + }, + { + "name": "type", + "type": "string" + } + ], + "Tx": [ + { + "name": "account_number", + "type": "uint256" + }, + { + "name": "chain_id", + "type": "uint256" + }, + { + "name": "fee", + "type": "Fee" + }, + { + "name": "memo", + "type": "string" + }, + { + "name": "msg1", + "type": "Msg1" + }, + { + "name": "sequence", + "type": "uint256" + }, + { + "name": "timeout_height", + "type": "uint256" + } + ], + "TypeMsg1Amount": [ + { + "name": "amount", + "type": "string" + }, + { + "name": "denom", + "type": "string" + } + ] + }, + "primaryType": "Tx", + "domain": { + "name": "Greenfield Tx", + "version": "1.0.0", + "chainId": "0x15e0", + "verifyingContract": "greenfield", + "salt": "0" + }, + "message": { + "sequence": "2", + "account_number": "15560", + "chain_id": "5600", + "memo": "", + "fee": { + "amount": [ + { + "amount": "2000000000000000", + "denom": "BNB" + } + ], + "gas_limit": "200000", + "payer": "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1", + "granter": "" + }, + "timeout_height": "0", + "msg1": { + "from_address": "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1", + "to_address": "0x280b27f3676db1C4475EE10F75D510Eb527fd155", + "amount": [ + { + "amount": "1000000000000000", + "denom": "BNB" + } + ], + "type": "/cosmos.bank.v1beta1.MsgSend" + } + } +} \ No newline at end of file diff --git a/rust/tw_evm/tests/data/eip712_unequal_array_lengths.json b/rust/tw_evm/tests/data/eip712_unequal_array_lengths.json new file mode 100644 index 00000000000..b1279af8a31 --- /dev/null +++ b/rust/tw_evm/tests/data/eip712_unequal_array_lengths.json @@ -0,0 +1,66 @@ +{ + "primaryType": "Mail", + "domain": { + "name": "Ether Mail", + "version": "1", + "chainId": "0x1", + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + }, + "to": [ + { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" + } + ], + "contents": "Hello, Bob!" + }, + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Person": [ + { + "name": "name", + "type": "string" + }, + { + "name": "wallet", + "type": "address" + } + ], + "Mail": [ + { + "name": "from", + "type": "Person" + }, + { + "name": "to", + "type": "Person[2]" + }, + { + "name": "contents", + "type": "string" + } + ] + } +} \ No newline at end of file diff --git a/rust/tw_evm/tests/data/eip712_with_chain_id_string.json b/rust/tw_evm/tests/data/eip712_with_chain_id_string.json new file mode 100644 index 00000000000..7d078cd3904 --- /dev/null +++ b/rust/tw_evm/tests/data/eip712_with_chain_id_string.json @@ -0,0 +1,83 @@ +{ + "domain": { + "chainId": "5600", + "name": "Ether Mail", + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", + "version": "1" + }, + "message": { + "contents": "Hello, Bob!", + "from": { + "name": "Cow", + "wallets": [ + "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF" + ] + }, + "to": [ + { + "name": "Bob", + "wallets": [ + "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + "0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57", + "0xB0B0b0b0b0b0B000000000000000000000000000" + ] + } + ] + }, + "primaryType": "Mail", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Group": [ + { + "name": "name", + "type": "string" + }, + { + "name": "members", + "type": "Person[]" + } + ], + "Mail": [ + { + "name": "from", + "type": "Person" + }, + { + "name": "to", + "type": "Person[]" + }, + { + "name": "contents", + "type": "string" + } + ], + "Person": [ + { + "name": "name", + "type": "string" + }, + { + "name": "wallets", + "type": "address[]" + } + ] + } +} \ No newline at end of file diff --git a/rust/tw_evm/tests/data/eip712_with_custom_array.json b/rust/tw_evm/tests/data/eip712_with_custom_array.json new file mode 100644 index 00000000000..116612888e1 --- /dev/null +++ b/rust/tw_evm/tests/data/eip712_with_custom_array.json @@ -0,0 +1,86 @@ +{ + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Person": [ + { + "name": "name", + "type": "string" + }, + { + "name": "wallets", + "type": "address[]" + } + ], + "Mail": [ + { + "name": "from", + "type": "Person" + }, + { + "name": "to", + "type": "Group" + }, + { + "name": "contents", + "type": "string" + } + ], + "Group": [ + { + "name": "name", + "type": "string" + }, + { + "name": "members", + "type": "Person[]" + } + ] + }, + "domain": { + "name": "Ether Mail", + "version": "1", + "chainId": "0x1", + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "primaryType": "Mail", + "message": { + "from": { + "name": "Cow", + "wallets": [ + "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF" + ] + }, + "to": { + "name": "Farmers", + "members": [ + { + "name": "Bob", + "wallets": [ + "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + "0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57", + "0xB0B0b0b0b0b0B000000000000000000000000000" + ] + } + ] + }, + "contents": "Hello, Bob!" + } +} \ No newline at end of file diff --git a/rust/tw_evm/tests/data/swap_v2.json b/rust/tw_evm/tests/data/swap_v2.json new file mode 100644 index 00000000000..5794290fd00 --- /dev/null +++ b/rust/tw_evm/tests/data/swap_v2.json @@ -0,0 +1,69 @@ +{ + "846a1bc6": { + "inputs": [ + { + "name": "token", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + }, + { + "components": [ + { + "name": "callType", + "type": "uint8" + }, + { + "name": "target", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "name": "callData", + "type": "bytes" + }, + { + "name": "payload", + "type": "bytes" + } + ], + "name": "calls", + "type": "tuple[]" + }, + { + "name": "bridgedTokenSymbol", + "type": "string" + }, + { + "name": "destinationChain", + "type": "string" + }, + { + "name": "destinationAddress", + "type": "string" + }, + { + "name": "payload", + "type": "bytes" + }, + { + "name": "gasRefundRecipient", + "type": "address" + }, + { + "name": "enableExpress", + "type": "bool" + } + ], + "name": "callBridgeCall", + "outputs": [ + ], + "stateMutability": "nonpayable", + "type": "function" + } +} \ No newline at end of file diff --git a/rust/tw_evm/tests/data/swap_v2_decoded.json b/rust/tw_evm/tests/data/swap_v2_decoded.json new file mode 100644 index 00000000000..85bb0570a62 --- /dev/null +++ b/rust/tw_evm/tests/data/swap_v2_decoded.json @@ -0,0 +1,105 @@ +{ + "function": "callBridgeCall(address,uint256,(uint8,address,uint256,bytes,bytes)[],string,string,string,bytes,address,bool)", + "inputs": [ + { + "name": "token", + "type": "address", + "value": "0xdAC17F958D2ee523a2206206994597C13D831ec7" + }, + { + "name": "amount", + "type": "uint256", + "value": "20000000000000000" + }, + { + "components": [ + [ + { + "name": "callType", + "type": "uint8", + "value": "0" + }, + { + "name": "target", + "type": "address", + "value": "0xdAC17F958D2ee523a2206206994597C13D831ec7" + }, + { + "name": "value", + "type": "uint256", + "value": "0" + }, + { + "name": "callData", + "type": "bytes", + "value": "0x095ea7b300000000000000000000000099a58482bd75cbab83b27ec03ca68ff489b5788f00000000000000000000000000000000000000000000000000470de4df820000" + }, + { + "name": "payload", + "type": "bytes", + "value": "0x" + } + ], + [ + { + "name": "callType", + "type": "uint8", + "value": "0" + }, + { + "name": "target", + "type": "address", + "value": "0x99a58482BD75cbab83b27EC03CA68fF489b5788f" + }, + { + "name": "value", + "type": "uint256", + "value": "0" + }, + { + "name": "callData", + "type": "bytes", + "value": "0x0651cb35000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000bebc44782c7db0a1a60cb6fe97d0b483032ff1c7000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000470de4df8200000000000000000000000000000000000000000000000000000000298ce42936ed0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666" + }, + { + "name": "payload", + "type": "bytes", + "value": "0x" + } + ] + ], + "name": "calls", + "type": "tuple[]" + }, + { + "name": "bridgedTokenSymbol", + "type": "string", + "value": "USDC" + }, + { + "name": "destinationChain", + "type": "string", + "value": "binance" + }, + { + "name": "destinationAddress", + "type": "string", + "value": "0xce16F69375520ab01377ce7B88f5BA8C48F8D666" + }, + { + "name": "payload", + "type": "bytes", + "value": "0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000072000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000ac000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf300000000000000000000000000000000000000000000000000000000000000010000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb1400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf300000000000000000000000055d398326f99059ff775485246999027b319795500000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd300000000000000000000000000000000000000000000000000000189c04a7044000000000000000000000000000000000000000000000000000029a23529cf68000000000000000000000000000000000000000000005af4f3f913bd553d03b900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf30000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000100000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000104414bf38900000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c00000000000000000000000000000000000000000000000000000000000001f40000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd300000000000000000000000000000000000000000000000000000189c04a7045000000000000000000000000000000000000000000005b527785e694f805bdd300000000000000000000000000000000000000000000005f935a1fa5c4a6ec61000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000055d398326f99059ff775485246999027b319795500000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + "name": "gasRefundRecipient", + "type": "address", + "value": "0xa140F413C63FBDA84E9008607E678258ffFbC76b" + }, + { + "name": "enableExpress", + "type": "bool", + "value": true + } + ] +} \ No newline at end of file diff --git a/rust/tw_evm/tests/message_signer.rs b/rust/tw_evm/tests/message_signer.rs new file mode 100644 index 00000000000..a1491c61dcd --- /dev/null +++ b/rust/tw_evm/tests/message_signer.rs @@ -0,0 +1,258 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::modules::message_signer::MessageSigner; +use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_evm::modules::message_signer::EthMessageSigner; +use tw_keypair::ecdsa::secp256k1; +use tw_proto::Ethereum::Proto; + +const EIP712_CASE_1: &str = include_str!("data/eip712_case_1.json"); +const EIP712_CASE_2: &str = include_str!("data/eip712_case_2.json"); +const EIP712_CASE_3: &str = include_str!("data/eip712_case_3.json"); +const EIP712_WITH_CUSTOM_ARRAY: &str = include_str!("data/eip712_with_custom_array.json"); +const EIP712_UNEQUAL_ARRAY_LEN: &str = include_str!("data/eip712_unequal_array_lengths.json"); +const EIP712_WITH_CHAIN_ID_STR: &str = include_str!("data/eip712_with_chain_id_string.json"); +const EIP712_GREENFIELD: &str = include_str!("data/eip712_greenfield.json"); + +struct SignVerifyTestInput { + private_key: &'static str, + msg: &'static str, + msg_type: Proto::MessageType, + chain_id: Option, + signature: &'static str, +} + +fn test_message_signer_sign_verify(test_input: SignVerifyTestInput) { + let private_key = test_input.private_key.decode_hex().unwrap(); + let chain_id = test_input + .chain_id + .map(|chain_id| Proto::MaybeChainId { chain_id }); + let signing_input = Proto::MessageSigningInput { + private_key: private_key.into(), + message: test_input.msg.into(), + message_type: test_input.msg_type, + chain_id, + ..Proto::MessageSigningInput::default() + }; + + let output = EthMessageSigner.sign_message(&EmptyCoinContext, signing_input); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + assert_eq!(output.signature, test_input.signature); + + let public_key = secp256k1::PrivateKey::try_from(test_input.private_key) + .unwrap() + .public(); + + let verifying_input = Proto::MessageVerifyingInput { + message: test_input.msg.into(), + public_key: public_key.compressed().to_vec().into(), + signature: test_input.signature.into(), + }; + assert!( + EthMessageSigner.verify_message(&EmptyCoinContext, verifying_input), + "!verify_message: {}", + test_input.signature + ); +} + +struct SignErrorTestInput { + private_key: &'static str, + msg: &'static str, + msg_type: Proto::MessageType, + chain_id: u64, + error: SigningErrorType, +} + +fn test_message_signer_sign_err(test_input: SignErrorTestInput) { + let private_key = test_input.private_key.decode_hex().unwrap(); + let signing_input = Proto::MessageSigningInput { + private_key: private_key.into(), + message: test_input.msg.into(), + message_type: test_input.msg_type, + chain_id: Some(Proto::MaybeChainId { + chain_id: test_input.chain_id, + }), + ..Proto::MessageSigningInput::default() + }; + + let output = EthMessageSigner.sign_message(&EmptyCoinContext, signing_input); + assert_eq!(output.error, test_input.error); +} + +struct PreimageTestInput { + msg: &'static str, + msg_type: Proto::MessageType, + chain_id: u64, + data_hash: &'static str, +} + +fn test_message_signer_preimage_hashes(test_input: PreimageTestInput) { + let signing_input = Proto::MessageSigningInput { + message: test_input.msg.into(), + message_type: test_input.msg_type, + chain_id: Some(Proto::MaybeChainId { + chain_id: test_input.chain_id, + }), + ..Proto::MessageSigningInput::default() + }; + + let output = EthMessageSigner.message_preimage_hashes(&EmptyCoinContext, signing_input); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + assert_eq!(output.data_hash.to_hex(), test_input.data_hash); +} + +#[test] +fn test_message_signer_sign_verify_legacy() { + test_message_signer_sign_verify(SignVerifyTestInput { + private_key: "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d", + msg: "Foo", + msg_type: Proto::MessageType::MessageType_legacy, + chain_id: None, + signature: "21a779d499957e7fd39392d49a079679009e60e492d9654a148829be43d2490736ec72bc4a5644047d979c3cf4ebe2c1c514044cf436b063cb89fc6676be71101b", + }); +} + +#[test] +fn test_message_signer_sign_verify_eip155() { + test_message_signer_sign_verify(SignVerifyTestInput { + private_key: "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d", + msg: "Foo", + msg_type: Proto::MessageType::MessageType_eip155, + chain_id: Some(0), + signature: "21a779d499957e7fd39392d49a079679009e60e492d9654a148829be43d2490736ec72bc4a5644047d979c3cf4ebe2c1c514044cf436b063cb89fc6676be711023", + }); +} + +#[test] +fn test_message_signer_sign_verify_immutable_x() { + test_message_signer_sign_verify(SignVerifyTestInput { + private_key: "3b0a61f46fdae924007146eacb6db6642de7a5603ad843ec58e10331d89d4b84", + msg:"Only sign this request if you’ve initiated an action with Immutable X.\n\nFor internal use:\nbd717ba31dca6e0f3f136f7c4197babce5f09a9f25176044c0b3112b1b6017a3", + msg_type: Proto::MessageType::MessageType_immutable_x, + chain_id: None, + signature: "32cd5a58f3419fc5db672e3d57f76199b853eda0856d491b38f557b629b0a0814ace689412bf354a1af81126d2749207dffae8ae8845160f33948a6b787e17ee01", + }); +} + +#[test] +fn test_message_signer_hash_eip712_case_1() { + test_message_signer_preimage_hashes(PreimageTestInput { + msg: EIP712_CASE_1, + msg_type: Proto::MessageType::MessageType_typed, + chain_id: 1, + data_hash: "be609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2", + }); +} + +#[test] +fn test_message_signer_hash_eip712_case_2() { + test_message_signer_preimage_hashes(PreimageTestInput { + msg: EIP712_CASE_2, + msg_type: Proto::MessageType::MessageType_typed, + chain_id: 1, + data_hash: "a85c2e2b118698e88db68a8105b794a8cc7cec074e89ef991cb4f5f533819cc2", + }); +} + +#[test] +fn test_message_signer_hash_with_custom_array() { + test_message_signer_preimage_hashes(PreimageTestInput { + msg: EIP712_WITH_CUSTOM_ARRAY, + msg_type: Proto::MessageType::MessageType_typed_eip155, + chain_id: 1, + data_hash: "cd8b34cd09c541cfc0a2fcd147e47809b98b335649c2aa700db0b0c4501a02a0", + }); +} + +#[test] +fn test_message_signer_hash_unequal_array_len() { + let signing_input = Proto::MessageSigningInput { + message: EIP712_UNEQUAL_ARRAY_LEN.into(), + message_type: Proto::MessageType::MessageType_typed_eip155, + ..Proto::MessageSigningInput::default() + }; + + let output = EthMessageSigner.message_preimage_hashes(&EmptyCoinContext, signing_input); + assert_eq!(output.error, SigningErrorType::Error_invalid_params); +} + +#[test] +fn test_message_signer_sign_and_verify_eip712_case_3() { + test_message_signer_sign_verify(SignVerifyTestInput { + private_key: "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d", + msg: EIP712_CASE_3, + msg_type: Proto::MessageType::MessageType_typed, + chain_id: Some(0), + signature: "446434e4c34d6b7456e5f07a1b994b88bf85c057234c68d1e10c936b1c85706c4e19147c0ac3a983bc2d56ebfd7146f8b62bcea6114900fe8e7d7351f44bf3761c", + }); +} + +#[test] +fn test_message_signer_sign_and_verify_eip712_case_3_eip155() { + test_message_signer_sign_verify(SignVerifyTestInput { + private_key: "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d", + msg: EIP712_CASE_3, + msg_type: Proto::MessageType::MessageType_typed_eip155, + chain_id: None, + signature: "446434e4c34d6b7456e5f07a1b994b88bf85c057234c68d1e10c936b1c85706c4e19147c0ac3a983bc2d56ebfd7146f8b62bcea6114900fe8e7d7351f44bf37624", + }); +} + +#[test] +fn test_message_signer_sign_and_verify_eip712_invalid_chain_id() { + test_message_signer_sign_verify(SignVerifyTestInput { + private_key: "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d", + msg: EIP712_CASE_3, + msg_type: Proto::MessageType::MessageType_typed_eip155, + chain_id: None, + signature: "446434e4c34d6b7456e5f07a1b994b88bf85c057234c68d1e10c936b1c85706c4e19147c0ac3a983bc2d56ebfd7146f8b62bcea6114900fe8e7d7351f44bf37624", + }); +} + +#[test] +fn test_message_signer_sign_eip712_invalid_chain_id() { + test_message_signer_sign_err(SignErrorTestInput { + private_key: "03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d", + msg: EIP712_CASE_3, + msg_type: Proto::MessageType::MessageType_typed_eip155, + // Actual value is 0. + chain_id: 1, + error: SigningErrorType::Error_invalid_params, + }); +} + +// Test `TWEthereumMessageSignerSignTypedMessageEip155` where `domain.chainId` is a base10 decimal string. +// Generated by using https://metamask.github.io/test-dapp/ +#[test] +fn test_message_signer_sign_verify_eip712_with_chain_id_string() { + test_message_signer_sign_verify(SignVerifyTestInput { + private_key: "9066aa168c379a403becb235c15e7129c133c244e56a757ab07bc369288bcab0", + msg: EIP712_WITH_CHAIN_ID_STR, + msg_type: Proto::MessageType::MessageType_typed_eip155, + // 5600 + chain_id: Some(0x15e0), + signature: "248b45acf2920a9cef00d3b469a875482b5f0e8ce16f6290212d395aaec7f3be0645d6a5cb6fcdfdca9ecefbadd4e77dae656124094ecc984c5fcb9cb4384b05e3", + }); +} + +// The test checks if extra types are ordered correctly. +// The typed message was used to sign a Greenfield transaction: +// https://greenfieldscan.com/tx/9F895CF2DD64FB1F428CEFCF2A6585A813C3540FC9FE1EF42DB1DA2CB1DF55AB +#[test] +fn test_message_signer_sign_verify_eip712_greenfield() { + test_message_signer_sign_verify(SignVerifyTestInput { + private_key: "9066aa168c379a403becb235c15e7129c133c244e56a757ab07bc369288bcab0", + msg: EIP712_GREENFIELD, + msg_type: Proto::MessageType::MessageType_typed, + chain_id: None, + signature: "cb3a4684a991014a387a04a85b59227ebb79567c2025addcb296b4ca856e9f810d3b526f2a0d0fad6ad1b126b3b9516f8b3be020a7cca9c03ce3cf47f4199b6d1b", + }); +} diff --git a/rust/tw_evm/tests/rlp.rs b/rust/tw_evm/tests/rlp.rs new file mode 100644 index 00000000000..df7bb0c9915 --- /dev/null +++ b/rust/tw_evm/tests/rlp.rs @@ -0,0 +1,247 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::borrow::Cow; +use std::str::FromStr; +use tw_coin_entry::error::SigningErrorType; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_evm::evm_context::StandardEvmContext; +use tw_evm::modules::rlp_encoder::{RlpEncoder, RECURSION_LIMIT}; +use tw_number::U256; +use tw_proto::EthereumRlp::Proto as RlpProto; +use RlpProto::mod_RlpItem::OneOfitem as Item; + +fn make_item(item: Item) -> RlpProto::RlpItem { + RlpProto::RlpItem { item } +} + +#[track_caller] +fn test_encode(item: Item, expected: &str) { + let input = RlpProto::EncodingInput { + item: Some(make_item(item)), + }; + let output = RlpEncoder::::encode_with_proto(input); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + assert_eq!(output.encoded.to_hex(), expected); +} + +#[test] +fn test_encode_string() { + test_encode(Item::string_item(Cow::from("")), "80"); + test_encode(Item::string_item(Cow::from("d")), "64"); + test_encode(Item::string_item(Cow::from("dog")), "83646f67"); +} + +#[test] +fn test_encode_number_u64() { + test_encode(Item::number_u64(0), "80"); + test_encode(Item::number_u64(127), "7f"); + test_encode(Item::number_u64(128), "8180"); + test_encode(Item::number_u64(255), "81ff"); + test_encode(Item::number_u64(256), "820100"); + test_encode(Item::number_u64(1024), "820400"); + test_encode(Item::number_u64(0xffff), "82ffff"); + test_encode(Item::number_u64(0x010000), "83010000"); + test_encode(Item::number_u64(0xffffff), "83ffffff"); + test_encode(Item::number_u64(0xffffffff), "84ffffffff"); + test_encode(Item::number_u64(0xffffffffffffff), "87ffffffffffffff"); +} + +#[test] +fn test_ethereum_rlp_number_u256() { + macro_rules! test_encode_u256 { + ($num_str:literal => $expected:literal) => { + let num = U256::from_str($num_str).unwrap().to_big_endian_compact(); + test_encode(Item::number_u256(Cow::from(num)), $expected); + }; + } + + test_encode_u256!("0" => "80"); + test_encode_u256!("1" => "01"); + test_encode_u256!("127" => "7f"); + test_encode_u256!("128" => "8180"); + test_encode_u256!("256" => "820100"); + test_encode_u256!("1024" => "820400"); + + // 0xffffff + test_encode_u256!("16777215" => "83ffffff"); + // 0xffffffff + test_encode_u256!("4294967295" => "84ffffffff"); + // 0xffffffffffffff + test_encode_u256!("72057594037927935" => "87ffffffffffffff"); + // 0x102030405060708090a0b0c0d0e0f2 + test_encode_u256!("83729609699884896815286331701780722" => "8f102030405060708090a0b0c0d0e0f2"); + // 0x0100020003000400050006000700080009000a000b000c000d000e01 + test_encode_u256!("105315505618206987246253880190783558935785933862974822347068935681" => "9c0100020003000400050006000700080009000a000b000c000d000e01"); + // 0x0100000000000000000000000000000000000000000000000000000000000000 + test_encode_u256!("452312848583266388373324160190187140051835877600158453279131187530910662656" => "a00100000000000000000000000000000000000000000000000000000000000000"); +} + +#[test] +fn test_ethereum_rlp_raw_encoded() { + let raw_encoded = "946b175474e89094c44da98b954eedeac495271d0f" + .decode_hex() + .unwrap(); + test_encode( + Item::raw_encoded(Cow::from(raw_encoded)), + "946b175474e89094c44da98b954eedeac495271d0f", + ); + + let empty: &[u8] = &[]; + test_encode(Item::raw_encoded(Cow::from(empty)), ""); +} + +#[test] +fn test_ethereum_rlp_list() { + // Empty list. + let list = RlpProto::RlpList { + items: Vec::default(), + }; + test_encode(Item::list(list), "c0"); + + // [1, 2, 3] list. + let list = RlpProto::RlpList { + items: vec![ + make_item(Item::number_u64(1)), + make_item(Item::number_u64(2)), + make_item(Item::number_u64(3)), + ], + }; + test_encode(Item::list(list), "c3010203"); + + // ["a", "b"] list. + let list = RlpProto::RlpList { + items: vec![ + make_item(Item::string_item(Cow::from("a"))), + make_item(Item::string_item(Cow::from("b"))), + ], + }; + test_encode(Item::list(list), "c26162"); + + // ["cat", "dog"] list. + let list = RlpProto::RlpList { + items: vec![ + make_item(Item::string_item(Cow::from("cat"))), + make_item(Item::string_item(Cow::from("dog"))), + ], + }; + test_encode(Item::list(list), "c88363617483646f67"); + + // ["cat", "dog"] list. + let list = RlpProto::RlpList { + items: vec![ + make_item(Item::string_item(Cow::from("cat"))), + make_item(Item::string_item(Cow::from("dog"))), + ], + }; + test_encode(Item::list(list), "c88363617483646f67"); +} + +#[test] +fn test_ethereum_rlp_nested_list() { + let l11 = RlpProto::RlpList { + items: vec![ + make_item(Item::number_u64(1)), + make_item(Item::number_u64(2)), + make_item(Item::number_u64(3)), + ], + }; + let l12 = RlpProto::RlpList { + items: vec![ + make_item(Item::string_item(Cow::from("apple"))), + make_item(Item::string_item(Cow::from("banana"))), + make_item(Item::string_item(Cow::from("cherry"))), + ], + }; + let l21 = RlpProto::RlpList { + items: vec![ + make_item(Item::data(Cow::from("abcdef".decode_hex().unwrap()))), + make_item(Item::data(Cow::from( + "00010203040506070809".decode_hex().unwrap(), + ))), + ], + }; + let l22 = RlpProto::RlpList { + items: vec![ + make_item(Item::string_item(Cow::from("bitcoin"))), + make_item(Item::string_item(Cow::from("beeenbee"))), + make_item(Item::string_item(Cow::from("eth"))), + ], + }; + let l1 = RlpProto::RlpList { + items: vec![make_item(Item::list(l11)), make_item(Item::list(l12))], + }; + let l2 = RlpProto::RlpList { + items: vec![make_item(Item::list(l21)), make_item(Item::list(l22))], + }; + let list = RlpProto::RlpList { + items: vec![make_item(Item::list(l1)), make_item(Item::list(l2))], + }; + + // The value is checked by using https://codechain-io.github.io/rlp-debugger/ + test_encode( + Item::list(list), + "f841d9c3010203d4856170706c658662616e616e6186636865727279e6cf83abcdef8a00010203040506070809d587626974636f696e88626565656e62656583657468" + ); +} + +#[test] +fn test_ethereum_rlp_nested_list_recursion_limit() { + fn make_nested_list(nested_lists_to_create: usize) -> RlpProto::RlpList<'static> { + if nested_lists_to_create == 1 { + // This is the last call in the recursion. + return RlpProto::RlpList { items: Vec::new() }; + } + + let nested_list = make_nested_list(nested_lists_to_create - 1); + RlpProto::RlpList { + items: vec![make_item(Item::list(nested_list))], + } + } + + let nested_list = make_nested_list(RECURSION_LIMIT + 10); + let input = RlpProto::EncodingInput { + item: Some(make_item(Item::list(nested_list))), + }; + + let output = RlpEncoder::::encode_with_proto(input); + assert_eq!(output.error, SigningErrorType::Error_invalid_params); +} + +#[test] +fn test_ethereum_rlp_list_eip1559() { + let chain_id = U256::encode_be_compact(10); + let nonce = U256::encode_be_compact(6); + let max_inclusion_fee_per_gas = 2_000_000_000; + let max_fee_per_gas = U256::encode_be_compact(3_000_000_000); + let gas_limit = U256::encode_be_compact(21_100); + let to = "0x6b175474e89094c44da98b954eedeac495271d0f"; + let amount = 0; + let payload = "a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000001ee0c29f50cb1".decode_hex().unwrap(); + + // Empty nested list. + let access_list = RlpProto::RlpList { + items: Vec::default(), + }; + let list = RlpProto::RlpList { + items: vec![ + make_item(Item::number_u256(chain_id)), + make_item(Item::number_u256(nonce)), + make_item(Item::number_u64(max_inclusion_fee_per_gas)), + make_item(Item::number_u256(max_fee_per_gas)), + make_item(Item::number_u256(gas_limit)), + make_item(Item::address(Cow::from(to))), + make_item(Item::number_u64(amount)), + make_item(Item::data(Cow::from(payload))), + make_item(Item::list(access_list)), + ], + }; + test_encode( + Item::list(list), + "f86c0a06847735940084b2d05e0082526c946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000001ee0c29f50cb1c0" + ); +} diff --git a/rust/tw_evm/tests/signer.rs b/rust/tw_evm/tests/signer.rs new file mode 100644 index 00000000000..9b43ca7cea3 --- /dev/null +++ b/rust/tw_evm/tests/signer.rs @@ -0,0 +1,586 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::borrow::Cow; +use tw_coin_entry::error::SigningErrorType; +use tw_encoding::hex::{self, ToHex}; +use tw_evm::evm_context::StandardEvmContext; +use tw_evm::modules::signer::Signer; +use tw_number::U256; +use tw_proto::Ethereum::Proto; +use tw_proto::Ethereum::Proto::TransactionMode; + +#[test] +fn test_sign_transaction_non_typed_erc20_transfer() { + let private = + hex::decode("0x4646464646464646464646464646464646464646464646464646464646464646").unwrap(); + + let erc20_transfer = Proto::mod_Transaction::ERC20Transfer { + to: "0x5322b34c88ed0691971bf52a7047448f0f4efc84".into(), + amount: U256::encode_be_compact(2_000_000_000_000_000_000), + }; + + let input = Proto::SigningInput { + chain_id: U256::encode_be_compact(0x34), + tx_mode: TransactionMode::Legacy, + // 42000000000 + gas_price: U256::encode_be_compact(0x09_c765_2400), + // 78009 + gas_limit: U256::encode_be_compact(0x01_30B9), + // DAI + to_address: "0x6b175474e89094c44da98b954eedeac495271d0f".into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::erc20_transfer( + erc20_transfer, + ), + }), + private_key: private.into(), + ..Proto::SigningInput::default() + }; + + let output = Signer::::sign_proto(input); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected = "f8ab808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000818ba0c34040ff76f6d5e397b54b47f7fa2b3a7213f3c2a39a750260211fa15249ae8aa01ac5061e9bcf05aebef461864662652f25c45ee99240e3bb91b31f456208a6cd"; + assert_eq!(hex::encode(output.encoded, false), expected); + + assert_eq!( + hex::encode(output.pre_hash, false), + "b3525019dc367d3ecac48905f9a95ff3550c25a24823db765f92cae2dec7ebfd" + ); +} + +#[test] +fn test_sign_transaction_non_typed_native() { + let private = + hex::decode("0x4646464646464646464646464646464646464646464646464646464646464646").unwrap(); + + let transfer = Proto::mod_Transaction::Transfer { + amount: U256::encode_be_compact(1_000_000_000_000_000_000), + data: Cow::default(), + }; + + let input = Proto::SigningInput { + chain_id: U256::encode_be_compact(1), + nonce: U256::encode_be_compact(9), + gas_price: U256::encode_be_compact(20_000_000_000), + gas_limit: U256::encode_be_compact(21_000), + to_address: "0x3535353535353535353535353535353535353535".into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::transfer(transfer), + }), + private_key: private.into(), + ..Proto::SigningInput::default() + }; + + let output = Signer::::sign_proto(input); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected = "f86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83"; + assert_eq!(hex::encode(output.encoded, false), expected); + + assert_eq!( + hex::encode(output.r, false), + "28ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276" + ); + assert_eq!( + hex::encode(output.s, false), + "67cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83" + ); + assert_eq!(hex::encode(output.v, false), "25"); + + assert_eq!( + hex::encode(output.pre_hash, false), + "daf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53" + ); +} + +#[test] +fn test_sign_transaction_non_typed_erc20_approve() { + let private = + hex::decode("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151").unwrap(); + + let approve = Proto::mod_Transaction::ERC20Approve { + // DAI + spender: "0x5322b34c88ed0691971bf52a7047448f0f4efc84".into(), + amount: U256::encode_be_compact(2_000_000_000_000_000_000), + }; + + let input = Proto::SigningInput { + chain_id: U256::encode_be_compact(1), + nonce: U256::encode_be_compact(0), + // 0x09c7652400 + gas_price: U256::encode_be_compact(42_000_000_000), + // 0x130B9 + gas_limit: U256::encode_be_compact(78_009), + to_address: "0x6b175474e89094c44da98b954eedeac495271d0f".into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::erc20_approve( + approve, + ), + }), + private_key: private.into(), + ..Proto::SigningInput::default() + }; + + let output = Signer::::sign_proto(input); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected = "f8aa808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844095ea7b30000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec8000025a0d8136d66da1e0ba8c7208d5c4f143167f54b89a0fe2e23440653bcca28b34dc1a049222a79339f1a9e4641cb4ad805c49c225ae704299ffc10627bf41c035c464a"; + assert_eq!(hex::encode(output.encoded, false), expected); + + assert_eq!( + hex::encode(output.r, false), + "d8136d66da1e0ba8c7208d5c4f143167f54b89a0fe2e23440653bcca28b34dc1" + ); + assert_eq!( + hex::encode(output.s, false), + "49222a79339f1a9e4641cb4ad805c49c225ae704299ffc10627bf41c035c464a" + ); + assert_eq!(hex::encode(output.v, false), "25"); + + assert_eq!( + hex::encode(output.pre_hash, false), + "fe34a2b97f583db2d4baca3753f105d70dcf2d9800ddd0247900a026b9de6183" + ); +} + +#[test] +fn test_sign_transaction_non_typed_contract_generic() { + let private = + hex::decode("0x4646464646464646464646464646464646464646464646464646464646464646").unwrap(); + let call_data = "60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161094b38038061094b83398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b5050505050506106da806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f357806318160ddd1461016857806323b872dd14610171578063313ce5671461025c57806354fd4d501461026857806370a08231146102c657806395d89b41146102f4578063a9059cbb14610352578063cae9ca51146103f7578063dd62ed3e146105be575b6105f2610002565b6040805160038054602060026001831615610100026000190190921691909104601f81018290048202840182019094528383526105f493908301828280156106b75780601f1061068c576101008083540402835291602001916106b7565b61066260043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b6102e260025481565b610662600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101c4575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101d05750600082115b156106bf57600160a060020a0383811660008181526020818152604080832080548801905588851680845281842080548990039055600183528184203390961684529482529182902080548790039055815186815291519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060016106c3565b61067660045460ff1681565b6040805160068054602060026001831615610100026000190190921691909104601f81018290048202840182019094528383526105f493908301828280156106b75780601f1061068c576101008083540402835291602001916106b7565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b6105f46005805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156106b75780601f1061068c576101008083540402835291602001916106b7565b61066260043560243533600160a060020a03166000908152602081905260408120548290108015906103845750600082115b156106ca5733600160a060020a0390811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a3506001610162565b604080516020604435600481810135601f810184900484028501840190955284845261066294813594602480359593946064949293910191819084018382808284375094965050505050505033600160a060020a03908116600081815260016020908152604080832094881680845294825280832087905580518781529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a383600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105965780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f19250505015156106d257610002565b6102e2600435602435600160a060020a03828116600090815260016020908152604080832093851683529290522054610162565b005b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106545780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b604080519115158252519081900360200190f35b6040805160ff9092168252519081900360200190f35b820191906000526020600020905b81548152906001019060200180831161069a57829003601f168201915b505050505081565b5060005b9392505050565b506000610162565b5060016106c35600000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000754204275636b73000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003544f540000000000000000000000000000000000000000000000000000000000"; + + let contract = Proto::mod_Transaction::ContractGeneric { + amount: U256::encode_be_compact(0), + data: hex::decode(call_data).unwrap().into(), + }; + + // `tx_mode` is not set, Legacy is the default. + let input = Proto::SigningInput { + chain_id: U256::encode_be_compact(1), + nonce: U256::encode_be_compact(11), + gas_price: U256::encode_be_compact(20_000_000_000), + gas_limit: U256::encode_be_compact(1_000_000), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::contract_generic( + contract, + ), + }), + private_key: private.into(), + ..Proto::SigningInput::default() + }; + + let output = Signer::::sign_proto(input); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + assert_eq!( + hex::encode(output.pre_hash, false), + "5d2556f7d0e629dc6ce9dbc8a205853a7b89c136791840a39765e34cb5e3466a" + ); + + let expected = "f90a9e0b8504a817c800830f42408080b90a4b60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161094b38038061094b83398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b5050505050506106da806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f357806318160ddd1461016857806323b872dd14610171578063313ce5671461025c57806354fd4d501461026857806370a08231146102c657806395d89b41146102f4578063a9059cbb14610352578063cae9ca51146103f7578063dd62ed3e146105be575b6105f2610002565b6040805160038054602060026001831615610100026000190190921691909104601f81018290048202840182019094528383526105f493908301828280156106b75780601f1061068c576101008083540402835291602001916106b7565b61066260043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b6102e260025481565b610662600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101c4575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101d05750600082115b156106bf57600160a060020a0383811660008181526020818152604080832080548801905588851680845281842080548990039055600183528184203390961684529482529182902080548790039055815186815291519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060016106c3565b61067660045460ff1681565b6040805160068054602060026001831615610100026000190190921691909104601f81018290048202840182019094528383526105f493908301828280156106b75780601f1061068c576101008083540402835291602001916106b7565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b6105f46005805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156106b75780601f1061068c576101008083540402835291602001916106b7565b61066260043560243533600160a060020a03166000908152602081905260408120548290108015906103845750600082115b156106ca5733600160a060020a0390811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a3506001610162565b604080516020604435600481810135601f810184900484028501840190955284845261066294813594602480359593946064949293910191819084018382808284375094965050505050505033600160a060020a03908116600081815260016020908152604080832094881680845294825280832087905580518781529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a383600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105965780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f19250505015156106d257610002565b6102e2600435602435600160a060020a03828116600090815260016020908152604080832093851683529290522054610162565b005b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106545780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b604080519115158252519081900360200190f35b6040805160ff9092168252519081900360200190f35b820191906000526020600020905b81548152906001019060200180831161069a57829003601f168201915b505050505081565b5060005b9392505050565b506000610162565b5060016106c35600000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000754204275636b73000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003544f54000000000000000000000000000000000000000000000000000000000026a042556c4f2a3f4e4e639cca524d1da70e60881417d4643e5382ed110a52719eafa0172f591a2a763d0bd6b13d042d8c5eb66e87f129c9dc77ada66b6041012db2b3"; + assert_eq!(hex::encode(output.encoded, false), expected); + + assert_eq!(hex::encode(output.data, false), call_data); +} + +// https://ropsten.etherscan.io/tx/0x14429509307efebfdaa05227d84c147450d168c68539351fbc01ed87c916ab2e +#[test] +fn test_sign_transaction_eip1559_native_transfer() { + let private = + hex::decode("4f96ed80e9a7555a6f74b3d658afdd9c756b0a40d4ca30c42c2039eb449bb904").unwrap(); + + let transfer = Proto::mod_Transaction::Transfer { + amount: U256::encode_be_compact(543_210_987_654_321), + data: Cow::default(), + }; + + let input = Proto::SigningInput { + chain_id: U256::encode_be_compact(3), + nonce: U256::encode_be_compact(6), + tx_mode: TransactionMode::Enveloped, + gas_limit: U256::encode_be_compact(21_100), + max_inclusion_fee_per_gas: U256::encode_be_compact(2_000_000_000), + max_fee_per_gas: U256::encode_be_compact(3_000_000_000), + to_address: "0xB9F5771C27664bF2282D98E09D7F50cEc7cB01a7".into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::transfer(transfer), + }), + private_key: private.into(), + ..Proto::SigningInput::default() + }; + + let output = Signer::::sign_proto(input); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected = "02f8710306847735940084b2d05e0082526c94b9f5771c27664bf2282d98e09d7f50cec7cb01a78701ee0c29f50cb180c080a092c336138f7d0231fe9422bb30ee9ef10bf222761fe9e04442e3a11e88880c64a06487026011dae03dc281bc21c7d7ede5c2226d197befb813a4ecad686b559e58"; + assert_eq!(hex::encode(output.encoded, false), expected); + + assert_eq!( + output.r.to_hex(), + "92c336138f7d0231fe9422bb30ee9ef10bf222761fe9e04442e3a11e88880c64" + ); + assert_eq!( + output.s.to_hex(), + "6487026011dae03dc281bc21c7d7ede5c2226d197befb813a4ecad686b559e58" + ); + assert_eq!(output.v.to_hex(), "00"); + + assert_eq!( + output.pre_hash.to_hex(), + "6468eb103d51c9a683b51818fdb73390151c9973831d2cfb4e9587ad54273155" + ); +} + +#[test] +fn test_sign_transaction_eip1559_erc20_transfer() { + let private = + hex::decode("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151").unwrap(); + + let erc20_transfer = Proto::mod_Transaction::ERC20Transfer { + to: "0x5322b34c88ed0691971bf52a7047448f0f4efc84".into(), + amount: U256::encode_be_compact(2_000_000_000_000_000_000), + }; + + let input = Proto::SigningInput { + chain_id: U256::encode_be_compact(1), + nonce: U256::encode_be_compact(0), + tx_mode: TransactionMode::Enveloped, + // 0x130B9 + gas_limit: U256::encode_be_compact(78_009), + // 0x77359400 + max_inclusion_fee_per_gas: U256::encode_be_compact(2_000_000_000), + // 0xB2D05E00 + max_fee_per_gas: U256::encode_be_compact(3_000_000_000), + // DAI + to_address: "0x6b175474e89094c44da98b954eedeac495271d0f".into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::erc20_transfer( + erc20_transfer, + ), + }), + private_key: private.into(), + ..Proto::SigningInput::default() + }; + + let output = Signer::::sign_proto(input); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected = "02f8b00180847735940084b2d05e00830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000c080a0adfcfdf98d4ed35a8967a0c1d78b42adb7c5d831cf5a3272654ec8f8bcd7be2ea011641e065684f6aa476f4fd250aa46cd0b44eccdb0a6e1650d658d1998684cdf"; + assert_eq!(hex::encode(output.encoded, false), expected); + + assert_eq!( + hex::encode(output.pre_hash, false), + "aa0ec30afa12acb48a080aa7157254193eeb2a4d248538b0747535baab98141f" + ); +} + +#[test] +fn test_sign_transaction_eip1559_erc20_approve() { + let private = + hex::decode("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151").unwrap(); + + let erc20_approve = Proto::mod_Transaction::ERC20Approve { + spender: "0x5322b34c88ed0691971bf52a7047448f0f4efc84".into(), + amount: U256::encode_be_compact(2_000_000_000_000_000_000), + }; + + let input = Proto::SigningInput { + chain_id: U256::encode_be_compact(1), + nonce: U256::encode_be_compact(0), + tx_mode: TransactionMode::Enveloped, + // 0x130B9 + gas_limit: U256::encode_be_compact(78_009), + // 0x77359400 + max_inclusion_fee_per_gas: U256::encode_be_compact(2_000_000_000), + // 0xB2D05E00 + max_fee_per_gas: U256::encode_be_compact(3_000_000_000), + // DAI + to_address: "0x6b175474e89094c44da98b954eedeac495271d0f".into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::erc20_approve( + erc20_approve, + ), + }), + private_key: private.into(), + ..Proto::SigningInput::default() + }; + + let output = Signer::::sign_proto(input); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected = "02f8b00180847735940084b2d05e00830130b9946b175474e89094c44da98b954eedeac495271d0f80b844095ea7b30000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000c080a05a43dda3dc193480ee532a5ed67ba8fbd2e3afb9eee218f4fb955b415d592925a01300e5b5f51c8cd5bf80f018cea3fb347fae589e65355068ac44ffc996313c60"; + assert_eq!(hex::encode(output.encoded, false), expected); + + assert_eq!( + hex::encode(output.pre_hash, false), + "bed87d402cc536fac3dbb346eac221f5ee783e04f0a7e3713817e55a78d08065" + ); +} + +#[test] +fn test_sign_transaction_eip1559_erc721_transfer() { + let private = + hex::decode("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151").unwrap(); + + let erc20_approve = Proto::mod_Transaction::ERC721Transfer { + from: "0x718046867b5b1782379a14eA4fc0c9b724DA94Fc".into(), + to: "0x5322b34c88ed0691971bf52a7047448f0f4efc84".into(), + token_id: hex::decode("23c47ee5").unwrap().into(), + }; + + let input = Proto::SigningInput { + chain_id: U256::encode_be_compact(1), + nonce: U256::encode_be_compact(0), + tx_mode: TransactionMode::Enveloped, + // 0x130B9 + gas_limit: U256::encode_be_compact(78_009), + // 0x77359400 + max_inclusion_fee_per_gas: U256::encode_be_compact(2_000_000_000), + // 0xB2D05E00 + max_fee_per_gas: U256::encode_be_compact(3_000_000_000), + to_address: "0x4e45e92ed38f885d39a733c14f1817217a89d425".into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::erc721_transfer( + erc20_approve, + ), + }), + private_key: private.into(), + ..Proto::SigningInput::default() + }; + + let output = Signer::::sign_proto(input); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected = "02f8d00180847735940084b2d05e00830130b9944e45e92ed38f885d39a733c14f1817217a89d42580b86423b872dd000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee5c080a0dbd591d1eac39bad62d7c158d5e1d55e7014d2218998f8980462e2f283f42d4aa05acadb904484a0fb5526a4c64b8addb8aac4f6548f90199e40eb787b79faed4a"; + assert_eq!(hex::encode(output.encoded, false), expected); + + assert_eq!( + hex::encode(output.pre_hash, false), + "6089b11574c558e717fd25fe84bb7525bc32408f7124c2f199e26dfb0845abdd" + ); +} + +#[test] +fn test_sign_transaction_eip1559_erc1155_transfer() { + let private = + hex::decode("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151").unwrap(); + + let erc1155_approve = Proto::mod_Transaction::ERC1155Transfer { + from: "0x718046867b5b1782379a14eA4fc0c9b724DA94Fc".into(), + to: "0x5322b34c88ed0691971bf52a7047448f0f4efc84".into(), + token_id: hex::decode("23c47ee5").unwrap().into(), + value: U256::encode_be_compact(2_000_000_000_000_000_000), + data: hex::decode("01020304").unwrap().into(), + }; + + let input = Proto::SigningInput { + chain_id: U256::encode_be_compact(1), + nonce: U256::encode_be_compact(0), + tx_mode: TransactionMode::Enveloped, + // 0x130B9 + gas_limit: U256::encode_be_compact(78_009), + // 0x77359400 + max_inclusion_fee_per_gas: U256::encode_be_compact(2_000_000_000), + // 0xB2D05E00 + max_fee_per_gas: U256::encode_be_compact(3_000_000_000), + to_address: "0x4e45e92ed38f885d39a733c14f1817217a89d425".into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::erc1155_transfer( + erc1155_approve, + ), + }), + private_key: private.into(), + ..Proto::SigningInput::default() + }; + + let output = Signer::::sign_proto(input); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected = "02f901500180847735940084b2d05e00830130b9944e45e92ed38f885d39a733c14f1817217a89d42580b8e4f242432a000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee50000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000c080a0533df41dda5540c57257b7fe89c29cefff0155c333e063220df2bf9680fcc15aa036a844fd20de5a51de96ceaaf078558e87d86426a4a5d4b215ee1fd0fa397f8a"; + assert_eq!(hex::encode(output.encoded, false), expected); + + assert_eq!( + hex::encode(output.pre_hash, false), + "d18f2ac6b3bff71457bfed1fc897b42a5a4220b1589eadbbace8d3def656f914" + ); +} + +#[test] +fn test_sign_transaction_non_typed_erc20_transfer_as_contract_generic() { + let private = + hex::decode("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151").unwrap(); + + // payload: transfer(0x5322b34c88ed0691971bf52a7047448f0f4efc84, 2000000000000000000) + let contract_data = hex::decode("0xa9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000").unwrap(); + let contract = Proto::mod_Transaction::ContractGeneric { + amount: U256::encode_be_compact(0), + data: contract_data.into(), + }; + + // `tx_mode` is not set, Legacy is the default. + let input = Proto::SigningInput { + chain_id: U256::encode_be_compact(1), + nonce: U256::encode_be_compact(0), + // 0x09c7652400 + gas_price: U256::encode_be_compact(42_000_000_000), + // 0x130B9 + gas_limit: U256::encode_be_compact(78_009), + to_address: "0x6b175474e89094c44da98b954eedeac495271d0f".into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::contract_generic( + contract, + ), + }), + private_key: private.into(), + ..Proto::SigningInput::default() + }; + + let output = Signer::::sign_proto(input); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected = "f8aa808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec8000025a0724c62ad4fbf47346b02de06e603e013f26f26b56fdc0be7ba3d6273401d98cea0032131cae15da7ddcda66963e8bef51ca0d9962bfef0547d3f02597a4a58c931"; + assert_eq!(hex::encode(output.encoded, false), expected); + + assert_eq!( + hex::encode(output.pre_hash, false), + "3a3fc6df8815e15874cc8d6c65f88ea0643b375e5b22726269d187035a5cb486" + ); +} + +#[test] +fn test_sign_transaction_non_typed_erc20_transfer_invalid_address() { + let private = + hex::decode("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151").unwrap(); + + let erc20_transfer = Proto::mod_Transaction::ERC20Transfer { + to: "0x5322b34c88ed0691971bf52a7047448f0f4efc84".into(), + amount: U256::encode_be_compact(2_000_000_000_000_000_000), + }; + + let input = Proto::SigningInput { + chain_id: U256::encode_be_compact(1), + tx_mode: TransactionMode::Legacy, + // 42000000000 + gas_price: U256::encode_be_compact(0x09_c765_2400), + // 78009 + gas_limit: U256::encode_be_compact(0x01_30B9), + // DAI + to_address: "0xdeadbeef".into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::erc20_transfer( + erc20_transfer, + ), + }), + private_key: private.into(), + ..Proto::SigningInput::default() + }; + + let output = Signer::::sign_proto(input); + assert_eq!(output.error, SigningErrorType::Error_invalid_address); + assert!(!output.error_message.is_empty()); +} + +#[test] +fn test_sign_transaction_non_typed_erc721_transfer() { + let private = + hex::decode("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151").unwrap(); + + let erc721_transfer = Proto::mod_Transaction::ERC721Transfer { + from: "0x718046867b5b1782379a14eA4fc0c9b724DA94Fc".into(), + to: "0x5322b34c88ed0691971bf52a7047448f0f4efc84".into(), + token_id: hex::decode("23c47ee5").unwrap().into(), + }; + + let input = Proto::SigningInput { + chain_id: U256::encode_be_compact(1), + tx_mode: TransactionMode::Legacy, + // 0x09c7652400 + gas_price: U256::encode_be_compact(42_000_000_000), + // 0x130B9 + gas_limit: U256::encode_be_compact(78_009), + to_address: "0x4e45e92ed38f885d39a733c14f1817217a89d425".into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::erc721_transfer( + erc721_transfer, + ), + }), + private_key: private.into(), + ..Proto::SigningInput::default() + }; + + let output = Signer::::sign_proto(input); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected = "f8ca808509c7652400830130b9944e45e92ed38f885d39a733c14f1817217a89d42580b86423b872dd000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee526a0d38440a4dc140a4100d301eb49fcc35b64439e27d1d8dd9b55823dca04e6e659a03b5f56a57feabc3406f123d6f8198cd7d7e2ced7e2d58d375f076952ecd9ce88"; + assert_eq!(hex::encode(output.encoded, false), expected); + + assert_eq!( + hex::encode(output.pre_hash, false), + "5de6e2bc3a0e95a32c2a6075bec622feccb8e8dab8fa564de7468416b44eff32" + ); + + let expected_data = "23b872dd000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee5"; + assert_eq!(hex::encode(output.data, false), expected_data); +} + +#[test] +fn test_sign_transaction_non_typed_erc1155_transfer() { + let private = + hex::decode("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151").unwrap(); + + let erc1155_transfer = Proto::mod_Transaction::ERC1155Transfer { + from: "0x718046867b5b1782379a14eA4fc0c9b724DA94Fc".into(), + to: "0x5322b34c88ed0691971bf52a7047448f0f4efc84".into(), + token_id: hex::decode("23c47ee5").unwrap().into(), + value: U256::encode_be_compact(2_000_000_000_000_000_000), + data: hex::decode("01020304").unwrap().into(), + }; + + let input = Proto::SigningInput { + chain_id: U256::encode_be_compact(1), + tx_mode: TransactionMode::Legacy, + // 0x09c7652400 + gas_price: U256::encode_be_compact(42_000_000_000), + // 0x130B9 + gas_limit: U256::encode_be_compact(78_009), + to_address: "0x4e45e92ed38f885d39a733c14f1817217a89d425".into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::erc1155_transfer( + erc1155_transfer, + ), + }), + private_key: private.into(), + ..Proto::SigningInput::default() + }; + + let output = Signer::::sign_proto(input); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected = "f9014a808509c7652400830130b9944e45e92ed38f885d39a733c14f1817217a89d42580b8e4f242432a000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee50000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000004010203040000000000000000000000000000000000000000000000000000000026a010315488201ac801ce346bffd1570de147615462d7e7db3cf08cf558465c6b79a06643943b24593bc3904a9fda63bb169881730994c973ab80f07d66a698064573"; + assert_eq!(hex::encode(output.encoded, false), expected); + + assert_eq!( + hex::encode(output.pre_hash, false), + "d183270df1081772f867c9e6d16601028dd7faa6103e5d7286f1f5e4bde4f547" + ); + + let expected_data = "f242432a000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee50000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000"; + assert_eq!(hex::encode(output.data, false), expected_data); +} diff --git a/rust/tw_hash/Cargo.toml b/rust/tw_hash/Cargo.toml index a1f57154a06..9d22a32c414 100644 --- a/rust/tw_hash/Cargo.toml +++ b/rust/tw_hash/Cargo.toml @@ -3,18 +3,24 @@ name = "tw_hash" version = "0.1.0" edition = "2021" +[features] +default = ["serde"] + [dependencies] +arbitrary = { version = "1", features = ["derive"], optional = true } blake-hash = "0.4.1" -blake2 = "0.10.6" blake2b-ref = "0.3.1" digest = "0.10.6" groestl = "0.10.1" hmac = "0.12.1" ripemd = "0.1.3" +serde = { version = "1.0.159", features = ["derive"], optional = true } sha1 = "0.10.5" sha2 = "0.10.6" sha3 = "0.10.6" +tw_encoding = { path = "../tw_encoding" } tw_memory = { path = "../tw_memory" } +zeroize = "1.6.0" [dev-dependencies] -hex = "0.4.3" +serde_json = "1.0.95" diff --git a/rust/tw_hash/fuzz/.gitignore b/rust/tw_hash/fuzz/.gitignore new file mode 100644 index 00000000000..5c404b9583f --- /dev/null +++ b/rust/tw_hash/fuzz/.gitignore @@ -0,0 +1,5 @@ +target +corpus +artifacts +coverage +Cargo.lock diff --git a/rust/tw_hash/fuzz/Cargo.toml b/rust/tw_hash/fuzz/Cargo.toml new file mode 100644 index 00000000000..af42f8796a8 --- /dev/null +++ b/rust/tw_hash/fuzz/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "tw_hash-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = { version = "0.4", features = ["arbitrary-derive"] } + +[dependencies.tw_hash] +path = ".." + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[profile.release] +debug = 1 + +[[bin]] +name = "hash_fuzz" +path = "fuzz_targets/hash_fuzz.rs" +test = false +doc = false diff --git a/rust/tw_hash/fuzz/fuzz_targets/hash_fuzz.rs b/rust/tw_hash/fuzz/fuzz_targets/hash_fuzz.rs new file mode 100644 index 00000000000..505c0facbe9 --- /dev/null +++ b/rust/tw_hash/fuzz/fuzz_targets/hash_fuzz.rs @@ -0,0 +1,33 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#![no_main] + +use libfuzzer_sys::{arbitrary, fuzz_target}; + +#[derive(arbitrary::Arbitrary, Debug)] +struct HashInput<'a> { + data: &'a [u8], + additional_data: &'a [u8], + hash_size: usize, +} + +fuzz_target!(|input: HashInput<'_>| { + tw_hash::blake::blake_256(input.data); + tw_hash::blake2::blake2_b(input.data, input.hash_size).ok(); + tw_hash::blake2::blake2_b_personal(input.data, input.hash_size, input.additional_data).ok(); + tw_hash::groestl::groestl_512(input.data); + tw_hash::hmac::hmac_sha256(input.additional_data, input.data); + tw_hash::ripemd::ripemd_160(input.data); + tw_hash::sha1::sha1(input.data); + tw_hash::sha2::sha256(input.data); + tw_hash::sha2::sha512(input.data); + tw_hash::sha2::sha512_256(input.data); + tw_hash::sha3::keccak256(input.data); + tw_hash::sha3::keccak512(input.data); + tw_hash::sha3::sha3_256(input.data); + tw_hash::sha3::sha3_512(input.data); +}); diff --git a/rust/tw_hash/src/blake2.rs b/rust/tw_hash/src/blake2.rs index 1968c4d2fdd..6eaef3a5ad9 100644 --- a/rust/tw_hash/src/blake2.rs +++ b/rust/tw_hash/src/blake2.rs @@ -4,26 +4,67 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use blake2::{ - digest::{Update, VariableOutput}, - Blake2bVar, -}; +use crate::Error; use blake2b_ref::Blake2bBuilder; +use std::ops::RangeInclusive; -pub fn blake2_b(input: &[u8], hash_size: usize) -> Vec { - let mut hasher = Blake2bVar::new(hash_size).unwrap(); +const OUTPUT_HASH_LEN_RANGE: RangeInclusive = 1..=64; +const PERSONAL_INPUT_MAX_LEN: usize = 16; + +pub fn blake2_b(input: &[u8], hash_size: usize) -> Result, Error> { + if !OUTPUT_HASH_LEN_RANGE.contains(&hash_size) { + return Err(Error::InvalidHashLength); + } + + let mut hasher = Blake2bBuilder::new(hash_size).build(); hasher.update(input); let mut buf = vec![0; hash_size]; - hasher.finalize_variable(&mut buf).unwrap(); - buf + hasher.finalize(&mut buf); + Ok(buf) } -pub fn blake2_b_personal(input: &[u8], hash_size: usize, personal_input: &[u8]) -> Vec { +pub fn blake2_b_personal( + input: &[u8], + hash_size: usize, + personal_input: &[u8], +) -> Result, Error> { + if !OUTPUT_HASH_LEN_RANGE.contains(&hash_size) { + return Err(Error::InvalidHashLength); + } + if personal_input.len() > PERSONAL_INPUT_MAX_LEN { + return Err(Error::InvalidArgument); + } + let mut output: Vec = vec![0; hash_size]; let mut blake2b = Blake2bBuilder::new(hash_size) .personal(personal_input) .build(); blake2b.update(input); blake2b.finalize(&mut output); - output + Ok(output) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_blake2_b_invalid_hash_size() { + let input = b"Hello world"; + blake2_b(input, 65).unwrap_err(); + } + + #[test] + fn test_blake2_b_personal_invalid_hash_size() { + let input = b"Hello world"; + let personal_data = b"MyApp Files Hash"; + blake2_b_personal(input, 65, personal_data).unwrap_err(); + } + + #[test] + fn test_blake2_b_personal_invalid_personal_data() { + let input = b"Hello world"; + let personal_data = b"MyApp Files Hash ..."; + blake2_b_personal(input, 64, personal_data).unwrap_err(); + } } diff --git a/rust/tw_hash/src/ffi.rs b/rust/tw_hash/src/ffi.rs index 48fd50e627b..e49f96f336b 100644 --- a/rust/tw_hash/src/ffi.rs +++ b/rust/tw_hash/src/ffi.rs @@ -6,8 +6,32 @@ #![allow(clippy::missing_safety_doc)] -use crate::{blake, blake2, groestl, hmac, ripemd, sha1, sha2, sha3}; -use tw_memory::ffi::c_byte_array::CByteArray; +use crate::{blake, blake2, groestl, hmac, ripemd, sha1, sha2, sha3, Error}; +use tw_memory::ffi::c_byte_array::{CByteArray, CByteArrayResult}; +use tw_memory::ffi::c_result::ErrorCode; + +#[repr(C)] +#[derive(Debug, PartialEq)] +pub enum CHashingCode { + Ok = 0, + InvalidHashLength = 1, + InvalidArgument = 2, +} + +impl From for CHashingCode { + fn from(e: Error) -> Self { + match e { + Error::FromHexError(_) | Error::InvalidArgument => CHashingCode::InvalidArgument, + Error::InvalidHashLength => CHashingCode::InvalidHashLength, + } + } +} + +impl From for ErrorCode { + fn from(e: CHashingCode) -> Self { + e as ErrorCode + } +} /// Computes the Blake-256 hash of the `input` byte array. /// \param input *non-null* byte array. @@ -29,9 +53,12 @@ pub unsafe extern "C" fn blake2_b( input: *const u8, input_len: usize, hash_size: usize, -) -> CByteArray { +) -> CByteArrayResult { let input = std::slice::from_raw_parts(input, input_len); - blake2::blake2_b(input, hash_size).into() + blake2::blake2_b(input, hash_size) + .map(CByteArray::from) + .map_err(CHashingCode::from) + .into() } /// Computes the personalized BLAKE2B hash of the `input` byte array. @@ -48,10 +75,13 @@ pub unsafe extern "C" fn blake2_b_personal( hash_size: usize, personal_input: *const u8, personal_len: usize, -) -> CByteArray { +) -> CByteArrayResult { let input = std::slice::from_raw_parts(input, input_len); let personal = std::slice::from_raw_parts(personal_input, personal_len); - blake2::blake2_b_personal(input, hash_size, personal).into() + blake2::blake2_b_personal(input, hash_size, personal) + .map(CByteArray::from) + .map_err(CHashingCode::from) + .into() } /// Computes the Groestl-512 hash of the `input` byte array. diff --git a/rust/tw_hash/src/hash_array.rs b/rust/tw_hash/src/hash_array.rs new file mode 100644 index 00000000000..95ab0388a12 --- /dev/null +++ b/rust/tw_hash/src/hash_array.rs @@ -0,0 +1,312 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::Error; +use std::fmt; +use std::ops::{Deref, DerefMut}; +use std::str::FromStr; +use tw_encoding::hex; +use tw_encoding::hex::ToHex; +use zeroize::DefaultIsZeroes; + +pub type H32 = Hash<4>; +pub type H160 = Hash<20>; +pub type H256 = Hash<32>; +pub type H264 = Hash<33>; +pub type H512 = Hash<64>; +pub type H520 = Hash<65>; + +pub type SplitHash = (Hash, Hash); + +/// Concatenates `left: Hash` and `right: Hash` into `Hash` +/// where `N = L + R` (statically checked). +pub fn concat( + left: Hash, + right: Hash, +) -> Hash { + // Ensure if `L + R == N` at compile time. + let _ = AssertSplit::::VALID; + + let mut res = Hash::new(); + res[0..L].copy_from_slice(left.as_slice()); + res[L..(L + R)].copy_from_slice(right.as_slice()); + + res +} + +/// Represents a fixed-length byte array. +#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Hash([u8; N]); + +impl DefaultIsZeroes for Hash {} + +/// cbindgen:ignore +impl Hash { + pub const LEN: usize = N; + + pub const fn new() -> Self { + Hash([0; N]) + } + + pub fn as_slice(&self) -> &[u8] { + &self.0 + } + + pub fn into_vec(self) -> Vec { + self.0.to_vec() + } + + /// Splits the byte array into two pieces with `L` and `R` lengths accordingly, + /// where `N = L + R` (statically checked). + pub fn split(&self) -> SplitHash { + // Ensure if `L + R == N` at compile time. + let _ = AssertSplit::::VALID; + + let mut left: Hash = Hash::default(); + let mut right: Hash = Hash::default(); + + left.copy_from_slice(&self.0[0..L]); + right.copy_from_slice(&self.0[L..]); + + (left, right) + } + + pub const fn take(self) -> [u8; N] { + self.0 + } + + pub const fn len() -> usize { + N + } +} + +/// This is a [`Hash::split`] helper that ensures that `L + R == N` at compile time. +/// Assertion example: +/// ```rust(ignore) +/// let hash = H256::default(); +/// let (left, right): (H128, H160) = hash.split(); +/// +/// // output: +/// // error[E0080]: evaluation of `hash_array::AssertSplit::<16, 20, 32>::EQ` failed +/// // --> tw_hash/src/hash_array.rs:67:41 +/// // | +/// // 67 | pub const EQ: usize = (R + L - N) + (N - (R + L)); +/// // | ^^^^^^^^^^^^^ attempt to compute `32_usize - 36_usize`, which would overflow +/// ``` +/// +/// TODO remove once [feature(generic_const_exprs)](https://github.com/rust-lang/rust/issues/76560) is stable. +struct AssertSplit; + +/// cbindgen:ignore +impl AssertSplit { + pub const VALID: usize = (R + L - N) + (N - (R + L)); +} + +/// Implement `str` -> `Hash` conversion for test purposes. +impl From<&'static str> for Hash { + fn from(hex: &'static str) -> Self { + hex.parse().expect("Expected a valid hex-encoded hash") + } +} + +impl From<[u8; N]> for Hash { + fn from(data: [u8; N]) -> Self { + Hash(data) + } +} + +impl<'a, const N: usize> TryFrom<&'a [u8]> for Hash { + type Error = Error; + + fn try_from(data: &'a [u8]) -> Result { + if data.len() != N { + return Err(Error::InvalidHashLength); + } + + let mut dest = Hash::default(); + dest.0.copy_from_slice(data); + Ok(dest) + } +} + +impl FromStr for Hash { + type Err = Error; + + fn from_str(s: &str) -> Result { + let data = hex::decode(s)?; + Hash::try_from(data.as_slice()) + } +} + +impl Default for Hash { + fn default() -> Self { + Hash::new() + } +} + +impl AsRef<[u8]> for Hash { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl Deref for Hash { + type Target = [u8; N]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Hash { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl fmt::Display for Hash { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.to_hex()) + } +} + +#[cfg(feature = "serde")] +mod impl_serde { + use super::Hash; + use serde::de::Error; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + impl<'de, const N: usize> Deserialize<'de> for Hash { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let hex = String::deserialize(deserializer)?; + hex.parse().map_err(|e| Error::custom(format!("{e:?}"))) + } + } + + impl Serialize for Hash { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.to_string().serialize(serializer) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_hash_from_str() { + let actual = H256::from("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); + let expected = [ + 175u8, 238, 252, 167, 77, 154, 50, 92, 241, 214, 182, 145, 29, 97, 166, 92, 50, 175, + 168, 224, 43, 213, 231, 142, 46, 74, 194, 145, 11, 171, 69, 245, + ]; + assert_eq!(actual.0[..], expected[..]); + } + + #[test] + fn test_hash_display() { + let str = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"; + assert_eq!(H256::from(str).to_string(), str); + } + + #[test] + #[should_panic] + fn test_from_hex_literal_invalid() { + let _ = H256::from("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45x5"); + } + + #[test] + fn test_from_hex_invalid() { + let err = + H256::from_str("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45x5") + .unwrap_err(); + + match err { + Error::FromHexError(_) => (), + other => panic!("Expected 'FromHexError', found: {other:?}"), + } + } + + #[test] + fn test_from_hex_invalid_len() { + let err = H256::from_str("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45") + .unwrap_err(); + + match err { + Error::InvalidHashLength => (), + other => panic!("Expected 'Error::InvalidHashLength', found: {other:?}"), + } + } + + #[test] + fn test_hash_split_at() { + let hash = + Hash::<32>::from("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); + + let (left, right): (Hash<10>, Hash<22>) = hash.split(); + assert_eq!(left, Hash::from("afeefca74d9a325cf1d6")); + assert_eq!( + right, + Hash::from("b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5") + ); + } + + #[test] + fn test_hash_concat() { + let left: Hash<10> = Hash::from("afeefca74d9a325cf1d6"); + let right: Hash<22> = Hash::from("b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); + let res: Hash<32> = concat(left, right); + + let expected: Hash<32> = + Hash::from("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); + assert_eq!(res, expected); + } +} + +/// cbindgen:ignore +#[cfg(all(test, feature = "serde"))] +mod serde_tests { + use super::*; + use serde_json::json; + + const BYTES_32: [u8; 32] = [ + 175u8, 238, 252, 167, 77, 154, 50, 92, 241, 214, 182, 145, 29, 97, 166, 92, 50, 175, 168, + 224, 43, 213, 231, 142, 46, 74, 194, 145, 11, 171, 69, 245, + ]; + const HEX_32: &str = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"; + + #[test] + fn test_hash_deserialize() { + let unprefixed: Hash<32> = serde_json::from_value(json!(HEX_32)).unwrap(); + assert_eq!(unprefixed.0, BYTES_32); + + let prefixed: Hash<32> = serde_json::from_value(json!(HEX_32)).unwrap(); + assert_eq!(prefixed.0, BYTES_32); + } + + #[test] + fn test_hash_deserialize_error() { + serde_json::from_value::>(json!( + "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45" + )) + .unwrap_err(); + } + + #[test] + fn test_hash_serialize() { + let hash = Hash::<32>::from(HEX_32); + let actual = serde_json::to_value(&hash).unwrap(); + assert_eq!(actual, json!(HEX_32)); + } +} diff --git a/rust/tw_hash/src/lib.rs b/rust/tw_hash/src/lib.rs index 24a6f5d3121..8c8e83c80bf 100644 --- a/rust/tw_hash/src/lib.rs +++ b/rust/tw_hash/src/lib.rs @@ -14,4 +14,24 @@ pub mod sha1; pub mod sha2; pub mod sha3; +mod hash_array; mod hash_wrapper; + +pub use hash_array::{concat, Hash, H160, H256, H264, H32, H512, H520}; + +use tw_encoding::hex::FromHexError; + +pub type Result = std::result::Result; + +#[derive(Debug)] +pub enum Error { + FromHexError(FromHexError), + InvalidHashLength, + InvalidArgument, +} + +impl From for Error { + fn from(e: FromHexError) -> Self { + Error::FromHexError(e) + } +} diff --git a/rust/tw_hash/tests/hash_ffi_tests.rs b/rust/tw_hash/tests/hash_ffi_tests.rs index 9fc13561154..a66f6dd13d9 100644 --- a/rust/tw_hash/tests/hash_ffi_tests.rs +++ b/rust/tw_hash/tests/hash_ffi_tests.rs @@ -4,18 +4,22 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use tw_encoding::hex; +use tw_encoding::hex::FromHexError; use tw_hash::ffi::{ blake2_b, blake2_b_personal, blake_256, groestl_512, hmac__sha256, keccak256, keccak512, - ripemd_160, sha1, sha256, sha3__256, sha3__512, sha512, sha512_256, + ripemd_160, sha1, sha256, sha3__256, sha3__512, sha512, sha512_256, CHashingCode, }; +use tw_hash::Error; use tw_memory::ffi::c_byte_array::CByteArray; +use tw_memory::ffi::c_result::ErrorCode; type ExternFn = unsafe extern "C" fn(*const u8, usize) -> CByteArray; #[track_caller] pub fn test_hash_helper(hash: ExternFn, input: &[u8], expected: &str) { let decoded = unsafe { hash(input.as_ptr(), input.len()).into_vec() }; - assert_eq!(hex::encode(decoded), expected); + assert_eq!(hex::encode(decoded, false), expected); } #[test] @@ -24,7 +28,7 @@ fn test_blake2b() { /// Declare a `blake2_b` helper that forwards `HASH_SIZE`. unsafe extern "C" fn blake2_b_hash(input: *const u8, input_len: usize) -> CByteArray { - blake2_b(input, input_len, HASH_SIZE) + blake2_b(input, input_len, HASH_SIZE).unwrap() } test_hash_helper(blake2_b_hash, b"Hello world", "6ff843ba685842aa82031d3f53c48b66326df7639a63d128974c5c14f31a0f33343a8c65551134ed1ae0f2b0dd2bb495dc81039e3eeb0aa1bb0388bbeac29183"); @@ -44,10 +48,11 @@ fn test_blake2b_personal() { personal_data.as_ptr(), personal_data.len(), ) + .unwrap() .into_vec() }; let expected = "20d9cd024d4fb086aae819a1432dd2466de12947831b75c5a30cf2676095d3b4"; - assert_eq!(hex::encode(actual), expected); + assert_eq!(hex::encode(actual, false), expected); } #[test] @@ -81,7 +86,7 @@ fn test_hmac_sha256() { let actual = unsafe { hmac__sha256(key.as_ptr(), key.len(), data.as_ptr(), data.len()).into_vec() }; let expected = "a7301d5563614e3955750e4480aabf7753f44b4975308aeb8e23c31e114962ab".to_string(); - assert_eq!(hex::encode(actual), expected); + assert_eq!(hex::encode(actual, false), expected); } #[test] @@ -210,3 +215,20 @@ fn test_sha3_512() { "01dedd5de4ef14642445ba5f5b97c15e47b9ad931326e4b0727cd94cefc44fff23f07bf543139939b49128caf436dc1bdee54fcb24023a08d9403f9b4bf0d450", ); } + +#[test] +fn test_c_hashing_error_convert() { + assert_eq!(ErrorCode::from(CHashingCode::Ok), 0); + assert_eq!(ErrorCode::from(CHashingCode::InvalidHashLength), 1); + assert_eq!(ErrorCode::from(CHashingCode::InvalidArgument), 2); + + assert_eq!( + CHashingCode::InvalidArgument, + Error::FromHexError(FromHexError::OddLength).into(), + ); + assert_eq!(CHashingCode::InvalidArgument, Error::InvalidArgument.into()); + assert_eq!( + CHashingCode::InvalidHashLength, + Error::InvalidHashLength.into(), + ); +} diff --git a/rust/tw_keypair/Cargo.toml b/rust/tw_keypair/Cargo.toml index 0b7a81b8e7d..0328396aedf 100644 --- a/rust/tw_keypair/Cargo.toml +++ b/rust/tw_keypair/Cargo.toml @@ -3,7 +3,34 @@ name = "tw_keypair" version = "0.1.0" edition = "2021" +[features] +test-utils = [] + [dependencies] +arbitrary = { version = "1", features = ["derive"], optional = true } +lazy_static = "1.4.0" +serde = { version = "1.0.159", features = ["derive"] } +starknet-crypto = "0.5.0" +starknet-ff = "0.3.2" tw_encoding = { path = "../tw_encoding" } +tw_hash = { path = "../tw_hash" } tw_memory = { path = "../tw_memory" } +tw_misc = { path = "../tw_misc" } +zeroize = "1.6.0" +# ECDSA specific: +ecdsa = "0.16.6" der = "0.7.3" +k256 = { version = "0.13.0", features = ["ecdh", "ecdsa", "schnorr", "std"], default-features = false } +p256 = { version = "0.13.0", features = ["ecdsa", "std"], default-features = false } +rfc6979 = "0.4.0" +# ED25519 specific: +blake2 = "0.9" +curve25519-dalek = "3" +digest = "0.9.0" +sha2 = "0.9" + +[dev-dependencies] +serde_json = "1.0.95" + +[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] +ring = "0.16.20" diff --git a/rust/tw_keypair/fuzz/.gitignore b/rust/tw_keypair/fuzz/.gitignore new file mode 100644 index 00000000000..5c404b9583f --- /dev/null +++ b/rust/tw_keypair/fuzz/.gitignore @@ -0,0 +1,5 @@ +target +corpus +artifacts +coverage +Cargo.lock diff --git a/rust/tw_keypair/fuzz/Cargo.toml b/rust/tw_keypair/fuzz/Cargo.toml new file mode 100644 index 00000000000..17da9d20ca1 --- /dev/null +++ b/rust/tw_keypair/fuzz/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "tw_keypair-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = { version = "0.4", features = ["arbitrary-derive"] } + +[dependencies.tw_keypair] +path = ".." +features = ["arbitrary"] + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +# https://github.com/rust-lang/rust/issues/95240 +[profile.release.package.curve25519-dalek] +opt-level = 2 + +[[bin]] +name = "tw_private_sign" +path = "fuzz_targets/tw_private_sign.rs" +test = false +doc = false + +[[bin]] +name = "tw_public_verify" +path = "fuzz_targets/tw_public_verify.rs" +test = false +doc = false + +[[bin]] +name = "tw_private_to_public" +path = "fuzz_targets/tw_private_to_public.rs" +test = false +doc = false diff --git a/rust/tw_keypair/fuzz/fuzz_targets/tw_private_sign.rs b/rust/tw_keypair/fuzz/fuzz_targets/tw_private_sign.rs new file mode 100644 index 00000000000..b7a574f3073 --- /dev/null +++ b/rust/tw_keypair/fuzz/fuzz_targets/tw_private_sign.rs @@ -0,0 +1,23 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#![no_main] + +use libfuzzer_sys::{arbitrary, fuzz_target}; +use tw_keypair::tw::{Curve, PrivateKey}; + +#[derive(arbitrary::Arbitrary, Debug)] +struct TWPrivateSignInput { + private: Vec, + hash: Vec, + curve: Curve, +} + +fuzz_target!(|input: TWPrivateSignInput| { + if let Ok(private) = PrivateKey::new(input.private) { + private.sign(&input.hash, input.curve).ok(); + } +}); diff --git a/rust/tw_keypair/fuzz/fuzz_targets/tw_private_to_public.rs b/rust/tw_keypair/fuzz/fuzz_targets/tw_private_to_public.rs new file mode 100644 index 00000000000..25de9500029 --- /dev/null +++ b/rust/tw_keypair/fuzz/fuzz_targets/tw_private_to_public.rs @@ -0,0 +1,22 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#![no_main] + +use libfuzzer_sys::{arbitrary, fuzz_target}; +use tw_keypair::tw::{PrivateKey, PublicKeyType}; + +#[derive(arbitrary::Arbitrary, Debug)] +struct TWPrivateToPublicInput { + private: Vec, + public_key_type: PublicKeyType, +} + +fuzz_target!(|input: TWPrivateToPublicInput| { + if let Ok(private) = PrivateKey::new(input.private) { + private.get_public_key_by_type(input.public_key_type).ok(); + } +}); diff --git a/rust/tw_keypair/fuzz/fuzz_targets/tw_public_verify.rs b/rust/tw_keypair/fuzz/fuzz_targets/tw_public_verify.rs new file mode 100644 index 00000000000..394d2aad416 --- /dev/null +++ b/rust/tw_keypair/fuzz/fuzz_targets/tw_public_verify.rs @@ -0,0 +1,24 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#![no_main] + +use libfuzzer_sys::{arbitrary, fuzz_target}; +use tw_keypair::tw::{PublicKey, PublicKeyType}; + +#[derive(arbitrary::Arbitrary, Debug)] +struct TWPublicVerifyInput { + public: Vec, + hash: Vec, + signature: Vec, + pubkey_type: PublicKeyType, +} + +fuzz_target!(|input: TWPublicVerifyInput| { + if let Ok(public) = PublicKey::new(input.public, input.pubkey_type) { + public.verify(&input.signature, &input.hash); + } +}); diff --git a/rust/tw_keypair/src/ecdsa/canonical.rs b/rust/tw_keypair/src/ecdsa/canonical.rs new file mode 100644 index 00000000000..fdfc9244479 --- /dev/null +++ b/rust/tw_keypair/src/ecdsa/canonical.rs @@ -0,0 +1,143 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +//! This module is a Proof Of Concept that proves the possibility to implement the following in Rust: +//! https://github.com/trustwallet/wallet-core/blob/d9e35ec485b1366dd10509192d02d9dbb6877ab3/src/PrivateKey.cpp#L253-L282 +//! +//! # Warning +//! +//! **Not production ready** + +#![allow(dead_code)] + +use crate::ecdsa::signature::Signature; +use crate::ecdsa::EcdsaCurve; +use crate::{KeyPairError, KeyPairResult}; +use ecdsa::elliptic_curve::generic_array::ArrayLength; +use ecdsa::elliptic_curve::subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; +use ecdsa::elliptic_curve::{Curve, FieldBytesEncoding, PrimeField, Scalar}; +use ecdsa::hazmat::{bits2field, DigestPrimitive, SignPrimitive}; +use ecdsa::SigningKey; +use k256::sha2::digest::crypto_common::BlockSizeUser; +use k256::sha2::digest::{FixedOutput, FixedOutputReset}; +use k256::sha2::Digest; +use rfc6979::{ByteArray, HmacDrbg}; +use tw_hash::H256; + +type CurveDigest = ::Digest; + +/// Implements https://github.com/trustwallet/wallet-core/blob/d9e35ec485b1366dd10509192d02d9dbb6877ab3/src/PrivateKey.cpp#L253-L282 +pub(crate) fn sign_with_canonical( + signing_key: &SigningKey, + hash_to_sign: H256, + mut is_canonical: F, +) -> KeyPairResult> +where + for<'a> F: FnMut(&'a Signature) -> bool, + Scalar: SignPrimitive, + ::Uint: FieldBytesEncoding, +{ + let priv_scalar = signing_key.as_nonzero_scalar(); + + let nonce = + bits2field::(hash_to_sign.as_slice()).map_err(|_| KeyPairError::InvalidSignMessage)?; + let entropy_input = &priv_scalar.to_repr(); + let n = &C::ORDER.encode_field_bytes(); + let additional_data = &[]; + + let mut hmac_drbg = HmacDrbg::>::new(entropy_input, &nonce, additional_data); + + for _ in 0..10000 { + // The `k` number is different on each iteration due to the `hmac_drbg` mutation. + let k = generate_k::, _>(&mut hmac_drbg, n); + let k_scalar = Scalar::::from_repr(k).unwrap(); + + let (sig, r) = priv_scalar + .try_sign_prehashed(k_scalar, &nonce) + .map_err(|_| KeyPairError::SigningError)?; + let r = r.ok_or(KeyPairError::SigningError)?; + + let signature = Signature::::new(sig, r); + if is_canonical(&signature) { + return Ok(signature); + } + } + + Err(KeyPairError::SigningError) +} + +fn ct_eq>(a: &ByteArray, b: &ByteArray) -> Choice { + let mut ret = Choice::from(1); + + for (a, b) in a.iter().zip(b.iter()) { + ret.conditional_assign(&Choice::from(0), !a.ct_eq(b)); + } + + ret +} + +pub(crate) fn ct_lt>(a: &ByteArray, b: &ByteArray) -> Choice { + let mut borrow = 0; + + // Perform subtraction with borrow a byte-at-a-time, interpreting a + // no-borrow condition as the less-than case + for (&a, &b) in a.iter().zip(b.iter()).rev() { + let c = (b as u16).wrapping_add(borrow >> (u8::BITS - 1)); + borrow = (a as u16).wrapping_sub(c) >> u8::BITS as u8; + } + + !borrow.ct_eq(&0) +} + +pub(crate) fn generate_k(hmac_drbg: &mut HmacDrbg, n: &ByteArray) -> ByteArray +where + D: Digest + BlockSizeUser + FixedOutput + FixedOutputReset, + N: ArrayLength, +{ + loop { + let mut k = ByteArray::::default(); + hmac_drbg.fill_bytes(&mut k); + + let k_is_zero = ct_eq(&k, &ByteArray::default()); + if (!k_is_zero & ct_lt(&k, n)).into() { + return k; + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::ecdsa::secp256k1::{PrivateKey, Signature}; + use tw_hash::H520; + use tw_misc::traits::ToBytesVec; + + fn is_unsigned(byte: u8) -> bool { + byte & 0x80 == 0 + } + + fn fio_is_canonical(sig: &Signature) -> bool { + let sig = sig.to_vec(); + is_unsigned(sig[0]) + && !(sig[0] == 0 && is_unsigned(sig[1])) + && is_unsigned(sig[32]) + && !(sig[32] == 0 && is_unsigned(sig[33])) + } + + #[test] + fn test_sign_canonical() { + let private = PrivateKey::try_from( + "ba0828d5734b65e3bcc2c51c93dfc26dd71bd666cc0273adee77d73d9a322035", + ) + .unwrap(); + let hash_to_sign = + H256::from("71b7098e8150cde90f3ec00280815d3069f81c7cdb6d83bbe2b897b1afbe7cd6"); + + let actual = sign_with_canonical(&private.secret, hash_to_sign, fio_is_canonical).unwrap(); + let expected = H520::from("42ceeaa4a3d0ea0429ab09e4d969abd812c65ad4efef9e95e3a19cc3c41be3770ad0222dac6aa1b350cf9273fa922801d11b6142cb0fe639e2fe3fd988e5aec400"); + assert_eq!(actual.to_bytes(), expected); + } +} diff --git a/rust/tw_keypair/src/ecdsa/mod.rs b/rust/tw_keypair/src/ecdsa/mod.rs index 965960c855c..61e49f722c3 100644 --- a/rust/tw_keypair/src/ecdsa/mod.rs +++ b/rust/tw_keypair/src/ecdsa/mod.rs @@ -4,4 +4,25 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use ecdsa::elliptic_curve::bigint::U256; +use ecdsa::elliptic_curve::consts::U32; +use ecdsa::elliptic_curve::CurveArithmetic; +use ecdsa::hazmat::DigestPrimitive; +use ecdsa::PrimeCurve; + +mod canonical; pub mod der; +pub mod nist256p1; +pub mod secp256k1; +pub mod signature; + +/// This is an alias used for convenience. +pub trait EcdsaCurve: + PrimeCurve + CurveArithmetic + DigestPrimitive +{ +} + +impl EcdsaCurve for T where + T: PrimeCurve + CurveArithmetic + DigestPrimitive +{ +} diff --git a/rust/tw_keypair/src/ecdsa/nist256p1/keypair.rs b/rust/tw_keypair/src/ecdsa/nist256p1/keypair.rs new file mode 100644 index 00000000000..df9ff15a8e0 --- /dev/null +++ b/rust/tw_keypair/src/ecdsa/nist256p1/keypair.rs @@ -0,0 +1,73 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ecdsa::nist256p1::private::PrivateKey; +use crate::ecdsa::nist256p1::public::PublicKey; +use crate::ecdsa::nist256p1::{Signature, VerifySignature}; +use crate::traits::{KeyPairTrait, SigningKeyTrait, VerifyingKeyTrait}; +use crate::{KeyPairError, KeyPairResult}; +use tw_encoding::hex; +use tw_hash::H256; +use zeroize::ZeroizeOnDrop; +use zeroize::Zeroizing; + +/// Represents a pair of `nist256p1` private and public keys. +#[derive(ZeroizeOnDrop)] +pub struct KeyPair { + private: PrivateKey, + #[zeroize(skip)] + public: PublicKey, +} + +impl KeyPairTrait for KeyPair { + type Private = PrivateKey; + type Public = PublicKey; + + fn public(&self) -> &Self::Public { + &self.public + } + + fn private(&self) -> &Self::Private { + &self.private + } +} + +impl SigningKeyTrait for KeyPair { + type SigningMessage = H256; + type Signature = Signature; + + fn sign(&self, message: Self::SigningMessage) -> KeyPairResult { + self.private.sign(message) + } +} + +impl VerifyingKeyTrait for KeyPair { + type SigningMessage = H256; + type VerifySignature = VerifySignature; + + fn verify(&self, signature: Self::VerifySignature, message: Self::SigningMessage) -> bool { + self.public.verify(signature, message) + } +} + +impl<'a> TryFrom<&'a [u8]> for KeyPair { + type Error = KeyPairError; + + fn try_from(bytes: &'a [u8]) -> Result { + let private = PrivateKey::try_from(bytes)?; + let public = private.public(); + Ok(KeyPair { private, public }) + } +} + +impl<'a> TryFrom<&'a str> for KeyPair { + type Error = KeyPairError; + + fn try_from(hex: &'a str) -> Result { + let bytes = Zeroizing::new(hex::decode(hex).map_err(|_| KeyPairError::InvalidSecretKey)?); + Self::try_from(bytes.as_slice()) + } +} diff --git a/rust/tw_keypair/src/ecdsa/nist256p1/mod.rs b/rust/tw_keypair/src/ecdsa/nist256p1/mod.rs new file mode 100644 index 00000000000..890d77d610a --- /dev/null +++ b/rust/tw_keypair/src/ecdsa/nist256p1/mod.rs @@ -0,0 +1,109 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use p256::NistP256; + +mod keypair; +mod private; +mod public; + +pub use keypair::KeyPair; +pub use private::PrivateKey; +pub use public::PublicKey; + +pub type Signature = crate::ecdsa::signature::Signature; +pub type VerifySignature = crate::ecdsa::signature::VerifySignature; + +#[cfg(test)] +mod tests { + use super::*; + use crate::traits::{KeyPairTrait, SigningKeyTrait, VerifyingKeyTrait}; + use tw_encoding::hex; + use tw_hash::sha3::keccak256; + use tw_hash::{H256, H264, H520}; + use tw_misc::traits::{ToBytesVec, ToBytesZeroizing}; + + #[test] + fn test_key_pair() { + let secret = + hex::decode("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5") + .unwrap(); + let key_pair = KeyPair::try_from(secret.as_slice()).unwrap(); + assert_eq!(key_pair.private().to_zeroizing_vec().as_slice(), secret); + assert_eq!( + key_pair.public().uncompressed(), + H520::from("046d786ab8fda678cf50f71d13641049a393b325063b8c0d4e5070de48a2caf9ab918b4fe46ccbf56701fb210d67d91c5779468f6b3fdc7a63692b9b62543f47ae") + ); + assert_eq!( + key_pair.public().compressed(), + H264::from("026d786ab8fda678cf50f71d13641049a393b325063b8c0d4e5070de48a2caf9ab"), + ); + } + + #[test] + fn test_private_key_from() { + let hex = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"; + let expected = hex::decode(hex).unwrap(); + + // Test `From<&'static str>`. + let private = PrivateKey::try_from(hex).unwrap(); + assert_eq!(private.to_zeroizing_vec().as_slice(), expected); + + // Test `From<&'a [u8]>`. + let private = PrivateKey::try_from(expected.as_slice()).unwrap(); + assert_eq!(private.to_zeroizing_vec().as_slice(), expected); + } + + #[test] + fn test_private_key_sign_verify() { + let secret = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"; + let keypair = KeyPair::try_from(secret).unwrap(); + + let hash_to_sign = keccak256(b"hello"); + let hash_to_sign = H256::try_from(hash_to_sign.as_slice()).unwrap(); + let signature = keypair.sign(hash_to_sign).unwrap(); + + let expected = H520::from("8859e63a0c0cc2fc7f788d7e78406157b288faa6f76f76d37c4cd1534e8d83c468f9fd6ca7dde378df594625dcde98559389569e039282275e3d87c26e36447401"); + assert_eq!(signature.to_bytes(), expected); + + let verify_signature = VerifySignature::from(signature); + assert!(keypair.verify(verify_signature, hash_to_sign)); + } + + #[test] + fn test_public_key_from() { + let compressed = "026d786ab8fda678cf50f71d13641049a393b325063b8c0d4e5070de48a2caf9ab"; + let uncompressed = "046d786ab8fda678cf50f71d13641049a393b325063b8c0d4e5070de48a2caf9ab918b4fe46ccbf56701fb210d67d91c5779468f6b3fdc7a63692b9b62543f47ae"; + let expected_compressed = H264::from(compressed); + let expected_uncompressed = H520::from(uncompressed); + + // From extended public key. + let public = PublicKey::try_from(uncompressed).unwrap(); + assert_eq!(public.to_vec(), expected_compressed.into_vec()); + assert_eq!(public.compressed(), expected_compressed); + assert_eq!(public.uncompressed(), expected_uncompressed); + + // From compressed public key. + let public = PublicKey::try_from(compressed).unwrap(); + assert_eq!(public.to_vec(), expected_compressed.into_vec()); + assert_eq!(public.compressed(), expected_compressed); + assert_eq!(public.uncompressed(), expected_uncompressed); + } + + #[test] + fn test_public_key_recover() { + let sign_bytes = H520::from("8859e63a0c0cc2fc7f788d7e78406157b288faa6f76f76d37c4cd1534e8d83c468f9fd6ca7dde378df594625dcde98559389569e039282275e3d87c26e36447401"); + let sign = Signature::from_bytes(sign_bytes.as_slice()).unwrap(); + + let signed_hash = keccak256(b"hello"); + let signed_hash = H256::try_from(signed_hash.as_slice()).unwrap(); + + let actual = PublicKey::recover(sign, signed_hash).unwrap(); + let expected_compressed = + H264::from("026d786ab8fda678cf50f71d13641049a393b325063b8c0d4e5070de48a2caf9ab"); + assert_eq!(actual.compressed(), expected_compressed); + } +} diff --git a/rust/tw_keypair/src/ecdsa/nist256p1/private.rs b/rust/tw_keypair/src/ecdsa/nist256p1/private.rs new file mode 100644 index 00000000000..3149967ce68 --- /dev/null +++ b/rust/tw_keypair/src/ecdsa/nist256p1/private.rs @@ -0,0 +1,66 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ecdsa::nist256p1::public::PublicKey; +use crate::ecdsa::nist256p1::Signature; +use crate::traits::SigningKeyTrait; +use crate::{KeyPairError, KeyPairResult}; +use p256::ecdsa::SigningKey; +use tw_encoding::hex; +use tw_hash::H256; +use tw_misc::traits::ToBytesZeroizing; +use zeroize::{ZeroizeOnDrop, Zeroizing}; + +/// Represents a `nist256p1` private key. +#[derive(ZeroizeOnDrop)] +pub struct PrivateKey { + pub(crate) secret: SigningKey, +} + +impl PrivateKey { + /// Returns an associated `nist256p1` public key. + pub fn public(&self) -> PublicKey { + PublicKey::new(*self.secret.verifying_key()) + } +} + +impl SigningKeyTrait for PrivateKey { + type SigningMessage = H256; + type Signature = Signature; + + fn sign(&self, message: Self::SigningMessage) -> KeyPairResult { + let (signature, recovery_id) = self + .secret + .sign_prehash_recoverable(message.as_slice()) + .map_err(|_| KeyPairError::SigningError)?; + Ok(Signature::new(signature, recovery_id)) + } +} + +impl<'a> TryFrom<&'a [u8]> for PrivateKey { + type Error = KeyPairError; + + fn try_from(data: &'a [u8]) -> Result { + let secret = SigningKey::from_slice(data).map_err(|_| KeyPairError::InvalidSecretKey)?; + Ok(PrivateKey { secret }) + } +} + +impl<'a> TryFrom<&'a str> for PrivateKey { + type Error = KeyPairError; + + fn try_from(hex: &'a str) -> Result { + let bytes = Zeroizing::new(hex::decode(hex).map_err(|_| KeyPairError::InvalidSecretKey)?); + Self::try_from(bytes.as_slice()) + } +} + +impl ToBytesZeroizing for PrivateKey { + fn to_zeroizing_vec(&self) -> Zeroizing> { + let secret = Zeroizing::new(self.secret.to_bytes()); + Zeroizing::new(secret.as_slice().to_vec()) + } +} diff --git a/rust/tw_keypair/src/ecdsa/nist256p1/public.rs b/rust/tw_keypair/src/ecdsa/nist256p1/public.rs new file mode 100644 index 00000000000..5255e920621 --- /dev/null +++ b/rust/tw_keypair/src/ecdsa/nist256p1/public.rs @@ -0,0 +1,94 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ecdsa::nist256p1::{Signature, VerifySignature}; +use crate::traits::VerifyingKeyTrait; +use crate::{KeyPairError, KeyPairResult}; +use p256::ecdsa::signature::hazmat::PrehashVerifier; +use p256::ecdsa::VerifyingKey; +use tw_encoding::hex; +use tw_hash::{H256, H264, H520}; +use tw_misc::traits::ToBytesVec; + +/// Represents a `nist256p1` public key. +#[derive(Clone)] +pub struct PublicKey { + pub(crate) public: VerifyingKey, +} + +/// cbindgen:ignore +impl PublicKey { + /// The number of bytes in a compressed public key. + pub const COMPRESSED: usize = H264::len(); + /// The number of bytes in an uncompressed public key. + pub const UNCOMPRESSED: usize = H520::len(); + + /// Recover a [`PublicKey`] from the given `message` and the signature over that. + pub fn recover(sign: Signature, message: H256) -> KeyPairResult { + VerifyingKey::recover_from_prehash(message.as_slice(), &sign.signature, sign.v) + .map(|public| PublicKey { public }) + .map_err(|_| KeyPairError::InvalidSignature) + } + + /// Creates a public key from the given [`VerifyingKey`]. + pub(crate) fn new(public: VerifyingKey) -> PublicKey { + PublicKey { public } + } + + /// Returns the raw data of the compressed public key (33 bytes). + pub fn compressed(&self) -> H264 { + let compressed = true; + H264::try_from(self.public.to_encoded_point(compressed).as_bytes()) + .expect("Expected 33 byte array Public Key") + } + + /// Returns the raw data of the uncompressed public key (65 bytes). + pub fn uncompressed(&self) -> H520 { + let compressed = false; + H520::try_from(self.public.to_encoded_point(compressed).as_bytes()) + .expect("Expected 65 byte array Public Key") + } +} + +impl VerifyingKeyTrait for PublicKey { + type SigningMessage = H256; + type VerifySignature = VerifySignature; + + fn verify(&self, sign: Self::VerifySignature, message: Self::SigningMessage) -> bool { + self.public + .verify_prehash(message.as_slice(), &sign.signature) + .is_ok() + } +} + +impl<'a> TryFrom<&'a str> for PublicKey { + type Error = KeyPairError; + + fn try_from(hex: &'a str) -> Result { + let bytes = hex::decode(hex).map_err(|_| KeyPairError::InvalidPublicKey)?; + Self::try_from(bytes.as_slice()) + } +} + +impl<'a> TryFrom<&'a [u8]> for PublicKey { + type Error = KeyPairError; + + /// Expected either `H264` or `H520` slice. + fn try_from(data: &'a [u8]) -> Result { + Ok(PublicKey { + public: VerifyingKey::from_sec1_bytes(data) + .map_err(|_| KeyPairError::InvalidPublicKey)?, + }) + } +} + +/// Return the compressed bytes representation by default. +/// Consider using [`PublicKey::compressed`] or [`PublicKey::uncompressed`] instead. +impl ToBytesVec for PublicKey { + fn to_vec(&self) -> Vec { + self.compressed().to_vec() + } +} diff --git a/rust/tw_keypair/src/ecdsa/secp256k1/keypair.rs b/rust/tw_keypair/src/ecdsa/secp256k1/keypair.rs new file mode 100644 index 00000000000..665d341e8e7 --- /dev/null +++ b/rust/tw_keypair/src/ecdsa/secp256k1/keypair.rs @@ -0,0 +1,72 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ecdsa::secp256k1::private::PrivateKey; +use crate::ecdsa::secp256k1::public::PublicKey; +use crate::ecdsa::secp256k1::{Signature, VerifySignature}; +use crate::traits::{KeyPairTrait, SigningKeyTrait, VerifyingKeyTrait}; +use crate::{KeyPairError, KeyPairResult}; +use tw_encoding::hex; +use tw_hash::H256; +use zeroize::{ZeroizeOnDrop, Zeroizing}; + +/// Represents a pair of `secp256k1` private and public keys. +#[derive(ZeroizeOnDrop)] +pub struct KeyPair { + private: PrivateKey, + #[zeroize(skip)] + public: PublicKey, +} + +impl KeyPairTrait for KeyPair { + type Private = PrivateKey; + type Public = PublicKey; + + fn public(&self) -> &Self::Public { + &self.public + } + + fn private(&self) -> &Self::Private { + &self.private + } +} + +impl SigningKeyTrait for KeyPair { + type SigningMessage = H256; + type Signature = Signature; + + fn sign(&self, message: Self::SigningMessage) -> KeyPairResult { + self.private.sign(message) + } +} + +impl VerifyingKeyTrait for KeyPair { + type SigningMessage = H256; + type VerifySignature = VerifySignature; + + fn verify(&self, signature: Self::VerifySignature, message: Self::SigningMessage) -> bool { + self.public.verify(signature, message) + } +} + +impl<'a> TryFrom<&'a [u8]> for KeyPair { + type Error = KeyPairError; + + fn try_from(bytes: &'a [u8]) -> Result { + let private = PrivateKey::try_from(bytes)?; + let public = private.public(); + Ok(KeyPair { private, public }) + } +} + +impl<'a> TryFrom<&'a str> for KeyPair { + type Error = KeyPairError; + + fn try_from(hex: &'a str) -> Result { + let bytes = Zeroizing::new(hex::decode(hex).map_err(|_| KeyPairError::InvalidSecretKey)?); + Self::try_from(bytes.as_slice()) + } +} diff --git a/rust/tw_keypair/src/ecdsa/secp256k1/mod.rs b/rust/tw_keypair/src/ecdsa/secp256k1/mod.rs new file mode 100644 index 00000000000..ebb0400205c --- /dev/null +++ b/rust/tw_keypair/src/ecdsa/secp256k1/mod.rs @@ -0,0 +1,153 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use k256::Secp256k1; + +mod keypair; +mod private; +mod public; + +pub use keypair::KeyPair; +pub use private::PrivateKey; +pub use public::PublicKey; + +pub type Signature = crate::ecdsa::signature::Signature; +pub type VerifySignature = crate::ecdsa::signature::VerifySignature; + +#[cfg(test)] +mod tests { + use super::*; + use crate::traits::{KeyPairTrait, SigningKeyTrait, VerifyingKeyTrait}; + use tw_encoding::hex; + use tw_hash::sha3::keccak256; + use tw_hash::{H256, H264, H520}; + use tw_misc::traits::{ToBytesVec, ToBytesZeroizing}; + + #[test] + fn test_key_pair() { + let secret = + hex::decode("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5") + .unwrap(); + let key_pair = KeyPair::try_from(secret.as_slice()).unwrap(); + assert_eq!(key_pair.private().to_zeroizing_vec().as_slice(), secret); + assert_eq!( + key_pair.public().compressed(), + H264::from("0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1") + ); + } + + #[test] + fn test_key_pair_sign() { + let key_pair = + KeyPair::try_from("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5") + .unwrap(); + + let hash_to_sign = keccak256(b"hello"); + let hash_to_sign = H256::try_from(hash_to_sign.as_slice()).unwrap(); + let signature = key_pair.sign(hash_to_sign).unwrap(); + + let expected = H520::from("8720a46b5b3963790d94bcc61ad57ca02fd153584315bfa161ed3455e336ba624d68df010ed934b8792c5b6a57ba86c3da31d039f9612b44d1bf054132254de901"); + assert_eq!(signature.to_bytes(), expected); + + let verify_signature = VerifySignature::from(signature); + assert!(key_pair.verify(verify_signature, hash_to_sign)); + } + + #[test] + fn test_private_key_from() { + let hex = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"; + let expected = hex::decode(hex).unwrap(); + + // Test `From<&'static str>`. + let private = PrivateKey::try_from(hex).unwrap(); + assert_eq!(private.to_zeroizing_vec().as_slice(), expected); + + // Test `From<&'a [u8]>`. + let private = PrivateKey::try_from(expected.as_slice()).unwrap(); + assert_eq!(private.to_zeroizing_vec().as_slice(), expected); + } + + #[test] + fn test_private_key_sign_verify() { + let secret = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"; + let private = PrivateKey::try_from(secret).unwrap(); + let public = private.public(); + + let hash_to_sign = keccak256(b"hello"); + let hash_to_sign = H256::try_from(hash_to_sign.as_slice()).unwrap(); + let signature = private.sign(hash_to_sign).unwrap(); + + let expected = H520::from("8720a46b5b3963790d94bcc61ad57ca02fd153584315bfa161ed3455e336ba624d68df010ed934b8792c5b6a57ba86c3da31d039f9612b44d1bf054132254de901"); + assert_eq!(signature.to_bytes(), expected); + + let verify_signature = VerifySignature::from(signature); + assert!(public.verify(verify_signature, hash_to_sign)); + } + + #[test] + fn test_public_key_from() { + let compressed = "0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1"; + let uncompressed = "0499c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c166b489a4b7c491e7688e6ebea3a71fc3a1a48d60f98d5ce84c93b65e423fde91"; + let expected_compressed = H264::from(compressed); + let expected_uncompressed = H520::from(uncompressed); + + // From extended public key. + let public = PublicKey::try_from(uncompressed).unwrap(); + assert_eq!(public.to_vec(), expected_compressed.into_vec()); + assert_eq!(public.compressed(), expected_compressed); + assert_eq!(public.uncompressed(), expected_uncompressed); + + // From compressed public key. + let public = PublicKey::try_from(compressed).unwrap(); + assert_eq!(public.to_vec(), expected_compressed.into_vec()); + assert_eq!(public.compressed(), expected_compressed); + assert_eq!(public.uncompressed(), expected_uncompressed); + } + + #[test] + fn test_verify_invalid() { + let secret = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"; + let private = PrivateKey::try_from(secret).unwrap(); + + let signature_bytes = H520::from("375df53b6a4931dcf41e062b1c64288ed4ff3307f862d5c1b1c71964ce3b14c99422d0fdfeb2807e9900a26d491d5e8a874c24f98eec141ed694d7a433a90f0801"); + let verify_sig = VerifySignature::try_from(signature_bytes.as_slice()).unwrap(); + + let hash_to_sign = keccak256(b"hello"); + let hash_to_sign = H256::try_from(hash_to_sign.as_slice()).unwrap(); + + assert!(!private.public().verify(verify_sig, hash_to_sign)); + } + + #[test] + fn test_shared_key_hash() { + let private = PrivateKey::try_from( + "9cd3b16e10bd574fed3743d8e0de0b7b4e6c69f3245ab5a168ef010d22bfefa0", + ) + .unwrap(); + let public = PublicKey::try_from( + "02a18a98316b5f52596e75bfa5ca9fa9912edd0c989b86b73d41bb64c9c6adb992", + ) + .unwrap(); + let actual = private.shared_key_hash(&public); + let expected = + H256::from("ef2cf705af8714b35c0855030f358f2bee356ff3579cea2607b2025d80133c3a"); + assert_eq!(actual, expected); + } + + #[test] + fn test_public_key_recover() { + let sign_bytes = H520::from("8720a46b5b3963790d94bcc61ad57ca02fd153584315bfa161ed3455e336ba624d68df010ed934b8792c5b6a57ba86c3da31d039f9612b44d1bf054132254de901"); + let sign = Signature::from_bytes(sign_bytes.as_slice()).unwrap(); + + let signed_hash = keccak256(b"hello"); + let signed_hash = H256::try_from(signed_hash.as_slice()).unwrap(); + + let actual = PublicKey::recover(sign, signed_hash).unwrap(); + let expected_compressed = + H264::from("0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1"); + assert_eq!(actual.compressed(), expected_compressed); + } +} diff --git a/rust/tw_keypair/src/ecdsa/secp256k1/private.rs b/rust/tw_keypair/src/ecdsa/secp256k1/private.rs new file mode 100644 index 00000000000..088b3bd549f --- /dev/null +++ b/rust/tw_keypair/src/ecdsa/secp256k1/private.rs @@ -0,0 +1,89 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ecdsa::secp256k1::public::PublicKey; +use crate::ecdsa::secp256k1::Signature; +use crate::traits::SigningKeyTrait; +use crate::{KeyPairError, KeyPairResult}; +use k256::ecdsa::{SigningKey, VerifyingKey}; +use k256::elliptic_curve::sec1::ToEncodedPoint; +use k256::{AffinePoint, ProjectivePoint}; +use tw_encoding::hex; +use tw_hash::H256; +use tw_misc::traits::ToBytesZeroizing; +use zeroize::{ZeroizeOnDrop, Zeroizing}; + +/// Represents a `secp256k1` private key. +#[derive(ZeroizeOnDrop)] +pub struct PrivateKey { + pub(crate) secret: SigningKey, +} + +impl PrivateKey { + /// Returns an associated `secp256k1` public key. + pub fn public(&self) -> PublicKey { + PublicKey::new(*self.secret.verifying_key()) + } + + /// Computes an EC Diffie-Hellman secret in constant time. + /// The method is ported from [TW::PrivateKey::getSharedKey](https://github.com/trustwallet/wallet-core/blob/830b1c5baaf90692196163999e4ee2063c5f4e49/src/PrivateKey.cpp#L175-L191). + pub fn shared_key_hash(&self, pubkey: &PublicKey) -> H256 { + let shared_secret = diffie_hellman(&self.secret, &pubkey.public); + + // Get a compressed shared secret (33 bytes with a tag in front). + let compress = true; + let shared_secret_compressed = shared_secret.to_encoded_point(compress); + + let shared_secret_hash = tw_hash::sha2::sha256(shared_secret_compressed.as_bytes()); + H256::try_from(shared_secret_hash.as_slice()).expect("Expected 32 byte array sha256 hash") + } +} + +/// This method is inspired by [elliptic_curve::ecdh::diffie_hellman](https://github.com/RustCrypto/traits/blob/f0dbe44fea56d4c17e625ababacb580fec842137/elliptic-curve/src/ecdh.rs#L60-L70) +fn diffie_hellman(private: &SigningKey, public: &VerifyingKey) -> AffinePoint { + let public_point = ProjectivePoint::from(*public.as_affine()); + let secret_scalar = private.as_nonzero_scalar().as_ref(); + // Multiply the secret and public to get a shared secret affine point (x, y). + (public_point * secret_scalar).to_affine() +} + +impl SigningKeyTrait for PrivateKey { + type SigningMessage = H256; + type Signature = Signature; + + fn sign(&self, message: Self::SigningMessage) -> KeyPairResult { + let (signature, recovery_id) = self + .secret + .sign_prehash_recoverable(message.as_slice()) + .map_err(|_| KeyPairError::SigningError)?; + Ok(Signature::new(signature, recovery_id)) + } +} + +impl<'a> TryFrom<&'a [u8]> for PrivateKey { + type Error = KeyPairError; + + fn try_from(data: &'a [u8]) -> Result { + let secret = SigningKey::from_slice(data).map_err(|_| KeyPairError::InvalidSecretKey)?; + Ok(PrivateKey { secret }) + } +} + +impl<'a> TryFrom<&'a str> for PrivateKey { + type Error = KeyPairError; + + fn try_from(hex: &'a str) -> Result { + let bytes = Zeroizing::new(hex::decode(hex).map_err(|_| KeyPairError::InvalidSecretKey)?); + Self::try_from(bytes.as_slice()) + } +} + +impl ToBytesZeroizing for PrivateKey { + fn to_zeroizing_vec(&self) -> Zeroizing> { + let secret = Zeroizing::new(self.secret.to_bytes()); + Zeroizing::new(secret.as_slice().to_vec()) + } +} diff --git a/rust/tw_keypair/src/ecdsa/secp256k1/public.rs b/rust/tw_keypair/src/ecdsa/secp256k1/public.rs new file mode 100644 index 00000000000..68a48621a6e --- /dev/null +++ b/rust/tw_keypair/src/ecdsa/secp256k1/public.rs @@ -0,0 +1,100 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ecdsa::secp256k1::{Signature, VerifySignature}; +use crate::traits::VerifyingKeyTrait; +use crate::{KeyPairError, KeyPairResult}; +use k256::ecdsa::signature::hazmat::PrehashVerifier; +use k256::ecdsa::VerifyingKey; +use tw_encoding::hex; +use tw_hash::{Hash, H256, H264, H512, H520}; +use tw_misc::traits::ToBytesVec; + +/// Represents a `secp256k1` public key. +#[derive(Clone, PartialEq)] +pub struct PublicKey { + pub(crate) public: VerifyingKey, +} + +/// cbindgen:ignore +impl PublicKey { + /// The number of bytes in a compressed public key. + pub const COMPRESSED: usize = H264::len(); + /// The number of bytes in an uncompressed public key. + pub const UNCOMPRESSED: usize = H520::len(); + + /// Recover a [`PublicKey`] from the given `message` and the signature over that. + pub fn recover(sign: Signature, message: H256) -> KeyPairResult { + VerifyingKey::recover_from_prehash(message.as_slice(), &sign.signature, sign.v) + .map(|public| PublicKey { public }) + .map_err(|_| KeyPairError::InvalidSignature) + } + + /// Creates a public key from the given [`VerifyingKey`]. + pub(crate) fn new(public: VerifyingKey) -> PublicKey { + PublicKey { public } + } + + /// Returns the raw data of the compressed public key (33 bytes). + pub fn compressed(&self) -> H264 { + let compressed = true; + H264::try_from(self.public.to_encoded_point(compressed).as_bytes()) + .expect("Expected 33 byte array Public Key") + } + + /// Returns the raw data of the uncompressed public key (65 bytes). + pub fn uncompressed(&self) -> H520 { + let compressed = false; + H520::try_from(self.public.to_encoded_point(compressed).as_bytes()) + .expect("Expected 65 byte array Public Key") + } + + /// Returns the raw data of the uncompressed public key without the tag prefix (64 bytes). + pub fn uncompressed_without_prefix(&self) -> H512 { + let (_prefix, public): (Hash<1>, H512) = self.uncompressed().split(); + public + } +} + +impl VerifyingKeyTrait for PublicKey { + type SigningMessage = H256; + type VerifySignature = VerifySignature; + + fn verify(&self, sign: Self::VerifySignature, message: Self::SigningMessage) -> bool { + self.public + .verify_prehash(message.as_slice(), &sign.signature) + .is_ok() + } +} + +impl<'a> TryFrom<&'a str> for PublicKey { + type Error = KeyPairError; + + fn try_from(hex: &'a str) -> Result { + let bytes = hex::decode(hex).map_err(|_| KeyPairError::InvalidPublicKey)?; + Self::try_from(bytes.as_slice()) + } +} + +impl<'a> TryFrom<&'a [u8]> for PublicKey { + type Error = KeyPairError; + + /// Expected either `H264` or `H520` slice. + fn try_from(data: &'a [u8]) -> Result { + Ok(PublicKey { + public: VerifyingKey::from_sec1_bytes(data) + .map_err(|_| KeyPairError::InvalidPublicKey)?, + }) + } +} + +/// Return the compressed bytes representation by default. +/// Consider using [`PublicKey::compressed`] or [`PublicKey::uncompressed`] instead. +impl ToBytesVec for PublicKey { + fn to_vec(&self) -> Vec { + self.compressed().to_vec() + } +} diff --git a/rust/tw_keypair/src/ecdsa/signature.rs b/rust/tw_keypair/src/ecdsa/signature.rs new file mode 100644 index 00000000000..64fe61bfe83 --- /dev/null +++ b/rust/tw_keypair/src/ecdsa/signature.rs @@ -0,0 +1,174 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ecdsa::EcdsaCurve; +use crate::{KeyPairError, KeyPairResult}; +use ecdsa::elliptic_curve::FieldBytes; +use std::ops::{Range, RangeInclusive}; +use tw_hash::{H256, H520}; +use tw_misc::traits::ToBytesVec; + +/// Represents an ECDSA signature. +#[derive(Clone, Debug, PartialEq)] +pub struct Signature { + pub(crate) signature: ecdsa::Signature, + pub(crate) v: ecdsa::RecoveryId, +} + +/// cbindgen:ignore +impl Signature { + /// The number of bytes for a serialized signature representation. + pub const LEN: usize = 65; + pub const R_RANGE: Range = 0..32; + pub const S_RANGE: Range = 32..64; + pub const RECOVERY_LAST: usize = 64; + /// Expected signature with or without recovery byte in the end of the slice. + pub const VERIFY_SIGNATURE_LEN_RANGE: RangeInclusive = 64..=65; + + /// Creates a `secp256k1` recoverable signature from the given [`k256::ecdsa::Signature`] + /// and the `v` recovery byte. + pub(crate) fn new(signature: ecdsa::Signature, v: ecdsa::RecoveryId) -> Self { + Signature { signature, v } + } + + /// Returns the number of bytes for a serialized signature representation. + pub const fn len() -> usize { + Self::LEN + } + + /// Returns an r-coordinate as 32 byte array. + pub fn r(&self) -> H256 { + let (r, _s) = self.signature.split_bytes(); + H256::try_from(r.as_slice()).expect("Expected 'r' 32 byte length array") + } + + /// Returns an s-value as 32 byte array. + pub fn s(&self) -> H256 { + let (_, s) = self.signature.split_bytes(); + H256::try_from(s.as_slice()).expect("Expected 's' 32 byte length array") + } + + /// Returns a recovery ID. + pub fn v(&self) -> u8 { + self.v.to_byte() + } + + /// Tries to create a Signature from the serialized representation. + pub fn from_bytes(sig: &[u8]) -> KeyPairResult { + if sig.len() != Self::len() { + return Err(KeyPairError::InvalidSignature); + } + + let v = ecdsa::RecoveryId::from_byte(sig[Self::RECOVERY_LAST]) + .ok_or(KeyPairError::InvalidSignature)?; + + Ok(Signature { + signature: Self::signature_from_slices(&sig[Self::R_RANGE], &sig[Self::S_RANGE])?, + v, + }) + } + + /// Tries to create a Signature from the parts. + pub fn try_from_parts(r: H256, s: H256, v: u8) -> KeyPairResult { + Ok(Signature { + signature: Self::signature_from_slices(r.as_slice(), s.as_slice())?, + v: ecdsa::RecoveryId::from_byte(v).ok_or(KeyPairError::InvalidSignature)?, + }) + } + + /// Returns a standard binary signature representation: + /// RSV, where R - 32 byte array, S - 32 byte array, V - 1 byte. + pub fn to_bytes(&self) -> H520 { + let (r, s) = self.signature.split_bytes(); + + let mut dest = H520::default(); + dest[Self::R_RANGE].copy_from_slice(r.as_slice()); + dest[Self::S_RANGE].copy_from_slice(s.as_slice()); + dest[Self::RECOVERY_LAST] = self.v.to_byte(); + dest + } + + /// # Panic + /// + /// `r` and `s` must be 32 byte arrays, otherwise the function panics. + fn signature_from_slices(r: &[u8], s: &[u8]) -> KeyPairResult> { + let r = FieldBytes::::clone_from_slice(r); + let s = FieldBytes::::clone_from_slice(s); + + ecdsa::Signature::::from_scalars(r, s).map_err(|_| KeyPairError::InvalidSignature) + } +} + +impl ToBytesVec for Signature { + fn to_vec(&self) -> Vec { + self.to_bytes().to_vec() + } +} + +impl<'a, C: EcdsaCurve> TryFrom<&'a [u8]> for Signature { + type Error = KeyPairError; + + fn try_from(bytes: &'a [u8]) -> Result { + Signature::from_bytes(bytes) + } +} + +/// To verify the signature, it's enough to check `r` and `s` parts without the recovery ID. +pub struct VerifySignature { + pub signature: ecdsa::Signature, +} + +impl<'a, C: EcdsaCurve> TryFrom<&'a [u8]> for VerifySignature { + type Error = KeyPairError; + + fn try_from(sig: &'a [u8]) -> Result { + if !Signature::::VERIFY_SIGNATURE_LEN_RANGE.contains(&sig.len()) { + return Err(KeyPairError::InvalidSignature); + } + + Ok(VerifySignature { + signature: Signature::signature_from_slices( + &sig[Signature::::R_RANGE], + &sig[Signature::::S_RANGE], + )?, + }) + } +} + +impl From> for VerifySignature { + fn from(sig: Signature) -> Self { + VerifySignature { + signature: sig.signature, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use k256::Secp256k1; + + #[test] + fn test_signature() { + let sign_bytes = H520::from("d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a00"); + let sign = Signature::::from_bytes(sign_bytes.as_slice()).unwrap(); + assert_eq!( + sign.r(), + H256::from("d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47") + ); + assert_eq!( + sign.s(), + H256::from("786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a") + ); + assert_eq!(sign.v(), 0); + assert_eq!(sign.to_bytes(), sign_bytes); + } + + #[test] + fn test_signature_from_invalid_bytes() { + Signature::::from_bytes(b"123").unwrap_err(); + } +} diff --git a/rust/tw_keypair/src/ed25519/keypair.rs b/rust/tw_keypair/src/ed25519/keypair.rs new file mode 100644 index 00000000000..e9240d2d4d3 --- /dev/null +++ b/rust/tw_keypair/src/ed25519/keypair.rs @@ -0,0 +1,68 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ed25519::{private::PrivateKey, public::PublicKey, signature::Signature, Hasher512}; +use crate::traits::{KeyPairTrait, SigningKeyTrait, VerifyingKeyTrait}; +use crate::{KeyPairError, KeyPairResult}; +use tw_encoding::hex; +use zeroize::Zeroizing; + +/// Represents a pair of `ed25519` private and public keys. +#[derive(Debug)] +pub struct KeyPair { + private: PrivateKey, + public: PublicKey, +} + +impl KeyPairTrait for KeyPair { + type Private = PrivateKey; + type Public = PublicKey; + + fn public(&self) -> &Self::Public { + &self.public + } + + fn private(&self) -> &Self::Private { + &self.private + } +} + +impl SigningKeyTrait for KeyPair { + type SigningMessage = Vec; + type Signature = Signature; + + fn sign(&self, message: Self::SigningMessage) -> KeyPairResult { + self.private().sign_with_public_key(self.public(), &message) + } +} + +impl VerifyingKeyTrait for KeyPair { + type SigningMessage = Vec; + type VerifySignature = Signature; + + fn verify(&self, signature: Self::VerifySignature, message: Self::SigningMessage) -> bool { + self.public().verify(signature, message) + } +} + +impl<'a, H: Hasher512> TryFrom<&'a [u8]> for KeyPair { + type Error = KeyPairError; + + fn try_from(bytes: &'a [u8]) -> Result { + let private = PrivateKey::try_from(bytes)?; + let public = private.public(); + Ok(KeyPair { private, public }) + } +} + +impl<'a, H: Hasher512> TryFrom<&'a str> for KeyPair { + type Error = KeyPairError; + + fn try_from(hex: &'a str) -> Result { + let bytes = Zeroizing::new(hex::decode(hex).map_err(|_| KeyPairError::InvalidSecretKey)?); + Self::try_from(bytes.as_slice()) + } +} diff --git a/rust/tw_keypair/src/ed25519/mangle.rs b/rust/tw_keypair/src/ed25519/mangle.rs new file mode 100644 index 00000000000..f635cc19a40 --- /dev/null +++ b/rust/tw_keypair/src/ed25519/mangle.rs @@ -0,0 +1,17 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +/// Clamps the scalar by: +/// 1. clearing the 3 lower bits, +/// 2. clearing the highest bit, +/// 3. setting the second highest bit. +/// +/// Source: https://github.com/dalek-cryptography/ed25519-dalek/blob/1.0.1/src/secret.rs#L277-L279C26 +pub fn mangle_scalar(scalar: &mut [u8; 32]) { + scalar[0] &= 0b1111_1000; + scalar[31] &= 0b0011_1111; + scalar[31] |= 0b0100_0000; +} diff --git a/rust/tw_keypair/src/ed25519/mod.rs b/rust/tw_keypair/src/ed25519/mod.rs new file mode 100644 index 00000000000..32c079ad6af --- /dev/null +++ b/rust/tw_keypair/src/ed25519/mod.rs @@ -0,0 +1,265 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use digest::{consts::U64, Digest}; + +mod keypair; +mod mangle; +mod modifications; +mod private; +mod public; +mod secret; +mod signature; + +pub use modifications::{cardano, waves}; +pub use signature::Signature; + +/// Standard `ed25519` implementation. +pub mod sha512 { + use sha2::Sha512; + + pub type KeyPair = crate::ed25519::keypair::KeyPair; + pub type PrivateKey = crate::ed25519::private::PrivateKey; + pub type PublicKey = crate::ed25519::public::PublicKey; +} + +/// `ed25519` implementation using `BLAKE2B` hash function. +pub mod blake2b { + use blake2::Blake2b; + + pub type KeyPair = crate::ed25519::keypair::KeyPair; + pub type PrivateKey = crate::ed25519::private::PrivateKey; + pub type PublicKey = crate::ed25519::public::PublicKey; +} + +/// A hash function that returns 64 length output. +pub trait Hasher512: Digest { + const OUTPUT_LEN: usize = 64; + const HALF_LEN: usize = 32; +} + +impl Hasher512 for T where T: Digest {} + +#[cfg(test)] +mod tests { + use super::*; + use crate::traits::{KeyPairTrait, SigningKeyTrait, VerifyingKeyTrait}; + use tw_encoding::hex; + use tw_hash::sha2::sha256; + use tw_hash::sha3::keccak256; + use tw_hash::{H256, H512}; + use tw_misc::traits::{ToBytesVec, ToBytesZeroizing}; + + #[test] + fn test_private_from_bytes() { + let secret = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"; + + let private = sha512::PrivateKey::try_from( + "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", + ) + .unwrap(); + let actual = private.to_zeroizing_vec(); + assert_eq!(actual.as_slice(), H256::from(secret).as_slice()); + } + + #[test] + fn test_private_to_public() { + let private = sha512::PrivateKey::try_from( + "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", + ) + .unwrap(); + let public = private.public(); + + let expected = + H256::from("4870d56d074c50e891506d78faa4fb69ca039cc5f131eb491e166b975880e867"); + assert_eq!(public.to_vec(), expected.into_vec()); + } + + #[test] + fn test_private_to_public_blake2b() { + let private = blake2b::PrivateKey::try_from( + "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", + ) + .unwrap(); + let public = private.public(); + + let expected = + H256::from("b689ab808542e13f3d2ec56fe1efe43a1660dcadc73ce489fde7df98dd8ce5d9"); + assert_eq!(public.to_bytes(), expected); + } + + #[test] + fn test_keypair_sign_verify() { + let keypair = sha512::KeyPair::try_from( + "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", + ) + .unwrap(); + let to_sign = sha256(b"Hello"); + let actual = keypair.sign(to_sign.clone()).unwrap(); + + let expected = H512::from("42848abf2641a731e18b8a1fb80eff341a5acebdc56faeccdcbadb960aef775192842fccec344679446daa4d02d264259c8f9aa364164ebe0ebea218581e2e03"); + assert_eq!(actual.to_bytes(), expected); + + assert!(keypair.verify(actual, to_sign)); + } + + #[test] + fn test_signature_malleability() { + let orig_sign_bytes = hex::decode("ea85a47dcc18b512dfea7c209162abaea4808d77c1ec903dc7ba6e2afa3f9f07e7ed7a20a4e2fa1009db3d1443e937e6abb16ff3c3eaecb798faed7fbb40b008").unwrap(); + Signature::try_from(orig_sign_bytes.as_slice()).unwrap(); + + let modified_sign_bytes = hex::decode("ea85a47dcc18b512dfea7c209162abaea4808d77c1ec903dc7ba6e2afa3f9f07d4c1707dbe450d69df7735b721e316fbabb16ff3c3eaecb798faed7fbb40b018").unwrap(); + Signature::try_from(modified_sign_bytes.as_slice()).unwrap_err(); + } + + #[test] + fn test_keypair_sign_verify_blake2b() { + let keypair = blake2b::KeyPair::try_from( + "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", + ) + .unwrap(); + let to_sign = sha256(b"Hello"); + let actual = keypair.sign(to_sign.clone()).unwrap(); + + let expected = H512::from("5c1473944cd0234ebc5a91b2966b9e707a33b936dadd149417a2e53b6b3fc97bef17b767b1690708c74d7b4c8fe48703fd44a6ef59d4cc5b9f88ba992db0a003"); + assert_eq!(actual.to_bytes(), expected); + + assert!(keypair.verify(actual, to_sign)); + } + + #[test] + fn test_keypair_sign_verify_waves() { + let keypair = waves::KeyPair::try_from( + "9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a", + ) + .unwrap(); + let to_sign = hex::decode("0402559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d00000000016372e852120000000005f5e1000000000005f5e10001570acc4110b78a6d38b34d879b5bba38806202ecf1732f8542000766616c6166656c").unwrap(); + let actual = keypair.sign(to_sign.clone()).unwrap(); + + let expected = H512::from("af7989256f496e103ce95096b3f52196dd9132e044905fe486da3b829b5e403bcba95ab7e650a4a33948c2d05cfca2dce4d4df747e26402974490fb4c49fbe8f"); + assert_eq!(actual.to_bytes(), expected); + + assert!(keypair.verify(actual, to_sign)); + } + + #[test] + fn test_private_to_public_waves() { + let private = waves::PrivateKey::try_from( + "9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a", + ) + .unwrap(); + let actual = private.public(); + let expected = + H256::from("559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d"); + assert_eq!(actual.to_vec().as_slice(), expected.as_slice()); + } + + #[test] + fn test_private_from_bytes_cardano() { + let secret = "b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71effbf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4\ + 639aadd8b6499ae39b78018b79255fbd8f585cbda9cbb9e907a72af86afb7a05d41a57c2dec9a6a19d6bf3b1fa784f334f3a0048d25ccb7b78a7b44066f9ba7bed7f28be986cbe06819165f2ee41b403678a098961013cf4a2f3e9ea61fb6c1a"; + + let private = cardano::ExtendedPrivateKey::try_from(secret).unwrap(); + let actual = private.to_zeroizing_vec(); + assert_eq!(actual.as_slice(), hex::decode(secret).unwrap()); + } + + #[test] + fn test_keypair_sign_verify_extended_cardano() { + let secret = "b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71effbf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4\ + 639aadd8b6499ae39b78018b79255fbd8f585cbda9cbb9e907a72af86afb7a05d41a57c2dec9a6a19d6bf3b1fa784f334f3a0048d25ccb7b78a7b44066f9ba7bed7f28be986cbe06819165f2ee41b403678a098961013cf4a2f3e9ea61fb6c1a"; + + let keypair = cardano::ExtendedKeyPair::try_from(secret).unwrap(); + + let message = keccak256(b"hello"); + let actual = keypair.sign(message.clone()).unwrap(); + + let expected = H512::from("375df53b6a4931dcf41e062b1c64288ed4ff3307f862d5c1b1c71964ce3b14c99422d0fdfeb2807e9900a26d491d5e8a874c24f98eec141ed694d7a433a90f08"); + assert_eq!(actual.to_bytes(), expected); + + assert!(keypair.verify(actual, message)); + } + + #[test] + fn test_public_verify_invalid_waves() { + let invalid_sigs = [ + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ]; + + let public = waves::PublicKey::try_from( + "b60076cc30ffff5c29c65af9a13ce01c3affc231d09fccbcd1277319c7911634", + ) + .unwrap(); + let msg = vec![1, 2, 3]; + + for invalid_sig_hex in invalid_sigs { + let invalid_sig_bytes = hex::decode(invalid_sig_hex).unwrap(); + let invalid_sig = waves::Signature::try_from(invalid_sig_bytes.as_slice()).unwrap(); + + public.verify(invalid_sig, msg.clone()); + } + } + + #[test] + fn test_private_to_public_extended_cardano() { + let secret = "e8c8c5b2df13f3abed4e6b1609c808e08ff959d7e6fc3d849e3f2880550b574437aa559095324d78459b9bb2da069da32337e1cc5da78f48e1bd084670107f3110f3245ddf9132ecef98c670272ef39c03a232107733d4a1d28cb53318df26fa\ + e0d152bb611cb9ff34e945e4ff627e6fba81da687a601a879759cd76530b5744424db69a75edd4780a5fbc05d1a3c84ac4166ff8e424808481dd8e77627ce5f5bf2eea84515a4e16c4ff06c92381822d910b5cbf9e9c144e1fb76a6291af7276"; + + let private = cardano::ExtendedPrivateKey::try_from(secret).unwrap(); + let public = private.public(); + + let expected = "fafa7eb4146220db67156a03a5f7a79c666df83eb31abbfbe77c85e06d40da3110f3245ddf9132ecef98c670272ef39c03a232107733d4a1d28cb53318df26fa\ + f4b8d5201961e68f2e177ba594101f513ee70fe70a41324e8ea8eb787ffda6f4bf2eea84515a4e16c4ff06c92381822d910b5cbf9e9c144e1fb76a6291af7276"; + assert_eq!(public.to_vec(), hex::decode(expected).unwrap()); + } + + #[test] + fn test_public_key_from_bytes_extended_cardano() { + let pubkey_hex = "fafa7eb4146220db67156a03a5f7a79c666df83eb31abbfbe77c85e06d40da3110f3245ddf9132ecef98c670272ef39c03a232107733d4a1d28cb53318df26fa\ + f4b8d5201961e68f2e177ba594101f513ee70fe70a41324e8ea8eb787ffda6f4bf2eea84515a4e16c4ff06c92381822d910b5cbf9e9c144e1fb76a6291af7276"; + let pubkey_bytes = hex::decode(pubkey_hex).unwrap(); + + let actual = cardano::ExtendedPublicKey::try_from(pubkey_bytes.as_slice()).unwrap(); + assert_eq!(actual.to_vec(), pubkey_bytes); + } + + #[test] + fn test_signature_from_bytes() { + let signature = "418aff0000000000000000000000000000000000000000000000f600000000000000000000000000000000000000000000000000000000000000000000000010"; + let actual = Signature::try_from(hex::decode(signature).unwrap().as_slice()).unwrap(); + assert_eq!(actual.to_bytes(), H512::from(signature)); + } + + #[test] + fn test_waves_signature_from_bytes() { + let sig_bytes = H512::from("af7989256f496e103ce95096b3f52196dd9132e044905fe486da3b829b5e403bcba95ab7e650a4a33948c2d05cfca2dce4d4df747e26402974490fb4c49fbe8f"); + let signature = waves::Signature::try_from(sig_bytes.as_slice()).unwrap(); + assert_eq!(signature.to_vec(), sig_bytes.into_vec()); + } + + #[test] + fn test_keypair_from_invalid_bytes() { + let invalid = [0; 1]; + let _ = sha512::KeyPair::try_from(&invalid[..]).unwrap_err(); + let _ = sha512::PrivateKey::try_from(&invalid[..]).unwrap_err(); + let _ = sha512::PublicKey::try_from(&invalid[..]).unwrap_err(); + } + + #[test] + fn test_debug() { + let secret = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"; + let sign = H512::from("ea85a47dcc18b512dfea7c209162abaea4808d77c1ec903dc7ba6e2afa3f9f07e7ed7a20a4e2fa1009db3d1443e937e6abb16ff3c3eaecb798faed7fbb40b008"); + + let keypair = sha512::KeyPair::try_from(secret).unwrap(); + let sign = Signature::try_from(sign.as_slice()).unwrap(); + + let _ = format!("{:?}", keypair); + let _ = format!("{:?}", keypair.private()); + let _ = format!("{:?}", keypair.public()); + let _ = format!("{:?}", sign); + } +} diff --git a/rust/tw_keypair/src/ed25519/modifications/cardano/extended_keypair.rs b/rust/tw_keypair/src/ed25519/modifications/cardano/extended_keypair.rs new file mode 100644 index 00000000000..6da604cc660 --- /dev/null +++ b/rust/tw_keypair/src/ed25519/modifications/cardano/extended_keypair.rs @@ -0,0 +1,71 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ed25519::modifications::cardano::{ + extended_private::ExtendedPrivateKey, extended_public::ExtendedPublicKey, +}; +use crate::ed25519::{signature::Signature, Hasher512}; +use crate::traits::{KeyPairTrait, SigningKeyTrait, VerifyingKeyTrait}; +use crate::{KeyPairError, KeyPairResult}; +use tw_encoding::hex; +use zeroize::Zeroizing; + +/// Represents an `ed25519` extended key pair that is used in Cardano blockchain. +pub struct ExtendedKeyPair { + private: ExtendedPrivateKey, + public: ExtendedPublicKey, +} + +impl KeyPairTrait for ExtendedKeyPair { + type Private = ExtendedPrivateKey; + type Public = ExtendedPublicKey; + + fn public(&self) -> &Self::Public { + &self.public + } + + fn private(&self) -> &Self::Private { + &self.private + } +} + +impl SigningKeyTrait for ExtendedKeyPair { + type SigningMessage = Vec; + type Signature = Signature; + + fn sign(&self, message: Self::SigningMessage) -> KeyPairResult { + self.private() + .sign_with_public_key(self.public(), message.as_slice()) + } +} + +impl VerifyingKeyTrait for ExtendedKeyPair { + type SigningMessage = Vec; + type VerifySignature = Signature; + + fn verify(&self, signature: Self::VerifySignature, message: Self::SigningMessage) -> bool { + self.public().verify(signature, message) + } +} + +impl<'a, H: Hasher512> TryFrom<&'a [u8]> for ExtendedKeyPair { + type Error = KeyPairError; + + fn try_from(bytes: &'a [u8]) -> Result { + let private = ExtendedPrivateKey::try_from(bytes)?; + let public = private.public(); + Ok(ExtendedKeyPair { private, public }) + } +} + +impl<'a, H: Hasher512> TryFrom<&'a str> for ExtendedKeyPair { + type Error = KeyPairError; + + fn try_from(hex: &'a str) -> Result { + let bytes = Zeroizing::new(hex::decode(hex).map_err(|_| KeyPairError::InvalidSecretKey)?); + Self::try_from(bytes.as_slice()) + } +} diff --git a/rust/tw_keypair/src/ed25519/modifications/cardano/extended_private.rs b/rust/tw_keypair/src/ed25519/modifications/cardano/extended_private.rs new file mode 100644 index 00000000000..5c3ac800c52 --- /dev/null +++ b/rust/tw_keypair/src/ed25519/modifications/cardano/extended_private.rs @@ -0,0 +1,152 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ed25519::modifications::cardano::extended_public::{ + ExtendedPublicKey, ExtendedPublicPart, +}; +use crate::ed25519::public::PublicKey; +use crate::ed25519::secret::ExpandedSecretKey; +use crate::ed25519::signature::Signature; +use crate::ed25519::Hasher512; +use crate::traits::SigningKeyTrait; +use crate::{KeyPairError, KeyPairResult}; +use std::ops::Range; +use tw_encoding::hex; +use tw_hash::H256; +use tw_misc::traits::ToBytesZeroizing; +use zeroize::{ZeroizeOnDrop, Zeroizing}; + +/// Represents an `ed25519` extended private key that is used in Cardano blockchain. +pub struct ExtendedPrivateKey { + /// The first half (96 bytes) of the extended secret. + key: ExtendedSecretPart, + /// The second half (96 bytes) of the extended secret. + second_key: ExtendedSecretPart, +} + +/// cbindgen:ignore +impl ExtendedPrivateKey { + /// The number of bytes in a serialized private key (192 bytes). + pub const LEN: usize = ExtendedSecretPart::::LEN * 2; + const KEY_RANGE: Range = 0..ExtendedSecretPart::::LEN; + const SECOND_KEY_RANGE: Range = ExtendedSecretPart::::LEN..Self::LEN; + + /// Returns an associated Cardano extended `ed25519` public key. + pub fn public(&self) -> ExtendedPublicKey { + let key_public = PublicKey::with_expanded_secret_no_mangle(&self.key.expanded_key); + let second_key_public = + PublicKey::with_expanded_secret_no_mangle(&self.second_key.expanded_key); + + let key = ExtendedPublicPart::new(key_public, self.key.chain_code); + let second_key = ExtendedPublicPart::new(second_key_public, self.second_key.chain_code); + + ExtendedPublicKey::new(key, second_key) + } + + /// `ed25519` signing uses a public key associated with the private key. + pub(crate) fn sign_with_public_key( + &self, + public: &ExtendedPublicKey, + message: &[u8], + ) -> KeyPairResult { + self.key + .expanded_key + .sign_with_pubkey(public.key_for_signing(), message) + } +} + +impl SigningKeyTrait for ExtendedPrivateKey { + type SigningMessage = Vec; + type Signature = Signature; + + fn sign(&self, message: Self::SigningMessage) -> KeyPairResult { + self.sign_with_public_key(&self.public(), message.as_slice()) + } +} + +impl ToBytesZeroizing for ExtendedPrivateKey { + fn to_zeroizing_vec(&self) -> Zeroizing> { + let mut res = self.key.to_zeroizing_vec(); + res.extend_from_slice(self.second_key.to_zeroizing_vec().as_slice()); + res + } +} + +impl<'a, H: Hasher512> TryFrom<&'a [u8]> for ExtendedPrivateKey { + type Error = KeyPairError; + + fn try_from(bytes: &'a [u8]) -> Result { + if bytes.len() != Self::LEN { + return Err(KeyPairError::InvalidSecretKey); + } + let key = ExtendedSecretPart::try_from(&bytes[Self::KEY_RANGE])?; + let second_key = ExtendedSecretPart::try_from(&bytes[Self::SECOND_KEY_RANGE])?; + + Ok(ExtendedPrivateKey { key, second_key }) + } +} + +impl<'a, H: Hasher512> TryFrom<&'a str> for ExtendedPrivateKey { + type Error = KeyPairError; + + fn try_from(hex: &'a str) -> Result { + let bytes = Zeroizing::new(hex::decode(hex).map_err(|_| KeyPairError::InvalidSecretKey)?); + Self::try_from(bytes.as_slice()) + } +} + +#[derive(ZeroizeOnDrop)] +struct ExtendedSecretPart { + secret: H256, + extension: H256, + chain_code: H256, + /// An expanded secret key obtained from [`ExtendedSecretPart::secret`] + /// and [`ExtendedSecretPart::extension`]. + /// It's used to generate a public key and sign messages. + expanded_key: ExpandedSecretKey, +} + +/// cbindgen:ignore +impl ExtendedSecretPart { + const LEN: usize = 96; + const SECRET_RANGE: Range = 0..32; + const EXTENSION_RANGE: Range = 32..64; + const CHAIN_CODE_RANGE: Range = 64..96; +} + +impl ToBytesZeroizing for ExtendedSecretPart { + fn to_zeroizing_vec(&self) -> Zeroizing> { + let mut res = Vec::with_capacity(H256::len() * 3); + res.extend_from_slice(self.secret.as_slice()); + res.extend_from_slice(self.extension.as_slice()); + res.extend_from_slice(self.chain_code.as_slice()); + Zeroizing::new(res) + } +} + +impl<'a, H: Hasher512> TryFrom<&'a [u8]> for ExtendedSecretPart { + type Error = KeyPairError; + + fn try_from(bytes: &'a [u8]) -> Result { + if bytes.len() != Self::LEN { + return Err(KeyPairError::InvalidSecretKey); + } + let secret = H256::try_from(&bytes[Self::SECRET_RANGE]) + .map_err(|_| KeyPairError::InvalidSecretKey)?; + let extension = H256::try_from(&bytes[Self::EXTENSION_RANGE]) + .map_err(|_| KeyPairError::InvalidSecretKey)?; + let chain_code = H256::try_from(&bytes[Self::CHAIN_CODE_RANGE]) + .map_err(|_| KeyPairError::InvalidSecretKey)?; + + let expanded_key = ExpandedSecretKey::with_extended_secret(secret, extension); + Ok(ExtendedSecretPart { + secret, + extension, + chain_code, + expanded_key, + }) + } +} diff --git a/rust/tw_keypair/src/ed25519/modifications/cardano/extended_public.rs b/rust/tw_keypair/src/ed25519/modifications/cardano/extended_public.rs new file mode 100644 index 00000000000..1a8eaa65b08 --- /dev/null +++ b/rust/tw_keypair/src/ed25519/modifications/cardano/extended_public.rs @@ -0,0 +1,125 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ed25519::public::PublicKey; +use crate::ed25519::signature::Signature; +use crate::ed25519::Hasher512; +use crate::traits::VerifyingKeyTrait; +use crate::KeyPairError; +use std::ops::Range; +use tw_encoding::hex; +use tw_hash::H256; +use tw_misc::traits::ToBytesVec; + +/// Represents an `ed25519` extended public key that is used in Cardano blockchain. +#[derive(Clone)] +pub struct ExtendedPublicKey { + /// The first half of the public key (64 bytes). + key: ExtendedPublicPart, + /// The second half of the public key (64 bytes). + second_key: ExtendedPublicPart, +} + +/// cbindgen:ignore +impl ExtendedPublicKey { + /// The number of bytes in a serialized public key. + pub const LEN: usize = ExtendedPublicPart::::LEN * 2; + const KEY_RANGE: Range = 0..ExtendedPublicPart::::LEN; + const SECOND_KEY_RANGE: Range = ExtendedPublicPart::::LEN..Self::LEN; + + /// Creates a public key with the given [`ExtendedPublicPart`] first and second keys. + pub(crate) fn new(key: ExtendedPublicPart, second_key: ExtendedPublicPart) -> Self { + ExtendedPublicKey { key, second_key } + } + + /// Returns a public key bytes (32 length) that is used in signing. + pub(crate) fn key_for_signing(&self) -> H256 { + self.key.public.to_bytes() + } +} + +impl VerifyingKeyTrait for ExtendedPublicKey { + type SigningMessage = Vec; + type VerifySignature = Signature; + + fn verify(&self, signature: Self::VerifySignature, message: Self::SigningMessage) -> bool { + self.key.public.verify(signature, message) + } +} + +impl ToBytesVec for ExtendedPublicKey { + fn to_vec(&self) -> Vec { + let mut res = self.key.to_vec(); + res.extend_from_slice(self.second_key.to_vec().as_slice()); + res + } +} + +impl<'a, H: Hasher512> TryFrom<&'a [u8]> for ExtendedPublicKey { + type Error = KeyPairError; + + fn try_from(bytes: &'a [u8]) -> Result { + if bytes.len() != Self::LEN { + return Err(KeyPairError::InvalidPublicKey); + } + + let key = ExtendedPublicPart::try_from(&bytes[Self::KEY_RANGE])?; + let second_key = ExtendedPublicPart::try_from(&bytes[Self::SECOND_KEY_RANGE])?; + + Ok(ExtendedPublicKey { key, second_key }) + } +} + +impl<'a, H: Hasher512> TryFrom<&'a str> for ExtendedPublicKey { + type Error = KeyPairError; + + fn try_from(hex: &'a str) -> Result { + let bytes = hex::decode(hex).map_err(|_| KeyPairError::InvalidPublicKey)?; + Self::try_from(bytes.as_slice()) + } +} + +#[derive(Clone)] +pub(crate) struct ExtendedPublicPart { + public: PublicKey, + chain_code: H256, +} + +/// cbindgen:ignore +impl ExtendedPublicPart { + const LEN: usize = PublicKey::::LEN + H256::len(); + const PUBLIC_RANGE: Range = 0..32; + const CHAIN_CODE_RANGE: Range = 32..64; + + pub(crate) fn new(public: PublicKey, chain_code: H256) -> ExtendedPublicPart { + ExtendedPublicPart { public, chain_code } + } +} + +impl<'a, H: Hasher512> TryFrom<&'a [u8]> for ExtendedPublicPart { + type Error = KeyPairError; + + fn try_from(bytes: &'a [u8]) -> Result { + if bytes.len() != Self::LEN { + return Err(KeyPairError::InvalidPublicKey); + } + + let public = PublicKey::try_from(&bytes[Self::PUBLIC_RANGE])?; + let chain_code = H256::try_from(&bytes[Self::CHAIN_CODE_RANGE]) + .map_err(|_| KeyPairError::InvalidPublicKey)?; + + Ok(ExtendedPublicPart { public, chain_code }) + } +} + +impl ToBytesVec for ExtendedPublicPart { + fn to_vec(&self) -> Vec { + let mut res = Vec::with_capacity(H256::len() * 2); + res.extend_from_slice(self.public.as_slice()); + res.extend_from_slice(self.chain_code.as_slice()); + res + } +} diff --git a/rust/tw_keypair/src/ed25519/modifications/cardano/mod.rs b/rust/tw_keypair/src/ed25519/modifications/cardano/mod.rs new file mode 100644 index 00000000000..d5f0f3d2eae --- /dev/null +++ b/rust/tw_keypair/src/ed25519/modifications/cardano/mod.rs @@ -0,0 +1,15 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use sha2::Sha512; + +mod extended_keypair; +mod extended_private; +mod extended_public; + +pub type ExtendedKeyPair = extended_keypair::ExtendedKeyPair; +pub type ExtendedPrivateKey = extended_private::ExtendedPrivateKey; +pub type ExtendedPublicKey = extended_public::ExtendedPublicKey; diff --git a/rust/tw_keypair/src/ed25519/modifications/mod.rs b/rust/tw_keypair/src/ed25519/modifications/mod.rs new file mode 100644 index 00000000000..7f19f608c5d --- /dev/null +++ b/rust/tw_keypair/src/ed25519/modifications/mod.rs @@ -0,0 +1,8 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod cardano; +pub mod waves; diff --git a/rust/tw_keypair/src/ed25519/modifications/waves/keypair.rs b/rust/tw_keypair/src/ed25519/modifications/waves/keypair.rs new file mode 100644 index 00000000000..890ac668041 --- /dev/null +++ b/rust/tw_keypair/src/ed25519/modifications/waves/keypair.rs @@ -0,0 +1,70 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ed25519::modifications::waves::private::PrivateKey; +use crate::ed25519::modifications::waves::public::PublicKey; +use crate::ed25519::modifications::waves::Signature; +use crate::ed25519::Hasher512; +use crate::traits::{KeyPairTrait, SigningKeyTrait, VerifyingKeyTrait}; +use crate::{KeyPairError, KeyPairResult}; +use tw_encoding::hex; +use zeroize::Zeroizing; + +/// Represents an `ed25519` key pair that is used in Waves blockchain. +pub struct KeyPair { + private: PrivateKey, + public: PublicKey, +} + +impl KeyPairTrait for KeyPair { + type Private = PrivateKey; + type Public = PublicKey; + + fn public(&self) -> &Self::Public { + &self.public + } + + fn private(&self) -> &Self::Private { + &self.private + } +} + +impl SigningKeyTrait for KeyPair { + type SigningMessage = Vec; + type Signature = Signature; + + fn sign(&self, message: Self::SigningMessage) -> KeyPairResult { + self.private.sign(message) + } +} + +impl VerifyingKeyTrait for KeyPair { + type SigningMessage = Vec; + type VerifySignature = Signature; + + fn verify(&self, signature: Self::VerifySignature, message: Self::SigningMessage) -> bool { + self.public.verify(signature, message) + } +} + +impl<'a, H: Hasher512> TryFrom<&'a [u8]> for KeyPair { + type Error = KeyPairError; + + fn try_from(bytes: &'a [u8]) -> Result { + let private = PrivateKey::try_from(bytes)?; + let public = private.public(); + Ok(KeyPair { private, public }) + } +} + +impl<'a, H: Hasher512> TryFrom<&'a str> for KeyPair { + type Error = KeyPairError; + + fn try_from(hex: &'a str) -> Result { + let bytes = Zeroizing::new(hex::decode(hex).map_err(|_| KeyPairError::InvalidPublicKey)?); + Self::try_from(bytes.as_slice()) + } +} diff --git a/rust/tw_keypair/src/ed25519/modifications/waves/mod.rs b/rust/tw_keypair/src/ed25519/modifications/waves/mod.rs new file mode 100644 index 00000000000..45f0320be09 --- /dev/null +++ b/rust/tw_keypair/src/ed25519/modifications/waves/mod.rs @@ -0,0 +1,18 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use sha2::Sha512; + +mod keypair; +mod private; +mod public; +mod signature; + +pub use signature::Signature; + +pub type KeyPair = keypair::KeyPair; +pub type PrivateKey = private::PrivateKey; +pub type PublicKey = public::PublicKey; diff --git a/rust/tw_keypair/src/ed25519/modifications/waves/private.rs b/rust/tw_keypair/src/ed25519/modifications/waves/private.rs new file mode 100644 index 00000000000..569f9dde44c --- /dev/null +++ b/rust/tw_keypair/src/ed25519/modifications/waves/private.rs @@ -0,0 +1,61 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ed25519::modifications::waves::public::PublicKey; +use crate::ed25519::modifications::waves::signature::Signature; +use crate::ed25519::{private::PrivateKey as StandardPrivateKey, Hasher512}; +use crate::traits::SigningKeyTrait; +use crate::{KeyPairError, KeyPairResult}; +use tw_encoding::hex; +use tw_misc::traits::ToBytesZeroizing; +use zeroize::Zeroizing; + +/// Represents an `ed25519` private key that is used in Waves blockchain. +pub struct PrivateKey { + standard_key: StandardPrivateKey, +} + +impl PrivateKey { + /// Returns an associated Waves `ed25519` public key. + pub fn public(&self) -> PublicKey { + PublicKey::with_standard_pubkey(&self.standard_key.public()) + } +} + +impl SigningKeyTrait for PrivateKey { + type SigningMessage = Vec; + type Signature = Signature; + + fn sign(&self, message: Self::SigningMessage) -> KeyPairResult { + let signature = self.standard_key.sign(message)?; + let standard_pubkey = self.standard_key.public(); + Ok(Signature::new_mangling(&standard_pubkey, &signature)) + } +} + +impl<'a, H: Hasher512> TryFrom<&'a [u8]> for PrivateKey { + type Error = KeyPairError; + + fn try_from(bytes: &'a [u8]) -> Result { + let standard_key = StandardPrivateKey::try_from(bytes)?; + Ok(PrivateKey { standard_key }) + } +} + +impl<'a, H: Hasher512> TryFrom<&'a str> for PrivateKey { + type Error = KeyPairError; + + fn try_from(hex: &'a str) -> Result { + let bytes = Zeroizing::new(hex::decode(hex).map_err(|_| KeyPairError::InvalidSecretKey)?); + Self::try_from(bytes.as_slice()) + } +} + +impl ToBytesZeroizing for PrivateKey { + fn to_zeroizing_vec(&self) -> Zeroizing> { + self.standard_key.to_zeroizing_vec() + } +} diff --git a/rust/tw_keypair/src/ed25519/modifications/waves/public.rs b/rust/tw_keypair/src/ed25519/modifications/waves/public.rs new file mode 100644 index 00000000000..47e3e2ce638 --- /dev/null +++ b/rust/tw_keypair/src/ed25519/modifications/waves/public.rs @@ -0,0 +1,93 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ed25519::modifications::waves::signature::Signature; +use crate::ed25519::public::PublicKey as StandardPublicKey; +use crate::ed25519::Hasher512; +use crate::traits::VerifyingKeyTrait; +use crate::KeyPairError; +use curve25519_dalek::montgomery::MontgomeryPoint; +use std::marker::PhantomData; +use tw_encoding::hex; +use tw_hash::H256; +use tw_misc::traits::ToBytesVec; + +/// Represents an `ed25519` public key that is used in Waves blockchain. +#[derive(Clone)] +pub struct PublicKey { + /// A public key that is calculated through converting + /// a standard [`curve25519_dalek::edwards::EdwardsPoint`] to [`MontgomeryPoint`]. + curve25519_pk: H256, + _phantom: PhantomData, +} + +/// cbindgen:ignore +impl PublicKey { + /// The number of bytes in a serialized public key. + pub const LEN: usize = H256::len(); + + /// Creates a public key from the given standard [`StandardPublicKey`]. + pub(crate) fn with_standard_pubkey(standard: &StandardPublicKey) -> PublicKey { + let montgomery_point = standard.edwards_point().to_montgomery(); + PublicKey { + curve25519_pk: H256::from(montgomery_point.0), + _phantom: PhantomData, + } + } + + /// Returns the raw data of the public key (32 bytes). + pub fn to_bytes(&self) -> H256 { + self.curve25519_pk + } +} + +impl VerifyingKeyTrait for PublicKey { + type SigningMessage = Vec; + type VerifySignature = Signature; + + fn verify(&self, signature: Self::VerifySignature, message: Self::SigningMessage) -> bool { + let Ok(standard_signature) = signature.to_standard_signature() else { + return false; + }; + + let montgomery_point = MontgomeryPoint(self.curve25519_pk.take()); + let pubkey_sign = signature.get_pubkey_sign(); + + let Some(ed25519_pk) = montgomery_point.to_edwards(pubkey_sign) else { + return false; + }; + let standard_public = StandardPublicKey::::with_edwards_point(ed25519_pk); + + standard_public.verify(standard_signature, message) + } +} + +impl<'a, H: Hasher512> TryFrom<&'a [u8]> for PublicKey { + type Error = KeyPairError; + + fn try_from(bytes: &'a [u8]) -> Result { + let curve25519_pk = H256::try_from(bytes).map_err(|_| KeyPairError::InvalidPublicKey)?; + Ok(PublicKey { + curve25519_pk, + _phantom: PhantomData, + }) + } +} + +impl<'a, H: Hasher512> TryFrom<&'a str> for PublicKey { + type Error = KeyPairError; + + fn try_from(hex: &'a str) -> Result { + let bytes = hex::decode(hex).map_err(|_| KeyPairError::InvalidPublicKey)?; + Self::try_from(bytes.as_slice()) + } +} + +impl ToBytesVec for PublicKey { + fn to_vec(&self) -> Vec { + self.curve25519_pk.into_vec() + } +} diff --git a/rust/tw_keypair/src/ed25519/modifications/waves/signature.rs b/rust/tw_keypair/src/ed25519/modifications/waves/signature.rs new file mode 100644 index 00000000000..8c1819a6a73 --- /dev/null +++ b/rust/tw_keypair/src/ed25519/modifications/waves/signature.rs @@ -0,0 +1,82 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ed25519::public::PublicKey as StandardPublicKey; +use crate::ed25519::signature::Signature as StandardSignature; +use crate::ed25519::Hasher512; +use crate::{KeyPairError, KeyPairResult}; +use tw_hash::{H256, H512}; +use tw_misc::traits::ToBytesVec; + +/// cbindgen:ignore +/// Equals to 0x80. +const PUBKEY_SIGN_MASK: u8 = 0b1000_0000; + +/// cbindgen:ignore +/// Equals to 127. +const DROP_SIGN_BIT_MASK: u8 = 0b0111_1111; + +pub struct Signature { + bytes: H512, +} + +impl Signature { + /// Creates a signature by mangling a standard [`StandardSignature`]. + /// Ported: https://github.com/trustwallet/wallet-core/blob/3.1.31/src/PrivateKey.cpp#L225-L230 + pub(crate) fn new_mangling( + standard_pubkey: &StandardPublicKey, + standard_sign: &StandardSignature, + ) -> Signature { + let pubkey_bytes: H256 = standard_pubkey.to_bytes(); + let sign_bit = pubkey_bytes[31] & PUBKEY_SIGN_MASK; + + let mut bytes = standard_sign.to_bytes(); + bytes[63] &= DROP_SIGN_BIT_MASK; + bytes[63] |= sign_bit; + + Signature { bytes } + } + + /// Returns the signature data (64 bytes). + pub fn to_bytes(&self) -> H512 { + self.bytes + } + + /// Tries to convert the signature to a standard `ed25519` representation by removing the sign bit. + /// Ported: https://github.com/trustwallet/wallet-core/blob/3.1.31/src/PublicKey.cpp#L159-L162 + pub(crate) fn to_standard_signature(&self) -> KeyPairResult { + let mut bytes = self.bytes; + bytes[63] &= DROP_SIGN_BIT_MASK; + StandardSignature::try_from(bytes.as_slice()) + } + + /// Returns a public key sign bit. + /// Either `1` or `0`. + /// Ported: https://github.com/trustwallet/wallet-core/blob/3.1.31/src/PublicKey.cpp#L157 + pub(crate) fn get_pubkey_sign(&self) -> u8 { + let sign_bit = self.bytes[63] & PUBKEY_SIGN_MASK; + if sign_bit == 0 { + 0 + } else { + 1 + } + } +} + +impl<'a> TryFrom<&'a [u8]> for Signature { + type Error = KeyPairError; + + fn try_from(bytes: &'a [u8]) -> Result { + let bytes = H512::try_from(bytes).map_err(|_| KeyPairError::InvalidSignature)?; + Ok(Signature { bytes }) + } +} + +impl ToBytesVec for Signature { + fn to_vec(&self) -> Vec { + self.bytes.into_vec() + } +} diff --git a/rust/tw_keypair/src/ed25519/private.rs b/rust/tw_keypair/src/ed25519/private.rs new file mode 100644 index 00000000000..9384bc66096 --- /dev/null +++ b/rust/tw_keypair/src/ed25519/private.rs @@ -0,0 +1,88 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ed25519::public::PublicKey; +use crate::ed25519::secret::ExpandedSecretKey; +use crate::ed25519::signature::Signature; +use crate::ed25519::Hasher512; +use crate::traits::SigningKeyTrait; +use crate::{KeyPairError, KeyPairResult}; +use std::fmt; +use tw_encoding::hex; +use tw_hash::H256; +use tw_misc::traits::ToBytesZeroizing; +use zeroize::{ZeroizeOnDrop, Zeroizing}; + +/// Represents an `ed25519` private key. +#[derive(ZeroizeOnDrop)] +pub struct PrivateKey { + secret: H256, + /// An expanded secret key obtained from [`PrivateKey::secret`]. + /// It's used to generate a public key and sign messages. + expanded_key: ExpandedSecretKey, +} + +impl fmt::Debug for PrivateKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PrivateKey") + .field("secret", &self.secret) + .finish() + } +} + +impl PrivateKey { + /// Returns an associated `ed25519` public key. + pub fn public(&self) -> PublicKey { + PublicKey::with_expanded_secret(&self.expanded_key) + } + + /// `ed25519` signing uses a public key associated with the private key. + pub(crate) fn sign_with_public_key( + &self, + public: &PublicKey, + message: &[u8], + ) -> KeyPairResult { + self.expanded_key + .sign_with_pubkey(public.to_bytes(), message) + } +} + +impl SigningKeyTrait for PrivateKey { + type SigningMessage = Vec; + type Signature = Signature; + + fn sign(&self, message: Self::SigningMessage) -> KeyPairResult { + self.sign_with_public_key(&self.public(), &message) + } +} + +impl TryFrom<&[u8]> for PrivateKey { + type Error = KeyPairError; + + fn try_from(data: &[u8]) -> Result { + let secret = H256::try_from(data).map_err(|_| KeyPairError::InvalidSecretKey)?; + let expanded_key = ExpandedSecretKey::::with_secret(secret); + Ok(PrivateKey { + secret, + expanded_key, + }) + } +} + +impl<'a, H: Hasher512> TryFrom<&'a str> for PrivateKey { + type Error = KeyPairError; + + fn try_from(hex: &'a str) -> Result { + let bytes = Zeroizing::new(hex::decode(hex).map_err(|_| KeyPairError::InvalidSecretKey)?); + Self::try_from(bytes.as_slice()) + } +} + +impl ToBytesZeroizing for PrivateKey { + fn to_zeroizing_vec(&self) -> Zeroizing> { + Zeroizing::new(self.secret.to_vec()) + } +} diff --git a/rust/tw_keypair/src/ed25519/public.rs b/rust/tw_keypair/src/ed25519/public.rs new file mode 100644 index 00000000000..883147a92c8 --- /dev/null +++ b/rust/tw_keypair/src/ed25519/public.rs @@ -0,0 +1,169 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ed25519::mangle::mangle_scalar; +use crate::ed25519::secret::ExpandedSecretKey; +use crate::ed25519::signature::Signature; +use crate::ed25519::Hasher512; +use crate::traits::VerifyingKeyTrait; +use crate::KeyPairError; +use curve25519_dalek::constants; +use curve25519_dalek::edwards::{CompressedEdwardsY, EdwardsPoint}; +use curve25519_dalek::scalar::Scalar; +use std::fmt; +use std::marker::PhantomData; +use tw_encoding::hex; +use tw_hash::H256; +use tw_misc::traits::ToBytesVec; +use tw_misc::try_or_false; + +/// Represents an `ed25519` public key. +#[derive(Clone, PartialEq)] +pub struct PublicKey { + compressed: CompressedEdwardsY, + point: EdwardsPoint, + _phantom: PhantomData, +} + +impl fmt::Debug for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PublicKey") + .field("compressed", &self.as_slice()) + .finish() + } +} + +/// cbindgen:ignore +impl PublicKey { + /// The number of bytes in a serialized public key. + pub const LEN: usize = H256::len(); + + /// Creates a public key with the given [`ExpandedSecretKey`]. + pub(crate) fn with_expanded_secret(secret: &ExpandedSecretKey) -> Self { + let bits = secret.key.to_bytes(); + + PublicKey::mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key(bits) + } + + /// Creates a public key with the given [`ExpandedSecretKey`]. + /// + /// This function is useful when the given secret is mangled already. + /// For example, created via [`ExpandedSecretKey::with_extended_secret`]. + pub(crate) fn with_expanded_secret_no_mangle(secret: &ExpandedSecretKey) -> Self { + let bits = secret.key.to_bytes(); + + PublicKey::multiply_by_basepoint_to_produce_public_key(bits) + } + + /// Creates a public key with the given [`EdwardsPoint`]. + pub(crate) fn with_edwards_point(point: EdwardsPoint) -> Self { + let compressed = point.compress(); + PublicKey { + compressed, + point, + _phantom: PhantomData, + } + } + + /// Returns the raw data of the public key (32 bytes). + pub fn to_bytes(&self) -> H256 { + H256::from(self.compressed.to_bytes()) + } + + /// Returns the raw data of the data of the public key. + pub fn as_slice(&self) -> &[u8] { + self.compressed.as_bytes() + } + + /// Returns a reference to the [`EdwardsPoint`]. + pub(crate) fn edwards_point(&self) -> &EdwardsPoint { + &self.point + } + + /// Internal utility function for mangling the bits of a (formerly + /// mathematically well-defined) "scalar" and multiplying it to produce a + /// public key. + /// + /// Source: https://github.com/dalek-cryptography/ed25519-dalek/blob/1.0.1/src/public.rs#L147-L161 + fn mangle_scalar_bits_and_multiply_by_basepoint_to_produce_public_key( + mut bits: [u8; 32], + ) -> PublicKey { + mangle_scalar(&mut bits); + + Self::multiply_by_basepoint_to_produce_public_key(bits) + } + + /// Internal utility function for multiplying the given bits to produce a public key. + /// + /// Source: https://github.com/dalek-cryptography/ed25519-dalek/blob/1.0.1/src/public.rs#L157-L160 + fn multiply_by_basepoint_to_produce_public_key(bits: [u8; 32]) -> PublicKey { + let point = &Scalar::from_bits(bits) * &constants::ED25519_BASEPOINT_TABLE; + PublicKey::with_edwards_point(point) + } +} + +impl VerifyingKeyTrait for PublicKey { + type SigningMessage = Vec; + type VerifySignature = Signature; + + /// Source: https://github.com/dalek-cryptography/ed25519-dalek/blob/1.0.1/src/public.rs#L220-L319 + #[allow(non_snake_case)] + fn verify(&self, signature: Self::VerifySignature, message: Self::SigningMessage) -> bool { + let mut h = H::new(); + let minus_A: EdwardsPoint = -self.point; + + let signature_R = try_or_false!(signature.R.decompress()); + + // Logical OR is fine here as we're not trying to be constant time. + if signature_R.is_small_order() || self.point.is_small_order() { + return false; + } + + h.update(signature.R.as_bytes()); + h.update(self.as_slice()); + h.update(&message); + + let k = Scalar::from_hash(h); + let R = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s); + + R == signature_R + } +} + +impl ToBytesVec for PublicKey { + fn to_vec(&self) -> Vec { + self.as_slice().to_vec() + } +} + +impl<'a, H: Hasher512> TryFrom<&'a [u8]> for PublicKey { + type Error = KeyPairError; + + /// Inspired by: https://github.com/dalek-cryptography/ed25519-dalek/blob/1.0.1/src/public.rs#L92-L145 + fn try_from(pubkey: &'a [u8]) -> Result { + let pubkey = H256::try_from(pubkey).map_err(|_| KeyPairError::InvalidPublicKey)?; + + let compressed = CompressedEdwardsY(pubkey.take()); + let point = compressed + .decompress() + .ok_or(KeyPairError::InvalidPublicKey)?; + + Ok(PublicKey { + compressed, + point, + _phantom: PhantomData, + }) + } +} + +impl<'a, H: Hasher512> TryFrom<&'a str> for PublicKey { + type Error = KeyPairError; + + fn try_from(hex: &'a str) -> Result { + let bytes = hex::decode(hex).map_err(|_| KeyPairError::InvalidPublicKey)?; + Self::try_from(bytes.as_slice()) + } +} diff --git a/rust/tw_keypair/src/ed25519/secret.rs b/rust/tw_keypair/src/ed25519/secret.rs new file mode 100644 index 00000000000..ed39c3e7a34 --- /dev/null +++ b/rust/tw_keypair/src/ed25519/secret.rs @@ -0,0 +1,129 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ed25519::mangle::mangle_scalar; +use crate::ed25519::signature::Signature; +use crate::ed25519::Hasher512; +use crate::KeyPairResult; +use curve25519_dalek::constants; +use curve25519_dalek::scalar::Scalar; +use std::marker::PhantomData; +use std::ops::DerefMut; +use tw_hash::{H256, H512}; +use zeroize::ZeroizeOnDrop; + +/// Represents an "expanded" secret key produced by using a hash function +/// with 512-bits output to digest a `PrivateKey`. +/// +/// Represents [ed25519_extsk](https://github.com/trustwallet/wallet-core/blob/423f0e34725f69c0a9d535e1a32534c99682edea/trezor-crypto/crypto/ed25519-donna/ed25519.c#L23-L32). +#[derive(ZeroizeOnDrop)] +pub(crate) struct ExpandedSecretKey { + /// 32 byte scalar. Represents `extsk[0..32]`. + pub key: Scalar, + /// 32 byte nonce. Represents `extsk[32..64]`. + pub nonce: H256, + _phantom: PhantomData, +} + +impl ExpandedSecretKey { + /// Source: https://github.com/dalek-cryptography/ed25519-dalek/blob/1.0.1/src/secret.rs#L246-L282 + /// Ported: https://github.com/trustwallet/wallet-core/blob/423f0e34725f69c0a9d535e1a32534c99682edea/trezor-crypto/crypto/ed25519-donna/ed25519.c#L23-L32 + pub(crate) fn with_secret(secret: H256) -> Self { + let mut h = H::new(); + let mut hash = H512::default(); + + h.update(secret.as_slice()); + hash.copy_from_slice(h.finalize().as_slice()); + + let (mut lower, upper): (H256, H256) = hash.split(); + mangle_scalar(lower.deref_mut()); + + ExpandedSecretKey { + key: Scalar::from_bits(lower.take()), + nonce: upper, + _phantom: PhantomData, + } + } + + /// Ported: https://github.com/trustwallet/wallet-core/blob/423f0e34725f69c0a9d535e1a32534c99682edea/trezor-crypto/crypto/ed25519-donna/ed25519.c#L93-L94C30 + /// + /// Here we use `Scalar::from_bytes_mod_order` instead of `Scalar::from_bits` + /// because `Scalar::from_bits` modifies the last 32th byte in some cases. + /// Although, `Scalar::from_bytes_mod_order` changes the given `secret` significantly, + /// but the result signature seems to be correct. + /// Unfortunately, there are no public functions to create `Scalar` without mangling the secret. + /// + /// TODO make sure if this is the right way to create an extended secret key (extsk). + pub(crate) fn with_extended_secret(secret: H256, extension: H256) -> Self { + let key = Scalar::from_bytes_mod_order(secret.take()); + ExpandedSecretKey { + key, + nonce: extension, + _phantom: PhantomData, + } + } + + /// Signs a message with this `ExpandedSecretKey`. + /// Source: https://github.com/dalek-cryptography/ed25519-dalek/blob/1.0.1/src/secret.rs#L389-L412 + /// Ported: https://github.com/trustwallet/wallet-core/blob/423f0e34725f69c0a9d535e1a32534c99682edea/trezor-crypto/crypto/ed25519-donna/ed25519.c#L97-L130 + #[allow(non_snake_case)] + pub(crate) fn sign_with_pubkey( + &self, + pubkey: H256, + message: &[u8], + ) -> KeyPairResult { + let mut h = H::new(); + + h.update(self.nonce.as_slice()); + h.update(message); + + let r = Scalar::from_hash(h); + let R = (&r * &constants::ED25519_BASEPOINT_TABLE).compress(); + + h = H::new(); + h.update(R.as_bytes()); + h.update(pubkey); + h.update(message); + + let k = Scalar::from_hash(h); + let s = k * self.key + r; + + Ok(Signature { R, s }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use sha2::Sha512; + use tw_encoding::hex; + + #[test] + fn test_ed25519_expanded_secret_from_extended_key() { + let secret = H256::from("b52a1a9f4ae3bff2d16a06e144bcdd4147a35aa9843cfd0edf458afdf5ba1a3b"); + let nonce = H256::from("b33b86344897745b35bb3ef8ca8fe8a3758bd31a537280a6b8c60e42a1f3a00d"); + + let secret_key: ExpandedSecretKey = + ExpandedSecretKey::with_extended_secret(secret, nonce); + + // In `trezor-crypto` implementation, `a = mod(secret)` has the following: + // https://github.com/trustwallet/wallet-core/blob/423f0e34725f69c0a9d535e1a32534c99682edea/trezor-crypto/crypto/ed25519-donna/ed25519.c#L109-L111 + let expected_mod_a = + H256::from("eeae3808eee7222aee44f9013eaa33100347a31aa512f234eff05d24627fbd2e"); + // On the other hand, [`ExpandedSecretKey::key`] represents the same `a` value. + // But it differs from that - `eeae3888fbb988ea4e941ff8a8ce400347a35aa9843cfd0edf458afdf5ba1a0b`. + // TODO probably, `secret_key.key.to_bytes()` should be the same as `expected_mod_a.take()`. + assert_ne!(secret_key.key.to_bytes(), expected_mod_a.take()); + + let public = H256::from("7950119e049a53a9eaa6ecfbfe354337287056ba0ea054130c1b0c97f1b69697"); + let message = hex::decode("f0").unwrap(); + + // Anyway, the result signature has an expected value. + let sign = secret_key.sign_with_pubkey(public, &message).unwrap(); + let expected = H512::from("ed55bce14a845a275e7a3a7242420ed1eeaba79dc3141bebf42ca0d12169e209a6e56b6981a336f711ae3aaea8d063b72b0e79a8808311d08cb42cabfdd0450d"); + assert_eq!(sign.to_bytes(), expected); + } +} diff --git a/rust/tw_keypair/src/ed25519/signature.rs b/rust/tw_keypair/src/ed25519/signature.rs new file mode 100644 index 00000000000..cb45fcce1fc --- /dev/null +++ b/rust/tw_keypair/src/ed25519/signature.rs @@ -0,0 +1,96 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::{KeyPairError, KeyPairResult}; +use curve25519_dalek::edwards::CompressedEdwardsY; +use curve25519_dalek::scalar::Scalar; +use tw_hash::{concat, H256, H512}; +use tw_misc::traits::ToBytesVec; + +/// Represents an `ed25519` signature. +/// Source: https://github.com/dalek-cryptography/ed25519-dalek/blob/1.0.1/src/signature.rs#L22-L53 +#[allow(non_snake_case)] +#[derive(Debug)] +pub struct Signature { + /// `R` is an `EdwardsPoint`, formed by using an hash function with + /// 512-bits output to produce the digest of: + /// + /// - the nonce half of the `ExpandedSecretKey`, and + /// - the message to be signed. + /// + /// This digest is then interpreted as a `Scalar` and reduced into an + /// element in ℤ/lℤ. The scalar is then multiplied by the distinguished + /// basepoint to produce `R`, and `EdwardsPoint`. + pub(crate) R: CompressedEdwardsY, + + /// `s` is a `Scalar`, formed by using an hash function with 512-bits output + /// to produce the digest of: + /// + /// - the `r` portion of this `Signature`, + /// - the `PublicKey` which should be used to verify this `Signature`, and + /// - the message to be signed. + /// + /// This digest is then interpreted as a `Scalar` and reduced into an + /// element in ℤ/lℤ. + pub(crate) s: Scalar, +} + +impl Signature { + /// Returns the signature data (64 bytes). + pub fn to_bytes(&self) -> H512 { + let left = H256::from(self.R.to_bytes()); + let right = H256::from(self.s.to_bytes()); + concat(left, right) + } +} + +impl ToBytesVec for Signature { + fn to_vec(&self) -> Vec { + self.to_bytes().into_vec() + } +} + +impl<'a> TryFrom<&'a [u8]> for Signature { + type Error = KeyPairError; + + /// Construct a `Signature` from a slice of bytes. + /// Source: https://github.com/dalek-cryptography/ed25519-dalek/blob/1.0.1/src/signature.rs#L115-L190 + fn try_from(sign: &'a [u8]) -> Result { + let bytes = H512::try_from(sign).map_err(|_| KeyPairError::InvalidSignature)?; + + let (lower, upper): (H256, H256) = bytes.split(); + + let s = get_scalar(upper)?; + Ok(Signature { + R: CompressedEdwardsY(lower.take()), + s, + }) + } +} + +/// Source: https://github.com/dalek-cryptography/ed25519-dalek/blob/1.0.1/src/signature.rs#L83-L102 +fn get_scalar(bytes: H256) -> KeyPairResult { + /// Equals to 240 decimal. + const SIGNIFICANT_BITS_MASK: u8 = 0b1111_0000; + + // Since this is only used in signature deserialisation (i.e. upon + // verification), we can do a "succeed fast" trick by checking that the most + // significant 4 bits are unset. If they are unset, we can succeed fast + // because we are guaranteed that the scalar is fully reduced. However, if + // the 4th most significant bit is set, we must do the full reduction check, + // as the order of the basepoint is roughly a 2^(252.5) bit number. + // + // This succeed-fast trick should succeed for roughly half of all scalars. + let last_byte = bytes.last().expect("H256 is exactly 32 length"); + if last_byte & SIGNIFICANT_BITS_MASK == 0 { + return Ok(Scalar::from_bits(bytes.take())); + } + + match Scalar::from_canonical_bytes(bytes.take()) { + Some(x) => Ok(x), + None => Err(KeyPairError::InvalidSignature), + } +} diff --git a/rust/tw_keypair/src/ffi/mod.rs b/rust/tw_keypair/src/ffi/mod.rs index 9a641758528..15cac0a8cf7 100644 --- a/rust/tw_keypair/src/ffi/mod.rs +++ b/rust/tw_keypair/src/ffi/mod.rs @@ -5,3 +5,5 @@ // file LICENSE at the root of the source code distribution tree. pub mod asn; +pub mod privkey; +pub mod pubkey; diff --git a/rust/tw_keypair/src/ffi/privkey.rs b/rust/tw_keypair/src/ffi/privkey.rs new file mode 100644 index 00000000000..30f68811458 --- /dev/null +++ b/rust/tw_keypair/src/ffi/privkey.rs @@ -0,0 +1,133 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#![allow(clippy::missing_safety_doc)] + +use crate::ffi::pubkey::TWPublicKey; +use crate::tw::{Curve, PrivateKey, PublicKeyType}; +use tw_memory::ffi::c_byte_array::CByteArray; +use tw_memory::ffi::c_byte_array_ref::CByteArrayRef; +use tw_memory::ffi::RawPtrTrait; +use tw_misc::{try_or_else, try_or_false}; + +pub struct TWPrivateKey(pub(crate) PrivateKey); + +impl RawPtrTrait for TWPrivateKey {} + +/// Create a private key with the given block of data. +/// +/// \param input *non-null* byte array. +/// \param input_len the length of the `input` array. +/// \note Should be deleted with \tw_private_key_delete. +/// \return Nullable pointer to Private Key. +#[no_mangle] +pub unsafe extern "C" fn tw_private_key_create_with_data( + input: *const u8, + input_len: usize, +) -> *mut TWPrivateKey { + let bytes_ref = CByteArrayRef::new(input, input_len); + let bytes = try_or_else!(bytes_ref.to_vec(), std::ptr::null_mut); + + PrivateKey::new(bytes) + .map(|private| TWPrivateKey(private).into_ptr()) + // Return null if the private key is invalid. + .unwrap_or_else(|_| std::ptr::null_mut()) +} + +/// Delete the given private key. +/// +/// \param key *non-null* pointer to private key. +#[no_mangle] +pub unsafe extern "C" fn tw_private_key_delete(key: *mut TWPrivateKey) { + // Take the ownership back to rust and drop the owner. + let _ = TWPrivateKey::from_ptr(key); +} + +/// Determines if the given private key is valid or not. +/// +/// \param key *non-null* byte array. +/// \param key_len the length of the `key` array. +/// \param curve Eliptic curve of the private key. +/// \return true if the private key is valid, false otherwise. +#[no_mangle] +pub unsafe extern "C" fn tw_private_key_is_valid( + key: *const u8, + key_len: usize, + curve: u32, +) -> bool { + let curve = try_or_false!(Curve::from_raw(curve)); + let priv_key_slice = try_or_false!(CByteArrayRef::new(key, key_len).as_slice()); + PrivateKey::is_valid(priv_key_slice, curve) +} + +/// Signs a digest using ECDSA and given curve. +/// +/// \param key *non-null* pointer to a Private key +/// \param message *non-null* byte array. +/// \param message_len the length of the `input` array. +/// \param curve Eliptic curve. +/// \return Signature as a C-compatible result with a C-compatible byte array. +#[no_mangle] +pub unsafe extern "C" fn tw_private_key_sign( + key: *mut TWPrivateKey, + message: *const u8, + message_len: usize, + curve: u32, +) -> CByteArray { + let curve = try_or_else!(Curve::from_raw(curve), CByteArray::default); + let private = try_or_else!(TWPrivateKey::from_ptr_as_ref(key), CByteArray::default); + let message_to_sign = try_or_else!( + CByteArrayRef::new(message, message_len).as_slice(), + CByteArray::default + ); + + // Return an empty signature if an error occurs. + let sig = private.0.sign(message_to_sign, curve).unwrap_or_default(); + CByteArray::from(sig) +} + +/// Returns the public key associated with the given pubkeyType and privateKey +/// +/// \param key *non-null* pointer to the private key. +/// \param pubkey_type type of the public key to return. +/// \return *non-null* pointer to the corresponding public key. +#[no_mangle] +pub unsafe extern "C" fn tw_private_key_get_public_key_by_type( + key: *mut TWPrivateKey, + pubkey_type: u32, +) -> *mut TWPublicKey { + let ty = try_or_else!(PublicKeyType::from_raw(pubkey_type), std::ptr::null_mut); + let private = try_or_else!(TWPrivateKey::from_ptr_as_ref(key), std::ptr::null_mut); + private + .0 + .get_public_key_by_type(ty) + .map(|public| TWPublicKey(public).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} + +// #[no_mangle] +// pub unsafe extern "C" fn tw_private_key_get_shared_key( +// key: *mut TWPrivateKey, +// hash: *const u8, +// hash_len: usize, +// curve: u32, +// ) -> CByteArray { +// let curve = match Curve::from_raw(curve) { +// Some(curve) => curve, +// None => return CByteArray::empty(), +// }; +// let private = try_or_else!(TWPrivateKey::from_ptr_as_ref(key), CByteArray::empty); +// +// let hash = CByteArrayRef::new(hash, hash_len); +// let hash_to_sign = match hash.as_slice() { +// Some(hash) => hash, +// None => return CByteArray::empty(), +// }; +// +// // Return an empty signature if an error occurs. +// let sig = private.sign(hash_to_sign, curve).unwrap_or_default(); +// CByteArray::from(sig) +// } diff --git a/rust/tw_keypair/src/ffi/pubkey.rs b/rust/tw_keypair/src/ffi/pubkey.rs new file mode 100644 index 00000000000..e1c86476b07 --- /dev/null +++ b/rust/tw_keypair/src/ffi/pubkey.rs @@ -0,0 +1,104 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#![allow(clippy::missing_safety_doc)] + +use crate::tw::{PublicKey, PublicKeyType}; +use tw_memory::ffi::c_byte_array::CByteArray; +use tw_memory::ffi::c_byte_array_ref::CByteArrayRef; +use tw_memory::ffi::RawPtrTrait; +use tw_misc::{try_or_else, try_or_false}; + +pub struct TWPublicKey(pub(crate) PublicKey); + +impl AsRef for TWPublicKey { + fn as_ref(&self) -> &PublicKey { + &self.0 + } +} + +impl RawPtrTrait for TWPublicKey {} + +/// Create a public key with the given block of data and specified public key type. +/// +/// \param input *non-null* byte array. +/// \param input_len the length of the `input` array. +/// \param ty type of the public key. +/// \note Should be deleted with \tw_public_key_delete. +/// \return Nullable pointer to the public key. +#[no_mangle] +pub unsafe extern "C" fn tw_public_key_create_with_data( + input: *const u8, + input_len: usize, + ty: u32, +) -> *mut TWPublicKey { + let bytes_ref = CByteArrayRef::new(input, input_len); + let bytes = try_or_else!(bytes_ref.to_vec(), std::ptr::null_mut); + let ty = try_or_else!(PublicKeyType::from_raw(ty), std::ptr::null_mut); + PublicKey::new(bytes, ty) + .map(|public| TWPublicKey(public).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} + +/// Delete the given public key. +/// +/// \param key *non-null* pointer to public key. +#[no_mangle] +pub unsafe extern "C" fn tw_public_key_delete(key: *mut TWPublicKey) { + // Take the ownership back to rust and drop the owner. + let _ = TWPublicKey::from_ptr(key); +} + +/// Verify the validity of a signature and a message using the given public key. +/// +/// \param key *non-null* pointer to a Public key. +/// \param sig *non-null* pointer to a block of data corresponding to the signature. +/// \param sig_len the length of the `sig` array. +/// \param msg *non-null* pointer to a block of data corresponding to the message. +/// \param msg_len the length of the `msg` array. +/// \return true if the signature and the message belongs to the given public key, otherwise false. +#[no_mangle] +pub unsafe extern "C" fn tw_public_key_verify( + key: *mut TWPublicKey, + sig: *const u8, + sig_len: usize, + msg: *const u8, + msg_len: usize, +) -> bool { + let public = try_or_false!(TWPublicKey::from_ptr_as_ref(key)); + let sig = try_or_false!(CByteArrayRef::new(sig, sig_len).as_slice()); + let msg = try_or_false!(CByteArrayRef::new(msg, msg_len).as_slice()); + public.0.verify(sig, msg) +} + +/// Returns the raw data of a given public-key. +/// +/// \param key *non-null* pointer to a public key. +/// \return C-compatible result with a C-compatible byte array. +#[no_mangle] +pub unsafe extern "C" fn tw_public_key_data(key: *mut TWPublicKey) -> CByteArray { + let public = try_or_else!(TWPublicKey::from_ptr_as_ref(key), CByteArray::default); + CByteArray::from(public.0.to_bytes()) +} + +// #[no_mangle] +// pub unsafe extern "C" fn tw_public_key_is_valid( +// pubkey: *const u8, +// pubkey_len: usize, +// pubkey_type: u32, +// ) -> bool { +// let ty = match TWPublicKeyType::from_raw(pubkey_type) { +// Some(ty) => ty, +// None => return false, +// }; +// +// let pubkey_slice = match CByteArrayRef::new(pubkey, pubkey_len).as_slice() { +// Some(pubkey) => pubkey, +// None => return false, +// }; +// +// TWPublicKey::is_valid(pubkey_slice, ty) +// } diff --git a/rust/tw_keypair/src/lib.rs b/rust/tw_keypair/src/lib.rs index bf7edad42ab..945222877e5 100644 --- a/rust/tw_keypair/src/lib.rs +++ b/rust/tw_keypair/src/lib.rs @@ -4,12 +4,64 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +//! `tw_keypair` crate defines the keypairs, private and public keys that are used to sign messages, +//! verify signatures and more. +//! +//! # Usage - Generic TW solution +//! +//! If you plan to work with different curves in the same app by using the same private key, +//! consider using [`tw::PrivateKey`], [`tw::PublicKey`], [`tw::KeyPair`] (TODO). +//! +//! ```rust,ignore +//! use tw_keypair::{tw::PrivateKey, Curve}; +//! +//! let private = PrivateKey::try_from(YOUR_SECRET_BYTES).unwrap(); +//! +//! // Sign an ETH transaction hash with the `private` key. +//! let eth_signature = private.sign(ETH_TX_HASH, Curve::Secp256k1).unwrap(); +//! assert_eq(eth_signature.len(), 65); +//! +//! // Sign a SUI transaction hash with the same `private` key, but different `curve`. +//! let sui_signature = private.sign(SUI_TX_HASH, Curve::Ed25519).unwrap(); +//! ``` +//! +//! # Usage - Specific curve +//! +//! If you plan to work with only one curve, consider using a specific curve implementation. +//! For example, if you work with ETH, therefore the`secp256k1` curve: +//! +//! ```rust,ignore +//! use tw_keypair::secp256k1::KeyPair; +//! +//! let keypair = KeyPair::try_from(YOUR_SECRET_BYTES).unwrap(); +//! +//! // Sign an ETH transaction hash. +//! // [`tw_keypair::secp256k1::KeyPair::sign`] returns a [`tw_keypair::secp256k1::Signature`]. +//! let eth_signature = private.sign(ETH_TX_HASH, Curve::Secp256k1).unwrap(); +//! +//! assert_eq(eth_signature.r, H256::from(EXPECTED_R_HEX)); +//! assert_eq(eth_signature.s, H256::from(EXPECTED_S_HEX)); +//! assert_eq(eth_signature.v, H256::from(EXPECTED_V)); +//! ``` + pub mod ecdsa; +pub mod ed25519; pub mod ffi; +pub mod starkex; +pub mod traits; +pub mod tw; + +#[cfg(feature = "test-utils")] +pub mod test_utils; pub type KeyPairResult = Result; -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub enum KeyPairError { + InvalidSecretKey, + InvalidPublicKey, InvalidSignature, + InvalidSignMessage, + SignatureVerifyError, + SigningError, } diff --git a/rust/tw_keypair/src/starkex/keypair.rs b/rust/tw_keypair/src/starkex/keypair.rs new file mode 100644 index 00000000000..b827bdb80db --- /dev/null +++ b/rust/tw_keypair/src/starkex/keypair.rs @@ -0,0 +1,69 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::starkex::private::PrivateKey; +use crate::starkex::public::PublicKey; +use crate::starkex::signature::Signature; +use crate::traits::{KeyPairTrait, SigningKeyTrait, VerifyingKeyTrait}; +use crate::{KeyPairError, KeyPairResult}; +use tw_encoding::hex; +use zeroize::Zeroizing; + +/// Represents a pair of private and public keys that are used in `starknet` context. +pub struct KeyPair { + private: PrivateKey, + public: PublicKey, +} + +impl KeyPairTrait for KeyPair { + type Private = PrivateKey; + type Public = PublicKey; + + fn public(&self) -> &Self::Public { + &self.public + } + + fn private(&self) -> &Self::Private { + &self.private + } +} + +impl SigningKeyTrait for KeyPair { + type SigningMessage = Vec; + type Signature = Signature; + + fn sign(&self, message: Self::SigningMessage) -> KeyPairResult { + self.private.sign(message) + } +} + +impl VerifyingKeyTrait for KeyPair { + type SigningMessage = Vec; + type VerifySignature = Signature; + + fn verify(&self, signature: Self::VerifySignature, message: Self::SigningMessage) -> bool { + self.public.verify(signature, message) + } +} + +impl<'a> TryFrom<&'a [u8]> for KeyPair { + type Error = KeyPairError; + + fn try_from(bytes: &'a [u8]) -> Result { + let private = PrivateKey::try_from(bytes)?; + let public = private.public(); + Ok(KeyPair { private, public }) + } +} + +impl<'a> TryFrom<&'a str> for KeyPair { + type Error = KeyPairError; + + fn try_from(hex: &'a str) -> Result { + let bytes = Zeroizing::new(hex::decode(hex).map_err(|_| KeyPairError::InvalidSecretKey)?); + Self::try_from(bytes.as_slice()) + } +} diff --git a/rust/tw_keypair/src/starkex/mod.rs b/rust/tw_keypair/src/starkex/mod.rs new file mode 100644 index 00000000000..35db9f4e98c --- /dev/null +++ b/rust/tw_keypair/src/starkex/mod.rs @@ -0,0 +1,124 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +mod keypair; +mod private; +mod public; +mod signature; + +pub use keypair::KeyPair; +pub use private::PrivateKey; +pub use public::PublicKey; +pub use signature::Signature; +use starknet_ff::FieldElement; + +fn field_element_from_bytes_be(bytes: &[u8]) -> Result { + const FIELD_ELEMENT_LEN: usize = 32; + + if bytes.len() > FIELD_ELEMENT_LEN { + return Err(()); + } + let mut buffer = [0u8; FIELD_ELEMENT_LEN]; + buffer[(FIELD_ELEMENT_LEN - bytes.len())..].copy_from_slice(bytes); + FieldElement::from_bytes_be(&buffer).map_err(|_| ()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::traits::{KeyPairTrait, SigningKeyTrait, VerifyingKeyTrait}; + use tw_encoding::hex; + use tw_hash::{H256, H512}; + use tw_misc::traits::{ToBytesVec, ToBytesZeroizing}; + + #[test] + fn test_key_pair_sign_verify() { + let keypair = + KeyPair::try_from("0139fe4d6f02e666e86a6f58e65060f115cd3c185bd9e98bd829636931458f79") + .unwrap(); + + let hash_to_sign = + hex::decode("06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76") + .unwrap(); + let actual = keypair.sign(hash_to_sign.clone()).unwrap(); + + let expected = H512::from("061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9a"); + assert_eq!(actual.to_vec(), expected.into_vec()); + + assert!(keypair.verify(actual, hash_to_sign)); + } + + #[test] + fn test_key_pair_get_private_public() { + let privkey_bytes = + hex::decode("058ab7989d625b1a690400dcbe6e070627adedceff7bd196e58d4791026a8afe") + .unwrap(); + let pubkey_bytes = + hex::decode("02a4c7332c55d6c1c510d24272d1db82878f2302f05b53bcc38695ed5f78fffd") + .unwrap(); + + let keypair = KeyPair::try_from(privkey_bytes.as_slice()).unwrap(); + assert_eq!( + keypair.private().to_zeroizing_vec().as_slice(), + privkey_bytes + ); + assert_eq!(keypair.public().to_vec(), pubkey_bytes); + } + + #[test] + fn test_field_element_from_bytes_be_invalid() { + let invalid_element = + hex::decode("00058ab7989d625b1a690400dcbe6e070627adedceff7bd196e58d4791026a8afe") + .unwrap(); + field_element_from_bytes_be(&invalid_element).unwrap_err(); + } + + #[test] + fn test_private_key_to_from_bytes() { + let bytes = hex::decode("058ab7989d625b1a690400dcbe6e070627adedceff7bd196e58d4791026a8afe") + .unwrap(); + let private = PrivateKey::try_from(bytes.as_slice()).unwrap(); + assert_eq!(private.to_zeroizing_vec().as_slice(), bytes); + } + + #[test] + fn test_public_key_to_from_bytes() { + let bytes = hex::decode("02a4c7332c55d6c1c510d24272d1db82878f2302f05b53bcc38695ed5f78fffd") + .unwrap(); + let public = PublicKey::try_from(bytes.as_slice()).unwrap(); + assert_eq!(public.to_vec(), bytes); + } + + #[test] + fn test_signature_to_from_bytes() { + let bytes = hex::decode("061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9a").unwrap(); + let sign = Signature::try_from(bytes.as_slice()).unwrap(); + assert_eq!(sign.to_vec(), bytes); + + assert_eq!( + sign.r(), + H256::from("061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f") + ); + assert_eq!( + sign.s(), + H256::from("04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9a") + ); + } + + // https://github.com/xJonathanLEI/starknet-rs/issues/365 + #[test] + fn test_verify_panic() { + let public = + PublicKey::try_from("03ee9bffffffffff26ffffffff60ffffffffffffffffffffffffffff004accff") + .unwrap(); + let hash = hex::decode("06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76") + .unwrap(); + let signature_bytes = hex::decode("06ffffffffffffffffffffffffffffffffffffffffffff06ffff5dffff9bffdf00ffffff9b9b9b9b9b9b9b9bbb9bff9b9bbb9bff9b9b9b9b9b9b9b9b9b9b9b33").unwrap(); + let signature = Signature::try_from(signature_bytes.as_slice()).unwrap(); + + assert!(!public.verify(signature, hash)); + } +} diff --git a/rust/tw_keypair/src/starkex/private.rs b/rust/tw_keypair/src/starkex/private.rs new file mode 100644 index 00000000000..973d2c06699 --- /dev/null +++ b/rust/tw_keypair/src/starkex/private.rs @@ -0,0 +1,124 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::starkex::field_element_from_bytes_be; +use crate::starkex::public::PublicKey; +use crate::starkex::signature::Signature; +use crate::traits::SigningKeyTrait; +use crate::{KeyPairError, KeyPairResult}; +use starknet_crypto::{ + get_public_key, rfc6979_generate_k, sign, SignError, Signature as EcdsaSignature, +}; +use starknet_ff::FieldElement; +use tw_encoding::hex; +use tw_hash::H256; +use tw_misc::traits::ToBytesZeroizing; +use zeroize::Zeroizing; + +/// The maximum number of attempts to sign a message. +/// As the number is coming from `rfc6979_generate_k` so the probability is lower. +const SIGN_RETRIES: usize = 5; + +/// Represents a private key that is used in `starknet` context. +pub struct PrivateKey { + secret: FieldElement, +} + +impl PrivateKey { + /// Returns an associated `starknet` public key. + pub fn public(&self) -> PublicKey { + let public_scalar = get_public_key(&self.secret); + PublicKey::from_scalar(public_scalar) + } +} + +impl SigningKeyTrait for PrivateKey { + type SigningMessage = Vec; + type Signature = Signature; + + fn sign(&self, message: Self::SigningMessage) -> KeyPairResult { + let hash_to_sign = + field_element_from_bytes_be(&message).map_err(|_| KeyPairError::InvalidSignMessage)?; + let signature = + ecdsa_sign(&self.secret, &hash_to_sign).map_err(|_| KeyPairError::SigningError)?; + Ok(Signature::new(signature)) + } +} + +impl<'a> TryFrom<&'a [u8]> for PrivateKey { + type Error = KeyPairError; + + fn try_from(bytes: &'a [u8]) -> Result { + let bytes = H256::try_from(bytes).map_err(|_| KeyPairError::InvalidSecretKey)?; + let secret = FieldElement::from_bytes_be(&bytes.take()) + .map_err(|_| KeyPairError::InvalidSecretKey)?; + Ok(PrivateKey { secret }) + } +} + +impl<'a> TryFrom<&'a str> for PrivateKey { + type Error = KeyPairError; + + fn try_from(hex: &'a str) -> Result { + let bytes = Zeroizing::new(hex::decode(hex).map_err(|_| KeyPairError::InvalidSecretKey)?); + Self::try_from(bytes.as_slice()) + } +} + +impl ToBytesZeroizing for PrivateKey { + fn to_zeroizing_vec(&self) -> Zeroizing> { + let secret = Zeroizing::new(self.secret.to_bytes_be()); + Zeroizing::new(secret.to_vec()) + } +} + +/// `starknet-core` depends on an out-dated `starknet-crypto` crate. +/// We need to reimplement the same but using the latest `starknet-crypto` version. +/// https://github.com/xJonathanLEI/starknet-rs/blob/0c78b365c2a7a7d4138553cba42fa69d695aa73d/starknet-core/src/crypto.rs#L34-L59 +pub fn ecdsa_sign( + private_key: &FieldElement, + message_hash: &FieldElement, +) -> Result { + // Seed-retry logic ported from `cairo-lang` + let mut seed = None; + for _ in 0..SIGN_RETRIES { + let k = rfc6979_generate_k(message_hash, private_key, seed.as_ref()); + + match sign(private_key, message_hash, &k) { + Ok(sig) => { + return Ok(EcdsaSignature { r: sig.r, s: sig.s }); + }, + Err(SignError::InvalidMessageHash) => return Err(SignError::InvalidMessageHash), + Err(SignError::InvalidK) => { + // Bump seed and retry + seed = match seed { + Some(prev_seed) => Some(prev_seed + FieldElement::ONE), + None => Some(FieldElement::ONE), + }; + }, + }; + } + Err(SignError::InvalidMessageHash) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_starknet_sign_invalid_k() { + let private = PrivateKey { + secret: field_element_from_bytes_be(&[0; 32]).unwrap(), + }; + let hash = vec![0; 32]; + + // Return value does not implement Debug, so we cannot unwrap here. + match private.sign(hash) { + Ok(_) => panic!("Retry limit expected"), + Err(err) => assert_eq!(err, KeyPairError::SigningError), + } + } +} diff --git a/rust/tw_keypair/src/starkex/public.rs b/rust/tw_keypair/src/starkex/public.rs new file mode 100644 index 00000000000..16dad099952 --- /dev/null +++ b/rust/tw_keypair/src/starkex/public.rs @@ -0,0 +1,66 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::starkex::field_element_from_bytes_be; +use crate::starkex::signature::Signature; +use crate::traits::VerifyingKeyTrait; +use crate::KeyPairError; +use starknet_crypto::verify as ecdsa_verify; +use starknet_ff::FieldElement; +use tw_encoding::hex; +use tw_hash::H256; +use tw_misc::traits::ToBytesVec; +use tw_misc::try_or_false; + +#[derive(Clone)] +pub struct PublicKey { + public: FieldElement, +} + +impl PublicKey { + /// Creates a public key from the given [`FieldElement`]. + pub(crate) fn from_scalar(public: FieldElement) -> PublicKey { + PublicKey { public } + } +} + +impl VerifyingKeyTrait for PublicKey { + type SigningMessage = Vec; + type VerifySignature = Signature; + + fn verify(&self, signature: Self::VerifySignature, message: Self::SigningMessage) -> bool { + let hash = try_or_false!(field_element_from_bytes_be(&message)); + let ecdsa_signature = signature.inner(); + ecdsa_verify(&self.public, &hash, &ecdsa_signature.r, &ecdsa_signature.s) + .unwrap_or_default() + } +} + +impl<'a> TryFrom<&'a [u8]> for PublicKey { + type Error = KeyPairError; + + fn try_from(bytes: &'a [u8]) -> Result { + let bytes = H256::try_from(bytes).map_err(|_| KeyPairError::InvalidPublicKey)?; + let public_scalar = FieldElement::from_bytes_be(&bytes.take()) + .map_err(|_| KeyPairError::InvalidPublicKey)?; + Ok(PublicKey::from_scalar(public_scalar)) + } +} + +impl<'a> TryFrom<&'a str> for PublicKey { + type Error = KeyPairError; + + fn try_from(hex: &'a str) -> Result { + let bytes = hex::decode(hex).map_err(|_| KeyPairError::InvalidPublicKey)?; + Self::try_from(bytes.as_slice()) + } +} + +impl ToBytesVec for PublicKey { + fn to_vec(&self) -> Vec { + self.public.to_bytes_be().to_vec() + } +} diff --git a/rust/tw_keypair/src/starkex/signature.rs b/rust/tw_keypair/src/starkex/signature.rs new file mode 100644 index 00000000000..9ca0de029b6 --- /dev/null +++ b/rust/tw_keypair/src/starkex/signature.rs @@ -0,0 +1,83 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::KeyPairError; +use starknet_ff::FieldElement; +use std::ops::Range; +use tw_hash::H256; +use tw_misc::traits::ToBytesVec; + +/// cbindgen:ignore +const R_RANGE: Range = 0..32; +/// cbindgen:ignore +const S_RANGE: Range = 32..64; + +/// Represents a `starknet` signature. +pub struct Signature { + pub(crate) signature: starknet_crypto::Signature, +} + +/// cbindgen:ignore +impl Signature { + /// The number of bytes for a serialized signature representation. + pub const LEN: usize = 64; + + /// Returns the number of bytes for a serialized signature representation. + pub const fn len() -> usize { + Self::LEN + } + + /// Creates a `starknet` signature from the given [`starknet_crypto::Signature`]. + pub(crate) fn new(signature: starknet_crypto::Signature) -> Signature { + Signature { signature } + } + + /// Returns a reference to the inner [`starknet_crypto::Signature`]. + pub(crate) fn inner(&self) -> &starknet_crypto::Signature { + &self.signature + } + + /// Returns an r-coordinate as 32 byte array. + pub fn r(&self) -> H256 { + H256::from(self.signature.r.to_bytes_be()) + } + + /// Returns an s-value as 32 byte array. + pub fn s(&self) -> H256 { + H256::from(self.signature.s.to_bytes_be()) + } +} + +impl ToBytesVec for Signature { + fn to_vec(&self) -> Vec { + let mut to_return = Vec::with_capacity(Signature::len()); + to_return.extend_from_slice(self.r().as_slice()); + to_return.extend_from_slice(self.s().as_slice()); + to_return + } +} + +impl<'a> TryFrom<&'a [u8]> for Signature { + type Error = KeyPairError; + + fn try_from(bytes: &'a [u8]) -> Result { + if bytes.len() != Signature::len() { + return Err(KeyPairError::InvalidSignature); + } + + let r_bytes = H256::try_from(&bytes[R_RANGE]).expect("Expected a valid r range"); + let s_bytes = H256::try_from(&bytes[S_RANGE]).expect("Expected a valid s range"); + + let r = FieldElement::from_bytes_be(&r_bytes.take()) + .map_err(|_| KeyPairError::InvalidSignature)?; + let s = FieldElement::from_bytes_be(&s_bytes.take()) + .map_err(|_| KeyPairError::InvalidSignature)?; + + Ok(Signature { + signature: starknet_crypto::Signature { r, s }, + }) + } +} diff --git a/rust/tw_keypair/src/test_utils/mod.rs b/rust/tw_keypair/src/test_utils/mod.rs new file mode 100644 index 00000000000..2e2d49008ed --- /dev/null +++ b/rust/tw_keypair/src/test_utils/mod.rs @@ -0,0 +1,8 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod tw_private_key_helper; +pub mod tw_public_key_helper; diff --git a/rust/tw_keypair/src/test_utils/tw_private_key_helper.rs b/rust/tw_keypair/src/test_utils/tw_private_key_helper.rs new file mode 100644 index 00000000000..c8cc3795578 --- /dev/null +++ b/rust/tw_keypair/src/test_utils/tw_private_key_helper.rs @@ -0,0 +1,45 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ffi::privkey::{tw_private_key_create_with_data, tw_private_key_delete, TWPrivateKey}; +use tw_encoding::hex; +use tw_memory::ffi::c_byte_array::CByteArray; +use tw_memory::Data; + +pub struct TWPrivateKeyHelper { + ptr: *mut TWPrivateKey, +} + +impl TWPrivateKeyHelper { + pub fn with_bytes>(bytes: T) -> TWPrivateKeyHelper { + let priv_key_raw = CByteArray::from(bytes.into()); + let ptr = + unsafe { tw_private_key_create_with_data(priv_key_raw.data(), priv_key_raw.size()) }; + TWPrivateKeyHelper { ptr } + } + + pub fn with_hex(hex: &str) -> TWPrivateKeyHelper { + let priv_key_data = hex::decode(hex).unwrap(); + TWPrivateKeyHelper::with_bytes(priv_key_data) + } + + pub fn ptr(&self) -> *mut TWPrivateKey { + self.ptr + } + + pub fn is_null(&self) -> bool { + self.ptr.is_null() + } +} + +impl Drop for TWPrivateKeyHelper { + fn drop(&mut self) { + if self.ptr.is_null() { + return; + } + unsafe { tw_private_key_delete(self.ptr) }; + } +} diff --git a/rust/tw_keypair/src/test_utils/tw_public_key_helper.rs b/rust/tw_keypair/src/test_utils/tw_public_key_helper.rs new file mode 100644 index 00000000000..daecadc61aa --- /dev/null +++ b/rust/tw_keypair/src/test_utils/tw_public_key_helper.rs @@ -0,0 +1,51 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ffi::pubkey::{tw_public_key_create_with_data, tw_public_key_delete, TWPublicKey}; +use crate::tw::PublicKeyType; +use tw_encoding::hex; +use tw_memory::ffi::c_byte_array::CByteArray; +use tw_memory::Data; + +pub struct TWPublicKeyHelper { + ptr: *mut TWPublicKey, +} + +impl TWPublicKeyHelper { + pub fn with_bytes>(bytes: T, ty: PublicKeyType) -> TWPublicKeyHelper { + let public_key_raw = CByteArray::from(bytes.into()); + let ptr = unsafe { + tw_public_key_create_with_data(public_key_raw.data(), public_key_raw.size(), ty as u32) + }; + TWPublicKeyHelper { ptr } + } + + pub fn with_hex(hex: &str, ty: PublicKeyType) -> TWPublicKeyHelper { + let priv_key_data = hex::decode(hex).unwrap(); + TWPublicKeyHelper::with_bytes(priv_key_data, ty) + } + + pub fn wrap(ptr: *mut TWPublicKey) -> TWPublicKeyHelper { + TWPublicKeyHelper { ptr } + } + + pub fn ptr(&self) -> *mut TWPublicKey { + self.ptr + } + + pub fn is_null(&self) -> bool { + self.ptr.is_null() + } +} + +impl Drop for TWPublicKeyHelper { + fn drop(&mut self) { + if self.ptr.is_null() { + return; + } + unsafe { tw_public_key_delete(self.ptr) }; + } +} diff --git a/rust/tw_keypair/src/traits.rs b/rust/tw_keypair/src/traits.rs new file mode 100644 index 00000000000..fa9216daa91 --- /dev/null +++ b/rust/tw_keypair/src/traits.rs @@ -0,0 +1,39 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::KeyPairResult; +use tw_misc::traits::{ToBytesVec, ToBytesZeroizing}; + +pub trait KeyPairTrait: FromSlice + SigningKeyTrait + VerifyingKeyTrait { + type Private: FromSlice + ToBytesZeroizing; + type Public: FromSlice + ToBytesVec; + + /// Returns the reference to the public key. + fn public(&self) -> &Self::Public; + + /// Returns the reference to the private key. + fn private(&self) -> &Self::Private; +} + +pub trait SigningKeyTrait { + type SigningMessage: FromSlice; + type Signature: ToBytesVec; + + /// Signs the given `hash` using the private key. + fn sign(&self, message: Self::SigningMessage) -> KeyPairResult; +} + +pub trait VerifyingKeyTrait { + type SigningMessage: FromSlice; + type VerifySignature: FromSlice; + + /// Verifies if the given `hash` was signed using the private key. + fn verify(&self, signature: Self::VerifySignature, message: Self::SigningMessage) -> bool; +} + +pub trait FromSlice: for<'a> TryFrom<&'a [u8]> {} + +impl FromSlice for T where for<'a> T: TryFrom<&'a [u8]> {} diff --git a/rust/tw_keypair/src/tw/mod.rs b/rust/tw_keypair/src/tw/mod.rs new file mode 100644 index 00000000000..114affc4ce6 --- /dev/null +++ b/rust/tw_keypair/src/tw/mod.rs @@ -0,0 +1,131 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde::Deserialize; + +mod private; +mod public; + +pub use private::PrivateKey; +pub use public::PublicKey; + +pub type Signature = Vec; + +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum Curve { + Secp256k1 = 0, + Ed25519 = 1, + Ed25519Blake2bNano = 2, + /// Waves blockchain specific `curve25519`. + Curve25519Waves = 3, + Nist256p1 = 4, + /// Cardano blockchain specific `ed25519` extended key. + Ed25519ExtendedCardano = 5, + Starkex = 6, +} + +impl Curve { + /// Returns `None` if the given curve is not supported in Rust yet. + pub fn from_raw(curve: u32) -> Option { + match curve { + 0 => Some(Curve::Secp256k1), + 1 => Some(Curve::Ed25519), + 2 => Some(Curve::Ed25519Blake2bNano), + 3 => Some(Curve::Curve25519Waves), + 4 => Some(Curve::Nist256p1), + 5 => Some(Curve::Ed25519ExtendedCardano), + 6 => Some(Curve::Starkex), + _ => None, + } + } +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, PartialEq)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum PublicKeyType { + #[serde(rename = "secp256k1")] + Secp256k1 = 0, + #[serde(rename = "secp256k1Extended")] + Secp256k1Extended = 1, + #[serde(rename = "nist256p1")] + Nist256p1 = 2, + #[serde(rename = "nist256p1Extended")] + Nist256p1Extended = 3, + #[serde(rename = "ed25519")] + Ed25519 = 4, + #[serde(rename = "ed25519Blake2b")] + Ed25519Blake2b = 5, + /// Waves blockchain specific public key. + #[serde(rename = "curve25519")] + Curve25519Waves = 6, + /// Cardano blockchain specific extended public key. + #[serde(rename = "ed25519Cardano")] + Ed25519ExtendedCardano = 7, + #[serde(rename = "starkex")] + Starkex = 8, +} + +impl PublicKeyType { + /// Returns `None` if the given pubkey type is not supported in Rust yet. + pub fn from_raw(ty: u32) -> Option { + match ty { + 0 => Some(PublicKeyType::Secp256k1), + 1 => Some(PublicKeyType::Secp256k1Extended), + 2 => Some(PublicKeyType::Nist256p1), + 3 => Some(PublicKeyType::Nist256p1Extended), + 4 => Some(PublicKeyType::Ed25519), + 5 => Some(PublicKeyType::Ed25519Blake2b), + 6 => Some(PublicKeyType::Curve25519Waves), + 7 => Some(PublicKeyType::Ed25519ExtendedCardano), + 8 => Some(PublicKeyType::Starkex), + _ => None, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_curve_from_raw() { + let tests = [ + (0, Some(Curve::Secp256k1)), + (1, Some(Curve::Ed25519)), + (2, Some(Curve::Ed25519Blake2bNano)), + (3, Some(Curve::Curve25519Waves)), + (4, Some(Curve::Nist256p1)), + (5, Some(Curve::Ed25519ExtendedCardano)), + (6, Some(Curve::Starkex)), + (7, None), + ]; + for (raw, expected) in tests { + assert_eq!(Curve::from_raw(raw), expected); + } + } + + #[test] + fn test_public_key_type_from_raw() { + let tests = [ + (0, Some(PublicKeyType::Secp256k1)), + (1, Some(PublicKeyType::Secp256k1Extended)), + (2, Some(PublicKeyType::Nist256p1)), + (3, Some(PublicKeyType::Nist256p1Extended)), + (4, Some(PublicKeyType::Ed25519)), + (5, Some(PublicKeyType::Ed25519Blake2b)), + (6, Some(PublicKeyType::Curve25519Waves)), + (7, Some(PublicKeyType::Ed25519ExtendedCardano)), + (8, Some(PublicKeyType::Starkex)), + (9, None), + ]; + for (raw, expected) in tests { + assert_eq!(PublicKeyType::from_raw(raw), expected); + } + } +} diff --git a/rust/tw_keypair/src/tw/private.rs b/rust/tw_keypair/src/tw/private.rs new file mode 100644 index 00000000000..c0e5fb5ae48 --- /dev/null +++ b/rust/tw_keypair/src/tw/private.rs @@ -0,0 +1,195 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ecdsa::{nist256p1, secp256k1}; +use crate::traits::SigningKeyTrait; +use crate::tw::{Curve, PublicKey, PublicKeyType}; +use crate::{ed25519, starkex, KeyPairError, KeyPairResult}; +use std::ops::Range; +use tw_hash::H256; +use tw_misc::traits::ToBytesVec; +use zeroize::ZeroizeOnDrop; + +/// Represents a private key that can be used to sign messages with different elliptic curves. +/// +/// TODO add `secp256k1: Once` for each curve. +#[derive(ZeroizeOnDrop)] +pub struct PrivateKey { + bytes: Vec, +} + +/// cbindgen:ignore +impl PrivateKey { + /// The number of bytes in a private key. + const SIZE: usize = 32; + const CARDANO_SIZE: usize = ed25519::cardano::ExtendedPrivateKey::LEN; + + const KEY_RANGE: Range = 0..Self::SIZE; + const EXTENDED_CARDANO_RANGE: Range = 0..Self::CARDANO_SIZE; + + /// Validates the given `bytes` secret and creates a private key. + pub fn new(bytes: Vec) -> KeyPairResult { + if !Self::is_valid_general(&bytes) { + return Err(KeyPairError::InvalidSecretKey); + } + Ok(PrivateKey { bytes }) + } + + /// Returns the 32 byte array - the essential private key data. + pub fn key(&self) -> H256 { + assert!( + self.bytes.len() >= Self::SIZE, + "'PrivateKey::bytes' has an unexpected length" + ); + H256::try_from(&self.bytes[Self::KEY_RANGE]) + .expect("H256 and KEY_RANGE must be 32 byte length") + } + + /// Returns the 192 byte array - the essential cardano extended private key data. + pub fn extended_cardano_key(&self) -> KeyPairResult<&[u8]> { + if self.bytes.len() != Self::CARDANO_SIZE { + return Err(KeyPairError::InvalidSecretKey); + } + Ok(&self.bytes[Self::EXTENDED_CARDANO_RANGE]) + } + + /// Checks if the given `bytes` secret is valid in general (without a concrete curve). + pub fn is_valid_general(bytes: &[u8]) -> bool { + if bytes.len() != Self::SIZE && bytes.len() != Self::CARDANO_SIZE { + return false; + } + // Check for zero address. + !bytes.iter().all(|byte| *byte == 0) + } + + /// Checks if the given `bytes` secret is valid. + pub fn is_valid(bytes: &[u8], curve: Curve) -> bool { + if !Self::is_valid_general(bytes) { + return false; + } + match curve { + Curve::Secp256k1 => secp256k1::PrivateKey::try_from(&bytes[Self::KEY_RANGE]).is_ok(), + Curve::Ed25519 => { + ed25519::sha512::PrivateKey::try_from(&bytes[Self::KEY_RANGE]).is_ok() + }, + Curve::Ed25519Blake2bNano => { + ed25519::blake2b::PrivateKey::try_from(&bytes[Self::KEY_RANGE]).is_ok() + }, + Curve::Curve25519Waves => { + ed25519::waves::PrivateKey::try_from(&bytes[Self::KEY_RANGE]).is_ok() + }, + Curve::Nist256p1 => nist256p1::PrivateKey::try_from(&bytes[Self::KEY_RANGE]).is_ok(), + Curve::Ed25519ExtendedCardano => { + ed25519::cardano::ExtendedPrivateKey::try_from(&bytes[Self::EXTENDED_CARDANO_RANGE]) + .is_ok() + }, + Curve::Starkex => starkex::PrivateKey::try_from(&bytes[Self::KEY_RANGE]).is_ok(), + } + } + + /// Signs a `message` with using the given elliptic curve. + pub fn sign(&self, message: &[u8], curve: Curve) -> KeyPairResult> { + fn sign_impl(signing_key: Key, message: &[u8]) -> KeyPairResult> + where + Key: SigningKeyTrait, + { + let hash_to_sign = ::SigningMessage::try_from(message) + .map_err(|_| KeyPairError::InvalidSignMessage)?; + signing_key.sign(hash_to_sign).map(|sig| sig.to_vec()) + } + + match curve { + Curve::Secp256k1 => sign_impl(self.to_secp256k1_privkey()?, message), + Curve::Ed25519 => sign_impl(self.to_ed25519()?, message), + Curve::Ed25519Blake2bNano => sign_impl(self.to_ed25519_blake2b()?, message), + Curve::Curve25519Waves => sign_impl(self.to_curve25519_waves()?, message), + Curve::Nist256p1 => sign_impl(self.to_nist256p1_privkey()?, message), + Curve::Ed25519ExtendedCardano => { + sign_impl(self.to_ed25519_extended_cardano()?, message) + }, + Curve::Starkex => sign_impl(self.to_starkex_privkey()?, message), + } + } + + /// Returns the public key associated with the `self` private key and `ty` public key type. + pub fn get_public_key_by_type(&self, ty: PublicKeyType) -> KeyPairResult { + match ty { + PublicKeyType::Secp256k1 => { + let privkey = self.to_secp256k1_privkey()?; + Ok(PublicKey::Secp256k1(privkey.public())) + }, + PublicKeyType::Secp256k1Extended => { + let privkey = self.to_secp256k1_privkey()?; + Ok(PublicKey::Secp256k1Extended(privkey.public())) + }, + PublicKeyType::Nist256p1 => { + let privkey = self.to_nist256p1_privkey()?; + Ok(PublicKey::Nist256p1(privkey.public())) + }, + PublicKeyType::Nist256p1Extended => { + let privkey = self.to_nist256p1_privkey()?; + Ok(PublicKey::Nist256p1Extended(privkey.public())) + }, + PublicKeyType::Ed25519 => { + let privkey = self.to_ed25519()?; + Ok(PublicKey::Ed25519(privkey.public())) + }, + PublicKeyType::Ed25519Blake2b => { + let privkey = self.to_ed25519_blake2b()?; + Ok(PublicKey::Ed25519Blake2b(privkey.public())) + }, + PublicKeyType::Curve25519Waves => { + let privkey = self.to_curve25519_waves()?; + Ok(PublicKey::Curve25519Waves(privkey.public())) + }, + PublicKeyType::Ed25519ExtendedCardano => { + let privkey = self.to_ed25519_extended_cardano()?; + Ok(PublicKey::Ed25519ExtendedCardano(Box::new( + privkey.public(), + ))) + }, + PublicKeyType::Starkex => { + let privkey = self.to_starkex_privkey()?; + Ok(PublicKey::Starkex(privkey.public())) + }, + } + } + + /// Tries to convert [`PrivateKey::key`] to [`secp256k1::PrivateKey`]. + fn to_secp256k1_privkey(&self) -> KeyPairResult { + secp256k1::PrivateKey::try_from(self.key().as_slice()) + } + + /// Tries to convert [`PrivateKey::key`] to [`nist256p1::PrivateKey`]. + fn to_nist256p1_privkey(&self) -> KeyPairResult { + nist256p1::PrivateKey::try_from(self.key().as_slice()) + } + + /// Tries to convert [`PrivateKey::key`] to [`ed25519::sha512::PrivateKey`]. + fn to_ed25519(&self) -> KeyPairResult { + ed25519::sha512::PrivateKey::try_from(self.key().as_slice()) + } + + /// Tries to convert [`PrivateKey::key`] to [`ed25519::blake2b::PrivateKey`]. + fn to_ed25519_blake2b(&self) -> KeyPairResult { + ed25519::blake2b::PrivateKey::try_from(self.key().as_slice()) + } + + /// Tries to convert [`PrivateKey::key`] to [`ed25519::waves::PrivateKey`]. + fn to_curve25519_waves(&self) -> KeyPairResult { + ed25519::waves::PrivateKey::try_from(self.key().as_slice()) + } + + /// Tries to convert [`PrivateKey::extended_cardano_key`] to [`ed25519::cardano::ExtendedPrivateKey`]. + fn to_ed25519_extended_cardano(&self) -> KeyPairResult { + ed25519::cardano::ExtendedPrivateKey::try_from(self.extended_cardano_key()?) + } + + /// Tries to convert [`PrivateKey::key`] to [`starkex::PrivateKey`]. + fn to_starkex_privkey(&self) -> KeyPairResult { + starkex::PrivateKey::try_from(self.key().as_slice()) + } +} diff --git a/rust/tw_keypair/src/tw/public.rs b/rust/tw_keypair/src/tw/public.rs new file mode 100644 index 00000000000..e8a31f0fc20 --- /dev/null +++ b/rust/tw_keypair/src/tw/public.rs @@ -0,0 +1,138 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ecdsa::{nist256p1, secp256k1}; +use crate::traits::VerifyingKeyTrait; +use crate::tw::PublicKeyType; +use crate::{ed25519, starkex, KeyPairError, KeyPairResult}; +use tw_misc::traits::ToBytesVec; +use tw_misc::try_or_false; + +/// Represents a public key that can be used to verify signatures and messages. +#[derive(Clone)] +pub enum PublicKey { + Secp256k1(secp256k1::PublicKey), + Secp256k1Extended(secp256k1::PublicKey), + Nist256p1(nist256p1::PublicKey), + Nist256p1Extended(nist256p1::PublicKey), + Ed25519(ed25519::sha512::PublicKey), + Ed25519Blake2b(ed25519::blake2b::PublicKey), + Curve25519Waves(ed25519::waves::PublicKey), + Ed25519ExtendedCardano(Box), + Starkex(starkex::PublicKey), +} + +impl PublicKey { + /// Validates the given `bytes` using the `ty` public key type and creates a public key from it. + pub fn new(bytes: Vec, ty: PublicKeyType) -> KeyPairResult { + match ty { + PublicKeyType::Secp256k1 if secp256k1::PublicKey::COMPRESSED == bytes.len() => { + let pubkey = secp256k1::PublicKey::try_from(bytes.as_slice())?; + Ok(PublicKey::Secp256k1(pubkey)) + }, + PublicKeyType::Secp256k1Extended + if secp256k1::PublicKey::UNCOMPRESSED == bytes.len() => + { + let pubkey = secp256k1::PublicKey::try_from(bytes.as_slice())?; + Ok(PublicKey::Secp256k1Extended(pubkey)) + }, + PublicKeyType::Nist256p1 if nist256p1::PublicKey::COMPRESSED == bytes.len() => { + let pubkey = nist256p1::PublicKey::try_from(bytes.as_slice())?; + Ok(PublicKey::Nist256p1(pubkey)) + }, + PublicKeyType::Nist256p1Extended + if nist256p1::PublicKey::UNCOMPRESSED == bytes.len() => + { + let pubkey = nist256p1::PublicKey::try_from(bytes.as_slice())?; + Ok(PublicKey::Nist256p1Extended(pubkey)) + }, + PublicKeyType::Ed25519 if ed25519::sha512::PublicKey::LEN == bytes.len() => { + let pubkey = ed25519::sha512::PublicKey::try_from(bytes.as_slice())?; + Ok(PublicKey::Ed25519(pubkey)) + }, + PublicKeyType::Ed25519Blake2b if ed25519::blake2b::PublicKey::LEN == bytes.len() => { + let pubkey = ed25519::blake2b::PublicKey::try_from(bytes.as_slice())?; + Ok(PublicKey::Ed25519Blake2b(pubkey)) + }, + PublicKeyType::Curve25519Waves if ed25519::waves::PublicKey::LEN == bytes.len() => { + let pubkey = ed25519::waves::PublicKey::try_from(bytes.as_slice())?; + Ok(PublicKey::Curve25519Waves(pubkey)) + }, + PublicKeyType::Ed25519ExtendedCardano + if ed25519::cardano::ExtendedPublicKey::LEN == bytes.len() => + { + let pubkey = ed25519::cardano::ExtendedPublicKey::try_from(bytes.as_slice())?; + Ok(PublicKey::Ed25519ExtendedCardano(Box::new(pubkey))) + }, + PublicKeyType::Starkex => { + let pubkey = starkex::PublicKey::try_from(bytes.as_slice())?; + Ok(PublicKey::Starkex(pubkey)) + }, + _ => Err(KeyPairError::InvalidPublicKey), + } + } + + /// Checks if the given `bytes` is valid using `ty` public key type. + pub fn is_valid(bytes: Vec, ty: PublicKeyType) -> bool { + PublicKey::new(bytes, ty).is_ok() + } + + /// Verifies if the given `message` was signed using a private key associated with the public key. + pub fn verify(&self, sig: &[u8], message: &[u8]) -> bool { + fn verify_impl(verifying_key: &Key, sig: &[u8], message: &[u8]) -> bool + where + Key: VerifyingKeyTrait, + { + let verify_sig = + try_or_false!(::VerifySignature::try_from(sig)); + let message = try_or_false!(::SigningMessage::try_from( + message + )); + verifying_key.verify(verify_sig, message) + } + + match self { + PublicKey::Secp256k1(secp) | PublicKey::Secp256k1Extended(secp) => { + verify_impl(secp, sig, message) + }, + PublicKey::Nist256p1(nist) | PublicKey::Nist256p1Extended(nist) => { + verify_impl(nist, sig, message) + }, + PublicKey::Ed25519(ed) => verify_impl(ed, sig, message), + PublicKey::Ed25519Blake2b(blake) => verify_impl(blake, sig, message), + PublicKey::Curve25519Waves(waves) => verify_impl(waves, sig, message), + PublicKey::Ed25519ExtendedCardano(cardano) => { + verify_impl(cardano.as_ref(), sig, message) + }, + PublicKey::Starkex(stark) => verify_impl(stark, sig, message), + } + } + + /// Returns the raw data of the public key. + pub fn to_bytes(&self) -> Vec { + match self { + PublicKey::Secp256k1(secp) => secp.compressed().into_vec(), + PublicKey::Secp256k1Extended(secp) => secp.uncompressed().into_vec(), + PublicKey::Nist256p1(nist) => nist.compressed().into_vec(), + PublicKey::Nist256p1Extended(nist) => nist.uncompressed().into_vec(), + PublicKey::Ed25519(ed) => ed.to_vec(), + PublicKey::Ed25519Blake2b(blake) => blake.to_vec(), + PublicKey::Curve25519Waves(waves) => waves.to_vec(), + PublicKey::Ed25519ExtendedCardano(cardano) => cardano.to_vec(), + PublicKey::Starkex(stark) => stark.to_vec(), + } + } + + /// Returns a `secp256k1` public key if the key type is matched. + pub fn to_secp256k1(&self) -> Option<&secp256k1::PublicKey> { + match self { + PublicKey::Secp256k1(secp256k1) | PublicKey::Secp256k1Extended(secp256k1) => { + Some(secp256k1) + }, + _ => None, + } + } +} diff --git a/rust/tw_keypair/tests/ed25519_blake2b_sign.json b/rust/tw_keypair/tests/ed25519_blake2b_sign.json new file mode 100644 index 00000000000..f8bed662a02 --- /dev/null +++ b/rust/tw_keypair/tests/ed25519_blake2b_sign.json @@ -0,0 +1,1002 @@ +[ + { + "msg": "", + "secret": "fda53a4f1f7f8361e79499b9c4614afb0810d609c9a379f6140e238623ad8d40", + "signature": "aba55cd80c67a7cbdb2a02872513bb6c17a3c1cf17c9ea6711a30dff872cc71a63ae40aea20a6823505187aec5f7a7c4c353da11bb7f0efd363453b907cd0d0e" + }, + { + "msg": "dd", + "secret": "750e4878e05923801ab8cce8212539aceb09c96b0ce3b46c96e5fd7a53f9d95d", + "signature": "4de9e23f62f538408beceb48dc43c27e30cdd7bb65e2cfa1e876722db6017e525df94292f6222c71ee22e42818686a80634db61099f3aee4336d90f854ad930d" + }, + { + "msg": "39ff", + "secret": "a816462a487a2c7276b36babb42cf795a6d1902471175daf8295c8141dd468f4", + "signature": "5de642c554a644880f1dfdc909f0749213a1f35f7cbad83f5c224126f91a567888e92446612dd632345d062186132c4ec2eae6da1330234446e4fc9ff4897c07" + }, + { + "msg": "6dfc7b", + "secret": "f7073cc5544d056e1a34fbdefad229bac342cc96905d139476f70b2beafc5608", + "signature": "df409c4686d1da327a2f1ada96e672e4ba9c0430e0f229d7e48ba2d3a15118bca8e008b31a85212332d48c8dbba46750b2ef96c2736242d5a66b966597253705" + }, + { + "msg": "62e7acf4", + "secret": "a9b2ea7114d55795488da696be343b0feb9585b979753a835b94880b55032db1", + "signature": "43232c680dc241380827b48367f3a4c4847fe462cbe92c2369ab1ffdb3492c151822e5c64781568a8a70c73ffe0623b8ca88c0f694c0eb84df75cb503edd490d" + }, + { + "msg": "f5254c8d72", + "secret": "6721136b51d0dda9f68b97c1687aecfa4b78b80b80d9d4dbfa88ad0269d7d3ca", + "signature": "c0f1023054428b31a597826705fdb0ca8214e0718777e4db0b1442f7728adbaa5e4185b521d006f89837d1ec84888620c4d320ddcb554201f6a8869f24743a09" + }, + { + "msg": "beb08b836c68", + "secret": "e45654103bd263dd7c25632b7bf8fad59dbe888d57a35ba0aaf68ea062abb144", + "signature": "3f665af8767484baf32e636d749a01fc496c1e1adc390197fc16030b834bf444e97dbef34aed083b58e81f76e1829a04d12c32e6e9974253bfe1691cd5d53605" + }, + { + "msg": "e6d60266f8c3a0", + "secret": "edd10e2484b20ad3dbf7c4372736cf27ad78d7100d457efffe912690420dd701", + "signature": "5c8509c07ed1e02c63d8ac0b42b8bd81b086a136b8d246af41103b8588997da42b3c1595d397fd0877556d16d148b4d71f01ff7c1ffbfc4480fcf6fe43205b04" + }, + { + "msg": "b82bc420aaba3c3f", + "secret": "4c295addb346646a2d14fa54f87e181c594b3c9d310cdc47d2d3610f99f6977f", + "signature": "9a50b76f6497ff29b483ea517fe46813fa31ba27efbcead4a5ec7da8a5aec513025397fc5d93264341a2bf0a3417f53ca14f4a5a56f841849cbaff6492974406" + }, + { + "msg": "3c5c3a79b56ceb6b70", + "secret": "d14de53a47d7b125d897c23a2f99c483df7c80e45c951e7fa1007810835c383b", + "signature": "e3fcdfd7720dfa833c7941eb8ecc86f2a5a193784f0f8916e42a8b135701cd1950f52eaeb98455919a5e184e4efcae26113f05843bfe35ccfd104ec64149d001" + }, + { + "msg": "aa688c4202d26d1ee9c8", + "secret": "906ac18066baeae8230bdc788e3e365cb2d2c67603d1a96c10a59b05baf5eeea", + "signature": "0542b5e24c611f2bfe8b837b4ee3c15891e18755888be5afb767f401d3a231269e2a292b7ccfcec5db5a7b25092556711f06fd289d8ae7c15e0741a1a1effc02" + }, + { + "msg": "0c8bdec7564ed702770cd5", + "secret": "824e4e5a43d797727b76d079ef748f6969479cfee28471fec3d104f58888231e", + "signature": "b1e75b49235239013fd4044096bdbdce81c404cb6ac733feaa190e6a5bc5c6a5664676f236ef2d3c2a4e13dc120f507d917e2a38840ace2dcf107c8899d0df0b" + }, + { + "msg": "099dc2d90d9c2d181859abbc", + "secret": "0665d6a654b04aa97be29fcb9468db8d738781cfa8ec6888f24216b713ec8338", + "signature": "215a701bc9cb2dd6322c02c008ca8279d404ffb70e50e07c2073eb5a8dc4e44d10cecebddee1614ed362d0cb02aa2e14f3da805e0d88153a7db148d7bce74a0b" + }, + { + "msg": "de6e254e3bed4893b082b08932", + "secret": "c340a9557695fc9ad374c15e1cf46c5c1acaa548547f785469094fa41de28707", + "signature": "93ce56942b61557f464f4b64254d271d75cc51034065f8ab443286d41944906a5034d9d3422461c43d7b9f673aaf22f239b6f432dd35b787c4734402e96cf10d" + }, + { + "msg": "baf997758807f1724bd1c9618950", + "secret": "fd3a9fc4c82523062dfb529167c131360463037ab4ef57922abc2edd316dd0b9", + "signature": "c8129ba6e45aa0f6930135261501e500d36c5033074a1f732618159c26ef69c00fcc49967101156b7dd32ee2b5b4cc365134ffe64d1b9ae81887c025ecce0800" + }, + { + "msg": "55eef7db5c2a7d80f23512cc17bd53", + "secret": "91221539b17f92623bb38b5b8911d7e43b584191573d1243e5f3b88336c5b75e", + "signature": "360227427879255c1a4a5d02377085f3fd731b51c72ea51108302e7a98d557abb184587752390da90bec4b0e891f478679f5bd7a0db7adff4334b5c042eb1406" + }, + { + "msg": "bdd1bb669e2fccca51ad2036b9600746", + "secret": "612054d8d7d81c537cd87d1c0484e3214e93ab7fff9e6e3af563c7b4b1cd775d", + "signature": "ead78e7a0fb3fe757ba291d3614757c7a58a8fc97e2ffbf540aaa5e74e473a4c7187ac365db87918a0c92dd9533faf17ec69ecbe39ab4acec4e0f0056d2f020c" + }, + { + "msg": "d38763b90a01ae15946330bc25f6279b84", + "secret": "82143dd0a1a7cf8914aad078a7f7eb25f395ed39a71bf8fdf2425ca77dc783a8", + "signature": "25719a12f17f5e9b92d94e64f337fd4b3687889a1001dc8426d88d72917bb05745d951ad4d2c3da6bc6b6548a3b86fab3c1c1619075ba6aec2064a566b82630d" + }, + { + "msg": "b5a9c5fee75c06a8ca275671884650b49c06", + "secret": "f4e8391d09b8379190621adf4906fb2dbb15e2c70b869204843fc1de280d7beb", + "signature": "e32efd63936ae162755911d594b9ff85e870615d451bb6e212ef27df7e56c2d4e1da511564824a23f6df1f69036cd287438f05ef47858620cc442f2222053101" + }, + { + "msg": "475a79be6f5ecbb16c375a1011f7b23059a92b", + "secret": "eb9376f74ec402d86dee0eb419313b2427f434cacd89782ff1acffed08b89cff", + "signature": "18082aebbdd5bb8e819ff207cb32a2347bc2666f2acd1627ac7926471f46355b5bbf45ac8b33709a922c75029b5d2a3a97651b807bbbb748e10cf3bafe65f709" + }, + { + "msg": "89b8565d6971c5eb6d1c618b02a81371eed8a035", + "secret": "9c5fc4e636a867fd6bebcbb5c91c571b05c1557880548da4770589a7249ddeab", + "signature": "a27915f2b0f323b0e780611dc44b5311a173e5cb905a8e5391d811232fd2ce5c7ce7e56db022b04357686868838d482850b57f0220b42b02ab03290b93521407" + }, + { + "msg": "b166dc5d545da6a0c40c5efa4a6fbb9b662db684dc", + "secret": "488e113e78a940f04df5fc411132f6b41f1f99fb71505b62b939ea2f3a5bf787", + "signature": "ea1638e435aa44cbf1fd4e4b2bf86813af25be8f1b3db56b15c7c2ee9e5b3d5d0c545d2e317b2525b7ff39cb36fb06f7608650cd52df8b508ab24a0ff954d407" + }, + { + "msg": "6fba6099d8e2245b30e38efd39b464a51fc6c0d845e3", + "secret": "a939dd49d3ce0d14401bc09490bed3500f29a28ad23076057e9814b9faea62f0", + "signature": "182ba7680551ab0bd8d2a881abdef961cb9a43b1d7ef6a3f8cc8ae4a7f0bb3022164d3a2f64c7a1814d5ee71838ec1b4599988a86dff2ea730e71fabcbfbcd06" + }, + { + "msg": "aa4e0ff3fca92d3661815658a51ef9b85622ad6015a504", + "secret": "87f33c433bff439de255372af056b1360ef67b289ed3e054e2ffa827d7c5bce7", + "signature": "da4cca55fcf5d1f81c6cf260c7f161c9af5db66102f49b10a0479ea52c50efd03a57badab1750ef68436eb37324064c9d2b51f4100bf3b42aa65a7d8a91be106" + }, + { + "msg": "ad6eedde28ff07393affe4cbbe72c4bbad275f3e892e37c5", + "secret": "3554acc8330edd0a7bbd17408b1dce9ac09a60b5f56c9c6f4e94cc43a0618da2", + "signature": "3a87a357bac8da376d8dc980a7f632431a6d8620246195face393c86c21ca911cf38885c69a3cded025217c192bd55c25b195ed72ced1a2334ad6d563afcc404" + }, + { + "msg": "60bebfdeb03a0ca369e97eef25bb0f4cf3a2661ff8c9b7713c", + "secret": "2800fce83436536ebe1b622472a4a207beafbffa5b729d18f3e154f23f51125c", + "signature": "602bfca670c7ba82972539d545cf8140264cc9861601bdc392ff28080ec7a408f28a31b4ed002ea6490379178a7901b01ab4e1e9a81d1aee70d2a7dec0eb750f" + }, + { + "msg": "76ad988c91c3e21dc03cd5fa37f8e7e213e2cc25558a9c3d6e48", + "secret": "24e07ee4bb95ee5133ef128a622e1dc6adb523f9e1a1a5aa1f5fcc1c869a0621", + "signature": "f80c2e504941a4de74d8a3ae7e1269c1a846b11c898cccae9a82bcc4aabed5423749578e659aaa0a8088c78af28647b49a728214411ae6947d49a7bce7bfd808" + }, + { + "msg": "857b29b170ede8817b8d37fe56ee3a4b4103c22db1f41264d6044b", + "secret": "0c6f87d32fbbcad37a6693b9d8ed011bbc2d7aa60f6d835f807374a2a3521a55", + "signature": "d83e96de3ca39f8e8756ad7acd3d2a491661519605b68af0123ec5092c0a570951c856a5b7d4a833108d34b6623a8d91302f623f39b6a371448287b473ec8d0d" + }, + { + "msg": "c36dd9e48f817428f37537c3d88a57eaab9a97e6f4a6d26901bd88a7", + "secret": "8d88e77647d10a0e014eda49e4a5a5fac88a5814da1bdb3a64b86cae9c47c7c7", + "signature": "1230bf7dc2f76bdef826e7f153605bcc5e2270e39d073febc5803f3c65a836f9e59eea3187a024ca34c04d9eb30622658ab1028bd372582bf2e179c02d491109" + }, + { + "msg": "f798e56441150cd8085bbba8137a8293d4a3d25c9f3de900289505486e", + "secret": "9b7439917bf1ec65b7fc4a9618922c6b441bb14575dd27a9854aaaa31fcdb417", + "signature": "148ada5c9a12d96136696161105336e33eb95fd1169aa81cf0e77984b374819ecf4d36ec4680a6b4e14118acd70d4647ef0c284014630012155c954844787c01" + }, + { + "msg": "80daf165cd68f723f31f33fc40776a907f416ca878ccbd4b76b8c0b9e1fb", + "secret": "2069a87ecca9c9d29a2095477949c127f8a1a70b4e1b4340640d16401af2ee8b", + "signature": "ffe92dc4edee96224f82e3e0ab6d79cfbde43a1a5a7eb103c98399716f950a5ecfb475ecd738c3471e0c7d787be737a711084d36da82fa14b2ef636c247c2300" + }, + { + "msg": "ad94dd59e2d94feb1bf1f9cf7dedbc284681c7dfb6f8af7a2f06ce81b07c17", + "secret": "759c801eef52bbe4e680aa49aea0dfa1f5ec322847bf88eae72df6d0aa2aa063", + "signature": "8dda6f16d6f2144bdfa1a02ca9422b365ac468af254e6feb6e44a5643af6d03b2e1e94240aab7d7f77abe484e7ac0147098f129c2edb5d9cc41a8349b0225e00" + }, + { + "msg": "79dea9acf45141f648ab86f2971ad4394d4f7a54df16ad4ab56ee17417a58784", + "secret": "45bec12dee32857ae9a70009e316882648e231cb747511fed2a728316da0491d", + "signature": "1ab6bb5ca12a059e462b340aab9e500c92e13be4437e547e99da723fa81fcc16813cf5d84dc10bfe7131688b2a9739679969b0861f38a05be666ab38d08e270f" + }, + { + "msg": "8e78bc8c558c3aa9ebefd6678098ccd49327ab9209d8bdd8d011d67a0a10d4686c", + "secret": "5f497c44c327bf656fad471d8d2bb76040909f816fc9e70be75083fd7988ccce", + "signature": "60606f5addebfd182df43654724d08e8b99a5778ae9a333e8218ee87bb27d00aab28d918556879019f803d6c17fb9a8ff67aee917707ffebadcbfcf8440fc602" + }, + { + "msg": "e28a6b53c78614ba7ae05865c2c695ec280d299afdb7dcd602788bf1147c85c562e8", + "secret": "4d96fc8f18dba850c6c5eb55de4ad9d2a5fd161e159b33261cb5238fb2fa4bc8", + "signature": "57baf0124b0964e5625faf548eeb9c2e3bc1995261e0c27c7967d39eb743f19003060009a7782c683ff1e3c4f46777ba399f83cc1582eaccf3ad63ac3504560e" + }, + { + "msg": "11021bd7007dc3b3299b837fa43d9661e5c513b6e64cc59713441598cc08b9adc97e77", + "secret": "2e23ed6e663ceb5aea669f7bfec40774a333ef0945fa93fb40b6c36e55eef11a", + "signature": "f8277bd2957848f12c5b227316219c365f25afe862a9572de1943b26a8fa0562a9a2ab50bae3fd87adbaa8a8a510b718e6629cb4a34713b706797bf4563e6308" + }, + { + "msg": "c6451d0d36c6650233f8e27b1d51909751349ca73b0bb2670983fdfcd01e571812e71093", + "secret": "7919086d46b9078ccff124d8e19a285c03e72a4d02650ef7843b8d3733f409ce", + "signature": "a18444e1e255c2530d283adeb151ded083b6a06bfed384f3cc759e1d93963cd371e8a14485928df1aebce7fc2f3ace62c5b9a073efa60e8d773845a142840c04" + }, + { + "msg": "3e84b7645980295f9fbfba3c3d975240e2f5a6aae8d5386365901bac4da0c4d0cca0aaa394", + "secret": "8f1b5c1aa4ed5d06a4f42883af2e96c98e394e15f02a556a3b87b649c027e56f", + "signature": "487d3141ae45a2260c3604bf1c9cf918d64e097c0f61407dcfa1dbe56bc32b6a21c0ffb3c2217e4fa17acdbe85fffa174c27e7d93bbc42cf5a43550749029a01" + }, + { + "msg": "a0877608dc1f76df885b16e6f394977b5df6c35d89776839dc8ca1dcba1b570e807cca1b7ba2", + "secret": "f1643ad34f54e089afc91ff06d6c19caa38ef7cd72eadbee3e3cd2054548fd5f", + "signature": "407b96a05da2aef298cfb045d1ed7545465f5cb4ca42a80c0e0bf372a81de26de2058fd01304a6730caa1a59d1121c65ccf1a047a0e3197bc27bd52c18e80a01" + }, + { + "msg": "ac8b1331fed75c7ae8afefb2d84d5b8e504c880f82fe9b10c486172b08ac9766f0b8ad158c205c", + "secret": "47c4a61a557d476509b97f8a788602c49290988bad073fb86479deeb6bc15147", + "signature": "89d209692f43a918a2ae1582c4e87cd0a9d51edb7b556733ce898c1e14fdb542b9d5d6263423b6825a8ff3a16f0ac139e739168e03a176994a8d1c81f3ea3f0a" + }, + { + "msg": "e70a019079527b552bb9bc0735f00d2bd863382e721b176dad9a9038dd728a5da371ce6234447165", + "secret": "18ef2c77e74f2cda45ba4b8e47c1f4303ca18c84f3983c98970005c50aeb2c29", + "signature": "391fd18d5e49052acec4ad33bad06ab44e1878a0df51df8c7a972fdf7ac0ba50e7d2fb5c73102a789d5922b67623eacfd5c19a8152575d244674806d9208ae0a" + }, + { + "msg": "d3adff1fa99e5e4450c85c1103fd7a5ea881771dba9a2d40aec65fda6a3cf88442f5627839022bbc14", + "secret": "324a9b71f1507a05e80c9305d18ef3b1b31bb0ede7b90bc1c190b1b54af49fc2", + "signature": "8843cc02f99f7c1910d7cd3e42a9dfe8c8beb5e2e8715de18713b447487ca73ef54b79388e10afc1b59c21d3280cce2d7ffe59bbc2d4b79f4aa0d9be60d49a0a" + }, + { + "msg": "2588486c5960831cf6b095309398b995e141af1043497d3c445f895d0cf0df638bcd625c63d1972db4c9", + "secret": "0cabad2998043b72074f5d043072eec6c647147f3c70b81dd56668dcb094c5de", + "signature": "0393155b8062587c95c98075f0ec9520104d0c8daaaeda776aaba8a4665347b4b57a47c0165734ab69f14ec6e6add928140708f49e3284b1bbe391c19d885808" + }, + { + "msg": "4e5f0a551fa320669deb39c51be1f19f1974cf6ed36cfcd95df1c3ddfedcbf9fd4ea0456c9b5ffdd9f1688", + "secret": "8e691b297853d6f0678d70b3a8400be40a2ed6f1cf1124c119b0b853c0e09dd3", + "signature": "af1793849734dc4a2b179bf2f8b05498a44eea9777683dab2a6fbeec5d64c71ab4583689a8dbc78f563c2cece0601ece836643f99ef35acb723b105c3fd90409" + }, + { + "msg": "918bb077a5bea3e9e2481dbe3b1b8a50e7cc9cd666f77310937ef05829805d1ea996b441fec992e1f4c8722d", + "secret": "44d7e4e0d3fce508408f47686f517ffa71d0c2e67a074bad4d50f9c618c1809d", + "signature": "19e0bc2e2116b2b4df4e899ade6adba9aae00f8be2aba47dd1dee77b46f49363f0a55e74ccd1d8f30cbac4e39b09012e81654ef275cb4f4d6658dd18dcc1de07" + }, + { + "msg": "8f180dbe77eebb6edc353ac9af510608ce55e4c661d32ac1a3fc21b8e77e191478d95b7d115d5d30f33a64bc24", + "secret": "e4b2137b74520ba65090959711575340d4f0a0dd4c525746ea3109354b2394e8", + "signature": "4f84fdbc0ebf4831a024854f90c9797c0e260c0b8114b65ae293e49007a9663964dc568ba32d1544a282a58dd280b6d3dc10abef2c6b848c67a634869a01880c" + }, + { + "msg": "d23a61d1ee186e3efa620a173a6372f64fd8ccf6f7a49c4506c27f736074ebd69466c35a15be0b1589a1d9d28b96", + "secret": "0a77612d779e49ab425f6b1f19232c95c72d703522086ae865af4f038f1098e7", + "signature": "d372138d762cd6cb5300521bcde7fdb12ae69c3808b26960b888342c78deb825bc34e03533dbb4bc5e86d5ad13c44e266907b832bd52e0d76784f59d86d3c001" + }, + { + "msg": "c6aaef4bab3c28af36c05402863e9cf7fd029ed86509f78dd045d3aa1f3760fbc66588288fa42e402b8e7e323d44e4", + "secret": "ad5d2dc106e164495d142abea045ace165c9c01c19ef81afcbdc841dfdc4fa3f", + "signature": "fcad011d7f29fb324854edd8105cdd49b6cddba95b2919df162b46d36e4c4b924baebde53ca9bf698be59b6b7e7df1f88968e4333b8ae5640eb9bd462f348d0a" + }, + { + "msg": "b00c76b0e166b376582e6694d505a423bd83e075a03c351bc3a73e768010713ddc442324e0e851fe79b16d60ff0ad78e", + "secret": "64b5997fed822631a0d7d36c13cdc10e4bac8def99320e5f4008adece4308f4b", + "signature": "13cce775f825c94bfa51d0e0ca221bb21be6b15056fdbce97f1b37e8ef39357bb42d3e0c0ac39a22750dfc8d9862ff9e86193b45424f29df5fc41d18ed28e502" + }, + { + "msg": "3f5a761d6c51106f55af39ed5511dbde9ee07c5a5d517c3857e287d3a8fb4d87661b80efd522145063f4c56f8789742ab2", + "secret": "4671da9d0d339aa43d7da911fcbeb733b0184860b62135904d618ff54bcaa55b", + "signature": "d956ccfce30b505d386a733a3f4c5d44c234d91a6fa3c932060dd7321bc60e7e4106cac3ccb0add3693fbec18360fa389654a2b93f453a144021ced61603e507" + }, + { + "msg": "bb15e2732919c7941d7e4bb5d9faf5951c9765fc761fbe9ae2ff308b44d40d4f17a02167fd2c3ff3732837db6611525c9eb4", + "secret": "b3441dcf5d2839c7ebc35d0aa977223fe94be8a98c8ff616eb88b787606ceadc", + "signature": "e4fa37751ef2d3b7a9b3848517c65bdb342dea2096be88d03831c8426073ccca06ea80f66f9b71db5316912e20328f9c36e3e29b1932ecc0ef956eaa4fefb801" + }, + { + "msg": "09", + "secret": "425c7f124a2cee88d9446cb6f701f729aabef142ab032dc718163d216dddb4b6", + "signature": "6876cfb1472d54630230d5655e4ea0c957339abd0d3ca99ad76a379135db5a833eafba3ecbb58815ea0945a852bf2d90c9693b3e5a83f291a9e940f445bb0504" + }, + { + "msg": "6ccc", + "secret": "7d58a0fa673f51e00e40d4aa4b21321681fa6edcb6f3e2f4b2a64392b1e8b1e4", + "signature": "549d947d2a2703afc91072638465fc63fc9f90a12e63f5fde5188647965e7c09b23eed1810c6be7f4d47a4192a414ae54fa89b003924c74ca771eb5ca287140b" + }, + { + "msg": "fe20cc", + "secret": "dde5445994b45a6aa07ef2db44aeaca78ffa5454acffa22270e85f72bc3c718d", + "signature": "a04f533968be2be4e98a28c948651961f25440d20b226e622507019e0837d5a4bfcf38eccd6f45473975f53d7d61cb4e2ae2f4c041e85ec8ebedd97145820d04" + }, + { + "msg": "ad2b1bf8", + "secret": "8a16d75784b5b7aa7d063f7bd5eaa0516a6c93104e13d8e98f4be590ab6e05d4", + "signature": "853ca2855f8f2c26f9e9b735056321677733b9d5a9cf7647f16b242d4e56f972a9703812a75974646d59aad5dbad9a3570d6c780af364def09b7b2c2026f8d0c" + }, + { + "msg": "31cabd24c9", + "secret": "fe6b88bd90eb8866f17197819e75c6a18c450bd00cc1b16a6a1f5df2a90d4989", + "signature": "d6892c516a0e6540a96e6fa9a9bb4b75d85317aefd2c6aa82100db73ed6d8c3c396275c93d377d0d3fad9de16ab59e223f0cc5c9c6e078d1d6e4902163d41607" + }, + { + "msg": "0419e6ee3416", + "secret": "c51dff716c97a680bbeeab2b9c235f1f9b8cf493858e3def87f4fba553d4c3c0", + "signature": "755659118fe54ac9488d2a330044458494a6161b86c0a1e76540286c8c911a82620383edaa83297541b16a0fbac42515cce4be86e3dccc05611791245f84c00a" + }, + { + "msg": "00031c7010bb21", + "secret": "7a184761207b3448c46f2f54a2fc7094e616fec9be0d0e06cbf74e8ecf9ad458", + "signature": "91466f60b906a3235dc686e072a9244f9de090d9af23e5df180859227db6b031f7013c71bfc083884bbc49105b088938e4d8666753c2438538699db3fef4f304" + }, + { + "msg": "9e97b1719062f239", + "secret": "1f3782573f9fe179ec2d1f6ad8c7b979d85ba24fb53ace751e46dbfef64ea4ba", + "signature": "01711a7cccfc540941b363410d353082a52eba46699e863e5ad69cd82f7d0aa0db1e04f63990d8f63759a6aa481800c5b110eab104645c7443139d1c4f1eb709" + }, + { + "msg": "ef205ef7f7387b8abb", + "secret": "94d23732d8267966ad938f1767f87cdb4677e07b4b13254745ed0895aa3baf5c", + "signature": "7f81876fb67cc5bc25f79636f7a377939ef147eee8467ec2b41098a0fafecbf9b52ceb8adfc038c325900ef279fcbe44c494b1abffb5bfeb965a3f2514f21c00" + }, + { + "msg": "f0cdb192b2a14023e2f6", + "secret": "f2cb1e404eea5d8ea9bfe729560b8bd2d57728f6b4aa52b4c111b14807258b85", + "signature": "e09d2b9689c4b25bf5875370d8ca5030b7ea695d6bb95265faa19aeb90bd38b2af301c1532e67de0352c6dc92a13393407aa0465e23d021cd96ee1d0ce6b620b" + }, + { + "msg": "689db47e1f9d36191e3501", + "secret": "2f9dcd117c2b0dc7eb9bed41d2d19ba950c53d0b24249844956da28be497d948", + "signature": "9017f8567fde0bd6a85f9c3347b26b77c6cbe784ebb19a2bfe56251402b4039f0e9803a6b1a69d018765dc2a781887302591d4ca7301a83d95ed902ab084e602" + }, + { + "msg": "263fa571ee3eb4e6f49b5f22", + "secret": "bdb192b5daa7db1955c7def70fbb0423ea7550f3cbae5a0997118f531d2863d9", + "signature": "407ebae7fe15f461dd9d7cc4430cc8662544cb76f2c332692cbd466b1316e3014b49a7244a067c6661d62e914203981e666f15eec348b3fbadb72b3720c5510f" + }, + { + "msg": "f19b8c8ea3a001a60de324ca5d", + "secret": "ac16fcd231564a40f7b0d8eddddca79e28ae3b62ad73d3bbfff47f13cc00068f", + "signature": "0a05f1bb48552f8bc0e05bed94db39c70b34e8e8ace71825857bf70abb50bf47cf76bcf3c6d789e95cfc5914f860ac1fd4a924df2ece2dee556eeb9677a37200" + }, + { + "msg": "8d3d5b30087ced930214e6db75a5", + "secret": "f1e9f5ea79a98e2aac6be31be7352e0d82bb232e95b70856d1611f2338325e1e", + "signature": "a457db77dcb2ec9f4ba59d23a220abcc7e37334514ddf79a4cab4ed9ae3369aebf99c8d5aaa0ef83ec503341df8c398642264742659390cc7f6c90376f821f04" + }, + { + "msg": "120bde3bb0d3279487c79676301654", + "secret": "75c23a7cc7829d363b4d85dc86a823f6252f7641decce6923fc8aed6c7954aa1", + "signature": "1670d47bf9df18e3bb2ec4387ac2de7cbc4e670c8b4e2089cbf9929fe031dd6f8f218bd6e59bea521bf6bed0e05f5ae7ed0ee987aeadc7f192daecb8be74340f" + }, + { + "msg": "7d58b0c0d0a3370fdf0e77f199e3150b", + "secret": "1ceb01711fcf728edc31dd45c1f36e6d99c0315a6c9f62d278d5959ce26151dd", + "signature": "68850838b0271ec376f80f4819c4e6f312cad8efae894dea2276f3e85aba7e4743f1ff1613469f5bd5dac8a1bdc4aa9ea1c330156d183c3671a0826a1580e609" + }, + { + "msg": "41082316450f0174d8576e577fb8a72065", + "secret": "fab8682cc8fe0c4d9892cb7979db10fe22bdb83fd18bbe420aa19aebc26dd2fa", + "signature": "30aff36a2b088bf87f81e621a4b0aa04f4911b2ee2da882b8d06ac116588a1968994dbf98f659712c5dc5b0c21b1943752732761c55f70bacac1834632cccf02" + }, + { + "msg": "92f5d8045cab4aefcd72fe818b5dbe3ecbc0", + "secret": "2d5e664ee4db6919e1de1e27361d2824ea9489babfc7950c98a994a6f25a6ccd", + "signature": "434e5cde5bd1288cce0d9a0847e28aec1d9d55fdb954e592c662fdbc52e2950a18a80025516742035b25e3838aef4f1f8c9b28ae8394cffd0de6da112d122c03" + }, + { + "msg": "a79d909de16c1a0577df16eec9cfecdfda7bf1", + "secret": "eac99ebd16239c8b4b8fe01175f2c8a1d955874161b8517b8023cf4efd24f53b", + "signature": "8e1222cda5c610443060f2c7704bafeabbe2150ee8343e7e4b8325dcb764b1cb57497f044aa80e1edfeba1cd89b2b0f646004e321b892523a4f4919791951b07" + }, + { + "msg": "11ec0e73f26cc7b28f4eb4f6130c3d38821c45a2", + "secret": "8dec565327c1d3f294027d108387ccdc8f8a32e7a54553d88e4e1bd5ceaa8cd2", + "signature": "711f0e64637ebee5a1d164dafa3d22e3aa322b420b75aac69d8ac090ddba33dc9f190c7cde2074301d0f692a961d1f9e4af033092dd783bbed6e3b0afebb6c07" + }, + { + "msg": "a9b3b5b712f5cbcba41c43df6e7d8311be4d22809f", + "secret": "8c10dbe83fe9ce4734fd99c56275a0cf2b29a8a26de7ff6edff926c37c05102e", + "signature": "db402f386a70f8a81842cad19981f820ddf1e65be39e8b12df5aefccbd1f753b12f6b9a9f776efdf8f9bf8c940d7fdac3cd1250ebbddccae524636ba59e56c01" + }, + { + "msg": "4444a413b3eba41e5f77184c43c214332c8a92d0529b", + "secret": "92de0794d99fb17a48408a11f6f8b7a0549424aca0d0c20df5a9f5dd5d84f00f", + "signature": "cb1553f6d37ae917d9ec51bcfbba8b4737530cbbb4ad10970eafe5f0a56db15857fbff4c8b0282d4ac75b050c854e0d071f8d8902fb906a9b635ff52a420b406" + }, + { + "msg": "9c2911abf8f03afe1ec172d379099032949e6b72de6eef", + "secret": "42427b150c2fb03c372d624c17d16f07fed09e293e5b48d79848d2734138163f", + "signature": "5ee13d4c7d18a3abfac02bfe20fc5592d0d5664ebcb8569524fb61a18d4eba85c60f7a11f9314b42850d383cc1e8e939ad0a0df6e67d223d3855cdbedd319603" + }, + { + "msg": "4dd24b5156bd4c17f59893a25da4b580094cbfdf4b5ba72d", + "secret": "75701135a80450980e423032c0d9a90dc1022f1a3cb56fad212d288d523cc7b6", + "signature": "ba55bee15094bc8cc4d38bec04e93e7431bc1729fb46ddd83c92b70982ac51904e93d8c6cbb39feed9e074680330ddd25c27411c637326f74d301b6a3b34e101" + }, + { + "msg": "c135fb0cf523479dcf114ffc662553083cc9dbf911ea651472", + "secret": "65dbe47d6d2bbb5b770297537f40834d9d41b53cb7b81cacf4bfab2f5cd9cc21", + "signature": "51209851e39187313c58dca2a59895445fd682ee81165abd51f9144415bc70dbfff64414b66c6188bec70aee15cee31231c73a2ae4a182902620fbf4aade6208" + }, + { + "msg": "41cf555f66bfa1ac45a44a8c58d58c1d26aaf6c67f048cf448c4", + "secret": "20ad7f07a1b73daf76525b12403c8f03d0b813d16cdc3c260fea9676eb2565cd", + "signature": "37a6eb5c396079cf113f7875fe31fa69936d479acecbb4cbd1a66dd59173ffe269b920b73a4898573fa75794466d3c7126f4a6890ef4c7ec8e99042dcecd2c01" + }, + { + "msg": "ef047627fba787e3637a960320539ea467d73f1fc989999bbfb860", + "secret": "285060061709806df36712efd810490a35be932b7a7903b8955f77a99d2cf252", + "signature": "95e91347e4d2f7e046a4567d588dd73bd4583e1788c04942b87e354e3a870ed61a646a6421536179060c00c9e82a8f8f525898a96b42fe884832f459116e850d" + }, + { + "msg": "accdaf34bfdc3e14a459c0015e2456f91d05ebb75c00cc10025aad27", + "secret": "ea6fc1023adb4149d76ca1311416c362095f679e2d073333f60568dbbb54f55d", + "signature": "d0a313051b4bc8fece3dde94e9a98e584b771c7a2bd488be0f366518add9178473acb188156f3a170f835f801adb7c773a540642a236982356c5fdd96a453c06" + }, + { + "msg": "f6952a61326d0976013741bc54b66b844bdbe1e8382be9f71ba5b49c39", + "secret": "df3747aa2689e41bde810e20f1fb03a890d4f126e80a2b694613ae61ee96389e", + "signature": "6e9ef71b5753c0ca0aa03561d11f4a59da751db623202e7e63ba8cc7764c806019cab7b47065d339bda1edcd3be1be30b9b75ad647e9d7a69559cbbd6e3adf01" + }, + { + "msg": "14abedd6d83d959a68fca5f85990001321d1b70ddf696c219f6250a6dc48", + "secret": "a2c136a31e0b25402f64251945a562e3a0214b0194fc6c1389df5480041be8f8", + "signature": "3d617d134f22070143776ed26478e8acea806cad784014ab36114f388c295b072e112bf4259c1dab5e8fc691a745837706ef5511e4483f95664d5a76568f0f05" + }, + { + "msg": "1b32abec6a27d76a5259bf8dad7f4c9542c5751af5ea9c4eb1a90767d999cb", + "secret": "4845201cb48b7cce5b4157e97580da40d7d757cfb33414608ecd4e2f16341c9e", + "signature": "9bc26d1dea6ad0f85f87f3ec97d5fe9b9b7c7af5ed62940ab5625d8ccd3afb668f70d3ffbea713b62c1128cfcc52bf39446c12f08413a03c423c7be3d5fc6605" + }, + { + "msg": "d08565d04b2e20723b1cf844d6a9c572a72687f3888b52d6fe9105a73721437b", + "secret": "7266e81ca4f78860441e2f0cad2b87734395a0e38c384af36e2db65f3cff3f85", + "signature": "167f63ff714212a2948aa536efd5c95f645848be19020bd7c0e513906108344ffeaef994a24c667841e81ad31c6efdf715d4b24e3c97354fdb9a9cf943bc1708" + }, + { + "msg": "1422c58a7b0ddc50b8ba906f8908deb1aebc53d3087f5735ea8e307dda1be8ffe9", + "secret": "70194ac3dee1cf30a93b3d5ced53aa23eac41b9d36b493222e59be18c6492c35", + "signature": "c4a7fe00984487a86c0aa4e7dcec2f0aa3803107a6dfe084d7254e921ecb7e20303f371ca23677dbf7c91a07e2d8881d7dcdcc5655e3f353127ea02bdd1cbd02" + }, + { + "msg": "9ad96b0dc9cdb7c2eccb0cfa8b497ba36260dd540cd5e0ac37548056fd37e9eabf4c", + "secret": "0abfda7daead06eac29db130e6936d85e8896ee7fd71fc3de40a3736546f134f", + "signature": "4e59e718690661cd7eb3d88ff599521f6fac1431a4c02c5c7c40066b51da9700bee6bc7e13cc7d442faa619e442d9ed34906b590c42690bab61edb2cf2ec0c08" + }, + { + "msg": "e44ac974a8beebaea615385c2efba586ba2c0a465bf80d2949b56adbf4aa4afaf96c1b", + "secret": "f8e9ab423f1ca7532e483ad7a7de9f34671568b704bb37ac042f18ab3c0df7dc", + "signature": "38d9dad19cdaabd9f6fe757902a55fcbcd3fa9b5604b36bd45716b49632134ab922c23ba438b1819e4df91df8aec3c844bd6641e0f41db00ac426e67ad8ee108" + }, + { + "msg": "d86313e5377157723f3ede8ab1e4c51423ca56dae002352efc29615c445b636b81cfd91b", + "secret": "87319d671ed78c2e7c24166771744738dd91f736e10c3cffa63b33a1e307d7c5", + "signature": "d63b56e5116f26f6ae91fe6ce6efa3c352bab893e4987e734f603cfb2d8ec8bf1c18fa21baaf0ceecc21f2157fdc3b108b51641bade4ab71412e37ffe12fc509" + }, + { + "msg": "4eed93eba775affbabb2b58f2798120e6b0ec04ea4775eae0e1c57d069fbca6989dd2c1ee8", + "secret": "e7c4c8cf18f072fa054628c5d8be22102665a7d9f71183143433f2d012e47996", + "signature": "d368434fe1824034b85c2b9dd7858252fac10660a434be421cc0df1af8c66c7772e6d8f107fc8f0244f3be2a990df0d8121307c199ee4efee8a7857e423dd50a" + }, + { + "msg": "5c05676ee455351f81973c9b4c1ec4d96c8af4b74986bfa0acee5b5106684f4df9a9a23bc12b", + "secret": "cc1de6ac3bf904e31ed7aa0319087c75b9110d78062eeae45594ef776a95da05", + "signature": "aa548bee20f1c4773fb8e75109472a3e73d88e270c317665ee5c316797943685e77697973b67852dda04c51288b7f41eb3f65a0e74ce46ff3284319816eb1906" + }, + { + "msg": "8e090ae89ce9ab91cc1ba5edf066f9c447a837ea1df448783e957c9de704cc1d5f5cbb10347dca", + "secret": "441816f870c636be2c5056e0adbcf50bf79510c8fb7cbbe4d6d1bf33433902ae", + "signature": "51644382aa396c18d2f4476c70bb8214fe65773e6cde4f2d3cde837ed9fc02f608a6fc5bd593b6fc19c9160b357554a94c0baa8ff700a89ba3e02865cec4150f" + }, + { + "msg": "dcabf3c0f91f347a0910de451177b85e67918349c790935c5b02724bbeb39b6de345727316d924b1", + "secret": "63a3f782413795e1f85e2792e0a232b407994cb2d53755fcc29d4aa7cdb4f9dd", + "signature": "067aee4382ae07f037b703b3cf6241fe1ba0e49a63946d8208e68f62da0cc8d085465b7a507a71d07dadbedd7ffa78a20f4db5936a2b45a127a7963e2215c607" + }, + { + "msg": "5ecd05f55f9d7c8759c643c3cfb5bbcdb419f2aea2d20eb42c7fa608edeb0b98174a70939be407a3d8", + "secret": "6ca91dcf0fb234616ecf53075e5d8562a83b33baa7b5fd86a4b5a4ecfa1acdbf", + "signature": "4d6810113eeac878e7fce611b4b8ac083664c32ba73356af2512161b9ab556b375489fce565f09495b71603d251dacaf2786142330eb1a3613c2c631679a4008" + }, + { + "msg": "46dd2959010b4c3f7ab4d58e11a00c42d85c95801d74425627b35afccf59ecda59ceb5f301694ad582c8", + "secret": "907825d15aafae6fb66e14e32da813ffaab707caae1d97479fd56dc97963f345", + "signature": "61c6b79d06501ad71d88736f665f50b3c81328a64cd9ab83fdd13fae3640832b88b8b1778291728ad575e91fde48b462609d5c5bc09c687cb7a154ba52ae340f" + }, + { + "msg": "802095e38b664a0a671e8697d3c8f95257995272578c89f4803872eee9046cb6b1231f087af1b76cf8f33f", + "secret": "18f57880dddff966f6656d0c71bdc44c8da86bb9ffb7d2ea06cd6722e061a611", + "signature": "298231b3dd131bcb464cc2867c45783cbf39273b736762340b52aeb8db38109ee201bf9e77c832d35d1c182c109006c3f8b25c75c9eb59ac65c27908a5399e05" + }, + { + "msg": "17aa19cd4079e1bff74cc08e0f7394a43f2b9f5bffcbe24a1239f58b652b601e707b7a30999da24549de740f", + "secret": "628973dc709961b3f654d1112d9bb5cee8b722ba1767fedbe47476288e5f963a", + "signature": "88452119fd9aab40f52438bfb96b55c880f8c44f9707b267ba21346db70b58e0730e40b3e8abf8ec7e095e313b63f09986d1489f253b645a943b3d7ca9940b0b" + }, + { + "msg": "0825e24307cea8a7d592ac5ee95bd6c1195762ee48be6801379c955ca9241615e0614708baa1fca64be2bf4faf", + "secret": "4143614b2d7c40e45826aeca4fccfca798492014333f6955d73203e2513423dc", + "signature": "05b7a6cec0361d967e0b30e23470fcac2ceb93dd6f093ad3e161545edb6b773c89439f1f1174bf698d640350c90869931e8298017519665588f558a2a14ffd0a" + }, + { + "msg": "4f67972a306263d9283e81f4933f9e93cb91701568062b801878178f7cc8b8db5754e3fa5884a7a3f5b534b33f48", + "secret": "dd12b27b2d8e2354f038129e8e32b943b0bfa2a18d3562e4e750254ffa6e341c", + "signature": "f5ff191aa06d4817cb9b891dc6cee60e27a1f68d46a5b4d8d31064cde81f3c4bf6e65a5c5348f2b1833156075d019cd18156e4f0c0fb8bef6eeec629a7560c04" + }, + { + "msg": "0b38dc19a4961c5862dbd4a72360378eef5e74bef3f6f36e66186a6dae5d62d13b013d9c82d4bd3a03019734597309", + "secret": "4c6b3d5c6acaf2fe97f0821934448e68feb4acc6610a4d3b0f53a5612fcd6017", + "signature": "ad5d6390a9078473c334fb5db9e1291a99d2b73fadc66607cbcb9aac44a065d322a8aa553b8c8cf05ce9b5fb211a5b5a45b5799c38b1e60f1ae9572a3d3df009" + }, + { + "msg": "06f29605ff7c3915238611911c42174ba60f518ffbd82fc7bda06e6ee0700b598f75d5eca702567192236656331722cd", + "secret": "1d05268763f655dd43cad53b90c9f1e076f6d5bfb25891b7adfc0db56e4ae367", + "signature": "fbc62aa456225505ed74fba70b91d9e71c98ba800a31787f5c53d31787dba203c843dfe779f703725deeaad49b60cf45f11bf162e6e3edad6f9c2fb470fb7103" + }, + { + "msg": "2edf54337e62d84504fbf716c211123def11c593fbb82b5db140394f32bd7119daadd7ebebe9a582bb2962362187e92459", + "secret": "a5a0815594d0fb0ae7489aeb872a9f86e9c3cf5e4b3251cc5c742db20f9d9e60", + "signature": "475d7d8e0630b67b559e633f1e11890f85ad996ca368d28e1b483607d4be916626ad417ebd71f74429320bcdad24f2614534e3e46a42a0e875f18d3ef2a7c704" + }, + { + "msg": "", + "secret": "2bc79b2c5e1f9da1b7e5ef4b0a63348abb7cd143096ed8b646091f110706a823", + "signature": "ffced1d14e3b15874d33e17b2a65f0baa5f430b819b06e387b9770aec2d4fea8bc45047c1ee666cef8a01c90735b470735df5a3f416760fbbe7b12cff947760c" + }, + { + "msg": "15", + "secret": "005e9dfa7d924dfbf81f124967648f3511445d3c7785560b5c3c7300f00473ed", + "signature": "e94579da3c9c51294c82a5a2c3e2d976e206625381c5adc37207f792740b3d42d6aed03c17f76d5657ef0ac3a3fbb3d9d7d1601005ef59df54a46037d8163909" + }, + { + "msg": "7700", + "secret": "77fbf59fcf9cd985141119093b069cc1dbbfcc87b3a7438a24bd5d5087d45b73", + "signature": "36f67c15aa0d8063b24bf1c7818153ccfff8956825de9320a8ee4b0764d3db2eb23edf55d6ea3276d639f0c1e9cfc2653d76c154589237e8c664920c0cc85d02" + }, + { + "msg": "c6704e", + "secret": "8901e92efe1b85330050c097a4d86e5616d6584452c5d1b5d5674a31af24494b", + "signature": "3673621bdead6f25e7595afaa59fee21c3910923d35725e633b54b5cbda0de57a8637f12761d6e51abceb3615d42c5b20bcbb7973ec4c3c95a40195bd0ca1607" + }, + { + "msg": "1268912c", + "secret": "551896b6d68b5343052f37df5b0fda391a1bd9092ae434b65c109e4a99ea8b0e", + "signature": "6a6671ab03eefe450affe619be0f7434b7627dae820333e24c906f0c835e741b4b8ed2bd42839e03e75916da80672e7e72cd3d28e1f960394f8f2a0519099f05" + }, + { + "msg": "ea5ceab348", + "secret": "ac7c38cd8d69f7c167c6e0c41c465de2d58e361b2f90ee88abbd13acd0d38988", + "signature": "0766fbd2e35175bfd6e0979b744c9a5a3842b56e5a7fb3ff629e32d1dc6c58a353ef9e659394c0d5958b7b43a9eac3b0045d74c25b0bc023b6c63a792be85301" + }, + { + "msg": "84b1b81db406", + "secret": "7d0ba62f80e1f29a9e4d48d09072cfe602ba0594b2a5b7ef4550c0202799c448", + "signature": "d3261db6eae8de661e232c0b09af93c15c852f285b835c4468f9dd5c7698b00ddf5cf534ac1c832eba9967c704129452a8daa0104decbd6feddac27fc5fc5f02" + }, + { + "msg": "c5113fd8351885", + "secret": "bd352aaa7d54dfe3cb9f9a73fc03c612e206ee6bcf122e93d20bf39d85cea720", + "signature": "ff1d07ed55237deccd1501bab4b9db2de8a57aeca58be19905b2ab7431a06f81942169e84c34d50d9bc78c980727123d59b4d8a25fd5ea6f0a4c0d48e6f13706" + }, + { + "msg": "ea3e8afbc7134e50", + "secret": "3e1102decde67e0523c55a37abea8091b7adeebc964c7787d80e2cd9d8a0b3d4", + "signature": "0fce5e6d2a70815122b62ca510c513f99a994fa27871165b8dc8b29df532929a0b25b29591435075d047701bf3397d2647fa764533b51c09141358c27672b205" + }, + { + "msg": "8e546647aa4ddd302e", + "secret": "89afac616fcaac740e10e26da36dc99547cf6a080cf55611b261e7ab54f46d2c", + "signature": "931715e2b75bb0e0e741d4223556bc46090a9d1e07a53c0eaa6dfb4a9a0b1118957ff666031b4746bc1b96252c73425c81166ed2d0f57c03628cf8a180404b0b" + }, + { + "msg": "5731f264d258bd6b470e", + "secret": "a35a6bc5261240d7184bd1fed2258d4104ea56405e464bb7cfd7ec2d3e3d32f5", + "signature": "6da44fbb0cd4608decc5b32277f8a9bb16f33a31efce58c47444791af87eca3a1c1ad972504c4b183883f8baece7203d62e20e0f05ed8ac4f5147a1523c62c07" + }, + { + "msg": "837b91252ea4edadcb3cb6", + "secret": "eddc007c156544c257f290d535c99c6c28b8ebf2190915e77a086a5d85f20117", + "signature": "6c3f4043459f8fc3c294422cadcc497c241fea21e93e921e4326cc8dc0c0d8572659f1d79a070fba8bdde72d9778f25e0b3ab1afb74aa6df8f6db6dca077350c" + }, + { + "msg": "6ed8180813e185b1e00cf00b", + "secret": "7ed2d142e76434a52ba3083a057ef39ed3f7fa1dab98fac26b6a4cb416cf40b6", + "signature": "ed12ae2dde8e3c14a2f8c432ecf55561cea069c380a2841c7e95b436031bd91b056bab4d726b72de65b86e31a15232b0800e8bda98b0cb0f126dc6d3e8fa5a0f" + }, + { + "msg": "f91aadd09f46a84d58c22fe00e", + "secret": "198b6f5d7aca4e49229ff53b11d292563dda6b950f6bd23bf1d5a87d3778bbd6", + "signature": "c1fcfaf2ae741b3dcbd7b87af98b14f98afc98f260fda1f16b54215ba55ca73a0d8f3c99fe6ec867dcc4a9e0206cbc8f95e8d60b612e636d60dacbf5f7cb110b" + }, + { + "msg": "b68aa81da8800755d4efb9d7defd", + "secret": "98d4d3ee424fc2d01f65bf60eed223862dbfd1efd8d5771f1f3ed6350e2ca30f", + "signature": "41a369ea3204548d7d09c7f5e9c3e8111448ca80527ce6818ae606e9f0b4f4ee693149ba349df4d98cad0e8a57f9e36ce880ba0eed374d5082b51271affda80f" + }, + { + "msg": "9afd7016f4975f838e63f2fd6e7585", + "secret": "8236d0768b2a475907a8c747a4e7d83fc9690a49358531938c8022117feec2fd", + "signature": "92c5b4d63f064f90c2a6cdea5764f44ec3ba8cd9bcb986ad43ce92e2e058666ede07158ef3ac44c1da48b915381717d722211f196d83c3eb4a84acc0f1857205" + }, + { + "msg": "b71a5af398fd5be3b4ecdae59f88fdee", + "secret": "547f853a4f8dea7de28f6d3af5c8c077c4287399839fd5a0e8816508f26116fd", + "signature": "028cd8f41ac0f8a0c51b43492cfc4b99d7e4884ed9031978b8a72fe18410d1af0034f3597765c777168ebb9908e1e01617da701dde4c3baeccffebb07747710e" + }, + { + "msg": "ed6104a35130477175a0141885fa00833b", + "secret": "b21dfb61b16d96e78ddbf8db8044e04cb97f63b86010bef90ca3bfd83708578f", + "signature": "b646e23ce36ece52c893a40a709e6a8dd80dbe23ce66d1c86df315234337daa7f09493a3e1a27fd6b4a5b613d79ef3605fac93db07b6a3f692b15f01b8567909" + }, + { + "msg": "388ee13864adc1ee643661ac250f07f81ede", + "secret": "a3d70d1b6668ff2070d07d6e2e58b0072ae76bf155124a650bf5f82307dbc022", + "signature": "3892e2538db569d39af09f15c40f6efa83ccbf4bc72f5560a667f382c62621b0daf6fba1d536b12b330e343b1ee0ad851a20660731e06139530f76d2941fea0a" + }, + { + "msg": "1517844bfb0c0e58d7d4bccedd0b8b57b6032f", + "secret": "2042eae92fe7bc36b99f2c459c8a8418c9b238f31f827eaf5ee255d831c05c1f", + "signature": "3f75eb39d9db14fde28dce006f0750ca1abe62704100d3b0aa578777bc02ff770530079dbd6d5e31d5c48805af69e85cfe4eeaed55464cc4cc9ee2863afa8c00" + }, + { + "msg": "d79f2e0f0a33013deb71cc2978ee1d7246251408", + "secret": "597b5ac93f03f85281d74b5f57450ee294159f5fe1da9aeea8477442594d5908", + "signature": "3392db1c13639c90fe1ed2fd69a125dda2beb15d3fc92aad1e32816c6bdca01b429505d307bf745adda1de0d9c8ab4d23fdf561858f54b7485fe2e866c747203" + }, + { + "msg": "9e2e41a59d252efbd4b16e66d15b46b2bca120b6a3", + "secret": "cef558c5e37165c090cc354251e05e0e13e50451eb3ff3b675c236c19a29a378", + "signature": "c68dfd2283bb939441864180348375e9090cfec7fdd988715674428b2f8530cd7d65abd90d06983199c1b558078f4c80414da0a656fbf93be35d10097d56720e" + }, + { + "msg": "4d9c3fd816935485e810517a79503b5d44e9e00fb914", + "secret": "a2b3f03c0a5c34444a814b0a399ad0fe65f4181fcff6a59f2d3ca061c53f452e", + "signature": "3a2bef91bc5fc230a7b99a5eaa88054497ca9644531857f7c7aa0ff99b3e9fdad01e12222d68810901e39739a1be89840f0b6b9fb72504e52115349af6d3a70e" + }, + { + "msg": "b4d18e314a0649996388540bd0ce67d7cf4d890ad7f2c2", + "secret": "a3e1d7e3957039114f63db9b5a9ff7e38ce0c85c679096707dc6900441311c7c", + "signature": "3444c1a59a87a08de6d5da1fb8612f93ed4f6f9b5d61313769115a91265cad6a9006b85137b72c9aa64fb4c401cc075a3e884667cedb933f36617b2748046006" + }, + { + "msg": "71524c8eb7ac5defb4812589ceafa852a33442832931ccde", + "secret": "829c2addc95af0a91a400b1414a4d7c8eeb310a50883f6aef12f429dddce422e", + "signature": "14d822257b811ecb3649624cf0078d9b05871c34612bcf84464a11d3d442a399142a5c574fbd9da392084dce2635a206465996c751bc14d819b5dcb74082e30c" + }, + { + "msg": "083d009ccf4bcc9d562e6bfc34c515f0433426d5bc2aee6491", + "secret": "956dacf5ca973d8d0a80172bc87eccce6bab0de83f275b22b3b8b380c470f713", + "signature": "8c0d2c4fee7a5e24db678e4307294d13f79cdffdf6f59229a830206b404d6df53fb4918809b86a2f93a50f48b56a9e1de85bd5a8c8a75bc650b7d77f143f8a09" + }, + { + "msg": "7d68a31eacd5a6de1fed51a6a78dacda4714095d4ead6b3803c5", + "secret": "54a63c9142170aa9b8cf664233d0a13e08e9d945f315b154e4dac2ca7f24e6c4", + "signature": "04f9b5a835997a94bb60516caa92e238931c23379bcdbfd7315d283312decfa4403a81a61109b20348507e9eb5026d266fa7e0b9f01513ccbc50301521e6a301" + }, + { + "msg": "909d8cedd4dd6fd844ea48979bbf6d6eb1500ea35b0a1e03fe42f9", + "secret": "808dd5c5c6a09bc1999d563f61c11744d02e2db292dcca1d35b28b8e95b648d9", + "signature": "8bfb815806a6d537a130905161910264746ea4d3ae3f21d00241cb951a33ed37fb321767c0c0e99508785beedc7ebb1b70c0159a79ac52ba723768578f32f109" + }, + { + "msg": "d8a90c2076459a6a3cb028454072304255b0160f111842edf3e86c9f", + "secret": "9e77296d4eab9b71ad1d0571d07062ae9e04e9ce7a82a5adcd501c234943f6c0", + "signature": "532af5836a18addbe867ec6d466b8236b72ef0442a39fbe757a4d92c39c3e42c68fbd405b4f2bfacf6716bc503bcb491398c4d7a53bceeff0b27c084445aba0b" + }, + { + "msg": "75329831073147d0117e47ecc5f1f4d8350574d796fba818c202eb490f", + "secret": "d3885adf94b44b433e839997e0ea2bd75ae55577369983a81461c1ccdd132422", + "signature": "4996c2f686f3b4334115ed1b5a160e844038301ee8846c4bf8a775099966b6a0fde9f3bdd30d7fd5fe3ee3e2e28ea4082546ab14cbfa9d8ec6849d01539e1d0f" + }, + { + "msg": "78dbe7ec4799ab24cc0d7e8e0bab374114e776e6ee381d6506ab830031cc", + "secret": "40fc403dd4f6657f3eb82db5f6c4320ccfd93550d2b8e056d0f290230527d62e", + "signature": "4f226823f2ff34b2f13bc7e0b88a2003581977d6537750e740d4d2bf2f7aba55fdfa730008e37bf083b54f6b8c19e889e4630c62b4f51517d364c979a86df10b" + }, + { + "msg": "4cf9d398df60f62f40f1bbe2bea8bfbc44ba7c1a20d3958e4aad9b5c2514e8", + "secret": "a9dbcbe1df7cde66c8a39acb64ac2341acfd24c78ebb7ead74761c058531f4d0", + "signature": "726564c95dcb3d5aeb4bf82737a14eaf36b131d80e85da5580a0cfbd7aa24fe40c0326895b5188f574a3f22f9cd538ea933191e022214f6f3945e2ec57d56005" + }, + { + "msg": "b0daf0583c1c08418b792b875860b6303fd5e2db999cd20c97121d0afc262c98", + "secret": "d9196bccc5dbab9e7ed0868224a97bab2bcfae7f9ec2cc2c27f8a44e1f1beb9e", + "signature": "ddc1bd980982953dae41f6ab3f7d329a15077b4fa6e43e50cbfb1c1b71a6866092ead5310927415d87061d2113dd045f436d51f5b0ab0810cb5eb7cc90faee09" + }, + { + "msg": "0f5e53a2b993e88086c2cf2483ba61f3f700445b5e072e3aafdb19e6df792cddfe", + "secret": "fa09c12ad283dd16c3718842e4e6cb66a1826881465840de75c81c94c7d8a405", + "signature": "b6eb3f94efe53c85094b28c1137dcfc2bf3d4048f167ea1866158e2abbfa2951eba663d5c40e5b307ce33f827778bb4d5f77c1c29b229fd09788ca184e92a305" + }, + { + "msg": "466e01d60ed15164a4496b66b71ebb12b1afc60d884bd593d61a33775284f9cd1f04", + "secret": "0f23759ea11f1d3c57d3f7f907dad7df618efdce665831b222f2032cfe24c6b4", + "signature": "c10af8c93621c0649d8813120a5b6647c4d489e9b61fe7956ca852baef4be3d44fe2e8f1e3e0d33c33bd972d388ae1a6752e578ec861cce8e98ac0c3b16b2601" + }, + { + "msg": "06db42089304d50bc332c99a731c26b033a524ee38da75cba6f470bf495cb0a0304ad4", + "secret": "bf421b700b08935b93e3ad1e4220059dfaad973557cddc9d402404fa6e53b702", + "signature": "326f8fee62ac5997a173d8c2e5cb656c60324ed131998f8fb94e2488c2d3dc67af1eb85e617025074ac802240783443e66cdf1a6b808c497e99f6ab301cc1b02" + }, + { + "msg": "2ab5bfbd471e5e75da553139481beb87f61a3b2ee06f481abf58f0468dc7eb1772606c2f", + "secret": "4dd19bd052722a23cd574e4ef20c39ec566376412e2c3ac7aae9233a7c0f81ea", + "signature": "ed66759652e3ecbcd57f5f51c91af39eb5c7ced908914cd41a9a56c7fba056752c9c7c3e1493ac49b3a6f93b4f22967c66ec585062ec6e197b2f69d8791d650d" + }, + { + "msg": "491f9ebdfdc658f60eb2b4aecc2f6df16aea28e7d2faa2a8b4be4d51fb691560d46cfe1cbd", + "secret": "f9d806909f5073423bf3c99ac009a6da8377fcc98259ea1874b07729f8108d92", + "signature": "ee3b850319e01b1653e7ea8a27eca322d6bbc7205b77e0d3e68cb256d8ee655636bafb905e1267f6d893cf17d256438b843488de94663a4cdbc837dff5354e0a" + }, + { + "msg": "661cb7fe373b0f4e406aa834f93f9cf65c97ecedfd6d5939654588439598ea0d3727aa48d2cb", + "secret": "23d4728dd86637edd7bc9c982ecdcb984ed49f6f3989bdd22b9bb5af418a89a2", + "signature": "9fb9cbaf8212d60cd72e114c269e1eb3fc82b2b44bc5fa8e06e99b10c52a34485eca7231b20955da442930108055791867dad4b9de4bbadb252fd3169c827c03" + }, + { + "msg": "73c0f459d7e39a1d46be2be197e9ba966548ca96420505dfec30f17b62826bbc301853cc1d6fe8", + "secret": "d64389d6e08407220fe5c645950f9766ca4fb52268f0b1b351e77c55a42dd01a", + "signature": "200722ff51d30ba48be9b4bfc4a3f0aabd23de60ec02956ec8eedca838ce40452740e6e618d86cb4a18ce3e837e19fa005ed3b5efc5db5294eddb54fea7a6202" + }, + { + "msg": "e88023331860527fd016ea9f506c965edce768176ef6a88cef284cd1716384b90da56012885e5796", + "secret": "5e664ae962b824fb28b44b4ee39add25555a65bb72da628cae3403c4727c4eb3", + "signature": "bc17cd458503d85e541521af3e4e41a2e94d2187259a538421714b62f165baec951bdb4e2f1a1d7b1474db031a36e0e7882decb5fef23069bb17eb486c8b560a" + }, + { + "msg": "bd506994d55e6806331ce320c67116818c19f4166df82c6d81295a7ac8764a2523f8ed9eb52f6236e6", + "secret": "964afd25ff6ae08cd16dcc0f6c1d6fa2331fa5eb8e5aed2b531ead983cc4ba22", + "signature": "aa68839de2669dd52137d2e4fb2f27993815338fa97e82a828e21df9ecb0b7aa6487c6371b2b2017fd8399f3addd54435a4c207df89b219488cbc1ac32a10507" + }, + { + "msg": "6165baf9695027057ddd5fc156e6a488ff50030485362d22c350d28de14dd70102d61ae79532cfe7e7ef", + "secret": "16b72253cb918177b3b4dbaaa42e5d6b9bebf8668b9b9cf923905e25ce15043a", + "signature": "2fe7563c0f7e8a942e265884b559803130eabb99d011ed41acd06690ee0ed22bb84cb8abe0c65e188b455e74656bd60128d73e41139de07288c5b11e74e04f07" + }, + { + "msg": "1fd286cd7b8041eb8e95339df03817f1020ba73cc8399ce75b445d5e35542ba4c93cf7926e9844ef16327d", + "secret": "23b6be308e4b7254f41ea8990c27b4b2343361a6b724981e33eb9ad154a8d99a", + "signature": "c07c1c56c38a29b65810b32e9334dfccfe546ba231e1e261882ed96d9a2580f4d5e5aaa1f7cf579d4c497ebb4445b9e08096502414eb2e7088e031025eac6002" + }, + { + "msg": "6cbbb7324fffab6d1ca6b519609ba79a28d25efbcfbfdb4fcd4bc8bca8845117349453002d0ffea0c7945e32", + "secret": "27295f928229ab3598bf59d90998d8c2fe267d546b587cfbf497951c8682342c", + "signature": "22e59619225c7c47d2588c986edb4a8a361987d217b71276e14acc88e8de5e2bc72b118750be6df94c8c394e4daee28a5ffc4710378789289d8f713b877a5000" + }, + { + "msg": "d351b6b57e0b01444413ac3d9cf7ac1ebd42666fd66e8f08cbe06751143eb398892320057941dbc50d746d13d7", + "secret": "f69e27942f96b98d8b5c2d4ceb929806a07a9998e59f641157c24b5b20011c66", + "signature": "36ef3128044457272b67ba94bd3b2d9abe6fb40b54cec2948c7fff2830998aa16a0322c80f328d51ce64bf69f360d5543cdb731fa0b03c048ab7dc49de755408" + }, + { + "msg": "0c02a3962a8f58057d938c66fb11d6ff7817feaeca84997a7b4dcdaeaf3929dae7f91de39584fdc9716ff526e3f5", + "secret": "03977d96781d00e529a68a3a701e00a41d72c7bac940a9a8f091e392da2da33c", + "signature": "8debb6e8f77121b9f020a6502e9cbe87987bca131dd8ca7b0da0b750653eb7e1e9c343ef4d0037d12a04155fe1776a9d1dcc3b25c82dc19d55f0edc4176e3f0b" + }, + { + "msg": "6699d86e4118d0dc72eea758e89d7aaf123fd175e961b301f2045caf3e8702687ae1f4accd8be6189ee57336e6c47b", + "secret": "7449239585dcaad79afd0c0bdb31c50f6d06433b9f986916541a6d7951b4d718", + "signature": "203c2985541b5b4c4aa329c1ad6f3478a7fe8b339c439ed61fa4183c50d82d2405dbb2ed5574ae9e7120f6b7b6ee249a0fd950a2ad33f0a52506da935966180a" + }, + { + "msg": "3f8ab7c71215ffafa2ecaa90909ec276be8214059f1f59d94dafd47aedca0ae27a744f4021f8d921e83fdf95b68d2d10", + "secret": "e14e9b9b05a74d78d3587c166c6bb42506b133b054c8fadf8be54fc10bfcc633", + "signature": "b02e85d9c61eb89feecff7012ec68b3367c701a07845e91c251f8c75fb06fd7e9f2ac5c40e5e47795c93b4e184726e51be13c28f971441bc0cd9cfae0069860b" + }, + { + "msg": "3e1dae215aff8efac511316ed4363b2d4ddac813471bc7d43c6b8ebb58322d17ccf0db08fcecf4b1096e73bb46daf0687f", + "secret": "379bcf26e38f4d33e7d2218ad843a404710f8367269197626aab2727e03ff60b", + "signature": "487ab592c44a5dad6568802e820e6d6826bf72389516d6b6750cdbd39f1b98366b449375ac2ab94c8ca814ddb70b5e382fd74cff41e76786735316b72b9aed00" + }, + { + "msg": "", + "secret": "3649ece8427b101cba77b060c3fe59a1976b24cf78e371dd759eafad7fa534d6", + "signature": "cd86fd686986233539ecd31f8cc7c929d4e048e1bb178be099ade8f582e83ac6801b3a4f0a55dc8ffb76d33f01702d507e05210ad8487bf07e91fe25583a8108" + }, + { + "msg": "95", + "secret": "ca397e3535ef63bbf9de34bafc10bf39638eb7e33fef42d508747a8960574c18", + "signature": "51395032ed5558bb20d81f32c07eb890116013281192957802f8081c4373bdc70ba55af063e817e00c6891ad5287d232be474d385372663cc7b5e1a7790d420d" + }, + { + "msg": "828a", + "secret": "7362747987cd6fc2782f5ffc37467b36a949bcf7c915211d32a58a8105e1b7dd", + "signature": "15d9a5ab247ac93e0ef69bdaf6e94a12054f1d80abd919afb2e87499f1598e853c6e40707cc50546f3ccb4b3345f66ca8d5077adb13ee48f7eb7fd53592fe307" + }, + { + "msg": "715b1a", + "secret": "c04fbeab76614eae5c770886b3f8c93a62f7f1a1400ae8b25cffd74ec1c8ce48", + "signature": "d11c34ddef402522823b2e3abcba2b689ca52ea69d075747453c68524915df3acee345a38700395ec2d77d59c0d1b5d594af09acc30341ebebecf63263806602" + }, + { + "msg": "2e3eb5ba", + "secret": "906ce921888483e5717f3ce44fb90a17333cdd474345ef39b8e5b59157862999", + "signature": "98c002e1fa2e2473f6ab2660e1a4db4e641b5a910bafc67f57a7ab7808a6ffc00eb215ed31227d7f82454809fc65551f1ec714075f9aadd449b8d5fdf163ed09" + }, + { + "msg": "ef55e39330", + "secret": "7b7900fddb6b1559a8f1bc1b6a1e68af562300201709a95bccdab3a1d19a2cea", + "signature": "556cf6d9e15191741b2a6a213414180fb5355d752508052020aaf091db8839bc7e7f64aede395f7740857c31387c328e64a0f9977de28e70c5d2b01c3d5cd80e" + }, + { + "msg": "c94f96d9c36d", + "secret": "1f3be56578589aee2805d73b66a89cb05b557b4a219a306a4c251ebe8cfe98f0", + "signature": "39258bc55e2847f683490e43a8a815aaed8ccfaa2689a6976df257d0b90442ab8e3bf08835046a57cd88278a8739488c3f2dcc0f00c5924b8514e5c128912f0e" + }, + { + "msg": "2b5db387ca22e4", + "secret": "4b1b8a820c051473c82d609fbebcc62ef07492d9bab7c3e1af2911287cf373d4", + "signature": "64e4359507784f1fab477a16103d94cc0eeed69a326bc5d39f999a7b05e5be5d723da6bc737be1ec96410e1f4af2bf8390b9a64abf4bbdef9b2ecda12bc7760f" + }, + { + "msg": "86b340ee9ff1062e", + "secret": "4c99aa0bddae4720d9b9d3f609c6c96b29b8f130f55349e76202e83713c6ea5c", + "signature": "51797279f8208fa0a58b8efd1249f364aa94f96b1f9abee2312e7564eff0b142a10c62fd9b1683307598bad3b6816e799dd41db87eb06f630e9e0a07e2ef8b0e" + }, + { + "msg": "0e6fa0c7fb33910b43", + "secret": "412658bb90bff626f970210f002b85da4d5a57d21ccf3424d01ee7aaa5ef1f40", + "signature": "99bccd2c54e099f1ca64fc6b2cccf770ca8e1bd36e463b09939134450590751dae761d626ac0f0be9eb6688cbc938cb7c3bdfc88648a2b8d680be4854e55cf09" + }, + { + "msg": "0eca84252317c176842f", + "secret": "c8465766268fa7075924d32a784e9c5401049290c0b792f283c89b73f7812c58", + "signature": "342c73e5894b9970d36a54f1aa20c2d9d7e35533b9c9a9e526b8ae1a3004f724b68cb8bddafa01644e00eb44b55e8cb0a5f519698854fc114804838f47bc0f08" + }, + { + "msg": "3dc672063165fee87b210f", + "secret": "fc0c8e61d89ea2ec1b3484f34377d1fab75099325d16bd99595aebe2f3119e73", + "signature": "8c3630e8c0a0cb71474f940320a9785fa1d6bef5c57f0955d000aa6da97ddde7ddd414670bb74af2c20d6dd79153743315d830ef52f3e54b3a461f953b506c0d" + }, + { + "msg": "ca6cf782a1d1829b891fb08c", + "secret": "378bf5829684cf4ab75e901068a36ec42140f469158c19c76abedf07f1105743", + "signature": "d0f0fcb2da1d9c719e5220a020c2f1cb679225fbe6a3a2de94514112ad05deb52b2b2fb8e6788780356b3b0633253eee7addd8bd9aa448679bf5d73eaecc5209" + }, + { + "msg": "20214043a9a0c96613b916f76a", + "secret": "cacf0196a4a13245be33ccc846ac5837fd15a16d0f37d1333c55e79dbb3321d1", + "signature": "527c968a607106fb6eee7c2f9ad8372cc0824eed4e98ffac0b1a5c12fda58f9892d00adb920033a5b24f19102cdd07c93f2dae1aa65766b5ee8f52407f62c90d" + }, + { + "msg": "5cad140587d642d883c6f016177b", + "secret": "6014714cde61671984639187a856c4e3d1af9c3160b8cee9a1bb0ee8179264b9", + "signature": "1582d6c3eb0f8bbfbac443e3497f3b830be63b7269e07c5a12e24db9d047041eb84f723191ea5b207d1bff6fe24971ada4a739d387bca29d4e1ee67df086b302" + }, + { + "msg": "46fd983aa0e68c3c7055444a44e1b3", + "secret": "d27d0d674a7b89af80a1732c350991f5f6df709bc4f3d8b8d45ad31ac821e64e", + "signature": "22658b246b4032fae8411f0faf40006391193284ffee7229946965465e3c13c63ff939d8e631aaf81580d164e14014d9f55656975933526e301c5a0fb5fca10f" + }, + { + "msg": "f057e7a602e42806c3ae99a1e84e7bbb", + "secret": "06d24f20c4799b8bfb14740f58faf6ee2ec6c62bf4ec2c704f2e033cb698d7b8", + "signature": "c9514c37616a12a06ca1b0197183dbae62ac84bcdf3e28e1865b66f1bc71e35153edd8fe22498b2202bd98b857c3ab3b6cf7abfb41199a90ad66e01535320202" + }, + { + "msg": "75fb64579ad73047d611f8d75636b34a94", + "secret": "d6ea7f197b3ab202b2b5ec20c6dbe164cc074e107564e74035febf9b0e781451", + "signature": "ec3f26e245fe81ca8f264c7d28727e0280c39e9234cee5cd8ebe1dfec9c105eaccbfd09aa705a1133bab46c3bf97448d639e39d43caeb90d12c11c94c3733108" + }, + { + "msg": "50dd064a197fa5ec6faf88c9e0853ff26c6e", + "secret": "36e3ab98d9707b36df670a24d036d719c4fc0879188f4e12e0377956e10f9366", + "signature": "f12a74e957d02e077639187abb87aa6dea410d332478e8293c107eb370ada6dc30cb2b22e21be7b272b08f73bee727f86f825cc55eeba96b508ac42628a2db00" + }, + { + "msg": "655ae28d05b42fa1bffa4b49dfe2667898a003", + "secret": "9f7f9f81ffba51b119b7daa1edb65f35fe6030b37b59325af8e192febfbda15d", + "signature": "a618ee0708aa88380d7a3ffc0b1c897dcc58fbc7321f436740257242f71fd0370e37b0ac2d4869f775483616ae9f0becd12136f94b22ea3375d5d466f4083e05" + }, + { + "msg": "2e81045378482d5cde1331839c7a96cc2f7d178a", + "secret": "7e8d31e42229138bd9cd42498c249333431c3cdae1da2b836c16bd168845e3eb", + "signature": "808b3625afc01aa5efa24ae12b416ecd671c946e666f01fcdad9c499f4a307266e0622ae659632cd6c60fd5a8ef3ef9bb415a8f0ed1f1e57e5d5db5de9231803" + }, + { + "msg": "5032a7fd8965c879b80ef63afe838b9345d42085e6", + "secret": "485d73344925efc16ee233cbbcf01494472ff9735e3dae60d31fb79c01d0bfcd", + "signature": "9a93db7126b7df17d26677560ec4425a4b92e20ff436449879b3fa0f03dbcd6443cde8c88f89dc58dc22f9690d1544a87b7fc3e5384d97639c89bfba46f18008" + }, + { + "msg": "8e9675bf7fd7d0b006463ba284413f71401be341ecc7", + "secret": "c04163e216b561dfeb6c0b595be01d9bcf02f82c9149c8d47922dfdc05571abf", + "signature": "7e09f1329141f6c8f808954e63ab13e60cd8b4cfded761cb3e418e854bd4df07f6900385efb79d701c0e0c108c6fd620f13b9acf228fc1dc489ccf10e47be70e" + }, + { + "msg": "797602dbd67269f1299f5bcba576d336b6aec85bdda1ef", + "secret": "031789c76b54d41ce9708d92c390f103ddbed48ed303d79b11e8cbd6c3fd51d5", + "signature": "c1a620832f610664ddbcb02bec8ccb529afdb85f4d6a218cd8f5bba031008a5584d395d7051060b32f6cf2abe534f147048f05e3adeb62b175e7baf92cad0008" + }, + { + "msg": "7f2cee6e1a865d64b74606784b1d4946773b8b8730d57816", + "secret": "6ab579119045a31c7a6251433a08209342d90bf68ce208a978b92607928cc5c8", + "signature": "2ba2c38288bab092a68a9bcd52363f1292e08a78fa02b8f6ee67900849ae342ef250f6e7b669fcb16484c5b277a48acaaa9fcc78b7e5e7c34af7665600779509" + }, + { + "msg": "0e47bccd76bccb58de57960234fbfae391089bb949f36d82d0", + "secret": "692724dd8bccbe27fb007ffa85baba8e1c1641a1dced88c222e1d22d02179304", + "signature": "edc5d226107c1a6b6570c4be462395557d3ef65a1a761544b8bcfcb851eb74b6c9e787ee9b13bc723d297f445fdd02d7a640e079dee9fd05529c5874d4a88a05" + }, + { + "msg": "55da20a51582e10981042d2c35cd1321a08b3eed09f0a19401fd", + "secret": "df2e78ddba0fb729ebc4fc2044fc35371dcffa20ac135021d4ec2c1f2db1e6c1", + "signature": "98316215a056c999c0775ebc215f7cb2c6ed75bcd6b2829c82cd752853beead57f35a7fe38800e84dde148459b5e3b6d280f75f258b494ff01fcf7fa2371c800" + }, + { + "msg": "6a19b5041ce0cdbbaf2ccc252b202deba7f6a71a1cecdc51ef2aee", + "secret": "9864f612767dff80a0241b4fb56c7c625baf94c28c8026637dd2d3f7d3fc5573", + "signature": "9d9b5326283b0532ebdb5749acabc8948e29af7a2e80dca8b49d3bc26f5343e878eaf12babba5adf368db4f35677eb43794292d4037335455674d277d717fd0d" + }, + { + "msg": "bc745a47275a853b281d7fdc1c059eca512549af2c18a228437bcc04", + "secret": "78afe3ef521a2088d7a56adb5ba43eca4fe651965fe61fe7d3a6e5970fccaf61", + "signature": "d9e324b313521798fc7dd3a14c891c8020d193fbb1333bf49c79fcbe57646a48fb4b16b3095cfdf7c5ad8ecc579dcc9fd7adb1d05cad254b6d17a83072a15c08" + }, + { + "msg": "ec60035f58f56782d449f91200bf609823253e21975ae9d00cc10881a7", + "secret": "e783b58713de2b12a5ef4dea6b61fcac29012be1971e750bd283df935a7f7069", + "signature": "34c12faf8f6a56f6ad012d483e097da6a51a593b3938556f4636678e46e2d146d292537b8e775dbbedee366b8d1e2719dfeac6ea07082df57d65c3e7e2512a0d" + }, + { + "msg": "2147fe43f82ddc3cf962b88b2de52dc6b8ea7c29160ccb06e14d42fdb298", + "secret": "7ac8a03385998286d377f75597dc52e26345a2baeebccb47d18c3e57a5d30f6c", + "signature": "1fdd00ee6eebed1a929d3a7243bb57938be5805880fbaab12aa369f96f3ab74a3250ccd553aeca3264cb91258e81c1efe492b367c4d151301242777a96f3cb05" + }, + { + "msg": "5044b534715b70f6666fce3badabf7c6780d1862d2166b3d31b8adf1b9d7c9", + "secret": "943cda4bcc42b59fd8bbdca35fd6d193491d53f8ef90b9586aa45acbf512aed8", + "signature": "450c1de39da3ad136e96a0c68282477ae8ff2758d4cd906c939071748b0114791d3c2fbfa15f191a3669a35953a193340adcd11d86d47f32ebe569fb35a16008" + }, + { + "msg": "d3dc1f82be80467cc3e0c8c511335d16e82271835f7d4977fa2669fce8d9aec0", + "secret": "ac729ec4905ba157d23e6610eed6aa2f0446cb270258d3210ec9f154c37b374e", + "signature": "d2a31b63fdcbfc3b57aa5211e632ae8f39d728ec32a063bccdf89a32683d43bc5b8980bb9c23fc9674486de9bce337ce3efba49b0b25d8df3d1727d28c063308" + }, + { + "msg": "0d4ad4474e1adb9918c38bc10bcbf0887e5a660ab250597aa063ef1ac2dd50ba3f", + "secret": "9cef9c04420ba45c1a309326fb833173d254261ce96edcbfa17a20a4ec42a5ec", + "signature": "5f0004111ae819bd1c5c4d88cf223b61a1a62d97c88af9749c574485f1bd794800a99bf1ba715c2674cb459ba68e31092b850fa3e0193418fb5fc973b6f1da0f" + }, + { + "msg": "ff03abd4ea9e74abf17dc5603f6f1eb98562f1a49fbe4f7ecec385e2529f23e02ced", + "secret": "f10016c5c4a4d3643b3093e1778dfbb13338ad66dcdec310c37d0f842f28f668", + "signature": "2c2bf167e05db1372bd1f7689ab2f482d035160f6e7784dbff53a4eb017531e56ac9a7278d8109e7dac5d60650a58d4cac15ac6f2c405e1801a2514627da070a" + }, + { + "msg": "4a241aae0a7568b8e26256e38cbe15f700fa9ab7461550230649cfc6630c495febcb63", + "secret": "9d6c97933f01a446dd63ab7a3262c5773d394d45e90d77d25b59d15fa963564c", + "signature": "68f7bd28dfa0ba38bff4e9783b3293af1cbf47154897a1a8d5e576b1ee547d8fc9bf709a5c68145e7c7db419007292773523079a4a4d1c312cc4a7ae30903307" + }, + { + "msg": "513a6af466a81178f592ae0ebccda8b6acabfbae735c63b20faa57b7f2995baca34d5841", + "secret": "169966f59f4c7c0e27883e24ef79ad20fb6452bd4a2ad2ba0b4b19643629fc35", + "signature": "8a4aed7c95f1a6ca9167873878074bb6525eaae04b1cf98053efa2fd76e070f58c9a44517ec40d6c888919a74ada8743fad674d5cc0471900b5ce36a282ca10f" + }, + { + "msg": "b00a6c1eec3c750d1645a57bf9eddf494aa43ed666b69d4b8b8f3828bc972281b446171ff2", + "secret": "385b4f65e9e428233969c76134697f4c255993376254bc6ea1289f7ca1357e6d", + "signature": "37fb8005634cdfcadce7275a572013331f16ef39cb5c0f81236014dfa056d6d850d3213768988195f87319ba43612e2812c7fb8077b425498c784fe8aadce50f" + }, + { + "msg": "f4fdca909fbe5500494e34ad8e018fd07b1fe5b0ea09248e45ee0b347460ee845b0df8bf7ae9", + "secret": "e3317d186766b0b917ef38af522b01fa0a5be0970cd67c4514270e9243520d54", + "signature": "41a2cb393536b5c0b31cce44312f64824f5ad63e8006a733790127ededcddb993910b9178b2a048e524cdcc6adcae8ea390746ab8be4941309ab95253de2040e" + }, + { + "msg": "703176aecb5dfc64f2cc51086f20059bb2b1a3858aedc8406f34e41f6e0430853eef16c88566b8", + "secret": "22c60bd19ad06131fb3e1276da28e1d2e48284f91c84081d5ac889b43d8d337f", + "signature": "cbfa317cc757c98a09f6e0e1c38c389b59fb5861895d14208e6e35f2537aba5e118504f690f8e033241de3880ffcaf6bec76bf46eaef14bc7c97bd0ec9ed120b" + }, + { + "msg": "f3bef8440e52fd71ab00c74eafad5c8db5e85de1ad253bf8e91983e560727df92ec825155721d839", + "secret": "5cfff124190b1139b2725d457f10b76a902a5faa77aff97b34afd0e1019207f2", + "signature": "a0569112807894b4ffd87b6eab44e30eef847282b8858817bcd33ef3aa64d4e0aace33200b766b14fea47d4e0ac68531e8e4b907a37a1e4752181d417a3a8c06" + }, + { + "msg": "d2aab2d61aa063f1da2b81df1334457a75653463e70459a8a5928e3894055ece0af67aca436fdf96d4", + "secret": "3e41db85b3f75a718eca247abb0b8e49192a0fbd5da1fad6f05ce9a187905ea8", + "signature": "ec414a78385c1dcf6713e654c7f1ce21ba4e86f3d36fa1db4434379fd6f1e0e7d04cfe546308bc4e2aa548da8f33ce5d7b57006a176370c9574f8d5e8a7d670a" + }, + { + "msg": "8003fcccae8cea89a2d1743cddddbfc195d771e94e3a489adc652ef3628e7e59ab9ff6b1401ae6809fdf", + "secret": "a4c9afede9a1cc2c14176e023bdbab6c02bc8c42042ab3b5752c14b5ac2d97ac", + "signature": "a7ea34bc04aba74c7ba2c53f6321483d892f635e5968321f04a5f581004109bf8e77f02ad33aaa51b48d3e2f68eb71d80d09ec16b3c5f3f7dbdc90430e953f02" + }, + { + "msg": "79a06ef07dfa8f62a30d4c0d2f43681c3d45a5fde20ac78b24060ef855c760039fdad8fe6b06b16a205d05", + "secret": "3fc9ace4887e33425bcca94c80694beaa9d439e742c20ffaec8c811981c7db2f", + "signature": "bc5f6fa1a226a579184d006fd5ecb38834e57d68ab48f96f4a85ad03d9c1c7615cb1d56e75e7b110415ae136ee039471207395136b69b166659322658f55af01" + }, + { + "msg": "c6813445e3a75857eb3ce49302f2f96fdedbd0987f1ecff702adafd1782180b165d990dbb2284d1b91cc9b1b", + "secret": "d3b72f6ccac077b4c64204903908e34a5378a51754a9d879c960e3bf4e7ce24a", + "signature": "a3f31207bf5fe1a473362ae0b590f87a8ba69f1654cd6c008ee6e39a1e7bddff4a58b11a8fb52291ce6a0ec60246360e858908790b914c4ef38d57aab99bbe00" + }, + { + "msg": "022e8ceea7812f1c4981dd3adaf9bb5f48c94f9b1bae0b67a72c686656f41e0cc51d5e348154ee0605346c125b", + "secret": "c5e63ddd9e37830ae0a7a538dd631b34f3a11a7599d53ae0eb6ff540132a1380", + "signature": "00a94344a8c4fbc856cb1588454a2f69ab35708864885e49f21b4fd1d2df2ea84c2b37ae1f8f638c1cf6169c84943f48a23101d87389e63a780e299ff9c20e00" + }, + { + "msg": "2457ca4eac6c3baae944ef3ac6415bdf60872cc704e526ae83be0167211818400a7a79d739e8d5bf732a5f9b11b5", + "secret": "d2e54e32c4428ec2e58c8abc6b3b396991256c63d9207b43e5eb0c2857c80590", + "signature": "708343246b46c238886dff519f04376fd979e76b2e87ba64b6634924413e5660eb73fb95d2954657b1695da2565879bfe1c53b141fbb34268c1684cf267f9701" + }, + { + "msg": "20b8e01eec426b2337b8f2a89441b7f1fc9c38ee98bdacf24fa7c6f6dc6f8d0ea60ac6782f4dbf28bc085ae1e4267d", + "secret": "202a8a94ba2c24f2d48171bf86367ea5a7fa4a43faf70ea0f6f55f970a125595", + "signature": "5158ac5f594f2d84bda84afeef1d82c1fca10ce38618ce96eb66a5fc0729d5346cb08870636719c6d694944769cf4f1bcfb61a3562cef96a34ebf0f0d0e1c500" + }, + { + "msg": "f72dd28acc422443eae91065dc54e9215c13cc30831878f07110a10231e0272fbe377ca390446965fff315c86d8efc4e", + "secret": "79def281b7781ddbf85cf3a22e2b6f9a2b70799287b54e823516bb0f62c737a7", + "signature": "470456951a4051943f81b7491ee1509cc30da74f0e33e493fccc4e9206198e3f224ef27d82185a99215bfa461f52fd259b1817937148541c33f49a0d975dd503" + }, + { + "msg": "4f9bfc6d0726b96d4eb265d1a535eaa67ff43ff8b6f12e5e98424ca63a50d50e962b4b6a2e12954b49d89a77f30f1ab4d0", + "secret": "bc7c8e62731a566f79f3569dc1a245268fd2b97376b3ffda8f425de911f8fbaf", + "signature": "116a7a24e4c090de93318cd6773ed0f065e3ad4bc7974c96a91ceb51162d4b0122210be41d1996e071af83a1b755f5cb76791d83891313a38b47a41eeecf8604" + } +] diff --git a/rust/tw_keypair/tests/ed25519_blake2b_tests.rs b/rust/tw_keypair/tests/ed25519_blake2b_tests.rs new file mode 100644 index 00000000000..d6f1b29a821 --- /dev/null +++ b/rust/tw_keypair/tests/ed25519_blake2b_tests.rs @@ -0,0 +1,35 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde::Deserialize; +use tw_encoding::hex; +use tw_hash::{H256, H512}; +use tw_keypair::ed25519::blake2b::KeyPair; +use tw_keypair::traits::{SigningKeyTrait, VerifyingKeyTrait}; + +/// The tests were generated in C++ using the `trezor-crypto` library. +const ED25519_BLAKE2B_SIGN: &str = include_str!("ed25519_blake2b_sign.json"); + +#[derive(Deserialize)] +struct Ed255191SignTest { + secret: H256, + msg: String, + signature: H512, +} + +#[test] +fn test_ed25519_blake2b_sign_verify() { + let tests: Vec = serde_json::from_str(ED25519_BLAKE2B_SIGN).unwrap(); + for test in tests { + let msg = hex::decode(&test.msg).unwrap(); + + let keypair = KeyPair::try_from(test.secret.as_slice()).unwrap(); + let actual = keypair.sign(msg.clone()).unwrap(); + assert_eq!(actual.to_bytes(), test.signature); + + assert!(keypair.verify(actual, msg)); + } +} diff --git a/rust/tw_keypair/tests/ed25519_extended_cardano_priv_to_pub.json b/rust/tw_keypair/tests/ed25519_extended_cardano_priv_to_pub.json new file mode 100644 index 00000000000..72dfa59ce6e --- /dev/null +++ b/rust/tw_keypair/tests/ed25519_extended_cardano_priv_to_pub.json @@ -0,0 +1,2002 @@ +[ + { + "public": "a4db179c005d3d0906c67fba2af0b1b74af9d8c3856d4f47e7ef856e95ab906923b2cf101d0419123231f604cc80155614e5595257ef598fc94fd85637973c64b540f854650e5e32d0961a5bdb7c7ddf673006644514dea011ccee16e49f96a18d9decee025aab6deac1d9a227f28ada4f6bda9fc67c533e417388139d663f40", + "secret": "7608214e79fb386d3c48d96084f3af17684bda520feaee0c499961b28d7958cee5e1bd6cdfcce99e95a58a7234985e6c635974e757d45f04bda51c29de7e961623b2cf101d0419123231f604cc80155614e5595257ef598fc94fd85637973c64b1fc032a5d306bf973513f617868fefd019e089ec592a0b34ecf17c7a6f220cab697bc4963f5e6921d92ada9e67b9f1c4c0049995d38fbdc48b889ddc91df5e58d9decee025aab6deac1d9a227f28ada4f6bda9fc67c533e417388139d663f40" + }, + { + "public": "3c5d1dcb1aad445aa88982d5a3fef525000e8ccc30b977828a55bd957ada9935ba2bbd8cfd270d193cfca4372febb15fd19c4cee26f94e0fb1b3d4f7287f044d97adf0f1b8ca9e67e93cfa86e47b53c653f3ff85506caf0a3851dbf3e43f90a30fd81b0c1148436956d5a546d45628a44c3dd6121499fb58478ed5a3038d4611", + "secret": "079a15370bda7ffca3962280b883ff55e9fafdfab3e7dcd449cab8baed4af498a60ec734f0bf50952d5b44d4edec0b91d53ac0e4b40768ce0d3b5f1f3227619dba2bbd8cfd270d193cfca4372febb15fd19c4cee26f94e0fb1b3d4f7287f044d4f53b7f9fbaa3ff066eacddfe19b1b81eb1d0dd65fde1fbe54a4b3c71d21951110da06966fbf2805dcc41d48e2265dd60bf45bdf1ed43a7c8f10d64bb83840680fd81b0c1148436956d5a546d45628a44c3dd6121499fb58478ed5a3038d4611" + }, + { + "public": "76bcd96d0f2f0252046da32595883b99d66355c26e187f27c9954091123fa86747121ff8e1f7776d14e33efc86ca809236b1b74e9ae7d43d5ab94151a71f7e50606060f81b13c893510f56b6a9f9f8930b2fa99a7ad212939b0f7482ceaad48872fd21da6fda1faeb5b58f290de3806fc0b0a78f080f4703747d5678d713f587", + "secret": "253b79c87603aa98f84f1e3971e7343d24279e4a3ae2f1dfc16792d6bfad1db1960fcbac421d3b0e40abd4411aaaeb8dafd63e42cd7425d8ede60bda0685870347121ff8e1f7776d14e33efc86ca809236b1b74e9ae7d43d5ab94151a71f7e508cf878169ce4ba8e1b5d528b2b0ef7e102a11414d69913c2a71f2b68ea708a19a61283b962d6fdd1920374cd8e6799b8be732dfc4c0c692b956953fdd8389bb972fd21da6fda1faeb5b58f290de3806fc0b0a78f080f4703747d5678d713f587" + }, + { + "public": "2f9ba2baadf9a8d71b5501d2d0863dded91d4dd603e2c52f82ba7f431ca07fd34728b8ce2bb8cc1dd7ac8b65b9df480e389fb4f9add088a54f4f973e373cc179eec81d7c8bec4070d3d32f59c597cafe37016782f74a4cf16227ab1db1a0ea90374d9c0bbcd257b6862c4cf7d45f4eb4268f480912a516d5e0182175bac9d7c5", + "secret": "ec8fedbbc03717de45108abdf0692ccd1ded071bc665c1412225afeb893447a9bbe0bcb48809634bbe7f46a992d008f01d14ad9e7cf2fa1b46760b82983a28c54728b8ce2bb8cc1dd7ac8b65b9df480e389fb4f9add088a54f4f973e373cc17992fd82917030497d9946de8cbe340989650bb93a3894c7cd44b933c40005f4f2ef5e3485be2e0955b17bd1ec40dd1240fd643fe9051432c0ee19615e9df5bf65374d9c0bbcd257b6862c4cf7d45f4eb4268f480912a516d5e0182175bac9d7c5" + }, + { + "public": "d1a159d151aec2ff1228a1be62164bbd2f471d53051baacde86f44807a119d727bb9a0c01ba4e6bbb8fa78385507457e76067648f6d84b7c971fb03d388088923c773d97de236741129a464f72192461df8badada24bff1373233cd072003297c849f04f0ad8cddf49873291935f7c3180bbbf6d0366e4c1d2ef7c89db18b209", + "secret": "8b1010f9e926ffe7ab4f88a40b40c02e940df96d7990ae8776fa223202a3abe5966de461596b65fa23f80dbe1294721d340b319e3339095908140c981be0ccef7bb9a0c01ba4e6bbb8fa78385507457e76067648f6d84b7c971fb03d388088928765d2aeba3b2da4f2d21d739a01fcb67212c0e039248afc8e4d1374c976f064dd5bdde7eda17b7b26e55a7affc3cd085612c87d111f61cbc6e401ab109990a3c849f04f0ad8cddf49873291935f7c3180bbbf6d0366e4c1d2ef7c89db18b209" + }, + { + "public": "9cb0af351ffbe327aedfddc00cb6b01ba289e88fe69d8834e1be93b2991375acedc9e7be7a8388c83384b6ae25b2acea01a85909f40b5917e901254903b6ba056f159d3ae4081ff7faa3943d991d616b1daa028b9f0a048a553cbb32b2683495867a60af436d78294f9484b5850cce58c1e277a42f23203a325ed5665947d1c8", + "secret": "fe2886d526c3c2bd0bb5d64d3afe4a38529fe02b954858540738fbdc36d14fd3ad8151e971a3476f609d2de2dfb8b9ca0d53bd4f56c88f361005a830e56fafecedc9e7be7a8388c83384b6ae25b2acea01a85909f40b5917e901254903b6ba059512b7fe3cf0ee4a2d5dd5b397bf117f714abc2ff7d67a5e101accd477cab07c13855fb4eb9bcd8dd517aea3fce10cbda2dc6405ea508e86eb985fe8cbbfeaae867a60af436d78294f9484b5850cce58c1e277a42f23203a325ed5665947d1c8" + }, + { + "public": "ffeedf9ee09e6a8b6ba4c512f3ccc2b37146226953fb7a96fb4cc563331856cc1c46787c9f71a9b58af5e7175cdbd7c32e3bcc460d6d6ac5a84dfef5c4ffa116485f8ccf83fe0b02197b87db8758c113258c07772b52b6fde789fc2f46431e51b7203ca9aea19f573d4352d4717d33072d28ef5816333a48882d9da2ca9b52ac", + "secret": "999307b88fd8877a5cc242fb92ca6043e795fffac0e5bdc03b0f1a8ffbd0813f79ef180d35589a084a11a53cf9ee45b213c4fd185dcd5b3d64d06be6663d0a7b1c46787c9f71a9b58af5e7175cdbd7c32e3bcc460d6d6ac5a84dfef5c4ffa1163dcb9a92fe73fb3f11c773bff7f7c9b79ed15ee19a9b2e947dc63a0b49e5e44e1e9be37355ac800340b44dfe8d47583c1c193d8fe62520c123e01e30a3ba6204b7203ca9aea19f573d4352d4717d33072d28ef5816333a48882d9da2ca9b52ac" + }, + { + "public": "5133b7c2815fca37ee132fb2294e2c61629bcfaac3bbf73af8a19106eef9d2edfe80c56e01c72325614bcf2c99a0124181669b8e0f0ebce8cb7716fa70efa67bea768ad0e46b5ec25692ee22669f7c974258ce2bf1a455669cf0365cdad5be3be9abcd6bcda70eeb633d8ef8c0ee588fea450869aa96de917ca636ca4d8d9f2d", + "secret": "86c27964f7ef60c847624b2e0ba25a22b5683e2ff2a4ea414f3c995888aff8b5d430a6a7d14826506dc8d63797677b9dd42d154b19ee311c2f429c0dd6ce2ffffe80c56e01c72325614bcf2c99a0124181669b8e0f0ebce8cb7716fa70efa67bc69740a9d95246a565b2e10ee7dc08faaac49556dc427a675743d087f3f5a0923c9ba780e1728f138c645069c2a907e73155986f04e9b5d239a1e8e335728dc4e9abcd6bcda70eeb633d8ef8c0ee588fea450869aa96de917ca636ca4d8d9f2d" + }, + { + "public": "2d4ac77fa910fc3ccb69bb9f02a17e431ad3ff36f388a6b43714b3c7e6ca7aea57ab1f6ad1db8faf44cdeabaf9e1dec51ecdf32c1d22bfe926765fb410e467d5bedbbbacc12dde7eb384792ab08e22d6d665b1d2f06be7e15817cdb3dc04f45c717c3fb588db754f39ec9ddf2dd6e12c753a78b16020188c9bdabec1aef54975", + "secret": "609094888b49a3c54f2dd390e6f47e2ec7bf43dc85a0598f054bd4396eaf5d13c82f9cd1d03bf0ec10fda23639338283f6b7d5c179bb8fe0a20f7b9f4b10a43657ab1f6ad1db8faf44cdeabaf9e1dec51ecdf32c1d22bfe926765fb410e467d555f1a4303be749cdf69d5bd7b52cabbacfb2ad8ec2bc0b795e0f6ed4055d3b7eebe03cfe4f6ab0aa486b4da592b833d57d75395ddbc016972a714507a29894dc717c3fb588db754f39ec9ddf2dd6e12c753a78b16020188c9bdabec1aef54975" + }, + { + "public": "4141541284b02fc2996fb4811b79e4a4b6ea1e969fdaf21fc27de4bec0847f1f1bfd6d7ab71faf9047dfd0e63ca5a3f10b4a1e5365a83726266d24223997b1dfb6e0269eaf8d39fd593926f6f9e2d74af017985fc34b060f91eb61f48c6d3820255dbaddf3c6cac8c2dc4d566cb78f9a0fb0d9997df930cc8cb8bdb9c51c1e6a", + "secret": "2a67660f17b088515cc8816d80250b727523dead57b548d053ddd76430b438274b123e7999866c18870221892f7f05a5446c23db0b2e8484755e92b4caffe54b1bfd6d7ab71faf9047dfd0e63ca5a3f10b4a1e5365a83726266d24223997b1dfad2760eb7ce9a6efd9decbe2fa1cdceef70b54bce2dce6762aa7e13555984ed5694004ba203ef5ebd168c3325133ce61a9e3c4af1cf28d941d075c79508fdf0e255dbaddf3c6cac8c2dc4d566cb78f9a0fb0d9997df930cc8cb8bdb9c51c1e6a" + }, + { + "public": "b2ab866497d7307b5f466ace43b235f2117ab208dee2c8108742f51e874bf56723a267599e520726d2ce9482c8da43e5fc971d16e3e420c9b66be59e307296d798df2ae0af88b0ba957c49fc8cc35b5d7571d41586b55b9aa60b44167baaf069563d4f95bca71b6412518a0009420a1fea3a843d19ec01af4095c84cc79bd40f", + "secret": "a1ab8deb033197623bd15e29abbe454da1cc62ba05060d17197e4e3f5c5513c98068ba6237ff64093f4bd3f9444cc4439b22a3fcc9f19653b1d6e5a145cc773e23a267599e520726d2ce9482c8da43e5fc971d16e3e420c9b66be59e307296d719deca3c6f07f094acc037e345d5628476ef6802ca66d4ff4a74c00fd847a87ac687af64dc63a48bd1b3e5fe74ab6630637db2a1e9a531293a3143a8ba8d6b91563d4f95bca71b6412518a0009420a1fea3a843d19ec01af4095c84cc79bd40f" + }, + { + "public": "9be81c1e9125d942a6de2be35ab74425fa5402e92610cc66562215db0acbc42df7d22c4ab153074a21be9ac4d2a588927139135d781401125323cc3334c4fe8074873677ce2d546d1050696fb301020ee12f8637b1d2ffa60c2d83f486ddd35732fcfc7bfec0f7b40f19d955dafc73e6b11da982c4d1b0e86ccd71bde3d7934f", + "secret": "45b6df9e20e35ba729ca7ab132da9f3d869ba835e3538b741f6ff4fcbb95313c31dc718aadd379b65d14b1e33f7bf4532f00dc1299c3c739d5743aa832218ef5f7d22c4ab153074a21be9ac4d2a588927139135d781401125323cc3334c4fe800b1be8e3d1e4cb49cd5210fb074b0015d4c201e6b8857b679bcd6f3b4030d2b7a3d0f2d8bc284b5af8f3968b00ec3b6810de49df60b220659d24e6c1c3d3481732fcfc7bfec0f7b40f19d955dafc73e6b11da982c4d1b0e86ccd71bde3d7934f" + }, + { + "public": "02b5b6b07a4c1fc63299e2d9fb4cecd7447b652fa4dd4bd0c5513a731dad841b4ce355e694a25f83c84074c7fd7904876abc860e75716d88d86bed471a8df5851a252c893679eceac2c91c98ec476d921d239b56377348e86cf3f47b2dc320197ac7326b2c2529928de702cc8326b67fcda4f997d68a904a248a67199d6d2663", + "secret": "11ea783d020705cbdbb4b5c79657057ac46fea97bf53f706e75572e6e875be4ef2812830023ccd95e881ee84226e128e2f49c94661e62dadede3e41114236f714ce355e694a25f83c84074c7fd7904876abc860e75716d88d86bed471a8df585c30b8bec8b8939e5addb191d2018ed0be07785e36b78eb29c0a2207b76b28a93f5db7f73936ad2eb480674fb48f1deedbf56ec079c368679dc1b525b79886c5d7ac7326b2c2529928de702cc8326b67fcda4f997d68a904a248a67199d6d2663" + }, + { + "public": "97b038f242449e321a338f689b8a31ea21f38a4e020132b61632e8124d92a6f4c2d3603cd357392d309b0751a9b4474ec80de38d41255b0fc93e47c9ca798b4b3d9f0720e903c825022cc03e862208302d0cb2aa79481b0b71572bd5975e7e07072525b013690baaf1c92197be78bf831fd8a9ebbb28302bc0f334223dcd04a0", + "secret": "c06ee098bd7476d9703cbfe9a7aa010efd502e5f9a5d8e3f057ccf5035d485ffa8a38a92d91d002ca5925af9f77144579e7d5768c6b5dad27601e19c5db7f35ec2d3603cd357392d309b0751a9b4474ec80de38d41255b0fc93e47c9ca798b4b2a4c3b83f3f1af450b0bcc70634e44a1324f799fcdf705b5a52524bad812673d4da293ad5b7d46c9a0538490ca1661ebfc9c6fd1a36ce740c62b3bc51be2d5ae072525b013690baaf1c92197be78bf831fd8a9ebbb28302bc0f334223dcd04a0" + }, + { + "public": "990337d9d1048db27e4223a711b6cf76087b28d5ba01b446c1053eeda25cbf0c5ed5d37addf8f604b1063936a7a87d1cfb18c43fcce0eacdcf11bb0650fcbb013886b2bf35b1b74a9048bd4f4f7a9c5481783bb65b7c6c5407bddc6b6d6e3c0ba05f2082070d1b8d14411c14f1453088dc2720335cc87786fcdb6135eb978c5e", + "secret": "b8cfe20c037dd8301fd5762edd1a07a6cfb7ee0bf143bb3a123c6172274450563101bbc1bc36d63d3da6a4a8bc213540cdbcb6607286186e1b8a7b09bbb70c035ed5d37addf8f604b1063936a7a87d1cfb18c43fcce0eacdcf11bb0650fcbb0157efc45dd0961e9cf4260e4daeb748fe2a8642f355c1ff42e8674422dcf4a92f452ee93a9f0474b2a40ea8d351c9c5db52b40a3f314ab77ba407e10d12175d6aa05f2082070d1b8d14411c14f1453088dc2720335cc87786fcdb6135eb978c5e" + }, + { + "public": "2e131d6c4a1d372a5408c8becc850a1a2e40c3a08fe543ab44ff6ae0be9177551e573204639e419891b4b2c8d50eefb41b31c983a66cecde960b158975a859464c29b8f8963ee4f154e82bf45aec0fc4b0f9faf197ab24e5120785434852553c0bc91f94c512d6b5c754e3603b57ea10e17e5423a40950955bf6d2bccc48314a", + "secret": "40afd4e82a58bc8ee7742e9d5cd91f49268f63905d50d5a8f77f178b21b366559a2a48f565bd66f96fb8121daf2a5854c53a96d89b9311eb8c18ca71bbde7ad21e573204639e419891b4b2c8d50eefb41b31c983a66cecde960b158975a859463355f8cd196b2343e692eefe8a9299518ebabe4f2d9ea39db40b559d6097fdd8593c7d0d0334a2aa7db8515d6681ff8bad1345b2b24b2028fb4e3c0a22a84ee20bc91f94c512d6b5c754e3603b57ea10e17e5423a40950955bf6d2bccc48314a" + }, + { + "public": "0d27537d7fdb697503c65097b82366ccc1f58d09b8ceb9800187d42d60e118ea120fccad0fae858f9363d17841fc6b26a81bb874a4f150ed107ec8ec65787b5bbf229fa51d4710d87c8454218e91f305e1d52b707c4953039ff78c2768132f86cdbff438d4680cf77b3ed9bdde0d08b854bfe9b21a2fbf78e0b32cd617796a0d", + "secret": "abc2d745f632b2ea59546d439e5b3626d2247a4cfd561cfebce89e31cda36f7834697465ca16b80d077ba9bd1af908966eda2ad1d67bfb44ac64d10f935be7d6120fccad0fae858f9363d17841fc6b26a81bb874a4f150ed107ec8ec65787b5b765d99e46bb811edd0e54875a4b26de9c6b6aff63c1587a4dcaaa962ff41f858e20530895201d8e56e49e8403285706b122e0a6958b1f90198abc365d6b07f20cdbff438d4680cf77b3ed9bdde0d08b854bfe9b21a2fbf78e0b32cd617796a0d" + }, + { + "public": "0bcb4c0034088a7ce210a9c8d3ac4ec8ca69bd1544c6e98c8d87d8ef3e0aff86b2c7128b5578acf5a1465a2eb3e00f3b50c7309ddde65b7d8f0cca903a6042e2c57b7a0553c9b04788e28f70c9c8a493a830c8ed4079666bc130f2bddf9f94f8a2f7cc426cb4c9ed786b875bd9f216245d408fabcb74bab4e9b69a5d3fef50db", + "secret": "f73815dd273a341fcf5d57de8c17b71978f55a397b89192b1e84f0bf55d25761fe343a25ec7ed424ccf13d68ff2e81f083df6cbf7031b61db3c5017e1f2fd384b2c7128b5578acf5a1465a2eb3e00f3b50c7309ddde65b7d8f0cca903a6042e2eb9c6e40628839ca499ae54585fb1dba308b3570da69690a06b9af2bcb7865c5584a23560c720e60489d2b5756617044463f99a050bf83ae2267bd3710f1ee05a2f7cc426cb4c9ed786b875bd9f216245d408fabcb74bab4e9b69a5d3fef50db" + }, + { + "public": "344a831d6df5ca85abe3fe790fc4d093473bc49ccff0342ca46a95b4fada96950f4ce53d7530c5865faab0ce900579beb5952194c7f4de061734add0edefe4591e5ede2809b45d478c93f513abc1e3ce6a9b7ff0892b168c905252ce8dc0f945c7a642bf10b2bc63e7d9f988da01aebcfb8d239fcdf0ebde62bef9e7a487af28", + "secret": "18695ce7d39a71063a15c90c4f80373da9b4fd0f153613a31216b02bd28b9ca06ae4108523aa63938ed811c5e4e7b2001490d9879bbfe1bfe9df27cb027103740f4ce53d7530c5865faab0ce900579beb5952194c7f4de061734add0edefe4590f95a7fb01a2df7ba52850c1a7e3ddd4cbc0fb6ff91d2332c51ae2037f14f2e421313d160ca5cb2eb4fce1be1b9cceb5ff20f7372d898578b35352abc096ba8ec7a642bf10b2bc63e7d9f988da01aebcfb8d239fcdf0ebde62bef9e7a487af28" + }, + { + "public": "720afd999e9f16c983c1aeaf2f6a5e1fda4eab529337ef5c24e92819f4a3a70a38a0f6a15ccffbee8a53cea5d2cc6d08a41d2412e14b7d09e2395f6e1ade8d7d4921616aae0fbe7952f8f1ac09ee22dc89cd358e6d1e02b60509e03571da1811edc3551909b88b46bee950f461d90412b1500fabdacb5e286428386e9da995a1", + "secret": "3314c0749f7ef58b1e283711b870b5ea8a56523a5fdc268bade23ef4e201d95f52e46e4bf0e64e9c91ab661a3c142453d3a66788ff392068cffdf84a9ddc2d5738a0f6a15ccffbee8a53cea5d2cc6d08a41d2412e14b7d09e2395f6e1ade8d7df53955b47285a9d164cafa1548ca6537892c767a29be8e8b9e70468c976fdcc20de05a999b1531d2292c28de6d722918fc1d1737f9279063d14ceaa9f420421eedc3551909b88b46bee950f461d90412b1500fabdacb5e286428386e9da995a1" + }, + { + "public": "31e8d01fc6ce7b74e622a456d64821c8fccf7ac9f943aaa2a9203c89bbb9446a0f3c706e57ef54602c16ffef63ac872e67af43796020a2fbd701838b0af7521f089552b19758c6ab7fff56d7ebc0ddfdba79708fa2dcf406d64977f556433b8e4ca384ade31c9507ba64ab9c4fdd7f8f5fcfa74a1deed78bcd8821f31ed60e8b", + "secret": "c9f9b3bb3b7f3072051cd1b33aea7ef72dcb105ce5e393295a70bc376375812c9735832f61606918a9de7dbafcb76862f737e0bb00f66b119902d06fbe06b8d80f3c706e57ef54602c16ffef63ac872e67af43796020a2fbd701838b0af7521fa3223432e2cb9e514d70a4f7ae20c43d1bde03aa1c32f0bcc5dfd15b092b8eda2a074e005b21e6b1043c9b648a3674f4f76d1bb542c9648a3e1502a8bc56b7de4ca384ade31c9507ba64ab9c4fdd7f8f5fcfa74a1deed78bcd8821f31ed60e8b" + }, + { + "public": "ed95592bd61d79635a4daca366d8dbb0fbae04447c0ad686cff205867c1ada2a6fbea3f11d74528ebbabdea1c4792ba400e810c33d8447476d319c0c44133ec01108386adee6a0f986aa10fab96e5d6d8900423474d7ee726d578a5349ca15574fe2eda1766de28c7942f79278e2666348c14aa51b976ad1582b611377d11ee5", + "secret": "67dd95a9c409e9c9c32a88a74bdae36faa7bece273ec19ef1e23874799cebb43f8cb528777e90628da5e6f3c4208829acc22fe43ee716c6f6b43585ab8b96c446fbea3f11d74528ebbabdea1c4792ba400e810c33d8447476d319c0c44133ec0dd790d46c94943f04203b9755fb9b850cb715d265d369cefb30b9276b849b5a1d6859ef966cb82623cab552f483917629903c8d9788dd63c90998a079f79ae2e4fe2eda1766de28c7942f79278e2666348c14aa51b976ad1582b611377d11ee5" + }, + { + "public": "807e02d6fd3929a0bcbc09518a7c50a33138d8d12a677e26c610523c12483b6669284ef09bce3555df4d935913ac12d49d7074264eb922e7491cb2ad06ee66558ae4abda01a73d3dc68b59863e55d47b658cc80e3b6413d9240690d1e2f3bb308074284680b4b8b4cfb11d5b6e0ee5ab849fb51b492edc8ff5bccde4ef68e5da", + "secret": "5e12b1f2a1ae0bd257a2725dcb85645222469361cad14763128cf7beab71e9d95d58db52929d571b45e4b8b616a9d96bc30b626b7e417b5c9273aafa787929b769284ef09bce3555df4d935913ac12d49d7074264eb922e7491cb2ad06ee66554e38800b6f5682ae2f5ddb2330dda620c371461278891116caa1aba5d69044390fe1e4ff4da096a6e2a6ef137d271ddc40fbde753ae6deb3b55a89c5023c79a18074284680b4b8b4cfb11d5b6e0ee5ab849fb51b492edc8ff5bccde4ef68e5da" + }, + { + "public": "a6e1882a92572bb33c00d0fcb0ae586a1dca09368507450be37814ef0bc9b6b1430b8948700785a383efd728a4bcf32441bd97a2ba6286dd3c3347a98e4bf695f115786883706a6be5998f39a11a67ed32d1d3b2a5757b9ce24c2affc71e650073a1124df5dbc6c895afdea52054924f70a6cc70093d9759066aeba6483e2b53", + "secret": "d5e3f79cbb05759ed6b64dd6309d792cfadfdf3d42dc39aaade6c048d93a362d756459a973c4ae7a45c16b6ddfa7006902dcfd1af8204e4652772b6706769896430b8948700785a383efd728a4bcf32441bd97a2ba6286dd3c3347a98e4bf6958bcf69ba9a3a0a96ffa76956a6510a31367073ca2d426074997d4920fd3ce612e36adf5e76b8fdba5573bbcd236e23db013336ffb3bc2480f1a06cba78e4132773a1124df5dbc6c895afdea52054924f70a6cc70093d9759066aeba6483e2b53" + }, + { + "public": "734c62c6d2de5430d58ca1dcb31da7e8cbd7c6aa3fdfdbe8ee3056638080182b85f1c687e18ffc6f39865621debbed66d33bb5ac226d2d10f917614a9cc299c56b7de95eab38e49c1a666705a86015e578d89c7d65f5b8cd0761394e6cd18d4c8964e4ee7377268ea670371d0111fa6f1e289c9db096ba1a8a6093ae4694573f", + "secret": "ca9a817d47de153026782d4d806d4e3d60afeba01b1997efc99c91f63da725503e965ceb83d48b84afe9368d2cefadcb8a1d6bac766566c2a88665cc88fa249f85f1c687e18ffc6f39865621debbed66d33bb5ac226d2d10f917614a9cc299c5b6a43c8f55fe3c8fa2b938d9e7241796bb6ca63bf2afa941a8a6177da52c0a70118376267880dad955d8a79ef81d2841b810f5d263bbdbb22f1ec91ac9b491b28964e4ee7377268ea670371d0111fa6f1e289c9db096ba1a8a6093ae4694573f" + }, + { + "public": "b2d16a70e1170704e24b8fd80826e73a1812a4c66b2db1557486541a6952aacba357a360fab2d57895eb262e31c1632008376e8a7bd3c657e3912023c48370f8895f2b12468414f76993b1d8e3d0ca23d6823967843b12540e2f5b8ed36447839fc8b75c50e82fb04234b05b9ee19147bb45ebc823ca96cabe76b81fc31ea15a", + "secret": "01118ef8e6b5d19a63e3a9364cfdd225fde83d389bc9bd75466fdfe843c362d5a496591b7de884914fb78c8760c1b25380b8e7e8345458400f5657a0bd0235d9a357a360fab2d57895eb262e31c1632008376e8a7bd3c657e3912023c48370f886b6b6c821bae1839eaf13fdad1e55703ea437a2e2a0e76e530066fe01a39568d2f1d59f55a08c3e98fbd22ceb27856d85e9ae5d2c743574ff57fad6877e6e259fc8b75c50e82fb04234b05b9ee19147bb45ebc823ca96cabe76b81fc31ea15a" + }, + { + "public": "4f89c2d2b5bab4b26853f720151bc00e827d7102ff9c4cb5a357c5d36ae8e3c9447aa5bd0f722f366acce40c299f50e6b353a39bf3d730b36091043e5e76ef431120d0b0fe8a6262959fffd3ef81dc5d16002766c150eb23bc3fb314a94fbbf2bfddb89dfb2bc479252c40fc759798e66c135bd8299cf324e6802498e62d4cdd", + "secret": "ae047f4614a1fef5ffbb9482f6bb20d195157c53c278d4ecb78ca577cf4fb2b94034689437b68498482d006cfb33a943b1dd9e5397255aacfc4fae62dc1d19db447aa5bd0f722f366acce40c299f50e6b353a39bf3d730b36091043e5e76ef436346cd43879e8d2c4783d913848073c791e5512cc1da9ddf38461fc1aa8505ca6af1c51e5449b7d256c16815b4e36ae64ca637708c549418e245a644ebdcb9e8bfddb89dfb2bc479252c40fc759798e66c135bd8299cf324e6802498e62d4cdd" + }, + { + "public": "6a5bbb3799f9e79abd6c290683b7e1962bb2bee6311ff4b0940137932d7d8ba9ccfc26d939380856f9c0b9aad904e7108f39286489da38d904196354ef6a28e8f6a6197ac2949f44c3654061f6dd54ac8da216ec0b178fc27e8a05bcc43aadfce0719fd94c5ca73b538bccaf6773ad6ff3344e22f1f962853b970d46ee5f78e3", + "secret": "75046d7dde06b6f58cb0b6d10259f91f753d03230af3930f0ffee0ce0d9cb7607bad4f19300dd29515c718d9197a808b8c94cad5258472d63bf6f167e4a457a0ccfc26d939380856f9c0b9aad904e7108f39286489da38d904196354ef6a28e856d40b25a519f1fdca25ea8480a1dcce18d9608483422a4260f183d470a765d4d6426ebdf569560040843569133310dd355502b035ecb6c0f4def984ef0b42dde0719fd94c5ca73b538bccaf6773ad6ff3344e22f1f962853b970d46ee5f78e3" + }, + { + "public": "3ed7d7a55b2e7b7801aba7fe333c31fd522811f5adac3ccd674c1b532d37784e01f963ff16710d68543fd243efaee4e8d269a6957f8aac112f328b3a48f81c4dbf94cea5779ee5bcbaf595a30519a3649c3bbafeb36ea906290575166c45da9da0e7fd0063abfd6e662f0d6de79c3b7acba865b4648bf32bd831dd5d346c5324", + "secret": "650fc35f234b22bb3f711cfa8165306130831526e7232a27691071ab3b81c093e094dd17df93247527cf3b47a8d24779114f2a049ef87e1f8643a782200bad9d01f963ff16710d68543fd243efaee4e8d269a6957f8aac112f328b3a48f81c4dbf44922498b16f8e5497b74fdde67a5d2c2fb969056ef1842e810b7d70f3a11ccfb9659437f141813f4df9f146251540f71f06778de98463500751bbd25858c1a0e7fd0063abfd6e662f0d6de79c3b7acba865b4648bf32bd831dd5d346c5324" + }, + { + "public": "858c3834be411da5907e2601666b47060cd5c4c7b72e628d14ff2cf7ec1a80a5efc97df8fd7badcc80e7672554ec8dd90cee1cd33be87ec5fff574f5da060e4b9dd9e10687ce6dca78c311b015a639cdd1cce8f85a9f141612f1f5d6b5a8fe6e83243b093c089442620387803808a4964bcfb2d17130fcf6bf4c02183fdd677d", + "secret": "f14a905bb7924823fd8cf867a3b84cb5603e26aa64266371bda3194aab99e9a199d82332fa773000c3b8aa886c279ef0b296f09d6bb563949acc62025eaa5e21efc97df8fd7badcc80e7672554ec8dd90cee1cd33be87ec5fff574f5da060e4b4038022484f2b96163187bd1a463a743185e6182bd31695f1bc28d3878f715b289d5ae4428ba023cae8483d0901b136ea5bd5e68653955b7f06712647aa79f9283243b093c089442620387803808a4964bcfb2d17130fcf6bf4c02183fdd677d" + }, + { + "public": "02cc5f260b604e78243bcee01cbff8124249060b1a5d0954fb7e2c7fe1011b8d5ca8d8c28c4beae00aea6a150acda7e02d7024b5fcaf6300b744577c36b9739f4362729ed9f614890b02c1760f82b431411dbf13ec82c2189788d9b085ea8c71dc2739f02c4355d255865be9c516ad035ea0bdea1094489c5da0c39de493c4ea", + "secret": "065bae455d2ff5250a5066572713818a5bd80e314d428c8923e3ca6ee8e7ce4b85ae4b39ec8ce2dda2e65160ae151eecb6e20241c4ccd5c573e7331a3106c3e65ca8d8c28c4beae00aea6a150acda7e02d7024b5fcaf6300b744577c36b9739fa1886170504172850b1e00290af7cbc804c2a1862287772cf4c30148cd8bd54048a7aef8fffa0d0b5af58faf384c7ade4c2ff4fd11c9e36868fe2831cad2c35cdc2739f02c4355d255865be9c516ad035ea0bdea1094489c5da0c39de493c4ea" + }, + { + "public": "f725bfdf52eef39d77c5d6023c5dad79dd859203430b5c22e110de7b4e7e7f4b31d9f056c4d77d9daf77967f8eb6faee870db4fd9f6c69a12bd307d97571d9123302ac8f8725f6e0741cfab41c9eb61edbe93b95462e7212e6009d08bcc184e04a0857b8a425c13572defd885a7dcf8b6540e903e40dc8e6ec7672110035feb9", + "secret": "0973adb948e73a7371e12c8af6589977c22dd7f777d6b2433b3626e9b3a88c263dabcda6ee43421738b7ddd0ea5905b870c197e0008f1eba148a6d03f9f4ef1d31d9f056c4d77d9daf77967f8eb6faee870db4fd9f6c69a12bd307d97571d912207b1ec2195ce7e116b0f7cd54b12d8de3e71584a048c33533f5d137d7f1c8c0323ca48da8798570f05d641235b0df3aa0f301790d555e671657dfb3fa6eca3a4a0857b8a425c13572defd885a7dcf8b6540e903e40dc8e6ec7672110035feb9" + }, + { + "public": "401468112093b3824f9aba781246308a53c09e78cda248de622b6e285ef3ebf499a27002f29a85cc397ffff55d0ce80787763bda801e42ce92a2c1763c091ad6b7febcf35fce5eb37a8fd9677f9e1e6f127dbb3c8ff303ac6b949970750bda8877b2a67b9495d9a31a3d0dd7d93752edf9115ba6f6921625f111c329ca1a937a", + "secret": "fc34eef21f9e31a88abb50a643a4ff2d97946800074768a2a90fe7362065499ec9073f8388684e7feff5918972c5a390871af233a73c021d368eb5fa690e6fab99a27002f29a85cc397ffff55d0ce80787763bda801e42ce92a2c1763c091ad626ed47bc724af69966ed8e5fd57d646272f00b5f53d30f466c27157939f822a48fb0a1a27b4a9cf15592f59c5c58ebb27bfe5e02ae555d056bd794a62598bce877b2a67b9495d9a31a3d0dd7d93752edf9115ba6f6921625f111c329ca1a937a" + }, + { + "public": "19921577c1c7349c15dd6b88009a393b5835be549c672a6fce8937769cf279179cbcc80655b818a4346fade491923e6a3b284627c9c17bce7dce249be2021eb933babd41cda8556d1e18e930f0cf49c95bb8c77d5a4d24bb0528d9a2bc9e4f9943c96da25c54813bd6ae98820728f1dbe0e31da361ded3c2614528d6b9efc6ec", + "secret": "ff9611a2ad96f2cc64cae2bb8a6b9dc3c520298b4d244dee2b5030b85277880df7b4987f24e42f5f4b62a835ce8ceea1ed2de6d8c1e32c363fe95c1dbd18fe889cbcc80655b818a4346fade491923e6a3b284627c9c17bce7dce249be2021eb9fea0fb1f02d747cea7f3cf41cde805ad8f6649f72c7a10c803e34277c38747778ef40aab3854c0254fb4ec28c5e6af3106eb77894f1d0dd00c62bb5220605dde43c96da25c54813bd6ae98820728f1dbe0e31da361ded3c2614528d6b9efc6ec" + }, + { + "public": "7fe3aa867ac7af8ad6defcf7e52d0c1a925fc35eaa394f43178209510c045f202f9fd5008c272b2bc956e18b133cdab68dd615469971656f69a94b5bda5db14a4da7cb60b291236c81c1616bfc4c4feacb651c6cbe00b7ac86dd802200466a106e7160f6e421e05ab2ca854a5b31ee6e54e4f2b61bb6f432866608727f09ed63", + "secret": "4021946cd10820e21183ae9b3b6824a812b302a03d05ea8bcd1dca35cac163d1a97142c9c0349d4019bbb365a9a46d054cb0e2ce86c717a8367d76b44f5ca97c2f9fd5008c272b2bc956e18b133cdab68dd615469971656f69a94b5bda5db14a7ef747fb99e255e136433221006eb0429ea5f070a0f9131e0e2bbf4b5c24687987b87a4d70a87c48d199e3be1a005fc67f042921430972fb5f7a61a1c97ede616e7160f6e421e05ab2ca854a5b31ee6e54e4f2b61bb6f432866608727f09ed63" + }, + { + "public": "29fff684e855644ea905c55038704a0ae2c347a5de2f42a6ed0f3b7e254579db71d60dd1f7affd09b11dbf4eebb4231e442769c8bff56b7f2f38d5d82c4446e0a53c5f24806ffcd72712594a2a505ea82d02c5ebf55ba3e50abc8d480049f199c54e682004ae2293046c7774fb987793d54d75a9a83c84e4b56b7fb8c9a1adc7", + "secret": "dcdc5d680216acfb7fe22d0d3b717fe775d7b1303d8eefb6d4430a8335dc3bd7320bfe6dbac9458f55e0429f0384f3be26dc58c2692b8042b4e9a159464a07e271d60dd1f7affd09b11dbf4eebb4231e442769c8bff56b7f2f38d5d82c4446e03f3177d6c475501a15ae9fb6f1808a088f502dcd807a23c679c289de364d400819e351fc1d1309f1d347e1ce316f5f5ff34009ea3a19f5da7f19395371277282c54e682004ae2293046c7774fb987793d54d75a9a83c84e4b56b7fb8c9a1adc7" + }, + { + "public": "a02d48cf74b417fa155646b29ecac6af9bb4b2f942e06fb346d1f54774bb132f009a43536288081e94d1cb458065fca02ead0e24989fa9d275e03affe4615a44e36ed31fef9407ee819e48b5eb030f7f84f1d8e1b32252d826e92ec4578c033db3fda80eb7a0dc2fe6e8699c8cd35f0df19f0684e16df8c448f5ba0f359d046b", + "secret": "b27efd2dc998da5c8b12a9a92f68204e8647aa5c6c95f6abb8e0d3f930056c668f8921c3871311b2da97cbd7b28721de59f8a4ddbe50243cde7eb08163c04c5a009a43536288081e94d1cb458065fca02ead0e24989fa9d275e03affe4615a4433cb5967a83f4e219e9675331f6a435fce900f4c3b45b7b1277f545972a774bb95bf647281bbec96b73c9d51a9d43282da4c3c86069d5c92b708ababa30784efb3fda80eb7a0dc2fe6e8699c8cd35f0df19f0684e16df8c448f5ba0f359d046b" + }, + { + "public": "9f0d592b3e4a0cab6ae68a481ea6d62bf2bfe20aecf7aaa491cff860b041cc0a6690410da62e97b50ef0258b77e50d94bd334e4f7b9a70e16bdd894ac65a3f6f0a6bfde25e8cbf830bd98ef022a4c5aea17630023cb57067a777e25eeb8a7d4970c7564eba4d86797cc135e08ce89d50f5d444e6a86f2d65dffc22a0ad276e96", + "secret": "712b24a05a4fce8d07b123a4647f6a5d9dfc111e79cc4c51522e64e796ee7082bb6f6bcb2d689dd597bdfc4f15c865e3e177e09ece518a23f55aeed59bf6c1eb6690410da62e97b50ef0258b77e50d94bd334e4f7b9a70e16bdd894ac65a3f6fecaef29343ebbdd3a073f7da60c016aeb71c18d3c7d54dee8056f8e08e9af11e901da3eb66f6264901b9b0591383508f67848b6c5fee018087a0e98630bc23c270c7564eba4d86797cc135e08ce89d50f5d444e6a86f2d65dffc22a0ad276e96" + }, + { + "public": "b80c1bc693fb7aeb6495e19e0f2ae7d417b5bd368d16a20bbabec37a550c418943b075b1770cbf84f81d3f0289d620f9b78ee664f2906329a14ec6285395358020f3a43a21d63d77d45e3fcdfe0e8f24d1d13dcd002917550fcb1373658bcd6222ac90e1db7eebd936f07aad274b279a40f11d9c92675231935eeaca567f6b40", + "secret": "a1807a0ad24823e24dcba53872d3a190e125bb8856af1f7099651717704cba47ef0ba3dd5742a43828260ee200c672167f709b90d188644b2681e19d38f8fe3943b075b1770cbf84f81d3f0289d620f9b78ee664f2906329a14ec62853953580176733f06786c74d940826edf21d63b6b30f5f150d2edc95c2cd42a9181bfb19236b47d9c2da8668005109a7cf7caab91d28d6032af4fa5cc0e8a08e595b906c22ac90e1db7eebd936f07aad274b279a40f11d9c92675231935eeaca567f6b40" + }, + { + "public": "5bc75e3d76b3bae80f5be1b6cf7297d15c70c8534eae9c62f6fdd2500aa1c7fa9716c0bd9bfc62932580167f716fa01b4d5d07ee0948989d7be25360a60cf0ab6423f8464f7fcd0516ee3023b1448f5425a0667c2cea6d5e02147fca277b0faf412ca0de50f7b1756f53160932064ddcbd5d56f8205c98f9b7ee6cca6005dd37", + "secret": "27b4ae2930aaa6ce4605210ec2b08214eab4325b1933962d58d368947d0b776b9728c7483dab6128f15611bf237fd7d2395341471fd62262d3bb9992199f74669716c0bd9bfc62932580167f716fa01b4d5d07ee0948989d7be25360a60cf0ab7de54ed0071380b934de9a17fa19f6e45f6cfc0885af603c0c804d5367418828f59f1dbfc199b310ab2fa43b707e07af64390f1d1590231068d473e9c2f0fda9412ca0de50f7b1756f53160932064ddcbd5d56f8205c98f9b7ee6cca6005dd37" + }, + { + "public": "75ee884697c3fd78197fd9190d563b0f51652509fee8c8f86b320de08eb11960b725d47c9aa0587b17aedc30f1ba458345bb36e85e714978ae944a31402718524a39385bda61e4c02f8c68dd7baf4c0e6d6246fa86fea291aba4a7ecab9a8d21ca2db4a6723c321e4cfa5029e5be9fc1b06eef3d5f1b56763bd9f6a0395a741e", + "secret": "22a8b583a5f27f36c95939d5fae54dd9eb233fe5771398a899856fa997a913af1cbb8ce89fb5a95dbcc8500dfd8bc30450f670528070863e1533279f132a63b4b725d47c9aa0587b17aedc30f1ba458345bb36e85e714978ae944a3140271852f7f095a03eed14f2d82672d223b6e0a4c9cbe396daba886ecf2f92fb971db40965ac28028628f6e6b23f4dcd2d4d34d8ac2791f4a16b93eca6c9deacdd97a1e6ca2db4a6723c321e4cfa5029e5be9fc1b06eef3d5f1b56763bd9f6a0395a741e" + }, + { + "public": "6392c9afe9469bd025cf5839cd0824f9a164013ba68d2a6ce15237b2dff295f828c1779165009dee8a91f6c307bc3ecab19bacef376bd681fe1220a44442b471592ee2afd3d1c5a1f908d4b87f02ed59741ffc3670d576aa2d468e76a6be5a3b37c8e514a46fbb84a7a69c9ec549656c9bc46e27be06ec6b3fca9644978b35da", + "secret": "15f038cc121a89d458ad55cf75260810adf0001e797ea5d98426fc7ea25c495760dd0c371b186e9c6faeb45dfde59fe8445d0ce5cc6073e20028b182113d309628c1779165009dee8a91f6c307bc3ecab19bacef376bd681fe1220a44442b47135b8965edb7658067bb57edcd40b427385a63ec379a1457d7076d5a99c2505254642646384df0dfef61c8f5ef3c8fd987f21144bb10316073457fea61850867137c8e514a46fbb84a7a69c9ec549656c9bc46e27be06ec6b3fca9644978b35da" + }, + { + "public": "3342db472c6f1410d70e184b848b575d3c0bd9be7d429753b7eac80d72a7df60d8f5c92ed56964d80b6c628d250baa4fa9692fe1f5240262b3f4138c22204e99335d509364adbe595c40e0313aee504d93cc076d978b1c369c026328e9557b9f3abe9d54e33a7a409e715b53485d6b119d8125410fff5e1658a47996cff852df", + "secret": "cdc39f505a338f68802ae2e8ee56d64513112bfb58fa8c5177f4377bfede579f1c0caeff56ae86a8889d619b04731a5d58c370792a092e508118fa9c92292c3fd8f5c92ed56964d80b6c628d250baa4fa9692fe1f5240262b3f4138c22204e99a59d1ecd404bf6583ef91aa94f7a75e7a50d7c176d65e57c738844690d06b56d97582025675777ce58499d8c53bf62ab70676d10bacd4b5ab6adbb954d5ef80a3abe9d54e33a7a409e715b53485d6b119d8125410fff5e1658a47996cff852df" + }, + { + "public": "d8a288c73a40f3728974a80dad55720f0b90a2e15d8ba543bf9866a9a61440c7baa5cdc1e7ae769cfc483bee9c39fe991d6d1f412ebac4a783f39c265cc8631a41250e3c3d7d706fd3db7be1cbe61b261966f60f6be40a7f4648738c5bca3206debff556b524ba1a412061c0420a0ad7958a2db9b44b306413590ba14b8bbe6f", + "secret": "73d155cd04ac9bab7475fb6e8f8e061435c83eb9514cfa6fd30ab49b1b9eed36360876a6837bd4610e699242ff17754c38949c59b0ced2d0cd60b10f22c0734bbaa5cdc1e7ae769cfc483bee9c39fe991d6d1f412ebac4a783f39c265cc8631ac50ff5a502885fb95a052d86d095b05b551a3604563cb5b8c2c598951fd5b648438b028a2535aed37aa24b62c77cece578b77bbcd699b1ebbdd26b47d9223699debff556b524ba1a412061c0420a0ad7958a2db9b44b306413590ba14b8bbe6f" + }, + { + "public": "fbbc8d126b800e5853254b4dc1f18cbba6fe777cc13daf254f9a08f4b63a7a77edd7a4115745cf64c1751a361ee5adfff493696a7e631cfd64abba876e0d760a148eac6c805636594c1601e0a5ed5a3f86ece6a49fca4bf47884dd810ed0dc05bebdb4718a80b1920fad1dbce18c093757e60bd5a17d3c4f4c4b034be7acbe21", + "secret": "7e6ed95f37ad13409b1dcf1cb43083501f5937fd5687ff9d8604e29f5646df174dd3531913324bb69f9b4734a0b5086385b127b4fb90cae19b6616f018a7f8fbedd7a4115745cf64c1751a361ee5adfff493696a7e631cfd64abba876e0d760a68a05b97d73dcdf384434b95195ebb3aa14c3973f3bb0060c97cc451d21a55df3b7f38a1666ddb067ac857bdb4b07ce7eeebe576e7e7fd42469659b4666c3f0bbebdb4718a80b1920fad1dbce18c093757e60bd5a17d3c4f4c4b034be7acbe21" + }, + { + "public": "2279d7c22321118a912f79459fbef18a4344a29e9807b50dc888ff74d5d852965bc52a1ed07991eef562c61593bfbb54b54049e5a06967ee55adb6fe87d6080286ef55550e8325be28dffe5c856f489720b062358a634877ccd4dc038797d2fbe30e6b788699d11bc34c88b70e42149e7667f30ddf8048e15c3fdcd7f44cf1df", + "secret": "ef6abc6def37ab4baec45643e32189d1838cdac3a9ad784b2c04e7c90b1b405d244daaf6e165b989a3a59a37a1d86fc292f4e8141a95104e5b1fa44f2b21aae55bc52a1ed07991eef562c61593bfbb54b54049e5a06967ee55adb6fe87d6080279302045e611f51d93bcbddcc456ff64bc7d0cc9a3dfcad64ae57008644a3c3b39c7c26849ef6495d6b3141cb3709417727dae7240888a7f8712b67b822c0d81e30e6b788699d11bc34c88b70e42149e7667f30ddf8048e15c3fdcd7f44cf1df" + }, + { + "public": "f1b021f5f3f950712765fc809fdd2d1ba13eb3f29cf2e1f7741effdb66392dc6c10dae1edd5b6c4bb6b72d72d181e16ce17ab1c7b096417cc0fcf84ebd3a6719a36a109745651c1d607e845f0aa1487430f3b2286c2b54a7428fbf1fd2085d1b2a0b83bcf4aeedfb5a77317619d55f2c2d546791baa199f0713cb0716d22c290", + "secret": "675225521bec85c3e6ff5e117cb77fd06cadd419fc650c503909051fa2b6b9dc93777787cf0d01e7e04e0c86749eafd54c1cf149e83551c4be4e2e879a81e126c10dae1edd5b6c4bb6b72d72d181e16ce17ab1c7b096417cc0fcf84ebd3a6719b3c00bf1a282b5acacb2208264bfbabe27debb129be48fa3c7dcc56b07ed58817fd3a819cb134e7d8000f9055ad06fd4d915df2c63f5677fbd81b6b6845cf7902a0b83bcf4aeedfb5a77317619d55f2c2d546791baa199f0713cb0716d22c290" + }, + { + "public": "ab6d25d6c9f942cdb20ca35306a65596b4c6f4f8f9b9bf8c660b7e41156a085a5d8da89bd37f09a6cf76b023f539acbfef70e115ea369172ace31d2da1c9d6f3e745e3671e8211dc51c5006e53798ae8345957f2caba22e8ecd0c65c5b8f769df05865bda9cf8cbd795b9dcf2eb2372bc2a361b9138735410f824e3b589a11f3", + "secret": "7dd1b3a777767da8983e8e9a895ff7d58d211e5889d2136a46efc6de7ea2aca23c7572e73ca51d3c1141fa473bbf88c74565f4129ea6b9188e0060a451c421935d8da89bd37f09a6cf76b023f539acbfef70e115ea369172ace31d2da1c9d6f3b3da5bc033e5d1ed69678567d92aee5c98e3f49f20b9dc48ef4171adcbb86b9098aeea9c8f289ffc69b069dfacbecf872c3d6faa3337b3c9ec94cffb13140869f05865bda9cf8cbd795b9dcf2eb2372bc2a361b9138735410f824e3b589a11f3" + }, + { + "public": "7b446de3cf9f8d1505db41fdd8dffb8d91b93cdd6a24d9b9edae8a1c826b7f34fde29ffd6dc63004cdcc16b430881a114bb5623e6960dd612b2049d038a9e7c42d555d8b4ff7d14890c96e4fe983cdb43e37061ed479b74d315d5f6aa38056dc813d7f27a1f9f3170166da22f98403e6fcf7a9c371a125d8ea7cd011bc378c14", + "secret": "3a1c051aff0ca65447fbed0450a6b1f493c07058cba2b624c84bd391176f8a4202a4b143f22c0b7c669f3ddc6b89f5bdee38c8f4de64c746fdb1d463b9ef57bffde29ffd6dc63004cdcc16b430881a114bb5623e6960dd612b2049d038a9e7c46326aa76890c56272f2154ae75e8e0b0386b92a12370a8fdef06ec5a46cf4b2db80e37f96fe5cfa3f5b0567514bb9d7a7c6859b5736ee6bfe1f94fb15a8be400813d7f27a1f9f3170166da22f98403e6fcf7a9c371a125d8ea7cd011bc378c14" + }, + { + "public": "58cdfaca51585dd64a63ab61c25792868e1c1ef2771548c1aef77dd7b07f2e5110ee701cefade4841c9761dde24171e2de52dfcbff42083596e271f9335b002526a2daa2a9c11924da2376cf123821727f79e8d7b361b8f784856a909778c9295d17a0f51692a47a222e2f6778698e8de146da4148b6bfe34e5487bad40f0ac9", + "secret": "86488ca527c5135d6e5974125257d2e418572238469774dd17edacc5c14121f92a25703f07e90f049c26bdb8b2f667408c136ff2dd56a4e8b7ee2ebc447a850810ee701cefade4841c9761dde24171e2de52dfcbff42083596e271f9335b00252ae04ebb5653dcffc116f5576e4c8fa98751d0366cb43930d80aca97c6519153fb909efc28e5472f5bf0fb603d3f61eec4a0a09e29809fb49c0a85d75893df465d17a0f51692a47a222e2f6778698e8de146da4148b6bfe34e5487bad40f0ac9" + }, + { + "public": "709c934f9794da22e53181bfa9c61ba3e35a156d30b70523e40e44ac00329f62f41ac76e5f663b91b1224b2a18652c38233f4f0065d6cb44006977ef08305a67b6078bea86439b8e1af64538f0716c29d159d34b0793e7db1767b634169f9777dd0aaf744c1e222ce0f0df2e0f9368feb8c694e804a2748a77d70f5ce454ff64", + "secret": "39c708019fe30b9368cb99fbb389edfe46ea4a346db8b44ca26788ea6fc87ad2526731a36d9dab32687b7cc5bdad49e38577c8fe7ac985d32fcefd4b77f85794f41ac76e5f663b91b1224b2a18652c38233f4f0065d6cb44006977ef08305a67dfe38c000c3c151b7355da09fd7c0be260668ed2e915cf30c71596525830ea9ab35430a494998c39395d9de632908b82e9c107243628195a874c703ec9180c67dd0aaf744c1e222ce0f0df2e0f9368feb8c694e804a2748a77d70f5ce454ff64" + }, + { + "public": "5b7433b98894fe1e975ec92445d0507e2fa1d7e8f7787ae51b7f65e5061dabb62d05db11a8601949de91b266cb8e942adeb58ece658ff5f6e38433eeac20b122788181888143dba953530c7642912a53ddf8368f123415793f511760b8d81b6411099c2aeeb19fae2018bcd5658e82cf15c7870714e860edd823d5f94e027c95", + "secret": "3470d0a641cb72532b32f22cc320862c0214b320602a794d56aeab74707a1ce97c1e0688f22f0e3996e2f7d78830a637738f882e67ce29d1cbdb4963da1c60fa2d05db11a8601949de91b266cb8e942adeb58ece658ff5f6e38433eeac20b12206206b1ec1bb9c2c04bd8e74d047668dfe095ba8629589d116e08d1b58827d0c2161b01c2120c32efcfa1d8af12659e92d18aea8e1dbd334a30ec34f57b8840311099c2aeeb19fae2018bcd5658e82cf15c7870714e860edd823d5f94e027c95" + }, + { + "public": "2bc3716bf9529875b8a39fe41f40c2fc7fa802b7c1d6639ccf33026e2ebab4daaa496e4a411e745151e4e95021b98124ab0d1c92c76b64bb32d6414b829edbec8922d37cf03c2c6931d76ea8f1364e5b9382f8ed93c89c011d61fb94667d5b28e9c2072b2b96b2ae2407cfe1b9a25cbb562453dd20055ef9d50cf52e0b46ef4e", + "secret": "466a6d80ae831ffd0b3fe336c6acb5fc4609da585ee338f421a5ec34d236744204e1ac6008a4a72ab2ef9765c7a7361c5522d3ce93662e2bcf4d261c884a5ba2aa496e4a411e745151e4e95021b98124ab0d1c92c76b64bb32d6414b829edbec9816c4c8d20aa21b1a88d39ae3b1d9a3f6327852dab905f1925dba63bb54ed51f38d90b7fbaa497bdee6d0f738f5ab5a1dab85a7826c92bd84f210e63e2cd301e9c2072b2b96b2ae2407cfe1b9a25cbb562453dd20055ef9d50cf52e0b46ef4e" + }, + { + "public": "6b09ed1612b9c156a5e55b8e990548bf7570fa1c719f6d0f9d18e9219bfc0f95daf97ce1c02c351274fc9397980e03ffcdc0945ff58ebce89e6a2cbdb107e58216a9ad62570d258d0a15783439f149d79980704493bc9edb8d1dfe7a872c3211d86c68aa4f4e1163c919083d16ce02467e8b4962df9dda77e854bca166f5c4d2", + "secret": "8ac9f332a30ee7e4b493047ec01217e3603422ec25cffa5c0dbcb21e74e15eb71a5cc39a65b76a0a904f37c6759a65229311f8422384b772f3adf3addf0f337fdaf97ce1c02c351274fc9397980e03ffcdc0945ff58ebce89e6a2cbdb107e582d79f6e805b374168d848089256efdb5bc6a09d25608da0548f97fca5f2af0ef38c7ea0c71d673c639a5db56e93090660ec679dc6f18c1186a8e6d348d1e1a243d86c68aa4f4e1163c919083d16ce02467e8b4962df9dda77e854bca166f5c4d2" + }, + { + "public": "1d3cc69557b1cd6101210b030610822a21d6a569bbabd51d4b5e5d686267020a603991cf33cd2b3361e5efee06ad8c10185aad07be028476121528133d8c61a4b955b75d9e6c7baa854623ca188399439c6eea47efc78448387d5e9842804ad7893763d9960cfbb6b0a19f8381d836e77dd0e9462645d41c7be22b38dc6556a3", + "secret": "d4972231677129e772c5f9a95669b41e24ac3a1550683fc9e34f44f6b574af244b2ebeadd8a73005e3d0b2a7fb10f90096df2b9244a912ca3af96f51926bdff3603991cf33cd2b3361e5efee06ad8c10185aad07be028476121528133d8c61a4b484c3b5eef7d9a843fd452ad18153d0014589a3828f8c0c1507fccf0a13b3d3fcedd6bacd477dfd12c072fe4d8089a4f2339045dd8408ca8ba0b2d63dc4629b893763d9960cfbb6b0a19f8381d836e77dd0e9462645d41c7be22b38dc6556a3" + }, + { + "public": "465bc7cdb5cda2f92d671c397f72b77cffc04288c965e2bbba5be4902cf80f574143c2bdc03975c3f90604b3207772356382229911f10f4bcce8fa488bb80659ce9c49b716913096fe880effdd670f2a1413be09932a914dd948a353752b7233bf6fc391daf49453f0c8f319c713ef5100428f70896954c45d4b6ffb294304a0", + "secret": "1205555922c86889672d0cf0a58eed5e5d70295cb8e4e83da20b66956c92d5c74815815ca34670c574acec817c9521537847b5466ca222935a465eb3e07e9e644143c2bdc03975c3f90604b3207772356382229911f10f4bcce8fa488bb80659e42fecc9351fd5d28b8b6531dd65aaf6a976525136bb5f0ad8a2e4c90644181b24bfe401ee17365bf6e10868141ca4bcf9b41585cc09d8bd5dc3518625058ea0bf6fc391daf49453f0c8f319c713ef5100428f70896954c45d4b6ffb294304a0" + }, + { + "public": "8e3076caf35f95fb0d462ea1a4bc1447294472fb51ec71436d6aae35b1c6765670906276b4d01b14c4cd797f5e9864025cf7c18674e5f3972d4e8ce3afb94c5953a37257ba621a4369f7a46ddbc0ecb2c2b23900bd082075660cd4791c5f279cf74e040df332be4039a4104e27c31d78965638404ab930928f5c7736ae05a0a1", + "secret": "2d7ad3ca04e40df080e8764ec5e2850c6dc4e2cc52e95740cab66652e62b1748b333890bac28914d0ae620112b545aadfb9e6dad5a82d0199c57a4e510764cd370906276b4d01b14c4cd797f5e9864025cf7c18674e5f3972d4e8ce3afb94c59976723a251196a4c29fa299b40075af4c464d66bb7c35b2a6758f3673329a8fa338658cfa8d519a580df9158d043652ba81dfd4169ac0639d4fd6573a0cfee0bf74e040df332be4039a4104e27c31d78965638404ab930928f5c7736ae05a0a1" + }, + { + "public": "9247ea5fe88d43e6296b336964c2bc43a868f353c96793f5cc68f2acdbdcf08dff89b1d3bf7eaefedf2df2c5f5346b4cb1eedc23a78939ce1134a140f60e91ea4d313da3e134b81bef8feb7e5c71bde6157d47c926eb04a769d7e17f019e38d9118c804b2da4e8eec16be02562472fdff21feb354f635fc2162ced3bef314eb4", + "secret": "701a02be629b1dbd9bfb87c62823d845b3a5095ee49e17de924696abf43724cc7685becf24990308dbacd7362a10320b6580793df8432a84270bf76eab45acb0ff89b1d3bf7eaefedf2df2c5f5346b4cb1eedc23a78939ce1134a140f60e91ea43151ba5b12f8f51ac8e6c4ac3a5840daddd14ed9e526f4c01a00ea6ba2fb37bdf9139006dcf05080845182ccaab78463ab511f6ca9ef0f079b78131b2a0f902118c804b2da4e8eec16be02562472fdff21feb354f635fc2162ced3bef314eb4" + }, + { + "public": "40cc9214b05debfd0f6ac85253a93b9795b3405128d67cd95cfeae27cc1a0ceec5469552ad18903c69dbc0abffebf3add4becb38e8ce437ea13097cf3183325759c30e7190c737ac1fa00345854c6643d7c75210d9885dbfe755c0e2790a0868b17e1a47673ffd27fb9cd906f13b5937a63061e7a75b76e6bf37da37f5c999c9", + "secret": "02e1b4505fdce97f82876a71d4e9e983f985ff57b5199ccbfeb49ba5a5bb653222425968cf3641de2a2e1566263c24dbf131a27a311a9fc46e7ab1ff51b292aec5469552ad18903c69dbc0abffebf3add4becb38e8ce437ea13097cf31833257bf64013bbb542d9254997e9b61c797ffb2dc201484e6621359ef0cda4180685b0c2f2d32fa71b7dc6dfb14974af7682fa120ccd301800729751a2466ed06529db17e1a47673ffd27fb9cd906f13b5937a63061e7a75b76e6bf37da37f5c999c9" + }, + { + "public": "94347f749ebc6d4a467e453932c5bb49e4c45b202a403a79732fce75cb9309a0838db6a8f8b743223209ad63ba2e4aff72cdf9da48eb73092e73b8fcd4cdb5ad3dd1070d8be62cd3830a7e84ca888d2571ce548ef4b09ec29c5039e6330671df422160aabb36d5c7521f54a58429086ca097f2db977ed084b03b86c7ec030ed2", + "secret": "acd7f428147d459af3ac01efc5006df3c1d0cf971cce85ac655fabff74c6dcc0ecb493fe192a572b227a2f341271c9c0b0dc8fe0c4a1032c5952a1fd8da4738c838db6a8f8b743223209ad63ba2e4aff72cdf9da48eb73092e73b8fcd4cdb5adffe09758b3c09da5e7d8fa09ac0de249c9b92e514ef3d9629fd23dbaca836117dd6094f805bfe8b8708db83a5c6df008b51b6adb0093bd039b3b7829291e60a4422160aabb36d5c7521f54a58429086ca097f2db977ed084b03b86c7ec030ed2" + }, + { + "public": "e67be3cd0ba541dc742e5bdd003a79f2b43e3a04050a4ee7ab08d54b0f84dfbfd253b179749114250586acde58edf34d8f5b438791ad12868a865e72ffba3ce91acb8f2bdd27720efb5c5c89eda690bccacd2c4d3ff3d83c124d1331495724fe27b74a819a33f6bdf718355c8aabedae334a14e3bc1ad75719ef404d8847d29a", + "secret": "7b1aca9f38a4c5eb9b0a061602477c017b9aa7e3971f97da8c3b79fd2b4efd2c7fdf33245d1b951f0ecbb6c659275bdaa20a18cdfa3efb5c60b47de6a9507bf0d253b179749114250586acde58edf34d8f5b438791ad12868a865e72ffba3ce9539e7b48d22370c76836478976ff2322acaf26c86fe071d978c80c634ef49e2f621bf428485eb9a827d5ad822a16532e8486aa2897575a01c5c701c535e165c127b74a819a33f6bdf718355c8aabedae334a14e3bc1ad75719ef404d8847d29a" + }, + { + "public": "33d85f3f67834bbc71c44524f646f586d4acb430454332e6b3f9c98db0d3326ced5f8daae648b232c5d8a9a5010baaa29e30a254ccf7bd2e5081cc6cdd2fabffe844b2aad81191ef43e584d62e4e01da31d9ddf36d8278e349b712e9d2680c712844a43e583399e6c82295f29680579f6077199a0df1c99f6f801e73052fc28e", + "secret": "5269bdf6a26f14cbc474f6e12e2052aa48a7508eff98967ce5a7b215ee521546f18e5d06de492d951d7cb8087cbfbb3dc032f832db70fa3154f9c35aee9cc76bed5f8daae648b232c5d8a9a5010baaa29e30a254ccf7bd2e5081cc6cdd2fabff221ebd18fca51a7f3c6d18e054c0da65748835a69200ffa9f9ab85a3cc2418526c6bca2dd0747759f171b6cff783e615f050d5eb5d7062b1f587b858f3137eb42844a43e583399e6c82295f29680579f6077199a0df1c99f6f801e73052fc28e" + }, + { + "public": "10a00a2677607b95ddf45ba5c4567f790e1611a8de1449ce086f568a7792a439de77133c971427147eb78f927bd493de2493fc9a3eec10ac2d1d4ce4581c5ce64c16053a8e3c898901c62f9e681ec08ecd6eea5df17db74f89a61222bbe1ea5fdc0bf5f2c6167cd9e0f5b2da87e7c7a8b6135d1207265d27803799a67aa19ab7", + "secret": "8c41e665393f88c0b376abdf32f0203d480a9c5f9edc0b98cfdc2d063faf8c4576a6ebaf43abede503565f024c4111771ea59c1c8e2e7d75a3f779ab3f82ac81de77133c971427147eb78f927bd493de2493fc9a3eec10ac2d1d4ce4581c5ce606250fbd1cadabca9bb73f66d925ae15251d2ab61358cc33c6ad3f55fce64eb0cbc880ae5e6badbcd80c5208ba0d3b8a9b776b553d56eda237c9bed728da8e65dc0bf5f2c6167cd9e0f5b2da87e7c7a8b6135d1207265d27803799a67aa19ab7" + }, + { + "public": "88bfccb4dd4215d5b881529e337f8513eeca7ed1268c4448094c5fbdc80dea5e2ffc3b80a034f9c579214856dec8fca4d0de7a213b928a00f7c5b5c9ad5dbf8f2079f1f192c9ada7d12b2a021fa51e24c4758def6fb4824394ad07c874becd4f6657edd55022a2111309cec0d50a8a12710d147c6224078c2137d732d1c7ca0c", + "secret": "b5c43bfcfa11d0affa07f126137ebbe1a3b33d65e39d9406b754ae53593c85d30c5b3de4c93e56bf8149892ed156b8675bb91875aedc745b09bca20efc3394612ffc3b80a034f9c579214856dec8fca4d0de7a213b928a00f7c5b5c9ad5dbf8fa0b85bd333ac47de79074bd44a5a9f039df6c528255a545ce7072df7fa42c8048865c7fe0912aae0c442b1a95da5d762091f550923189518fb1d005d6f5234776657edd55022a2111309cec0d50a8a12710d147c6224078c2137d732d1c7ca0c" + }, + { + "public": "60b2498e5b200c3af95b9f90a6cd6a155f75cda57ef3cd76e1dcae6f32c0dd205316255905d8cd7d517e769b5f7d3b64ed3d52802923db90e4e2f62b9f350e670dc02ba3084df1d9a04321b5d58b2cc9eec7d149f6675df47b8fbef14c1c50a64d3a44b5834890cd05eb92137240d68186ae21fc392a18a76394edda892be10d", + "secret": "29a1c8cdce3986b4f5c0ed22a6e6e1aaf3523c669362c76225fda2e2f30ba2f4b16494a840d0a45b3ae459cba69147004d89c4b7bb8537e3312a9929fb39a8615316255905d8cd7d517e769b5f7d3b64ed3d52802923db90e4e2f62b9f350e67dc870f0c897fd031d4c94711d0b18d503fb7fa0152f4055649258722ecaab09105c8d16871f4af8e1b205760a5c8d163cd04ef777f15c72a606340ec8cfcec244d3a44b5834890cd05eb92137240d68186ae21fc392a18a76394edda892be10d" + }, + { + "public": "9f596bb3a07e89d76bbc4af9d487676d25ac7af1aae1fe2e91e1589ed4b719d5b7a2e338549099d4cef34da4be68a35034e246fc6a4f1ccb46aa14eb029e46080aebaa962cccef4f8218ea526c2d572103aa62e55e7ab570e4b3c9ead2593f359e8d4fcf0b0178343f06d66c9e83076828564c4ddc48a2abb50932b91c40ef80", + "secret": "af3da16789014f93ebf6d9f2f2863afcdba7b16d7525b2f1c13c3b07e4738dd78b8af092408a5fc05e792a14a2cef762648f1233dd75b7e353d2ac833dcafb65b7a2e338549099d4cef34da4be68a35034e246fc6a4f1ccb46aa14eb029e46080c825933c32a337f743920ab41346d8fff0510a17a8a457a31734ab323d1deec6cf8d4f110b580ecae67139560cc227a1299c1a06f61223ab6cdd0a404b2df389e8d4fcf0b0178343f06d66c9e83076828564c4ddc48a2abb50932b91c40ef80" + }, + { + "public": "862b4c24ed2fc08223e1ae173d670e1dc251d2693d866504981610ad424d0d7110ea47bc327c3a3dd2ed6ade54647dc48d05e8a856af51f5418681a71d903e3db159a4a68389138e7938e4f72cdb9912d592bc8452bfd4be9a20a59ee915310cd82a5c5ae294abd3097eaa00be420d3ce5bf4ba6f303c1fd52c2dc5f72318caf", + "secret": "e0a7eda48bf754c94fdd49e175204fd6236e630938c85e2ba5d9f3946433f3857aeae6162cce9ac472bca42dc5fe703f614192fbfd9bb871f76e1a8f1ad09a9210ea47bc327c3a3dd2ed6ade54647dc48d05e8a856af51f5418681a71d903e3d911c5a2615d607c9e56f264cea8c43667e56916f6c9b25a4bd4901602ffd66c49dc317f7312a146dedbaa34e63b5f105fcc208230ca3f09abe9f3c9662b15046d82a5c5ae294abd3097eaa00be420d3ce5bf4ba6f303c1fd52c2dc5f72318caf" + }, + { + "public": "4749fc048895ea14232faee6b20b7942e2be0f89b5997ea5576758cd5252f57ad8393ce685eb74859db4e7fb18bcea10446901acc553b2674436c94cfd2555c450ee4995dbfcbea6fc6b6d6a786734184aafff3cf808f7fa0b41027dc28d2550252ba2a7dc798b1851dbfd84fc7ae5b02d5b637dfaac714c1aa41cba3641dc20", + "secret": "de9c5e8f18eba76dbbcbbe6ab6d8c8fc4893b35141a27541248d1efcdc3f4cca1b52f0c2d16ad91371d66f1cb35144befab18b8ea86dee9e3fa4f9212cbb073ad8393ce685eb74859db4e7fb18bcea10446901acc553b2674436c94cfd2555c4c7bf0fad6db92dd636fd02c04d16e3dd05f6cffb3fde1bb043cdd1f324010e30f5398b0b23082ae63b04c226bb0d2d789d281842c592d7c124877cdf0fc5b787252ba2a7dc798b1851dbfd84fc7ae5b02d5b637dfaac714c1aa41cba3641dc20" + }, + { + "public": "5f98241b77f6ce99f7bc761b5da7d49bdcf1c9edd52ae1b2c37a779aa5201b12f78f766e5ddc13f81bd47e04b523e91a9a14f6bc70ca60c6ebd8d564935dcb4bac44c2b24b3fdbe75cfcf641e60b64b8a142e93cacb2be3d0e88aa2cd8de99cca6cbe0f8235e0a11de1f24095318cacd6f80c9a70c8001a869ee4487503f9d03", + "secret": "d34d32175e5d16a9972169f51abb60c07170c9660dae01b9ef1c8b65f285552b323f55fe3926c2666103fa49b6218de3b5b06347b2b55fe1d083811c6bab3cfcf78f766e5ddc13f81bd47e04b523e91a9a14f6bc70ca60c6ebd8d564935dcb4b62e5b200761b5ff15cd874146a9a8cdeea6a782950a7bf9c6e23d9b26a1b0e2b9e9b7c8450fe0510158af5f06e2a07fb5cd40c4fe358f71fac5312ae4597f0f9a6cbe0f8235e0a11de1f24095318cacd6f80c9a70c8001a869ee4487503f9d03" + }, + { + "public": "8c51524faeb68ceec15ae67426f5bbd9f6dfac866b9e5499e57f574e0ad4b0e8754ddf16eefa806e865514a305e8f40e856626d818081cfac1d80d7cbe894a89fdd0940ed948b0160e92c30fc4544229e454ba6caa37e39da2c34ce557c01ff26245d31856cd5f648adbadbc7410ccb29c1f3c253643627cc00f287aa5c65afd", + "secret": "f26b14b48b136057d1b169706cbf182a49e24d18c396b96763a834259c78d73e3b8854dc25e72804ec0310fc2f7a43757628451f67666623b3915d15857691fd754ddf16eefa806e865514a305e8f40e856626d818081cfac1d80d7cbe894a89a22f7691cf1fe3e5fa5d426c7a95baf8905e97aefb9048cc2e3803415d99d6dd03dbd02f159d687dda4a0e510fcda6839d650c39b54618630d8f8a26e37347426245d31856cd5f648adbadbc7410ccb29c1f3c253643627cc00f287aa5c65afd" + }, + { + "public": "e32544bd7e3d2e2751fb5557b6cc2a224067c2546bcef32193245d0b036d5bb7f267b5610bb07323d1df9b45bd58ad444bc5e472d7ac71bd07e23e941910e5e84b3a3368ed0a94cd12c03d8a40c17a40e7dba090312859f950cd86c2b2eabce82e71691f04c737981e45c409470045e3a2505d1d5a8c4cb1fa6e75d602ab6a6e", + "secret": "0047fd925f66beed48a2644b6586e46e0af3f958cd9efb9777fb3597d1459420fe3c3ad5a2cf70ab4ce3b41799205f09062cc7ae89952c49649ad2c0548de8b4f267b5610bb07323d1df9b45bd58ad444bc5e472d7ac71bd07e23e941910e5e822c52ac495105a12ed30eed7f08301bd1a53c2f4c6c2afa15833adcf09702114f5ec5b128670a0927e12b10f5a5f128bc988280b45f42259bbe5bc763e6b6f702e71691f04c737981e45c409470045e3a2505d1d5a8c4cb1fa6e75d602ab6a6e" + }, + { + "public": "3009665e18de85cdb69d7c34b83abbc17587d479c49c01dc9ba0a57f59db87e02c080a5d437464edd6047aeaa2e1d3d2b67ceb19f8034848e30122ce2f1aacfc16ad28e8174b8510104008ba98408670a726d64276a0f407dc038d596791f434022347e39cf6ba7be74d6310bbac148d375343f47026f9f6e19d548818b05152", + "secret": "27a4ccbd6d49f3e042f5844efff7a1218f0e0fca3c3cfe5fc8699f99daef8d6a5c6008331c648c52a8a423abd28ec3bf0a85d240a984ec538da416b7e4e1d5932c080a5d437464edd6047aeaa2e1d3d2b67ceb19f8034848e30122ce2f1aacfca71f9da097e9e34f842653cdc071763ac87dd31b974af4361734d8e7a516cfa2e739a4911283519631cd244aa728e878e4f403602b57000c838c8fad66add122022347e39cf6ba7be74d6310bbac148d375343f47026f9f6e19d548818b05152" + }, + { + "public": "a2139e2e4bf3c823b4200bd5ec3905687ed75860c40ee5cafe74e51fce17cec9ad84e929f8edeea613354fe129582888ba7eba175b3e9e6a4e73cb80617d7361be84152ec5cfc7d86a7ab55dde7582a5873c01003d9bb95d9a6d9c5260041a8ae56236295f0435aea91a97feaf688cef685ff4a9093820a2da28766f9ce90d7e", + "secret": "a0c08d223683e1081d68697d063b875cd6b329648235a888b2665be6b6897dbbf2a71cb0d56a84c47c80c7ed01a691474483b856923018e127eaaaa08506d576ad84e929f8edeea613354fe129582888ba7eba175b3e9e6a4e73cb80617d73612f81142c71d211f0ef2cb5d99c6b98e4edd2f6c664b64d6a6235a85784b5db53e9e8b2eb04bafe8263e07fe30ed2a6ff72fd33724f2b77dc9287a46fc21d8f61e56236295f0435aea91a97feaf688cef685ff4a9093820a2da28766f9ce90d7e" + }, + { + "public": "8fb0deb4bc0f9a10b5dad40bfb33432b62bef91a8526e86b78c179ee1784d438d4fd3344094c0e8d0ccae1af56ac9f614a48011348f0962789eb21220b8bf195b146f621c1b2af98605c893c27b83369ee9d7ce2e5f7985cbc4ca51823a26a0c74fc2ab59001df5676bc3aa223e725f16e9cffe6798f7a38f68cade50e1055b4", + "secret": "4c11d2d456ae3fdec1a9c7e8dc609d335e10ff491e12e4949550addae3b15896ce08250b8304a1a5a666fb490baf66502e1ae52df64623577d1061b9a0e2fc07d4fd3344094c0e8d0ccae1af56ac9f614a48011348f0962789eb21220b8bf1956dbb144e8a30d8552767ba10c78fd1ff9dbaf2b14119d86ec596deb457ee00af151e5fbe3ba58b1142970cdba5ea06cbecc66b42d22f6c0b6e9c9236c8ba44ca74fc2ab59001df5676bc3aa223e725f16e9cffe6798f7a38f68cade50e1055b4" + }, + { + "public": "838487b98dc8373c312bb8ff2376cd778be3c1e60e8da253e72d55ed80c015ffb522a1a0612e9260c5728b4838f08d2c5d35a44437d01bc708a9f5fdf5d6eb64d8a10100c8421ad01723ef9d9986084ee102932f9aee67650e8d632c89f36e75c159fde4ca6bdd9921a674090c2f06570c9fe988cd490d30d72a6464389a8a03", + "secret": "f2cd2029872238863a5bddb109b3a9ee3f2bb2e2c3da734662aa548442f8b189ff9008234034286bb5b670cb7d4a96ac10081f0aaf950d0d3b9afb3ceb95cb7fb522a1a0612e9260c5728b4838f08d2c5d35a44437d01bc708a9f5fdf5d6eb641bae6ecbc191c1883f171989591e4e466813d1f72702f82b5a99803afff6fa1fdc76881c46de1cfff0d65967f24cc8e3e6ce3e2cf4e5c6b0d517fe25728881dac159fde4ca6bdd9921a674090c2f06570c9fe988cd490d30d72a6464389a8a03" + }, + { + "public": "c7f0c9fb6cc6c4e63fa836e3ed8dee913496ebd9dc12e40ead9b4d2393d78da38d1c47083fce7b525258d2706a416c4bb599fe3335ef4f6cffdcdae0f9b9c9cfe55e76d6be80754caca17f15fee2db4813d755ba2a1fcf789a566c3a6740eef5f2ad4b9ce54dc8e7920c1e351d7f1103fb03e6b7595a283f656f5dddb87f2d09", + "secret": "646a1a40256d8c6de6065451482193193bfd1d118c48fe1aeeec8a0c20820631ae497171ba402980480de1fe438f04b590e3df1881a812874251a83ff31befab8d1c47083fce7b525258d2706a416c4bb599fe3335ef4f6cffdcdae0f9b9c9cfe9823411eba49e05b1954929a875b3ceddab08a2e4843af823817bd57b8cb86ae59d6a7a7b8006ddc47b6a470d529dbf523d63272cb96a6cb046ac28be8b4966f2ad4b9ce54dc8e7920c1e351d7f1103fb03e6b7595a283f656f5dddb87f2d09" + }, + { + "public": "84423871144430f107335e0210129f81bb3b58843f21d6a583e456ea89106221f9464d356fc6d55a5de0047b2d60ca5d1b3447b38b605b4d460a07cb1bb747d748c8bc6291fd252bbcb0e1249e224d078c6de87bb56e0a52eae9935f8ae3660064c2b3a5a6a5c14eb23bfc8376fbb1bd731ce08dd1ca2dd64fc48f62dfc4db04", + "secret": "6420f186b591d5e426eacdba2afa0c432acc2b7ab3ccb240d51b11bc679bb0c888c44b543dc2b1dbf1861b82ed44291c9bbbb22457e2eb709797965775e23ebcf9464d356fc6d55a5de0047b2d60ca5d1b3447b38b605b4d460a07cb1bb747d797942a93a4c2dc0b626991a197fd72c04fe5b871ce75ef540318904be85bf8e07084eb287c17c16edc9547aca96581d1e6568ce4abd57253f758635e357cc6ca64c2b3a5a6a5c14eb23bfc8376fbb1bd731ce08dd1ca2dd64fc48f62dfc4db04" + }, + { + "public": "5a5e97d48337a8a418e80c4d0c2dbed8e28e7dedb38134b0d4c1f33313165a4c6da68f11b0c9adb80afa2ed9f7bf2ddf542c84e3ed10247554660b0f2461f4ef9504f5f3eebc208de657bca0bf20efb39fa76ec22a29e891d792254e98066b0084688f10087daeb11eeb4d0aa1e9a76bffc35ee4496a67cdcc690e448d815747", + "secret": "25e9d877c2fb4bbb8a5e1932863bca885faad3ac17d324302e9dffa7db49ac6dc70c30d833862be718e678ae8b186fa6b418c214d88cb55ae1054370bc706db66da68f11b0c9adb80afa2ed9f7bf2ddf542c84e3ed10247554660b0f2461f4ef44b25df16af520a05631187e6aa493393ca0964b4fdaba4cea4edf8905efc81291ab14f4cb986afd229c3c1555d6c371dc1655418a0354a50e72f2cc7fe127d984688f10087daeb11eeb4d0aa1e9a76bffc35ee4496a67cdcc690e448d815747" + }, + { + "public": "5d384a38eb9afef6e0283c00f592a06e5a7833e51ffbe1bdd1fbbc4511bb5d1c31c430c6f8e7ffca1d80104282f260fe185b40f8d824079d090120787328c6974821b3a0028cad5016cf599522923591f66c9957920eb11c32de3731bdb407f240c87e0eb4db5ba8a90478f75b1933e2ebf7d227218b23b0f3f3b1f9c54929ab", + "secret": "0a53106e2667ea22413b2343a1cb69d6a715e84c6896f9c48340b3bb6ec2151d66916d4953dc000ddec28ad5997cf272a00886e8f0178fcad6110e40271c485c31c430c6f8e7ffca1d80104282f260fe185b40f8d824079d090120787328c697a94018a919dde568e98b5bccccf03bc21d993e98e539c22016734623604f7753e20d8a192397704411065c9ffcf8f5154b981862181403092c56672c559c9a9b40c87e0eb4db5ba8a90478f75b1933e2ebf7d227218b23b0f3f3b1f9c54929ab" + }, + { + "public": "40f7fbfb0b5c1cb6899a05760735427d5fcf1343eb396cf24087d0337bb781f446422e0bc9fbf1be8cf056968ddc15c1470f9a361925f1080ae38be1550a870d1950e377918098f1bc113fb7330231a53126b73f9a126091673ad8d6801bf448b2cb3beb42c5cb52c231ade4a23d6046ded21f06c917610db8f2ba788a5dfd46", + "secret": "bfe39abd061d98ac32071028474e12c31132b86181a5c263ab064be099db0336f4571ba5db9550c58b8bf8bf94fa1636ca61a8259d4cba1d0b1335f327485cfd46422e0bc9fbf1be8cf056968ddc15c1470f9a361925f1080ae38be1550a870d98e2e4a14ba733c19eb0a10fa74dcb4aa3d816a00fe66de4da73ca5452a642366c0d5671e5be763383b30876c4628f66ed36837218a1c25141ba4201f37d4f87b2cb3beb42c5cb52c231ade4a23d6046ded21f06c917610db8f2ba788a5dfd46" + }, + { + "public": "2260c90f67cc0a832269862eead82e93826fe1ef0a6a1271fdefb98e76950884c9271803a372835b2e9da0126f8283871106dd5af2f53dd08ca314ff09a4b56a69352c5cf3f79d06e412aff0599632ee6cd06599f4db0cf6e842cf1af7451d55db144f75b5667391d454e6a0b7400c615d970088cff0f71a4c3e2b2e7bc9ee6c", + "secret": "01f39c4280aff1a76871910fb1bf58f4f571c5958c636af2d688e7435e5f0aaa07f329c78d66b1af6d446ee5f37af1762b6f2bc42bbb957930d26bac97e4f841c9271803a372835b2e9da0126f8283871106dd5af2f53dd08ca314ff09a4b56af1ceac0d86e05968fdba1fb24e9f390b8d9ec90b8c461f5da8603ec6cfa9f4d21d31e8fcb1e044ceadd732fca1a0120a812051b63093a9ba015c9022632e314ddb144f75b5667391d454e6a0b7400c615d970088cff0f71a4c3e2b2e7bc9ee6c" + }, + { + "public": "ac58613289ddb05994e3fd328c9fb43fac281a409d5d99d2764c733f13d17b77b8daec400d3d7fe4e7bf951bcaee364613258c737f6ecbd9acc5f950a7dce8836cefcdfab13453675a91ba71bfb8984589d47b8bd1b1d3ca71f7d0ca6a160d33764f6e125fcecf7157b6170bd9766549dd3bd1f6cf477b2e210b14a8b9039091", + "secret": "836411533d99f63713c639fbf900187e60ef5a9f2a2906f2a5bec97fa05f111bef90c6a61b940876809b2cd698c0426e0667cf1fa04e26c026ea1f9dd3a74cc2b8daec400d3d7fe4e7bf951bcaee364613258c737f6ecbd9acc5f950a7dce8835a6e7fea120d07b6035a1f493436b30f0b47683e642528cc40ec3d7b941db0bdb77d793775d060e15de7a8e0ab94e0e9661eaeded976a222eba813c6368847b9764f6e125fcecf7157b6170bd9766549dd3bd1f6cf477b2e210b14a8b9039091" + }, + { + "public": "7eb029259d5ec8a075c66d6969ec09710d57324e55ff49dc8b4ea86dc0a2b3342cb2f8f8db8ff9969c3066c2624eed913357f978070625451daf01f14e635e101567fcc195318ee278165e557b24b7622c643097c8c57120bb5a492b2e1b4961133d0db40038e87fa58be86fd7fc3f564882ae7a6c312a0bb8f0085fba0a2dae", + "secret": "ed36ee5fc1860507be0e47c2216550d6361a3483e70c22e3ad70f43035f70cc5d432b4f621d76dc61ee7c44804c5ce17f38c8bada1617b7105043e0e5019bb152cb2f8f8db8ff9969c3066c2624eed913357f978070625451daf01f14e635e1055ad20f6934ea344563e96fa01bfd9ff3470725b2b73bbb374f4389ee73e67266956bec8b18bae6805a2766fbaae0ba5d3c06de9de62be434aef4f2d3b8aab5e133d0db40038e87fa58be86fd7fc3f564882ae7a6c312a0bb8f0085fba0a2dae" + }, + { + "public": "b717b201a8f013e79ac89ec634a4a1be3270902cc8e71c19a32613db0bc2f67f1427ec05d2e2ff7a2ade48ffd72ed650d062ee07ed07cb089ed040737327f76f36f21596c08c66047f5d9e700bf791184088391476c5414e221204f1e3b43752bd8b56575cbc0fe5d00bbf22c5655ac4723ea2006593ab61bb1d00b24a0d6771", + "secret": "3d3cd39d13f5d0232735cb3dee8cdc2a4ead61aa5094e333b436d4f89f23e6f619df703bedb2d1a15c44f62fb70ecd8547b48d1bd27b3d7f5118c2c489c335461427ec05d2e2ff7a2ade48ffd72ed650d062ee07ed07cb089ed040737327f76f762646ec37d8fcd2cd877b38c1cd70da95116a499a5acaabbefcfdf613d13a4014ea0f2e22461832356d1d80670ce59bacc5b8da197b15b3cc5e0b5dcee7d0debd8b56575cbc0fe5d00bbf22c5655ac4723ea2006593ab61bb1d00b24a0d6771" + }, + { + "public": "0d435bc0a9095fc61256d4457bf0fc6d15f002402e1fabc9342b55c9163a7ab5340893948e3a244122112aed68b945e4326621eddf727bcf642ef0897e7b805d1b56d624ba87f15311aa2147fb70651531f8abe54b0cdce6cde3f4ce056378f241977dfe57f0ba6c017afe09c319785183e0a1b1dd9d3211e224b38e249d7414", + "secret": "b771fe86283beb2f0a3e317ce7ef21ba9229f78dbb3888dd8fb2f952417b611b892bb857bcada11a62c8a0599e3dff67ae63b8c8bdd545aa5da613a67bc1eec5340893948e3a244122112aed68b945e4326621eddf727bcf642ef0897e7b805d9ff35dd62c612d6e9a1de2a7f06d6516c02527332d038530fb2e9ca25af2d53bf28267637ae55086cc4a0d066c7b820b12691b782bc73358407f936b0358298841977dfe57f0ba6c017afe09c319785183e0a1b1dd9d3211e224b38e249d7414" + }, + { + "public": "e31e5eeb51e1f1602120d373937349a42e31b24580e726c27be2a38c672e6ea47a549cef532f3adce53a77e95d08c9d23ec7d3e284c26bab453bcd1392d83dd83193fc5dee5baa02d8ec7a798b484aca067c3efaedce970647ca9228a9b7d9e284a9b6318cad47a777bbef5eebcb67b07f8c80c684f52575dcacdfcb45e4d1e0", + "secret": "603fdde8a351a9da4a55e3921c2dfdc60c7814d02c452cc216ae6b41fa707770e0c9d89289b96e993b667e9cad34df4cd0eef01f4bb6be649f79fb533621f4b87a549cef532f3adce53a77e95d08c9d23ec7d3e284c26bab453bcd1392d83dd888d8020c50dc614b6ea784d1cca4f39e9fcd915c5eaaf307ea66613f08d060d80f70f219be19b1634bcc71b8f14e4f2c0370a853e249996b8e2dc8608bfee70984a9b6318cad47a777bbef5eebcb67b07f8c80c684f52575dcacdfcb45e4d1e0" + }, + { + "public": "744a8ecf0692e4376081e88c02847a4f7fa9f15b7d374b8bf76e8769b21bd2d22d22dbc8f081bfca484a99e3d21dbbfc1a1b89ea3ab107437eb64ba4e6c12d164facbf78cd812aa0c23355ca2e2a641accfca1801e48bfb16fb67fe49b9f7197e48950fd9df6c56f120c5df28a206b4e7d6daa35310e34b171906f18c00c56af", + "secret": "ff4f1df9d65fd24e0302c8f206b1fc35c1ae95c332bd558a82b3ae57b7e8a56aba7a1065278e0f9ba83a6ee8afee63599669595d45005c288b302885a6c525312d22dbc8f081bfca484a99e3d21dbbfc1a1b89ea3ab107437eb64ba4e6c12d1660ea1922b425f44ae2faefa45fca122157ea46043b9800527dbe9d04085f132c4439587b230d0f528edce8f10fb85cd98fa4068c807e5aca6af90fc70f474af5e48950fd9df6c56f120c5df28a206b4e7d6daa35310e34b171906f18c00c56af" + }, + { + "public": "805d485d02718d7639dfb1f9e82338cdd0e0154d90051daad684b37965a8ba8d932a8841eaae6581806f8aec8dae32808bc489ba4c5c3bbc8ea3caf846af2d46afe7e18686749f0fbefe84e034210424fbbf99c8739bb71a2e157184d55d6bf1d99829a194379f9a857524a035295a8c4e83f539599b58bd45097389d92895ea", + "secret": "de3d1961edf3167048a2036deec3bfb176cc133dde17b03eb7125313d7bbaf1d5c8d0201d05966485e5cc9a266ab33d7e55a7efd2dd4118cbee761e0b358e37f932a8841eaae6581806f8aec8dae32808bc489ba4c5c3bbc8ea3caf846af2d464b201781b9ebd39a51b9c8e1d40344afd8c56eaa740ecbb18360238b6317932bdb8172efef7fc37cbfa93c1256bf3785347c94d86d9051170b18b3a07cfc9f32d99829a194379f9a857524a035295a8c4e83f539599b58bd45097389d92895ea" + }, + { + "public": "3ec546723ef6fdfa7706596da635d14ffd55052a4b6d6b8334a1d71756f9aca9f2b996931d792b9aae24428ef91c0d4148219c307bc6540f4af0a9bee452100070ecc63ea909bee274d98ff2271cb99e643c0850e7daae0c81a3ac8408d0898a48307372a42d11cc83a5d2d0a4a448ec59875f13dea44b0cc1c29c6f279f9487", + "secret": "5cdd110cde48c54eed00c1625169acd49983f67ec36059e0151501cb454c529712508e883c3426078112a0a10d522e4af7e6232c0574e657cd75724eb6f4f87df2b996931d792b9aae24428ef91c0d4148219c307bc6540f4af0a9bee45210001f5f3dddd56c52143019a13ef8238ce56e9a6a20a4cca884199d277fa1ddf861eb2f064f02838ddfc2faff5223a93d045a22bb76f0544a33db2504b8f006460748307372a42d11cc83a5d2d0a4a448ec59875f13dea44b0cc1c29c6f279f9487" + }, + { + "public": "b7bedb81bc6dc525f2a358d4b883592a52c8a1cca38d7bacbd760410c1fb174eb736be9acf70ac3d7b4845f5a665f282333f6f5b87fc0f7f0799593ee3bf7f7f11e2b6de9e5c57e4b2e29f5c6294dfff076e1cd462547ca00f93a395cfadcfb176314d21f895eafbcd0863c0a812a14f7182939222bc27fe4fb5bff47587c38e", + "secret": "8ce53e2236a3734c696932e35b6aaf6ef07b706ce8ba99f9f7f0d8e235a7266a40d675cd133cadd16749f53f5ee99fda11c49514d1e69f112d43e0917e65799db736be9acf70ac3d7b4845f5a665f282333f6f5b87fc0f7f0799593ee3bf7f7f8aa7350abafafcda07cf93ac340b3efaa523e5f7b1ee8d630b61ed40201eea7db0654e71d4aac5efd1e32be68ef0d0d7c187f93680f614abde7aa9216f8001ad76314d21f895eafbcd0863c0a812a14f7182939222bc27fe4fb5bff47587c38e" + }, + { + "public": "2dbaeabb63e4a8b3e6d929519bf989cbf5039dccd030097e0c9559b511c904c544fa6a7856c5abaa1f19b55fd61f0930e593cae0ab618f089f51dbaaa4aa0b05e5091be439a6c28c0adef798c3bfe93e560b6ed0a63ac0b6666bb976c8b0108fad2d189e3da3d928c0000ccd5381d5d4e303195df165492772260e2783830017", + "secret": "f0e9664a8aeb799e67ce1c497dc38604cabe623315df74edae9ec9bacb891545079c50745fe97b6520518f44fc7dfaa521bf4fb4c166ddb7fce9752f7b94bcf544fa6a7856c5abaa1f19b55fd61f0930e593cae0ab618f089f51dbaaa4aa0b05ee4a4cd695dd08261a308f80ef50b560d1a16c2b7a0eddba2d59c5e4ac1390e5e389efcdd99c7339013fc52c7816ce7fc394888d78a5f65f963ccd168d4f502fad2d189e3da3d928c0000ccd5381d5d4e303195df165492772260e2783830017" + }, + { + "public": "e8ead06aeca54e7e207891c99ebe1e0d43dd1d68cf223c4171cbae7e108c9e73cff0fb0fe670fff21bac7cd3b32a891f99cbdf67451e4625895882bbe2a1bdd74245e1db8b8079fbc0028352fa71abac27e449c6c910d86bc21cf092f506c8cb8a467e03026ea129d186354e59479f1bf31ba0bfbd5c01c9363ba09b9d603cd2", + "secret": "a1653dd2a5be5f6115848fee826fef70e98d9949bbc90f6ec670424eac6120ccca91e599db9e6141b1184813ead88dc2c24ad0d1ea9460c185f35764c06ffe54cff0fb0fe670fff21bac7cd3b32a891f99cbdf67451e4625895882bbe2a1bdd74454b66f24ebd39488b889bc3a25346b8fcc7e6f6fd2a8d079caf65035f19d5b5fd0306b6173fc6a46a00415737b86360fb6a90ff562b5866f8b315759f770888a467e03026ea129d186354e59479f1bf31ba0bfbd5c01c9363ba09b9d603cd2" + }, + { + "public": "952344f1a11200a672e37e05dac9915aacf9364f3143f82d573e0ce76a7901f5e3e6dd67c3afa193473b7ae890fe27dc43ee50773224800743012d1f38f84708eca08014eba03e852db828c6d06c28bd95a2871c346ddfc706d88a9eead39e0c73760f5dfa950596d494f255cb7fd07973f5fe2aa87e4caf54256b72ac27f0dd", + "secret": "5018cc002bef24518b147ae56a84262d5428baabc16bbb82bb6e35b17b131366897f89eb2cd2f380874760c7cd35ff26405c60e5eb4bccc41070511704e04731e3e6dd67c3afa193473b7ae890fe27dc43ee50773224800743012d1f38f84708f2714afb5f5e36fa447dfc9e6db074e406fda225be341be85cf2b788112554ee7260e8ce2e412d31f5826fb28d2df31e5aa0d5eeddc5a7ad42b3e47f3c3fa64773760f5dfa950596d494f255cb7fd07973f5fe2aa87e4caf54256b72ac27f0dd" + }, + { + "public": "8b07ec70d9ed42fd01049e11ba410b508c5a42021ca009a1c46bd0bbe8c97ba93eb649502ba4fa44ec7338392a11ecf92ea3e7a8967e9244e72ec5cb257bda4cb252ab02d55c562a5e26c52b4252e1b16d6334b4984e5ae11d950fa02896a0d0fe3fb9ec5a8f88df07aa62d035aab10083fbd42295b441a6cfed0aa186a86b32", + "secret": "a3c42a9971b97f972589e745264b856d29725ccdb53552de9bfcb0dcab1834b7a84c4cf73c8a2102cce41f3ea56283d75cd30c3225b6eb046f032ae914592ec23eb649502ba4fa44ec7338392a11ecf92ea3e7a8967e9244e72ec5cb257bda4c23d1ccc62ff357aa61c1f758999a7bf1b974d85b433ee6d6a5021faac5780e11f80c9573682c3ed739b4aec44627bbfe718aac1e4d4a40eda3a26a2bd9cfcb91fe3fb9ec5a8f88df07aa62d035aab10083fbd42295b441a6cfed0aa186a86b32" + }, + { + "public": "117e4b9399274a455aa6aa339011c4c4fe899c7268029a905a99b5ca86fe3a0ac66230621ffc06825ac6ffbd5dedba402b70039eaa4a24ab0e6357142c277898789cac4a7ff751727859b85335bca960f7aea3f74b8760b3488c1a6290c8728b30f06f96ceb85314063ad76bcf599cdb5cd52db1ae67da4729d6035409a45c72", + "secret": "52d111dec37b4dfe625f0884a7fda0632624f26f786a1e593c68df6761df2ca50d0405b4598c1e9e4b45c74c2f68a789738a44353630f1c2059c65725f243569c66230621ffc06825ac6ffbd5dedba402b70039eaa4a24ab0e6357142c277898f3c6dc2f847046a7d5c1254fbe54e2bc9dbd20bd161abc0542f8ea33826605b5ef7e18f77534b22bd792989b7f1244ce854bacc850510c229e1d03f81809db0330f06f96ceb85314063ad76bcf599cdb5cd52db1ae67da4729d6035409a45c72" + }, + { + "public": "98518185a45b9cf53ef1f11b9e1cf84e8f3ca1388005935476de5167cbade7d4f586126a3c7c08379db063631cc4b0602421b51a108087193a28ee613bd3054eb095a027b82d715265b4fcd7bd244370c8d0052a12c6151b976f96703ded0b61ea26e53cf32ce660aed303d383ce339d13ce5a654fbaedea7672896c8e9b6d77", + "secret": "96c457661bfb3fc1af99978e8f3e36aad4148c9e2c588b25a958ebcd471728bccfa0bc998ac6349772eee3c18272ab5ae6b1973cccb2a63abbf40ed3c335abd8f586126a3c7c08379db063631cc4b0602421b51a108087193a28ee613bd3054ea7baee3160dfe29da0547951d9b81647594a642b88f99a39ec8f9e1b6e57fbf2a9fbe376b4847f26eb644537fcf03a8a5911b29c8c11ebf79bd1425ea50e3b76ea26e53cf32ce660aed303d383ce339d13ce5a654fbaedea7672896c8e9b6d77" + }, + { + "public": "369e99b111112648543b3fd7ad8a59278d0da849789e854ef315393042a35218b848883d999a0bef7fa75a90fe779416d83f996edffa0762205303a29e93b29b27ceba94f89ab9dd0a1961c2f2ddda05e5e36cf97cffa83cb479df3ad923407b9c84024144fd25ebe1edf58e1a04ab031646d89cd0eebf09d5abfa853a06dcf6", + "secret": "6f43b50aec2b4b89e4cf4e6c68b4ecebdaeb64f6a99ca8081992f0df31b664dc25b30d6876cb45c9f4fb51abe0d0f64def84ff5f0b12140b42e86debde5e0f77b848883d999a0bef7fa75a90fe779416d83f996edffa0762205303a29e93b29bcdbdc2e81844bf90cadf62056e9a08ee9903e6156f2bcaf1262150e0ff2a6661d238bc10afd68fe8d382ec30f3e28c079cbfc104f684ca216c0d9a91eb93f5a49c84024144fd25ebe1edf58e1a04ab031646d89cd0eebf09d5abfa853a06dcf6" + }, + { + "public": "194adf2ea19eecbe3a594def0df90950231592db1dcd92f20576393ced73cc1359a211a643e74daeb46d3ef133038f1a5715c50dbbe1a55dae5393cee6731c545d22ee009e8d214a7e62c65336f0f3be30dda7aa1a3a1e469b77786becab031610cd5fb5b4cecdf914c00d7c119ed5f44f8cdf27d1fa691a9c9306f407811e0b", + "secret": "7353b29e7a50007e9178adc242a84837e4fdfc6fdcbda640999302396ffca028b00dcf87306426d8b3b69447e2ec00b4ecaa7b91ceaa3ad31056b89bb516d6d359a211a643e74daeb46d3ef133038f1a5715c50dbbe1a55dae5393cee6731c54dc780e0d8a15cd59354c9d1ec68e6ebd0848eed7af5d74f7173fbb0cd0db05e6e4a746736a7c60e0b6fa55ad4fe70cbbecb1113f93244b36a58bf7dbc83b964910cd5fb5b4cecdf914c00d7c119ed5f44f8cdf27d1fa691a9c9306f407811e0b" + }, + { + "public": "d3d415bfab13e061578d52cef6884c7ea61a793534a8ff232a93c1e698fff6a464049af40062d579b3ba06dc69bcae486aecaa730cd49612f4226a438a704f9d574623df7e3aa7dbc98a6bb9a5fa5469d3eeaa2a903c0b3699068af16e758676989a7883c38b5a362feab544b3549f48673f01f67b527971a4540ec70d3c58df", + "secret": "9e5575cfb5a68d81ebd2930e631858373114357c2a7e06ffb116feb395be41452c1d8917177e4a5ae42f0b0009e44bba48521c38dec248ea134064f1329b4d7b64049af40062d579b3ba06dc69bcae486aecaa730cd49612f4226a438a704f9d73e66f3d6b4e0ed76aadd35ad4eaaf34c418f129fed85965d002c2920cc8511c095265ba88c1c67f9f2aeb098f8301c2b205fee1ee2f70eb063a103f3a88f119989a7883c38b5a362feab544b3549f48673f01f67b527971a4540ec70d3c58df" + }, + { + "public": "57e94f6587e59dd6875924d24698e70a6f8a121ab2fcfe5410fa0cfda6e50d6bda6af5bb117b0cd65a06e85cee87d4e44334dce129c5c8244a1f31d99bed35226a967fcbeab8eeb6f2af7c7377064c81262dd5468757b36ccedbd62160c9d6a57a55b96b1b9b2cfecb8e10210b9321df1095c886c18f493a0885f36bccf678fd", + "secret": "9d770522b0f53fe5ad34f990e7b75c0b618ee5909903d36e7c7e1f23775ac359068ee7c223633903ceb5834b804aeabdac35c669b5b0701d18fbf67b5a260681da6af5bb117b0cd65a06e85cee87d4e44334dce129c5c8244a1f31d99bed352214679a8e28949289eb623c0d3800419017aad1d89a32537a5e30cee93de550056376172bd4e7bd26e1b5501c7cd5b28aad2857d5463f50a342fc87325b3311c47a55b96b1b9b2cfecb8e10210b9321df1095c886c18f493a0885f36bccf678fd" + }, + { + "public": "9a02cce2a2304063be6c02e431f0ecb74aa33b0d669fdac317eb602278d2ea6b373103a197c8637a30467c89850ffa449cc5077f7bb73b900b632741206a7dbf7d088db70c08c3fa9425361e998cbb37da196ffe3ba931ca9771a942c057222c106b8f0b75c6ad4971a545511e864027f2e855e7a84f7778afdfe23833d4c495", + "secret": "2243249c980c338411548bef7c88d444c214026579fc14c4c4435d6a4e1f00156348974ff1cae0229fc8f3a762c7fd3a3e5e8996b80165af6b9e42beeb98d7d5373103a197c8637a30467c89850ffa449cc5077f7bb73b900b632741206a7dbf41c1ed0a1879e8e490c3fd74fd8e873dd98af8c852844357f782ee5665d445a32d141d0466bf3360500e600ff143b0fb435bc95c58c312713fbf97f920fcadbd106b8f0b75c6ad4971a545511e864027f2e855e7a84f7778afdfe23833d4c495" + }, + { + "public": "473ebffc232e5e32620cb645a679e7ba19d5ebc951b5fa41e31fe8dc01122c1b015cb6d016081c4cf9f13cfc7e77cdd7d43a620db97a66b97404df77d4657942427f88ebb56bf8e764edee9f432a3a1196716721dcde4c43125c1360c3d34d750fccde94ffe8c13fc836510cd11c02e6161e274f2a1943a6cb285a6e195d56e1", + "secret": "7eb81ba3e6532d062da017401dd2797b2d50223013e1c0012c131e46375bafc1af76b32547578b750e3d108f98373aac9d9b24d763155ba411c7745cfecc0253015cb6d016081c4cf9f13cfc7e77cdd7d43a620db97a66b97404df77d465794201fa77a2d53dbe066de0d4588c70b084c495d46d7b03d25c4b369ded8b6a481f41eb9e70cc0bb2c234e41445a30648b141421bcea638a32fefd8aa12b25fc6d00fccde94ffe8c13fc836510cd11c02e6161e274f2a1943a6cb285a6e195d56e1" + }, + { + "public": "0cfad893173f2558ab86c09c1692d2df59733e5e9f69d25c96fdd4235a2bba8810b283081fdbdcb0ed4788741d46aeda7ba93a7ed03416b0a87c19807d096814860d9b4c16d5f6185355c49f94c4933dbcbbe3b1d8d2da521490f47f4da2949d8dd0d041d672ebbc763c3c96e4ce78cd78020d11c8900b4fd6aa26adbb5a257a", + "secret": "c326ff1d6c979bd4db85fc6b1cdd524bb8680b48a49ab33d1515d98abacad2f38c2d0d4be349670d6900e2b11f0580d0e30279f5daa9c519d51ec81e66f7ecb810b283081fdbdcb0ed4788741d46aeda7ba93a7ed03416b0a87c19807d096814c0cc37c1ed56f75f06c88a225644ee95bbca9a4ae4e959922e890db46d6d25cb43747be0133f64d9b157e5a2d5ec3b997fa877fed7a6c2c4d9ae2132b7a00fb18dd0d041d672ebbc763c3c96e4ce78cd78020d11c8900b4fd6aa26adbb5a257a" + }, + { + "public": "6ce53df1bcb84e0f7689d5137a107765e0b37107b6c99b8643f31868ddef319f4d081f919aaa75bf57e78ca94738e8b6247021905e20eccc9e32b402d4aa03256b9786ec12e85b29d8dab54c214bba8f0e58f77fff057f9a2213532cdb57bf92598fbeeff890492811fa1e41ba43bc136d410c36fc07f5b7a141a58b78b4875c", + "secret": "ca1a2d55757cedf47f6a9f5d3781b007995a0fe7ab7f63da9c670ffb31a6e7edbe460a89c7e290acdcf1a8abc34ec733f11a867fee971aa1cbb2ff458017426e4d081f919aaa75bf57e78ca94738e8b6247021905e20eccc9e32b402d4aa0325975191a4c95a0e3e7b4deca40a908ad0d9dc4be82b3a3d4bfc96b830af2223aaafa45a96e8194653b52abd0528981aedd21c5ed88c5ed678007b2ecb9ecdc9a0598fbeeff890492811fa1e41ba43bc136d410c36fc07f5b7a141a58b78b4875c" + }, + { + "public": "06ae73bee785f3b0da97a3a7eeccb7f761b9469748545fc6f9709dca86dcd6476fe553806c7ce16b0771de8eab86a16451d63bbe1c4d18d7176b93822d3ad9b3ecba9782d31f781bfe593823180b339b8f114186375d3ae963fefa7ed9d72a05460295f2a9f498f75f4dbc50ca15e8c9c332a78043ba71686d9318e245306019", + "secret": "2a54db99a205abdc30bba9634a2a0bc47fdadb1902a824953b86a66b25d8cf915fc63255af7c37b966927fd8422b6fce0a1414672af9d20882fdaafa4a6efdc76fe553806c7ce16b0771de8eab86a16451d63bbe1c4d18d7176b93822d3ad9b3a2fc92a891cb3fe70bd0ddfa629f530d0359c173c1c577e705cae5026ac37e47ae76fff8080a275073676586bee16fd57b7b9a56486579c925c30f8cb367c51d460295f2a9f498f75f4dbc50ca15e8c9c332a78043ba71686d9318e245306019" + }, + { + "public": "f24a6fe9206115f41c5f4e31337f0de2fd81e1495d118de9a9258b26dc3a083b2d3885cbc9c6f5a4141f3c5948d4f62a028a4794fdcf1e1b64890798011ea395d1588d137553395d0963a2c634648802476edd7458aeec488532bba0107277409f92d48edc32a3676b4ee8a1ea24b0e6add9c8aa995aaffd1e3c151b97d54a53", + "secret": "e772b91ce82dbfa9d2477663b66a4ebd99d79ba7917c6345436f0e8941cac9c97f3dbbcbf19cfd847da776b0e005d36e4ac5cee92e210ec27f54f164baaac79d2d3885cbc9c6f5a4141f3c5948d4f62a028a4794fdcf1e1b64890798011ea395afa126b1cc325291bdfbb063e928c09cf9edb70079da49705084673902b0cadf3a3e6168161aee7895bf1a8a24cf2d5ba0293f1e882e81a8be3b85e6d47fbefb9f92d48edc32a3676b4ee8a1ea24b0e6add9c8aa995aaffd1e3c151b97d54a53" + }, + { + "public": "03061858764a431b68e8ad948f2c42e10c502a2aa3d8f657436259f1a5207e9fdb01546343cadb1adcdbab52ebfd143924fdb37287b191329549a1812e86233e4d98b3d4f9b1ea6059a901c8a8ba8bba397879c12ae0cdc8e8329f678cc2381758fea53a8ac53949fe271f1e423ef46343c20285d8b4bea79c5dbe2461bc3d29", + "secret": "f72c908748d5c2a489a1f5785f347fb4f5a706fd3242614b0c10fd24078e9f9d771be4bfd166086386504c4e474857e7f76a04f384f1da3a86b27a3b938d11e4db01546343cadb1adcdbab52ebfd143924fdb37287b191329549a1812e86233ecf682a88fc2bea0097abaf435d301d49518cfd6d9f23f4cdefc00c4ae2d5a90a6704007d49bdb8d4c3461bb234f6365a968b31b13a2956c83d277224a1fa2edb58fea53a8ac53949fe271f1e423ef46343c20285d8b4bea79c5dbe2461bc3d29" + }, + { + "public": "be7dd768614a452e3608daa0caeb17c66b02020b81597eea5bd360dcc5c43f529371b9a0ce6f9f0b89cf4de9857ef943d0727aa04574646c2089dab086ae29f3ee9c85805e836fd3f27598d7be5ad4f9e62cda141abeb79c3f8dcd525f6091948d13b47366b6f66743ceae1fa3d0ef6a86dcd41f3a12cfdaec2d9e6c365059a8", + "secret": "4838c7865bd0b0ce1e08aec9eee76546a801b3bcbbfc97e5f6bc2cd4a2998640c2eb3bbc48df0357e25821e5e957e1f6a02fe96060814b673c4aa1ab725819a79371b9a0ce6f9f0b89cf4de9857ef943d0727aa04574646c2089dab086ae29f3190cb51bc2ba50a2be1f113fa7167526cc2b3e34f9ead04823c49f4a65759f74ffbd456720893b39537151705b7562d65ae66303ab1cf37700bc79c156b57bdb8d13b47366b6f66743ceae1fa3d0ef6a86dcd41f3a12cfdaec2d9e6c365059a8" + }, + { + "public": "2d40c1e9cff84863ee69cdd6398853b2639bbcb0c589f1e9fe585f85ed81a5a2219da0836be79d24d4e79cc4179a68cecc4d62365ad4cbb27e159d62d0a18dfa74d04c25281943a2103bbd626f83e475514f93cafc053107dddd9d6967c4dd5200720a28289658b37028b4430de7171770b4dd90669f2a18271f170aa3df2d26", + "secret": "260d468f1e911ffe67c6f061063ef26ded23397c2e9f12306bef7882cbf424b5a665e289cf1b6b70bddd7d94ca158ac3eb07e6617735f894926b351d3e897853219da0836be79d24d4e79cc4179a68cecc4d62365ad4cbb27e159d62d0a18dfaddff74807c214889e6bea209bd16115effddb850cbdb8c5ebc3b6b8f39221a077e87efbf88089a5b321b12d7dc8b3174446e38f1c4e2d94d615266342e29e7a400720a28289658b37028b4430de7171770b4dd90669f2a18271f170aa3df2d26" + }, + { + "public": "968dd4338b8e5348e209aee079f12a22b76beeafd48e04d3d7d0468b3072e6fd5602614cbfe71dff088e1725b64ff267aa1b825d8041459285be5fe25a68b7cf0d719e63b43e26c05849bae087a16e82ec167ca03db6b5ea5fb874d3fcb57225bb4b4c7452b877774f82046a32d62f163f9d3674e705829cfa692a6cb615e9a6", + "secret": "799183d3835dbc7863c91f052da2f1b76e3d8b80d09b5719074d8d1246e64de231cbd8fb95000a3ec5d117cbbc216fca3d2646ad7959f369a0436253bd9189755602614cbfe71dff088e1725b64ff267aa1b825d8041459285be5fe25a68b7cf825bc14f86445a64add36d1cd53c053bce0a4e6663aceab3246bcf91efb48e97f9ec50285b819d98b308f475d8e7872c87c7aa1e7e248b8175d956d670a9cd6dbb4b4c7452b877774f82046a32d62f163f9d3674e705829cfa692a6cb615e9a6" + }, + { + "public": "ae01f80b18d6ef4b3fe66f8b880a87f9ef65636903acc5d734330f10a82b44c7747bf148a802f13830c4becdd91f2cc3511c4f40cea97bc05401ba18ae3a09479ab472ba72b8046fd132ee8b2e9389bfb2f5c3dbdba07be0d939b83e05ca3d4606d62f8fc76896ac77c34f12e78b2bf476499ef44e72fabdc187919eef8384c0", + "secret": "b2d0885ceeea7f6ec936479c1419c878163b43e45d8b5dbb22f6ece60499f4b23612f2001236265b979c32b49c20c7987064ed4f16d6ab022c748885ab2d9b45747bf148a802f13830c4becdd91f2cc3511c4f40cea97bc05401ba18ae3a0947b39c52f3a1fa3b207d7cbff5f226abff14458d8dc11d7d241526d023b837ab6a2cee96be94843879e09fd500e6be3dbd23c60ef59dfb775d6505a389d4a7f31b06d62f8fc76896ac77c34f12e78b2bf476499ef44e72fabdc187919eef8384c0" + }, + { + "public": "c53db6a2b148e8f2e384e6931ff0fc6483f639e6cf177a519332e16fd022009882e24263097bcccba584f62b62f7fc3822ea30a53b67f72ffff21c5d6a8535d8a7dc5a3542f2a9311d637a8800953f54304f463a69b07d049095fba83d1b8bd33d43bceb0655d52e995e9ffaada1f1ad88d275b5e0600c6a8500c2d2827dd9bd", + "secret": "d20be92103188c4e2674e3d0f29d72c077a7b8be937e8b175b704519442f3021faf3a7e6acd163c4afe7f1aea2db78cb7eca27b3395ae5d6ef75b46157e78c2e82e24263097bcccba584f62b62f7fc3822ea30a53b67f72ffff21c5d6a8535d84e4804c1dc3f9b5148d38914100c75f6bc7f93830b82ebda819ea887ad76ffd0662cb2799808e465923c86b59f2f643896eab0a5a436990e26ad2e0a1b78df2e3d43bceb0655d52e995e9ffaada1f1ad88d275b5e0600c6a8500c2d2827dd9bd" + }, + { + "public": "700f6f49df738c0a4c3d840bad941dfe32020ff6e1a60c4024cc9408b2da7845b55e46f150a1cb60e8914e9f3ace6c8b44f562151c94580a0e2fa7b10aa930e9b6d04de36468137394403baafc0658fab1a2c8ef8c80603c715d1f9bfd7d5677abefab248b8f736ceb10ee8e23b4c2d32ce6d71de71de26d344ca9ac943d09f0", + "secret": "fe9b4f6b0db80612a5b9467a016c939904a5b5ac9ff07e268250da5e0327b8541ee04ba1c325c33bc879ed39828090578c95136ae517280371124a450e7a9bc6b55e46f150a1cb60e8914e9f3ace6c8b44f562151c94580a0e2fa7b10aa930e9bd28f1b5100b013bb51c2f80d91de3d7562fe1910ca56cfb084adfa5ad95f08c9df6ae179c5bca356038aef90622b96d34967f52d5353b6d1af3a8819abb9953abefab248b8f736ceb10ee8e23b4c2d32ce6d71de71de26d344ca9ac943d09f0" + }, + { + "public": "9167f3e6a21c4665ec88d22732abd0a234235db60ef2a291a389dc00943c3ba48ba88cf06c9ec9cc50ff2d26b0a2ebaeb24aafe7b9c525f6c99363b295d8323029e0856d6118e2e5db9d6428eff40447cd75da9de36dd8018566d9f632919fb1b9b86d02573e446e8205af62a4282fd97f532502089b09234472cb907bcc240f", + "secret": "f46ee5d3b1d4d0d6ebfe7f8c1ab0973dc312f23a4d02c75dc85fdcee4d30af4ea7ef5ca6f0a102b371530ad90801828bb90ecd67fb1d478c1c511423f177ae3e8ba88cf06c9ec9cc50ff2d26b0a2ebaeb24aafe7b9c525f6c99363b295d83230f7a20818a648926088fbdbb0d2477379abaeed4bc0c26addc91dd6f2991889c202e48929ec9294b0c4b421800e3b421cb0ee49a6a8fe6f0ad2617cc81e83644cb9b86d02573e446e8205af62a4282fd97f532502089b09234472cb907bcc240f" + }, + { + "public": "9872578e5d39ad912045c1b2b61c996b14d550a8c24fed13c7ef1861e54b40a9092e94f3b92bec971bce5c57e933ffb8e95cba2c2cf95cc5e24e5fef6b007bb5b56daf276879a25685035bb041a066c635d08dcb3fb65d6a6bd52c249f40019578c676733edbb94950e03fb9f0cd61e2d1091e73f8d52b7a1bc2080166090127", + "secret": "ef941b19bc57525fece9d371864f7fae6b06384eb0c35ed080b3ff6d6a8da76e49792716a188a2ebdc2d1975ad2b9de3a7212fcc3ed531db84227f81c1b41d53092e94f3b92bec971bce5c57e933ffb8e95cba2c2cf95cc5e24e5fef6b007bb5a22367abca4939d686434f2b602492e0fee1c8b973c0aba9fa8badaab2c97cdd7f71ee351b5a27e2b9d2a389db0b0740b49c39ffd45d9799d34221449944b54978c676733edbb94950e03fb9f0cd61e2d1091e73f8d52b7a1bc2080166090127" + }, + { + "public": "5f5362dc070d1c176388cf8aa7384cb3afb050483ba3cda908e9ac3bfd20b6b570da20d5ad498cbfe5f40de6ebd3c44fe988b364d6ac1901d5ba63d7a9ce04c0982590300040b42c225699332e08396d3e065d7a3ebdad69a847651db586913264d9ac930a762de12045211d1631e4c04d32dacb1dc670281dec1daf6bc5955c", + "secret": "6c287acdf981de221b719b5e3a595348b41e87c9ce3c25ce8b5e7ef5d06e9d2fcea0cb248493379349df754de21e958c4b6b3595ba90f8ec4faf7331a5a8081770da20d5ad498cbfe5f40de6ebd3c44fe988b364d6ac1901d5ba63d7a9ce04c0fa532802fa7d1c1c784870aae4975dcf596ebb0922447eec66ca207b4efaff50e684721f89ac22e0e4d345f4b465559499d2148ea6c85c8e8faf781e6cfea8ca64d9ac930a762de12045211d1631e4c04d32dacb1dc670281dec1daf6bc5955c" + }, + { + "public": "4c819765c16b0c0cab9ce2808bd50d51aab6353738d4e8824b7748c7f3d0102769c29ea2deae5c7b425c9c2fe6928994af6e6f9bb98fada6aecd6d3fb70c9f82d23d2b49cf46f27f86a1333b865b310145a71ec8411b09c89a60a550f6e2c2ba6f224ec0bdfe3299e979e86dbe552ea2af8516aab717827235cd146e5a3b13bb", + "secret": "ff9f8615a2701e2c15111a0a757b6eaec81eb3179df72a6785b21b2e81cb57911e50025de4679581126ab5044a8ddfa00efbecc4b9eb142acca0bcaa1288d5ba69c29ea2deae5c7b425c9c2fe6928994af6e6f9bb98fada6aecd6d3fb70c9f82ad2f187430b5d2092e8f933887fb5e8811c13699728ca67d7968878cb00ee768479566ada6a7ac513886b8693b6c77dcd4eb39449d128c6051fee6384f7fd7c36f224ec0bdfe3299e979e86dbe552ea2af8516aab717827235cd146e5a3b13bb" + }, + { + "public": "9919af287561335d2a19ae7c5335c4383d658d1714f5ff62dd2fc30d0150f0b48da80603015cf29b74b9d7f6d248c3b1944160ba1637d91ea4f018b3d9b504bc947a366e053a1727f544538a56d022dbcb1d4174e4bd8b4e1f3375b6ef8c4ce6a5c172d839a6b82c30ec938abc652bb09ecf4c6ed8882b0853d1262efdd0e9a6", + "secret": "945585883c5c27234dee9b7ada9f04b30c4e82b77adb0e9fa3d077edc7fc10d95ab0116b4324e01167228aefa837c82f4dc2356591c528d86c19f2b32aa3d5728da80603015cf29b74b9d7f6d248c3b1944160ba1637d91ea4f018b3d9b504bcd192eba58eacd52c7bf6bb70a39809555417bce385e43ff48ce6103f6d4b4f3611551c8d38b964799e28a68efbb85c31ad24a8087d8857b6020a4a5763e6d4f6a5c172d839a6b82c30ec938abc652bb09ecf4c6ed8882b0853d1262efdd0e9a6" + }, + { + "public": "97567eebc0ed433b3cc0040636f2f16635ec13f03605d172d0e7bc51ae259a6cc63c4ee29e9c73ea9963d35d80b1eaeb4f83f877158c4b7c2ca3cc9ce534a8fdbb20a79a0a5a433c0726f83be541dcb0a76990a2bffaed36ab3fc1823b329519446daa3f4ce8b572739b807ddaca078413d60a855ed58666036856debe3dd036", + "secret": "921e158313651c9c880c1e4d84268d7c872b2b615f3705722aa74b17080a4c3b1859e188fcd3f7dbb11778883cbb1c801fc87385ded81668e0669715bd2c3b8ec63c4ee29e9c73ea9963d35d80b1eaeb4f83f877158c4b7c2ca3cc9ce534a8fd2144a87038c7041135b6dbff9b20f372d471036a0155042b720bd1c84bed5fd2c3ff67ec2bd393f5f6b868c35dc97713899934610ff4af8ac3296cfa3cfba44b446daa3f4ce8b572739b807ddaca078413d60a855ed58666036856debe3dd036" + }, + { + "public": "5057bbb90eb921f4db0cf9753d12c2dec3588b35b337293b643d6a9f086d508f1b242967d2f540fe263f882aed1ada06b64c7f85cb1692292ba90801936935656b0ffd621da3a09ba0699a20aa0014bad336c736fd092e5cb5b6abbc96aa17313445a75e59ab09297263236ff040d94c1e2c13645b77bc7fc40e091e8bb5ca94", + "secret": "a7dcef6392e0ea6ef2aafa2ea8bc0bbd096ed187dc0decd6c694481bff8106966512177073dda1dacf79532740a0e626f27c890967a2507f33784757b0aca78a1b242967d2f540fe263f882aed1ada06b64c7f85cb1692292ba9080193693565e8d5509aec3a2097978c70343973e0a455a9802b081425c1c224e6df78c00885eb5dcfb0cb8115562e6db912ae79c1ce3e4f2434441b4e13db3e0d6a2dad57093445a75e59ab09297263236ff040d94c1e2c13645b77bc7fc40e091e8bb5ca94" + }, + { + "public": "71d91e90ed2dc81614d940aa222e0dcebf77776cac645cf16518ef56fbd85e15d9076aae5d438583fe1ad78636ef68363f685ccb3ee4562c542d71de8cccb6872e7ef4a9296cff56329f237a3fc3c6c1f7393dfdad3b685b7b1937e3c069dfe7d2699a6a55ee9872b52c51522d6104418455ab402cdfd05e8dffd8e0adbfd8a7", + "secret": "65860a37d510e06bd1b5cd69f3b667946e4f847b4771b897a88259400f3ab152e5bfffa9fee0aab93e639c239e3813e9af5dad1743bc582fcc960c36147741e9d9076aae5d438583fe1ad78636ef68363f685ccb3ee4562c542d71de8cccb6876bac4cd4aecfadc54122beaebd1e63ba5f9418f9c056986f1b98497176646cd4dfe43eafe4812f7a9e87745588d80164a1866f6284110baf1399e6b1c3f0398dd2699a6a55ee9872b52c51522d6104418455ab402cdfd05e8dffd8e0adbfd8a7" + }, + { + "public": "fc5e39c9760407aaf094fa496ac0fc2a19b886fdb7af7bdd4ef911a93734afad87ada8443d350c9bafe46b1b570b8c8ac980018b2e0af2978382cf6144b0622b2c469baba42d7485f4b4926e889cfe426dad3980ed70b67f3a324fc6fa4bf401f719e84084fb0d47d29089fe8b6549ba5bfe65e2a4a8b0f3cef201ec382877e4", + "secret": "871129b42e5ec16fa4deec48f678bf060269c65009fed5ef1ec2aca267da5595826d1cc39cf9d8014199511b6d2c694f1c776a23e882b5d4328e5a76bd8eb35b87ada8443d350c9bafe46b1b570b8c8ac980018b2e0af2978382cf6144b0622ba98c99266e07083c6c083f005ac156db7bf1c88958f54f48246222ee7d9182ac93e31def50bbcbadb33e56c27ba037ee448214f445ad5898b2b4d9f7c8b720a3f719e84084fb0d47d29089fe8b6549ba5bfe65e2a4a8b0f3cef201ec382877e4" + }, + { + "public": "73d7a15c7a45fdb05df32cdc65dc5bcf68c385ad292f082f8ff2e2ea9cef454c76a6a94c5476f20f1d4e808cf5c5ca30c12cde5b85746ea78b6e8625f2707e02bd917c4d334b04050ae62c27624cacdd74e5a377308c8ffdc7061a2f7a97f42d04f6e40b4977f05356fe1a63c9a0131b3d7a653481c83febf4fd3efb18c4ad74", + "secret": "f7095ab862de773f4914db0f794e1c9261bdfae247943b4d623a7187611e1911ce4eee4f5a5d6ed0e3bba6368bf9d190b6998f74e8bf26e844689badb302566976a6a94c5476f20f1d4e808cf5c5ca30c12cde5b85746ea78b6e8625f2707e020a1e54666b50bd41b849261f853983c403c56949d081855dc768424c2d3030ab3373f7d78f935b232ef2454dd105a5b6c7f76fda36adffa058eb2afb9f9bb9fa04f6e40b4977f05356fe1a63c9a0131b3d7a653481c83febf4fd3efb18c4ad74" + }, + { + "public": "aba7aa170f37b423905493de59b2ecc3083d1ccaec663a800fdf187bcb38cfe6183da8484a1170b0e822f713779b25d7f193634ef494d12b31f934cacc9e79ee76c3d49944d6bae01aeec98bb291e90185b2048ae9944839c27019030cda5de4ace0ca6ba44ff8c26c9b8106a0aedace7194548c8e4760d5b8f20fc554acd8b2", + "secret": "0329f5e215210de13bea8ad59ff4db6222a67efd64e57ea0912d12c746a73d0ff9a7d4616013e42ab13e717e36516adfc6de6a701423dcc3387a8201ca165fe6183da8484a1170b0e822f713779b25d7f193634ef494d12b31f934cacc9e79ee0ceb8ea36b05091d225880ef92849e8e853286484345992a2b67041f9af30acb38b17f8347f8f29f07a2d7b959f34d5036787b9732516ef7bd770e31cf130b4dace0ca6ba44ff8c26c9b8106a0aedace7194548c8e4760d5b8f20fc554acd8b2" + }, + { + "public": "566a626ff22416d2d02894573d06570192b9cf263ad82421a33aaf29b141c1c212e315b37f71dd39d9ba3529261ae8642d04df6fba4ac08478f3d397621cec9ddfe14e0b1a503ec7fd850a08d1c61442e00baa8c0a68915b231fdf4d9c5d09c21d049a1c73c995825b9a12445463adfcd5e50d9993ec5294169ff786b2689793", + "secret": "22dbe4a67d24d9e7fea026aff0033a7d2356bd1c3e150f0a5cbfd31f5db0a25c8e4aaabc50605ae46ccb69fe36bebd2045eea9ac2ce50d33e94e6f5aabad874b12e315b37f71dd39d9ba3529261ae8642d04df6fba4ac08478f3d397621cec9d981acff032aad2b8191b5e56f081e35e93d81625cc9b8f9d68ce8e1e407004af6c97d35ac91b0a88e5a6df86fc2d8e82539feb016befa8994b3b783cc73d35e71d049a1c73c995825b9a12445463adfcd5e50d9993ec5294169ff786b2689793" + }, + { + "public": "b141a70668cb8533d40fd8fae3db8e786e0361c7f82e014be837036644c58dfc173c2ceecd803add099b6d3770812837c9b313e8198b675da5dcabfbcdc7876296c46c389bc489265b8b883936401ab4df2b23e0cd2209ec8c0383efb9dc4bac8c612f9def0c902db455d26affe8c32b523b16083b3d9e638541f3de050c7595", + "secret": "0c68ea877512a44b7c2e9c123effc31e7bf194d9cf440fc09550e6f69c6c6b395d562339ef8aa18c1fe82a7ff546b25c72ccad3f20de96b5ddf707d3d09e2cba173c2ceecd803add099b6d3770812837c9b313e8198b675da5dcabfbcdc78762ce66143f975a1dc24f0bb4a7b844450b9a30e61fb93ec7add929a6587497ab82b731699fdac3b2c58ed6846129bc8f45fc25ca0f704948a8d3e0c2f2a4ec24ff8c612f9def0c902db455d26affe8c32b523b16083b3d9e638541f3de050c7595" + }, + { + "public": "e8846c74958315151ede7ca6584ce67dd657ae9f2105eb1ee416d8b16a641fdd7dc7118c3beb0d722c555eb87cac2536864bdfafdaba7b731a14e80d1121fe4a80da7f46af6f7b06344ff0d52aed2e20aa8e1440fe8bf4985c25e6acaf4ae8584071e263064d2b6d0aad4e52745aed1280387dca779cef4c724bcc0b91eec525", + "secret": "c49418990f2ecd5283dae8f77d6b9a2f5f2e8394765cbb927dc175cd5cd9028429e12b5d78ab8282d95842eb0ca75af302c90eb1b6c4c964f4a4942df38a251c7dc7118c3beb0d722c555eb87cac2536864bdfafdaba7b731a14e80d1121fe4ac03d0b94dfbb2bcaf82bd403520c722036c68d3edcc3bad5e51336444dd82a757b9503e9c7602cb71f02b2c7ef4c8a8e0689c153ca25c496aa3ea75ec31c893b4071e263064d2b6d0aad4e52745aed1280387dca779cef4c724bcc0b91eec525" + }, + { + "public": "1e9f81494e5d65c707b44712507f3dea3e6a13bf4f229dad669be55910c6ea1e1bb9ab9e6fa0ea233793aad529a5b3ca844a87654693b4058f4aeb97422d946bb62942255ab95c5d3187f7d8f441eb118669cbb2e416a8d11680a56623bf9fdb2297e016f78942697a35c1613478597f41d9076cf8a0034e7a4370700e7240cc", + "secret": "80ac8cff66b8584dfa5f472884f35d16614e632247b302e20403b6b8570d4245ea532360b055bee1efbfc82aa8ed31fc649a768b1d18a7321c942c77b1efd2b61bb9ab9e6fa0ea233793aad529a5b3ca844a87654693b4058f4aeb97422d946bc0a2d05f833d53c442e22ee91994f9cfdbfdab98ccd826f666c5721528bbc2e87b5ee5bd8a19c104ab205edf4b70c022ef47afcb7d894bfa0a1d473185b8e8c42297e016f78942697a35c1613478597f41d9076cf8a0034e7a4370700e7240cc" + }, + { + "public": "a12ecb4b2dc1a7e3a427a6dc54ff09f0d41152cb13c57b61ed2c039413a6e7c713b22f372ebf8e49c321c82ab3043af5400064dcdaed3f387440da5796a94b131020766ef528bbc4afa7489d24f1bf8efc67aed70a9644701efa4bce549e18863ce3ea501b16f30435a8ecee6940b75f42c8fa5cfcf26e5d6f75e9a1e5a5795e", + "secret": "331ef7211ba9c8e69e81b200c679eefe737cd087e18860d637b3c0369da4daf2a2a6355fa58fd94a6362f39944343ed81761ac79d7a373e72188aba9ac9928ac13b22f372ebf8e49c321c82ab3043af5400064dcdaed3f387440da5796a94b13a3471e18a580a1f86ee382c53b764a6789267cdac2ae7c098b34acc21f9dbe5fb403291c310c886579e136ff1c0f6aadb7ebe86f1772e017e0c2f0c9aaa5c1ae3ce3ea501b16f30435a8ecee6940b75f42c8fa5cfcf26e5d6f75e9a1e5a5795e" + }, + { + "public": "7c2a777f622652b845452b35cbb567e22e9f7ef583c31a85c29895b2b0eb0271b867c3151c8a2cda9b7d1907d2f5b98f4e3d7626d5ed6310c1ac9d28fafb7bf2a9b76facaf4dd2027f5f22d8f0de6a33630686b121b1afe1e21be0b7269e7d4b55cafaf0f4c57b62f3af04a665a0f7b93fc5b6d7e70698b5f4668b774098ff2b", + "secret": "c0a98bf4ebbc5eb79dd83f814ca016ccc66a613e8bbfe44b25922e771f611a19a39f61bdfcbaad784bd54058eeea02817164eab9a6564382150f5b85013fae21b867c3151c8a2cda9b7d1907d2f5b98f4e3d7626d5ed6310c1ac9d28fafb7bf269edf6635147fe49a74c5267e0f3d751758f4f9be9127abb667e3251dcf65074fcf9ab3ef6961e63d984cf6bb15853de07425ffcf2bdf7adee2d0af133d2cb5e55cafaf0f4c57b62f3af04a665a0f7b93fc5b6d7e70698b5f4668b774098ff2b" + }, + { + "public": "06a300d1cdc1ff65041f6711884bded571e6fa3947f62a8c04c1e5980ee776a432e5b51f9c7a4927f10dafbad204aa0833cf8286b052c3adc7873f15cca9758d69f3756e35c8a03865dab2b0447a75b5e743c87a95fd9ea9c40f9d82077f1bace8b2ce7bb586958e5379b9770b050ccfabf582b24e478ae5ad877fdf9c2b6334", + "secret": "97baef272e2d1c71bab9f05b1b567c5eab42462803b59f02672018bb71f494f51a8d21294e4ac2baece44cd60836cace98e1770629818cccd17b503f5f16554232e5b51f9c7a4927f10dafbad204aa0833cf8286b052c3adc7873f15cca9758dd359f40302387e554e425ef14a009df41c17dd5fbe4736d559a571ceabb85fef127819a21219117c135d43386010fac84051ebca29bbfb37c4184396b1803a4fe8b2ce7bb586958e5379b9770b050ccfabf582b24e478ae5ad877fdf9c2b6334" + }, + { + "public": "ea4061bff68e5564218f910da7bcf95a092eab543be53c8d07015163f7c8eea110fab30b24de1003a409056f1627f945b86599f4b6a5defd65753b714b24119df9f60870ca79141dbc2f133676cf8464f4032689462d338dbc863bf45bf31ac4ca069230f829cc880f4411445c6defa3ae087110954de129a1d3eaec872d8fa5", + "secret": "f496e399edaefae95b47e17bef722f2b99f2d77ad54617bad5e865670addeda4a305ba978dde2b1f1035c22db870194d18b5423467179a9abe8516a8e8aac9d110fab30b24de1003a409056f1627f945b86599f4b6a5defd65753b714b24119d90fbd436bc5287d02330b2e283671f1a15abd40cdbe99abf528c29a8587737d68ea3ffd3e116a9c30e46b698540b0ac5361a6465728c9f8cbded701c48cf139aca069230f829cc880f4411445c6defa3ae087110954de129a1d3eaec872d8fa5" + }, + { + "public": "0f6602041c4c829aec44c66b63718d113ae52bfb399372282591910bd30334de62dae694595848d528afda85b9a4461108d6ffbcee08f2d6e757d4a53d8e688f166dcf685c535bf1517cd88f18f718dfd7aa55facd325f97f22872457838221ecad6a040673afed50dccd9fd12eee18c3b1d9b1046dd41a1a026b0754329e61a", + "secret": "72e28cf583bcdfb885100497de0d83ddc094d1a8d899af90711b890fe906be20a3340d21356aee57f012886102de02dd8dcffbd3b535b4ca8cdd8ff4159cd2a662dae694595848d528afda85b9a4461108d6ffbcee08f2d6e757d4a53d8e688fb34d58bbd2e8b56da6d0a0166de3b3a575fc90501f55302540519893cea5fe2c70d6fbedf9ddf40eb4d468f533525f7573b6d74e4a59b642d06e2fc796080c26cad6a040673afed50dccd9fd12eee18c3b1d9b1046dd41a1a026b0754329e61a" + }, + { + "public": "c4b66819e68410b5d167414961660d30e8d9b913c7e69e710041ffae0dd8fae818ff8f7a44c24670b76f8ad34b311a2f1596e590282181ae23592d1b91ff16cb7cc89880e3091132589c020906f78d6f116f5277027ade716090fbe1330321116c83edeedb652ebb8e15b27162139718192f4963ec10fdd915dbcdf65f0248aa", + "secret": "83ac4dc9a632b79e480a454e7b2aecc004955919d390ad72c75c46d194a5a01563a693fd491feefb79c4e485063ce30df57ced90c1d63d5569b19fb9314e53f218ff8f7a44c24670b76f8ad34b311a2f1596e590282181ae23592d1b91ff16cb876e22fd5b0d86461b71242215dd69bea1636c587cce269351be996a84d7578942e34b3a5571a2d6de6802a6a3aaaf83358b27113a4931759baa002d1e9ed6066c83edeedb652ebb8e15b27162139718192f4963ec10fdd915dbcdf65f0248aa" + }, + { + "public": "66ad7b5af14c35c57b47aabf09b9de02f88ae8456947771ce65e1399d1777b6c8eeaeeccab567021ab61ee787baa12c0de9aaf094f92da0533ad2ac963d45b93456d75c8b0ac5f2605eb1bbdb936bc0b8f9ec71025b5f5843fcadbf3af97fd5dc7a279099c32ea88819a67b2f03ce99059333738ebc01432eceb3a604787bd37", + "secret": "b82ce3861a5f894568020da64ebc1748c09554d2ca72ec44cee63f4492adbb5b08602fd2bd292b58b80b6d14a7487fa098663246475fb952b5a77df836f8be7e8eeaeeccab567021ab61ee787baa12c0de9aaf094f92da0533ad2ac963d45b935fc66382ce91f52b14242b83bf4c9bee4210014376d358e29e6355f21f0f0b076671316ea87437f4794252966190d4edd419461d839f77e0d39f78ee7b5d7b5ec7a279099c32ea88819a67b2f03ce99059333738ebc01432eceb3a604787bd37" + }, + { + "public": "0afb5456bd9220f32b8332901f831bf22ecaa2161e656d0da543ee28b752a00fd95f7ed05e5cf8931cc0a370061f9d8232cf754bf50564a9392a6f14852acf2cdd22906acf8ea93fcbcb73774878246ec951e75450b6a43a0ef4e274efa2a72830d5242628ba80e849b1fc3666f4a5b1fcb7688596cac43d3afb00f6b9104710", + "secret": "4bc2ce0648310faad57b157fd85e17de05ef4e99d352e71c237940c8e9c49676bbe39316f3e890f284f44bcf2e5b56d80c0810b3339503c632a7f9b33a3c9eecd95f7ed05e5cf8931cc0a370061f9d8232cf754bf50564a9392a6f14852acf2cef34d722d2488c00f531c34dd5af4ad04316ade02604951c67e7eeb534bfe9f7314946b1cf2c0a295a1bc6ae0ebc1feb61a743ee3659fd73e3cae5a557d3cc5230d5242628ba80e849b1fc3666f4a5b1fcb7688596cac43d3afb00f6b9104710" + }, + { + "public": "96ffaea7739738f5f26b56d5988b1a83f0b963ca08890d13b2a09c288725734f55266bf7198415fccc54e0bc15ed5d33d9ea94bc0d44033902a73fb35f17d4e84a484e5be4f248a9f046ad5968715f251d4a2358430f8eb86217b214e66bab96f6fb38d7a3862ccf685ad2960d85f127938335ac9a4fbf8cffff6db336c85588", + "secret": "d20e64dcb78f602c2cfe66ba9bdb21da392114e7efdb195672a0c432648c096b4d8da732d454c4103b542180b86f28471021b04049637fa216e78c79acafca9d55266bf7198415fccc54e0bc15ed5d33d9ea94bc0d44033902a73fb35f17d4e881cb830de014948bb547c2054f89800db655c9d5c40e11e6808954daaa9f9a16be1863177dbc46095b7beb45f85b4c285131e19741dc4c663f0cdf0d704f968ef6fb38d7a3862ccf685ad2960d85f127938335ac9a4fbf8cffff6db336c85588" + }, + { + "public": "bef84c82b265acb6593315f4ae3530e88ebddaa2dd2c60cc4163c1c91fa6df1eccbfb6f65686506be9616d7372d5292187fe9ac4ef1bc4c06d9f595ef7eb9b8744bacfc9085a5df302190402d1dc2b66eb97ceffcad1ede85604d31f03b8c96342d5f56b27a0ac52979eb9622224d476b7bbe525a26dcffb82c66dd66dcf01aa", + "secret": "58669f3c24e9737f70fb11e68f6b3cbeb279f81993e95b06929900608dd00df200880566fe4891916f061c6e8b1f51c27361c05211a5df40504773b0176ae967ccbfb6f65686506be9616d7372d5292187fe9ac4ef1bc4c06d9f595ef7eb9b87987a09ae4265d2e0ccc88afe4fa702062140bd6305cd637b329abccee2abae92699b257415e5c48a293d66a346ea0771e0eaad0b658a508bb21086fab75a371042d5f56b27a0ac52979eb9622224d476b7bbe525a26dcffb82c66dd66dcf01aa" + }, + { + "public": "5adbb4498c1f86b69c29e01eb78a2e33c810b2f67af31bf766aa757a26acea2f640f18eb616aa7e7fa74b9aac43987c2379458d34750603f420f0ed95e890ebd5d6fc434352666b4f288edb6b7ef464bea4e638aa6c52e81f7e37af97c5c46d58c41cf3082275e5a3942085450549262ce99ca2b39a539ef2a82b779d7008720", + "secret": "ff913b4f2a002de1c411c1e205ef5f24ce9aeed88646f264d776a2f85f2d5f6e084d6a6d4b5a58909e41375a440f052b1884da8e68e098f429094a5faeb78ada640f18eb616aa7e7fa74b9aac43987c2379458d34750603f420f0ed95e890ebdb842a503736ccf5bdde9b433121807d1b92a6be77309bcf4399c51a50ed8be954546417434cff7468f9b6be5aabbacc7806a3f2c4f121e941b2e34db46b9277a8c41cf3082275e5a3942085450549262ce99ca2b39a539ef2a82b779d7008720" + }, + { + "public": "57cdea78f8f13271f6eccc7b4685f3b80f246ef53a40f389d0fadf72484a1ccd0fc3efa1845678c15c909eb6bfd2689954c178d09a67970f84ed68676a2e13c2a01daf8657d2e58fac407dbce7fb8be2ebf536902092b8b712b02b46bf3af93414bf2b05deeb89df56177e223db7318ad8799e029d9fcc2f473e7349dcef939c", + "secret": "41334f40652fcd6f242746acc853a325f1b754e0424b3669c6956cf6aa3723f5a43b393171927ab80267ed347ab3db069753f1553c7fd57af228fd8ccf44450e0fc3efa1845678c15c909eb6bfd2689954c178d09a67970f84ed68676a2e13c21511afa27889b82a941d289703023e08db6bbdc67d75207a1f49bfef42933022f8f96a29aee2543bcf0198102870fa8b1f6c72d59262099f1d751490d5a6e72d14bf2b05deeb89df56177e223db7318ad8799e029d9fcc2f473e7349dcef939c" + }, + { + "public": "dedcea4f037e09eb8a4f9a6821d52f3b400df5dfda86666aae45b59a7c9043be1b42d3b804059d5e189750a2632f9f1d76169fc9d22ef30c4d4a9d14ea79d3e4f9279b42259ae5da3f9e13d9c14daaec4592e617f09521ac7e07ab688b43bb7d9cf16315cc92378571151d922d33a831649e5ee44cc1acb76b3a8fd044d6de48", + "secret": "7aa2f7b9b59fa30ac688904da719c61ae071102854f0a3c97ba76f1018af14952d068907983219c45634ff8dcf90245c34633d64f6fc1e2ec8d8e5a66da1cfe91b42d3b804059d5e189750a2632f9f1d76169fc9d22ef30c4d4a9d14ea79d3e4640e1f331b3c5bb04e2aa6a632496ad94416b53713596630f9b1151bb190083bd7663feae44af632153deec4710a3c31b2542339240d5b7eb9f1a50df14d7e409cf16315cc92378571151d922d33a831649e5ee44cc1acb76b3a8fd044d6de48" + }, + { + "public": "b6e00962d5ba70386b6e4db7e3b04b21658e130c3de397fc76e68c253350f78698723bbd415a42f1781944e88a2b595c890c3d6e794d4be3204d3ef7fcb7aedba9b80966778c13dc9d5592bde3568e4fa255ce299782d84e868d55b6131b7d3078592e0c2b622891b6613ed42ab1e071a822bdfe05ed9e86558edeab6cb984f5", + "secret": "a1b3597224d08c097a41f765ab4e40719f07f0b88c70ba46032d807d6ab595699363c012e205a4457dba2f144ee209fb4ac8fc644a50b8de801b4f6c002d61b298723bbd415a42f1781944e88a2b595c890c3d6e794d4be3204d3ef7fcb7aedb8280110cdf20d1d585ec057d42449a3dc698b2509cfc962482605ba3cb5e29d7e9299d88a72616f147a2ce50e14b675ee669ce72ad674b62aeabaec8b0be625578592e0c2b622891b6613ed42ab1e071a822bdfe05ed9e86558edeab6cb984f5" + }, + { + "public": "b1af2c942fcf301f3293536b871ae8c9bed4929e4760af9075ab6d527ef01988c7c2bc92b7584b6409ff21d998f1fc965cf9a205acc65da1a8528e3fc7341988467a7e70d4116ff838ab42567fe4fa83441878ed2322a07e9843a3c041e91e6946eb9d2983d9635f51cc20f45edcd16c0a25b3f361502bf32c22a28b367368a8", + "secret": "254ae3c8d16b1e946c3ae445c8b04d382686c93faa59aac1b01ea6ec15c737ebc56b894a7a8ea46458979ba3e51f6e5cef5027469f865a7e4f62222dd0835cffc7c2bc92b7584b6409ff21d998f1fc965cf9a205acc65da1a8528e3fc734198855d1eeff2cbf4671bcf8c33e27ca9a986a5fe816422010e2b492293a96d250f9a4bf9474cd65e40f2129cf6d10dc27d5734da9feb6a9dfcf520bfc81b817003946eb9d2983d9635f51cc20f45edcd16c0a25b3f361502bf32c22a28b367368a8" + }, + { + "public": "c293b68ada2fc3bbe6c8a71509d16c0771b9c7d2fffd230f3e18557d54ae6b5c9f0301394a8bfd8519ae5ac6507b73f8f97511bf6d17bc202166ffb35e11363219c155eb80383b332b200e6cf062871fc52b7ffadd3ea383673fa491dfeceab7cf955eb2cc7aa87ac3ef79188b35a4c60e17b2fd70decd8e5b921205d4f330b1", + "secret": "412f414350e558e5318e9ad7e1815e4c9346e0b091a9b09fca02af4e7479af37448fb0ede6f2ab53bbcfd0de19873a595cbc1a71ee8da62c538a94ce469f7f389f0301394a8bfd8519ae5ac6507b73f8f97511bf6d17bc202166ffb35e1136328bd5371863eda121164ec72fd1bb05be2e087b99f3150d61020d18fda338d76dceb7cd7ee5d16605408e29f146b2f20fef4994f16c27b07491501ace8a590eadcf955eb2cc7aa87ac3ef79188b35a4c60e17b2fd70decd8e5b921205d4f330b1" + }, + { + "public": "158d86b020175c4d18ea446b0d3ed7b7cc9f2cc52ae42617ddbde3396a46590094b4c016c04575ce79cd95957a1b0dcb46621c979d28a1f72cbd2f92d2b750002649dd3894976b3a9ac427a840e1b4a897ef7a0700d9794824642fa0977aebfc3b0c6642cf588710892a0fdea10d06c40674fd046582337fee0834f3f1a80803", + "secret": "55538897e04787f89aa1caa6141377a0eb426b3c02e768ee6b7169ab5aed9b02f2a9041accf9394bf04a3993abd43295e67083e6a00320348d9ee11af837c2df94b4c016c04575ce79cd95957a1b0dcb46621c979d28a1f72cbd2f92d2b750007a246aa20e7454252e4b8dfb24c1b055c7e002dc233e9be09d0c55d5a9843153afe76a00f2c490e707fb861dfb173f23ba263992fdfce2540624e44553e1e1663b0c6642cf588710892a0fdea10d06c40674fd046582337fee0834f3f1a80803" + }, + { + "public": "24de95ac44d2a34cafb13c6ef9d0e33fe11116fe3466d913de9eebb03fd43ecf3a7c6d0f2dee09ee24fbb159a9f217a956a91a28e79e6f2e8004035567b5c124ffe2fa8a9eecff9e5049ab925e4afe093c336bcc8f0e177c3c4e3d57afdc3f7c793eea8fb123c8b5781bb518256a1f578a011efbb8ddebd1c6347de987d841c6", + "secret": "3e77fb389047eca793d0a6d880336f2a55a8f8a71fbe8b6c29201ab6df1fdb5766b5dd08ccd57970a72642d9ebd879b8dd43d0ce7bfeefe4a43415ccb31765853a7c6d0f2dee09ee24fbb159a9f217a956a91a28e79e6f2e8004035567b5c124eea910b4a55afac1efe3e73e9bbf77dc8fff9373808943048ca5c88edfd6bd3d33ed102f5e21a108ea9dd8732dde442fa5957b1ffd48e2603a2ce18f423d0eea793eea8fb123c8b5781bb518256a1f578a011efbb8ddebd1c6347de987d841c6" + }, + { + "public": "0666d281f12c1a4afb7da3ee522a5e49f2e6eb1ed55f9e9a61187312bfecdc110f1c4e9df274c6030c9af4abda14c1d36eb14294b0226778b9f3aa4ffcc831b63932b05727464c4fe45267e0e78d895016096f0b9dd1280734b9e455e5c3b2be57c67c1af4c860b8b77cedc8db6a6039f04f18903f7adf973c0b8e307e856913", + "secret": "bd7b9d7d5845b3901c3b4c9d57942b90b4f905ad4ed3f54a4dc7b8dec2b4fdf77320a1f559bf1848825cde5e164132711fd5b125ffbd6e5dda91687069f3cc170f1c4e9df274c6030c9af4abda14c1d36eb14294b0226778b9f3aa4ffcc831b699b58fe5f70bddc802c6f44d726fd54b2553077db66def6be960986b5a0cff666989f56cfa6a6d63042bd6e0531d935197f27e404b5570da6c490e8229974c1f57c67c1af4c860b8b77cedc8db6a6039f04f18903f7adf973c0b8e307e856913" + }, + { + "public": "d86e65ccc986c0f475c922754db46fc7ec7b58760823f4e63c22cfbaf9002d2cfd4ef60db732e67ff99b188a6ca2b5220811d0c23d35c115fd045c1e214a8af58d5d61176273f16ceba5cdf3764ca4d7330471c567de39974711a3476f3330fcdf7a660e0b67432a73ffb4ea78f9317b2e77856beb2e056cbfb980cd697dabbe", + "secret": "79d2f43fd620ecad06d932d8118066858db84849d9e1faea1c65d947a9b034e41d3a598013dd9657d25c767367ea344833d70bb2b580b11092427f7cd645ad30fd4ef60db732e67ff99b188a6ca2b5220811d0c23d35c115fd045c1e214a8af5a7191b4a061e82b407f76b266da6b31ee1b9a642f18301961f66182060b6585f1226e4c7b7773fad495a1af9fc6874df90dac3b4e354077f9205d3bb9af1fd95df7a660e0b67432a73ffb4ea78f9317b2e77856beb2e056cbfb980cd697dabbe" + }, + { + "public": "ba7277f35a4c7409e7461787d62f883b03740630d80a79713664302cb552cb792494f6647236dc2deeb0083842f5d28b22b01a7098dbc19994c44fe357f35281183cb735c5c6d12f97a9b3b22c8561489e01ec9b771c69724242ad9c92dd0e24cadcca046d29ef02a4d631b6b28222b257bd66e9e359f48ac7020fb97b4bd54d", + "secret": "fa8d5eb550b98b3f65ec0986461d940b47f2fa754b3c2c011634f296f41e5b5c9fb059fb5f1e7ba4e6736370664e1ceff7d255578a74b56062a63ca4cb5abadb2494f6647236dc2deeb0083842f5d28b22b01a7098dbc19994c44fe357f352811014286fa6902b54ec0f2bfe55baf693b748bfb5b02af94c2b74714e06b7be821a71717bff87a22c06309a37128e90900dabe163a60a3dcdf6862fb4dfc60fd6cadcca046d29ef02a4d631b6b28222b257bd66e9e359f48ac7020fb97b4bd54d" + }, + { + "public": "866d071ec0acbe310c4c1fd59c31b9c44f69eb560fd767a255db9473fcb957be97242d5afe8d28d96417c1dc8a3d5d5e758885898ad1da2f75e560f76faeda939b2a4f00cd3297aab220c698d69229f204a518d980dc735dc51c6fe77f1c34cc9b1fdb49b2efbd765abc4fbcca58084c7befb246f8a1d68f1459b5ad2a1c72e0", + "secret": "aa9c61e2fcfc940cea6cd0a023ac85909819b470aacd470933ca62b5ff9d755911d810e4b552d0a67e46b77f494730de10a603bb828e7940563c71e71bb516af97242d5afe8d28d96417c1dc8a3d5d5e758885898ad1da2f75e560f76faeda93b3438687bf005c897245d3775765d42859097ad2d0fbd2edc3bf0ec0213179737a8f111d6916fba461afe0737b9393e7f5dbb0d00fb095cdfa1ad2005e8afe899b1fdb49b2efbd765abc4fbcca58084c7befb246f8a1d68f1459b5ad2a1c72e0" + }, + { + "public": "5218160989aa2aed99d8700deaff2b339bd686b85d37724690cfcc5a7599ca56b207b5b78d9183b58d90a7c437ae192c4f7bcaded7e659c59e7ec5eca6318ef9db5a222b8f9f18d4088f16939d075f15753c7ba535a5c6a6e8cdc2ba8a5d0086c4e592f4604ddf99671847d8a142d0da2bedddea4ecd1efc8748b8fbd5498eb8", + "secret": "ede46da46d926323e71afe621f364dc3de3c4474a6d37a3c1b2609598d1d2b375222b44ab1892b58ffbac56b87dda25009db8003a5b83b0fd2575181958539cdb207b5b78d9183b58d90a7c437ae192c4f7bcaded7e659c59e7ec5eca6318ef94ec934e4860386f4de0aafe90eb1d8eff866fc496160c9da04e1a4fdceeaad3e0a414534681bb54885bee88b0b5b123cfce7132a2b3836e2da1d889b923e2e08c4e592f4604ddf99671847d8a142d0da2bedddea4ecd1efc8748b8fbd5498eb8" + }, + { + "public": "a1d92d15d27b48ca61cf4a46c9e29da8a4fc3e407cd2b29dcb7fc3b6579a27b0b90140a7ae9752da37e370ca5e74a1e1313bc02e88381d8d25cfdd6200348aa6202774c0ad9d6ccc24d837b860403a095760af35793776d3e13cf83a840107fcc52fe2965f1023a837dd11b5039fc79077206ce9b45f6b1f06077b9cc0626b4b", + "secret": "ddea85074163db2f261670b9cf2f4cc065b9c8b1938972cf4594cc90c404c483a855265e7734ad2c9c0bec30b14694a809dbf7a6a0eb6889b67cf6fbc6892bedb90140a7ae9752da37e370ca5e74a1e1313bc02e88381d8d25cfdd6200348aa6fe9e636776c8bcb0adfa9b32a26178c0f2ceda9dc0d545f6ad11a9b4644106deb79366609fb3aeaaf7cd7925f3b89826531871d2913c4c8ee7df5550b13c8426c52fe2965f1023a837dd11b5039fc79077206ce9b45f6b1f06077b9cc0626b4b" + }, + { + "public": "d90e1350345b0a2f06b48dc3ad39251739fec6c07b586e2579521d05813e598337579b92246450e34a761ebe1f7430834da1cf08e07f1e5fae3119c9419d279d66e7f7679b143c32d56eb8a84c5b13f9dd7d3fd774de828d96529d4a5dd9b1ef1b906937f8e1b270fa6bcdd040eb50bcdaacb7563c39b9f2ec4305c1478548f0", + "secret": "46975cf424f3d8f5272106e93e0661f748e6f897ae9b0dafe8858c3bdb54f7a1a6d399591c383e2f653f0f9d464ae468acb7b2997b5a96f1575ddff6793cb3ad37579b92246450e34a761ebe1f7430834da1cf08e07f1e5fae3119c9419d279d8c6ecfaf70f622ba114171940e843cb15d1c0bbf7c08c1f430bb4e9390f6f97a64536245eb823628bce83b5f3e0a190fd79597b63a7dff73563f399d9e0fc7141b906937f8e1b270fa6bcdd040eb50bcdaacb7563c39b9f2ec4305c1478548f0" + }, + { + "public": "4eb4253b8d8c97aea13bc12997859dd493dbdfb4894e9c3157f3aab93db18dd8c4990b43b9cf9e1334c65583fd870c6d15fa438f13dad998e7afa24fb47db4b24c24146bb074bdf854b48d2b55c4327a084106e5c87725015b0e3e1a6f0a33e65641da2b3f0c099900c996393a595e3ecc40dab88cc19c5f3bd6e6742e2da8f1", + "secret": "c2dc49c6180d9d395a6e86271a4611b128437ad4a75784bb3ce0eae34565082964a96588294700e54be69bb5bcc145a6472a1be0b5e1bf1979026d1b5d7997aec4990b43b9cf9e1334c65583fd870c6d15fa438f13dad998e7afa24fb47db4b2ec8231845cac71a31e1a3837b4fd157e5af40089f4f95e8e86ca214a0987ca833cf6293662fd6142f74e0d92e9509bfaa3c6c2b432b0bf0c8ef850e5f95476225641da2b3f0c099900c996393a595e3ecc40dab88cc19c5f3bd6e6742e2da8f1" + }, + { + "public": "3b00a2ded480b70ee238665f392c92ee1daafd2ac1633ff025097f08a73ccb8491b9fb324b0f406a45cb126116787477699cd8a0e078a782659210b861cf7f0f94e363e417e1b9c1a82d318f98094558baa1fc06d71a39ae97ce0d5929a6c4a7040f982ff2858c1a2c223072869d0265b344a269ea2de266190ed70f6ef8ad34", + "secret": "29f11e611a1eb255f312b44b31b9120df917ff6c812588c9a1086e5605787427e73d3f3741f416fb7eeff5d587240e026b782793b7053ca3285665c75b3231b391b9fb324b0f406a45cb126116787477699cd8a0e078a782659210b861cf7f0ff6595f7cbed2c5cacbc0b5e1aab6c018accb8ec3eb0639b350cb5e9aeee2f31abe465815dfec8e19e5d1e9ea17cfa7a8f16073cbeccd6059b655113073c69f97040f982ff2858c1a2c223072869d0265b344a269ea2de266190ed70f6ef8ad34" + }, + { + "public": "eedfb935c57febdd02cd14a8f4f7a0681e9cd6c3b6150e6ae3a3527caceb44537e6d390280e7ce8ae7509a40d2a630582264a57334b4a3ba0616fe89bf6aadf12b5a550315cb593f7e61536dbdca108a1147a32bb33d135b6f0331209793e1cec15d09dc363eb96cc3bd2bb602c3986edae8c3191ff16c894951b3a057531b04", + "secret": "50f8dfd1bede3dc5ec99a1ff0b5301884a95b94aeccd1f44e70aaa7d31a7afded6095c4de4450dd495c1584f092266dce6c4406b3054f66630ea256567ec5cef7e6d390280e7ce8ae7509a40d2a630582264a57334b4a3ba0616fe89bf6aadf1dc646922b472cb135e186365134bb308e537e5872617d4391c005e3c2dc3a704b2d6212eb203126c17c979dd90bdb607f1e4993430474a7c7463e39db43e9be5c15d09dc363eb96cc3bd2bb602c3986edae8c3191ff16c894951b3a057531b04" + }, + { + "public": "5c7d602eaf67e4e4cfbd0eb897b55839d2461d9ddd9189c8e005f7a230ef785b593328c94c7b65337d5cd1938d3f634efa9338ac65c1840ed9f720891d99dacc1be8ddf8791ec06ed8c6fd575c987aab9a2cc1947abd3b9a9ded9f26aa429c4a77796ab122ac3e423aa052b9995ee87372745a2817430441e2bc928b9676d83a", + "secret": "0dab8436ea26e79985cd1ce56bdb049ec9c7f350786660f6ff60cd83d7ccfc1d4984d79bb5a39f401c5f1ccd5b0ce47af7545703d8914187ee134f90a52e7530593328c94c7b65337d5cd1938d3f634efa9338ac65c1840ed9f720891d99daccc84ef4b88725a5f5566910711674396773b441c6248bf9e0691003c15edb2c5c1782f8f05eef54c5f5e13bbbc901a6301211a580ffd87f87dfa51c2b3cab329277796ab122ac3e423aa052b9995ee87372745a2817430441e2bc928b9676d83a" + }, + { + "public": "ac41bae4c375e8327fcdda1d809a5c073da455e45864490f1a103c8ce23abb73f2786e5b8ffcb789b88507b498ec6162f1620a7d73604b6ce721f50d1d2f28d489c7632f6f2a9f0563dc2a7a67355bce5916d7f537cbe323ed0b9938b312910a575989c7b3f7a23f68bb66cc40e256706ecc1ed70bb03dbb8b44ef76e4cb0888", + "secret": "03980ba58b11d0488b0cb9b533655ce986d0fa590cd52e05f3979ef1c0472a44c175907b44ef0ef3ad89ced4b8ab58f730ee8aff7aba89e63b7b3c3ec49c3803f2786e5b8ffcb789b88507b498ec6162f1620a7d73604b6ce721f50d1d2f28d4a1c0f0e405b915ad9cfb0d03f4185bf27058a014e2892c6a96d6ab36374757eddd0be8be48eed7e3719c19c4f41e38f84500a8d01fc805f2cc839758dee6e390575989c7b3f7a23f68bb66cc40e256706ecc1ed70bb03dbb8b44ef76e4cb0888" + }, + { + "public": "ac021e54392992ea4e56f4d20829cb4664a64949423cbab20380bf5bc608f4f3cb26c8e9862d0bdab93d8d7e6af688de7fcc0ede486b15ebd35b3528d1959ca639e9c3a01a71c97e231fe141e39f705535c4de0d3a2a0970d878d5a75af8070c39a251a1abfb2a705e2d1b1baaebb83c0c852b4f58d3112d62d75ac043feddb0", + "secret": "afc84bf19cb727e52455ab6c0ef105879a804a0d54af2d7feb21bd9be64dc9c6d65665d9d50ac74b586cb61c0c0bc9ee3b1edb9195735cbc003aa61a6a2076d9cb26c8e9862d0bdab93d8d7e6af688de7fcc0ede486b15ebd35b3528d1959ca65e131375efb950852ff244f8965560fbc791604d4bff67ec975aeacce88a7cfaa76e00eab034870e305b2b374884fc21465f35bc45165a373d2a690009e340bb39a251a1abfb2a705e2d1b1baaebb83c0c852b4f58d3112d62d75ac043feddb0" + }, + { + "public": "689fe1faa248603cf6efbd77dc402568a693854ed0c238c6fcb80d36a7d95926cd46a2bfb66b797d54e9107dd4f6b98e62bd9344131432cfacc0dc09b12a323f40a9e58a5f97ce9798d33c7d4656feb3e4f03eee408d5a2f6a9ae8d61e06c55c6ff339a40b5ae71b62ab67592a9235084ea9da49d2c539bd88974ff75087cb1d", + "secret": "1dd0c6e1a02e1b97adae859be6db8d8e71a593b312898c9aa8cc544be3d77ed33d141566b7866a45a8cc466212cbed2854cf63fc2cc27e0ea6eb5c2438e5d1f5cd46a2bfb66b797d54e9107dd4f6b98e62bd9344131432cfacc0dc09b12a323feff4b0f60ef6f094e8ffe297fe02444126f5dda097c3b9ea46d22959573b6e8bbcdc52793c1389b4d583ef3af99b3de8c58339d3cf1905a9dd425f0d84da4b256ff339a40b5ae71b62ab67592a9235084ea9da49d2c539bd88974ff75087cb1d" + }, + { + "public": "c390d0e8448f9711bb4e46e56fe79d6a087ee99c48c8027587db24cc687b7672403eeb1df484690844ed416073365d7e8221816240389114f6dc2d96f952b680b642ec4ce39fd1ae66cfebb29f252b108ae2c2a5229ba17de510e1fffe518fcae9a74e6743e682d1d4493f320d3762819a8e05b5b735b5480f7493d3a2295c2b", + "secret": "b854031bff22fdd88d41bf71f96f13a6ba8bd1a2fcdf7d1691f93fe9798625a40fbb3cef3469a3faaf6ba663a5c46ad50071457275b15ad70c221c35ada7ee3f403eeb1df484690844ed416073365d7e8221816240389114f6dc2d96f952b680244d94cdd783892b797df11cadf046fdcedc9176b5bfcba452313c3de6574f43a90d10f570adc637436c19a002defa8f93e58ef85581d87e9bf39edfa3812998e9a74e6743e682d1d4493f320d3762819a8e05b5b735b5480f7493d3a2295c2b" + }, + { + "public": "a8c02aae86c64437adc26f10924429b199766dacb31d5fe638a1214b869091fc2eba0702fc6b3a363683d8f1e381ef2ac90a98a2f77beb6a30e36298a0817aa5862740b7504804b10c4000a212060e31cce1e4ef9de0675237a1095fad07caa6ee95bd2eb746b08950bdbfdab4f9dd0d195a9298b27eab6352138a3be4d341a9", + "secret": "295b611c5da64d8699a5e25e7e6cd7342ff52dfd8e3ff1a4980991ff3cd8366b03f34ac6596fc7c798ff304ed0a35e16346671e639c4aa5e42a3faf7fc35d3352eba0702fc6b3a363683d8f1e381ef2ac90a98a2f77beb6a30e36298a0817aa542d90d8d55f916d136dfa16f0acb1a339b8027db4fcb2c37713a3eb01d7c0038791b9d7d1f74f4bf967a4963f88821b3c3b17744c85fa3f67964bd55a3fc79c2ee95bd2eb746b08950bdbfdab4f9dd0d195a9298b27eab6352138a3be4d341a9" + }, + { + "public": "5632bc745828f215c3af2be27b6940dd29725ccf19a9c6cd493ba297d40a5d871369ae61be49afcc6f6accdf1eed7a04334035e60b92f1587f7dc7480750c628d2e180eb783344fb18a3f51d59f61e3e4ee51841ac8a61237366623df8eccb5148d65e4a8bf00e2eded2c3e77676f4226eddc318997b70699bcb429f63fd1a5a", + "secret": "4b85160e4b0620a208cad14f038e66484c9a7e9ec41c778322b10a03105b7b643c991388f4ba5025d6786e22bff0f38d1e54d5db8fd09cdb4e4dd63c540995d91369ae61be49afcc6f6accdf1eed7a04334035e60b92f1587f7dc7480750c628ee2c0fc1c8405f6b5873451e6402562e920627c2775352c17d64c3354dfb64a618e98119ba88b8355126644a3930e182da2b61b56030f031814f7721a560c69b48d65e4a8bf00e2eded2c3e77676f4226eddc318997b70699bcb429f63fd1a5a" + }, + { + "public": "7af99b9ff8204b60da9f1dca49bdf0abf8fbebc8be5d45201caed89cb7e7b76a8bedd2d5c57c4c741af474ad08f6be3fb4edaa21ca83c6ffd32d155a7fa2d51be91334c41ede61e05adb35417dc1cb99e67a2e2e26cbff9bd253937ab85acb45791c5976320b8eb56f16e320f05983cb53f4786d67752688618686ae24d44ab3", + "secret": "95535e504470a9ccd4c9d89365b3a0a44a18443469f866471cce4a33120b8cab29c9185235f9a83eb27500c14976f49562d60db83144717506caec841435dc618bedd2d5c57c4c741af474ad08f6be3fb4edaa21ca83c6ffd32d155a7fa2d51bbc6270e93a415b5643a82cfb83e4d6f38d8fa6c1c679f9fe4365695b81972f86defa70142f42cd8510212d5b76f1b5feb78e60a3efd9cf5128401165ff5cca4e791c5976320b8eb56f16e320f05983cb53f4786d67752688618686ae24d44ab3" + }, + { + "public": "03663448f00733caf973457e7b7ed2c0cc9fa7f4f9215b6ac2349c2b04d96a8f1645d1357a0457464a2e9bab1b039621f19e0d61162f5a37311ce62336c138b5f863e37a9a4a637d3017654fd7f38cdf08a6081efb2f9a37b47ed358c1fb3dbf86ab5e0ad10a3cdbda1c451505eb1aae6377667d9672c1c48f100500ef062aaa", + "secret": "ea8ef02294164fdd63b1cf00a6f4dd59600e37eb82d3c957c4b0548c9b8ae8416c5c690057287a6d736e3d5c970fded509f4c38f7e59ab8f56000b20fef703331645d1357a0457464a2e9bab1b039621f19e0d61162f5a37311ce62336c138b5cf0b8a6a6d2ad6037a577be9c9d18051603b790884b4284aff273b0a3168ef54065c36d435d8c81e8f081811031f40612f50156fbbfe335d1574ff34372e070b86ab5e0ad10a3cdbda1c451505eb1aae6377667d9672c1c48f100500ef062aaa" + }, + { + "public": "3dca205f50d70342d4978b5e9bf59077d7892a321f014a158bd373eaa7a58a5174379d5dfaa87d08cdd86e5cf4d89654c661221282f5af8503943acb91e260adb91d018e4448f215570cf4c6b009e3ee3c2889a9fee6f3a978b01dd223814b0c5109aa2d7983a812dfab98882528ff5b3abc73007e4105646d8caf32bd95a0ce", + "secret": "3fa930511acb5583dc3fe3378493d8ca9b75d72f37be5f46a5db623dce77deb3075503318c6a2dfaf188e15afb42ccf7508d5d06ea6c81730b9014987ac3808a74379d5dfaa87d08cdd86e5cf4d89654c661221282f5af8503943acb91e260ad354bd9aa1fdbd179b27f6a8fd3284f75ff031a9aaecbfccc87ca8a2813a2ae2c401807f66a8d89e0854672d7f199a6600d2003bfa6a1bb76e4d481821a02cdfc5109aa2d7983a812dfab98882528ff5b3abc73007e4105646d8caf32bd95a0ce" + }, + { + "public": "d8d9740016aa2c6878a2146ee564f8c0adb36b26e2d4244f485adb6cf33c48720d4c65889e9917e5698a0467378ee758ab48deed04592d7235fd720576fe0c96c02f905f295d74e453fc4d209a0ffea22c107016fafbea75d47e6830905bce20057a413d0076fee417c63cc1f03aa0ef19aededdf4107891f1825909d876b774", + "secret": "18c065f7383af9d4c306d431ac2551fef88fc6532cefc222c7039d6d0bc84c3d42898772aac2178eb9ade8a463be8923ffcd0647ff9365bf97588c8712c4f8c50d4c65889e9917e5698a0467378ee758ab48deed04592d7235fd720576fe0c964a1038027f279683e1f7a0b2545e3ef52a824358a358ccb88b4566c1d313594e82449e9b97f984f773425ad7761fc7f64586215ed8529f988bf2d34c0e68b9f5057a413d0076fee417c63cc1f03aa0ef19aededdf4107891f1825909d876b774" + }, + { + "public": "772e564ba34feeecbd83ff64d57bd260e796bdad11329a8e9dd9810cd1338b3d0690b5a2f221a1b16198789dd320c17d30b6267662ae4fdfd7f21b7d1432137ec1dc33a624dbd86246289ebf0106d84b7302bec3483c70743288a633e34eba82d6b2ba85f6fc69246471f2dbc8b82b369d2c2bc69d02a25107fa4392b8f28b5e", + "secret": "6ee406166ca1935f818ac4ea674f02b17d0d8ca3e3537e4d9034a82395877aee1a9b574a98fd36318b05c700bcc21e9103733361e6a435c536a1221f3e141dbc0690b5a2f221a1b16198789dd320c17d30b6267662ae4fdfd7f21b7d1432137e720cae15ebb67d5afd6c84dbab3e6f4f7b59b8d05b058cab5ec98c29e917ed9104234a0394fde4928ffec1821c4153bd4b0b0ccf694d7602501c79c81d11f51bd6b2ba85f6fc69246471f2dbc8b82b369d2c2bc69d02a25107fa4392b8f28b5e" + }, + { + "public": "81deb232ce18936dabf437ff70eb24210c28350d2fa33f76ddeb9ce3ac5713e4c3472083e03443226589c52e08c306e799d778c30afc1d89257885a7d1fbd14f7cb259d8fdde08de5ecc646e38ee112e338e6ecf44770acaa64c98f983dbf0315bb940d58862de6a3b123ca15f75e29849084bbdd7bd93eb5b7fcdb331320b22", + "secret": "e411e96755452322bec09af7e018349571110cabdb1e57e6671a54afecec029e46482fa99b846057650eb351a38f2a70f1de77bae5733f9a385d2d9d88ed9470c3472083e03443226589c52e08c306e799d778c30afc1d89257885a7d1fbd14f6ccc8cd5274afec58707dc4915ef7a8598561228a7f442ce93d82b503c12c6fb65163f854b737f3d9a78d5f37a772e0489aa2b71c2e8573dc67cf7f05eb6bf605bb940d58862de6a3b123ca15f75e29849084bbdd7bd93eb5b7fcdb331320b22" + }, + { + "public": "3100ee6e3f8d1c90b17a8f1c4a191c715b1c01482db07ab7e333b3e3133232b91bda3e0b52f20c4ea1e1365da491477621b828edaeff6faec2d36214811f60b89e0eb4800448bbcc0fd893c24467306b0c118505f0c575dded9e83e2ab767490f922a6b41463eee4c47c954522a91e596eea9796034ee7ea415ddb7ac69d4101", + "secret": "37d921bafcdef6bde76b9502edeb73d41bb3fd2f769d01e2f795e612d39992f4bd6ba6dc8c460745cc6d63c8a68aef25b884924b65192408324a12dc7042a89b1bda3e0b52f20c4ea1e1365da491477621b828edaeff6faec2d36214811f60b854d91952e2d5454f300043e342cec867ac3f174189f54dc6d09d6176abb0bd629823afa9269a01f14251d57f3f1b435ec7109cfc06cf4561f0f9415e7c83516ef922a6b41463eee4c47c954522a91e596eea9796034ee7ea415ddb7ac69d4101" + }, + { + "public": "2e74d33290f2a41794440acb2fdec99efb979d2bc5450a0b5aef5c3c420eae1ebd2a0ed46e8ded86dc6a6147d0e5772f3c75f5d51bc8eaabf018016b3111708d613eec9acdb0019713fd42c4dbcb306c51fa5649666c24175032d97a7b2a79e92b7e62f6caddb030a879ee4a2f6acf2c918281e6115a316955a17b714c41f9d1", + "secret": "9a5a09f211e0717b16d526a9b582fa3012c8a8a497fe2fb400730260cbfc2facfca84f74f6948bc0f1bebb15bcc393b48bff3440dbf05a318d8e83e8e2feeb1fbd2a0ed46e8ded86dc6a6147d0e5772f3c75f5d51bc8eaabf018016b3111708dbe82830bd9195e916d44c53caae4b902d742bb7d4bf3e36710fc0d2fe7718a284738a7ee814da206a8f921915740405af3c9b078c5660b69e66493a9c9c1796a2b7e62f6caddb030a879ee4a2f6acf2c918281e6115a316955a17b714c41f9d1" + }, + { + "public": "c8bcdc238e55c3493e74226f30c1043c6deadaf3365f021d95a815bcfd61a0ea5e18b64c0d4cede341a5d7b30262c12af5eb7cfdb77209f69f7fc21e2ea7dc1ae4e093be606a797c38703d38567f48c799227acb4cb6a83acf4e654260c476beb5529bf1dba4d28bf5b950e87312ad3196a0f337f46b0d13623bb9dae9f0cd51", + "secret": "70f4e8ef06a208a6fae5b4db55e7d9b68077fda51547a287012a7bd5f0ba3c64e26c0441186445f25367e936f5ab244ff0aa8c5c67eb21031778d22ceb9e4ecb5e18b64c0d4cede341a5d7b30262c12af5eb7cfdb77209f69f7fc21e2ea7dc1a1a36607b5e1e852085e5e8d0b3cc081b9f02312f2bef123e168d64edbc74bf49ef1835d619d41d9a77f36d90443bf56864caadd822e5f31ecc0b797681512a96b5529bf1dba4d28bf5b950e87312ad3196a0f337f46b0d13623bb9dae9f0cd51" + }, + { + "public": "db2871ea7cda1013a17c8177c34153b8cac258eea8d9fcd10c32b899d59c3a151c5719ed8ad8f4bbbbafe59c56e2ba1451ff411f9587f3895f787a0c4138b76c4bd62bfdfbea930bad00d08f31941e1b3371e7116f68c5f106ede4d733b017ae2e8dbdf316ef10f5db1217c0a6b9c1eccab988993eaa09792e0639450f25d6d2", + "secret": "eb2af3b702172eb1bbd36b04c7088893a2d6a4160fc8bb566176651085b70e9cf2a1b79c305855b456be9d349a154c09d77696fadc2fc67d3b3cd0de37ae703a1c5719ed8ad8f4bbbbafe59c56e2ba1451ff411f9587f3895f787a0c4138b76cf83f028d14edc8f5132f1ef7b66086f5a4011a51e3aa5e17cf0c1adc732ed84b4bf498e9a0e421695942a57186f72688ce75d052bb3d463f85f565eecc8788f62e8dbdf316ef10f5db1217c0a6b9c1eccab988993eaa09792e0639450f25d6d2" + }, + { + "public": "530749156685451eb96fec2d0c5ba4f09a81b850c29478b2e6b32ab4cf88033954d015040c0fbdc497cf3cd735cf2591cb6d4dd246db27038fd4e584d0e0bd2bdcad8055067cf4b1512df0013a8eb05f5251cbacaa42779d84c3eb5e64834ea726a56e30dc6a1bc83009c1567ef284a23e712944180a4ebac601715d8c660007", + "secret": "ed50a17b0cc991c15adbb1a431bbee539cbcfc9cc3e22339c49040abf6e0c0107c8d9e9a3795c3554a93bfe5a6a94b54673c6a90780a9e8159bf49dddc2092d554d015040c0fbdc497cf3cd735cf2591cb6d4dd246db27038fd4e584d0e0bd2be4f39b5a95a3509c48ea98595c1d26f34fda97f7db321352fafdfeb6923d041d8508c4274083b052ba474b04f32aa2f1dd9522137bee56a4fe13dc844c692fee26a56e30dc6a1bc83009c1567ef284a23e712944180a4ebac601715d8c660007" + }, + { + "public": "733a7c1586e4adc62bf61d1ea5de89953d7c4cc0213436650f8249f1a83abc293b25fc947622c9660779ea7963be30b74fb83927b906c5a6c7122cc32337f3ba625e6457f45dbf801f54c7a9cd75c5717570cb9684c2c50f30be1003e161ea02428b8c42949774647bca891c0fa21e0e87b3c20e6b7a46879163898007c3fa98", + "secret": "25da02923c0cc89e52af956be7b4d5a0a26d366faa1e1e5d5e9556c3f0ff9da3fc073b3132a6b8fd118848855e0ebbdb57f9d4ee397bafddbe195c6bf3e640953b25fc947622c9660779ea7963be30b74fb83927b906c5a6c7122cc32337f3ba4f5a789cb0908811f2a84b38fe84165fa7b1bff81d9b3ddabd74cc56fc75f0c426c8824aca24742706f2341407aa6430eac6563b23de7dcfe512fef9a467f086428b8c42949774647bca891c0fa21e0e87b3c20e6b7a46879163898007c3fa98" + }, + { + "public": "8d93ec3077e59d8d43ab30a1fa78aba5881208c1e587a9b4ecb85b7981f2b2bc0d949642160a3de3f895f54f0862321cbcf43ef9cbea43d2325544fe88610de0aae261113ac66dda9c25a2e03f300d61d5d8ea342bf2ebc1f3fc1183f6bcdd3edd01da03a6b15fe84c0fe561e0605ead872537586788065432bfa571de6cdc08", + "secret": "326f135802b25ccddf7a2f2c93010a17125416d871fa7883f342f4e0ee2616bfec0fc2d2e3f0772b0be07aee7748b8700b41ec5382d9859122ffa6ad662f30e10d949642160a3de3f895f54f0862321cbcf43ef9cbea43d2325544fe88610de085402078d727f2a15af5a586e3db106e6e2ad68b23cc11f7abfbac26e8c6fcbd218601ec0e6246fd7658832d229fe5a47961e5eabd0df1614616c1d26c913eefdd01da03a6b15fe84c0fe561e0605ead872537586788065432bfa571de6cdc08" + }, + { + "public": "ee4df3787b320a9dda7dc8ae2146b0034efee34b13f3a8700702cba003dca5ff614bb1dc778f74fbca93ae9b0157f9ee46047060cdd8a06cdc4281c9850e183b8a37754bcd266ea93d536f1b28170dd069f34ab6bdad751ed37c44d06df2b3bae7213d56c58a46c0c7f51afa0819e0e73d22e579ebda25063aeb07e31cb7e3de", + "secret": "2c3e1d8917ad96c2c4b6e06ad86465fbc48dc4fb593ea54cf1d1db1098d97f71f708c4a071dc3a17f8734eb6fc3f481900742999b7815472ae52170d101daf37614bb1dc778f74fbca93ae9b0157f9ee46047060cdd8a06cdc4281c9850e183b1d92360873514ec19af0864beeed00d89902283b91262cf4eba4c3e69c3c9549dd4537f40c4997d2df1eb102b29682f641d59951ee6d04676fb619c099f170d7e7213d56c58a46c0c7f51afa0819e0e73d22e579ebda25063aeb07e31cb7e3de" + }, + { + "public": "01d08a3c540989e4c94539410a03e8efd3367791e7a393ef117d5009328c0bd4fbe4b07e0c1c131bf8de235bc88a64d9f25db28aafc085c9f918f6543491ce4addace85c2342e4b6e8e0928adc03710cec7ffaaa450cf7f1d79ec91cbba3273a554dd9cbba75e3459e1193b322d6f63346d8ffe9fca66a6d44e4f019f26fb574", + "secret": "8c1444649940aecbd3adffe0c0bfd0764f1e11bbd22a3651f1e6a621d23a55242b0279dcb57a6876da5d95c1a7a6ded9845f6990667c5edd5a53843aed19a040fbe4b07e0c1c131bf8de235bc88a64d9f25db28aafc085c9f918f6543491ce4a6f195ce93b6e1c4bb70f37c0a704db245eea33071bf303d0b48edce281d7ca39b08bab40bc96574e63f350f61d65e598278617e6031623828490647605cc4c87554dd9cbba75e3459e1193b322d6f63346d8ffe9fca66a6d44e4f019f26fb574" + }, + { + "public": "ddd0de4c7c30b009cab97ecd90d56bade3d21b1970ce1dae7f9a1db5e1ea6cf8c7f504dbae81c455363773ce626e36b3f59a0c9d6f5835b40ce492fc982f1ad09b770cd7561199c020b50f0c7544b20089cdf52f6e6dbe45ed95d0246b94f2699a1f17307e52e40800a75f0e107f6a1af601b010eddaa5a02a8abfa00aeb6b3f", + "secret": "b02ae94039a6a1acf431390e6e6395e0ce714635157e22b8b1ab6fe988cea32daa39966dbc850cb8436df9d8d65220641cd6cd9632cf64a5d93f380ce7a67cfdc7f504dbae81c455363773ce626e36b3f59a0c9d6f5835b40ce492fc982f1ad0000ab0a9fb84057f520ce54c2aa36176d7da311a748dc91b40beee3d2ddd3c889668da675bd9690000fe510ea5ca12328680b3bdc62fb37050548c7298a6b9ff9a1f17307e52e40800a75f0e107f6a1af601b010eddaa5a02a8abfa00aeb6b3f" + }, + { + "public": "29c3c883788bfb8ea56e38cac294ad68281717340e7fe956050d73647ceca648eaf39c9bfd6508a6e22aa025bae02787ab981225ed89808aa1b09660f846d8d51d360d0bc1a6ddf78e280a39918cc79e0a937f6ef9e34b73b95b9fab361a8f3e915cede7da914dc01f53aa008e113e6bb35dc7cb272b4535826a5c0788c7bdd3", + "secret": "9a78ba72c19f0c2336b5ebd4a4d0a04689d9552c97823e28994f42d5db2fbfc0feba94ac96b9eb84aeb630da6612f0dd91c19dd6c284464e850f431d34904dbbeaf39c9bfd6508a6e22aa025bae02787ab981225ed89808aa1b09660f846d8d5cc0711583626c9d4a0296724b19952e43c384314ad4fd5a29ee2a9c541865daa726dc805e629bf9b088a9097a264f35bfb9fd78c3c797cfdfddbb37dd6dc42aa915cede7da914dc01f53aa008e113e6bb35dc7cb272b4535826a5c0788c7bdd3" + }, + { + "public": "234388568f7aa4fb725d2cccf614beefcdc2a1d9204b3013c15a1428dba9f650be2e3ac9031f83e1683a9d82c4178ba9b4fe2bb3b514378b202dc84221a4a520ed3349c973e3f296e6bfb4c26d738a0eae83c28f76f5a1c3aec5024177cc64d570a5ce94da1f224c45fd9c507ba4d2dde590048524ae58ac9862294fb16d891e", + "secret": "9f52a62899ad10fc9342fb461caad2f1a903e3bc9cabd86e3a631ceb1235877658e0d6709109d91f6a0beffd242ecdaf576f0c9ba277a4e0f5dfbe62ea09d570be2e3ac9031f83e1683a9d82c4178ba9b4fe2bb3b514378b202dc84221a4a52070caebb50e9ea622a7a12a8fbddde3ee9ddf7bdea513c243bea607143490e62220be1eab53c84af207033dadb909ecd980b94f3aa4db17062baf1c7f2c03a69f70a5ce94da1f224c45fd9c507ba4d2dde590048524ae58ac9862294fb16d891e" + }, + { + "public": "40b586d249c1df60b2995487952113dc1d8dfa68e9655cfbe44e9f8c03b9dfcdb8118ee78ff45a16149fb7bb414938a714fcf5a45b3255adac9ed9f4503f84efa864a66351ddc6fe8cdf4d7031676692538c888aa7610ba1aca6bff251c01e9035e25e74cbee195bb9ccf3d07916a9ce58d7d628f4a5495c5b8cb23f0ec61e74", + "secret": "0514b072f26251813e3d7c2e4c2e288e5e898d28a3b8455989f82d1b534395eb444e96bbd13f30c4f750b61f13768e2921175bfa4793f3108e4532081ac7bb94b8118ee78ff45a16149fb7bb414938a714fcf5a45b3255adac9ed9f4503f84ef656775047bc3ffa3dfeddfcc4c5d1bc323903c5f1b3e32a7c23d05d391ab48dd428faf7bad5b4fb740a015184d3d10ee00167ca5fbe4226d46eb25c5dc9567fd35e25e74cbee195bb9ccf3d07916a9ce58d7d628f4a5495c5b8cb23f0ec61e74" + }, + { + "public": "d607b455a451340171630b64c5ff365647e25e317006baa56432de775b481544d8b564a28e9066be2406b8af1f7a650fa22a82982b6df4eeb50c061311ccd12c43c69561b78055798c05ea2fd7339fd8c4ded1f4734639ab73ac0b7bdf47e96bb37b94f425e2347b78c8216a51954c1b322a888bb41653b468a77318c59204c9", + "secret": "84e5a67e55722f33bfa44cac24164458b569b548e2abc1a619d9e0e8fb058ca3b109dd19305bcb552291072a1cf2ecbd2bc9c1ba7fe31fb2970e8a0b73f8eff2d8b564a28e9066be2406b8af1f7a650fa22a82982b6df4eeb50c061311ccd12c94d1a5333139c9361ebfc52cfabb6b951da9f0d21d0dfb6fe08351ab696cec8221c5dde9169bf65ad8fe4408bd7e5e55156db3595f923fc9968652da0b2432d2b37b94f425e2347b78c8216a51954c1b322a888bb41653b468a77318c59204c9" + }, + { + "public": "15037c3c0bdc81a00d37300c1a726126a8fac4e2e5a5555a4368b141a5b2fbb3e416887f336ce513ed54493165ae970660b69a23e88efa1a780c9532e6f00a8c625548dce1343225effcc970d8d5ec8c9b9eca0f64f7bbc4c81ea519677c1f9619440463116c03a02ea292bcfbd1f840e4f7cf85b16a5b60b300910af2156798", + "secret": "2030830f044a8f95ffb20710846e42f2257fd56f9cbbecb2e1eff90f0c825e0f138748bb3637c0468a73d2e284a67ed379c063a9266d1a84d971bfc58615ec9fe416887f336ce513ed54493165ae970660b69a23e88efa1a780c9532e6f00a8c29a9532cdb41b40e7d6e3ecd297aa860d4d704e17dbb1b2433591617da5890e960f624a779a68fe589b047d9ee57d33004fea3e1e85303e0f05e613d042d215019440463116c03a02ea292bcfbd1f840e4f7cf85b16a5b60b300910af2156798" + }, + { + "public": "6d732c36c902cb88ab88ca4494940726927a9969b758906297d2c34a692d4a236cc47c8f1fc31fdb80366466c9952faa8625448b4b977caf8dec23ab9aa37d2c00dcb4b133a7dc63253f9179829d78c535659f9608fe32835bea73f1350c0f13ab993b9ee8b4aef994022ff757146cc2369494d875d3841960695ec376b7f53d", + "secret": "6d37472e610271f3f4f868b4a003365c59c418a388abf6b9a826a977286afe05c04587c4c202b2efaeca30480c3ad8651b28da4f1338e5c952f2ada2b947bc8a6cc47c8f1fc31fdb80366466c9952faa8625448b4b977caf8dec23ab9aa37d2ca951ee31b5410303f6fb5e54163cb8c84bbf7a520f964c62be0f69c365ec4dd55d2d7517a8c3e8c1aa8c5a13003d408fd5fb02026cd2061a4d8cd0a261be52cfab993b9ee8b4aef994022ff757146cc2369494d875d3841960695ec376b7f53d" + }, + { + "public": "a6c60936994b99aeffa2f6b9fe2c861a97fb4cae4ef37d228398ddd2f6ccbfaf776c7c6c10aea53ffe296d2ac39020a9eeb475ce3b9a606f85d8c3452d2c05f9cf49ab5c6962ff3d87e70da9d3fabc26707f5c6cdecedfad40b9b82ef4b76b6f9aaba6c9528695bd1a35c57fa2461302b1977e156d105e71019c4e9960e1d695", + "secret": "7103c2fbcff239507fb1279462115a129d316f54a911a21086add81fad0f1166f113bf9166d6ad4ed82d75d7fc95cb0540bd3af924490c27c0905b950ae6fc3e776c7c6c10aea53ffe296d2ac39020a9eeb475ce3b9a606f85d8c3452d2c05f9dc8f6971b0d61018115af79b7debb102d8085098e82787e2b458d3e7031227d9ba50ccaebdf570af1e02cf9c975a5f6413956c37eed0a91f35d5b535698e727b9aaba6c9528695bd1a35c57fa2461302b1977e156d105e71019c4e9960e1d695" + }, + { + "public": "4b9a1e3dbc7f1925d22718abe7df938170fee19d329ba2a4c8a5908e889a6b31f21abb4a66cf641a50cb8d392c592c0dab615e2cdbf23a96386328041b70952c45476e00faf35186a14a0090dcae8ceb2b159909c417fa1aa32e3238c0661b8be8ee4fbc67da060586e8dfbb98b6025f07188efb26a5df6c02224a63b2762cd1", + "secret": "10835ce5fefa599fd1b359d8702912a3ca5364636df837e3bb24ce95ab7b8b77824c0f7b0a3225ce9f9c1330b2a817869337753c68a18e4477d19848e320179ff21abb4a66cf641a50cb8d392c592c0dab615e2cdbf23a96386328041b70952ccfe5d98cb09e489d862d2b33a2c97cfd806b8eb0da3385e6302121fdbe3f6cd8b209c22c02b6c72e966d23f0a47a6e5962f9413a260456da9718460a6b74a9a8e8ee4fbc67da060586e8dfbb98b6025f07188efb26a5df6c02224a63b2762cd1" + }, + { + "public": "f9e3b2418d0d1917c490fbbd6bb68d1648b4a266ce6831a750fc2a4c509f4cc76c1339e3b7a23aacca2bab3514a667502041257475644c6920709e0370aebea39b0463131dd951fe4f3ad88bcef8762361fe565c03f89ce1030fc466e87bfd768df48e6b4c3c1a86a41fe2c6220f60f3764e058fde6e23c2f5be73150f7d5b47", + "secret": "25506056ed57a3b7c1e02f11f7e413f5d817e8f7b175d6bf7a18eb78b8bb54634934e67796b8330a19ce852abf62694b72b176d8365b3e686cd510a67581189b6c1339e3b7a23aacca2bab3514a667502041257475644c6920709e0370aebea3c4a4e93fac17b806399b07c998d1a8a585ed743f3063b3d36493659057978777ac1f1163d341cb7eba89fb85d63be56474e2d25e526a5a338faa4777496d3df88df48e6b4c3c1a86a41fe2c6220f60f3764e058fde6e23c2f5be73150f7d5b47" + }, + { + "public": "6767393f05582ebf1658d1f62ed5afdd305ecc3d6b2fc5ab422e097d295485fdc44ea600084e5048b36f63d7fe2ed08168de7b78625dcc82aed56cf64a38c3c43e12c4da29cec2ec605f39677d44bf9b314f353d99356705878ed56d8f2340b4212e8e42795f50530289eff2a1949f5280fde1d1070c72f18e78f0fb3e7f31b6", + "secret": "49cc6945814706065932cac02b7eb32789f5a24be1378c3ff2a11873c082258e39f10fc87bb667e717db3cd95a5b1569fe1ce7c5700e0e1278bfdb8537304257c44ea600084e5048b36f63d7fe2ed08168de7b78625dcc82aed56cf64a38c3c479f4cb06c644ca8bf0a100e1c03159ea49554e350a15278296997e07184a717f0e6a973367d165d143b3d6f812412f201e64a1759ed77dc236ad6e0f94fefa3e212e8e42795f50530289eff2a1949f5280fde1d1070c72f18e78f0fb3e7f31b6" + }, + { + "public": "01908de39a359b159b4575e653374883d745c91d75d3c0f62971039e3dba2967d0d1dd42276791fd55bbbbcedc8ad49bd2443234b9c75f96d00bfcfdbcff93b26c81fed2ffca715055801b40a026592504243797bd713354e041ae4990f2982479bb3bcb9b9b43b57734be15047c32f81b8e303535198842e6148ab405305b34", + "secret": "dd5ae49966cbf663ff97a68c96522357655cf2a0da79ed62d2a8078374d77e4fb09d1c0ff58e685562cd3b5f299e315a37752ae3d32d81a97963d23e99e41152d0d1dd42276791fd55bbbbcedc8ad49bd2443234b9c75f96d00bfcfdbcff93b297e23d43b5f15d5ccf8102f72d9266d84aedaf8dbf8c58f273ff028b16d85c185683511865f435be95ca5cbb38311bfa14023601e5c843555d53f052c075e9bc79bb3bcb9b9b43b57734be15047c32f81b8e303535198842e6148ab405305b34" + }, + { + "public": "ccc4f1c0d066c9accf5881e40207bf166b810491a2a8ec146d993910b4769d1739b63a5ef03d06f7d4eb1d5b871db315f3eeb644edd71d711c8d233205eec2dc2f174462b7aa11e95e69f40d137443c2ea2f72583264f8cc42aacad7d02d9f5f0bcde1c6fad0913cd2fcec235564c210198e4965d67cab47704c7576f5cdb03b", + "secret": "81594e6139f7ed070ff978f6753dd01d8389df1b0697847bbac586a00fdfd36c4cdac14153e4b07abf9dd429580eea0aea6fe6f68c70d1853e68dbaa4591a95439b63a5ef03d06f7d4eb1d5b871db315f3eeb644edd71d711c8d233205eec2dc71d7b6ee4d875293c498957a8c80a2f8c982dec6604d7df7359462bf2ce4212921d82a66298bb34f6ae62b652d393ffc8f079df77aef3ae9ae6813d547e68c4c0bcde1c6fad0913cd2fcec235564c210198e4965d67cab47704c7576f5cdb03b" + }, + { + "public": "ec89612c418f6edaa4d801e4569b57dcc2843d4259eb32a644882f301008c2392640430ff8a06ec50209ec8fec4ff33871dbbe0b165322140d3b2eb590b9fe0bef4432b5e81a1eb6ed4e67b75c122bff15235de7d234a38ed236e57641354cf8e7c390f86dfb3018c3e6e02bd62d7fee1511d6259aeb94bf1f60a274ab961641", + "secret": "4fac16a216da346f194b460803e8689e0aad395bed371eda5657c06191574a02ca01ec7761b018e050821fb8435b40f14f22fb3050fa004fb438504bef38428c2640430ff8a06ec50209ec8fec4ff33871dbbe0b165322140d3b2eb590b9fe0bbea9ab59246f551462267080ee6009ea23a1a51984ec9c04076379a1cff067bf522b77f1e3b2a4d49fa3659b874cf0c5827ad48d58050fa9611c6902a1faaf09e7c390f86dfb3018c3e6e02bd62d7fee1511d6259aeb94bf1f60a274ab961641" + }, + { + "public": "c11c8c1cc3f35532d54abc2eeace474439c2ef2bb2d1ab6e618cc07677d0d86c68c39c2ed3088d170b147b2b78da4a7d5887bde82ba9879aa0880f62c1c2babaa562ab1931afec70354ad6937b124dbd3cc4c481ff7e33963a6979b1bc661e8a8e9bb1a14d5873e62ffeff12ac707c4382f24c5ff1fa5093ef52b14931647d9b", + "secret": "3139c5fade80c9abcaa1acb3394d66f32c82c5954f0247f6c8157f3bfa23ceb8f9f2e885f15192ac2b440653d87ecbe77a2fcd8fc073d8ee39613d3603fb519568c39c2ed3088d170b147b2b78da4a7d5887bde82ba9879aa0880f62c1c2baba70415ff4b879a415e113711299222c39ad13ff08ad645fa9d6e8b82223b85f35484fce1d45f53e8c7afeca8473eb6c26492c1a429bfc55843431d608d7b45e098e9bb1a14d5873e62ffeff12ac707c4382f24c5ff1fa5093ef52b14931647d9b" + }, + { + "public": "cb352a4fa70a34b20d74baedc2992acf544cf0a28ff6568ab1cfce059882ef5d0a2cb70e979b6352e2ed49bca1b35f3520d5863ba4492b03695c06f128c505e9713367bf4f21c5f6a44e7f2ffc54d8f762996decaf3394d1a2c8fde4f518a35cbdcda42d21c450a99a4cb48ce2cec393b8ed9659163eea422b234d970f64b92b", + "secret": "e44941dcde099d77add9f5e930befaf05d8b3bae725eb9f5f1619eca89c7522077a10a2aa6e6b5aec86159a66a7b3764a1da9f537247ba7e311808bb218d39fa0a2cb70e979b6352e2ed49bca1b35f3520d5863ba4492b03695c06f128c505e9aa6dd2af996e52a843551e3ed5dd362629095e7267f4aa732c3940b0c1a120d7d6eab5d9e1a717356ad713dc910e374d720f5860e32d489b31c3acd9fe23fedfbdcda42d21c450a99a4cb48ce2cec393b8ed9659163eea422b234d970f64b92b" + }, + { + "public": "3bf821e1efed92012981a6169a6ddf261409e3e5fa02249d8fd561874b9cac340e1d6a724ecf3ba308daf832c70a33c0c2603b8b91d4fdbd68aa136a94996e9bdd1ba231f9d2283de3c437c3f15434ad2083aaeef90dde62df2dc3ee73e7a0da4c6338b3957fbf0c54b9086c83a0ea569b51e881ee6ea48407ef2b1f3f474d28", + "secret": "91009fa5fd299facf0f20169363b06fadc5f70dfceafa6a8756f8149133a0b8d088deb978cf5627aed766b8666c383ce7eb9b2d49dec01eb077040c87faa22f50e1d6a724ecf3ba308daf832c70a33c0c2603b8b91d4fdbd68aa136a94996e9ba3b94075ba8fcaf642d6bcdbd306fa6a6e5775251150e53991e66a6ad8e693cebd0f730d180ca786ce61abd5f681482a05981d91f44b79a9ce2766425894c0384c6338b3957fbf0c54b9086c83a0ea569b51e881ee6ea48407ef2b1f3f474d28" + }, + { + "public": "7b64d1b02e889ad3747f5b2a463895537ceb181fdac1b10a2dac27823069f771a4427863487845a205f6d1f2dd787a91d0513d1d2d3fdd433588c588775a4deb2620e896ffc00f207abb025ca4635a00bc7019d47eaff218c2f1302030e7ba3b4f9bfe5e514df3f7d68f3fc48c53b985400377cb9e11fb0573a1de16e10a1965", + "secret": "d2c7c4ad12564807663b8dea25dd6034e6aedb8a6d5de8e208dc1402bef67c83902431d3c4f5534e256ba384b9246447e815667ddc4a468c437934edee16305ba4427863487845a205f6d1f2dd787a91d0513d1d2d3fdd433588c588775a4debaaa73978c074ca6bb410099006169a96f8c74aeaf8bafc90925056beac476b58a9f10f45fa2fed3c5df9091f9d8e2bbe11b3d2051a9c1d36486ff6b50003e7bc4f9bfe5e514df3f7d68f3fc48c53b985400377cb9e11fb0573a1de16e10a1965" + }, + { + "public": "96125c2f05ef4723f8212fee4db9f5ceffecfe0b849010bb3fdb28d37c463ecf2e99e8dfaa2877e852e7149dd3ffbeb5b0df2701e605c54bf301949dadf4c6b115333ba0fd7be07785bee33415fa18c1254b605e08431f237a784c48d20f0e48f4309388c9ed8848b61393ceef2277e4f8fbb821e144b0e7941231f860ae4916", + "secret": "529e50a43538d728ac5f28dccc6df9a550cb30b5fbbc208a1c27157867759270bb45ee3b425be1140b85c14d4a0355bbe9ac14af97424e8368ec94b686319c952e99e8dfaa2877e852e7149dd3ffbeb5b0df2701e605c54bf301949dadf4c6b174424fef8159d1ee276d670ed7d20952017df234b387055bb67fd453a9d427f6721c4918d56d78512a97292f4e782e25a5d798751b8603dfb4ea8f9d713350cef4309388c9ed8848b61393ceef2277e4f8fbb821e144b0e7941231f860ae4916" + }, + { + "public": "ce16073d7ac4a6ed0afcb9f1364025127fc0726380e0e4198bbd2f6c155cea06c6aed7a8f5bcea9d57f2c46476367a5546fcd734da0a1ebc4283a532c16cc66859e34264c4f52aa3cff2c62b491a596f1bd057c5cba3281a7c5b59b4e54fbca990944bf49ef7836b204661a819ed9ecb2dc23fe8bb7c5047360d1bc8aa9a1ec2", + "secret": "f17c82bf5caf5aedc07e7a65f30c83febac7ed07f377029f0527c043a825d58c83167b209965869f3e0d25f8d94ce0fd7a4ecc5c32fedf8f4e6cf33bdc05bcadc6aed7a8f5bcea9d57f2c46476367a5546fcd734da0a1ebc4283a532c16cc668af90534095c07415cf9161aba91674db564e4bf2e94f63f0c7a67809aefc26c243705a798beea33715f42d04329ab87e34393e421c2cbb4c105bcd352d8c1d9f90944bf49ef7836b204661a819ed9ecb2dc23fe8bb7c5047360d1bc8aa9a1ec2" + }, + { + "public": "508aba367d54e4ee349124147e5d9e5d14dd4bd366dfe4794a2620f14ca1ed7bb4cceb77db09778c8ed6b3ac98d9f65faf9989fc24dd984e02e431a84b24b5d6c70efdea56a2805c10fa28295c7cdd4da788d8256399c2f4a948c1499be76639bd635423b29d11a9095d98fc2d7ca006f21000a19b7bca238600e357dc2227ed", + "secret": "c113e2e9533a93b340a173708d9710243f37f9d01c3e639968c67a3f724cafa1d6e9d9977d5a1f0a0e0eb789c5aa82850f89379f90a1f5f98311f403f72db96fb4cceb77db09778c8ed6b3ac98d9f65faf9989fc24dd984e02e431a84b24b5d6dc69270780d0b2b2c9283adb882faf2ca6b6c7bfb3118a8cdf9cd9f4b519ca80b1a981c9df49099e33d53c397a4f023b4b503ec4f64dc09693b7e4072113a085bd635423b29d11a9095d98fc2d7ca006f21000a19b7bca238600e357dc2227ed" + }, + { + "public": "cbe002f4039dc4368a455d60d83104fae2a6f831e19c02ab27fc48362e509d6a917dff6b9cf6e72349122d69036611f128ec80174535addc1587f2ac390b1f081603dd9f9af9d5c68fe6a3f8a450cb67ec2b136dee924dd060c46968e1c1bbf41454f6a5c4f7a15b54e3765a6b29f6edfcd443bbae89aa4f2a56cffccefd10c1", + "secret": "0f47f9fa65489c5e1c9a12f589543492c0af427b9c040c6f1b5788c056f94281bda1871ebd611583c6ecad41ae48ecc097c843b443e11157cc33784065cf7f52917dff6b9cf6e72349122d69036611f128ec80174535addc1587f2ac390b1f0877c4a31d9e4f9ce918ca7059d51762306ac11f79d575dc0df96259d7aa8094b0675453cecce792ad2dfd6c59f13c706742efff5ea3345203b2aad766d99a05aa1454f6a5c4f7a15b54e3765a6b29f6edfcd443bbae89aa4f2a56cffccefd10c1" + }, + { + "public": "9af74aa260c926fe4e64ea6b21e54b77acfd145f6d08cb6205d253a88b7a92abbf09e87204e9b85db153f8c852df3edc75688031486745409080790a1e745e5280117a05e14aea6929c818009d0424ad98f6cceddaf28b1f2c87a32335925cc0f83211f60cc5e20a385451114158c85b21f3ddb3f8820859ad0f855f3b5527ce", + "secret": "a78aee9fdddbcb6f8567bc6701bd3353291c04cc7ffb7f090ae2049bb7401e0fcf4691a3a89e1bb72e5ac3209b8443820b031798842a76b4462023ff2386bcc7bf09e87204e9b85db153f8c852df3edc75688031486745409080790a1e745e521d6376b5ea43c15ab8aafc2b8291e83c4385e4752d0d2367ee6f52771b6e579ef60b03ae1b6b610c9b7cf73d1263642eb9007f3f7a3e69cf93a125e676f12683f83211f60cc5e20a385451114158c85b21f3ddb3f8820859ad0f855f3b5527ce" + }, + { + "public": "b26c366df13e4125e2ca28ec5337ea398b41ef41daf7a23fd62af2d8ed036c4be2347f6c85dd25c8da6fdae9ce8b79fbee768214aa01582b9fcf6be9f0266a88b7709b49a4112c93f7aee5f48a7f2efbee784c68cb69290bcde9742c490e20b2af0b06fd7c88d61a9262ae5f126d792fa24f5ce07f4f2ff456d7097dc0e2ae64", + "secret": "8b49ce333c447774ab6c3d90815f9ee33049355e414a11f1d545a88bdb50a61f5aecb037c602817031af6c93fd56263fbfa5cd95904739de1f34706fd64c39a1e2347f6c85dd25c8da6fdae9ce8b79fbee768214aa01582b9fcf6be9f0266a88aa9588899252ff19d7c87bcd5c4c21b92b7377988846372c6b644dc49cda5abc21151e523bebb190c11882fe4f8fdd42ed7f1816a297c65046e6f4e53808fd5aaf0b06fd7c88d61a9262ae5f126d792fa24f5ce07f4f2ff456d7097dc0e2ae64" + }, + { + "public": "60b96da99f2840abc8b89efe8403a4c876692e69cd515b55704227d6cc915b2c95c7e6e5a6d25962bc4bda37d75fd9b38bd9d25916413e4c547dc835b22d6515cf17da290c3ef7c18d3f80b0d4b8bc8042d08c9d3b9cfeb3cc22f0cbf15c24704e70dfc213f2e9e6b25c37477bdd6fbbdb0005a919954c5d4820c371f16ce6a3", + "secret": "5367436c96a551b1a7783fec2fb827efeca5a0f4df5d2bcaea8588fb5a95bcee948f4c7eda213cfbbfa7d5f99970b4cfb888cc66fe9aabc6d0da7fec44bd710d95c7e6e5a6d25962bc4bda37d75fd9b38bd9d25916413e4c547dc835b22d65153ed606082756825911b958433d7ec8e2f8eeebb69ab31c48463f7a1694630f1746e524cc1afea035ca4888d0e03bc8151ce7fbc7edcc2ceaff2470bc96eb1f574e70dfc213f2e9e6b25c37477bdd6fbbdb0005a919954c5d4820c371f16ce6a3" + }, + { + "public": "6dc7322af1c4eb6d3e37385489bd25d1e6b2bcca579cf4934266914180ecce76a1cc9f79ea1f8002b84016bd3aa1955d42c61946a285e29d78cdfaf6af604ebc50fc0fd702b87a125dbbff1f0989e48a3738c4b0416207c252cadc045622c79ea061bb14982df1a019410a7c86d57b565402b1a7e3db6deeaeb2ac4e68c2c366", + "secret": "d3c15914c567f23d9e7a95228b2f607ea10554801ce6fb34b88207c9843b1aa7c7f30625e18e7f3c5f7e05c1b57553d717d913dec095a59d8de4d2de7d63729ea1cc9f79ea1f8002b84016bd3aa1955d42c61946a285e29d78cdfaf6af604ebc53e0d0d65df8f33eebe6d4afe7bd06d9ca924c851a66092e70ede37869891473ff066c37f703307e7ae89a1647b31baa9b2cb731ac80b43275fc85e024cde820a061bb14982df1a019410a7c86d57b565402b1a7e3db6deeaeb2ac4e68c2c366" + }, + { + "public": "2672571541fc25ea0d19164dfcca02dc0ea222f0d3f9cd3e8d82945ba69b05936f58934d4e36331fb5c1b5a5aa222cc9bd51071ac806818d0f4b9bab5f09a536f14ada7f1c9c862189e974f0fbce9c6e3bbcdfcebb07a50fc0d2ecf84a24c4f17666f9be20707189a7bfc7a633c9c436b3bb7b5588e0e48d98869e1f10cd74dc", + "secret": "0a1f48b7b7c1a77e6e8db967015f44a2fa87f199c34b3fe2877143c5550f132a06a777afddd7242d7a9f0086d6b00360ec444b46a7b0be3961d61c990f1f60036f58934d4e36331fb5c1b5a5aa222cc9bd51071ac806818d0f4b9bab5f09a5361f7d52a3586d4350c9e554153e508785b37878561b0df4b3d8e61f01aa0392e99485527520cd2de305544eba2ffdabab1140896669517626a91f2011b315eae17666f9be20707189a7bfc7a633c9c436b3bb7b5588e0e48d98869e1f10cd74dc" + }, + { + "public": "655159235fe3eb2887e954623f5adc8d5ad1ed46f056fd13cff56f40c57f432e9507768a4c2c787064ccc360a71fa22423c878528cf5756ce15481bef1dad3f766376ee9dcda054366a53ee7d36ca3f90ded86b0b2908c74496e047b9a508248745330a92881229da73790c97d20e3f759140a4e096d9a59ff6b5e02b7f8be8d", + "secret": "258e00cc21e2bab35ea5f8eb26f098fda043e2403676a88efea6048b5e29b53368f9c462aa4fee5d160cd4d9a0263876504e94c59159e067c1921d687d9afe789507768a4c2c787064ccc360a71fa22423c878528cf5756ce15481bef1dad3f728a309d5daa6daad872aef8b482355066cc48df16e992e23553a64ec5b4933eade7c2ccf6b09fbbaf7a6f0a2f3955f3fc2208128ab0ec644fcf1a8d2bae1ec0c745330a92881229da73790c97d20e3f759140a4e096d9a59ff6b5e02b7f8be8d" + }, + { + "public": "cb539dc95eb142e52539458c1b8efcbe150d06a928b82a28ec7e608dc9d04a130860442b34bfb92e1e6800c27575efae27c5c5ce1f92c04095221da2639b09959d3b8a2615307d3031ae9a24655f45f3248fbde2b29205042b71f8dbed422df063bfa31cfb692191a984ffeccaff788e148014b47bd0e3b429f0fdeaf30926c3", + "secret": "972510a06103ebfa2a55ae74f5ed5a6d33412c805f4f44cf5f125290ed20cf76a60d7789cf64ba0d057b0a4a7a56fe5c5f65edfaf89776955a1307cfc8bc83560860442b34bfb92e1e6800c27575efae27c5c5ce1f92c04095221da2639b09956cefffa2fcd59a38f4ea199e672700b744196740aa580e4e9cd916a17c6809dfc6ebac0da26153e860c85d85cfca54f16c61418a7cd3e079af75517d1b75f3f263bfa31cfb692191a984ffeccaff788e148014b47bd0e3b429f0fdeaf30926c3" + }, + { + "public": "66fd0678dbad2ece70528d6e0d622e9998eeca6a33e62559a60dfca30594bbd74130a0dec10ac36f797b16da91b1b10694ffe71574b5bf456c820e54fe1fed441ce67f48e2e911015f5051bb28fc39be46b77d512f1e868544d017974655ef30881ff7d04b0a045281082f27d71ba53cd76481f876736f164c05d14fda0b0162", + "secret": "3823d393a7b0a7766f5c85d0155de27c4455b4b1bc2d60bbaf7b268105c9f842a25377015f39fbac90d2d6ca6d04ac1399e02eb290861db77d4230f6c5dc8a5b4130a0dec10ac36f797b16da91b1b10694ffe71574b5bf456c820e54fe1fed4496a8130c29e337cc8c705e635967b7f1b7c348f5f0336965310f3206913e58da24c59c203ec0909ab0ebe0b08d68482cfce8be03276ef26ddde8b2a68554efd0881ff7d04b0a045281082f27d71ba53cd76481f876736f164c05d14fda0b0162" + }, + { + "public": "c1b4e35c7e991fde90340b5b4fa86d0d9c9ad23a4c8c7f1bd5f92a3ec588384abe15d77345143401d0578926c2892ee2f68316aa4483c6d6e0a102c78c36fee53639299e21fbeafef5393a42769adf99d0fbfa167bba4d29afc7baa0d2bb17486bfac701f9d96fc970a5c702cba2cd46ce0d0aaec5045a5a4cf14c63344383d8", + "secret": "179ba138d7dcf7534706ba69741269462412972d94a6f08ba4afc3ca1e1d6455a4df588d476e316d888aacfdaff5f2f26d45f0f311d884ad2da26d4f1bf9ba03be15d77345143401d0578926c2892ee2f68316aa4483c6d6e0a102c78c36fee59db9b66dd2e2d6427730d8588ef9258e4f44f41b926b53670347f88ad749d00525a7724ce52960b85a4d8211191c069aec0189df5a86652d6ffae3d4868831e56bfac701f9d96fc970a5c702cba2cd46ce0d0aaec5045a5a4cf14c63344383d8" + }, + { + "public": "1aa73dabd2feb815a0e678e68aec37bb8b1a8603b8161265747459783a833c8bbcc17c9a8b647b2468c5e7eb232fb651fe7eb3ed2f6fa71d9fc233487f219020da577acc36cd444d8f266912c7f9df5837ad45d2d54d5a29b177f476e0c07eddbcf0dc973b1872236c94d15626886f0b7bc9797bbb60026c2fb4aa534b8dad18", + "secret": "bb627ace8666b513bd2da2ec6d07395441e0ca8a5e33c91d5bb2a756fd486f48c36ccfea28f82175f6da0146ce7a2a44156bcb36f9a4ddaa88fba24ea7a8a6cabcc17c9a8b647b2468c5e7eb232fb651fe7eb3ed2f6fa71d9fc233487f21902016e1d69e89f7aa5dd6ce5558b3823a69fe42ae591378c58563b39040968cb19038197fa9bdfac675966e83b2da3896a802bdc76eceafe4659b0269d2e8a42525bcf0dc973b1872236c94d15626886f0b7bc9797bbb60026c2fb4aa534b8dad18" + }, + { + "public": "11de017f8745c05d7a276a2445b7afdca4560cb320af32f01cf2bef061344d2644994850555f570622b98fbe7d2192e0f0c3e6624dcaf20ecc7a6e96c4cd1d2ebe78b61e669521927998a493aa255a2371734a96fe103b0ecfceeaf3cf9d0f82070859fc95d6d201fb0661271bd9ada74ebec6b3d2f8b1b094f1dd2aae9df883", + "secret": "c25b2855d9badeb75d64d52ebafacb655f8bcd4b0a3697f31c63132ae9e7d247d514731349f0abfdd67903cfe3f495fea0e736b0579452314e91d9c6964ca0fd44994850555f570622b98fbe7d2192e0f0c3e6624dcaf20ecc7a6e96c4cd1d2e11c10da143edfee0732b4713d14c7cf0481b61b299831dc9bb7ad5cde7fb3c54449a975611cb9000ef3a35cd8ffe5ec409f481258824f42ce02db8c751ebc423070859fc95d6d201fb0661271bd9ada74ebec6b3d2f8b1b094f1dd2aae9df883" + }, + { + "public": "9cd80a661f99443ddb9c122d41807eadc5f456512089771e3dc33480f9de48584768e1555668a76c99062b48be4086eec27c4c052b29f4fa2a72c2e0a71ca22def9de288a03d064b19260a6f20d36826cc7be04136bedd3298295d8de58e0f9d8aeba458c7ff27e99a44fa95e5e58a6cde08821dbf486b774e1f8a3dff38ae67", + "secret": "c172ab7605a2b3ed4e2116417287abcde331e3d5ab2b8b821393120550998a5a8e8188f7cfd0e1ad4857a32c7e1054c863a305cdb457068ec18d0e53b56978054768e1555668a76c99062b48be4086eec27c4c052b29f4fa2a72c2e0a71ca22d39f07b4a68aa860555c0a64c9ff72025b7ade7880afac8b0473ae3540c840d612b99c9fd20919708a699af3c0cedae6e3fd6644cb74ef60a0fa7b1e2bb96ff8a8aeba458c7ff27e99a44fa95e5e58a6cde08821dbf486b774e1f8a3dff38ae67" + }, + { + "public": "c03f33d738c4d3f3497a746ea23c0e5ee4c4a38f223f272a4253751b6c442de0c7598abb94efd9b4fdb1e435a420a2c00e981b27a9f02c72b23cd58cbc6d0160ee0b7b0993ef5d67b17fbe06140a302724edf31ca1e65b2c01f6e94298ec40321c998b9eec3df8d3135104f30951ebcfd5cbc160df8e027fa4838949d1aaf86b", + "secret": "1d3aa726d7d188ccd0fb8b2b5bd0a36cf3c94730d6dc564d558964f57428e46cecd1c56df1cb9afe38c3ef9ee706f6c858228659ff2e1f20bb2d34a241b1fee4c7598abb94efd9b4fdb1e435a420a2c00e981b27a9f02c72b23cd58cbc6d0160a5ba1726b024bb5f2029b159af991413b5e611cf8bcc26974fd7269ec23c297c18423e8fb85fea88c33368b5778a68b2921f5f34ade065cc15f6d71539e3848b1c998b9eec3df8d3135104f30951ebcfd5cbc160df8e027fa4838949d1aaf86b" + }, + { + "public": "47b1b2d1f324a3692b59ffd80665f4412007a7eb16753e9cef4fe8b7fb9c2255e13eeeb931931e195f6b5a6faf4a7e0aea1568dd45e202b535e744511bad0dc9e1a6a14b88e7e64b5000fae1bdf6034449ce265138158b92c0942034486b4d186f327bc2be9c6a721e29d6a50167c780d6795dcd2661e477a43aa2737337b354", + "secret": "e7a0d702697813da450cc88e062eef0e96ecaa2fe199e040d57ffec4c2bd7128ad721b446021cd1aca303ef93e2401a09949d235d7e1c5f1e66db9ee770d614ae13eeeb931931e195f6b5a6faf4a7e0aea1568dd45e202b535e744511bad0dc97d30ed9951d0d6c788bd7c989e76260ae94e6c289e1bc5eb2a58954454c388573e837795050c81475385a3f23361c7a87fcf2a72646581c267c1f051b4559e696f327bc2be9c6a721e29d6a50167c780d6795dcd2661e477a43aa2737337b354" + }, + { + "public": "d34ff5f1238a53ee310397b8ab80a192b6ac435f4079400ee6d2b469eba71c8070355fb9948101fd90c7e1c9ea105ad02397f346250f147df33bd3b5345d34c097341cf6f5078f0fe59ddb9e8cd411f1ee22a2b3276b7e0e4e302d89aec7e500dfae3fe1a389f1391249f4a2b4823420dacf9ebe39bd488837ea178aa56d31e1", + "secret": "9069374f07060734b73a77f74b9f3f1cd192d9f471719d9f76f1686f92714173f65d811311cf4df6044eea09bf09c8328058825f704f387a2ac7c6f88cbbc74d70355fb9948101fd90c7e1c9ea105ad02397f346250f147df33bd3b5345d34c0e7114418494bc8ad45988890fba81325962e1b65d1011fbb433e78e08db8ea04e6001aa2af2a997add566d5169e4bbb83982937856b2eb9d2dcdc94e9b99f4fadfae3fe1a389f1391249f4a2b4823420dacf9ebe39bd488837ea178aa56d31e1" + }, + { + "public": "a3d1ab90193e04596293147efdd1478d10e4ca8f4650f41429535192d509fb6b387fcd869b341b28cb9b1f97b5d30e0ea8cf24a2971149825f96817e9c895b7d0acd92dedeb68ccb08450db8eb0fa1c7d15baeba0499949df80ea91aafead8a4ed779345d1782a0e1461aaa56c1ad87f61ebf8f24235f90e9764450eadc0db14", + "secret": "f520a6c8afa6d50357419ca38d7e4092731e2d97b7a01dd3872f655c76fea4621fe5b4c00ed9c59b756804e3c2bea1df22767ee13ab95753a3cd2f00751dcc99387fcd869b341b28cb9b1f97b5d30e0ea8cf24a2971149825f96817e9c895b7d1397025abc4e13f8e4fa7bf0008890acaa0d2db4e048fa79ab477526fc41ee41da3ee95140e6059564980e0bff4b3c1593b29b7da2d3d4650e8d2242470fe34fed779345d1782a0e1461aaa56c1ad87f61ebf8f24235f90e9764450eadc0db14" + }, + { + "public": "93381b7bac8a92f92dca741188588b72288c401732b5b7a3b6d77e66fced4e84c977cb574c3ad5f91368908543b2251eafc844cf749ff783a7e0aa10998b1aeced5529227b8aa0d6d03fc66d69b92260530b853c93a65a8e030323478c63501d97969edd74a9d575fba8126c57e83fd621f2996a98c9c59ebb91d410190faff0", + "secret": "d019285df5c970a7c884ab6d89591ce7d7a147c5b8b4cc3dc0748229df72bdaf71490bdf2e1d56fe4521973cf2750fddbfe57ba5be318fe4173c3b8b0a3d1dd8c977cb574c3ad5f91368908543b2251eafc844cf749ff783a7e0aa10998b1aecc0436b94a436b9083dd69987df961a63458686c2d098e9dcc7f2531417e7a9db4da844053d37f9068de34e42f0c02cccd44e213ef341d60411bc443622f085d797969edd74a9d575fba8126c57e83fd621f2996a98c9c59ebb91d410190faff0" + }, + { + "public": "7deb84597a999b5bc4d7aaa0ecab0e12c719434b0d671ca0130bba08eba8eadfa994f8cabeded792985a70249489f302387211220773f83a872741f3a97403d1dbbefd6085ca364b7e4bbd9d4ffad7ac4f380ed33f9582690a88bef8eb97452bd0d92d7d76eee8dee547bffd48b150fb6a427bcc50f5ebef2bcf1889caf0b28a", + "secret": "d12a3a2793ac87624a8ddfea19f633cf69e870238f75485ee869cc67bfc4b6fda4331221dc7ff4c4c441791a7c620f72a494b7e351869f1176e15c91967ed4ada994f8cabeded792985a70249489f302387211220773f83a872741f3a97403d1071bca257e279fe3a6bcbe27dff21dfda7e12f5a9188ffb11ed3ffab36e9745d46cae2ac64a59a60fb08d69f5241c742f964a76c4e6ba18ddc9af8ff365190b2d0d92d7d76eee8dee547bffd48b150fb6a427bcc50f5ebef2bcf1889caf0b28a" + }, + { + "public": "61ae50fe84772c9b359b4f99e94c74715398506f545465323f306e93fbb0c5189654c1c3d24a33183c816fc394c7f935a1a259daed862cf01fabb56ee0a2f3bbdc05c54a5f3d0f3f115bea1c614a1c1cdd5fba111c726d6c367655ead4b76f43d8b42ca7b3e5d3a8465700a6171db890f7d3e9d98c6d16ef6b14178af307906c", + "secret": "6b88f8aa2b753f31b2caba39e18d0bf16deea7230f986c19bec7247d8a0efc100e8a1c96ae7602f1966500f65c7c7de3f2af8ea7c16372065d2d6b9b1fe1ba0a9654c1c3d24a33183c816fc394c7f935a1a259daed862cf01fabb56ee0a2f3bb0da7fa9c10fd447eb2ebaad7437cca74d39bf2492dbf2f6f52fe669fc743386f52b2992b45bd82f23f5d29f171f88c7f5d006ba5eab1ea77a573104085fd14b4d8b42ca7b3e5d3a8465700a6171db890f7d3e9d98c6d16ef6b14178af307906c" + }, + { + "public": "e8cca6d919f09f4336b650a8fb26331239585cd4161857cae8815a1cb20989045f1893e88d4b586334b005872978f49f34c5152628def49a51e29655a9f59563d8251ea85392829cdc94a601b90d678181ab2788a862c9032a61fc7d588d89a1e52e8fcb7eefafdf5b407d2012e791818fb97caac6fc9f1aac90ad8b50776197", + "secret": "3b9e96eeee7749e9659b5d516854e49afaf0a0f2140a15d284c2f91d5dbf6801389d51275afd42501699221916c3c8daa085b71c3296851c694a8ba6430273f15f1893e88d4b586334b005872978f49f34c5152628def49a51e29655a9f59563a0025a9ddb4635b3f5ec5160a0bc3f2d6c370dd0a33cd4daf558285fcc27371d63686d9c879a8e606d4fb5c6af072014c785b60c35bd1b89cf45881dfe841823e52e8fcb7eefafdf5b407d2012e791818fb97caac6fc9f1aac90ad8b50776197" + }, + { + "public": "8ccd36d72306599e5d6f2deeb848d9ca7e3c9089ca39c18d0643802762d17cecb1676c777508cdcf12e4fbff6db9785a815611e3bc2a8147483f1fcea75b5cbb15c3b84e065dd6a2deb197a4106f1c338127e24738987b7d56d5a0349479195d48a54662c84ebea106abced1e7889fc84c37c14beb249e0948864eea7d386d23", + "secret": "f6d92a04b138eb58e62739c04666b4f420dcac9f76c7adcbd055bf55363654512ef4a7664726a599151b8666176a18e894836d7668b35c7dd2341b56e5d72c2fb1676c777508cdcf12e4fbff6db9785a815611e3bc2a8147483f1fcea75b5cbb670871650435ef84b590a84b433a499888d12cba0e293f563b0ef3c1cf969bb85d3efe70f32b220aef6a0908dca2f1ba82fb138ef757cfb0233f761e7e86450a48a54662c84ebea106abced1e7889fc84c37c14beb249e0948864eea7d386d23" + }, + { + "public": "40e1224caaa28af1b4591833cc8f0e7901f87e30e48aeb2552fea657e12e7a713c9b0beda7793e1291405eda80f4dac38d5caf16437aef01e90f132a510a0d20715160cb1b0804cf1dad74e33f6b7eb65d936a9cb0204f2ad5b0212537c920ac86378527982fc3352752b881b1c91f455f2398e1d027a9a7fd405727501f1e4c", + "secret": "d7a03f9a4d0cbf422129395a8c16136905865ede63c54da8a55946295fa5c79a3729a66c3b8897308303d97bee43bbedf33f034237af51a184cfdc0b6a5fa49e3c9b0beda7793e1291405eda80f4dac38d5caf16437aef01e90f132a510a0d20f08fc8ddb56ce63df075f33ecf52943af5c7cf28e19e4202af4cbc0607bdc21573783a94f04cbcd2b39ee5f4a31d81b6a825576eca857d17d278c711acd07c1886378527982fc3352752b881b1c91f455f2398e1d027a9a7fd405727501f1e4c" + }, + { + "public": "614ea5c7de11d93fe95f2de38956794267f95ffca1cba7f86c7b1a05a16201a873e2f75d5b62fbca481e59ba1250fdf3fd681e1d7712fd6a18fc094c3389f7bad54aa40cc6269567f3d60421995f6e03952d678293dd0e4b17b30a3666ff5169a1b68f25de7027afbbb89c94ccf045ae6c029de77ae74e1aadbd63cd4a9985a4", + "secret": "41b85c6fa2adea8c91f87af1fa9ad04e8324d2a30128fce358681cc5d14cb66bece0841186b63512f4f1b1c6e8dd711b8002be7c21d06176bcb930f35c67905e73e2f75d5b62fbca481e59ba1250fdf3fd681e1d7712fd6a18fc094c3389f7ba905afaba266f9cf82e106440b3ffc3a761c1af7ef6bebc43cf51909a3490b8a2b8aab0b5ace5d6dc152348a8b12a8ec5ae27b3a0a95f58e41520d7a4203f26e1a1b68f25de7027afbbb89c94ccf045ae6c029de77ae74e1aadbd63cd4a9985a4" + }, + { + "public": "4098b8c2ee9eb7ec3a726c47da9801bbfae0de82a5f1f47aad07fe2bd59927519dd65a1eaba4641a71895adc6fdde4779e89cbb868ed01774d3bee122d8c30960a8373db58645990ee4a1b6faff8deb1437d269bfb020f1b5c28550552fd47ca02e3d63ff10bae0e95e43b67fc69778906c0dddd0d677338ea9cd47b7fcde988", + "secret": "40ef887c1ffe6c5ab45f41156199aa89dabb750b2792eed2c14e348e26ea43a17745db7707d9e64ed67fc175dd58b860a8c746e6afdf9595a9357416816f70ee9dd65a1eaba4641a71895adc6fdde4779e89cbb868ed01774d3bee122d8c309654a4a64e3dce937c66e156c679fb6811c6979ad7caaff1e13b276ac70f4435bb96d83acdf5b10f5c41c5c57e0ade73b5855308c473c6a33fa3cf510eb3cae80f02e3d63ff10bae0e95e43b67fc69778906c0dddd0d677338ea9cd47b7fcde988" + }, + { + "public": "72c0e54afd72cdfa394d1bf6d5dc8c8df907babf12ae63ae71d4348a54e5638f0c712d244cbe9ba919282572b123848c3805e0e82b6737e28948b5c4715ad1046b976c3e43d029630fc933e813e5b7b0ed83644f1327d6cfe36686f7e3714d560c0fcedb8bab4ea026ff58e1f859ca7a5ac7987ef4bb89aff4b3a95461c4bad3", + "secret": "30b142c5b3f16371bf67b5c1b59df1de8a694dff6fcc467e1b949c0d790cbef0301698b0ef1877655b9970cd42a14a73a680cb0678549caca133bd827d638f040c712d244cbe9ba919282572b123848c3805e0e82b6737e28948b5c4715ad104a0034d12c031eb8ed452fea296eb92e7ccd74a0ce086badd450d5c7367ac2db0a7a0e827ecf21010d0f2718e0ff99c8614c6902eb7900235f3cd10208cb2ae360c0fcedb8bab4ea026ff58e1f859ca7a5ac7987ef4bb89aff4b3a95461c4bad3" + }, + { + "public": "84ce6ae628faacbc3b72c0e02f4acaffac504ca276a2fc37e161c35542d9ed43b1650c54441d0fec04b01b4ea6926f39b314d3b2113f80bc133c13dcdab2e6fd25648ae567b6548a3cc4f951f68f2797dd8b36841cba9d8d697d35e904945569719aa790cc8c1b232bc3a69fdd6fb6eeda9f115b0749d29df7163d1ddd9212a8", + "secret": "02bf5dd48eb47e2a70485936126eb32e8d71afc989f00b49dddda2960cb2105403fd2cea5ea6270e0da89626448d838706de30aa66213dc80d8bf086e0f478ecb1650c54441d0fec04b01b4ea6926f39b314d3b2113f80bc133c13dcdab2e6fd7743f0d2a5d5341df4e8f801b84833b1bb559a49b71943dd1d207708fb0ee3e25137f0be4594b1a613e834b854239cc38c22bcc6114119b2c838d9ae5e49b789719aa790cc8c1b232bc3a69fdd6fb6eeda9f115b0749d29df7163d1ddd9212a8" + }, + { + "public": "68721dd942a4bc040f91778ca80f88c38587e20ea664782a2a34f8983c904c30a96902f9540caffa148ba21193d766d97bdae1a84e4a815526ae108d9e4818898419f85a76ab7bf2a3a8350e0fb8333488a581a1cc0e7c89bcda29d988a962a32204bebe6847b043f50a17a89d243decb55a64f734e16bd0f8f734ce5ae0926c", + "secret": "22f01dff45904cd6e1a341ea3c4c3b03c381675d943aec783baf06fdd8c4c384bd783f1d41216c2972ef82864e69c0a28017ce790ce0daaa1368bbb920dfb972a96902f9540caffa148ba21193d766d97bdae1a84e4a815526ae108d9e481889d0cb4297be81f4316d3d5853c9cebd5ae294d0b1c1121593532e48b7b56e54d93a7bd7c11941215d6ee8a88d11f9312cc83a262b0d5f4c25189b1a63e8f680bf2204bebe6847b043f50a17a89d243decb55a64f734e16bd0f8f734ce5ae0926c" + }, + { + "public": "8114aa30bc4ffbe4db8015130df9649edfbadb291e443b8856c4fdc7f38c7c12a237a8c68c7266bbd929f3fc4fd705b75a0d00f9dd7812486a176d6f4163160022531d82f0799be1a12f1e98e141d816743615030fd56624cc01d033015ab10e375236f60f34475ca7845dd2930314ee126692b3706223b203252a5e10c0fe0d", + "secret": "b98b4924ef7ebcbd5d51bff93b75baeb5c8f2738d47455c80f2d22b88f2df5926fd2611173acbaf50da524ac44ba378de319871ada4bd3f7152af5b617dc68fea237a8c68c7266bbd929f3fc4fd705b75a0d00f9dd7812486a176d6f4163160072b738abbe7cc16b0a6fe83ada31f0712db548002ce517ce318d2a040e44d7d4ea7777372475c676d6b3ee599d12f537ee2c674b6e62fbe26f20330057a85cfb375236f60f34475ca7845dd2930314ee126692b3706223b203252a5e10c0fe0d" + }, + { + "public": "94191a2d171c2d9d995cf6b4f09c2f300182339b693a68ad1cc0f049fa6b07bf0fddc4cd0fb710c4a1894234b865d8747fed168a294e511be639f1abd8c4eec94ff15a5f417929b8d20789ee81f7ffe5c86527cb19e74160a1ef4a24c7ee429565d242f281ebb5bb3ab85cc0387affb2ad461c4c75a8802b03b403c0dcb41d6c", + "secret": "c6798625e908d8933958b013820791b559ea6d71e2f1d192f7ecb1293e2a9385c86102a34f55151ec805d5bdc27f2010eca359df1c4092099cd546470308bbc60fddc4cd0fb710c4a1894234b865d8747fed168a294e511be639f1abd8c4eec9a7b64077fa4de42b697465cf4e7464b44a7d16e1134752ff690cac5bba0bd566668682dcff2c652759ced7c191c14beda65fc34ea38009d402ee2422b0f676fa65d242f281ebb5bb3ab85cc0387affb2ad461c4c75a8802b03b403c0dcb41d6c" + }, + { + "public": "de7ad7e3de2622b119b810525cac92b70f2369875772def2b4167fd2fe2692952f823b56740b91368e138b3ca06d990429e0a3e9f3fbba7b5ca85c182b748652f22474cfa8c197a1580efaad4f5d96ca3aaff081125924f9a5116eb045bfb04649157cd790ca1830cb9bf09c55631ae50b79d36147eee06631c50b5625f3d396", + "secret": "26b77a0c937df289676800f1cfa1b8accc4439536830ff6c22b49e62a60af4c7f668be9b15cc4c31fbc7fcf29faad78284c6989112bee7fc7446f8d792e97d4e2f823b56740b91368e138b3ca06d990429e0a3e9f3fbba7b5ca85c182b7486527982e0272c1992b6fcf5c7d9ae7d0c37b3888a5fcb3838c707bc00439ea5f9b05261a2d86c8dca53fdc1283c9926f4080e98c2136247b159e30c3b8755bb1e3c49157cd790ca1830cb9bf09c55631ae50b79d36147eee06631c50b5625f3d396" + }, + { + "public": "a62ea2ccb8ef6573841bff6f49089b9957c1bd18f4c7ec355474ba5a508381e8b38f39b30e572544fc5b8b26474638543c19529adc7e3f9f5fbb442cfbfe5c06121334eab1fb3cb4d9fa91d9b9680d45b644bf4d41a3d020d8b56ba414912c51b5300b31aaba79abf7674a81df0419a37352b2c3a6983c25d93cf4bbd02d8d1b", + "secret": "2df789224363d3dd804a06c2b76debf6174d1467a3db8c5320cecb994943050c5413eff066815f157a301924dbdb30713f3df0be78d331ff1ec7f8763d288fd1b38f39b30e572544fc5b8b26474638543c19529adc7e3f9f5fbb442cfbfe5c06fb60832f69404192a357e4446767013553bdb10791565a8698ddec806a18fefebe66389943035b737eaefd316e20d546169ad6299684e346ce324413af7e705cb5300b31aaba79abf7674a81df0419a37352b2c3a6983c25d93cf4bbd02d8d1b" + }, + { + "public": "02ae9147c718dbe7de0ea095c5a4385b5429945a4c295e2beeba955fa5d7e838167c2911e39f6aa0fd847397ab4ecb4994af84a96323a462b86b0362be7dc9c8fcec6e06223825836f5778cf049907ab269646ced127443100180010af102fe9b8e8dddb032264cb1eae4ccfdf2362f50c3b9ce2d34333e765c5b0cd8fa9f48c", + "secret": "bdb017986d524d6ad7d365d6ac015714cd3ffdc58cee1926833cf4813698b24e0b3541b0379f5856ad0c206d96c922d2aae31972ded15ec459d4fda9a3e70247167c2911e39f6aa0fd847397ab4ecb4994af84a96323a462b86b0362be7dc9c8310845b135f3f304315ecee670dc9ab5c1b2e14dc0425fe3f5fc422fe672b91e3e7b2b6f964966da3227cbe026a0c7f5b7b1c7ce654bb8e8ac3bd5774dcecd59b8e8dddb032264cb1eae4ccfdf2362f50c3b9ce2d34333e765c5b0cd8fa9f48c" + }, + { + "public": "a8b9590880c9e80ef0384d5b801e785395d81603bd7dfe7978e325680294705fef8ac9c895b83f3321e47778fbf65582e23322a173df5075933c08d51f12152e315d39b891f9411e69011ab2c70f5d341abe19a9a3d93cac3f1c64f4c9c7a0961728370bf72be225c0ee2d31133440d00d3811ce6af51e4a064e038b96535fc1", + "secret": "ec29ea584a65177d3c5957848a26d7f348bb079d95a3dad702efc56899221803fe88b4071fdaa16e26e8418fbd552c28be0438ed0e1c08560ac634d93e606092ef8ac9c895b83f3321e47778fbf65582e23322a173df5075933c08d51f12152e0dca85506f7a69bf08ffc8e6c0be98afcb3d73a3d47dec018e28044b21b80ff1825bfb861a728f10fbe896261996a97e8b3ce579317cfac17ab595ab24b419221728370bf72be225c0ee2d31133440d00d3811ce6af51e4a064e038b96535fc1" + }, + { + "public": "00517da315cef2d02d09d69f80df76598ac81bdbbf6a48c2b92c511b6ea2cb4a13b00f0fae244ceb1f13a1bbbe4fcb9f4056b5632b8df667d3d42bb125e4343b76db2e55b6602a79a860874bf8014f08a6cd31f33d20dc6ca32ed72c8f1d6d8f8d58dd6c8a664250236de2d7b090c052af689237634a364a7a40595a13b43752", + "secret": "3f0acdf8a5623bda06dd72858d4318babe2a354008475fb1c320ea411e8e512bb0e32149d30e4a3702ccfc00cf4a7a823381b74b69f77a06f8957a1d44fb8df913b00f0fae244ceb1f13a1bbbe4fcb9f4056b5632b8df667d3d42bb125e4343b86dbde9d6178eb26d4bf9d8340baeddf45bff1ca07aab781a46c002349f83118530c8bd488138f4fea485c51e9d5dedcfdfc4bc9169eaea2d6f006fbc6afec3f8d58dd6c8a664250236de2d7b090c052af689237634a364a7a40595a13b43752" + }, + { + "public": "031ab3c4812917c83adec898dbc193c66a9014458a7f0eb8e293d641a69da404ca7f0e2f232615a9fc236f43515bb46cd90019c16879223bc68b29dfbdc657427019184847457a3020232107ab2925e083cd45022d287609fb25c28020cbf913f64026856da3c987f7debd1f0f1f3e4a528f0b368c3a510414e7ca480e3b3b12", + "secret": "cb8788dc13a2ce448f60f40cfa2df59f44e62680fa8e9143043a01c8ff5e0bcf18a19861417a20492b1f1aec84669cf3d7a8892fe878e0b34e11920341a5e971ca7f0e2f232615a9fc236f43515bb46cd90019c16879223bc68b29dfbdc657421311fbfd6e0064b551319ad3c2390d6460137cc59c5d5348292d347f07e5cca48c8fce3ac1610c8a0bcf216026164a5bae65538b9b2cda71d8cecccad1a5a54ef64026856da3c987f7debd1f0f1f3e4a528f0b368c3a510414e7ca480e3b3b12" + }, + { + "public": "70803f1b29171460324cff5e88d9f1b6f74e4312d94405042fa7b1e8ebb11d3925908f5167fccc24eb6bc37ebadabe7d44cd901e13495b551f6f2e6dcaeb768e944cd2caa1050506d692e48ebcec632231c39d6be116574529901981646728d360664448519c11373c5465970ea97b3c95817a5bfd65db21d8573063b3d0d8e4", + "secret": "dfe2071543c5a1acbfdbddc0455f41395a480a4c7f4e62a472569f1f9929064f46faea325f38482f0d2eb9de97598e83c0a3d9576656b7c7c736a7361d6d8af525908f5167fccc24eb6bc37ebadabe7d44cd901e13495b551f6f2e6dcaeb768e5374f3c981548e28d306223e879267939386b2e37eb09e66ea04bd431bede4b1192d351aed62f8ff9ee586d3502c697ae3c3134e0b6d3632e5a30627d821bfc960664448519c11373c5465970ea97b3c95817a5bfd65db21d8573063b3d0d8e4" + }, + { + "public": "b4d295091b9d4bd029eee3edabd198b8dc793429925773a57bfbe3ee81b5551dd7fb55500482a4a7db730f757b7768ad7509a008acbd451c3b98a8bc384350a752c66dcc1b98c8aa95f588d5ed4639a8163533d74f84e2a82a3c4fd57f90a4d8b80588594e165c146408d06c37c3fa5dc2f1b4fd0a7795223948c79633afbf8d", + "secret": "300b6f3d6e53843545ecc2b7826ac1f143c177701572370415d268cd0c10ab9144a47a7552db0b47c2974a8bc4d15ca7b191c4304dd1ac77f9c3a9ab3599bd28d7fb55500482a4a7db730f757b7768ad7509a008acbd451c3b98a8bc384350a7ea800dd666444a2525134edacdd71ab6155e7c12af9fdfac91c6be1e8c15c0ad7b987f40d1e228fc5ebd7ae9f6b535fc0b55bc68b0f7d9f28d50855f2a5dee3ab80588594e165c146408d06c37c3fa5dc2f1b4fd0a7795223948c79633afbf8d" + }, + { + "public": "d38f66d685b0ee6d63ad14e44108d0320b001566283cdfe793e40902c07f835162d095c336af33ab2838c28be360f89534f693008cafdb2c0bb9974b2378f405ff1df8cc3ae7df46135b15b80fa9b7800ee006e58479fe253ada693c2a5f98ba5ee2f8d99c83bb352f186bd117b133d8c0e70768f140b78cf6cc0521ec83182c", + "secret": "a985ef8341937af1c2feb2cfadee68da9bab6f0e5499d315862caf36817b8d71d80d25344b57c64b74f23d34fdb217d9af200de0334b19452b2917a99d5e296962d095c336af33ab2838c28be360f89534f693008cafdb2c0bb9974b2378f405af01ad2870584ef3f38e7aa41890a2e3d8d1e56771e90e213e1871c09bdef274299dd2db37b1b01abe4b0966303aa6c981fea9d9dc3de1689a6db9ab2c81c8c95ee2f8d99c83bb352f186bd117b133d8c0e70768f140b78cf6cc0521ec83182c" + }, + { + "public": "6ebd76e8b5c21ef51244190c0e14312a1dcf785ad3a036f58eb189f37e1efc0ca453206633556f17ff0e3cdec032b60879b70883404a50e2a44c581a7ebe2fd510629018c62d9a903115b0e603b7c6ebd88004720d9cca2b08ecde6079b150762841b6bd2c990c8367b351aad42faf3b0e8e94bb2ec1b139d937fba0722537b4", + "secret": "fe68a0b9d2445f759b8c653fdc96fde6ad00f4a20b3d0e605cdd733e62d0d7f39c0b5c72ebe6c91ae5b369bcb93325a92171be7d197210e8cf84e801f8ab9a1aa453206633556f17ff0e3cdec032b60879b70883404a50e2a44c581a7ebe2fd5343537e941cd9fbfb5b66b4459ca4b7f19deecc3f507745accc69ff0983af199304d59ba6a73f462ad855355c4f91e02aeb97843acebebfddc7b2172d50952ff2841b6bd2c990c8367b351aad42faf3b0e8e94bb2ec1b139d937fba0722537b4" + }, + { + "public": "cb10518a41a13d4c469853c952c15b71243d6fa7dc838cce91320f07902f2315e9472272e18e28697f8476bc3cfb20ef490476d215b5831ab6e99bf3405b9f7ee6dc31d81db98aa6d3cd3f9574efc1b1d6fca0a8d96dda511cdace7bb4af2e909a230b3a3a971aebc1112bdbbba48c80b2fc93f7eb914e97ce04a1aee8b1bf93", + "secret": "af857a89ae0974dea48bce3eadf641f624279ff3b66147c098f70a7f61d75be0d85991923464264f8036f1efd5f4855aab76808ebd8ce7bf31fb58d467a04d65e9472272e18e28697f8476bc3cfb20ef490476d215b5831ab6e99bf3405b9f7e55f1ac1f2f3376756fac7ea333a651a4b1c21f3d2d0767b84774b45b1a4fa2e82791861af211a5838129155527b260e0717a5ac9b61b4d6da23a268bc88f23a79a230b3a3a971aebc1112bdbbba48c80b2fc93f7eb914e97ce04a1aee8b1bf93" + }, + { + "public": "12e688837265c8a96cd73dae41e128250c899996cb513b9a1bba13de42f79d3e8669bb6919d1e6ccae10aadd45ad6c79faedd70bd6170eef5f6a4068befac2c4d7853f5ecb9e58580a86bb8442d494c43fc6cb8f9501d6f65a69ad8c0cce15bc51fc738683b86a8e773c7021d4ed0908cd21da6cbb432d4adb046832f23839d6", + "secret": "d73a5517a7d6c826c99592ce2fabc3eb6e86f1f3f3d3c465bb10428cfcebe7c0407621315655061cb566db70057363be17717bf1a4f23f2f57e462ae0465ea5f8669bb6919d1e6ccae10aadd45ad6c79faedd70bd6170eef5f6a4068befac2c4e0360009dc29272ec88b446f062f38e48fe9ad7a40022bc609fcdd9e3e3133c03f44752851c71f53186a2e5561303e5a5aba8f51a00228510c14a6203359e80551fc738683b86a8e773c7021d4ed0908cd21da6cbb432d4adb046832f23839d6" + }, + { + "public": "04d86563409dcb1f3aa3f8467b6f3326488b1a997682b42929c2bead2a9d3154c122226ba461abf37b141e175f5c8154b0e4df33f56463c7da8bc3fd6cd56db13aa6edb3727f2eab95778eb7044a9ea788f5bf6c60d2f020355fc2f5e5beb22323fa817bdd6479fdb482bdec508da7b4f73a918ada9d2debe70b88eb88ec6ad3", + "secret": "7585e6bb2e68433488888a9eff470d2c22dfb415fd1a2b067629376046066174c09dc6e2ee083f1857e5e82cea29df2c85488609a52b3f458daa6a02a9d544f1c122226ba461abf37b141e175f5c8154b0e4df33f56463c7da8bc3fd6cd56db1f6456d23c5ab3fa28a13cedf93d69e4e5d37498be2f26b06eafb224afc8256a486507d5a5cb4a4fea9800ce9038a867ae58d15bac79b4ddcfe91168f0d22842523fa817bdd6479fdb482bdec508da7b4f73a918ada9d2debe70b88eb88ec6ad3" + }, + { + "public": "99a552328720f88686f71f87f18a25417c7e6dce28ee17ea585cb6ddd2f92f606759859a1b95048a0025da9eb5ab9d2113632904a80723e4de0f71381d4b6602dcdf2c8b4339b96390983dc5ae322dffc5decad8c46889562a04681534985e87473db3fef2b8dc1b716028f8bce2eed1ba40177e3119d61e7ae821520e5db1d0", + "secret": "54c7e3fdfaa47875fb163044dea6a3e86be92bd3b405654dcea3d009cccb4ef47aa8332069caf0c2559d18a75e6dc9064bc55fe0e5c6120b710f3bf91ab093356759859a1b95048a0025da9eb5ab9d2113632904a80723e4de0f71381d4b66020df55a4c8f76d5b0af859cb086bdfb741ebbffc9d96314c8a4d7b1d246b818d644187a30021cefc805514964b5a9cd6db32e2c8c0dc534b3af53187d727f2f8d473db3fef2b8dc1b716028f8bce2eed1ba40177e3119d61e7ae821520e5db1d0" + }, + { + "public": "9e0deb59e505aba4cd431a69999dc4ae45c94e3a01f908bad82d25dd71d8635c9e9b4d92fc79035b4bbf4d817306d553eddb74e56091b0de2aafc23c349d552a787c5061dde1b5e1d41ba7490118c7010c330f2f5d2af1d15ab096ace65e35f77a30ca969cdf4dd92c7c39ed58283084e8b9a752a44a69dfdd516022565bb25c", + "secret": "a073975aa0d1d8a677a87e20c8f3ba3864a0b2979ab7a43eee4edec1e41788fe4f06e04c4362d717feeb26496e676ec54b1436322ae9d6ec15917045be6e1f949e9b4d92fc79035b4bbf4d817306d553eddb74e56091b0de2aafc23c349d552a772946567f217206f694b5225fb29a35b2850e8ddddd12e3d870825b0464bd40b9a2172b709df8f93c56183846e4696d88a9ac5be52528cc4e200ecc34bfb2227a30ca969cdf4dd92c7c39ed58283084e8b9a752a44a69dfdd516022565bb25c" + }, + { + "public": "d43c8f7193b6a71762edca5eeb4e711c8ac9926b9089188efd1d675ef53667e3c9e689968c90bdeff8e1ffd58d47b856a8a809549947feb104e63be4f9c8f701ee7aaf549f8db6ed3cc594371b06ab0f5b2e8da3a041ca5b8cc2de8f16e4de523fc93bb33c0c9c433ac76732f6bd7e184084ebaaeeab0b5ad28d9d6e12bf5f1a", + "secret": "4f51cf2ec6cf6664e2857e3885fa87e6ebf45bd04b32118a39a753fe372e958db358ac35ad12205432b3a1b83882f9da118f278cf86807991a7b786f7152dc0ac9e689968c90bdeff8e1ffd58d47b856a8a809549947feb104e63be4f9c8f701d2bd5b985e6cb1e566a6013bd19b4b2f08fdfcaf50e171c4c9c921a75ac123dd65dbbf54e751944e0623b6f4a7fe86c8d4e480e044f43eb38364e6002cdb0ea43fc93bb33c0c9c433ac76732f6bd7e184084ebaaeeab0b5ad28d9d6e12bf5f1a" + }, + { + "public": "ccb3c1079e9f7b517d29c27f560801e2007fdf2b8a35cced0375de52ac1dde9819272b1f84ae4879614b64239bbbb7760925d14a347954d3284ace475328e7e023ead8857a5e1627a9bf1651e5da35f426a6d16b4775805abb409d8c7e6de35979f52dbee37675af7dffa13faa4491a23cf3dd474fa928f00c598d8690353625", + "secret": "3b6bb2d508c988f61b1b743cea6969c9c1f23ceeba9aedd2617851d9ad20787e60dee214d99ab5fd11fce637ab11bdc11b56329c7e73d5deb4dcb092ddaf0fea19272b1f84ae4879614b64239bbbb7760925d14a347954d3284ace475328e7e01f2b9e032bda94020508164c01fb24a4714bda1f1546c53ebd1c49d0fcf059ee26c9951fb5fc997b7f45935bbe56d0744ce7282fc6aa464069b05c87c666a09d79f52dbee37675af7dffa13faa4491a23cf3dd474fa928f00c598d8690353625" + }, + { + "public": "10a0939a69ba567bb5a99b41a69848ba826bf9db5aaff49de0dd8c46df6ed0c15f743c2756c6e4b870292eb8011bfb576c7f99636579555509db058c802735ebd446b850fa02eb3c580ef81c6a0d7492d8a508eee684b717bb0f98aacd07fd848f94c908957ccf1255c5c3281f691ef607c7d3b0682da72631ff634cad6a4d54", + "secret": "f49945321a84e6025bb39376e214831466f39fe18c215d839a7f49b52659ee48a359c309f03f0a53195cdcf7753a25777c5d0d060ce1cd06c33e89184818554c5f743c2756c6e4b870292eb8011bfb576c7f99636579555509db058c802735eb6d97701bd5b688375a9c5243f9eaec990a784ec3959b3305f2c11fe25658c746c91677a8d0e45595dd739b0cef8aa45c01a72d7cbd093dbc43cc2648a119a01a8f94c908957ccf1255c5c3281f691ef607c7d3b0682da72631ff634cad6a4d54" + }, + { + "public": "7a6fd379a0facfd1581c9804247d3a2edbce73baa45366e3c88d9d86a657d764d6ce47eebee056e75bafb0299b71fb702198970f4ee143bb712948ea5ed4131f7c39edf24a9f68d35783df2e4a9044e84aadf93a989b08feb0dc70c91a7d8002d4059f2d0a6e66ad84cdcc5489f2da0f1b4d7a965ed8b0b34129f21443ae3172", + "secret": "e5a8ae97aa4879a3d846babe662188abfdc92c5aab2fc1856c051e22d8d1ead264e28dfbbce68470488f7ea4659fa65cec295bd08529e4c549ed01ed36ba4357d6ce47eebee056e75bafb0299b71fb702198970f4ee143bb712948ea5ed4131f43f5c5455f7826c4ed76c4a45b95cb598c267f72acda4ba1cb26d1cbf30095a93a9040c45d9cd1e161d1da530b6702c4720d026417e8e63ca592911fe44c4207d4059f2d0a6e66ad84cdcc5489f2da0f1b4d7a965ed8b0b34129f21443ae3172" + }, + { + "public": "3e5e138ac9c37ff7cc718f0788a677ba3f8d7f1924ed0479d45e0544a50c7ab4da05cb0c2751be122df757f9dc8ab4962f468a4ff7c832f821d3b702d2eca50861bc725e3ebada7e34d1f26eff2c493080fe4c7caf50bf33f9ce4fe6f9f41a1c5cc312605c324ae51b2e7c90c567e4e95bc61633b9de3360696aaca87a240704", + "secret": "b18cf246090b72a7d32960f1d8eae184553d6379b0ebfa2c437ff5cf0d59a8932d23fe138c8dd5a482de162377185d8ba73cf32d3c2b12606925d48dbde7879dda05cb0c2751be122df757f9dc8ab4962f468a4ff7c832f821d3b702d2eca508329c5c56453c63f0823abf5cb88d5ead7a1c409103407c842d23a4aaa1752d396b7cb8f7f3fa641c23845824e120cdd7780ce66e873910cde395d6e67c5b668d5cc312605c324ae51b2e7c90c567e4e95bc61633b9de3360696aaca87a240704" + }, + { + "public": "6b68a1da465ee3904bb4d360be48bf34315b64314ddd598165e1a771837d4543df048a50bbf6048deca50cf3c803d3f5a95cf37e0075538abd7b8eb8b66b13e6388e65b7a0644ac65b696e7596266acf0e7d807f3db8234b9094708b7fb9b26ebf8c33d4229e69311d1ac34f151229ea6f44f58f8c3bee4c4719446fc51000df", + "secret": "13fcbeed2fbb1eea511b7286c39b8b041204362dd27d942a7930108e969ea6b12b8c7193a9e0b1da86a4bd35ad94700debbefb46412061371fb5549047b8982adf048a50bbf6048deca50cf3c803d3f5a95cf37e0075538abd7b8eb8b66b13e618891332949e4da9a093f2a5012e784c425b237e71f77873c6d8677884f0aae467067b841ef9e060806291da428d290e1074c207b691ba76ae1dab821a120efbbf8c33d4229e69311d1ac34f151229ea6f44f58f8c3bee4c4719446fc51000df" + }, + { + "public": "a6a68b394d81dbe70aaf4e8982d6e9c009d2af4553c4893b70c51d386f77315d0d85be743d6be5de764aac34229e678a72a78cedd89d3a294f81b135d6ee6be7a223cbd8adc56d0fb5107ae493040e8997f05b23bda8a0bb38bee86d2525ceea8646a7f3c74fe1018e35d452c4171c1d74adf058bb09239432534bede5b5d7b2", + "secret": "e350767e8ae42789c0e5dde678956ac7cf0ed91bf6f018b9862e423e98aec0284eba0ec4eaf9fc2ddd93501c4681709ee183b73b0d834e2f05c92d8eae7574c40d85be743d6be5de764aac34229e678a72a78cedd89d3a294f81b135d6ee6be7479622ac6d5f06a8bccba3156cb3e36c12de65b8f8a78e44e6e84e06d7212344dbc2dce3e07510f6bd5fac3af7777d015bc6ff038c646ce284c455359800f87e8646a7f3c74fe1018e35d452c4171c1d74adf058bb09239432534bede5b5d7b2" + }, + { + "public": "2563b2a2bef4ac7e393aa5942738868515f9778faa58b28c3e227e3b239b1c43f091c0e22599b746ae17e19a9f7989cebfdf9b3f58a1a592ef992bf6dba366a88aa8d632e684f66048a1d5952f42ec7989495be1937727dd1b8f327103495b2c92fb9207da4c03f2f632564125709672de2d4c94c5b72a5f6694fddf7eeb6eac", + "secret": "5d540c71ad4a8abb5799abf1c467db2ce4a738b611a2c0c1e2be025a7b1171d945e8504e90446a6ac9c8a012fbdd84520458f7dca1e82ebb415664d7e9b08f88f091c0e22599b746ae17e19a9f7989cebfdf9b3f58a1a592ef992bf6dba366a8565d240f5e36c2cb26c731940c22eb7bd274d22f9b96deb7e753f745b266240b8ba05619d4562464f08d7c46b2e9fa327bf7b0977c5bf18bc1d0818b86a3230792fb9207da4c03f2f632564125709672de2d4c94c5b72a5f6694fddf7eeb6eac" + }, + { + "public": "803116bce228a882f65a75a7ab56b95383c9970752cdd84e8d28f7963d83e67a60db0fff3ff97e64f3c392566bbbdba3bf80491bb24e444a0034a245d6f9351165c1fa7aada55ed3b1a29efe1c44c86030bfcf2bbbfa9911867b97c2ad1d078152574dc329afc4ee7b8e8583025af712146ff04935b4993c70fe58a6d6b64ad5", + "secret": "a009da1b430b7e3310ecdba614940434fbd15705d209d610655d4a9036726555a03dbf42ef9db8fb9dc753990a29aa1c6179c4c09bb54a4ea8774845ffecca7d60db0fff3ff97e64f3c392566bbbdba3bf80491bb24e444a0034a245d6f93511763aa871604fb5bd5d327e031a999685a13629ec966c950355703cf07de7ba4d88ad5f43d4982be3e94c7da278aaa6455ed7c233b0d3fd75652909ac3600703552574dc329afc4ee7b8e8583025af712146ff04935b4993c70fe58a6d6b64ad5" + }, + { + "public": "f04196493328783711722a23a01ff7c3395c2a2eaca5f89d3c970349d7c4ec52811e329cd87294ce563a08f3f5e0f0c9fd29061a60d68ec27b7ab854cf7aa59fa422a103f1be6b615caa0b5993da1bf4c76144abde1bfc4cb916c3c2cc1afd9f3567d3ffa2dc978c336315c7a9816ac63d77738fbbeccdcc42e7accbd77d4155", + "secret": "b2ed5db2062facb13ef141a06d85c13d484b5880afdbb88e73fef53481522e2ab9605f52714b90312d6413612f6d40556adc2c68f0c83b4e600dc01b9087d3d9811e329cd87294ce563a08f3f5e0f0c9fd29061a60d68ec27b7ab854cf7aa59f7ed0d06ae4278402df6d7bc04a2cb044c4c271cc2fbdfe4ceee98ea109ab5591c520e9a8c58450691d752d27cf2ccf7c56379647228d9d6e5b38ba0cf861f8603567d3ffa2dc978c336315c7a9816ac63d77738fbbeccdcc42e7accbd77d4155" + }, + { + "public": "ff36aadb162a7402afbfebab44f6c7497565de3a76a1d127a1fa90607b96b62a831964891adb7e17301c261ac84b4d100fa3f2bb8f028e354c09a1ca172279e6ba4d05eead74287996a8ed71cbea760e7dcd85cbb7a962749302c60dd1f12c9d21c1fe92fea22817e8ccd0a1f84517d82a3643716a8c45a3b2f95668e31f53ea", + "secret": "9f04e76f4f0bbe11455b113aed96542e3b7bc9c61b14e8b875f5890e2932d13097cf11120c521258c4c172b1d2d6013ccbffd41e29a1f1461c326db9e053bdc9831964891adb7e17301c261ac84b4d100fa3f2bb8f028e354c09a1ca172279e61b2b8e2cf6e2a37bf0768bde5242c19d8855f0e5ae73a7f58d9a2559c3ad05b76578da8c96ad496751b0156bc06d9e91c8fd409610ebdc372d99797e06f485a821c1fe92fea22817e8ccd0a1f84517d82a3643716a8c45a3b2f95668e31f53ea" + }, + { + "public": "21e3bece4d5705c187ff39cd537f3bfa57122973a79cf7615b7dc4c6e2dfd7f150df7d53e2ab0f8e9331cfc9f1ca211011e9bb02f8b082e4a70e45e1adc7d543b3f629814460ad7bbb7a1845e48c70349639f6bb647b66fa9d9548326f8092a7501fc18dbcfc8730b4e57249ea5bb92312309d36b9300a1a105dc2fb1c8b4879", + "secret": "39d71144c0500b9543fe9696fe07cfcdcde4178a11907ab24c929f6b88d4647fd515331edaf90e23b00d45d5a085adaf96760424818846cb2b6362c46aae873250df7d53e2ab0f8e9331cfc9f1ca211011e9bb02f8b082e4a70e45e1adc7d54396c6821f9fab777d195f6dcce99ab1278441044f632bef5829d815cfa3698f86526884c2741ef3cb8483eaa7245eed8182d5248288b96e3ccd6b70e8a1c86b46501fc18dbcfc8730b4e57249ea5bb92312309d36b9300a1a105dc2fb1c8b4879" + }, + { + "public": "baf10c4fb4aa517edc0af73671e5c8b7d232cfdf5529ae3be0ac866274c3f61a059f3df5793ae6dec2560d33ae19e2df1e5b3ab1c1aa4a6f42ee217e9483ec436184e5e63e0548e15d32e089e831fdd1465c4c9256f456f9325debfb05e37f24f209dc889e7173c5c310210290f22dd688d2c395ab7ee1a93078500dbc28121d", + "secret": "2a7d0347b612451d11fd101e18746456e6658eb5bc59b85f2a20699e953527af6a90c2712b36527f211578e22859e54aa12f305ca14faff19028ae208f29ac34059f3df5793ae6dec2560d33ae19e2df1e5b3ab1c1aa4a6f42ee217e9483ec43ae4172f80291f0391ef00826d8e1e549ad83645840c060700c5e2dd8c7eaedfb676f8171db0088afbba8b67586a8ee859e38d19d1fe4d7015ee0aedc9f0e35a2f209dc889e7173c5c310210290f22dd688d2c395ab7ee1a93078500dbc28121d" + }, + { + "public": "71da7322bd1392f93721d7499c02ceb1f64fbbeee1792dd2c831f3d486c58308d8ef1cbcb1c2983dcbc7ddfd96a79a6867937a3170cdf3ec74408940173b7ba06d62260077f772af15f7090fa43d3cb697b09a0b8f8abf1b419c0acc60736424e4df3910fcc3f6b6418de8d6291828bcf03aab18f05163660e8fd18668b9ef6e", + "secret": "9f0440ee8cfde94a3d4788fb908a2da4616b79cdd9e5e0051223d07b0d5bed236ddfb99c2c684adb190a1c81698c19c7c77ca41b9cd889859edf679ae6c12860d8ef1cbcb1c2983dcbc7ddfd96a79a6867937a3170cdf3ec74408940173b7ba05ee0a585b0555cd3d588e144ea0e124667b96b37ded127f001f09c8244534d3b20a6c2da29b9c2d06aeaaa59b2bcb45d82048349931ef5ed5a2a4516bdb4da53e4df3910fcc3f6b6418de8d6291828bcf03aab18f05163660e8fd18668b9ef6e" + }, + { + "public": "f228e252e684e97d4ea81343456a0a02cd78ae761f6290866d335ed5d5457446c5fcaaebd059de11817b6049daaabf557637525d6831654f4d2e461c38ff4db83511a310ef62d97da5e6e477fea919d84537fd9cc39ae3eb0609914e11427a0f386003b6633a1138f7ef4ff9f344ac7774942da5175602e949240b822a6efa75", + "secret": "52d5c01f22357005bc4761fe436f4f7c451b39f4b3ae9bc824350e3a61de45d9acf1a9c30fc7e01ab57bcaeb5758b1bf41f15aa70a9a6501da83054692515627c5fcaaebd059de11817b6049daaabf557637525d6831654f4d2e461c38ff4db8f9f222790d3908a630e227f803e5d111fc891d45e0fb77a98d001aff67a9f100099f1001de7f40dc6a0d695dad1712e2b94301d768e52b84bed4d8bae7f62eda386003b6633a1138f7ef4ff9f344ac7774942da5175602e949240b822a6efa75" + }, + { + "public": "e38ee8603b4e6ae6314049470301dda05ab1c05f30a88076493e786e2cea0ec35d9de2be305beed10b502b801e89419fc19e25256598e6372dd29e6019dec5e86e675d3d8b1d58100f97474546c3ce282771cc4c07eb3c8b738cccd32d879c844bb7203370a849e6434b4b090794d3994e8edbd7cf1799aff53cbc7f2b58a602", + "secret": "18cefa7a98c6e49be1110df10515366c76fe6e1fba80253da26faab7e7f4273966129046dbdac4c140651973e6c3caf40981d0d13fe10fad44d8ede9faac1f095d9de2be305beed10b502b801e89419fc19e25256598e6372dd29e6019dec5e89dfcdfc92daba79849a7c406a81003aa07b6b94aed4a288a63941d8c62adb1a93672ced60679000256b1278ea4fb023b3353420aac136442af101a76de2f33924bb7203370a849e6434b4b090794d3994e8edbd7cf1799aff53cbc7f2b58a602" + }, + { + "public": "35b291d6aa229ee5d5358f1075ee8ac68ea2201c9e77590a942a35666d573051ac72a54703d96763827d37f47b83da9a406070ce5dc9c9e5bc651e270deddf8f0d5a01b232671f555f5c410d52af0a774d73e71e36404601a5eac03f473bb2c9deecec980be496b499fadff1c6808952ba9fbe87bbf25c23a78c1ceafdc82b26", + "secret": "133a621ece1151c9b9ec5fc662a2b3076251ffc325b7dd6a738168af196f5bd787995c1f6db1b84408a14c9f25e7ab5021b1e45db33be1273d7254dcfb887355ac72a54703d96763827d37f47b83da9a406070ce5dc9c9e5bc651e270deddf8ff1d1b9152a0ee256d8f5a4bbfd026a8e7534240d27825e30a9a3f66daf154d40e5700ca0535da1cbce2b18e42a55fb0095a0e75042a10db3b0592eb04b8c1178deecec980be496b499fadff1c6808952ba9fbe87bbf25c23a78c1ceafdc82b26" + }, + { + "public": "f64febf30c656b0945e4a66cd8cd413190fa5f9418fed8469c07233f2a0767a254428e40e4d55e2c8d88e342bd9acebd4e658f4c9c156006f6ed37dbec1a4485a4fa8ca1e4f0e20b8e2c88d2afd4c57f46080c5a68fda4b50a1d250c5c638c9b4bf481aa9b409b035c7ffa989508a94a2e3e39191e149cdf6a088eb1f73a8a4b", + "secret": "f024678625195822f9ed29a297966f524d835ce05ccd89a446cf9d0db88b4d847af24828b7164b94a68a65912499f99b97fc25e6b3d8cfbaf6aa91c04cee886654428e40e4d55e2c8d88e342bd9acebd4e658f4c9c156006f6ed37dbec1a44854c80bbd9b8855baa38e6292f9e8cd7dd359de18bc259af30eca91b975761922a4e06d6697b67b538aa27d4ca162147d19280da74c53520d211fb9960ba1d8c1b4bf481aa9b409b035c7ffa989508a94a2e3e39191e149cdf6a088eb1f73a8a4b" + }, + { + "public": "1ecc6a6346596e4f438c427522800aef2d9e1dbeb45d2e6badc4e7405368fc3b967e2832dbee0e7eb4821c832eaa52cb6694df9dfd1f1d76b247f932accecb64ba2c70918bf5c28b2279fd352c99f06d4fa0baee5c8ae24c039c269b026ede8f690f825eb41e64e172e48ffb4d240b99131c66fd006e82887307960141071d96", + "secret": "a40cbbaa5f8fcf792b2367db42f402102d0121acaf5d9b41d41b5cfdc69b70f3d400b37dd71bb3ff2ac801ece7d976255412c9afe970fce5b29702fc6ae803c7967e2832dbee0e7eb4821c832eaa52cb6694df9dfd1f1d76b247f932accecb64e8da44ca110f7254a5165c32fde562c65e3e6b2c37182d5f73c998e50a72ac9ab95bc6ab1f4f95089fa1b5378d13525da0514cad7aa108a18ec5d43108524997690f825eb41e64e172e48ffb4d240b99131c66fd006e82887307960141071d96" + }, + { + "public": "f26f48ad621060b321054e29346f795e0d39aff92156f766d59b284b1f0921674d4ca35f8a9fe27aa467debbb4a55caac2788f3ef2a0976aaa44154dfe59478da1e4517a67b480269eb893f50632e727be8a59e9b66c490693cc67407ec1be269a08915eafdfc66e05a26aab7a1799a3a3befb53020babc061aa210bebbd93ff", + "secret": "d928ed03046ad36cc57f82cf98d1faff717e568f81172b4672d4936264be2e40f170c6875de3397f5cf428d96a07da192a3a23f724b609b787644e05954d07594d4ca35f8a9fe27aa467debbb4a55caac2788f3ef2a0976aaa44154dfe59478dbcc7fbd035fa6a69310f935abc7780ea29f4f420f3be7c919f62080679e2d21a6d294273c5c2033a9c70163cc3cc3295c0e9c648e5a2b2162a4ff16666abf9079a08915eafdfc66e05a26aab7a1799a3a3befb53020babc061aa210bebbd93ff" + }, + { + "public": "1e4ec591f7003feab7f374b024054a20d073e1ee710e7c1b638f7ec588bcfa8c81eac23411e4281414ae1ae0f44bfc3c7a33a4d2f6fb5bde2f6b6c188442f91a1f3f781d526b475ae61a52ca2d95c42bccaf949b4d008925a8e1ca81fa517c6caf19bbcb4bfb5b0048f69af5c44dcd7f4fb1fd0b5935ce89f79ac4cb629a822e", + "secret": "441515479e88b62b302f97dbaa17cddff02a16ae2b148731dd67cdbe093f7e9d13575a75416fffcaa66287f3a6827659a8b9972ec761c6fca44cb67a8869b16481eac23411e4281414ae1ae0f44bfc3c7a33a4d2f6fb5bde2f6b6c188442f91a020e3af6523ee81bc1695e9198e5120c5ae99a8581b845af2100797f3a5cefb808d0e25066d6e4d1b4caf7b420694635b616641d0887fd826d1f99c5546573e1af19bbcb4bfb5b0048f69af5c44dcd7f4fb1fd0b5935ce89f79ac4cb629a822e" + }, + { + "public": "a03d1a6a4ee241c5ab392f05b0763db30416e6a6a99b754b426ca9c33b770a10fcdf88547464ea80da78ea0a76aea607395b26b51fbd2d048a995ac5b86a97877100f78e80e5d1c03f471449debb75a32374b676da34830e776fe2d8ba27f700c16bef347ef56c0998663919fd8a8e3f89376288fdb1dcb0e1f92a56073965c1", + "secret": "adaa9b5ee978daee505544ad70fef1da78ec67961fa10b19947b4b33ae89fcfa1cc70189586b30f8d46b27cea5c59662ee5035563cf7eaaa2b04b4bcf21d8da0fcdf88547464ea80da78ea0a76aea607395b26b51fbd2d048a995ac5b86a9787145af2506f73969c7a56e6a43b5b714ca6d0ac832217d61439b891baf0ceae4209a922d3d30eef4edc0b695adb738b713eca583dcd5890a006857b451e3762e3c16bef347ef56c0998663919fd8a8e3f89376288fdb1dcb0e1f92a56073965c1" + }, + { + "public": "c6e217eebb6159f8eb1ce7fcc672015315a241f30e01b8c847ecb726adae2c30de503a1dcb08a176cea5bdf36e5da986aa9e298e8c1e91737935f5bfe4b72f61c6b820801988103f0c0cffad736fcee76e859138126131d3c6bbfa7f240ccbe404d5412324e3ac2853f0bc49b11f36dd7d97e1c8a3112a7f4defc9146afb791b", + "secret": "1aa4a860a3d8e7f603445f29a0d9e92686241ea211159f71c0bc8d2077097e6bfff002aa8a39ff0e1bd554ed06ea0008c0ff0439ed946fd72f78ca890832fa7fde503a1dcb08a176cea5bdf36e5da986aa9e298e8c1e91737935f5bfe4b72f612391bde8ca0d0f9e86cd420f41c4fced1b75bd149c637e3877766ec0ae4cd18d6a7a7d88e10811fe6f2d1a15103865cf086122a0ed4290934eee4b4a206dce2c04d5412324e3ac2853f0bc49b11f36dd7d97e1c8a3112a7f4defc9146afb791b" + }, + { + "public": "1b373257f3fc76997167bd727449432e93bf1af9813e7e0c5ff1142f35107e07ec89c28e19684511b964f2027bafd362d038ff78bedc487b37c70e87d67cdfa4ec2ecc17a0b1f4b6273723482933897f4bac1576ca11612b35de4e3191253f51b5d2c68f1a871e29e39b41bca7173daa8472de7c69967c3039d95ea94995cab1", + "secret": "77b2679f6b381e97cd9ac05a11c8cc238ffee12ae3b0572d46d5a4d4c20e6aa2df931959a61196d4e4218e79c9852e754254831770be8531cae92f962eb45683ec89c28e19684511b964f2027bafd362d038ff78bedc487b37c70e87d67cdfa45fde1d659b0f60722c16508283fbc6503b96ee008970d127cfee6d1238eab262f815d8630ca3a3990695c541b23ed90341b42b1e227a39434062968b0dcad83eb5d2c68f1a871e29e39b41bca7173daa8472de7c69967c3039d95ea94995cab1" + }, + { + "public": "21ca77ebe9d0a91e3f40cec4b8819b9acdb1456ebf4f24c9589b85f754b6361c1045e968034f0766d3cb11bba0b75c8919223fe75e02b57e365a67f0de3991cb44b2095fedfa1d9fb3b854f7043231b2e7552905cdf0b306564ce92769ba12a4d5b2d9a655019006f8cc3c352c007bd2c9aae107447145c5d6f5e1ed3ac8f163", + "secret": "c32f5a6f5e57fbfd8a8a66090c6abfe5da0406c7442cedb34618b76455c428a2495b7fcc26d6733b09487a671558f974cdecc84f29afb53f2420493753bbe0341045e968034f0766d3cb11bba0b75c8919223fe75e02b57e365a67f0de3991cb09471d34c93d57308b4d492e9de38dfcdeb7285393bc8f007189057b1afc8de6e7c0ba657b3f62612d450289d0af2f5c3af699327b6f081ad4bddf1b3feae479d5b2d9a655019006f8cc3c352c007bd2c9aae107447145c5d6f5e1ed3ac8f163" + }, + { + "public": "8b23bad9384976de850cd2ba2cec29f918b75e30512ee8e626b38611b7c43bad625ded0ae4fd9d83b1d100a25b17c5f46dcf9e1a41b9a11bb572174daacd70aa903387d8ef9db4e7e82f95eddec209f44e0b2f9b07563a5bfb75be4c0f8284f179c7a29ef1b13606650d8ecb5644488e2877e0abda678cf70b0ba25b47e18b5b", + "secret": "f652c932339c9fcf723c64a4475bd6e4fbc21464b8a0e1de611d47c68596b8c717629d5894ac8d0da7b79b0167cfe015f271118d46029c992e4d7255c2559bf4625ded0ae4fd9d83b1d100a25b17c5f46dcf9e1a41b9a11bb572174daacd70aa1a680ef15a8f74533d7556a18dd65c6516c20a2cb62f13e9dbca5c35bcdaf247550a58599a7be9d6196614006c7287b77c58b4791edf1ece4b651728476c6ad179c7a29ef1b13606650d8ecb5644488e2877e0abda678cf70b0ba25b47e18b5b" + }, + { + "public": "48af843c6bb121684f6d8a1bf30782a4c63ecf00f88ff28ea0ec3388e7a87822a0b8c58d99f0cb27c98250f9d8d74f4efe3300390af072c7c94ab0ffeba93783e8ea1483e48f179f9b420db26605ce94431d37e1184f899439ec9315e753985b50b3c7da116fd30c634e12f20d11f16b1e1a13cdc8f380f7b95ad7d20cab1278", + "secret": "33f70458e79c7693232afbd56970efacf6e1df042b8ef2c76c7ef3471dea1be0598b9e04eb6f7beed13b9f2129c3002c7d827f5e5077898fb67efcb88f84780ea0b8c58d99f0cb27c98250f9d8d74f4efe3300390af072c7c94ab0ffeba93783db5fb7a405e03a07f64a775e52df801d23814de9e5e9fd98092c56920d61f5ed96705761ced6ae34fcab6a2279e25d7a1a6af3944db8a47572713941c3c3437150b3c7da116fd30c634e12f20d11f16b1e1a13cdc8f380f7b95ad7d20cab1278" + }, + { + "public": "571452e50043af12446a09098ab7fa009afc20b1ad5f66772e35bff3ab297fdff4408c83c6436031a426ef6eae6fc43854418a9199fd6ee76ef9e2ba2af7464d6b8494a33e22c4d3df121d45dff213e4e277ef438475e51e28e52a148506e1c2228889f5ae2cc55a52d13c4e956e8464699eeb4a263195256eab9c4dd2889ea7", + "secret": "02453700785c9d8d8b3d3e817d79f514225ccccc5b5b38c15a81cf0eb1ec35eb4e439cbe23f7732f44999b9e752a94571d2cb8fcb0297dfe785dcbe52eb4296bf4408c83c6436031a426ef6eae6fc43854418a9199fd6ee76ef9e2ba2af7464df11ad241f3f7f45bcbe404c9837cf41239f1b348d17933aca2d496f2303c512f79c382e97e456b673406ce5202c09069ddc4388005883fa71488ee6120ce53cb228889f5ae2cc55a52d13c4e956e8464699eeb4a263195256eab9c4dd2889ea7" + }, + { + "public": "381d83c0527c079e90a7e4c51831c0c55c614d8310df3c6553d5584975f69e9e6aeedaa35954a442f3b27a05296582eccc71c9012bcfe10f19e2adf429e7d5b8215a71b9908e8f4cc76dd01621ee867ff9582030a8176e5dc4945d2727502be531e278861d30fb35c788b52d90db73a4a6b2a98a84f9ece6d566faa46d34dd36", + "secret": "dc2d2cc455a93f516b74c137875a0062f2bc6b459bb3abe67ee8ec70569e3b926c149b274447d95a42a6e7459a5404ee8f876791e03f027e96e63afd5b385fa56aeedaa35954a442f3b27a05296582eccc71c9012bcfe10f19e2adf429e7d5b8cb0d01113da272b3456909b76b1f5e11a00c296ae3a4e9e096d3b38c35ee2f3e42d33a553086adc327b95c17549ea1ab8f5c5bfbecc3e6bf930b382352cfb30531e278861d30fb35c788b52d90db73a4a6b2a98a84f9ece6d566faa46d34dd36" + }, + { + "public": "e5fe3d6bb2970f09972a2adc43aafd83750123effead4610dd941adf181c9c83aefe40a5c8b494aa01ae23a0caa40618841f486242dd0dfbc9d11a8809603996fb62a9778cf4b11571e4b10c798f2de56a629c32d20bb283bbe20968e4bbe504462e8121e56b8ef7de7f068fe82b852acd149e4e3a9502d804ca52108616b83e", + "secret": "d111c1f1d6bb95e18b4567cd9e8e89a4e9352af1fb5a91a00c8558f4cebe714afd86086638d8cde0167d19cad69c838011b74abbb50accba30603668b7218ed1aefe40a5c8b494aa01ae23a0caa40618841f486242dd0dfbc9d11a8809603996785a3f4641e17f357e3092ce2fc1b2f21b77b4ba2bd3e04a5164b8c64ca3f8fd7bdae5f81c5a6ea65d9271efa5e4dce845a125e0efeb8f9aac9b71650ee31d36462e8121e56b8ef7de7f068fe82b852acd149e4e3a9502d804ca52108616b83e" + }, + { + "public": "c7fd81fe3b2fb592a2df59ad90e6bbc35023f9574be8fb9fd85f6a6230d5ec95166fa29a38531a011764ca998e731afcca93b8387ccd3a34175bca956d26c7c919e883fa11363340535ae347f43886068288a81c79f07926dc06764e4dfe966fb57b8d776e27a52c51b89f206cc7dced846a0721907c6e4b5d32f3cee4647a78", + "secret": "aff01b985bfce8dfe0d5b34aa0f76c2632a8dfacac1888f8638a647c10dde1760567e20bab411caa8dfd0be5910027013ecc294c481a8557a288e55b4be9e452166fa29a38531a011764ca998e731afcca93b8387ccd3a34175bca956d26c7c9d643b4680241a2b7ae5d1447619ad04eee826c770698d4d514699418fcdacc96023cc310b152bad374dbaab9794197618d448f7e32655b9621a4cfecbc452d92b57b8d776e27a52c51b89f206cc7dced846a0721907c6e4b5d32f3cee4647a78" + }, + { + "public": "11b7e285f7c3b649434180882123d6c7580beb0e28aee185188adfe389e89a391dbdd1e10fb94bec55b461bca947ba00a3d2f5057c684222b568b6270f68b626b78b94e1610fa7311f8e0079d034d5c9f1fb5ff5b60b4245ceb36616520174ec3d6e324b462db8f5ac97fab3c14c16f0b9415ee1b518d344420239e4fa521a59", + "secret": "05b58fcd30e6adfca19e09d6793e5e85852da3ee03aee8144f33b05777957d566be366ea0d117c48545bbcf47e3c18b593b86e3dba98d9f425ca882fb48963b01dbdd1e10fb94bec55b461bca947ba00a3d2f5057c684222b568b6270f68b6266dabe85760b9017c874dbeaa2b23a2f140133499b07f5557d8c06c5ad9bdc553fa5b2bfb747f517d5391be171f42de59bfd02273f0a76052c597d4fa1f04b67a3d6e324b462db8f5ac97fab3c14c16f0b9415ee1b518d344420239e4fa521a59" + }, + { + "public": "f133feb6e1db15ed4cc89dcba8f73f9c7f956e13d07406eb96b1598f7cb613e1511e7e6f6014f68c5f7fa4fcae45c93ac4ee246e037660500ee0aa216261761586f2fdd25a094126d3c489d65eb15d1921abf7249b3850a2b30cdfb00157a1bd15aae3e2fa8a8e0d00312a95a41834161a7f3462b62bfec19766dd0e735d413e", + "secret": "50ac5169235e3044d457757e3f75cf448263e153ea046322a5e5d5614be02b8373d28fec8286e2a473ae4fb024470fd73b7ff2d0863bcf93753c8fecea7da94e511e7e6f6014f68c5f7fa4fcae45c93ac4ee246e037660500ee0aa21626176159aaa015c4d67d60b08d50efba795d82832582814783c8f5e6251b7264f6ecbc3a5cfffaa0e4be626d5d98d70f6810106cbcb7223c1b58f8afdffd82112ec80af15aae3e2fa8a8e0d00312a95a41834161a7f3462b62bfec19766dd0e735d413e" + }, + { + "public": "81925a0d416195599a6123cd31198951038b7b12882055362f8dd8ba4210de1ba2595878f57efe63afd06c62e816f1480b0ce04e1a9957f2692409d4b05c9626ba05204cd09e90e34b8b042345b1b06cdccb2f125d7cde0517d9c3a1ea470cdd2d07f36fa6bc0a0f47d3d652c815927d97aad285c276112195e24b06f7a4bef2", + "secret": "ba8510769bf4ccd538b4fb8864daf1a1fc0b02fa7717b43f3bc122535ce60faf556d768d1a3214be52d0b03f105f6302389c55ab5d6c331c4407415bf494b4aba2595878f57efe63afd06c62e816f1480b0ce04e1a9957f2692409d4b05c962618e3a10e5cbc11dd383ee32be95423a07358edcf98e1e3e113225582fa0b68bf1aa540e8ecf8a06efd076c11cb72d8450e7b2d88a86c146ea2b5acd741cdfe682d07f36fa6bc0a0f47d3d652c815927d97aad285c276112195e24b06f7a4bef2" + }, + { + "public": "b495e69c86331c46ea46a0a21b97aad933f9399e73dd794284e5074899328d07bb52077b0b3145f954a959edfdf5359c8b4d90dafd77cfc59f7910237de5ad889503e7a0f8723179840237b439124688741a3d38730845eb180f62d6deca8dba351171079b97001c2296137e7943008fa83f576e8d85fec836f6d1eaeaaa982f", + "secret": "ab3b797d235faf3c9ff5686ac65ff0e8d85c11bd8c05c0d88a64c21b6ee3264e90e43031f4ae235e4fee93189655e4670489f11c77cbab1bd32753cceb647133bb52077b0b3145f954a959edfdf5359c8b4d90dafd77cfc59f7910237de5ad88016e9e41e21c5efad887ead459feb367a3a0e280b63aa75f4caa2de28c45907337bfd8cc609fd0a1d75fb4b5d51bae30606da8b8dfa3e0fdea3338cfd125ab81351171079b97001c2296137e7943008fa83f576e8d85fec836f6d1eaeaaa982f" + }, + { + "public": "d6c6d8f975950aad82cb0bbcd8bba5193b510e3a6af37f559c98eb666d3416283944182f665d6f83999bcedf9f94ae7bb599bcabfc3237217d3e5d0153e52ccc343973ac2d6f24873a3a76d839ca98726ab088fba860d18c1c22bceaf08fc69764ca7e74e68bb147663983f35bf29382d42a71298a6e393208d57f99f22c76f6", + "secret": "235f3d580282cccd29e582b44ded94a89a52d5d686d345f2d3424b69e2a170c8b9f5966c9ee69e221e06a3021eaf6908311abb83408fda36a65dfe223db813a03944182f665d6f83999bcedf9f94ae7bb599bcabfc3237217d3e5d0153e52cccb5602cc45f6fe314aced5bce704d070a117f2fad75f8b9995d3030d357974c39031d0acdcffccd8506c91910d3eecf4360c7d5885c78dcb9cdffb9e4f431eb1264ca7e74e68bb147663983f35bf29382d42a71298a6e393208d57f99f22c76f6" + }, + { + "public": "6149f72b82566c2fdd48f2e9177604a9098b60b20c3b3e5a364d10f733611aa8bdaf12f9fc885141215441c701588814a565590b384b361c097bd0d8880dd045bc23537a35f85c8a5355c9a55c691424738809d2830f4a9bf624b06cb6d75cbc1fdc8547dee693072d4ee95c7a756f8c309c02e9c75a407a86d674d463f4ebb1", + "secret": "7c1068c543b22e66a00b25b3b04e28811bd95352ae314793d3b5535764d8024ce6b75fa1748a12e1b576b7a45bc075523929b5806d2c5566186e02b4d512a34fbdaf12f9fc885141215441c701588814a565590b384b361c097bd0d8880dd045b4af0fe36422fd029b88b9baede3aae363d9467ebb931c03fb38093909d39a37ef2499ff421a50ad1e52556e2f20a96eef0cab8d8bc8c0ca8f17e86ad0f26c3c1fdc8547dee693072d4ee95c7a756f8c309c02e9c75a407a86d674d463f4ebb1" + }, + { + "public": "9c30062855d4593fadc327e951dbf833a7b00ea4da381343f3977a05d7877478a932b905d761f2ecd21caf2c4bb246db49342bcfd201e6dfce93c5c0b90c38b659c9747a8ad70b5f81a16b264cba9a786c21a012e431ce8f700bcce1fe21dcc4b343c813501bdbc0202c9fcd6e9a4644475acef905c86cf7e06c8ea59503d216", + "secret": "3c27465f0d1506e805ceeb8426b6d072b4c88f243e43036d26b03bc4a2043f80f4b057edb63e695642ed067aa08f0effd2fd35a3ccea9cacb3778b6a480883a7a932b905d761f2ecd21caf2c4bb246db49342bcfd201e6dfce93c5c0b90c38b669b379629fd2fc76fc485710f4cf2ddde3b289b8f334e50939c525a8b38f735cd32763e4d44daf9dc361652e4e86363ee3f8644bdd872ad57761527d98f57c20b343c813501bdbc0202c9fcd6e9a4644475acef905c86cf7e06c8ea59503d216" + }, + { + "public": "0c5f5d35ac7add164f3d2f40f796041e664bd51bc43dc6a2722bdb909c8917a13bcfd5f6ce089b87cafa45074dfbe84aeed15275742e30a1ab4febdc1fed33e19316feb8421c0cd3d2df88d2ca119d785aab5dc8151069f4a14c5675334dcbebfcd69464d3bb52a03221a0e91528e2ae7bc9e57a7214949e28b8d50876c72674", + "secret": "a581e0ce91d58b87aa1c6af9045016d5dd191fc7c24d21d9a349ac594856daa7929e5b0bace9010566943502b4e6b687f2f8f00340faea1afc92f9cb29decbe23bcfd5f6ce089b87cafa45074dfbe84aeed15275742e30a1ab4febdc1fed33e1515dc8c7f8cad64dce4fd2ddadc1a2ed948ba5b4d9924e7381ce33ddfba3cc7703cc38ad93a03cdc412d263ceb7c015b66c35a5c1e77eced7b74a52400e68498fcd69464d3bb52a03221a0e91528e2ae7bc9e57a7214949e28b8d50876c72674" + }, + { + "public": "55c45d813c01e442ce4eb32160fd215163a9e2922f48b296b80d633ce6497e75d7acdc78b088a68c005e68ae769222598ee14cd9491a911867b900cd5d0aff8b96bda3b2c0e88b2d552ef79472856b569f5a85aedb8c7ce23ab7514c8cb9b3b5b4cfc38eac2b55442709e9e74d49e77b67f4881425bcb99c7c0b650c3717869d", + "secret": "87e533f4bf8e5e56c12553e9c7c46f39b9eb9f343fdc268a25864789ec2f192f6e1aa3248be74fef811d0320743958d9b7b31e77fc1b8df41e78d1012a742677d7acdc78b088a68c005e68ae769222598ee14cd9491a911867b900cd5d0aff8b3a7c2e41aa98e4988c911fa47c96e9775b3a3ab1a5db44ea4b4340d3a619ec1b62414a40fb996187b70b645375d4ecfd37048e490d62000cf3927d0478e982b2b4cfc38eac2b55442709e9e74d49e77b67f4881425bcb99c7c0b650c3717869d" + }, + { + "public": "88a1f742132460917ba9d0de19f9295e77069e943d550fa5ffa57fd16b18c1e4395dfd57bc17b9dc06a1dbf6f4c1c16efc053ec7315b8d6bf43e16e70b5cac087e692a7375d762ebaa745928f02d5d1998318b05ab97d8333e65838d39280047d457c0ff9d4ea4a2856f7802d1455dc9fb23ad327d4fe0eaca5b6927cf1810f8", + "secret": "f8a7d73548184228df32dc2feffa80acb9033d0f66e7f0dee1331360265891e77eefb4c61da09a57c9454d430658fd617443be27c9c800664d1c6cbd24f492f3395dfd57bc17b9dc06a1dbf6f4c1c16efc053ec7315b8d6bf43e16e70b5cac08cab71fff809abd7725082c23e322c6c4a12aca38b087a9f980d1c766fec6fe8392bb773dba7769d60d3019005d4426031d2a15d57dec710fdb2cde6198b2f7fbd457c0ff9d4ea4a2856f7802d1455dc9fb23ad327d4fe0eaca5b6927cf1810f8" + }, + { + "public": "e5207a57729aac39d6267d9d91eef6a79936c49240ef83d1d6ce3e0de1d50448255a7e0f93f3ee688b001beb5c256e78c626bd8d88fc8d3c585cc3486556fca0406aba3ce2443919f1b71ae3ad9aec52158d20af41080b2b75fcfa9522ff2fbd38288f79e123558d68f6e3310cd70fa54d3754a88ba9c1e5f03eb376ddb79abd", + "secret": "bd66f4737497f954ab1ad19646f16ba6d4a05e299a3f39c030e5f367ebfa21b00068c0542f1f64c8aeb5a63302db87e4b59fb017d8fc08d6d8b84469ae0e2e63255a7e0f93f3ee688b001beb5c256e78c626bd8d88fc8d3c585cc3486556fca0d22852ee0ca55191520193cb94c3657651126b3589fcb42a3b7829a7184f3defe11b241a7df4ea18d7da4ed37066ba1e5e0f2844c875afdaad6ff087ced27c4538288f79e123558d68f6e3310cd70fa54d3754a88ba9c1e5f03eb376ddb79abd" + }, + { + "public": "801245cf79e93bc485379973107b9f474240e068c25fdbc8b24b1e2a7180bff77cd91e71532faf9f7610366e3818069052bcd0b692d29a690abbc06ad7178cdcacf68728938c699b07a776373a3cf197fcfb29956b51b8d401ea24c6f801c42ff6ab5098c32ec126958f3bacba07173ea19194254992dd6cb6e1e8328dc0ca2c", + "secret": "33e9da4401c1e3d7ffb1749b637b790e019bb3be897e2d4bb62d9ada4805cbca624566422e15cc21186813c3c20b2b756b3511a743964bd0811d2c159ba63fbb7cd91e71532faf9f7610366e3818069052bcd0b692d29a690abbc06ad7178cdc3b18a5fb81150e533d51cc2c3ab28fc323acd10a7b91bb3286a622b2150ff451d0f4aded65b2e1a2ff2007b93cbc338bceab9d6148c636e701866fb3b9affe78f6ab5098c32ec126958f3bacba07173ea19194254992dd6cb6e1e8328dc0ca2c" + }, + { + "public": "4758d50ce92d95d94c8e848ac0f825d96ace1462e596cb1a3e0259ea3271057defc21bc0cb245f0e4c8c65033ecfd05f1aaa0979aa283dba2a432ed3b80bd21172780dc515d8f36c341d0ff096ef5488dc90554c597e2c3c09bd5351813424b5327a8f3d9f2822d52ecb236e6aa2280877b1d2d473be862f00b55a4a53b399b5", + "secret": "bbb9a8e1de572adec63cf0f444e3185794eb6b01cc4df8bd2dd78e49141b796b28602a0313816053f41395d42af33de0fbd0019f18c632bdf07972fc7acb4a16efc21bc0cb245f0e4c8c65033ecfd05f1aaa0979aa283dba2a432ed3b80bd211ffa96a51f7a88441e1f96b1a79ae298ad272156f9be4cee317f36de4aa0eac1c9ae33554d03c2db8c075269a7c3c3232630f42cc5d9be06dcffb2b9de28e4d5e327a8f3d9f2822d52ecb236e6aa2280877b1d2d473be862f00b55a4a53b399b5" + }, + { + "public": "69c9bf46662b91fb5f812fb25816d538198260270d8c4abda4cdfe5d8f019fb1eab4a236847cbb8cd0c5c95d5bbd3c52193494cd0574c3fc8800e9d2ff1ebbb29cf06181b2d533dc7f63c4f464fb39f2725f781e5acac6cb420e77a493e3bc40cfb38a36b678faa1899b46693606f0676414bd3676daecc1ea80593d4b5d4b2c", + "secret": "4cd6a5c9c53ccad1de97241ad642eddad8f07ba12c8210c7fb75d0539511c0d449d013258256d6e3791d1649b96ba0591466eeb331878fbfd7440124a4b85223eab4a236847cbb8cd0c5c95d5bbd3c52193494cd0574c3fc8800e9d2ff1ebbb2074fcc3c5b93f2b0f402acaa14cad9fd0d1968f3eedaf0308f5604e451d698318353f1e0b5a26400a549e9a596fc3d4f55f2b5f6333f834cff017c5b72b17468cfb38a36b678faa1899b46693606f0676414bd3676daecc1ea80593d4b5d4b2c" + }, + { + "public": "4e7622665c1e827d2deb282fdb3a6a66c5a02bf68bf40f9eb2bc6fba7e8029b2040519f35dbc7ac0cfb70fa86f3348d41f9b94ea46c8620d162db7cef122f9bea160208b3454e05095b3711acde133b31c273aa5fa79a0efa17af2e8aea1ad59b070ca352094b51e5b46f921dec7f97a400298c1134cb01821b370810d5fd49b", + "secret": "0ebf0cf1bdff05fbdfec635a0038fd4a0d9eb198b33455cc9c5dc9d9e5f9faeed64cf14e6c0031ff9653991b988355106259349e1f1ae1621fc5f35d04f6ab44040519f35dbc7ac0cfb70fa86f3348d41f9b94ea46c8620d162db7cef122f9be5fed2c554322e89e006c407da0051ddf6c91dcd27abc8532b01cff3272707fb9e55f814499b58be2b13b318bdee849470cd4ece7d1781e7232c7ca15e3608828b070ca352094b51e5b46f921dec7f97a400298c1134cb01821b370810d5fd49b" + }, + { + "public": "2b1e8f23bdfe152de7004054a9112fb302779be26be695e9c4ca7bec1c96fad6dcba8a9f4416f19336d36c83e6aacdc61b55c53b5c4924bc87c3fcef22a3651b5937df6df3491728555bdeff5e309fb842d1168183f3057153b4b7e3d585d20aa9c7f6ff1cc517954c9aad9e48df8fd67cd6608e5061dd057c9ce670fb0be655", + "secret": "6ba06860415b581aea456e383ecc529db159ae3750bb2f5738afa7fded839e86c6dc7963f978fc8813a245614dc8791ff4237461fa490bc57cd7281d73748eacdcba8a9f4416f19336d36c83e6aacdc61b55c53b5c4924bc87c3fcef22a3651bdce6a089232ecc81b9b13206ab958081b1836c8dc6720d7bad0fe94166c99fc6f3a13f643f9fb278d2242d28d1bf0394abe4da5e4705cf4dd98d80afdb24f6d3a9c7f6ff1cc517954c9aad9e48df8fd67cd6608e5061dd057c9ce670fb0be655" + }, + { + "public": "644dae5e2aef883886309554d07d4409d56fa4071ea55273674a35e2ee8b642b833af1cb30ca77796199d7dbf215ef0415ce04bbd2748641f16f34f2fe44ee6d1cf00d649270a0642c7e2d244b80b9057c75575efed1d04e9def375e7b2e4455d60e4b209bfb1e0af94f3676de81c78c9159c6b8f92a938ccbbd8ded99ec91b9", + "secret": "13af589641360a792f05dea2e2ee7cae6e67fb63c26eaf6e5949554d396ab63d06bc19d58c819ff1b9f13c9121485dbe8ef1373cf41781e7db3415d7a8295876833af1cb30ca77796199d7dbf215ef0415ce04bbd2748641f16f34f2fe44ee6d68f3933c05bb774859c03745fc2a8926641aa151b3244baf55768506b04cd35ecfc66a484769b5d3e2e498ca72cfea5fe89cecff32d9c97aee22209d520329fcd60e4b209bfb1e0af94f3676de81c78c9159c6b8f92a938ccbbd8ded99ec91b9" + }, + { + "public": "63c7abc2ff04ae74eba7f799c2e58523eae91248d785a44d960e4cc4dfc3f557dfdd039db3808447a901041ef08b1c1a778cf787a6b2c0d59714de3cd40bbd427778b3173396a2bf867bc937c4ee6e2083ea2850aa131f9222420531d13501f01e90c868a0e1418e11811335d3347821f4ec68705a419f4539c3057210adac15", + "secret": "fe1cccf34217dd3f1ad40989dd759ceb4c39a3c900c8262d1c5a56bbbc9c6545d8bc2a6e1152a687aea3f766d61fddf0a33a5e8c9703b242a739d61f3e3a9d40dfdd039db3808447a901041ef08b1c1a778cf787a6b2c0d59714de3cd40bbd42865b86784834b75cd87f3d438dab6960d689085f3a022dce7922e6493eca70ffdaf2344fb09f04cb387da74d1ace8d5783f4effefa1e041acbd9e8f22da6582c1e90c868a0e1418e11811335d3347821f4ec68705a419f4539c3057210adac15" + }, + { + "public": "f903fa3a1d4eb1005cf0099fb8b03b79cafb97b0133cb87b77eca900a354cf14378716f70da45149a0610c0f0510bc67394f81a9073e75911b0dff5a9f5974c616d478924e0c714a0250ecf553b567658950792446ab679d9164d7c5cb6eb0f7c64048769922c515a5864823d9c162ab691c03a564f6163b59a05c1a3f71dbfe", + "secret": "0c742ea118d32647e87bb6b8850847ac3ef85e60311169f09b8ef05d0aaf239776e31a5eff390d5b7ba065ff64f98934b28d598e7052bd36aad772c45aeaa6cb378716f70da45149a0610c0f0510bc67394f81a9073e75911b0dff5a9f5974c697a056c3ba8139cc93e351ed7e2e9083b56e0823eeae8c9c17015a25f0e1876d24c8d65963dcfbb5a9f03c7ad85633f6890cc8d2e68f5d9f94e0451576b257c6c64048769922c515a5864823d9c162ab691c03a564f6163b59a05c1a3f71dbfe" + }, + { + "public": "34f992dec97ba5c086a16d327550aff41d1b1ea8a5c71811d3d9b1510d96bbafcc72d604102c6c835c362a5b3f45f421ab6408060f7bed572ae030f478b0d80e21bbafd651e704bcb49673574cd414ff24ff34e0ccb7bcc8cd9e5471c178831bf62b4fd168bb31d474c825c31091bd640c5c0df9ee8445a87859ab74d68290e4", + "secret": "c1a006e2e4b672018b9566914a35b8e4c4844290a9a068bde0b47097b1e5e3f0459f6bed016876de2b0738f8a8e06a562bc636cbe224b126a736b098d30182cccc72d604102c6c835c362a5b3f45f421ab6408060f7bed572ae030f478b0d80efb5fddb7e4f72ff702e6d4edf4fb1632c3364d6e6f337fd8f2babc4a243ba354a36145df05d8e9c4f347ea5eb3c71d1356f254439d08927b2e33f8106afd13acf62b4fd168bb31d474c825c31091bd640c5c0df9ee8445a87859ab74d68290e4" + }, + { + "public": "9a89ca8443fe5cc964813efcca21540ac2bfa2bf05f90b9d974b971df8dd3c78d067b9941beab729f708bae1c788a636a131d8afab523577b13d6803b93905a2669570df28426ba86c084d9ff5f87c6377691f7fb592be7f4cdb56817a64899f5fc16c97ead7e24651b918ec9950316de46eec19f278063c3c93640057619120", + "secret": "1faca721681785ccd2476e795c70ab6f8a9057528f274c282d350bf665206dbbe095c365df16a707d6c2605cbae5d6a67d89980284f422fe8949cca1bd1eb2bed067b9941beab729f708bae1c788a636a131d8afab523577b13d6803b93905a2c07b0ee64948d6f17811b5ec9e9eab918d9685466da6fa4ead59abc6f7da46768c3685b4dcc434a6fb0694537617955339dfae5ff0887cf7e6c3b23653a3dc765fc16c97ead7e24651b918ec9950316de46eec19f278063c3c93640057619120" + }, + { + "public": "43e46baace1998bcc38abd07d522bfa50fd7ae830165aa4956344dbb8aea5069287ffb8af928a130d95d7116809c80463de8c2e5132b2cb28089f7ec4812f5bd00129cbc4f5dfe40f976fbf254b95cf6a80bee1932327f85a31b5cca12f11627968cd93dcdf024ca2f4b1d034bcf4a0ac4796190b65d737a7d13702f1187277c", + "secret": "70c56e8989610520d3445e731e4f9ca50658cf1b9af0100d19de6da0466d7771b4b00588af2ad63a01b502cd8b52dbbd423dbca55c0599e5473a33ad4867cc07287ffb8af928a130d95d7116809c80463de8c2e5132b2cb28089f7ec4812f5bdaadf91ed78c4586d58e5e2ef01a511d3dbc821a66569ebc4fd7855d97eebeb92e986b0da3fcfebd201baea39bc583b8656a723576772c0c2b0fc37ebcd133ff5968cd93dcdf024ca2f4b1d034bcf4a0ac4796190b65d737a7d13702f1187277c" + }, + { + "public": "33668c864030570b4f01f1629dde40c65cc38c42b9769de9c4884b423c813ce76c05583bc172f54124d6958f94dd6039efee4d25d806809bc023ad13b7e28b68139db86d76504ef07efae9e2ca6d4238b6bd1dfbbee0bf1d25a7e27c376fce9574260c8c42e06b8ab533390cd5bdee3ac0c3f3fa414f4f4131fa52789708b836", + "secret": "1dd9ff8aba14992ad38d7e77d90c228075aeceeca061def1ae4b1e4b3b0bb1daa318341604089d1f6dd54eb132cb9f55bf6b064626b11425c8fcfce68bfef6fd6c05583bc172f54124d6958f94dd6039efee4d25d806809bc023ad13b7e28b68b068aa8bc7825d71854b7489400915e8222ca88d8f29ee92e5606b9555e118ca77b08b512ce4513f92024a7ab534c14a79a99dd7559704673969909d01f7819e74260c8c42e06b8ab533390cd5bdee3ac0c3f3fa414f4f4131fa52789708b836" + }, + { + "public": "876b03ae009828a2e45a455ae6ae2002bd8033eddb9a01d0b5339737e383ea2cd7bc6a077fc312fca6844afaf0a29a8eaa4dd71a33d75c1a92f5dbc2a42315dad39542f25c2a10c8082b58827efb7758a862d4098862b92c5ea4fac1ae35a27c8bd2f4ad7dcbd18d5801b86b93cfcb62eaf18a94017635aa38c633015a9103ce", + "secret": "8d19943f3b15168ba74448870e489894cdba81d8818006beb2bb3d490e335a0603983723d9514e56fed562ae42edc7fd180944cf6cadc7c0aeb1560d719cf7d2d7bc6a077fc312fca6844afaf0a29a8eaa4dd71a33d75c1a92f5dbc2a42315da54c615ca17618b76e534114115a87f05673eb8f51998da4937628273492a86556c452605f9d9e3d54f59df3ea44fbf1b8d3abc9b2e3928912217e66adeea4a7d8bd2f4ad7dcbd18d5801b86b93cfcb62eaf18a94017635aa38c633015a9103ce" + }, + { + "public": "0e33da85fa6d4a34cfbf1f60e5e6681fe2aa2f6089d8c08f3d6d0433a2e6720fdf01e633a44b0707fdb8fb63b630b5b9636a5d71c4b5646eaf00fb04caa0297a61f1626d07fe2b63a727d6137f10ffadb449e9395563e9b004605ba0e61ecc4677b8a7c489e5f1cd801ce8966f1110a9895f9a4f08805b37f1de374d38bd929c", + "secret": "35481e62db20172e955eb0ce3abd9c9dd8387323fd940aa683bbccac97eda727f22cd798d35a5245cc03d8547c5bdb0c8987cbeefead33fb6e42b7fe99374f58df01e633a44b0707fdb8fb63b630b5b9636a5d71c4b5646eaf00fb04caa0297a1b4411de0eb4bb067e57ae6197fc0f1d40607e89bcba4a7684b5285a8e8c0f3b0924fa53a02c4e45a5224ed551656fa7598a77fb1730960318ce826accc5eb9b77b8a7c489e5f1cd801ce8966f1110a9895f9a4f08805b37f1de374d38bd929c" + }, + { + "public": "8300f563b76a9d120014a0a46ed5ed48f50288ab149738550685c15dba56acf562bdb79171ac447ca50e0e560f2589d2b5eaae1147e88c2c84caa43aade72d78d876d360dd2266dcf5cb26fb6b3e752602075615edd02270b441236fb8c1397668859511c545232460bb92cfaf3335dc28ad883b664c04e4299b747c90ccf888", + "secret": "12b339a62c8d561cd367b270da0377c689168de6b9bc1b6df799021c76aa83a18e77a1f08433628eff0eda9fab205bbaeebb55a4e1c0966fbf5ab34576fd8cd362bdb79171ac447ca50e0e560f2589d2b5eaae1147e88c2c84caa43aade72d780f1f1b627679c58eecdf9a2b092dc25d300bce3668b146c81acd4108a521ce053a96a811505c8fb47a7dba4a45cca70d5ac5e99b1da45c9b6f2a9f4b7f2e1d8f68859511c545232460bb92cfaf3335dc28ad883b664c04e4299b747c90ccf888" + }, + { + "public": "4fa61f9a41190548ba33876d4f810fd7a9f708422091544f08ce07fb7896bce74d9d0056954088e94dcd80fc0bf447b473c69f0ded8ee56f89fed9aa438fe923c056e7a9d4081b98299c70a8b8fe67aad3184adab903d414668c1ea9d46ebcdf29e238ba11453a34dca8e6d16fa359869cd572535ebb67338ab228ba85739170", + "secret": "506e0d9edb20e4be9ae5000589b551420583970af89d2672cee56ba72ae0461b6595589d1d47de1a4294441ff073499ef7505c53fdd9ff54388220db72e91e764d9d0056954088e94dcd80fc0bf447b473c69f0ded8ee56f89fed9aa438fe923dea10bd6dc7ec2d3a15f83e46ce442c3c18bf870e78bfdf9533cc4ac28769660215c09156c8d5a4e81848a8a3cefb6f69bcd34b185aa72360d1b22cfd29c3f3729e238ba11453a34dca8e6d16fa359869cd572535ebb67338ab228ba85739170" + }, + { + "public": "c8c803700357ef35f94d09c9383f8bea365b841dcdd60dbeddc935e6aca8ae687df72a85b6c3b80726a5a86228dce1c6b0fc8c4ceaa6c5afa832018cbb173957b4f276fa2a464396a39349aa5435f05ccd7d54c5267a23b230638cf6933ed5e43a24be0f17a3dad257badb5e3d0f8e6eed90993a6703cd814c0aa2beca1272b8", + "secret": "a34e37d0aa73555256117a0f83fccff14bbdbdc248a22c5447abb7211a739793712612bd2bfe6e0e786e095b7c31c2ef6b58e5d716cfa1f09a631dac0eae39827df72a85b6c3b80726a5a86228dce1c6b0fc8c4ceaa6c5afa832018cbb17395762356ae14b2139748c340051fc47417943b0784ca57f886e72e4bbac493b21c8880942b2e6c1088ca3ac366fa7cad080a278ceb24cd7cac1d31a618c4debff3b3a24be0f17a3dad257badb5e3d0f8e6eed90993a6703cd814c0aa2beca1272b8" + }, + { + "public": "0dacd549e8a0bc93c36667c51c52f1a144ad62de4b09355dd58f24fae1c54329a7d2e64196680fbf71ba86e89b036d9e4b16e3b5bf51510fb7805300783234406ae95a9426c05ef99803daefa9d26edbe23b134c882a1792e514e100fbb1eeb5aea214efb3254618bc4a9941c42c8333a981b3580bbdff04fdd6fc866313f443", + "secret": "e1e2b32109869b0842979f076df173ee5e184bc44200f2159e18ba65ace00be45ee743214ebd78e7195975031a0fd08684d7897ed8a5a5c057a26aa19f4c0106a7d2e64196680fbf71ba86e89b036d9e4b16e3b5bf51510fb78053007832344056d48b89858db4388bd3cb3151078df237c8911afebe8c7ad53b4e35fa8904d8c91db503b500309349256aa6541701cf462973f08c77b63f15e1813128efdb47aea214efb3254618bc4a9941c42c8333a981b3580bbdff04fdd6fc866313f443" + }, + { + "public": "dc2b66b1c2567be633132d0ef7244005adea9e3bfe3a2e264b6a499697d2ed734afb54a9b40fe34a613685ff4277f9cb77594fdbeed8ce71938db5bfab9dadf14b5f7e2e9f23b8f25425f093314fec83a83bd80d8b558e8e2c7423db17638f0d83f9b440d2627f053f1f5db40b267ffbdb260ab783d4c8850bb6ce0d738b9ab9", + "secret": "30b3985edd9c25a2d52156f84f0caa4608c5779ebde0805ed4ccd7c0b868803f336e69eacfd5486ceb59d8c4b1d6ddd59e2ae5ecf96cc291418bcc08033dad9b4afb54a9b40fe34a613685ff4277f9cb77594fdbeed8ce71938db5bfab9dadf1124e5a4e1bb71ba419fc618080e5c3b63a4a772a12b53283c5bac2d4ed160c3f99ca269003a5b685f08b79229bb416b9dc1c8467c9b5c5dce0565d35846bc78883f9b440d2627f053f1f5db40b267ffbdb260ab783d4c8850bb6ce0d738b9ab9" + }, + { + "public": "89e13f8b99122558c00cfaf49c33fd062b8891062a07884a20b546ad3ecfcb683f51a339a89610feb3462db412f74f39db558065a8845f8509f8a0b7072df3dc6cb4409670122cab8597d18ba4764ecc4c771811bb939fd3b3f5c844be3f829c0030507bef10f045000968d4ad2380e1f4221577e1f76f171332acf286f6cbc8", + "secret": "d594b3a327fa3008c03438aa636ca5ca916c54f91e41cc29b5218b765ef3e27a3bd26f2a1777de20ab11736138f90af8dc5a0d3143dc9200431f982a57b499313f51a339a89610feb3462db412f74f39db558065a8845f8509f8a0b7072df3dc9ed60348c08db9807fb2665a2aaba4d13cd31de597ea9be6ab2935e815c5d07581e4f51acbff161d58a9c602fd3c0e170ea616cad42258cc5ff7da733ed22f8f0030507bef10f045000968d4ad2380e1f4221577e1f76f171332acf286f6cbc8" + }, + { + "public": "0152b50586783ae703b3b7db9737b8a620c8ee025a8440c3dd3aacd646377a0277c83b9a04aafcf32fc408a0bfc9b12b4d64cd91654b85fa58ff7712c338d49fdb740817151ab48595e5a44d6b2b15e54399ac85f5eafb695b3bc5aef156bfe6ca60c7b39177ab5e8aa479d0858a863ffc373bd1515d258805f214d02896f5c7", + "secret": "4b728ec12014e930c4a0a0e0a6cd9405b593f1dce81af3921c7ee36a9b58fe15975a6935521786cb8ded5c8464cb62ac313070a9de30eb52202aa6f42b29548f77c83b9a04aafcf32fc408a0bfc9b12b4d64cd91654b85fa58ff7712c338d49f6dad0750af26c9d9f598b239571b38d3ddcfd84cd9b572404b605771119b9e4a8ed8a0ba218c4cb6585a43725516a0fa50fa54d6bfc1dc6314437498993caaf4ca60c7b39177ab5e8aa479d0858a863ffc373bd1515d258805f214d02896f5c7" + }, + { + "public": "71586987c605e9f30354a5f7c3e928bfa0ade25679b9cd0f607a24987f578aad4c43d29f62a6d4bd3f9fc9bbf93e811a3f8fc2b57bc481656b1f3a9e72820a3af3ae5bb649ee797f610d2442cb3ffd6b285107331cd8b99d5d5127f66b077b60d95492cf3b8a0cb5bf47f329c18dcd42eb3959378f6c1214071b073cd33f01dc", + "secret": "5d7a5f599b23983710fe58441d47fdb2275721db37c606dd4fa27921d26fe8d9c4987df171f15c647d5cd17dfc179d0cde6f011f4dc89c277f2b3d347c33be314c43d29f62a6d4bd3f9fc9bbf93e811a3f8fc2b57bc481656b1f3a9e72820a3abdffff2c9cf2416b736a730d00590db483b034223a3bdd39954b9223a9aa6a7daf4fba0cccef1ce3734baf2bd6b443925e55d5828a7791b408d220dc059ec985d95492cf3b8a0cb5bf47f329c18dcd42eb3959378f6c1214071b073cd33f01dc" + }, + { + "public": "e5b7d0b33ca7982fcb3ee09686f4564e98d89577f01cfeb3cda6f2db066d89c38ae8ef7214cbb1b069462ed1fcf6b05bb6c4e88f21ec47a0b3832a6bceec6f31e1bd68a797c4618b315380c3b7c84033c9dd95fb6a5d46c1dfea9b586fa06ea2fe088ac967f649ac3062929911283106d0b6e24c49786f674f6fc521bd01b59f", + "secret": "d9bcc367ec68c27414b7eb6bf3104365ad421af0c2107f642c5405c4c733451092ec8b0cf3f7ee5d22feb627a99c5801659857a18c61b7a9630693e10bbaaa398ae8ef7214cbb1b069462ed1fcf6b05bb6c4e88f21ec47a0b3832a6bceec6f31531e520fb81c308f19585142cbc512a8bec625220d98db5cdb09b2d3fa928cc21e823376a5221644f92e9a77fa3987edde523ed727258a51dcff0cb6444572edfe088ac967f649ac3062929911283106d0b6e24c49786f674f6fc521bd01b59f" + }, + { + "public": "76f251d7caf61ccdad047c3a114d867fa0ea37a3ce09dc4d12542e348ee2ee20d5e6f822f2466efc0888a4682dbd07e3c448b6376afd86129db1bade0f604c95ecc7baa3003ec9a84934f06becc18296a50c54339ce1fb36b43b8ba92441d72bb61b0d7722646a3ad421111d759a98a2cf7774593130247a10fbf6f20e5010bb", + "secret": "f1c23c04bfe0d3984d32f1555010228fb60c1f330197c0608889eac10f34e785bc98bda6f542fbfbf52af23b6fd606ed3bf650782b4298ad2e1e3b4d0d97cd24d5e6f822f2466efc0888a4682dbd07e3c448b6376afd86129db1bade0f604c9529cd6cfae7b91cbf147dd58610faa9ac5ffbf21f8f36e0937b38ff98203178c9063184ab1905d90a78e17f790cd0731717f983ff191c02fb61460926e4d11508b61b0d7722646a3ad421111d759a98a2cf7774593130247a10fbf6f20e5010bb" + }, + { + "public": "cbcc77c24ef19ea2673deb8bbf6cbe261d6ca50487fd46c4c6d25f9daf4edc09b182424b2f3da7f6c2cb83421dfc10f95ad839bb8d80281c021ce7121c12d1b793a4c735c547b68e4845ccc61365dc779239465e58d287d1176cbc392c2b75f6790fefb9ba1403e7a9ef531f1539028bc78f787e3d2870d1d04ce06c07548932", + "secret": "0a309017dcfe9f648bc242696fb8f3af67df8ff5e852e575833b84799fe8aeb84d0aa145cf14e8453013ae155ee1313c9113d3b9113743940cb9be231ee4b241b182424b2f3da7f6c2cb83421dfc10f95ad839bb8d80281c021ce7121c12d1b7a53d74a70de20b69ca3993db4d3a0bf61954663264345ada1413d1fb6a90a1b50b862eb55148c80d6f29f55ecf002e10fc6afc85125aac55431553c9edbfef54790fefb9ba1403e7a9ef531f1539028bc78f787e3d2870d1d04ce06c07548932" + }, + { + "public": "9cb4ac1709a8525b98f2e36737c1cd0521eb0d1c3dc6f7e11edfaf5aa74fbaaed381fad65e26f14c94c0ae0bb7233d512cef44c590d71761fb4200ec7ebcdfea0b1a576c281ab780af11ea5d15b6b933ed0ced25a9fc5058e7b79da8f31fa0c363e3816066db090addafbf9b547bf3b4a45e2e1f7d2360388e3262cbab0f49a9", + "secret": "3badecfb2c9ac1b26a41f80175a4a2fac659b6e66434bf6afefcdf467e30f3e5d09ba3af9852a8ae0e311ce2483c6798405ff9a2d20c21fbb5c1cc8ac6c8380fd381fad65e26f14c94c0ae0bb7233d512cef44c590d71761fb4200ec7ebcdfea45a44483938ef7b50af76604bb92bb406e0f4d50c7c4d2c35254fb66fde74dd3235646e7b89cebc5cb8a834990919fac4a1bb40e6268c0f05097a3a20ed289bb63e3816066db090addafbf9b547bf3b4a45e2e1f7d2360388e3262cbab0f49a9" + }, + { + "public": "86d51864492c3c7083197464775f48806b72f7310f01e3b7f4ef42d8d1faafc0d361bca6a1d305c7e59d629aa93ad1040dc1cb8af7613a1ce0f99a2e2a7e74faab0ffc5d8a2c2236be36c0eed1b2d06ea0019b82689a6de76a7daba0e3606d876b37e7fcf8c10388e8da9fac10a118effe4b93a8ec8bc25237d683d82f5a0e78", + "secret": "6adde60865674b4c66019f67adde018dc4aa6c23b8c576eebbbfcd7f44c00cc6e3d24a6b2e31b76d8bbcaab024b76be8d0b2ca5e7896386711d068b2e438d1dcd361bca6a1d305c7e59d629aa93ad1040dc1cb8af7613a1ce0f99a2e2a7e74fa08ee8b57ecea13f16a90c6bffd8d5643fea5c6316342823292740272f6e11dfaec5a6e8ffdc6996461c0d1a3bf300d1b7cd47eec8d37990be28d293e379eef766b37e7fcf8c10388e8da9fac10a118effe4b93a8ec8bc25237d683d82f5a0e78" + }, + { + "public": "ddd07f999001833ab723ef8705b7d73e72d7779bbe835b27e8cd7db6a351ed80d59cfcca2bfed4a9684a9eb7d2dafeac7d2a025a64b0ff8c05d2684c513826e6b4a85482065d21312308442394fc5c28ae60db7a27581fec0c8a3d9330c2a78ecb0bb3460b252d8cf2035e4ac3cf1e58559afbb7f1fbc783f5aeaa5be65bfe50", + "secret": "0e20c71375664fe12b7dd056c6e4ead0e3f98e7148f763cfc080d7e349be65dfe575f4c7fefef5aa7ddb7dc91fa0841d41ad91e54c4369e33b3ba8fd5effc157d59cfcca2bfed4a9684a9eb7d2dafeac7d2a025a64b0ff8c05d2684c513826e6d2894ad20e71b643bb5fd98378e2299648c470220171e79bd94878660ada2013b169d660a000d16851d45fc55acca4c4ca445aa907bb87b70dbbe80ee06f3ea5cb0bb3460b252d8cf2035e4ac3cf1e58559afbb7f1fbc783f5aeaa5be65bfe50" + }, + { + "public": "6fc84eccd21f7dd94e3a25ed1085853e2f12b2bc7403e3c5c01af9e5de6f9345c2b075719e3d8b6b06a1ad216614ebcd19e6fe8123952bffaa57aff896537775dbeb23c03dfbfec2bf97da17088357619a8f5900101b3d50840d105ad03cd7703daed9d935cb2fdd5d95b8380ae52d092ecfa0a630c8ed01a9575dc3a587b328", + "secret": "aa51791e2a1cdf70550851f5682b04472b16247834a11289e180ae05f128e74cbe5f8ab1fb77fb07b3f06867a87c4d00fa74c009a1c2714c96a818201f296ebac2b075719e3d8b6b06a1ad216614ebcd19e6fe8123952bffaa57aff89653777509eaccc86d9f0e922f5aa00873541c627a36db6211ac3b5f3d0876ea0d908040118155899efef3e094928e6a1d4058a15b7b30d1a93b93aa734962f64921c6193daed9d935cb2fdd5d95b8380ae52d092ecfa0a630c8ed01a9575dc3a587b328" + }, + { + "public": "5aaddecba3c01ce3c03e18dea22c7443b76596201419a3f6265f82866ea760bcabaa5a651ef34121a5e566d7b06373dd509a7bc49ac346b7ab32dbf5c617e9423a1036b5ffaef22595f468e3d3705893c9efb9de86fb1300e1794d43f509292652a3cd8a46b87ecf77e61e49e4fae80116ec6d9849022142934301c7148a8a7d", + "secret": "0dcb2f7f4c55f4a7b51acaf7f0bc8f3e3830bc1446ed9adbb3ac28c519675d526c4fd185d1a8721be0e46008c1fb0962d59a0f994f6d52bfa64319bbf07fd9d3abaa5a651ef34121a5e566d7b06373dd509a7bc49ac346b7ab32dbf5c617e942bd31e8e982466c8650030a8d30b007c1aec8b6da632f8c9449e25109d94c08d44b0b297cc0c3dc9477c63c82cd2d9bc969de816e4c6b3fe15da1484fb0ef6a4652a3cd8a46b87ecf77e61e49e4fae80116ec6d9849022142934301c7148a8a7d" + }, + { + "public": "45772a0288dac09d3a61a09eb523277252c6b503cdfa2c7e00a9df9b34119f2e49a8deca0dd6cddc8679096fddb1b0e93b31b0e74faac2a9dd57e6f03698460aa737c47c9b24c738e33d43f4c26ff0e0fafcdec5977d78e530a95176a69f6a2e151d9f855ac01755765b18fdef1944883c5c14fd62da7125af74ef30a8ad687a", + "secret": "e75f8a00f0a91374f47c87a145c05745c1386671ad1120d72a1f0b34663909a6220aab197257d29c6863441a038a796a3b3e5ebc189227af827147ca9410c54d49a8deca0dd6cddc8679096fddb1b0e93b31b0e74faac2a9dd57e6f03698460aa8eb265daca58cba6b726eaf748d345885c7e41bef2af7d46d2dc7b08c21badf5a0bead6d46a8b3671b68d9535b3698c9dd13b98c0530a949d717689ffdbdf0b151d9f855ac01755765b18fdef1944883c5c14fd62da7125af74ef30a8ad687a" + }, + { + "public": "c759993fbeaaf2d46ae638201f12435b7a6afde750145a35725460481f47a8d11b7fb030f0f3fd08c98f0afb3f421904167562008cea3ee76821302afbba61c3372e85fcecf46f690a7b9372b9e970e1972139e489447a829072a1b6e94ff86c63a1f13b18747074d17713d7d44874124b57e74fd5ab13fd26262687bc2cbb87", + "secret": "cd1f34ff8c43493cdf870af655e3dde2d53234638820311bdb0906d27870195cedd1a576841ffa5bdb85e76be113b449b08c631fe9a5ebf4838a522d09a2dcd91b7fb030f0f3fd08c98f0afb3f421904167562008cea3ee76821302afbba61c3f9eacd0cece754a84738f081c6a80ae0c2612fdac9cede9068462a85955a51ef4e0bd147f98d516306c32ea5cd6d5787cd3f135c27b5cc9f28d2e6489303260263a1f13b18747074d17713d7d44874124b57e74fd5ab13fd26262687bc2cbb87" + }, + { + "public": "844939f9313918a4b883fd9dbb82ef8c6f3a32951d0cdd9750eda3ca7005f04b9fbe8e9584b215a215bd384bdf2ab3301920bc1ba84df11f94991ffb61ec859438af08fa798cd782fb3b249ebc6780a3bedbc9cd74b0ba4050be4c3cb7f4587c26cc1f878687ce05c8c2b9c1826f3599a944239c0929f35c311c624b562dd1c3", + "secret": "6d115a832c252f85f74525fbe3fbb423066ce0114ee8694f6b3e16bf2097bd50c2697c1ab668ec35338aade1f3944d50cf7bf77ab69c50fbec6d3d390d4182679fbe8e9584b215a215bd384bdf2ab3301920bc1ba84df11f94991ffb61ec85947666369bab70764327a5ac9b94d609867f0f5d43918779e88671340b5a78bba7f3a59d95e56b769793a669a23d87461c705a681b89db5dba4ee35e0783c2437a26cc1f878687ce05c8c2b9c1826f3599a944239c0929f35c311c624b562dd1c3" + }, + { + "public": "716ee445ddc1345e718ec696b315a6aa1e66d8363349341918dd30e75a86220a773f608868d1d5edfb2370fb55e18b67a18c0d299d838c5192d74a29b32f76177c0f840bbc51a89f15e29c479de8b0b7ce339ffe9bc71274bafa6cb8bb0b827c799ea74ef03eee8a8bc95c75fd0c814612365b4a874bfa72584bb909f24475e7", + "secret": "757cbcd3b4ce65cab1443b051f948f15477aa701bab5f0fe1f9ad4e2e3fcb0b7c56dcc71149743649e3f0ec9615c44ed130454e3021172273339e0cf8f6fdd63773f608868d1d5edfb2370fb55e18b67a18c0d299d838c5192d74a29b32f76178ea7c007dca16ad747c5f413bbc890477ca485a58b7d0f7a896b915a089733dde46cdf47dfdff34db89038b43b41a09c9f4ea9414a6a457350af0db5a048e6cf799ea74ef03eee8a8bc95c75fd0c814612365b4a874bfa72584bb909f24475e7" + }, + { + "public": "833fe26cbb49c8059432a66a8909bf2a2b318740266017a06132170022f487707a66c2b2b769502ddd6f4d8eca29878cbc600f0ec7170a4714b4d3a87c816e25ec1c6660a7471689da122f93ce57af2d3dd545c5b1ea969c225119ce5d15292d14eee3b849a6a3d0b14cb06e43c2a2190c3b334385d35a2ed39b2801905eb51c", + "secret": "0965a8f47d5780c573f0ca038d7ab75354b5cea6d88d4406418103fb9e56f638e639f6bb55293cb1badd9da118d67c5a2e957b6735324601e04ef28ce3c142087a66c2b2b769502ddd6f4d8eca29878cbc600f0ec7170a4714b4d3a87c816e25b77d0b264578f8729440f8b89a9b175d667fef1ea23dc5698cc98e367ba2126cfe8f989dfb1362e533de018e996098aecd60a18f62d40167516cd48acb5f47be14eee3b849a6a3d0b14cb06e43c2a2190c3b334385d35a2ed39b2801905eb51c" + }, + { + "public": "1af75a68f3a5799c16a10bb91c62d29642f3154e061296d3d2bd6625c6233af78e81f0f71251bacd60f60dd851db139e81e3f0fde34bdd4633c6168c20a8573cf37df5e5fa6f4739e878fef17a6cf99618a752e71f6a66a5a11dc6d5203a0bb41a0b419295d679c46c74d5d98c49b195dd792a830466d68edb016f8b57c3ccb0", + "secret": "e2dde1e54e8971164264e3ae7d1e7ebc177f2e76a09468ae7cfeb6025406e4c52399c86d4d1379dfc60751701a473073128073091d9ceda7613ae6de40fc7b858e81f0f71251bacd60f60dd851db139e81e3f0fde34bdd4633c6168c20a8573c2e27162406f53503e981dd57178e0344d8d9b0950c9508d5e9c915e29f9db78bdd57440680e673b8bdfa0c67685ea955f92204dab73033f491e7554f429ad75c1a0b419295d679c46c74d5d98c49b195dd792a830466d68edb016f8b57c3ccb0" + }, + { + "public": "9fb67edff2ca3a35984024d9c5d401e50a411c9c17b00c514341bff987e1af4da18c003514b9fd5fdcae9e763de35451c718fcbd34880d367826f02a3e57d66cca66d399d12d5df14f38ff559d16f14f6b372ca1bc5d19f62c8da11c99dd7b39cf6a3e67c3f2383649738dc8359df4b1133bbdb2a4c57e69111c06b0b584d5f2", + "secret": "5d389ddf703578a6c58985a841a12a868afc8bfe65803a755b027be8eca9fa61f12bd6c24d9501b36269783b19640fa81bd107455ba5cce9097783e962061dcaa18c003514b9fd5fdcae9e763de35451c718fcbd34880d367826f02a3e57d66c6f9db08891f4ab08b87c372f8d2e8c015d7d52ffb70c0c4ce1a92aa4dd5c6b973096e562b0bee0e7368dd1c22a75fa3b2045fa59688b610558302005c54aabcfcf6a3e67c3f2383649738dc8359df4b1133bbdb2a4c57e69111c06b0b584d5f2" + }, + { + "public": "24c06a8d48601668e8bfc58f88b297985f4c3b2f9c14a6e7e7c94e9154611638207bf05f8c19474752ee3017aff110d4af3a0fe4107bf055d9695f8562fcaf4b85ab0ec381d52d460dd5e3bc9f7dd0344e4ed996de995c401fc5f2ae6323e1b4ef9bf3d2d4847d58870d19b7e24da1ee9c5b9965938d5cb80f4f662c24cc5af4", + "secret": "cd84eaf61e184df2b9adf09579c8d23516cfe97a37c9833ce57faa77c74418b77629b11d0a60107bf283c48b29406575ba78628af0d4303270c47b0c59896b1c207bf05f8c19474752ee3017aff110d4af3a0fe4107bf055d9695f8562fcaf4b74e97dcdcd56991a953291712db9b303c9c52ec541b1dca9eaaae93775247f14a084ef393de710b432f44fabbf7cfde2d81fe67e3f508a997f22503f89f90f90ef9bf3d2d4847d58870d19b7e24da1ee9c5b9965938d5cb80f4f662c24cc5af4" + }, + { + "public": "cf8a812888f6922061ac869d3f03dce721d086ae4f6837f6c12ca0b656c9e6eb5af9d15354b07e811c95ac0ccc91c48928a2f103e6c74845b4aa24f40f09894ab5e1255658c105c3e18edddb9de88854ec6190d52b14abbbc01dcccfedd2999b01e001de7299b1f54f2e8cc2a7edbfa7940be1b73ce31136f14d4dabba198273", + "secret": "9f1f26b96721e6d165e8635b6bb61e200d635a588fa76f7892c05d306b40675199ac6119e5f6b0c87c26e81356005cc6202d3a939ae9264ff997f829d7950a395af9d15354b07e811c95ac0ccc91c48928a2f103e6c74845b4aa24f40f09894af99181387d1e06f73863805958bf1edd15152ae7888e6981db7c80d129006e843fbda4b00fae22ba08bbec56e847f3c63914aeb6f1b575b39a4e751fc4b3407201e001de7299b1f54f2e8cc2a7edbfa7940be1b73ce31136f14d4dabba198273" + }, + { + "public": "df20caa4575fd9f3a2f853aa0bcd95b6aeef7ea8537b755ab4a8f78b825435beb211a6b799c5676cba13f35aa3aed7e962814c29b240b2a78470e5fbf6c2afcd9417b6d78ffa36ecd7074a0b39fafd99d77014553566c53bc471a28bf3709e4e362108a8c9e9394ef255c9e6c202008d4d9e553ce4e494c8c25cc0d9c7854725", + "secret": "4f5888439a742314fdcbdb3bdc96833be8518400a3d4e4c6010c1ac68bcd5606014a884ed0fcc713c85e347b71ff352f39aeb9a3eefbd62b2df3621d02b6c927b211a6b799c5676cba13f35aa3aed7e962814c29b240b2a78470e5fbf6c2afcd6e5cebce031610396e4056e15131d774f20a4bcf3576a7a7f8679a65fc6f3d95345f8859aedbf96d2c740fdaf4e124bda56a85f695320fcb99d6ae0443b1f501362108a8c9e9394ef255c9e6c202008d4d9e553ce4e494c8c25cc0d9c7854725" + }, + { + "public": "e70e8759c46114fda94e9170a8224995ddf6c53651931f17f02154cac30973ca7ca728de96906bb121af975afc213b609b86edb25e7ef29281de7f78262c69fa90ed8df2f7db293c523c1a895a7e67a17cece1a727d52c510646118addaff6a457007ffe08546c16a626243ebd16ded0a504bcd7d12d35203f2c29ee0abf1d9d", + "secret": "1e5a457675f555091d200294bbb1a17b3737b060c762767e7d1059655fe787b6e55bd794675f43748e5339d982364db1d342c896319a56fa39dcf081b39cfffd7ca728de96906bb121af975afc213b609b86edb25e7ef29281de7f78262c69fa938372cc9e48eaddde6da8b7b62c109eefa96ea96f53bc3cbdc07d57a28ada6612000eee2fbecd2e67baeaf41a7d5f9a66dc7355b4ab54b379d9ed35614287b957007ffe08546c16a626243ebd16ded0a504bcd7d12d35203f2c29ee0abf1d9d" + }, + { + "public": "42bd119e805433a0fa4974cee2b9e10dc6f200f2f59479ae29bedb5ca60cd9b666f61cb4de4c64a0e2016617989398852b98a06a22a0da050c8c2764d13300a4174787cbaa34beb33d2da8c229eed203d9c539947bdc3c4b095f39f96ced7e28ff3ad9eceacfd5ce4e4a0a1c1fa2ca40f914c733138d04838e66c1bf2ffd0ae8", + "secret": "aeedf4bc4ae77e4e576f66dc5cd9fee5294c657e3b621b338d659c3d26a134802efdb9ab5646c881b67b5ce38e44359fdadee58f97c851ba4a40dac71c760f3966f61cb4de4c64a0e2016617989398852b98a06a22a0da050c8c2764d13300a4f69ff9f59fd862c869439b2f38009d6ccac8d61beebca85f8c2c708040e066366affe2103bf7bbefa6045523927a919d834518f2cc1cc8510a24f994e3c7b809ff3ad9eceacfd5ce4e4a0a1c1fa2ca40f914c733138d04838e66c1bf2ffd0ae8" + }, + { + "public": "4ce4df8eb2793872c83b82d154022f73e8425b2f26c44c4fcc2637f0a90e4109124690540c8fcd211698a21737fd51a529516e3f4c3d54a817a92909845499b123e3c163383b883728f5ed98d609a0f343f58848f95cd1f3821b2005078df68ab5f706c2cf5b5c41107ed2c5c2eb74cf5726c4b7a929377b16fbea29c5c0791a", + "secret": "e6e8196532e0165ec13f40b8b9f1c4aeca4ec2f89655710f70e32e6c889ee39bfac496118396e053c5cd52391776fd3c8a5a13798057e0af789a7a542c05c038124690540c8fcd211698a21737fd51a529516e3f4c3d54a817a92909845499b1ea2c71a4eea70662c8bf46f1cba2ebdbad1bc1459f2d4409f6552b891ce5ef82d058b67b9df823c10abceaa87c3544abf2818005f4fe9653eab6843bdc7a7805b5f706c2cf5b5c41107ed2c5c2eb74cf5726c4b7a929377b16fbea29c5c0791a" + }, + { + "public": "e91933f048e997d01ef67a3f8aafc7d5c9b3ad65b665a93300ab542a3e0a39b5db93642d6eaf48478549f67a4880ec86b63bfa4bf6deb9de18712676a5eb7513a3da87963bda01bad95c411be653c538af63e9ac9c8ff64a87366674d227efc3e51363709b20dcd26bf0c308f3da47b6f20a8b9961e41933bebe68877a8be600", + "secret": "8f15fed8f777391300125d6958650c3e759d0d85fce8aa930b927961bce0bb3e5f2a0c76b10a9ef908a73d6d5d78c42018842ad23652b6b92aea44b743d926bbdb93642d6eaf48478549f67a4880ec86b63bfa4bf6deb9de18712676a5eb75138eeee3220b3094d8f40b248c6c1bdf5f66e7d7820a205182f58a302fe70dc39be27fc3be89f44907e6194e3af7ab832c5743e9f248f0eab9c99533fd65c7b590e51363709b20dcd26bf0c308f3da47b6f20a8b9961e41933bebe68877a8be600" + }, + { + "public": "84b18def2a7be6966747e6aae29dca39df40fd053ebe8bd683637bd1e172892dc6b926eca62ebadd937c8ada89763b3d40d98ac657d67db33baa093de43ff95a9f7f1b5563d6f1813ddbbffe33617875bfc1a21a1bc24ee4cb75aa3682b2df0570811f63a24931a754528b131f6022d19769b8f18a622b6bc0a220169bbac90b", + "secret": "3e9e0d029514e6fe2df5d40171631fddfd490f22825b960780e8f3b2dea5ec3db9c36ff7301574c1fdc4b8b53b21595c5e6ce1fd6355274d7aaa26ca5bb22277c6b926eca62ebadd937c8ada89763b3d40d98ac657d67db33baa093de43ff95a8e1c9be2cb236b0f17301f97f6e0ff4d60b869848bbc1e57fc5ec987db8ff0a1f2c3e79ae4534425d9cb90960174a3471a31170797beb8b80be5a0093ac5ea0870811f63a24931a754528b131f6022d19769b8f18a622b6bc0a220169bbac90b" + }, + { + "public": "a6c980dacfd44deb811ac87788dea22217d211c81ce226b0912f73df22101718ec882db7e2cf67e02e426afbef4cab2917c83759924170027537a2d384228c94142f719c0722a5e245df79d562c302b7a13cf63ef348123a41ecf1eab2d77f6901f9950454ae73ba4b22774c53b9f7352127de1792fb0454994ffc953c0e357c", + "secret": "144599ceeaecca70d53b89b15d2855d82c93d980453727e49cb6c801ca228fbd6affa91de7236844d6080afffd61599d049a77022778d81eeab20625ad7bab4aec882db7e2cf67e02e426afbef4cab2917c83759924170027537a2d384228c9478ea98da91d9d145b378ba88effed2796c223c7868debce5c1d27586630e8f33dd8f9d431c3c12a75e153db14fefd1ece67dd9e8ec94844cd571f8739e034a2f01f9950454ae73ba4b22774c53b9f7352127de1792fb0454994ffc953c0e357c" + }, + { + "public": "8944af814dd87d1c32277571d7a46b822597269abc563bf62a5835b2f34658fc5a8c43708a4f53c27981a40b0040117fdcfd644e773d3a65db3f58618b25220fdf555d62cf6bb6cbb6dff78408026a67f0817f42c01f02116c2c10cab18b2e45d802a3b5d1354c090e5b20cfc80d8d40892268d277c5059e0d04e600bdbf37d0", + "secret": "5d053f7a876fbd2d0e13430bc4a88b13c2e04e9e6157952d36599e41c75d8d1122867a4d86d53ca21925f85fd062cc120165c934f032bb0dce550ebc722f52ae5a8c43708a4f53c27981a40b0040117fdcfd644e773d3a65db3f58618b25220f2ab3866a9a34bd7033f7e152d05c1d4be63f1344e5ea88a4ccdcda09c89e3a8a4c094df855a69af56f56e4c435c5708bdf328b8018b7178e309af07ac6125a1cd802a3b5d1354c090e5b20cfc80d8d40892268d277c5059e0d04e600bdbf37d0" + }, + { + "public": "f7247c6f34497e3d34cf77f251bb9ad14d636cc5ff4d577b72a6987834f4a85ae0357fd1f0ea8ca6c2c75ab662a74ec8771943106f7743c31ea0e28b4c973ea7c0d9d7f9bc2c1ddda5d0dced09fdd68859771168d06e21375b42140124a7d1d69a0d2c6f233cc86d9f9ed31c9bcac00936cadd62453ea65f083ed65ed0b52a9a", + "secret": "60f04b921aecdaf7aab738f068b519b3a9af0bc9f45328d7bbd781c3f5df77b540be4f61d5a128e34ec62626413c9fffe7aba415dbca5ea81b61727ef2407e98e0357fd1f0ea8ca6c2c75ab662a74ec8771943106f7743c31ea0e28b4c973ea7581fea29f04ac029a67522f421435ec0cf5c37fec088e00c2ba3742afff5294a61df3ad71459d2189acc33d21f703ff1b374e081ff792bef74c5bc481c7a82c89a0d2c6f233cc86d9f9ed31c9bcac00936cadd62453ea65f083ed65ed0b52a9a" + }, + { + "public": "5dd6d231db5c0da05012031015a6491656d2f23c3b8a0e5b4fb78af58e555f1a78d7b73010a4a1fbb33077d4c87cb8332365eb9ac3ce9f696f3adc42419a4c8807caeb56ae68e5c9703ec07132946741bf819029a8883a6917225db9e7847a246ca3f4027745a3e790f5cf5a21c1650a68be15d8d5a478114ea7082f69f67612", + "secret": "8ac7a957b3f2b0c44ef470c3f2cb56018be012ecf5a4e6fb94f0ea38c3cd695a9868cfab572d843ae3f886ce0f45b9150fce4ce13499ca42c8772821a24cef5878d7b73010a4a1fbb33077d4c87cb8332365eb9ac3ce9f696f3adc42419a4c88771dff551b57908959e10c8a90e7dcf7ad138cae6f0ea20d905648755e3218eaecd77c5c5033236fcafc00ccc7148c0f1d34812e0e68dc76cfcb8c5a6a8cc0c16ca3f4027745a3e790f5cf5a21c1650a68be15d8d5a478114ea7082f69f67612" + }, + { + "public": "ba3278fd51e1d771d0b352d6a94171111a745f6a4990ab571f00fb886cab9cbe8de4ef33a1f695c67999013101bebc5f2bcf3628d8530a410e758242b3ef3685b4cf627536f523ccebda770763dc005ea7fae7bea5e32d0d66e0e0c6dc6ec046c08762355260a8d8618697b5c846c05b7317ea68514b348bc739874ddf16e2fd", + "secret": "49b910776bc79dee73ba91fce8a06092c07d715c5c238cbdc50bc45118eb6d2f34c8bd5472fc1ccc8db955016757ed3098d1f3f3b2d98c80644f1210cc08dd178de4ef33a1f695c67999013101bebc5f2bcf3628d8530a410e758242b3ef368525cef43e317d2364d5d91a23af317b96411ad9c416c4af6b7d43125b77e7387dde35ba8883835a5ff8427c21a9403f6bf7deeeaaade70bd7a515633eb6f197eec08762355260a8d8618697b5c846c05b7317ea68514b348bc739874ddf16e2fd" + }, + { + "public": "77b9083cf6a3fb41c52f7fe684f8b88137236531117cac0775f88edf5111f8c4ab60d7b1357a66915fe85471f3b4cdef9189b30f0eba882395d5b239250597503a88654103c4d036549a22c0c22d5c419de2a100ceaf60f2ceceaf124dc4a05d34e01857a82f0ad230a2089e69132ce0efbefb68a6adac494fdc21fcb460527b", + "secret": "2b55dd46260788dd4a5b3b8688ae29841378fa46936cedcca5bd4449dea758d03dbff4d2a52bbfcab4da4437be2c34f9df9b7f2d2f7df05f4cc6a3fca889814eab60d7b1357a66915fe85471f3b4cdef9189b30f0eba882395d5b23925059750e87fd87f753c59edc4f608152130f5ef53ed86c9ccbd488f6fe886358bafa4ee297f8cfa9acab126542c2b9bfb2bcdd0735b74d307f4183aedbf274f4e8eecd834e01857a82f0ad230a2089e69132ce0efbefb68a6adac494fdc21fcb460527b" + }, + { + "public": "523ff71f40808cd2413a0f97e2253fa1cb1a81b732fe966aa9cf4547a1698657ef847e3388d9aa0daa63f380a2df0db7238c772c629fcce998babb7cfcf9a9300d5d02998890992043540ff00894731a75f6ca423400616b0ae37524ba0f8fb304b390cab0ab71a4cd0800d1883cae6cf685484f211175b2de1507a722e5ba42", + "secret": "248ecf80c53509120750eb550cba9741a75c4a59ca90a4a54ffe644c437a0298d8acfeab9a61a6f193cc0dba457deb9ac83fa74a7d1b83ec2ce89ac9b856cb67ef847e3388d9aa0daa63f380a2df0db7238c772c629fcce998babb7cfcf9a930cda7c6f26f842c94e78fc25683b41938da947d7c7376cbd8d2a47ed670b82138d6c324d1c457fcfcd37ffadeb51ea814cc005111692879a0ceb982b8da3e6fc904b390cab0ab71a4cd0800d1883cae6cf685484f211175b2de1507a722e5ba42" + }, + { + "public": "6b69538dcd7f0d860428809dcc236ca7e926955bb110b76a246028ea323f4bbe8305d737a2c7d6d31c15df9aca2ccf3318c441f3c38d031680f9e1e9f330fad0c34846af9392cb1273f14202c9b43350e509a8b3d93d0b100f7824beb49216e345f8bf447cb927f3aa71e647252b0be1caaf7ddbd53c55d6570781ab5b6b734b", + "secret": "a07786da330ce3af5277ed7df85b2ad2f0f0aeb572d967727bc0e4203c9ead6190b500b8657a619af5d7078fb1247ebf08a8af3a26d6ab31a67d262076753b308305d737a2c7d6d31c15df9aca2ccf3318c441f3c38d031680f9e1e9f330fad0bdd5bd834a29eeefeb6cd0710aa33f1c84c658e739a68c515b02d249ab621dc69725e2c88cf7ef5c44ef29a5064c526e2adc09a1f8c9519b74eee581b5f628f345f8bf447cb927f3aa71e647252b0be1caaf7ddbd53c55d6570781ab5b6b734b" + }, + { + "public": "ac7e1bbd2678499390661235348beccab2a727f1c13821b0ad9b017a97248f34255b93bded6516b30d8ee05b8f7d74bb28be2ea836602ee828d51455340fc5797df3f3bc7b1012e9b32aca86b632b02dc6d414bf24f11e11658c4ea00ec0b3a0cb36fa41a62039ddaf95f5f0834670d4aaf70054e9ef1b9f77ccfeae23f95ee2", + "secret": "cc5f877036f507111e99b59d05c837f6fb90afb3e9eb7461e7dd2ea3a8ce3bbd89a54bfe285957fc541e5d9b2477cebf9fa243c491276c5f733018f07eee12ed255b93bded6516b30d8ee05b8f7d74bb28be2ea836602ee828d51455340fc5798f3823fbed59128ec4e657c1045e5ee9716a2716694c4f5321b11eecd8ea214c381f3838d1cc8db9707d5287536b77212dd89b60bd26f9858f510647896b40bdcb36fa41a62039ddaf95f5f0834670d4aaf70054e9ef1b9f77ccfeae23f95ee2" + }, + { + "public": "f05ef3d5507f81804e7ec32ad1e8e2b050bd5421d1c0a9ea5465ae4ab6a1a2a9238aa256ad2f81439feb3dab55296bbfb164aae2888031b783fa30dea167b43e255a8b2bb7f51d6c0f6990ce2c60f16b7f50a82ffff064fda7d9a5a228fe8f1abc9b5dfdefb44188a32c892be615a0e469a7a9827d29dd9e8e5b466b90181c67", + "secret": "812f1bf6b1b59fd3a300b0e7a612fc2fdd3af69d95e6727b98903e23a9d194ad1d37f173933d6ac386f8b634015c589306da8579829a922ce17821191579b061238aa256ad2f81439feb3dab55296bbfb164aae2888031b783fa30dea167b43e347b6e8e6f9e11ae0cb4551d1c89f894e0afefd2fa229c51511d94eebbf7c76d25f87daf739c882f1dddff8688da7e7535075a747ffddb2994be0e621bb6d172bc9b5dfdefb44188a32c892be615a0e469a7a9827d29dd9e8e5b466b90181c67" + }, + { + "public": "4c8074c3d5d0dc11a0b04edcb77dc1d030e6d83e074ce553f98d835f0d37f40bf4b35ae379988de0bc718392f0c6a314fe087189bd5062deb92d98137ba3731b786d406c5216c220d26471bad59161316e4313376fe68e9ba3d3bde09de70cd6b19341419cf9173ab7f79030e67cb0a3e0723f4f7b21784c3a5ac790a838af73", + "secret": "c188fca3332811db818d001dfcf62b30bfdf071fed434650e9b7cd73b9bf5202ecd5087da2027830fcfffffc1b0418098337fa10877d436cb9032b793e289116f4b35ae379988de0bc718392f0c6a314fe087189bd5062deb92d98137ba3731b28d93797ed9a144421f7dac634ede58619090d96470e974c8f1756f69a4f24fc10de079756438d4652390f6ccbbd70a90b3981fc3b28ccdc4f14f648966c9b4cb19341419cf9173ab7f79030e67cb0a3e0723f4f7b21784c3a5ac790a838af73" + }, + { + "public": "e7aa2af5889fc0fddea1f432263bd4549e1c235778c6b89a4a31b365d3cacbc671b02788204345100d5a938fa0682849a215cce6641c7ad7cb1305586d628264f0c0e69984a3d8d89522b8da4bb8e23e0caf4ec329bc8bd1cffd4d64a5ac5dd084969231b810ab0866cecc94d93bb8cd4447d62dce3d28c98e0b9daf25f218e6", + "secret": "36b23e9801c047e2726c4b6662eb1e98a607b549b19cddb5fd03a812b822bf1e9432b8f8563707d6755d0af92db0cbf1b29f36226a04d57fa54b5a950544469371b02788204345100d5a938fa0682849a215cce6641c7ad7cb1305586d62826480df4ed63a130414545bfdd6175f72c5fd01187eb7c5cbe51e4a812cb438441def797400cb7b069b327a43a7dcc91fc9c960eea7670b983f4fc8ffc4836d41f084969231b810ab0866cecc94d93bb8cd4447d62dce3d28c98e0b9daf25f218e6" + }, + { + "public": "9ef075b540f0d912babb4bded94a263c02b28bf47123526ddb48d3df5cc782d0061f86228a0f0dfd61b7d675206c3b38f334d661362eb2b7097726c22645d12eae28465873d3ce8e231a79d5ad368af48f3524d236a2b962a706aaee2c192275f2c4d9e2d5272335d58226eabb70f1e14216c4fa860b6ebec76f3b2efde36b3e", + "secret": "cc6ac382946ea60932a2329d302bf853471e2278a2c29240dcbd76bdd45d3c59e7651b83138e1d8f4c820dbb7097d4e81ec92fcade7363114ac4e133bd0428f2061f86228a0f0dfd61b7d675206c3b38f334d661362eb2b7097726c22645d12e40cf7fbea891df8ba1e7222ba744d0f9782e234ef5f4e8b86ca139c6a2e256ed992b938ed098f1143f21977a5a053c3ab3230197dcc0868ad49b55bc30524d0af2c4d9e2d5272335d58226eabb70f1e14216c4fa860b6ebec76f3b2efde36b3e" + }, + { + "public": "c7a964c96b33151d0609713b2faaa436f3934d29bac1762c5e6af7b8e3fbcf508f335c1f54cbef0d14645c226cf1704130efd56f328f48d2287d49cf30d2965fec7de2204660a3ee726db0f907f2f3e31da3c6ba00388782e0ce5c48ba4a01f45ee0176bfee1b5b88ad7f9a4558f3166eacbc621a21dcc29be06ad8124814c16", + "secret": "eb15c4c1a3c2d51ac237a4be2babe70eb9a40de9322fbf2030851a748757558613f7a35d861889f176a4c3778cc35efa0dd52ed2190f26dada3e26e09054bf1e8f335c1f54cbef0d14645c226cf1704130efd56f328f48d2287d49cf30d2965f842de026a36c270edf74e43ffd0d3e02db140f88885aa32b56291f45e84ff2c5322b7a21aa42a8017dd08eb3b92da8980273c29a4cc121b069e3c8bfcc7256565ee0176bfee1b5b88ad7f9a4558f3166eacbc621a21dcc29be06ad8124814c16" + }, + { + "public": "4b85184148287c09cc8d48b6abbf171d9de09ee54d2fe6f104e7b9f2cdffbf9329a2243fd3562c20ffb970a22fcd7a5e29bcfdf82e71d0b38dee8aff2a3d4d743c33ec177013ef495cd554a58b1d3f2ad0382ca6eb13052601f8e8c422ee3ca6cf86cd3311992188e39b782db36efd274de4b910e0a128dbd128aa4c416fc38e", + "secret": "4ee307bcd31d1d031032b1f37559c3acc3d8c329dca872087b1792f58fed9633f2a446da53fa3c8294bcd9c4ac068215e48ff9fcf711543f26a60381772cc38429a2243fd3562c20ffb970a22fcd7a5e29bcfdf82e71d0b38dee8aff2a3d4d74c05356711d6bb940cc25c56eca778fda4cb708df35654ef32fbdad17ee2d74ae7b3dc3653f843c090e5bfa0fab1cee2d57cabba2cbadd11133c14c3cbf4f0eabcf86cd3311992188e39b782db36efd274de4b910e0a128dbd128aa4c416fc38e" + }, + { + "public": "86c06e6060003aca1b8fe81b0f9a422b0c6260482500b4094ee543eb5afcac48fbbb4f7d1aa6601f52c2d3ba9d7a2abff7dc922461ad0882eae91c6bff8e61ffc4155546cffd160de27a135f249b9c367490eee952104855e3899979e7faeca1172b3aa674a8377722eb7a4e991705f4f1d230553d65aaca70b02058ffdd8420", + "secret": "a76072d7236285e82b5bd8b786291c0c2eb1c130f0f37d1512e12afac0923e1f64609e98a11823eaa7cf230fc8753a48c456fa131d20996cd888447ec2e3a8a2fbbb4f7d1aa6601f52c2d3ba9d7a2abff7dc922461ad0882eae91c6bff8e61ff86f821558d043ab3704e84598b64d03cc0aecf8ea3882a40f0614a234fed874cf712a99896f83154ce08a54fc8f51a7854e3da111a691ccdf4c392135e05fcef172b3aa674a8377722eb7a4e991705f4f1d230553d65aaca70b02058ffdd8420" + }, + { + "public": "26aee49cb88931c50b389b909a89dfca975b0280ede9cb1a1132ea65ca254ea3a0d3b2f4daf929484c80b84de9f9255960ba10998602da4e13cbd23f3496944c0ad132297bb846fd6bb0055860d1f9bc21722f88b1202b1b507d47b1461190f86cf0831bac2db241bcaeea386882e4364a331a1a5c8a797ccb1d6f6a4db1490b", + "secret": "4b2aa172f2c2c844420211810de735cec4cd6095ed48af8ac9bc1b2b129357212489f158b62c0979a50df9845be7ed556a0fc857a27cd72534aa461e203649baa0d3b2f4daf929484c80b84de9f9255960ba10998602da4e13cbd23f3496944c07f1a933b2a2e87585a02a773abb03eeef3b192bd61a5dc1bdb9efc3035d10330d3e5f9608bf2c28e882f909039762bb3b1ba9fd3e927275ff5e40bee09ae62c6cf0831bac2db241bcaeea386882e4364a331a1a5c8a797ccb1d6f6a4db1490b" + }, + { + "public": "4bc504fefdaadbd36afae10174c0b6e94159be8092e192acfdd9fc16c27876d069bcadf330b9c9b12b80917901b9f88aa8a56ab9d2f98d503ef1a611d69b70cc264964d9c9b9347e1f33c21d44d6e7a89b24097428ae50fd050c0b3b33013bfcd2e1071b08066377e80131451d43a8ac51c6f8be36fc3e8860e1ad51d84ae6c8", + "secret": "f5b9b85167a8cd792aa9ae13ae2a9018fb924e2fb2496ac0ab28d816ab0302eb78158885ac730b7e22557e30e4462d842980484de828873822b25afd966c5ce669bcadf330b9c9b12b80917901b9f88aa8a56ab9d2f98d503ef1a611d69b70cc8ddbb0ba0580a3a9cb619311c9e7700c456ad93b9ce02313c6d6958c48433cd15def0df490ce2513e25fdea62ee440e28933e804c5e5be0529a3cc7789ec70cdd2e1071b08066377e80131451d43a8ac51c6f8be36fc3e8860e1ad51d84ae6c8" + }, + { + "public": "f661a138de27463fe67d1b95553f8ed6a877415c3a57ff0090bc24c7df3b28faff96286c28ceb8c6b87922eec834e8f1e49c8664dabc67f6528bf0ee8c2f606149ca75a91357a139b5ef11d4d20299ce197b995cff83cd24f77fe860c2233009f2243a2620f6a355a5af68cc91b9a1acf3af635fb25df657307bb12cc7071a94", + "secret": "0f46238754b9583c94bed293e246b8496cb89f4e095fa9161ede96430ec5a613c7893fbbb9cd8927d2944a6e6d15dae8c93e2811cf6fafedf8f685d1e9b47d7cff96286c28ceb8c6b87922eec834e8f1e49c8664dabc67f6528bf0ee8c2f6061bab23efd3036a73bd20bba1feb79fd1acecbe15345820e29245932466fd6b1aafe534265758d516a35c5471b9ce1b1e5162f4ec731d8e16d8e8ede2df5046372f2243a2620f6a355a5af68cc91b9a1acf3af635fb25df657307bb12cc7071a94" + }, + { + "public": "29f3c8617d323994e3d443dfd9caf1fea6f5301171ed9dee287655b0ad8aee9b5c548f546fac50899585f56a54d67979c4eae060023d8386bf54e477a09acb82c2b82c41bf306e1cd88ee594d7f10d5a151b97e7d90d6069ed3923581bcf4c5378aa567abfede46a4334afde30691433a83f563cebb467f3183213fa7b542116", + "secret": "8c36b44447412b8363d059e205864b1ba68e085a19f4dbf492c4c4e59910bcc63e9c59d792964ccd2eaddaa34683c779ae78c70d02ec9169286462d953f0f2405c548f546fac50899585f56a54d67979c4eae060023d8386bf54e477a09acb82f0e08b693b466cf1ccc90c6631def39dbb9b7236762f1dafaca9ff816f6f66518646d5685fd4e4bdbca4c7931159edce6b44c54833b39982945846c57981cc8b78aa567abfede46a4334afde30691433a83f563cebb467f3183213fa7b542116" + }, + { + "public": "8e5ec702f58f9d78a9c456d30c7c7bda7c9b4a5a6e2888b83236e911c0854b0c1127896cc9797d3daec8f327cec7f3d27fbb51e7ce3555bff02515dcdd85691740bb1d1871990d2771aa263472770b8f3ee10d59a57a1aabd26f722c4c11268f98f7c3174403053e13c114c35b315d86c41a1446d19ad7caebff3797cfcbe35d", + "secret": "5fb4c3c5fc9bbe1160beb7a7ffdb6b63631f9ad60109160291b2d52cbb6f8ee82dd2737e376bc82025dbb6214eead631e8729ff2a2478d90290a0c44f12a56b81127896cc9797d3daec8f327cec7f3d27fbb51e7ce3555bff02515dcdd856917e6e4362db3f15c552d0292218fb4b3d55ebc7f782307fa9fecd40d014b3e7c29b6cd0b0dcd48e3dbff906aea534e967af541a88c38b2e162d6cc0186c0201a4898f7c3174403053e13c114c35b315d86c41a1446d19ad7caebff3797cfcbe35d" + }, + { + "public": "d19c0685d0b4246f31850fa59efbeb885d3eb52899cd3ea5ae1f3fd3bd9e702090837d3d1926e0b78d45d0910903902ca6c3b0408b202f95369d1762f873f0442ffe7d8e637c76fd47de02d5abdf877db0ec82ca93c51e547ea54ff54ba46b6c948bbbabbb53644c6ea9441dbe6371c3944cadc1667f42859b65e25bee99db92", + "secret": "66254a021a4769c94cac8360aea14478c99138409a78f8a380e6dfcd8fcd943eba1c262a16db53e9537ac24fb75e4c06b9c905d14616f123a8d33503aa59af8290837d3d1926e0b78d45d0910903902ca6c3b0408b202f95369d1762f873f0449264f41cde7e8ddad2d73fd778246d42696654c2bcc9fdbe8dd5ccd9652805e8de7e7701bd634dcfa3fca7dce53f1746be20e4abd73bd356e6cbed90c4c52df6948bbbabbb53644c6ea9441dbe6371c3944cadc1667f42859b65e25bee99db92" + }, + { + "public": "4ec9c509f58aa0167af3a8bdb04ff7d726b97784fe0b434b627535541924ee30b718586fe70a2debac5e45075a63373be4a6964dcfaa98969821d328eed900e6a7fb7c8bc48080d185cafff196dea7fbf7f917a435018a9965e077d1af92c08cf4171795d446c90e02eeaddd1d7f739a3e729c6bde2c726bf385190a7bfb4d7c", + "secret": "78410adcc07764622d28c6452f1ee60ff714754b39062aaded169cbd9bd386b7c564fa1636898f498cafa184dfdea522c9aba987c039836c5c86e1cd206b3cf3b718586fe70a2debac5e45075a63373be4a6964dcfaa98969821d328eed900e6c139ef93280781c39d603c534926c42239c16d2bb36ac630b1e23bd9b76e05fa66b903f35fcf98b7d8081eb5588549c68a4eb84e988503caf69c2f357ddf5c6bf4171795d446c90e02eeaddd1d7f739a3e729c6bde2c726bf385190a7bfb4d7c" + }, + { + "public": "3a262bc3e9fcba4451b0d9d81844d74412ec144bea9aa1e040235d188f00c217c9cfb9763b20b61a4c7fca4944dadcc12e20d48c8a6c42cca6c73754d078e17cf00914b18a35b5ab8bc0b0c6f19e6e7c3f4bf63503692e5838ab42c515431b87d40f6d0ca575d58a5d1008a2c7e2a901fa9a9464a3edd0fafd6cbdc435771a1f", + "secret": "960f297b9b1623c65f26d8c094324eff8287a5df6122548472590cafda3a7695d9a99cbed932f1a17aadd5a39aa5378883fdf0ef4ea6a5c0cea99a193676b384c9cfb9763b20b61a4c7fca4944dadcc12e20d48c8a6c42cca6c73754d078e17cb55ebdc568d0853997752d6ef17ecf4b2560f8400dbd22002ac633a535a08b746efd9b92632e29d19c6814c2a589b06b011a2615ca3c2a75f81f31bff4fa79afd40f6d0ca575d58a5d1008a2c7e2a901fa9a9464a3edd0fafd6cbdc435771a1f" + }, + { + "public": "bbf0aa614d53f3ad1bdab20f2039624bf5d71c6b9f774ff997cb3e13d17ba9a79d10509f00daa9b3ff79d7f92c7c216843dbb45e5a606862076242c0daf2a9586745c246ecdef5398d64719e00d865b7616fd63478e8cee23d90d5e1d2ed1077ed6ff4e025a7e799e16b98410dad14172041694d18fc644af4cf6565ae3d64c8", + "secret": "60b721700d2ca2e6f412d8f560b6dbf76fdcdf8bafa0ae55edf530916d4133dbecf19d2c1c86a630e1c6dd9cfc5c83786d9b67412a669351caac17c8c49732fa9d10509f00daa9b3ff79d7f92c7c216843dbb45e5a606862076242c0daf2a958aa0a747b0b55875ec8c9ab569205e34c923456aa324c81d4460192b53e2401ab867314b754e59d00e7458271fe38a97d667f4f268fd287b5e0d2cafb091346d2ed6ff4e025a7e799e16b98410dad14172041694d18fc644af4cf6565ae3d64c8" + }, + { + "public": "8a4f2d30cdb336deeb111fb0e5f100263dc90efd91428fab100f411e31443c5849026d76057fd019f03b5998dc96c20b640da6890478937ef7e1ce9d4e2040c3cd0e1fa0211120cae00622742e6a320ee545dade4ade77347bd6d634296fab2091eba53a8b6b90f177ec5fa0086099b29a45e9f02c0a67a223e3c1f3917b5a42", + "secret": "30adebd82a1ec6d962612bebe86360d548bed40a7d87a34bd375ad6e61ca479f638eb1990af5266e10b6bd336b815cada9ecc6f145a6d9b394ac14cddfde089049026d76057fd019f03b5998dc96c20b640da6890478937ef7e1ce9d4e2040c3eaab22486b499cd9146a73960d43489e7e8d7e05a633d891d1fe5eef9da3b687e1133ecadec31ab84f6e86a321da67a6b83667dabdb1da2fa94430a8c9c401ac91eba53a8b6b90f177ec5fa0086099b29a45e9f02c0a67a223e3c1f3917b5a42" + }, + { + "public": "566cd08a043ce4c8897dc79db3764724aad562b7fe4130f6663e0d3c08dc756facb38b98bdbc5812c30c3cf8c16ae123d9132b86769608694ff6969422b119e063a02676f959b0bb81618487072658ff050b37a2cd96de10e2f075a3ae821d0e00ccc0ad2554f5694b6ba8a5e79e0a5f05750bf8e6f47d5bcef5f8fb808afa14", + "secret": "d9dd7a8908f62a835520ad83b591606fedaf5920cadc7ab954922172d0610553f17073e28642b78e8bfc984c29ed8e6f4c77ac6ac9293b992c65a8c66ad6bc9facb38b98bdbc5812c30c3cf8c16ae123d9132b86769608694ff6969422b119e0f6b90264bc10fce3e3c118c4d3b6eacf6c387056943f30cad947045186ef0c8b5f1ac1f757c94868b61d994b9b892052078d9620c05a6df6ed5b5c77ad2fc93100ccc0ad2554f5694b6ba8a5e79e0a5f05750bf8e6f47d5bcef5f8fb808afa14" + }, + { + "public": "2fed74e5ce1c333f60f058fb0968fa2e87f682b1879aba3313a9784a157682974f38ad38164e1c79fcc1916add4c72f9adda7d0cb20041d9adb65f49c0e35b8d96d085a008d8f4dbfda0f8387bbe3209132db087b06c4dd911ffc8c9e9d44feb0feda9bb5b0e0ffef2921820eb0242aa554f6ceeff73b53b62cb4ebbe974354b", + "secret": "78ca3c59c756439e370c09aba83e1e31d359a6fe628f7ebb4da348c00450977d148db47cd5513b7970b3953df1a2cdd4be547bf67e853b6ad2c2f6e03fe8ce364f38ad38164e1c79fcc1916add4c72f9adda7d0cb20041d9adb65f49c0e35b8d2862b0c7c3ababf84cce11eecd73d2aa5ab07e28b77a4b193354a45d98b2f4fba4888fa8dc579fd6f90900610d2ec5cd0a8250492214a3a668c035837f7d5b640feda9bb5b0e0ffef2921820eb0242aa554f6ceeff73b53b62cb4ebbe974354b" + }, + { + "public": "829322c0c62fddeb6a424d971888b7a579ef5d11244ec956b80c207b9724ad54f73e481fc544bebaeae2c3f64f333ca6fe4d270f4566c3048fc5cfb89bb1b60cd3e4697006b2fa125bd2c2515e38b6d03b2d0a92b1da158cf46d40eda3a34362f71e168f2f33d0c2903b675f69191f9968b6303d8a46d640b8dcfbb21a0342b0", + "secret": "eb59bc510de70099b9e5d801eecc4101110372c365049c0b025a34fcedeb0bc749c27bd71c91951ab294234405fdb77b1cc5eae36f42426da8ba0f0a748dbd0cf73e481fc544bebaeae2c3f64f333ca6fe4d270f4566c3048fc5cfb89bb1b60c3e847a7469289d05f1cc04e4445937d30003f6e7fb4fc289f895b3dfccad831aab809cac3cf521dfe81ea48d9422513062a66aeeb24cf19b528c3f304843ee9ef71e168f2f33d0c2903b675f69191f9968b6303d8a46d640b8dcfbb21a0342b0" + }, + { + "public": "14d30173dad995eb8bd5d3a984000b42efd07651402389e2c1afeecd3d25cc74cf9b2c1da1affed4560b2ddd4e80cbf93aebf2a1cf6f385bc96707a2a3111f79b0ef061d1608719cd62aab6b6deeb4460a8dee13d144741f07db07f6a34eb3d087dbc6e72f0eb051ea1025d6af34703bbc572f60e11b0330ebaac332a83f7146", + "secret": "8df084da995473b3de4f0d19a3c316d7f59ef23fe901ae1810447c9655d10b77d0a65924bc3feeb001189cafba5927adcbc5588f0bd1661d81c23ad1602a368acf9b2c1da1affed4560b2ddd4e80cbf93aebf2a1cf6f385bc96707a2a3111f79c0f4e9def1ccff686ac2e5d45870bee324d8c44ffc8c26e8618ae1fced0567dbcb1186a3220c4958042b15da7871eae23d953754355c68aa733c4fde072566e487dbc6e72f0eb051ea1025d6af34703bbc572f60e11b0330ebaac332a83f7146" + }, + { + "public": "7943e5deb0263465fe1dc61f5a123bff24ab3a405e5b0977836689a67273a25514d4863d69f19ae9f4c79630e1c2d95c3bb847d82355c3ed8b3d50df20d77c4fb0f1d9b53ee14f97c16b9bd5c7df625b6917622c29a4bfc67a5302e8e2055aedcc2d5b1fb383cd9947cf331ead3abe9988e29bf0e179bb8a94cb25f03bc9b1a8", + "secret": "f514c599df5056afb8dbe328bf30400723da39ae60275921b1bb923a48b0a9359effc8807a24ab3209e566538ced7a9a87e59b96dc554c7e9713b7e27d7f549f14d4863d69f19ae9f4c79630e1c2d95c3bb847d82355c3ed8b3d50df20d77c4f6a5fbf41ea81cc56a9a0aba16534d69aad99d588a9d4c4fc0cacdb0a9d2420b3725c3cfdea6bde99b84e50739b9ece36d7b9ce2e10fac128a7af81fd118d2c6ccc2d5b1fb383cd9947cf331ead3abe9988e29bf0e179bb8a94cb25f03bc9b1a8" + }, + { + "public": "065224ab7929093aec3669eaf5015a5339ce16af39ccb9cee8b935d431abba11cd4d6d3dfd2f652fdda516f599cd2d4b383881fcd89699b9b71ab8b259b3c46051b90ccbaefadb5bcff887e2f31f3458117d91bb041131351ba950b5cc98f5848070cf20da4dcdb347b7ac67962324f90ce128b14ba4f2da47bfc1fa36850d41", + "secret": "12535cd7278db63c43a0874e58cc7e30f886294c939f445822e52d27f965f833babf6f194554fe4881a4c3d0dac8eed8fc703acb1bf4ac8748285a0b340acab3cd4d6d3dfd2f652fdda516f599cd2d4b383881fcd89699b9b71ab8b259b3c460177795a8f7b609b4fc919885b2766456e26aa56a3c19d680e9d9ec10c6ec169432ef9e3c7cac4bdabd536d2d312d3fc13be03ac4b39b92c22637d86fc459b3ed8070cf20da4dcdb347b7ac67962324f90ce128b14ba4f2da47bfc1fa36850d41" + }, + { + "public": "5f1a27207074c50eb1e608f676434de8a950e2fbc14dceae2a0499a17ce6b89fa10a9df4f7eb8656f1e3a247612a4381b221622773faccf457049c06c5724f16454793b8198767a56ae0378c592ea76369064df631b9eb388263b77374d6523f33d1f4c24b3e86e5663b3ffba8abb5cb703ab9a8df90d7d3e89b704648c4923d", + "secret": "6fe91dead72d626e1ad311e57f4c20fc606d4307eb5c067be22a888ab81336936a82cb76af3d889d0afef6bf4b1939d318fe69797fb81c1724674901b6b9dacea10a9df4f7eb8656f1e3a247612a4381b221622773faccf457049c06c5724f1684e5fa289eb5903efa5d4d04fe4a0ac1e8437e57c06d17d01be6228fe8080ea6be34c040ff346e73d873e3a1c7cddddad0565b3c997b63cc9a861bcc8965a87c33d1f4c24b3e86e5663b3ffba8abb5cb703ab9a8df90d7d3e89b704648c4923d" + }, + { + "public": "c151854a462d783ed03d11ad7f6db9098ec74cd02ba671b65af0608c454f9e0c60a0a566146e9d6409fd46237cd3fbec2618772fcb30f6ec39d7769278a7fcf4106e87535fe388254f707af13430c848d0aeaf7fbb045b6fdf2051ba906d98e57417c96119f111e41aaaa3db44c8d2668e852eabfecdb16a5e459f1f1cfafb75", + "secret": "1adfacb54f2b7308483a59ed52cd3aa4b438a83a18c1de2d7387efec90978807d5096065989f7af65dec3868c193bb0431e990c1f9493b0ee4051394f09376ba60a0a566146e9d6409fd46237cd3fbec2618772fcb30f6ec39d7769278a7fcf4419cbaeeb8182d0e5a60df190eefcf3e4e5031e361bfea81aca57bcd0d2b7f6e242f6ad597d58627d20df89bacbddf270fc8f24c71bbbd30b5779fbdb1518bc37417c96119f111e41aaaa3db44c8d2668e852eabfecdb16a5e459f1f1cfafb75" + }, + { + "public": "480d642a20dc4ea3e374244f7d47bcc57dabf773c102224bd5611928276007fda184dcf49fb3a3c88faef615f85b97f58233f3b770541e3bc5a9a15f3a98368da66acebb61e1dd461e3653570691e9e82621cbe97b116d7c8f6cfc689d5622eade862261fd2b589141b4eae2c9ece6f4fc88f9a9e4aab654b94929c5b33b3ac2", + "secret": "456217fa6aa171be2ff0d8dcab5b26a36756b92f8f062592a7a9caa72103cf2814d749ab652323ca95b0ae5675ac9061723b8ad24c5f7144e75a37fc9add756ca184dcf49fb3a3c88faef615f85b97f58233f3b770541e3bc5a9a15f3a98368d82661a659cd7bd4bce28b86eb180624927f0b24215779a82487c0be9ed68342d0ea9ab2619b02d2047895338fc649138ad2a94f0b1d7dafee4caad5090485cbede862261fd2b589141b4eae2c9ece6f4fc88f9a9e4aab654b94929c5b33b3ac2" + }, + { + "public": "ffbbe4955b210304fd24b47d83ca69970b60320aeafded7226ae331cc25ac66ff4e19bab73bd86745214a5fc85340b467002e7fd325b6e86659f9f70bd29c40598f754b32b5a5f4278ce1d40a8db9dd6f608e7ce13373be579dab7fa778d4e3326baf75705cd0c81f450bebe8098e226c66e9a8981ef8d0ecba32920b1039a93", + "secret": "573d45238ef650fad842bbe766e89faf1e50169e0ef342fc375700414f87c930a791c9d85c957d4f0169a2a42c7f59ea88c5bb4fa36caf0e08b405eeaa1e7970f4e19bab73bd86745214a5fc85340b467002e7fd325b6e86659f9f70bd29c405ae089a5ea8633bcfb289e50caad2efc15f00095f03c0b92f54015018dcfe8c43747444ca0444b405d2b8502cae26081618e2e4c146ad19eadd1fb956f0fb610926baf75705cd0c81f450bebe8098e226c66e9a8981ef8d0ecba32920b1039a93" + }, + { + "public": "297196060a7ddc7d1ad95b124786b9b4256ae821e2cd825209a3c732766f64a019a1db3beafbe03c4601f4252ed3d5f621735952b635bb9b78c9c48324b08678ba130a3636cf9752b581b637e7f14c360f4119bb27375ee8882fef02983028c59ff019be897359c248c1e063ab66bf68392cd04469ee07b6c6fe2b74fafa5928", + "secret": "4793803767635bac4fa141ef726ca5e0a2bd7364ab419929e5b2eb922316d9da48f0dba96b8d133e3890acedd507c29f1cc23cca0f6360fe301aa422a1b169a319a1db3beafbe03c4601f4252ed3d5f621735952b635bb9b78c9c48324b0867887df33c3c62b7a13b65657babd4567e3f0ba27654d9c86142e1684ead0d0f5de03347ec4c871475b1714b84040a57053ef97f0ce620588f330cf7026e0edfa009ff019be897359c248c1e063ab66bf68392cd04469ee07b6c6fe2b74fafa5928" + }, + { + "public": "8958a2712b3ca206e6607d1d58a4c75ccf35ef3ba0c5bd5e87efa1c5138fa6344e82a6053f1ff760a0241460326c1e52efde3861a058344d6ff8646eb305b2a9fb7fcbd87ed2da40955a30b334e02f6c08b9147765d2de08a769dde9f11c5eb7730d2e74532b25c923f331b6be268ba8f950cadd9d7db156d61be1271cf722cd", + "secret": "6f0438acab59b29c5b3c93adb62fc94b851703347041073726da3ac8d2d0e9390a12c321c43c95da99468938050db8d36e3c07d57e689ee3531fc844d4bf87dc4e82a6053f1ff760a0241460326c1e52efde3861a058344d6ff8646eb305b2a9c7ca44e8bbe2131d053bb8f7e41f1b38e1a23d26d430c5cfc771f6e5437fef43001bbecff5c73ed63e682753ddb7c26d3e7e503a2c8fca999d8d90f4293125e5730d2e74532b25c923f331b6be268ba8f950cadd9d7db156d61be1271cf722cd" + }, + { + "public": "f1e352804b5b8f4039f22f881bd1aff4ef920a1e0f92659be941d5ce55dc787d3b9a3c873b54866e3f1673f5007d7967ce0b2909d3950647ae2bb055fa519969aadc14c353d2876c3c3130f96506018abcf721778715f0ca81423c87b1aa981c89ba42565c499b3ef1c3e1c402fa2b66ddaa131475884e0a897a22b73532312f", + "secret": "b9501c12f7db45d6c981b764f181a9a09a0ae961c3a405c21577cb59920b94f66ecdae92875a0228d317aa99c19e293a953eecd69dc135e364176dbb3bcb63043b9a3c873b54866e3f1673f5007d7967ce0b2909d3950647ae2bb055fa519969faa2af703e9f50dc6f6853729ae25939e0c3165e805fb8f6d9711abb7e74ae75ff5f91c16931802a2b48594c5aff663eabd6e758556ee8b30a747bc769389cdd89ba42565c499b3ef1c3e1c402fa2b66ddaa131475884e0a897a22b73532312f" + }, + { + "public": "5e80b12257e845a7f508fcf162bbc65b42295a73883d5b30797845f147b3dc42f0c8fefa4401f6d3e214b66b19db559842686f528945cb2527d3d35e051c72bb4d33b603eb7e12c27dff6e8cf7dc468ab2e65e0b88d7302b79e0d19c7a3454565f0c1ad7e81c870dac50827a04ea65711e3c5e09fb8b9446fe4a92b71ae0bb6f", + "secret": "e01dd3d01f5b2faec7c03e951406b2f47192762f9979b553770822885b676e75420383ef3a1e4e9ec6e943426b43afca5953470f4ef70fdba1a6e88471da1b86f0c8fefa4401f6d3e214b66b19db559842686f528945cb2527d3d35e051c72bba03de3e574c9e6557c0498058dc907fc613ff6f6ef555fc539f42f163bd670cfcf691a41563de766c2550056ae4b07c4f53e6b6df1887174f6e03162487998c45f0c1ad7e81c870dac50827a04ea65711e3c5e09fb8b9446fe4a92b71ae0bb6f" + }, + { + "public": "e7a6a93c87f74ef915d4402c333686c2f61b7316f5f2b1074655e85a736c53458c47fa8ba26e9d7b3822ad4796ea2a33d5894cf58c5fe8f2e18ad69609a011ee9bfb32c1bab65524b0110bece3e87733b3c84b3c840adaefe73fc58390637fedc6689ccc0327e0add9aa4862e1ff13d3f596cc6f287924b56c2fff826c41da5c", + "secret": "ef46b48fbbc241cc189b3690e8cabfdb6e0da5350c78cd4a8d012927ebf69e82f8cc4d439b9768abb2f6042c18550a70bc791d0d843c9e570e0c223d79e20a9c8c47fa8ba26e9d7b3822ad4796ea2a33d5894cf58c5fe8f2e18ad69609a011eee0091d923ec3e9c9663cb862c9f54aa034e8682ba5b86b3d9614c57da8d16d19ee82fca0b067d92ea8eb8269e4b8948bb1d5972fd9db398675441ac9328fd43ec6689ccc0327e0add9aa4862e1ff13d3f596cc6f287924b56c2fff826c41da5c" + }, + { + "public": "6e03c8b96a510a28d14262ad90eed7b6d1e374af241256f93a7de4c53302850438c59a5589c26d57559f0a72c7c77856a0f1e1e39b5c32993c4a572631c7d574d0929d8be76f663cf4c153946da34f98f639d8a38e8fbfa123a5f61b7ad6c8323da732ceab8de40f1f071cb190a6dbdec80ce8d4c800d28edef86fc6a00d17e1", + "secret": "5be665a24f74118e38ebc4ccc9d35be265c533fa5b259fab7875b15905e1635cd7dfa3a8077c71113a96c0bcbca839fd370ff10b1985822302cd4ee1071e3ed738c59a5589c26d57559f0a72c7c77856a0f1e1e39b5c32993c4a572631c7d574449ac28cd5db7cf451e79c2dbcee2063cde69b1e8fe621c7e77c6f4587078be3d37b677e30f7f013f033862fadb60c1211ec032622b8051d2620bd42db2bb1283da732ceab8de40f1f071cb190a6dbdec80ce8d4c800d28edef86fc6a00d17e1" + }, + { + "public": "8ea403c059acbf5cdcff7f642f2df3ba651983aabeff2de9e89147a6a635ea69267ffe9d1e067c142b73a68036bcbd3062d69c6e24ad6a862d96707218ca642a254b26c85b2aae4142d692a12f530b9ac3be54c4d9149aa92f7ee71c6cc4e3ca29cc98ab985ab4b79dfa25b4358d2417cc5504feba5ac41828df47b98099cd3d", + "secret": "35799d0716378c874faf40675b60ffbe4024faaf7d3c86d5cfe46c0e08d3ae490b4ef3d70572dd55b91bbf873a2562fb5ced02ddf30af02eb72fa64b63093a91267ffe9d1e067c142b73a68036bcbd3062d69c6e24ad6a862d96707218ca642a552949c8ab35c628cce99f81e571ca1689391cd3a4386a7b59949a71c8482d244487e4a1f474f8448f777580566a349e1dec293d71471801cf0ce698485c059629cc98ab985ab4b79dfa25b4358d2417cc5504feba5ac41828df47b98099cd3d" + }, + { + "public": "cc93bdaff6dbb90a99ef238b41dd87d13138afb21c8764189309e2c8ec532d5dc3bae8d0e8b77e8291c2ed6b1292d56ac10fa03238bd5db329ab3bad95a7388d71adad294a0493a0d42424dc4e6b46a8261309e5babcaa991d0bd435127e28a07c6ee7b5292932a297cc29fb5b98eb0723553413d1cd82cec8f89bae1c123898", + "secret": "a587ab3c620b54cad516fd7b2c0baaacfc5288a2fd17e0db99450a0c4a3cb558bd9817169fad191b9e68287640ba0bf6759f1ccd9419199ece18b08436a05d45c3bae8d0e8b77e8291c2ed6b1292d56ac10fa03238bd5db329ab3bad95a7388d8a594a9901570de9e00f9c034fbc52bc323acc79803f334c1402f4f5cb3e37b227c57ed7abf9540c0a799c65fe149b77936fae34b9d234356192912010dac0f97c6ee7b5292932a297cc29fb5b98eb0723553413d1cd82cec8f89bae1c123898" + }, + { + "public": "0e1f90960c251f27af8da1bb929be656de67f0e627fda305fafa5d47b63f656b5567a4d663db11e0a893f83c03e4decd011f545b4598211995681f90b1954c21a0e79e2b3fd0a6727575c1729461567bdc025019dd7ca2e1ca7d91b924b2781fdbb5a2a72ae1f2ec37bd2334e906d4d5a934cf908075e11a80138f28b9d868ad", + "secret": "73d6d49cb70e25476b3f418fcb2c08b622b50b701952656638c70b339240a6a4d81c39e69b103220718200739e009016f4fe34bcf4362d7bbdc949005eb5f2cd5567a4d663db11e0a893f83c03e4decd011f545b4598211995681f90b1954c21d3e97f86a7900459de8d6d88886b394f8eeb80f3adf26b7efa1bf8e6889d148e30096f3d26cd8cdc3ab84ab89a13f39589d7c2960dd4ec33bb638c9450bb17e2dbb5a2a72ae1f2ec37bd2334e906d4d5a934cf908075e11a80138f28b9d868ad" + }, + { + "public": "0d1f263975489dfc839c8eb30a6fa3a7a49924723e266ee1e17fd1d96882162e89b81b72a6cd55725ad386ad09cb64e077471c168c3153827fe90197c47520ed6aedd84d1a5486600311916ce85b3eaf46c2f74cbf4412cc541c7759d17510673f0d7fa35561592f57cde87cec8536649c4b1450736b831072cc4e75bd5319bd", + "secret": "352cf38075e1020926adf1aacf93181fe30623f109cf8c6599894f358d4d0eafd664beb950579c7990450bc838e77122692f047f71974b74080935d8e52ed59089b81b72a6cd55725ad386ad09cb64e077471c168c3153827fe90197c47520ed89ab920f3cdb64368feb27fbb2e6da5d38c1b61b278091d644ff453793ecaf9da5ba437a1df8190bc5b687118471f939532007ac8c2673a7b1b9219b1d2fccd03f0d7fa35561592f57cde87cec8536649c4b1450736b831072cc4e75bd5319bd" + }, + { + "public": "df34f779b69e8abad64eabe40ae7e31e43e63e5ef1fa0f26fb9159543b5a0883364b339b9055643d32591647a292bbc09bed144b0def97ab309dd1d00c18fdd1d72e1623b9fd87d7af12732fbf9f44540b3c3846f6f73f1c6b0c0e574f6a14970556c50c9fdad127831b1233f216031f372800bdc207eddf7fb5fdd270c5f204", + "secret": "24d89a780758a26633cc5cf21ddaa1e7c23f3d40861857d8ac978719842a523bf1ec8681da3dcae0ce9f77cf427cbde49ad107b5fe77bdac1824e7c036fc332c364b339b9055643d32591647a292bbc09bed144b0def97ab309dd1d00c18fdd19b7692c00f0f01d9d3801b1a29c5fc9130764fdb3f353a281ee1ef2d4f3a8b7e41acd7a2bf7415e501fbdfb4b8b58f777297ebc9c3db18dc53625a44fda904200556c50c9fdad127831b1233f216031f372800bdc207eddf7fb5fdd270c5f204" + }, + { + "public": "d810d6437b09819e3aa090f6045ff3a561d9ef48bc124d5d457b7f679ab39a85e2af0b8c391ed3488b23802c033c0fd9bccb223d6552f27d5e856c21c6a1e3893e4fb9e801e61f0e89908e5d60506b32cb73bfc0f02d8499276c6fbc0817c560414637016278a40be2d5c50e7ebb0d7e11480984cf0154fcfd0ada3dddf49015", + "secret": "59b70127eace25ff873c6e00ef7be234c5a6f0daead34a2610e78371e9669817b9d63c7d4d89d1b37ab97a69d559038b31f1d4eca53b10202e30e5157a902677e2af0b8c391ed3488b23802c033c0fd9bccb223d6552f27d5e856c21c6a1e3890d15fc071fbb6ac13699cadc1bee5253cf537988f09aff6261d8f996da12656ceca71e50de313a1042069844f660fe193942f58cdf3f81144f3b94362bcaf49a414637016278a40be2d5c50e7ebb0d7e11480984cf0154fcfd0ada3dddf49015" + }, + { + "public": "3b139b49782403e1c52a4d2fb7ab67495107b91d4384f981aed40b9c77c01713ab427ca71f13e991293df2dd05622be7488f6206e42f0fb31c7a018488323df8c0d4acb691d40bec49c55b1b0ccc5fe1bb052f80d631848694971f91109bf51b574e12846e67d6f348652246098634c3e31a7097c7d28a1d81a421d1b0dc4d82", + "secret": "7c01e2a6afdbc54f14b085032f0c9ccd91c27772177442edba314f740fe17c41b5416f1d7cee585081ffc9cdfade54072f71c15b5d4e1bfef93e7c9e90e77971ab427ca71f13e991293df2dd05622be7488f6206e42f0fb31c7a018488323df857f527c3f9471374047952612f73a07cafb359242f972f8e43d1b3fe2c781b171cce0caffab855025d529e1d56dffc5984aa7450eb5c332ef741de15de7097f0574e12846e67d6f348652246098634c3e31a7097c7d28a1d81a421d1b0dc4d82" + }, + { + "public": "8f4741f76f2e7d6a03a50f86a7ea47fa38bc86bad5f7f030ad1f20d5dd8e067d48ab066c1ce96a4e74473ce90b21d589404cda89f39ecbc6ee4ffb0b351d6223b01110da649161bf84a5a96ef5235b2b909f148337944eef52771c82380bec084e54c6b1500a6e6077de9876ef650f5dbebbaaa72f9a69533f603a0b8e13d942", + "secret": "6c14f1f9680bf3d548fc0a2b07d326bba758ccfa88138e2a07aee5c1c3c7727f4bd16c498f596be880be18fc04dd61771a171ca4f97e1e7b2b5f904da1e4269048ab066c1ce96a4e74473ce90b21d589404cda89f39ecbc6ee4ffb0b351d6223b5bd25352f3adfc95653706cf25cd4428780dfeff4a2a9043360c5fa789adc6d3d0887decdb047362b10adeea436f42fc25e71fd9718e4ef604cd985d2f79eed4e54c6b1500a6e6077de9876ef650f5dbebbaaa72f9a69533f603a0b8e13d942" + }, + { + "public": "1672d07ed8122bf4c63c0ac07b4877f800bec13b239f5c2b1bdd015244315e78490007c64b7ba972a9aeb4aa80da266988951fdab59e073e05facf13c3d2df1d380db560b95ae3e4566845d7bd9c78b567157b988ca670b67c5bd7d0e05c7c41927c0fa774715a3e86df637f8a53ffba9f0453d2e0a4bb67b9575b678903f042", + "secret": "148cc037ad297cd5c315da80b8a1bed7ad9d2989e46cafd72bd6608e8050e7fc6df4dc12a635d105f6c6ed42cf7df15890754a4489d95f500ebb198dc7d56ef9490007c64b7ba972a9aeb4aa80da266988951fdab59e073e05facf13c3d2df1df1d21a0c2ea0b7557b41981599eeb0b74053b1635d071255cf32d6844aba6800921d979e2de47bd06d2518b48cde7d9fc74dc8027732baa64c37d2bef2849f50927c0fa774715a3e86df637f8a53ffba9f0453d2e0a4bb67b9575b678903f042" + }, + { + "public": "089272ab716c2594f34302012b115d9bb378f6c203447f0ea66208ff2ca1900796e4ac4d85d7e1a3aa342aceb20d9ad6b01fa8c49bb23a451e95db45261ae9c09130ec80700075f4a629901680f5ff60c0ae1ead68c5e081df9517acd7e6c35413c4b5f274ff1c12a63d4ce2cde8f5662d3f9c957b4d8a07a2232cecacf47375", + "secret": "24408c464f69ff165d2d46f153a60338d7a25febf324b26a27574d632477a3425ad12038f32406a458d10326e6a8d707b54fbc839d70b09ba8169ed4906dadaf96e4ac4d85d7e1a3aa342aceb20d9ad6b01fa8c49bb23a451e95db45261ae9c04006c276b412f3f59d195cb4175e36a18db22b0d5a1708a40f84027f90242e3f6b741339e56fde6d2f39b26800fddbb268773c80398c0af6423ea449588130c713c4b5f274ff1c12a63d4ce2cde8f5662d3f9c957b4d8a07a2232cecacf47375" + }, + { + "public": "74d290f1d706a4b6a573762d25c76c4e2495cdb3823bd7ad4928fc860f217e81f2fc6c9b2827d93b6c3c662ef6a3519b0617782b1091053d564cc47153794636a0e1f2b280319af1ea10d55ad395badf4fd41b5d38e2a2cc82348c762e5e2d1380663062341186b30e64b63cc588a7274af998c897242f425db969b4e8df7c10", + "secret": "3f38e0a527505fe3f4317337cf2acb9358616def1ffc578e22708ff177abdb7850122eb7cc0bef16b9996aa2c7206fc4ffd304c64f29b1dce0930540b86b0d2cf2fc6c9b2827d93b6c3c662ef6a3519b0617782b1091053d564cc47153794636b1894880ee121df872a537eb8de2bf4731238eb6bb6fa6ff1bd0c023b65c641b11a67f36c799209db4f241b15c82454702049f2c2fbc1b4975ac6cadad524d0480663062341186b30e64b63cc588a7274af998c897242f425db969b4e8df7c10" + }, + { + "public": "7c89c10eccde82ce5deface00351da81bab67c4982b432e6c44073acd5ec27fb6061208c76805bbc754deae73e3b1560a02b28b2d3095108ac05fb3097b3cb7c04cdef811eebf5f275a69d0727c4b0c83ec88d8cd47907758ad6dbb122db0145f9222793fbfc3d7c06082329ac78358188cae6999a1fe7a50e53d06c0905a05c", + "secret": "97475619d0d612b8e4e8d5109aca3b45301a48943d7401453327635e94603a59439dc3c942eb2c8b8354fccce8135e5993ebb41e120c80c96864a84565d65a3d6061208c76805bbc754deae73e3b1560a02b28b2d3095108ac05fb3097b3cb7cab1a29cbd7247ddd7dc8400194798e84d7e5dff654078772f78810e81e5db025eb38a2ecbc175433eee7f168212099be0389c76ca19bc5135405d3802099b050f9222793fbfc3d7c06082329ac78358188cae6999a1fe7a50e53d06c0905a05c" + }, + { + "public": "5c78b782aca1cd37eba37e558db643d743b2fbd21e6bfa217f0d6c8e78a7c0d9e5b6c04e06014f6229b151f129e03c5cf8df617f4e770a7eb6cca0eb9a4314f740080834638edc2c0d8913c6654db0c403a5d1330c5a11c428f541e34c45ba393b52bd83af421f9015c61d48dba033ef657569e9e8d937fca5e9c9bad5bd3c72", + "secret": "755abaa49173dce1f0fcb986f77933ade2debd0d83fbc86b0ff46b4531c4d7499141af257595202d0d8102b453072eec1d1f38deac86aa1a440ed0649c19f793e5b6c04e06014f6229b151f129e03c5cf8df617f4e770a7eb6cca0eb9a4314f71c381f78d7ff4e9b2f609a1377eb1c6cbd1f7234ad8e7172e79acedcb457ada1658ae46497b76792813b4a084aa53628e263d394473e1d647841a2bba2679ee43b52bd83af421f9015c61d48dba033ef657569e9e8d937fca5e9c9bad5bd3c72" + }, + { + "public": "8f70c89ecc64f183b6797c0f89afb2d16367ab9a2ef2869e076bd13feaf5cf0e668d252080a822415bf14c09d6a55a33c66dfa9ebef83d42a0a845ea62aaf4eb3492f39ef253cffa85a9b0ba3c452f1e7538573c5bc7d3ed001210934b3abfa1e7c94f39ca77c30023a0354d57900c0ae1eab90ce158bfbed1e7bb7fa0a5e32f", + "secret": "4437a3cce3935fcfa59cea346f7fc20905789e3a34f79eb61fccc86f34f0bb4895e282e51e9198160b48277ceff7a79f9323452e1b9499e3c41b5da9c5cff450668d252080a822415bf14c09d6a55a33c66dfa9ebef83d42a0a845ea62aaf4eb3810fbac6bffa7f16428bc28daf7ba4863a322c4dc296afa9d57821eb3972a9fdcc1f86fd23088f04180a08270af11e7eeb099e4d5459d06f85623694a0a08ade7c94f39ca77c30023a0354d57900c0ae1eab90ce158bfbed1e7bb7fa0a5e32f" + }, + { + "public": "1ae288f857ebbbfa10cc172a966f8dfee5b0e222b0c7f233e6e5d0439d259b6adf2e2b2ea523d6c1c234aef6fd6c5556021ec3f64a183e0c55d12d6e33475a4907d8f0310501cd54a7023417f9f7e7b4824a116493c07d432bdec7bfc09ac94f4235770a3eccdbcbca825198e2d2672122643e20f5ec0900f54cb69df97a14da", + "secret": "1bbb812cad8b113c4b2fdf9683844d6b103606b0e26420db1b9624c71af05671ab79537254468812500b0540c75212a7636928c311608e0a1f5076cabb7581fbdf2e2b2ea523d6c1c234aef6fd6c5556021ec3f64a183e0c55d12d6e33475a498fc9ce95bf84ed6d6f5a147baf7f2f7265006bf5908372e0f8965b8f4cf18019dd3196745222c912c4cade05a539e7828b022ca5df42afa7e2d9afaf8a0cfa4a4235770a3eccdbcbca825198e2d2672122643e20f5ec0900f54cb69df97a14da" + }, + { + "public": "35ec2d97ee6a83f96f432d0fea70cdaf1d4270897fb8001398928d720007c01bf5e81337ff24c796d6f8e25de608488d95e0e7a118227c04fd3187c5f14626b2a19dfb5067a39889499488d0d3d9367f574e3113694803067d2499aaf2b0309c567c810743ea2bd08618c076e3bfc9d96610263ccb0ee29253515f1bc5ee051a", + "secret": "fa4a3f2d18706911e64c7a465ecec77d10b2f0938ac236eabaf5d4eae50f8724c2ed93a8acab67135db87ee558d34a507220fe64ef982ea9d8c27d79d7c9d075f5e81337ff24c796d6f8e25de608488d95e0e7a118227c04fd3187c5f14626b23b69c530927b6861b25e0972aa1f01d3a8870735496d157125a6ed331c2889a75dfec7286db28837eb7041131a1935dd9d6dfe31c995511ac80b95955c1ba770567c810743ea2bd08618c076e3bfc9d96610263ccb0ee29253515f1bc5ee051a" + }, + { + "public": "db0dab62d99b95501a0932c421f9c2a40d9345fb6236f7f033ada9cb79c43a112e5c6ba89c6ebb86ac7531a68b0b48db14224ea9c32f59d927a5603a84d590461d1585c5bc64dfd566f5be6d225f5f968b00aa385ed2909cbcb0ada29bc1c51f803bc3663424eea34702aa6d381bc749564372f4d1e6eced32f7fceb7972518f", + "secret": "38f33e1acfd3b456f962ab3458dad40aff7c4641215049d40b7c5227c7d72a46f7b96cc2702b8625c852dd368488f88a5f392f2d1455a9cd7b35d0c0cea0d0b32e5c6ba89c6ebb86ac7531a68b0b48db14224ea9c32f59d927a5603a84d590465aa3a4d3e0c60c45ac763d24a4a9e9458a2e14bd3b1cf80efc1bbcdc878750363a20480813597ea4175042f27ea6804fa9bb390a1bf5ef5234a4ff75bf8bc6c9803bc3663424eea34702aa6d381bc749564372f4d1e6eced32f7fceb7972518f" + }, + { + "public": "982a0aaccd04e0e70a5a21cecfcd3d238aba03469458915c8ba2ee47f40c03d9a94c793834ba5eeaa0c26f15b15e034baa4b5757ec8c055fa94d9fa6bb67cf15cf66459ffe769aafe5d060e805fd372b19f38205626633cc71ca5fe8a0ac9110aa3c49e760c2986f61e041b7ba079598cc07ef501b78f34c448db29b9386e926", + "secret": "3c0ed26cf557278843925ecff046d9ef535bfe8f8e61b8139596dca0c25563adbd994b298d84ea544d3bb42b5d7400c37eb17907b2c2c533a66a697c0bc90591a94c793834ba5eeaa0c26f15b15e034baa4b5757ec8c055fa94d9fa6bb67cf15abf32059511cfeafbe9033d8d464d71b83b9bb1823f3bd40f447e676d06f724005105e4389a9499f1273fe09fba1e05a155c0b9d1b3e584f47adced450bac5a3aa3c49e760c2986f61e041b7ba079598cc07ef501b78f34c448db29b9386e926" + }, + { + "public": "0d1547838648cf63035fb81a5b49995e5869dad2e2e4c5a283d45a382012e20a532798e1042499014452e985832e64edf226d8b8972c64c94737a67782f4bffd6815816d33512ab230ecf95d47aa3a0b1a4f4a22e237d1567f9cd7a5ce097977b0dbb8ffb269e397c1ef205a58480f480064cf3e07b2bad8f9fec403af75fc9f", + "secret": "90301a2c456a5d0ca03c2432bd3b656ef25ed283d7da86a9fd6960d76dc1c09ec4c84c3446c76ec3908254e7b6cb4480c7166c07f0f101e53d9dc546229b39b8532798e1042499014452e985832e64edf226d8b8972c64c94737a67782f4bffd5fda846d3592e3832c653d33b836acb5488fb1c0d5728b7840aeec0e20f504e4fa62d5c0302403fe1dda9b3ecbb33a8e7b5ee21c8f63d1f30d00e3beee30ce11b0dbb8ffb269e397c1ef205a58480f480064cf3e07b2bad8f9fec403af75fc9f" + }, + { + "public": "a2b38ca189065db3df09005131ebf0a432c42dd31e5a4a5d67180edada1a446a246117f4c7eaa0adbcc26c179a99157c7b88a4d630eb171575021c58ea30f6e7c53964cbf62eefa50649ad6d346751b245a77b89cde4fb94e119cc682a17a988465ef7c99903cd4fd263a8ab295fd3c20aac7c2d9d8a7a39dec9aafa7f1fce3f", + "secret": "8b4a8671612845f54a19a98fdd08878da75d267358b923bdfc17b8a1ae0a831d75d5e19b0f61b9e53d3eae5309564d33a4a019985bc7e2492da276df941cde1a246117f4c7eaa0adbcc26c179a99157c7b88a4d630eb171575021c58ea30f6e7ebe7a30eca10003db8546a3dc1c585d37d5e67a6dc82ae073bd6818d00b098fb0b28617736e2fd69b1c60c07dc9eb7d91c3b1643c1bba7380bd9fa9d75f7172e465ef7c99903cd4fd263a8ab295fd3c20aac7c2d9d8a7a39dec9aafa7f1fce3f" + }, + { + "public": "7fd6d4fb525b693840c00e1e6751f8194a0b00565b2f8ef94d437685105ec2445492154032db7653dd4cea002ba924799869847a6e060f4018b6e487596a5e4c69622d3fb82bcb07dc161ad8dcc29878672b668d97b0539611f7846f05eca7ee19640ad6031ef7ee5f42a7bb09bdb41b10df77ddb40bc93fe784ffaa7692844d", + "secret": "9438013148e3fe4eaab4e76d18af6cdc08e144e540f131b14c5038d6eff1887501448811254de149c424de48a9cfd5ef15c7b05bc43d08c3a2fec859549335ff5492154032db7653dd4cea002ba924799869847a6e060f4018b6e487596a5e4c3b99caf2bee397f5e715ed9e966c72424bc679a650230117b0f35cd4e8d0ff592bd6eb4b9b894c13affd8f8cd52c78ff1e184c97ee6e36683e872ec12af1971919640ad6031ef7ee5f42a7bb09bdb41b10df77ddb40bc93fe784ffaa7692844d" + }, + { + "public": "7271034dd55c74592ae7c74b75a40ccbafb4474cc78bb272ff34152310e1bda2970f627fce9d09ea55ce23d9a44e9e4c7b3e9c7e7c932b5fe776f27d7b603e6b0ba0d0217f16606bf6765976b0ff2f67f39db6fa04d412ab6184cb690bc263b71515e9cb5769ed60b40b67111609bc1a2065f4686e2ee3b81812e5ccaa2b8e80", + "secret": "87f8b88653f93b75d46869c0ef5a731253dc9547fd16ce0b67615f750483323e31371b9ad5f2b7ba2c10b5ddd5936b15fa859ea25a47384ac3f546b3b543143b970f627fce9d09ea55ce23d9a44e9e4c7b3e9c7e7c932b5fe776f27d7b603e6b8dedf28f19c90a3d9634ad2b8baba08c83cba1838860a4d600ad8a2b60664ce1e89d80c2ce992152587483604242811fdcfdc808ef945f085af9f0e3b6f7ed4a1515e9cb5769ed60b40b67111609bc1a2065f4686e2ee3b81812e5ccaa2b8e80" + }, + { + "public": "ed3ce8e79eca83a7136071573295dc1043e67fd3299f6a448b4d4f5a8771273223e5b4665ff8f2342ee39725fd20817bbf386f1e9ecdf08596fc987966365decfcada6e022620ae4950a9d4d02eee28272eedefba132e92e386fbda96c82a5956684cb77dcba18cba516f77e2f5bfecba1c3e9d84521877205209a3baaffe6e4", + "secret": "c64c75b1107bdbf1345322acb6050fcabe65d8aa8402fb1b427ae63cf4e56016af84d21f0438a7415b0d3b11b694c2278a911def7d0809beb0d9cc69bc4629c323e5b4665ff8f2342ee39725fd20817bbf386f1e9ecdf08596fc987966365dec9dfa07509b69c6eb8d2f66ce7a8d1dde8ba2e7dc12d4651d3fb666e2c476e7279422163fc1fb87722154975cfa60c6086078d9d57cb27d9c6f23bdf926345a906684cb77dcba18cba516f77e2f5bfecba1c3e9d84521877205209a3baaffe6e4" + }, + { + "public": "fcf2ea652b0ca060bcd5b9d90730e3af389361faa5489f7ce38c90708098804a7fb360fc140ba1e788de2e283d937460efaa973c3bc49981ffa1911159046c7888d0f27a93ab9b81dd52702096ff8006ccf116e50162691ec7986263097986f48dbbc1eaf4aeb7a554a4e2dd72162d59bc8ae4af951166b8bd6d7bc9609386a5", + "secret": "38c98ec46704bea118eea8e0f7d46525f07732e6afb6233926e8764c4de7b302bfbc85aaeee0026694632a6d6fc5c00f6ae1cd87a5519c7b4e51e6b7995be1ba7fb360fc140ba1e788de2e283d937460efaa973c3bc49981ffa1911159046c78362f2d5f9e0eff1c5320df8f3c01fc604ebdf93206e8723bf5e7881d4139a0509cbc16a2d887af5bdce2156f8b5a30eb7d02bc1c6fee1a2ac7e79b8cd33b2b3c8dbbc1eaf4aeb7a554a4e2dd72162d59bc8ae4af951166b8bd6d7bc9609386a5" + }, + { + "public": "65558f45fc9873e7ccdb4baf0dfb6d5e7c486f6f5a596389241aa0d47ab198e5e3c1a2581ef3bd6a75d2bdc34f4f782bcf52a2d8248203ae9559fba2304f9799b446872ffcfc6eac2a163984017c0a57988086191c70df3ba5d5e5452cf7e28fccaa9a6d385789cd02177f298501885ede6d40de51eae07ca803c17fae3be52f", + "secret": "70ddbf3e5c0ae2180739e62ddd61bc086bc8138ef07c987e8389e24d5342874e23b0ca26389faae9ba5a53e14bf6a4348adebce0c3f41e8816b98e9543503df2e3c1a2581ef3bd6a75d2bdc34f4f782bcf52a2d8248203ae9559fba2304f97999565b08ec46b78cb69ec6b4f484a394becbf6dd61ee3aa15404bbd81ddd43ab3a870e1f9b374583684f7edc1dc3f93883479eb3724e6191e8173ba6a1dc4e25eccaa9a6d385789cd02177f298501885ede6d40de51eae07ca803c17fae3be52f" + }, + { + "public": "b5a7fa3f3a22ab4638551708ccc3615e9a76b205ed93c09463f9aae446e68a3ad210a8b95f11d19942d1b4b4f164cc022e3d0b886917cf4f546e9a6456ee465eee7b52edfdd84279e7ebcf078d80b20b6cc185a297abfc341307dbb6fe0106068024b63eeeb75b4ff165d5a83b4d75ddd2b9ff0c3fda3b9b8a7d90dee0d6a541", + "secret": "3221b0a65bbdf9253b6c2ee818170a242978ece8ee0f9eb6bc85b01f9986826edc02ce795b7f948e741e49500c2f4b56477506f4c72ac808b1cccd57c6eb013fd210a8b95f11d19942d1b4b4f164cc022e3d0b886917cf4f546e9a6456ee465ed4bca8da5e2c0b14d29dd606dd800e76b94fbaf00ffd35f94e93ce18a6b598b51c530b25e5101bb6927a2701a28e61e587945cd7e2ba4dc68245191ee4e4f2b58024b63eeeb75b4ff165d5a83b4d75ddd2b9ff0c3fda3b9b8a7d90dee0d6a541" + }, + { + "public": "b92335716d74a3ee8b4c064deff017b23d2c0a00bc9a31d088150ea336cfadbfab278be0b714b0fe3de4ff8b7a011ec5cdd0bc7fa132a8af9d9cb6726efe0a2aa3b0e42aa553e0df914698481c5528fb1ef33a2eef9fc06564376756ee643b7eec1a500bb664a350d9658b1239be853a9dabccc01a439f0b94fb2566f4e229ef", + "secret": "563419d8ba2150edef50dca70c80c90ac2c1a33f9ce23ff787d757be0f03475460dfd7c1976546d24fb8c5cdb1755709d73859e5a0f50d637f6c1a8cd992858dab278be0b714b0fe3de4ff8b7a011ec5cdd0bc7fa132a8af9d9cb6726efe0a2afa6c32690962869600d0c1b68bc98781f32b8f1a09cd6db6f66339838f571b2a3ef53afa203d28739176baeaeeeb051107f179d3334db465b0761269b55ad2b9ec1a500bb664a350d9658b1239be853a9dabccc01a439f0b94fb2566f4e229ef" + }, + { + "public": "136c83b716d89459457f36ab1e597325fbde68c529abf3b8c7b483ac8f50ab798e3dd3ba1fda544c6e3044b18707c8f163f183b7fa9efc76aa0a65210b6892debdf8cd139fb45f7bd4c49ad37f1ca60ba4cc09a50d9fef2ff86dd3efb449a82fb1c1a31b08043cb590a514b1d749237c6b3bd77a1d329b5af815bf93b8041b27", + "secret": "8d8689be437ec45e1645498e1823fcd9e55f9392add882bee8d0ddd1bf21c6a9d44f90caec6dd9db3e507e15b1a4a2a6affc46d80a75195ef7edd94777ccb2858e3dd3ba1fda544c6e3044b18707c8f163f183b7fa9efc76aa0a65210b6892de0b328e16937c4226fb23645df9bd776aca632bff7358b4f865949e2776a3d7ec4243717dd83eca8ee67a7ec9255ce7e4bc958e590bd21f12cd53e70159028598b1c1a31b08043cb590a514b1d749237c6b3bd77a1d329b5af815bf93b8041b27" + }, + { + "public": "e9443f4998975797f4c73e3cf3a0fe69279c9834ca935312ed066092d79b95a5a7de9629972b74e1744cd171903f32a3da4e3fecb93108541cc5ed065f0cb705dc6decc1183c68ace9572bba18561158e1fff9029ab2af20609f1106fffd435feae8dfb39e9e1ab89da11bebf5cac6cb68ce31b2723c33fbae7797d37dc0f377", + "secret": "3702e0fa4e9b1b7bb341aa2b07390c171394c2a033a64972186987c68f0854826f1ccce7e63c014393673f11e1522541875a9b9ae0f40253d9c9a0b07627bd5fa7de9629972b74e1744cd171903f32a3da4e3fecb93108541cc5ed065f0cb705d826619deb97aa7fed308121d0ee2bf235af2efb0a6a103576e8ca72f945e084f46162c8e15f0db8b8aa2ef5d7f08ea66d074d6e22c17d0b92dc7ed0d0dea5e2eae8dfb39e9e1ab89da11bebf5cac6cb68ce31b2723c33fbae7797d37dc0f377" + }, + { + "public": "fd106a25c827d23ab65064047bdb790cbc70e9617607791aca3bf6c503d1c660b99f429ba3591336c03f57b757a71f70a7401958c8670012aa33e3c45af684030a9da3a68c9eb7bf2b209504dda8e7ec2be9732a425bcb7aab0a12604b340f69d46366382a344f21f22d3d114d3eca0c17f168415c2767fa6fbbf821d63d4a0a", + "secret": "2bc0ed2468f5b9be25e31000957fa05830ee3e8f857c2d093ccac962ffe9da96f8035e1eb7c4bb02eec4b5703a87f9087919a0d12537b2bd2f8aa5719b8012fbb99f429ba3591336c03f57b757a71f70a7401958c8670012aa33e3c45af68403bfbe4adb45e499ff1b7a669d1ecbe4f0346fd23f5cad44039a714b10846afbbcab692a67c7bb2719c66cee7d3d826d766cb7220c968ad70c6a97ef4061e830c8d46366382a344f21f22d3d114d3eca0c17f168415c2767fa6fbbf821d63d4a0a" + }, + { + "public": "ede3857db65380e127be94b9dd35a667e109162693d9dc121a1de24b187ac870942a09479a5d60c9dda37a646a37029fef6a7b0c8d32094bd49b9379afc820ac115ab2a16e9936756f7b9a8fc8b7cda65dcd15e3099efee4d5d6c38978188ad900009cb0ccd0d1658dfa7386262a976c6d2b72710df7f35ff1aef9ade8243764", + "secret": "fb577908a1bd270e8d7408c7ab9d008ed30340fbb63a745cd841255342526879af646578af4f97b9ca6f14dffc50b0ed15e5330607db11f8e9e3f8b646b1aad6942a09479a5d60c9dda37a646a37029fef6a7b0c8d32094bd49b9379afc820ac5639aa0c173d044ef3e2e5e37e77833cfe85ed444a65f72c1978afb93a9be39467b435db3dfe2beca60e0618e274916180e7f56dd7ec363f8afd6698eca46def00009cb0ccd0d1658dfa7386262a976c6d2b72710df7f35ff1aef9ade8243764" + }, + { + "public": "6ab9d4371d311b94956254736b53a375be9d4ffac080a340545b82f16dd0620ede624ad18f37b0ed46e69e05407940abcd773bb4541d7d03fd825c85094b2487c54445c4e1105827c34c728320a61522f78f297c9ddc78eefda599f11b41486769bf166536c38935a22b942c5d92baf8c213fc02e82cb523d9ce9f594af2e895", + "secret": "bd0741af08334e65be027cc29f6642ab5ed576b4dd6a3535876573502a697b63b409026981066095eb1b10305ffa8755807b5c1b7f84d71b78c3a13c19bf5275de624ad18f37b0ed46e69e05407940abcd773bb4541d7d03fd825c85094b248758235b5417359fac81656b24ad19cf8021db069a4d96edd2d8bd554e9fcf6379b6bca93920df09a555a6510f2a9c49fae7f607b2888e7040c82443ef4f73d3c769bf166536c38935a22b942c5d92baf8c213fc02e82cb523d9ce9f594af2e895" + }, + { + "public": "069f46766f3beaf40af6a9a8c831a2e96f2c212710d5644c4ec19fbd378262dd39073d7f98d56d6c189a2d6fec1946ecb822249c6b9751d01d179f8ee4aa3b0341276799425665e14c4f0c04518e76929274e9b5cc9e23f3f231bd12ac4075484e4d6349ba11755e139d2ab77620891ac4e360067865603b36bc1c13a331052c", + "secret": "5821835fe9d07ac9e3ac5b534c2e51ea149d8bb83972469045a891ae659bcc36d6c03b194a82025f231b7f824d5e85c239623d1b1c7081a6d949916d8421e9f939073d7f98d56d6c189a2d6fec1946ecb822249c6b9751d01d179f8ee4aa3b039c00087d5d01160e57bcc358df3ba02f8ce1d8c37246aac4d233d876c51f195611f16a223f57557fde950eaa8ccecca6b8d536acaf9dcb08bb96dfdf252be4424e4d6349ba11755e139d2ab77620891ac4e360067865603b36bc1c13a331052c" + }, + { + "public": "d5eb784778f6c02b5825c872fed952a2652577fb1d8cdab93623a5b9d7b658c82f5a500a0ae0184d08d853320467f220b4f154d57844f3b0733634161894a21d4edacdcf4f79ebb1e57f48e67e6b61abe233aec1a6b0761277787612e392669a96378535bc65c55e36bdf23221bcc2d6f83cfb607b4fcb2eb57054033129e623", + "secret": "d8acfbbe668b95391230a2a890194dc5cef4187c3e835b5249e7408778059baa83f38d0d1022ab8ebd9af9439b100ffd25f3313023a5c525b1cb580e9bd2e9f02f5a500a0ae0184d08d853320467f220b4f154d57844f3b0733634161894a21d737edf0af8efaa6e9aa9ba88ea0bd8d417ce1822a137b96efd68350a51936f7edf0a5dac67cc38eebb3ac81b1727760ae110c2a9c8b99232d9822b7c67b6a46996378535bc65c55e36bdf23221bcc2d6f83cfb607b4fcb2eb57054033129e623" + }, + { + "public": "721358935122c0458054181624e2bed00bfa67e73717d04d8381de70c6aa80e54592e08854db7fd640ecef6498438c2827c72c75e2232ad6c0019fc660a0c5813cb8079585e9031ca095b2cc7e3faec3c7dd980fcb87b057b672d0c2271cdbac81a2596f9fecd34402ee9755c926447978719f99fa20fbd419280da3c68abff3", + "secret": "95c630d5ea29517c78daf0615006e04dcafef52e85e4f58b07d2db4b78f923fb77eefa8f7ca0f67e06f37e3d5962a6d68ae36e80c1f87effc34d83de22e851a04592e08854db7fd640ecef6498438c2827c72c75e2232ad6c0019fc660a0c5815749c9b5710f39ff9326ff54e8c1785ab00f7285f2f1eccf268c24a16cb055973c37de28b135f136609a0d8a1fe2ac288f50e0ee76ca5d6c41d86ef713026b5881a2596f9fecd34402ee9755c926447978719f99fa20fbd419280da3c68abff3" + }, + { + "public": "d7b6652e15e872689f14628c494186eeb8ad04f4e86fdef5e667a7f6a1d0b0f534a79774b55ef20c6bc5252d051b7350bccb73460b2e40112d4ff8697e14e8b9bfdc82f7c6c23d53c2774a5e8f62a2945127e85e00269e6d802a5e2fdcbcada9f7b65abdb441493932b25a79f97218a917d619e6529fa9b4bfd79206add388ee", + "secret": "b9a053a7abd68ab3194f31eaa5f774d8361d5ba55e47f39b913eb0e304ad5e15935523e90efee7b6ce0d49a0f4cc48a6e96a41293ccf2b55fc2e4e3472d202e534a79774b55ef20c6bc5252d051b7350bccb73460b2e40112d4ff8697e14e8b9c2daee98bd0165bf3939666e7c890426c87468e03e3f2f50e4973299efc96f399d49a924ef93eaf35731ea31b32c866fb1cf93c3a3328d6eff88a7308679f887f7b65abdb441493932b25a79f97218a917d619e6529fa9b4bfd79206add388ee" + }, + { + "public": "da84aadabe2b618478b951fee663cb237566a8f4b05221421541785761b1d4689a845aa8caa0104ac13506a73f492d4139b32b8abeeb93ff1b287980c2c80417b2c29324ccdbd9e394b9f01b964e19b3d9f32702a2a98486f23410a1ca73881a66479a32efdfef25e80f740195184a0c30c5b7a3b7c0fe291ffc2bf5a8889f56", + "secret": "7f36176eec835dad7592438deab0435ea2a9fe19b614ce4bae5a21b5cbef742781ae662c92b3ecd17531c42e8ac70c8ba637e718e40ea869ee115483eb8a04029a845aa8caa0104ac13506a73f492d4139b32b8abeeb93ff1b287980c2c804174ae9983c8a6e41fc0620f42a1fc17ed1fd3a17349f9fe44825a8500e1b6bae20db7e5aac55441e0d1468303209937c6f1f57a5ae5e89c426d3a23094aaee0e6666479a32efdfef25e80f740195184a0c30c5b7a3b7c0fe291ffc2bf5a8889f56" + }, + { + "public": "44d565cadecc14ea7ed8aab12d3e654f174d22a353c5ce3c365ae379cf7fdc9b79aa5981c12945a2359493ab9ac777c63496b25f6e3e3094cbcb7701102ef639c959c4345855ff442349f2d7e137132dcb72ca575ee4135b19fa73c55d8e92a76daa232236e19f9c0c958d4faff801ec78ff040148d3b77001795dfaaabc20a0", + "secret": "bff5af8e8460470f62fdbbc56ea41bf0465e20df0ba1832e014124dd2ce0f31207984e1df60dcc9830606e3ab2f391271be97ce0202255d4a5e35f50944d639b79aa5981c12945a2359493ab9ac777c63496b25f6e3e3094cbcb7701102ef639993e7130700f31551ecc87da814e8e6e9db67bbe6f7cf9ed0d98b1144c7ee21ce5ed8fd8da99a39482b5e68b327cb9dceffeb8835f98e88d5b857668be500db96daa232236e19f9c0c958d4faff801ec78ff040148d3b77001795dfaaabc20a0" + }, + { + "public": "c26d72b2f6089805ef9e6de6b900f01f39a261dae437fe454d193065315d513e9c754861cbcdb884c975d219730bb50835879e129e3ad4292c5c655c426339398ebfa6677d91113b65c60e5b6b35a55164ab614c050a83ea214186d97be11f68146e07262e947fa52a9c3388e55023ab5c98099c4d7528046b51aa34a983e824", + "secret": "040a41b954905abaa5407a95284b445a194f26708e02e8188dde7b1e89d4d83a9e052b02ee4ed8de412126bb41b4426a45c93ef5ae97a8d8706866516448bee69c754861cbcdb884c975d219730bb50835879e129e3ad4292c5c655c4263393933830369b33fca8b93b8748ae660412ed9802ec0adfec7218bc8089de0aff319208a715315cc8db3a552c6cbbeea0ee006b6a81ad8291f223282242c9ce5911e146e07262e947fa52a9c3388e55023ab5c98099c4d7528046b51aa34a983e824" + }, + { + "public": "669c814fcad9df4fdaeab8061c020e9fa08c5355521189ed1574472194a823ea567519bd10400aa689ddc674d1ccae8867335f73554d607298f8e521dcfd6ede3b2c6c0b103341accc2669b85ed647673dd76ddf877f60967a8367bfa10332d0173206dff2f83839485fff509c07f5d72bd8809d230cb9e83f61b67c6b37513b", + "secret": "094fac2711772ff795a4947c3c0c34cb98d2941bdbb9332389b3b3a18b930a6f63928a624cd99efb8d31ae26acb06eac277da61048624dd4b55892f4697d2bca567519bd10400aa689ddc674d1ccae8867335f73554d607298f8e521dcfd6ede7478047e4fae649048f59b5abde86298362951df49bd82f0b2746354ac4e89b1cbede591721e1bd7f7e6502f1962d5c923258d2888dc99575616a2d7720118d5173206dff2f83839485fff509c07f5d72bd8809d230cb9e83f61b67c6b37513b" + }, + { + "public": "2a007942f85c95fefd9c60703a26260105391f1d5c195de4b413e2d385425c3ff21377ba73a9c87af8f283e7ac4fe957274902c637fa5258a0637f7e0340ad12ddd9fe5c76da41f6ef2beac2a606415d8eb828c22ca31bf4fa89bfd27d3fd592827588e95c7253f815a624244913be16472ee4129b99382b96baaa6c80e4458b", + "secret": "424b587b90e9178992e540af2857f156aef039c746893f8025a67cf8d0db98d26203dfd12b298aa8734f54b6a17e2eabc23b9a89bcc0f83b19daa6bc58c87cdff21377ba73a9c87af8f283e7ac4fe957274902c637fa5258a0637f7e0340ad128c2a5bcada918fdd0430549c7c4228c0459732c66827d21bcb7c82d87a6398660d9e40f0d2a88c871f18efe8a582ba6bcad630252a6028c19eb2ed94ecc3829d827588e95c7253f815a624244913be16472ee4129b99382b96baaa6c80e4458b" + }, + { + "public": "f41e555cb15c05b61db6ded22811d5aa48185f7e6c31b38f777b427962ea1bce8d64b3a6b0f0121ae3a4e9610e2e354e674d2ee88c64f83045469e14155b764d3618b7bd0362ea7aacfcee42c22a89cecc090a6e34e07425762b4db021b46c8a9a39535ded2acb3825945430e2b01ac8bf7d5f976150e1fe4d7fe13023114042", + "secret": "a6eac09cfa8b6803b382bd38d59460802b1d88b26081f2fcf2e72593d691df88d87ff344275744c4218da633e2e5ffeb77f31d15706546048fdfd21fff9184248d64b3a6b0f0121ae3a4e9610e2e354e674d2ee88c64f83045469e14155b764d2d598255f8d90f6fbad3fd6d894645dbaa336057feca970980dcae1dc1f5d25ed94ac747f590de431296172a57b8c079bbed371af306a5a09425c8161ec152e19a39535ded2acb3825945430e2b01ac8bf7d5f976150e1fe4d7fe13023114042" + }, + { + "public": "15a2abcace85cee23f212447d1830b485817a4dd94808792cd6a2f9379ec5386fe34b01d392ebaaaf2097d32694a9012a3986f9a906c32d2e934b5faf0adb8d399cb27db74bb4833a770e6faa0856a43c19d02861a0701620fb57896e330597181aba18b42a7395f74cedd78525b52d65590060b318e32035818bf5cde3bb61b", + "secret": "73ef2cb937bd9b7abf36d457e39cd9695ba5a4ff68c6eb7f26f8c43b410786daeb495170fc5a57dce70036cb9ad35b41eb7279700733da1c39478797e6ae4a5bfe34b01d392ebaaaf2097d32694a9012a3986f9a906c32d2e934b5faf0adb8d37c73a5d64615d2d5edfb0817f1a4ca64553706498c8edfc6a8f7eeaa8d4b73c6bdd9a89c0198c30d6264fff378c0b700b9f9cc65b601005068cb4b78e150249181aba18b42a7395f74cedd78525b52d65590060b318e32035818bf5cde3bb61b" + }, + { + "public": "41ef40d94d5263890a8e3cafa7b674b6a3f170de806059fc44f61ab934a9b518f3a0f899cd310763bba683c1464a19f30e0c63a924bc4138d68393c94046e7fdd9051d76ea58a74d48fe7910df3257722c3abce464dbb2cf22818438cc623279f0b53f124b036e484b62ad8cbe98508b647849297b3a13d6c67d5b04897f1154", + "secret": "7f7cab090d5b1b6465a5e5212ad13f991a49693ab2b61e5e3a1fd3e7a56b5a039b403604bfa5c88a3d1567c799186f1805ca72f89d0acabcef02bff081697efff3a0f899cd310763bba683c1464a19f30e0c63a924bc4138d68393c94046e7fd35b175a3580d599308766e356c9acc927304ec327b20b43147265cb6438d002ae874d6330820b158613d77ecd20eac1d18a7fbad314dc3de0b0f3b04f07c6ce7f0b53f124b036e484b62ad8cbe98508b647849297b3a13d6c67d5b04897f1154" + }, + { + "public": "cadf01cf3811469fb79fd98de289bce4ad6b6ab8ba503b62060460991bb746271a98042d8e7fb14809967d2c91f147ab7baa067c8b361b41d965408414fc17590282c2f3808f55fc9082cc9adc2a9d9e3263920feb8ee9b286c716e87ff3a9bfe428491d805844c45c501e83b867f7750a169cec3177b65e709b1704fdbbc93f", + "secret": "79ab3c62b6b9de5c2d9f263adb734831ae5522d1539c9c5e85b9a24cfddda149cf29567b65c3e66d640f1d40d7c1b15b5f05490f57588c50f287fb558f3251e01a98042d8e7fb14809967d2c91f147ab7baa067c8b361b41d965408414fc17598d5ee1d44e305c24c1c70fcc4ac70bd2218469d3ed6ed0df2754490f7df7bbe0472e3619da34fea946d41d3f2e0e19974ce0d4c67042d22345cc308267224e38e428491d805844c45c501e83b867f7750a169cec3177b65e709b1704fdbbc93f" + }, + { + "public": "e299c7d11155bbdd9e826aa15624f0621675ab69bb9243b90fb4c1770b35a19cfbc8b12899b5a463c82fd828f36b697c86518e1706997521d0a2677906b10091a48a2d92a7943a3a20001aae3f5fbf70f5d9d51f4707305bab3f58f9b08939a685d6eb6a25921c744ec573ac7dad97cff9d003bf30b29db9152fc5936723dc31", + "secret": "f6e7a9286cd200dda82cefba1e30040b9a07596074dd217be147c35e5bc53492cc5b76bc27196c57c1bce7bd59f410a7794d8b4c56be16ae18d79f887bfc014dfbc8b12899b5a463c82fd828f36b697c86518e1706997521d0a2677906b10091857f112242719de708e0fa2e13b003489fe802633af2aadb9ca2411439b4209f517d9178382ffdb8cc58f6b75c1c9b50da4ee833650bc96031bca910eaacd38d85d6eb6a25921c744ec573ac7dad97cff9d003bf30b29db9152fc5936723dc31" + }, + { + "public": "a0ccb0e10022cc8b688ccda14d8524dfbeb7a0c1d45e0b81b9de55950c55614680fce3b4abb8d412107a4508fd3b3e93293ff02792118c2472ced6e9c9d07dbcf241b053b924664d84e8a8d03a16da861057a54e9c7503bb492bb878faef7250afbb6b4d4c0b1f252ace2d2785cd3240345f30cf67dd628cc800d78a468709a9", + "secret": "0d0e5b949b35e4da15c694083eb3ba9228dd13432831742169aad15cdf8ef241a7b2096d8e700ced1e9631e4c79414551d1590f288fa40d4445831315b39b1b580fce3b4abb8d412107a4508fd3b3e93293ff02792118c2472ced6e9c9d07dbc338d327c0bee683ac16311918bd2e53d37e1a41f8fb9e63d7134d9e57298f36472ed44f5fd105db7a052e30e3f9bea2834de4c5220b4b7e54bd9546a4ff52f81afbb6b4d4c0b1f252ace2d2785cd3240345f30cf67dd628cc800d78a468709a9" + }, + { + "public": "22eb3ca1ac8b010670823db79af589331641677908c6b8c990866344686a5f7e3eaff0058faab2cf170a3aaea520273d917f96208c87743584d6b9577bd739c4d8b44d2c40efcc517f16b8304e55f75038bda56f6152f2c21527c5fa3d432b5bdf414120c19b0d512573a9cb880608ed1297cb415e686d34c8829edbedbb4e92", + "secret": "55b2ec69ec8e277953d6e8f22c252c51e00e3b20b1fc9b48e5888cd29f93fd86fa1392294fc31d5ece44f9f114dbe4413e0aaedeabfdd9a47af8e2aa19e660f43eaff0058faab2cf170a3aaea520273d917f96208c87743584d6b9577bd739c4c72ec327edcb0398d7265e8c73a514a3c9216487344e79a3ec7ab9160d91cda6f1009273e709f65ebe033a8bdd2040d9a571490b47c3d665fe666f336a5cacf7df414120c19b0d512573a9cb880608ed1297cb415e686d34c8829edbedbb4e92" + }, + { + "public": "e9896842ac49a7fb3b09d73356700668c302a8b9e3f7c1737238629bb74570b6237f46b5adc797ed3878efcb39bb182f52cbe1f949cca06e90723255d37b0b6cea251c95bbbd2b2dce376f746829c26f90771a497e0e6fe728c1cfd5849b7e95a273ad1d7f10e523e127a0dc3ac108e8f5c14aab6ebbf529c114af4dcd976cda", + "secret": "b55501b10623792e6795255e5e4c5a5eb5f04c15e56f6b26563efff2066d0e4c2c3240fb1f0a1f28a9d7e358749a06b13e4ff101fb9fd31a7a1d18023743eb58237f46b5adc797ed3878efcb39bb182f52cbe1f949cca06e90723255d37b0b6c8ff76cca1e1dd23d67b8899ebddbc44362c4a166153e5fefc15c28c3246ee2d62b704ba76803753b9122aae25130bfd8246274f8fb4bf953f890ebbd44f18429a273ad1d7f10e523e127a0dc3ac108e8f5c14aab6ebbf529c114af4dcd976cda" + }, + { + "public": "21b1499889f888280574c98037defbf4803b28a5b40db65ad6fed0cfcccd43f82505fcb39796b29e1435b3110a0f2c708155055432625db2b5e843420dc7e558b840bb1fffe2e361fcbd1aa1b1eb80fb7cf772a6e9e3e432a8e8115a931dd98bab1498984f1219549151bbde43e7780af9cc5ab6d5ff75cbf9d3df501c138fed", + "secret": "19a16fedc8c9fcf9b828276262c81ef17620b37227dd38d0cd295d0a14008488728a61b7f40c65e62ccf202aa4b4e76d1651b2ea885bcb456dcdbb4768d0cfa72505fcb39796b29e1435b3110a0f2c708155055432625db2b5e843420dc7e558d24570373b84ce398a79643370d31cf44b15169086a1d69568e9c0ea19d6fbaca2456e4857f0f66cb7b2148c7e1fb122325dde4e0d41bab9d5ff8b289e3ca2bdab1498984f1219549151bbde43e7780af9cc5ab6d5ff75cbf9d3df501c138fed" + }, + { + "public": "c59d85cc8b33477ffd28e3e7f6d9f6ef066f74e2879cc634a9edc8c8284e6f0a04411b5c03bdf6e9f15aa3d76c1689326eb99952adf72e03acba5bb1cac7b38ebedc4a380d054fc79ee5e10ceaa85cc3ce3a543ae7794f29363cec51c7423eba795f20adca50ab80adb47874670a78cc8fe1b63506d42f9848b35689b3779105", + "secret": "23a9ce00603b1187f7ce6057c95b4d219b9b2b7713d9e83acd257a17a5fab1d3d5185fe12ba1836e0bee44c3f6c773b302cdb51fd24208ef3844a18354b8ec5b04411b5c03bdf6e9f15aa3d76c1689326eb99952adf72e03acba5bb1cac7b38e28bc8ea17e45aab8d82d9dff86ad21ddc61ed9de032aac930e5f17cad7e705ddeeacdad3f20a6119c0287172bd900a8f8f6ade24cbf8913ff83497bb7b0f7650795f20adca50ab80adb47874670a78cc8fe1b63506d42f9848b35689b3779105" + }, + { + "public": "2e484ee3653ffdbb22046a413d086c665030f95cb01b76949b2712b3b3a1e2a641db90d4d381d4f3d524d80fa6ad926632b5b8f981c76700fc4be7dba8d1980c3be23373e4fac84b838af02b752b91cc491226ec841fc0f32bbb051442b3a1b5eac1c9b47f4e3f7df3d9ae40cb142902defea9455c13fca953276cc7d2bc256c", + "secret": "b086e0fc2eb879239d9107c1a5fa1cd59690206b057f53eacd37e0f24714983f2a2d91f55e3128bf9c633743b7c26833c812424e644f531690302fffe81b061e41db90d4d381d4f3d524d80fa6ad926632b5b8f981c76700fc4be7dba8d1980ce2ab696ce667415fc9b0b16372fc5da597b950f13b4ad03567e8ee616fdf6c382d6c969d03c9369a70f8074a7577d5392c6dd6b93a60983e2200ff93eb6c77eeeac1c9b47f4e3f7df3d9ae40cb142902defea9455c13fca953276cc7d2bc256c" + }, + { + "public": "e51f7d75d531f26644d3e81b0735541bd843737c7ee0fadc133926a76b5482ef0c65977a8486fa7e58b2bf05d038e99580ace4f00930f902a3488b803c2063629a055f820374997a01d6c9a6910843189c51a3047e1358f964953c9489cb629146b92d7315096cc9c86c47f126f4aadac5df2c9a17d23980a9fa2453e83e6741", + "secret": "f5d9c0280080c4ed2f3172b5980333773266b9f7da098b2402590a422513e27a6a2cad650207eeec34f590693a086cb8a1413176fe2ec54bf8a03816bd9bdc160c65977a8486fa7e58b2bf05d038e99580ace4f00930f902a3488b803c2063628f4342ac36a6e9ecc521f0b0526c26314e3f49b1a78e5c8147e0c0d80d91b44a9abca50287666f4aa069e2c9cc78250b456f5007238d688eb938d44062756a4046b92d7315096cc9c86c47f126f4aadac5df2c9a17d23980a9fa2453e83e6741" + }, + { + "public": "dab2de48e36c6e4ff3e43eca3b0d4f88a92efe8c74fc9a917faddbf5a3de40a82c3a073df21376296d67980680ce3ac6f922d1a9710b1e133f53e97306118b371418e86741ac7768940cc6233a59d009e2a31b713a59a530091a7a1204bf83865ebbfcc832e448acf346d3de496d26086dd50d88191d596ca6d1cb524ac1a9a5", + "secret": "290d0953ef6b61bd75bd7426f8a6e1e9b3dbb13b852f14774cf140aeca8bef3da58a0f589b39eada14c93aa4f9116f9c78c1f7d06c4e37491b80aed26585c49a2c3a073df21376296d67980680ce3ac6f922d1a9710b1e133f53e97306118b37447dd8804eda50dbcacbe249ffeb8b151093194fdadae1c0e0a369359cb958d3feca5174711c1c6552ec881efb4a1d7078e000d7a2efff4a8f378779f827f6995ebbfcc832e448acf346d3de496d26086dd50d88191d596ca6d1cb524ac1a9a5" + }, + { + "public": "8f04c1b6507836daca8ce4d16160f8f823749a7359856c1b81a82cb2bfbbcc62659afdb20f241be33762d2a09a0a7768e670ec0d540a7261138e7a5eee89a92422a69442e757d5f96bddaba97325a7e6085d88e82b2193931aa7ffef5cda214d2919dcf116f04d87352f8c215dc7090011b94a4432fc302caf9f9d6684989a36", + "secret": "ea5b6b6d46046b71b7727aec4e37d35529939087baa6d5eb4cf826c00b814dbffad287b63d6778ce5611922e66d106a08b817c70406723a29ec36b9cb8698d66659afdb20f241be33762d2a09a0a7768e670ec0d540a7261138e7a5eee89a92483f916e83b4e78d448f69d25c42e7671bb73c84ecf7bd142c10b4605473cd7a18f4b479c0106c6b5624ba57d27682eb2e143662e0e1464b60b161b7fb90fb5252919dcf116f04d87352f8c215dc7090011b94a4432fc302caf9f9d6684989a36" + }, + { + "public": "1f800fcc0abd541de71ace9d633ebace431f833134f36b41e3d8960e5da959469bc6543c65b9e56b24d638b950e96404f1130ab1c98bb2236f98bb0b35d1766d472ae5ffb19ff36c070e1091626d0ddb4efb7a81cbf56384c744a25df8fe02ff3d4acc0338c2ae8f9423db968259e0c680622ea4d07b6da158a4db5af49ff580", + "secret": "fbabf4d76f9b4fc84e20aea674afe473a535880b38db0d36c175182a224110e91b7895ec4f5222a9890403525e4a1e8b61d0fc669550b2619c70b1b9bf03f4889bc6543c65b9e56b24d638b950e96404f1130ab1c98bb2236f98bb0b35d1766d99b8fae56a3dc36443f0eb856ac95e990fb955ce7ad5156e58f92127190e90c41f82477aec896b7356fbcea5df189141fb6a9b17eab3bbf1300a68aa7744d2123d4acc0338c2ae8f9423db968259e0c680622ea4d07b6da158a4db5af49ff580" + }, + { + "public": "e8018473e9126fd565736a8afed9ab79bd7a4c047678163b8321cb2ecdf337f428ffe26d8d4914ce7f86f521ac9bd07bbd34e639fc5332e833b17559f4fb3f7293d3e52a0496622f7e1889007ddc268b456b4ff35b094f1ea4e4b5ceb3b60a11a2becf35382815d0f9318d4e41f59bf0895ff2ba8b04b20f413bb5fa2d2245d5", + "secret": "68cea38bc86bd21ee964488de7ba51171dde98b716eee062fbc103c178c50faa229381acb2b3938720a94c1c4574067563891c467d0493c4b4286dc136f4581d28ffe26d8d4914ce7f86f521ac9bd07bbd34e639fc5332e833b17559f4fb3f725bd591c1f95e7551f09c830df058021c2605ffede85a0605da62e31c762638b8251c1ad65c2b91720da31f9c8d507cf411aa4d23062653450ffca276e9f7c00aa2becf35382815d0f9318d4e41f59bf0895ff2ba8b04b20f413bb5fa2d2245d5" + }, + { + "public": "fc0e3afa2aa6bb1d6c5851b91a6a5a704ecb603635f757658cffc8ad44ddb7f153b3add44ee2de1b12a316b974c8bae5720a9c5e5d7f34941eb1d2d6ddb21cc9a9459eb200791c5c7b288649954d2f1fab48eb7a1e89f50cdf18bd36de7aaf0e3f0c1cc805565094e7052e0b5bb3f0a1301c9107fd126a4e51babfd0e45a277b", + "secret": "336e1525de0606ce8db78137389159a835919b14ca2a5ab5bd887b7e7182bf5aca717abaa55e81b893d5ab8033f0a124d8190ce5952c005590449d0ed0a364d053b3add44ee2de1b12a316b974c8bae5720a9c5e5d7f34941eb1d2d6ddb21cc9a5740258f663a7e59fc0edd248b5a8af5ca634a40d75f1d2ca0c41bd4969b68fad742fa5c9f2e125a3c9df5d20e593066a59a11108d5ec3fd52ed20c05040a353f0c1cc805565094e7052e0b5bb3f0a1301c9107fd126a4e51babfd0e45a277b" + }, + { + "public": "f211bc943104008e7ccd9a53d823b78e00572936665114e12293450f196f59e2621c5e27803d78f38f4a7f1e5ef6e2f0150b6574cb141b91aa9710b739d63b2683eb52bc1bce1734e52193acc84bad854f52619779cbcd6088c29efb74e41539b3a31d5dabde3c22921a9bf05c64cc6020d97b906c78541d59214527fe2a8e24", + "secret": "e7a4f7169bb2575a44cd76431f32b8d31238dea0c98add1ee1119e3797c34a8541add019aad2aba789b39be42e01c6b0e79f6a5eb0b5e2b0887398d864de7ee2621c5e27803d78f38f4a7f1e5ef6e2f0150b6574cb141b91aa9710b739d63b260531f22961c1decd5ce42888f3f34f7b20f18b47626cfd8bc91211adddd53be60591ba512616a71e8d83957ca31910d93250644eeff137710652cbd7afa02a5ab3a31d5dabde3c22921a9bf05c64cc6020d97b906c78541d59214527fe2a8e24" + }, + { + "public": "c3ef1f2f5679ce64632a9a9e95f32d699193896cb04dd5a3d7766dc7a09d0923622ca30258a5fdf4db825f82b3d6e2b916e5e18dc51a057695f61c727b4a99b501b52aa531c1b92abfe0acae28eb8b9dc77f00069cd194d071b7b9bf1bd7c73979ba6fa22ce68e6e415e6aae3b59455aa6bcbb0ec4e2c8598f46ef73f99757ba", + "secret": "d629b3e590d205702ab350439cc82c48ff5b972627a2b4fc13c1dd63bbdd22e49ff7b5f480db49485d5eecac52ebc6089a1f44f0ac1cac0b15a5e993f1893b8a622ca30258a5fdf4db825f82b3d6e2b916e5e18dc51a057695f61c727b4a99b5b1d5cb1f27d2d1311d7b9a7d9d39dc0ef57afd83f42f8e50608d9a2b99aa449926b5f086f74acb995e9c76cbafe47a4a5d93b75fed6f813d7b7f1bf35615ff6c79ba6fa22ce68e6e415e6aae3b59455aa6bcbb0ec4e2c8598f46ef73f99757ba" + }, + { + "public": "9ab68eb0a9e0fc9e61d6507b8fe2e35a76961185c97391c8efe459c1fa7d66cb5f60c691d8d0cfac418b5806cd16a61994c74c69a473bfc4ce7e5ebf61373750600ddc3c206a95481086d156f76311de9e5a876e9be5864782657446f5db4b7b81e702be8a9565f8cf2715bafa3a3a67c4d2a352ac1f80a8194c49debd0593b4", + "secret": "065339fd6a0f2cac0df79e0c56be6de62707bc41435a5e839c26f9a22ce255276417177e0bcaa47a1c9782906582d4c04bc78f039bd2d3967323c60a64b9267c5f60c691d8d0cfac418b5806cd16a61994c74c69a473bfc4ce7e5ebf61373750053210201a185fc3cb07d7498c7805c93346684f552d06d4d22f6df41e645200ddd75337558752bf7418f04a7411af34c0cd71ce84b6658c274eee355e498b5781e702be8a9565f8cf2715bafa3a3a67c4d2a352ac1f80a8194c49debd0593b4" + }, + { + "public": "c8c5d99719d970fbe60f61062931f561b029f4366e54b6561ea7d16741162649f7b4a39c2b4f3ebe3509a68be6ca52b5052e3be4b66cb0d7cd9087818401774a668d8d3143ed730c8a28bb13c2c6366b34ea5b54f56acf7437696537fd7a13015953d6de2ca3378b2fa160213c0d18fbe4eed01bb445a1b0a75108c0968efb55", + "secret": "5a5bf991da7b02c035edb3c3122185ecdc28c5b06235f4b93468a5062a836b0d54d505d0e777d2ab073e9ad5c8dd8e39b68169340d35704faa86e7b7e5ffa09ff7b4a39c2b4f3ebe3509a68be6ca52b5052e3be4b66cb0d7cd9087818401774a75a3f12e2af7b35c02a6860c8b6c5e4dd842f19b9938145ed18daf1bb78655e4f3f1b53d306175582f8f31a4aec8d74614e60328b6ff1ec072d0094be9092a3a5953d6de2ca3378b2fa160213c0d18fbe4eed01bb445a1b0a75108c0968efb55" + }, + { + "public": "7c59d5fe60e581cf6383377ac8541368947adf258685631af86f434d8c4b6b7fa0dee5df3c895f363e626620d5c916fcccfcb06aa63e778e954d7dc416e65900f54552b27c3d0f14db587e55ff3391615e1f78f71e30ccc125f894be1c2236757362e4d07258a08a6d1d3d14e826ddab9b5eb1317cb004911f747fb9369bb01e", + "secret": "c0f77d41af4ec2a0322e3f2914c20ae32fc5145aab75c223744213341a82e6f3c7ef4c8aef36cd5a5abc3bc6b1009c9291a04f779d5e805a2ff46534a74f5aaba0dee5df3c895f363e626620d5c916fcccfcb06aa63e778e954d7dc416e65900f252e8fcb2ed5f2bb13d737d738d6ca9f106b4cd7035c55c19d40f1851baef58af8f5306702b772b9f478b3345e62e95684b32dd64ea0f4820ee18c80861de047362e4d07258a08a6d1d3d14e826ddab9b5eb1317cb004911f747fb9369bb01e" + }, + { + "public": "2b5fe0c5dbb5574668d29f8dd33e73f554a37b5680c8773e1ee5340660bcc8e27fafa7fcc75451113520859595e6776bc0623fd8bb865a4f4c8d025e3c78dbff5174d7f1512932743c6e1d89292c3aeebf70a0405de9a12e3305575284891b4b84738c57cb4d120eb5a0732e6e84675e687519d62853d7dc8a143908ff5cd732", + "secret": "62bcab6afe90063b9e9594f4a934dbfcb96deaf3f5861ca0f16844d9767271f8a07fd2d4fa273ba4c628b59d23de60663614522d2f02ea0b0392765642cad1227fafa7fcc75451113520859595e6776bc0623fd8bb865a4f4c8d025e3c78dbff3c7a13785266e8b0ed318a2228fe95c286d1039cf96aae106a0fee6c8df51eb67ed6adc8b4f721951dc205412684780a61c60aafa063f8b30a068a34d438e4a684738c57cb4d120eb5a0732e6e84675e687519d62853d7dc8a143908ff5cd732" + }, + { + "public": "53a70f7d9715525cc2296d052c27cfe2aecd68999ea21c7d65b2766b375ca6118311bce34e0b0a1eab6dd274a6fdb7131d221ff995236876bbf9f13ba43a792184f55270a59e7a7dfb9afb056ca2a226fc0c37be30fd3e6bdbd7a9ad66b0240a8963ec99cae6dd7d82d3a5be8ab4eae4e2eeec1422ccaf32dadb673e12cf4b43", + "secret": "3be970f40b1f9f80c7687af5bf3fd339ac70ebca8c58ff620a5a6c266b6e08b8d855f09cd695211ca0a6c51452948a82bd448e847669021094f09cc67a7a185a8311bce34e0b0a1eab6dd274a6fdb7131d221ff995236876bbf9f13ba43a7921e028e67d5b83456e50b73a583eda3691de1044e4c31f3f2d8f442408e8950ee3ea83ca20dd969881ad37b9ebd16efded05839249df3c6858259882b6eac413cc8963ec99cae6dd7d82d3a5be8ab4eae4e2eeec1422ccaf32dadb673e12cf4b43" + }, + { + "public": "286bc9494fbbc90df10edc1f950ffa2db24bd0b32de9ead53d366d51925a1d7f4e1f7d3803adf73a194e730ceab6c18e4d91887a680a0ae3803e6510e80d9ab54d2b4ddc848e6c8386254afe00780c392a37328949a6160bfccab1643c89c337257bc3b3074a94b9d94bf70254af684410a9442f611b2757efddf186bfa4b02a", + "secret": "91d57986b2a76904a7673ec12c22ec0dcf37a9faef6d78740fabe09f829ba577e1c1cc0dba9dc363f151853f9747afc168604c037a203957853fa5bd5ea65b614e1f7d3803adf73a194e730ceab6c18e4d91887a680a0ae3803e6510e80d9ab5404f5dea7957fc3c1b6cc901c4a3865fcc32f1b191f64906edced7b4f67d61df6db71ec351bf53b36dbf109c53bce4c5ae741b609dac8174f32b8fe98b3ded2c257bc3b3074a94b9d94bf70254af684410a9442f611b2757efddf186bfa4b02a" + }, + { + "public": "b7c8c6a772b1d3359e38ef5ff2fe58c2521ecfd57f736f606111f5d2746d2d7273320c4468c7c93b4c4adc0daa51c74200b0cabb18404f9f7b05a546018ba2203c19a47065cebe74c897bdc1456c26323c905acabf88add63f13eec25223bed08e77feb1fa394427642de7d4b690c3b2a203889e2598801577ecf5b285150fdd", + "secret": "e2a869ce26400f1a3d8a00b821c52954279a87b3d9400562820b12a6caa34071cfb48b724c7194578c0e039b00792a58114b71c4344cadc393a2d5d0315abb5873320c4468c7c93b4c4adc0daa51c74200b0cabb18404f9f7b05a546018ba22039ba0057882b737a8eeb8787bef796f63f840f471a5c3543a289d70cebb9b53ccaa3e275b824838d4bcd82f1e049f39b194fddc4888b3dfe6fe2f09942267ab58e77feb1fa394427642de7d4b690c3b2a203889e2598801577ecf5b285150fdd" + }, + { + "public": "7603fafad91c45ee3bf08f5b42bdb316064c33f2a6cc3f609d006d55887b69ea4ddeb736381dd4219216e2cdf3030836cb5adb59fdbbe97ad14c9565b44a62801097efefe12b4b37cd752e327c77869c5c959626af658b477650424a7732e066a9f3ee126386f606e1cf03a086e68b604268bc6c7904aad6e4a0bb914f5f1194", + "secret": "0ded4716a0fbce297475ea1b86f8cf7cb11b6cc0073ee2fd7da01599d4a3e0d1b546c0aef5397724b9a4bfd9795aa6bd52f486f502bf2c179bdf081a76900fe94ddeb736381dd4219216e2cdf3030836cb5adb59fdbbe97ad14c9565b44a62807988bc49926ded177bd28854e40e4261fe8d3949d205c414e2a000beee594736013773c1bb868c7e21dde7680d83ca18e2ca94059f33eea89c6292754f190fcba9f3ee126386f606e1cf03a086e68b604268bc6c7904aad6e4a0bb914f5f1194" + }, + { + "public": "7b4ed462b30df059c3dce823adb376f156f03f43a3ff79eb02f2b62f880e7808f87119f0e27e45a3d53ed4ffabcbcfff6c6e0dad60f391758fee22f2cec1e0579af4a280de2ed52c6ed2c9f944e7c0261e4350191a7b6f3dd5ea201f4c84372c2f84ae498cb8f989bcf3180bbe8d3a086732b78e197edb0e00194f6769496862", + "secret": "83dfe76bca17140608b03d31f985e3a57dc33ae20cf81610dd97bdc42f20af0bb3af884661e577c50c3f869d91251673c76a5aa5ab6de65d84a1a668104ea719f87119f0e27e45a3d53ed4ffabcbcfff6c6e0dad60f391758fee22f2cec1e057842f77a47ef2b31bbc3127fe20a7db423c4533621a6128a62e6e87f5af79c8ec7b13966eca54656fde46a488ba04eefd8bdcaa4784abf60079bca07545bd6f952f84ae498cb8f989bcf3180bbe8d3a086732b78e197edb0e00194f6769496862" + }, + { + "public": "6c2deb001c19bf959b95652cc677b6505eb1a7d311ba8555557baed4feb0d2ffc909259183c7f6130280c15cac1a54165ea2712d95830b28b3a3b68954606982ef156fc6d5d16cf0cb3bc40c0c215709b6cb4d7f4c86e3a98f93573c7c1280792ec9fd4dc070ded8af194ad63de40e5f796d95c6e526242b05c2b9f82eeb437a", + "secret": "2f874496b529a6165467a884b9dc24c525f36956c2145f84187d9d18bc1b7bf4c2f8a93878281d4449f84d92346c3f7f70dc0abdd3ff65cb79f4bf9b7773f33cc909259183c7f6130280c15cac1a54165ea2712d95830b28b3a3b68954606982a8f45705f5052ba54424496a3813825c686614c3a43904086c3c8074c6afa06a7385535812f48add3b96d6debe14e447b76fb43e16f6a42d052fc5e2cdd840f62ec9fd4dc070ded8af194ad63de40e5f796d95c6e526242b05c2b9f82eeb437a" + }, + { + "public": "5d90bae1d9bd99283007bc0e34638a377c8ac59931b37671d2fa34b783c6a6e3773d8218c09a43b30e35ff88b6f05c8045137bf7579b4391a6f41a0a15b80d5b5de0463c2e3a35bb14ebce2b81306cde6ae82f954371bf7938368baac8d275c2c8c3b1abcec1eaf5f4fad3fef59c394f26beffbfb178c79e71a88c0fdc2b125d", + "secret": "1bef09b4f98525872acb5f3026b83f9982b225fd5489014cc39f4ba89e91e017d5ddc026772c51582934648194f464ac0290e5d761a0499ba7666d9072422de3773d8218c09a43b30e35ff88b6f05c8045137bf7579b4391a6f41a0a15b80d5b7f9a60b1ec85b7b9f03e1e27822d98bd8170511d06bfedecb67ea5731249ce6080a364d0625f5646dba270c51011275d17f73b5d4cc447e27b987c375cee7871c8c3b1abcec1eaf5f4fad3fef59c394f26beffbfb178c79e71a88c0fdc2b125d" + }, + { + "public": "4f3e908bf64a2451e80c4800f61b7bfb83ce4f0a3a4607d2d282f6099bf8becfbf443799e96893ace6b86d8f5a339f01a5d4f078bf9eced6091c29cadb5aee0ec79e93aa84287d531ec8e3f269693a8c60c365eba4b590fedf3a9e3cc99fe99371e835e5203eaf33e6837888363f2553d1d7437903415a86e9f834ee9a96a7c3", + "secret": "1d38b5073325498bd8abba8c13f84d8b741eafa6a135fef5fc036d9e6eeaf835b752815a1051d5fa99c08a8708ea63f32eb5dcddaec99875169da3b8753736ddbf443799e96893ace6b86d8f5a339f01a5d4f078bf9eced6091c29cadb5aee0e5421be61556e6a58b02ca11181c03c4189f059b0b0cb00358a36ba244f944598dfa6ca184bd8a70fd685396441ca4a0a7812a0fd63ae66628bbb49e55345fb1571e835e5203eaf33e6837888363f2553d1d7437903415a86e9f834ee9a96a7c3" + }, + { + "public": "911d09acd06a9c6de80f9587a7fea740694cf2f840202d14166dfd3b9ab04d659804c3e7f665cebd710be265a07af0dddb5a747c05720b3639b850f7790e53a5421e2be6c1acb5e145be33d94e7a523e2020f5d8866d0052bef0e9a0081d50cf377de14bb40e9552508e0120ee6d9f9aa6c3aed07266d1b879a22c098189655d", + "secret": "af19885de6c4481931d95b678e96c614a5533d14eff33ce589835dade35c66cdd3f3c126a4c3295488b09ae3b4b06766868caa8246a18e4bb11f68e343810efc9804c3e7f665cebd710be265a07af0dddb5a747c05720b3639b850f7790e53a56dae4bc3864c803a5d5ae47e5c8688dd263cae8f58add8a29c64fb2c120dbdb9e2d117fcb8b942d80b57696ec8131ca6682227ed74a44d68edac9cb227b69e77377de14bb40e9552508e0120ee6d9f9aa6c3aed07266d1b879a22c098189655d" + }, + { + "public": "d62b6adaae2f520071380d52c74a69af7e60ec92a22ec3bb8521beb7770b2e56a090c8b6b839f3e83bd205dc605579c9d47d2c4d0210d3602b66ada62b52b3ede783a22fb3fcef035eab2643abf14855446c914d9044295a28ede9bc05874242128a5cde6ff181e62741559c82c21f226786e9bc15e7445eeaf6ad3dfef6f594", + "secret": "c6f5b841199dddb0851db132e87cc19412b4ee48a651e92f956713620f26383e2bf06b7c2afa7584e3ac87e6eeeb557a3716d420dd57b5f4fec6ead67c5f58b8a090c8b6b839f3e83bd205dc605579c9d47d2c4d0210d3602b66ada62b52b3ed1f8ecf735a599369bc587799d448899e0bf6056176a027dc06aca4eb2be9e60810c3ead1b26c89136c9c82434699bddad02908024c959c55b9f9d5112882b777128a5cde6ff181e62741559c82c21f226786e9bc15e7445eeaf6ad3dfef6f594" + }, + { + "public": "844a0b77f7ed8e801713eb8247d436ded3662879852cbe352118c92509e1733ea2739ae684005634cfddee99868d98f394cc82a3e0812603ebb405820a3def9a88844c479dad4df327b65bf8eaf4a1daf44e2421fedd6cad66cb6853fef99e3e7a958e7aea3a4839dab88b81a58069dd13add31f4de0f4fced5b7d89972f3cef", + "secret": "c1ffa1c31d9acacebf6d3b88ae282e31f1fa0b89a30bfa306cc906a4da5093fb4e17180dc83eb8b890b01f1cd9acbadf7c4b12cd8f9153face0aec17b42b5813a2739ae684005634cfddee99868d98f394cc82a3e0812603ebb405820a3def9a40ec5ba532185fbe30b73a7e5bf0ad13c9e6d174c89316ddbf300f0e837f0e100dd5878870eb28c411dfca06e6500344bf35cb7239423b1c69e192748fd78b9d7a958e7aea3a4839dab88b81a58069dd13add31f4de0f4fced5b7d89972f3cef" + }, + { + "public": "4ca57fea7817c6f7d55cb42784658c980d5966036b3f56020b03ad95190835b699933b8db3c660fd89289c36eadff693e44308b5b80093a914600addf7afd05473d2aedc002bb9a325df91e98863e42aad8861919b563148da773e78a23b15c0cf4cbe5f32de8b9a086c6db4af6edbe9a29e1ba5bf6fd6f8f8bb6258533efc3d", + "secret": "6699954cd479b5f69063becc78adcd5d8d12bf07f8b5e69b22a66f3df1667618433c0b52a1bde8248638b945cdf96ebbd2f946b10a759b2d520331167aab195099933b8db3c660fd89289c36eadff693e44308b5b80093a914600addf7afd0542ced0ce3930515132c8f6e077c1f83cde18ea93d33d05e278bb25704c1e5f5ae0eaa4ba180009ba425512578f9d4d3d3a24e5856e905c847fba3b083ee2a9a12cf4cbe5f32de8b9a086c6db4af6edbe9a29e1ba5bf6fd6f8f8bb6258533efc3d" + }, + { + "public": "537e63e1de8d3146410c0fc1df71fc126f69e0b3f780b341300d791c18054f519a854462623db0babc9d8ad36f89bd3d6b25fda6d3f203c9ded913a32feaf53fadd803c1abc8f66396f26568ea70b021228099860c9d1759aa19f78160eb514bfcfd362b6728454e8a5b5cc292a619484c109a7597545872bff227384b96acf1", + "secret": "a89cebd4eb36342b8d77a354f529aa5531a7a0785292cc7e2b26506ca6faea48f9b574265aed1550496c3d81a31e13cfb8f0e748eaa0155c52c4ff8831d2b9629a854462623db0babc9d8ad36f89bd3d6b25fda6d3f203c9ded913a32feaf53ff3c39dd65dc636b9da8028c5e11628bf220494720135406d3978329bc0ade349a9299f456c84da3f5bd9dbcf2e8c7ca76fa7ad44ab585f8807ee635fb9133981fcfd362b6728454e8a5b5cc292a619484c109a7597545872bff227384b96acf1" + }, + { + "public": "63daa600623b0727f8c0fab5dbc21e0328840d4a2fe15a7d3db8606c4db1bb5668510e3a47ed1f0660cb7387be7e07b53291901ec7f23df2d4d55a93eb0f2a11182de6cfc2bbe587ea5a6a658c1a501ee5273930e1a7f92e20d8c3b57193abb4e40acd8b0d78f212b0317ea459193e43b41f3400743b2f0a023415a3b5e977cc", + "secret": "aa43e19d4887e19a1f798c50cb2f665344a95d2a735f4a241d2bf0aa0c20ad27a558bcb60c45071d8a3178787f1158685bb5362ea3e2481699a896891a4441d368510e3a47ed1f0660cb7387be7e07b53291901ec7f23df2d4d55a93eb0f2a11cb49db5f25369787a02234c61ab63beae29d086dad6080e2d63ff06f3293c6323eebe9adaa98f1210e88fdc0b8f56b81a487074aeff0771663f4006ba3e2792ee40acd8b0d78f212b0317ea459193e43b41f3400743b2f0a023415a3b5e977cc" + }, + { + "public": "22360c769ec9f20a0ad9499db5ebabd5dc91ba7894bbe92775228524ade01d8fe1647cefe92990a4215aebb9e39d3c04b87a8d60e1e87be4f7d247cf2395ecfe61043acdc9cf7b46080699a12c9115f0185e56ed5bc33e08ba970f17965621cf87c5d611181a231e952d6992ae5f5d18051b869467d3a964b278bae363b29f51", + "secret": "9b38bc7e846f6df6fa5b237d15d558a000e43a44962c4ef49a0e017ab71c1d3fb9121e8e222f35ed2691e24dad324369aa0fdb5bb0dc10f82240086b1dcdce46e1647cefe92990a4215aebb9e39d3c04b87a8d60e1e87be4f7d247cf2395ecfe51450cf0e94c14d62e5f5c2630a17f42391a0087f624e3a11faee4e4a9bbde74503d136ecf1e04dd18d8efc2627eb46b1084a27f142add9d37f260b8526caaed87c5d611181a231e952d6992ae5f5d18051b869467d3a964b278bae363b29f51" + }, + { + "public": "9fc0f5c385512f82b31fe2bfbb7491b52a178d82998f976a7a178b455cdf73289d217d402e2596626d5be566a020f948ef398785c44315a587861fd1c6e8b4c6339eb69e08a5ced05be1a41005ed2c6604719f3269156bac28b84805331d76d2a29139bbdbfec664a451250440b7f04c39f58c0d5bc4338658a1f0bb16fafafe", + "secret": "ae323b1e2b2fd9e5fd2ec21daac1e017271e8c76c1430163d757e31016bb819e8b4c55cdfddfbf15bfda610876c148dfb96e2b1f45f311a66f6988e6ef5348fa9d217d402e2596626d5be566a020f948ef398785c44315a587861fd1c6e8b4c6a7c56f1de8b2852b1c9d55e19571d345cf803c1ac4324807fee55281adeb73bcbc43dd5714518e7f49e000ea6587353a3da8c93e53c5534b8c550fcd6141dce9a29139bbdbfec664a451250440b7f04c39f58c0d5bc4338658a1f0bb16fafafe" + }, + { + "public": "fefd03f762f4cd277966caac9263465742dbc7d3556c1221bc3156596a45ac5673f71e0adf0233ea00470aaa2e72e0fce6c487e7bdf0b0983c7b0e8f6fe6ef95a1f2e160c81a4c7afdbe7827d4ec63e30b2ac93f68b377127599b9c961e3a46fba1e5a11e2775d591e9e2b4f4d2df1400986b8d42075acf4da34ca441ad37fd0", + "secret": "ba860558170072240966b9c0013ab9a2dc155700de3a84a8832bd5e358be59af275eead531cc900109c1de0916adb0f97d22ff545f0e6b709ff9462259de55d273f71e0adf0233ea00470aaa2e72e0fce6c487e7bdf0b0983c7b0e8f6fe6ef95d2c8dd675c8cf14cf5a69fce5ad29501c8180a5f77d19da0fb8c5ff6a176d0474ce2573a34344db0dd778049a794797da0ff681b60a8e3b5e4e369dbbc790976ba1e5a11e2775d591e9e2b4f4d2df1400986b8d42075acf4da34ca441ad37fd0" + }, + { + "public": "87483f2d2f35e6c7357e6f884b7aedc405f696f0f23e7e76b1582ba51f826a5e83ea4088c26ee3d0a3489d7650bfe7cd2d378f50639e97f074681194daca8dbf7e1774fea005a4ee96e48e4af14d51e96d22980867d14993b2283b1e6b20e5ec899dc49cc1f8d005e2c98735a81eaad42b98c36566b21c958e032c1d4935c780", + "secret": "41acb1a61bd3daa1b71bcdc31bd5dd8891146d2c59288f27acaf0180a02d048568375d57f27880e0796365d04bde37dcb517863cdd7eac1b563ccc85c2bac32583ea4088c26ee3d0a3489d7650bfe7cd2d378f50639e97f074681194daca8dbf4055453c760e9463b6c30ddf9717c54fbb9e2ad0d782da168f82c51f8ef32010dcec669c12cb66567e79df33f273b8100bf771a2593a88afc15ee230dd608f3f899dc49cc1f8d005e2c98735a81eaad42b98c36566b21c958e032c1d4935c780" + }, + { + "public": "6f9f420f372875df0ddfd848533bff68bc6fa3f2b7505172d691a74571004c32b439957e8fc56b17bb22e3a7d8780d913a59de8c339c1464d28ee5875f1018ef297ca59663fb1dde398f12eac2bbc732d683f54819df266c22e7e74404ff8006fdae1f55381b1991c37dbe7043476a1aa8c96ad79f6f2c5afae3f1e2a57c142f", + "secret": "d616ff9f65791e065da0a018287ffff6ade0b5b0ecc1f9e088190ff9bc4cf89314c8f613dc66a84d2a7c476f1c14439ae5cb7f46e36ca8ae956f65bfb4e8fe07b439957e8fc56b17bb22e3a7d8780d913a59de8c339c1464d28ee5875f1018ef298907936bd459273d6b6335d32c5202f9c8c32c6d3282ee530d9ea93d2c694500a6903283aca7323f8bb932c8f846efdca1b5b74ae06dc7d0cadff18e1fb8c5fdae1f55381b1991c37dbe7043476a1aa8c96ad79f6f2c5afae3f1e2a57c142f" + }, + { + "public": "cb426f6c082b1fd44032516286c4db2054a317a4a21fe1230d7ea2ea28e054ab4258825b67f36a4f6929fa2c765fd466a597d1273e64ef7fb233c6024babffe91218c8c42f70fadb4814af10c7f45dbde51985ab7c83ed366fad130ef87c043c6fa2bbfaff849a497329374f639d09c2a76a132ffa171464b820867cc79ac892", + "secret": "c96c68bfff0fc23f4e9351cd1fa8de2fee855a16eb1cb2980f742baba4ffc2a9d6ee4a817b2722c087ed85b030bb30a13deb6398c86772bd70904ef2dbb03e3d4258825b67f36a4f6929fa2c765fd466a597d1273e64ef7fb233c6024babffe9e92274f4c357ad6988a4cc845c3e61cdce8241249b665b0237545fdebd3f04e9ea6522023c609413d9d9853a53d2053c88cfc786b5d6816a975d27457477e4e36fa2bbfaff849a497329374f639d09c2a76a132ffa171464b820867cc79ac892" + }, + { + "public": "dfe4ce416fea53dfc26d2a420c667afe00e7e85c6667fd8b0618a7d7f344408cbd2025a87f06efac6ae4bb44d0d9af6e6f35ba3d202150b56acc962fd24de3211fb5070befbf0c663aa5e4f561bbbbea65ee87324b56f3f72fcc646e282c3a7a0fb5b6ad56c9b6c9de80a15c85e3ee4a3b0e43c16ac3c317b9829c3ad126a6bd", + "secret": "ecccf4385139845eff546d7e4dc123114eb6f482cada90aa9f6d7dd4e5f6a34d764217e16a50519e109eb74a55937df9e83c41c1449fd19bb0ee6bdf61658c61bd2025a87f06efac6ae4bb44d0d9af6e6f35ba3d202150b56acc962fd24de321ee3e4af91bc789b255070e6f0637b3fb8a4d39c5d422057034f2783e2806f04d3288998a2b2019ec40e3ca63759001c598d4761d04e0c1d8dd7a3d606a75f1470fb5b6ad56c9b6c9de80a15c85e3ee4a3b0e43c16ac3c317b9829c3ad126a6bd" + }, + { + "public": "ace7cf700ea401dd9d741d1ee91b7cee5c666904c5aaf0de4e2fe840087aa19b6ad114caa13e2867827f041483a3e4a1dad91a2b4a61c46b796855f6f8a20cb6b8ee06cc2cb062067cc4411b1e04c017acd314e6f06217e04482a1010c6741a41c12fdd3384e45a329a9665de4f075d8b203342e2d3aa459bfe4cb9e309b0435", + "secret": "d823b0a60074a1aa97c1bd65845c15b7efc9910215f170907d536ac096d3f8207d1d7fa94f960f71f9cac7a8a2001d5f3ce95476363ec5fb77366359fe5f74d46ad114caa13e2867827f041483a3e4a1dad91a2b4a61c46b796855f6f8a20cb6f76b8f9733410a806b9f292d4966474e73fc20d10f0f5079610367a79b6d41ade9171d9f01b9447b27e4d2becca07ab61917e1d9384394b058900f35f250f6cf1c12fdd3384e45a329a9665de4f075d8b203342e2d3aa459bfe4cb9e309b0435" + }, + { + "public": "5d18da3a745dd9c8cb7a91d6790ba04e4c7ec316ccb3931a19e45cbcf543edfca9b08e34f2d2aadcb01af6e2480947de527fce8375a4936fdb3dd8456314d786a0bd6c34cef38c66b864e4dcfc50d1ffb196dda878a63cfcb57bf0c349baab4a3996f1855256bdf3354395eab69f45a80b701c0f53831ad3a4ee0d1c9559c225", + "secret": "a967845e90cd22fb8315c82d85f33abd93f2a60f49c4c1cf90c3821f336b8ef044e4e94b6ac7fdda09de43919b32bde53c95b5b112f010762674b8f64e28d23aa9b08e34f2d2aadcb01af6e2480947de527fce8375a4936fdb3dd8456314d78621ef89d976bfbe7144bf8df36642df90dc9bb5feb0bde9d58fe7259e4e66a13e092c30a7f5b935b93ffccdabe60331f4861bf8b4859b76f21635852cb54bf3943996f1855256bdf3354395eab69f45a80b701c0f53831ad3a4ee0d1c9559c225" + }, + { + "public": "c64ed7875dbf453c824bfe0b0ec817ca3963d3c3fa8b1ac42e7db863d3a9d4dfc1660e7e731ce8be17a9ba1b3818e1390c352da215a828fa717d30f9826b1859717630ed3ac9d29dd44d7f800c7e173eba427968eeae617d16d61400728c91edee387124b2b66eae5abeced5e1c97b44ceb966b0026cdc5bca8645af4da1d55e", + "secret": "686c58284b069a20cb104bbb6454ee48f380a6200a8ad63f8cf8662e60c1115fe102b3f305f28b773e4de908a0fbce771de4434bd632239e49b1f7c4cc302f50c1660e7e731ce8be17a9ba1b3818e1390c352da215a828fa717d30f9826b185921c75e52b8fd11062fb933b451624926c3b8fa9769e2d14fdd74f528166730156df6ab1164418e8812777703ce4c1f4be2f667b04e2d30e0f5ba33ac8d8e5d6fee387124b2b66eae5abeced5e1c97b44ceb966b0026cdc5bca8645af4da1d55e" + }, + { + "public": "ef02f3cc08510f77fbd11c3025192f03135f15039d1963d24a8ead7f38525f0eee90e2353cc9a2ca2d8c0da955d326d030703aad88ad0b60c5eca04671cdf4b693d0b402c501b0a62a4c7abc32bb0dd3240456c3ab1563c33dbd763865d2b58b6102f6bfd97c9f2d105235bb56481aa83ef377b4c4368b2059eab2f42581f10f", + "secret": "8894bb0aed1615186c3486054072f83599922d5667398deee46de42ce2c1c3acb63cd87237fdd68581c8eb4871b4329583264f9374598f49ba0550c9051a8984ee90e2353cc9a2ca2d8c0da955d326d030703aad88ad0b60c5eca04671cdf4b6043d299e12bda6c9ecde6c206129640811b378de32d11f8c6be78c1225e426958a517cbff169f9a8dde5d7ee0d9887147934898e678e23fe6f14c929edf8ed706102f6bfd97c9f2d105235bb56481aa83ef377b4c4368b2059eab2f42581f10f" + }, + { + "public": "67e6bfcbe52c0fb830d4e252a5e834ba95a98b7cb67b5bedb010a0f7c8d30d73a4d4e52420b04136864a51182ed220a577e490d198af66b4dbddb3c4e10012da8eeefa1120caa3d41ee0f2d1af9b4f49643cf188beabd6fef852a12d318224e57902fb3085f3b835068fbbd266279a79e3480ac97dc7f127c9fc90b7af10fb33", + "secret": "ec75c28f37c1eb692364a6c478e0c664c2bece0cc1a9ef04b1013c5bf0fe482c85b1c21ecb6f1448f9c3b9964293ec14e021813dad750537421e108adb4ed115a4d4e52420b04136864a51182ed220a577e490d198af66b4dbddb3c4e10012dab077033aadf77f3236a2a7da2b20ef710c91fe82fb1175fadde0f42e223c1d689e4ccf509e1016ee2013ad1f1bf806c599c468c55cc088faf5b2896b758b77d47902fb3085f3b835068fbbd266279a79e3480ac97dc7f127c9fc90b7af10fb33" + }, + { + "public": "e8e619dc9b470817a2fcb3032f61a01cf35610ffa247b3cfbbe32aa0dcf6478ac739f4311853f5ab9a60021bf3d6f4d7ccf0ad36e21ecdb0ed4c9d4fc2bb5ba1216e0a3919c8705c3c7f6312e9ba13296d81a36408f8af08283095438411d5a70aac7ac4a553a7e00d9aa1050419f500e3c1fbe84171a67c91b9eabb66823139", + "secret": "c25f429c2bc8d40449665dafa6e167950eda3bad5d9354e4d5054d7ec22dec65dfd0e18fcc561d6f3ebb60cd8de81e9924d6b209674f97355df184738e9d7312c739f4311853f5ab9a60021bf3d6f4d7ccf0ad36e21ecdb0ed4c9d4fc2bb5ba15853f12bbf3cd6a934ca08db17295ada6d7ba697ba69324c0eee6e2b562f139edb23c7fabff2a6c7bcbab408856ee0110902c0fabaf7eab586f0353ddc0bd7e90aac7ac4a553a7e00d9aa1050419f500e3c1fbe84171a67c91b9eabb66823139" + }, + { + "public": "5699e7d166a6a1703489867f59c6dd31ab83a311e1ff0cfed2f1972ec9b419f7ee78e9ef2c80ed693089db294f2d72c1c278d1b483dd469ac8f04a9b64ab566ae4b15c2f2353a8729fb052a4c80b73711045a27edb34d448681a3e66b6e3cbc8f0de60c7caa783ef6468ab4873c7b3145fda558757ab7d6871414e83a7fb5f44", + "secret": "86957af85cd8da06eb67eda66a0565a2792cb403bb8957eb4476f221a11aaff0e726876beb796d585e1e0bea2182a8001a0c855cecde03eeeb9a53e4e56ff789ee78e9ef2c80ed693089db294f2d72c1c278d1b483dd469ac8f04a9b64ab566ad52c52ba0912e8bd2ef6de169a201c31f9f905193234441a8ed07e8cfdb60d461385a4f386b2342cec10455cd2771baf8004227fafac7302ad1c967d762d01f0f0de60c7caa783ef6468ab4873c7b3145fda558757ab7d6871414e83a7fb5f44" + }, + { + "public": "af8539775b2bd982a7d14a6be3b3269101235be6ab87cd10491112455b1db661b749340025f32b9b23499137c51cd8c59194f6b606bee80aafdc1c73cacf10a3b92ddcfde05042cbfa3ca009f205babb96d8f11a308373b8d0f87c63790019e703744505886d68edbaaa7b00f8378b85f8456a947f3a990dee104249eeb66191", + "secret": "3fdabb9eb716ce2a59e090460e62fa67d36c30f4207c3824d2beb9109b1c9561c9fd13868a9b832093c71f955f30dffb6f52c619b6f8ebfe0679d77948db287fb749340025f32b9b23499137c51cd8c59194f6b606bee80aafdc1c73cacf10a36976be78ef9577d98a8434d4dba9a3f8b8508344319b7fae78eec430a05b405d36c65beb3119bddb9783b5ab3b72a988e0dd252d08537f152a96efa6d9b449d903744505886d68edbaaa7b00f8378b85f8456a947f3a990dee104249eeb66191" + }, + { + "public": "22b2b8f5ce84c4f10f3a2b69fb4c512831f66eb942c865acf8a7a8e6419921c21bdc5174bffd5f09c6451441df2a6487ec2a013a498d9751f752af6805f1f85d4683a23a21b21c15aa3791299631231e0d270f6f6be33ad243be9686f7febface62eba6a78a7e32317f8d31623053e846745c26f86f6e95afe002a63affb5d05", + "secret": "db498d334e5ec236e7c7b85d7250c46f86b7ad53cb02d030bcbf6cfd2788c5fbed8631b15d1b0118808d8adf65979acd469556c9edf1e8aa3946bb70e50455511bdc5174bffd5f09c6451441df2a6487ec2a013a498d9751f752af6805f1f85dbc805ffc87ba7bbb9a7e12ac432182fa3e7dfcf97391552b2dcfd40c3b50f9f2840cd8c4d317cc3df3a8a0ec70de333ea0022ccaf7ba39bd2c98a90622176d3be62eba6a78a7e32317f8d31623053e846745c26f86f6e95afe002a63affb5d05" + }, + { + "public": "7de9fad0f50314ff05aec33f4563158b84df74fe1a1c68a0e613122c493a19e97c8668c81c23efe230143e1a32b7a1cfb11453d4e3ee77af9181278c09ee2d9fa947a8ed68774603c72cf0ba54cb4f7684c62c24845f343a9a7adb0126b041e850de685dcadcb4ef764449764025eef4e74cc988522cbd24bfa506ece7ea0b57", + "secret": "acb6c9f1c3ab411af1fe5f6e77fda427e53d92f0c4f6c2867842592ffca236562a17b9308044f2f844a056d25ef705344ae290ee57e41345bbc7411044476ff97c8668c81c23efe230143e1a32b7a1cfb11453d4e3ee77af9181278c09ee2d9fca59de7c380316b04932ed92dd1090ba57881dba788d5a4f3a6c91c2cfe65a836a6e16f81b7ed74e346ae6d50164bcb191d2b4e4951fdd134a2b01ca22cc007c50de685dcadcb4ef764449764025eef4e74cc988522cbd24bfa506ece7ea0b57" + }, + { + "public": "6570fc392082d8e30faa5de8b90e86e1b64069294f94fb11d9b7765ac7022360f2a3e7b94fc638461e7c0cdb199faa4ca161eb70ec5bf22d363d2fe6cbec519d1f84a53efe630799874a1507ffbd1b2ffebc8aedbb758b4230c6783b34a17e1d7e063dde9817a57df052c715de041f92f56e4996bc3087d8c2bf514488b444fa", + "secret": "24507eb7fc4862a685d1f5f7a403137b5efd63a39eed84b08580182283f2b8237a0e78cbaec20dc75519eea55b371721152e10b71a9100e4005a574b4c55e355f2a3e7b94fc638461e7c0cdb199faa4ca161eb70ec5bf22d363d2fe6cbec519d294aa991744df1d0f585fe955cb4aa025582e832476131cca8b6179711c53ea3589ce0309d756a734d3c7f2188ca9b978cebe71c41f249ea45fb2dddde5f35ab7e063dde9817a57df052c715de041f92f56e4996bc3087d8c2bf514488b444fa" + }, + { + "public": "1ba8c12b02fc2b33a7317eda88637ffcf0467778e06ba970cec9644365839d0b91a4aee5601d989e06e7c026ac3ef84f3cdbf666da42e1564c53c7163035eef0a63e011e2fc98da0d6b2e5aae3bff2552f4803863cb5c44f5becb7410b22b639f48f51839113ebf8bf162c31f62755851e3e6e68f01858373d2e13c7b5c96c48", + "secret": "a04dc93246245c9bd47139702636e64795af8639965c4bc2d76d7362064229058fc951af6277c8518d562f3bdeb88cbbbe6c5ae8cd6ffc290aa1076a29c32cfe91a4aee5601d989e06e7c026ac3ef84f3cdbf666da42e1564c53c7163035eef05a968b3fe366da47d2dae8ee418b9fe3c91eb5a9a49e88b47b88b6c814676ef7b85eccbd7f2da7ac064f908eafd2588686b4010d60973f426780181dc0055ae2f48f51839113ebf8bf162c31f62755851e3e6e68f01858373d2e13c7b5c96c48" + }, + { + "public": "2127f4ded67b981d6e58ccc0cb1190d5354e9505d0b06034a818c76cbca4d776779a047152edac6798c9ccc48b277c599b7306047ad56e33b72d9708d9bbd3cb79571e9a409c10ea0f89d8b4e729bc94868eb2550b807fcf0830f3487a2d42245c82077ac19e39d7ad1a71ec288deb87f2c3e439e1f9a3e0ece516ca1654db2a", + "secret": "7fb6b460e4923fca09cc1b4ca805ee6a4007a324ff3f19b96db535055534735311044294a75063e98f70cce6a932394f30b70688cd68a1a5a2daa05459cfd241779a047152edac6798c9ccc48b277c599b7306047ad56e33b72d9708d9bbd3cbe37f961097812bb8f712a8ff452858dfac6365e5d7026ff09e929ebcf23b2932d5352593444123d6bc6b983bf8c5b569654befac2e53ee678f0747e931dd33de5c82077ac19e39d7ad1a71ec288deb87f2c3e439e1f9a3e0ece516ca1654db2a" + }, + { + "public": "df2c11a924eeedd1198c1a6fed0356ab594c49ffa6be942396ffd4c575f403488a7f87fd513fe299f02e9fcd768960a32c2a5f85d5c6ed7956ee5518bb617010fa1e7986093620e0a1b764697b616ccedc4763bd72fc8236f4345e5dea757269732190743553ecfb5f09eef16b6d65ee61ca1540ff540b66ad261aa27b8786f8", + "secret": "0f619677f802c99d644fa872744abadd629ecc179c5b9d51dfd475b9bfdfe29037f4129f57a5058611d37eb73b1fdf1744cb3c3f6fef86e984c526f57df02cbf8a7f87fd513fe299f02e9fcd768960a32c2a5f85d5c6ed7956ee5518bb6170100b160aba76c228ebea3af9fbfedaa84f6739b96bb51fa97020cd40412caf9bac1e7f37f3eea5da60e1f1e3aa560b7499fa66d6eb5fb1c652b5a680a8f1e0e151732190743553ecfb5f09eef16b6d65ee61ca1540ff540b66ad261aa27b8786f8" + }, + { + "public": "b97184a6f491f566c58f310b97d1995592a9f93880d3856e5ac83965ecf11d04bedbbdcf6392c05eab29c7717df222e07c6bfd34030801a0481a819a3d675a67766aac8dfd2573312d3ebe3b4e6336e1f87c02ad36fe348d0a6387d6badf11e656397b41bdc37027bb7943690612d0dfbb39afe9166f8d998124ff7e0cfa35af", + "secret": "265c5d9e43d564b71cb19c0430c63c5318d2d8306bb1d8504d278aec134102c9aef7b4b24ba2ca0e054bb1844493e4ec78c96c706764c0be98a4a11cb5720ceebedbbdcf6392c05eab29c7717df222e07c6bfd34030801a0481a819a3d675a67e9b769cd36c1396e5b695f8015839b3a009a45dc7315d1dd08af06730a060cec79a446d82833f4a154883346bb32536269a07a4dad3a01da1abdd8a9f8a7b66c56397b41bdc37027bb7943690612d0dfbb39afe9166f8d998124ff7e0cfa35af" + }, + { + "public": "8cec36381bc13b52d726f963e618a176c166b5b15cae3ee0a3f71ba38044b2b0ae950a44debf5a9a0ed074c865f93f92272b20cdbfd37a1c56866098f0a742345cd4179649dd61c8c7d5be9413403848094f3f52747494fc99d5bcd0db36360979e8e7d154939367c958cb1fb8715d2b4ddd1c899939ee045a62294eee36dd90", + "secret": "11b40847fc3bbee98b8df5c6e566782bf4a145f3124a4ddad3c35c4bb1e993e058ae37ff7e23e760146e47d25a7fe57033d17481b1370fd50bfdc950756d0e2bae950a44debf5a9a0ed074c865f93f92272b20cdbfd37a1c56866098f0a74234f15d5882dedef72e08969635592ae8e5e2852f0021e26bbd9eb7192a224ff1d7f45b8ea0f58d99dc6e0c1ac686bda25271ac083599e865d29643eed96def049579e8e7d154939367c958cb1fb8715d2b4ddd1c899939ee045a62294eee36dd90" + }, + { + "public": "36f3366349657acd58d935406fcf630a96399e4445e70fad6feb85ef27e22db791c840584c9ef00baa5f406e9f9fd138035abe9c8e955d7180f44673bd22d86a0fe95b217463f1b352e22e1f54eaa9abac37f6f173fa21be48ee285cbcf734d973f0459e7894715d3febaa681b8224917c106b52f752db326a09aae24d12ca36", + "secret": "c82b5ed3b72406ab97f95db66de33df70a33d2ae71699fa62e301a8682cf2dd7db5dffac09510d88fbe015e178e15e2abf156a27d11d4fb20f23bd0dcc92608191c840584c9ef00baa5f406e9f9fd138035abe9c8e955d7180f44673bd22d86a706941edba62ec1a54c150cb67e35eb5be0641684948b60d917c6380397b0eefbb0dc73b25f696b5c55bca9d65075d485bc135d522ca0168de955cbeb982449373f0459e7894715d3febaa681b8224917c106b52f752db326a09aae24d12ca36" + }, + { + "public": "d585d2a09c7fa4a211d3289e46e503c8cd72b9cf29e8f17f12da616806eb1c03f581c805631564dc185948530bce9689a489c9b1d7e1bc5fdc92e8ec8e5cb29710e65b7dee7646fd56d1d03ad15acd181c7e855cfee87e10ba89b40b6214b8b02c0f85be420e27e8966066877808f9163743ed0410814805185cf7290efeca74", + "secret": "fd5a458427cb8b90dc5167d703992a83680ffec3bd3f5717f4ee591febc2550eb1491590a01d37313a810fcd67df04497f0fccdac66c8184ea3fd1c20cbd91a8f581c805631564dc185948530bce9689a489c9b1d7e1bc5fdc92e8ec8e5cb29772f8bd65f2f52cdfc41201912d7dbd13b53a6e643b841bce81e93f3d880554c7800448f21ee5ab55cf4ea06cfebf3dd42a40b6ce5ded8a6cba00ef93e1ecd16c2c0f85be420e27e8966066877808f9163743ed0410814805185cf7290efeca74" + }, + { + "public": "ea23a0497b49385f61b8608c39b8a2315502e094e23eb639ff2908b12211f24403edfc943c855be6ed1025159f43626f4ec3b5a86945fd26ecb65edf5ecec79114c8bf9f68733188cb11c9ffa022a2564dd4a56f3c49eb5d89a8402ad178ff417845a1d1730d2886047dec99ec347be3f23c67edc383da012fba062cf7e0cb34", + "secret": "5faae616cb6c8ae34590fe1f01e79a4a2891602ccaa3556b718bcedb645734c16ef12efc7b143feea2142cccd61910b65ef82a99d2e6892911ef603c38c904be03edfc943c855be6ed1025159f43626f4ec3b5a86945fd26ecb65edf5ecec7910c9bce4ab66111b775081eee26196901a9ab5c24c9374c5240bfa5de9fd68f68b33d30b3f3de10c1946393405e6ddbaaad6cd54f725e81115690fe619bacecf87845a1d1730d2886047dec99ec347be3f23c67edc383da012fba062cf7e0cb34" + }, + { + "public": "ffb7fc958d15edf21268467564f911722e690d10ac9a62f92b6d6f239faf9d08db4ae702fb75f1a2ee5f1a2e3b0ce9ff7d06c11ac3ff82b0bee23155c1fdad321cee714e7f9e11bc0091ef58d649fb107d8ce0b3b808a6a5669af2a3af8800097cca54e4085a0167ada6279d7892022f9dc8c90e10ec345395dd254d4a75c2f2", + "secret": "3043340c9c966c79b584cdc9bbcbb20e333a11f0ad3bec4288576b3e394957ad80659d8fdb3c671653570212069c53d78fb543dad3b61a622eda4218e50dd4bddb4ae702fb75f1a2ee5f1a2e3b0ce9ff7d06c11ac3ff82b0bee23155c1fdad32262c500278829705eadf0b16e775b6a66c19963f6948700e22252d87bb1ca578caf6f883bcaea55934196423a42187c79fe918a594bd5f59e2f7671c9f254aba7cca54e4085a0167ada6279d7892022f9dc8c90e10ec345395dd254d4a75c2f2" + }, + { + "public": "4e7157efafa5a470eac3d4449e34d7e1db576c30d6081a13442d79f3e2ef46f176f6361098d2a2695279a8ba7eb69d818b5c5c1f6b984148117cb7a60cdfeaabd6a470d2562e00eca12c911815f3580523ca4bd30068b117c28b351af6180c7bcae9bf9ee0b5ca42a9b8bd27bc978d059460a8a96019a80e1315293f221b1738", + "secret": "bf641192357cfae70e7b7a94a0ba056371760dafe18708c9d539daa35b96d05fc2f443916cdb5a6f68f13b75f02a533be100d7d5b3ab3820fe22d8d93032b17476f6361098d2a2695279a8ba7eb69d818b5c5c1f6b984148117cb7a60cdfeaab913c5e1b36bcbd7f60e4f400dc2d11dbfeba0d3c535745810c5e9f6dfdd1db02ab1c295aedf69a9aadb5d15fb4f84117950f06d04f471e34b864f20370d4a53dcae9bf9ee0b5ca42a9b8bd27bc978d059460a8a96019a80e1315293f221b1738" + }, + { + "public": "44fa479905493c5f2d8af7876d1125809c12cb3ab67778f329e93abadc97514a27f78444e2367ce0d2fee0a54020b031ed473f04699b4daeb5fddc6163a89c7bb5b369849b2cdd9674ba80aad4712f198e0518c226e419d8a04c52d20ee83453dee186cea0c7b46e202ee328c92fb76a4861d732c53d5de631747bea7be7d830", + "secret": "2e7c9f4fce8e948a4886ec9a2f5415abbe4f91746224b491595834eaeb0c09d8c428fa69b885d5aed76c9c4980f9066cda2f323d9e3136c40729eaa94980443027f78444e2367ce0d2fee0a54020b031ed473f04699b4daeb5fddc6163a89c7b2099638d2a29efaeae117faa5f12360aea434955af9d783c20076e54208b00f0aab5b4357e9fa91a31bef02ad8ecf3a1808f10719e328f5a2bffa9c69739c437dee186cea0c7b46e202ee328c92fb76a4861d732c53d5de631747bea7be7d830" + }, + { + "public": "491de8a2c9c9f895b7ac62b091b2ef8c05866373127a822e143532f963130588e631b847f7d3f7f57d994750c713d94c21d7fe4e02d1d874ad3f1945766d5d46515dfb0aa6560f71dbe5bdc1c3a67f77f6687f8b2fd944e6f958bea743a01e4a4d3841065cb84b8a7f638fdc2f55dcbc3a61c0397901029e2b7625fa101a8bb2", + "secret": "7a5ca0a6c8025ab62914f650a636ff29bd8d2bd491d3cb1031318c8e1a67b621444659563b7c07f5d0510ff083b8997ba6cdcb54c84fd0b8dbb2054bf2854b69e631b847f7d3f7f57d994750c713d94c21d7fe4e02d1d874ad3f1945766d5d46d2eb03a42e824434591d2323bd3e095f3b297761cf519fc6dee6add1af3c0b15a2566e24789fb5e706e64f12e8a9c864fe54ee1649fe31fe056f4f1600dba9554d3841065cb84b8a7f638fdc2f55dcbc3a61c0397901029e2b7625fa101a8bb2" + }, + { + "public": "1b085fd59b01925bd55f854ea07cb55c35198dcf75ca6d30e62733e10c681f86fc24268ce6b11dc4678f58b7107ec9561bd96f94c8942edf9031d6aaf202456de460fd97c368c840947a72d6fae3f185ae6352184e4499fb5dba752f0f6632553d252a41393cf4956440fed549bb2645ff4b0371d4611beffb8d1f17c437f3ba", + "secret": "dc938983287935b8fd3ea6e2829594b43b8d0ee7e0eb99e71e234dbb1648d3afc088673dc29e845c7b38a97ff82f7eb0a7ad8591886e0076cad4ec6d4ef16d5dfc24268ce6b11dc4678f58b7107ec9561bd96f94c8942edf9031d6aaf202456d6c7dc320cbfb3ba43129e584c1d39cb3028536372f3259377f642f9e9d8e1629974f36d7c1606f802b3cc2eb846884a20b9f0ae186a84af180086793a68662bd3d252a41393cf4956440fed549bb2645ff4b0371d4611beffb8d1f17c437f3ba" + }, + { + "public": "f73654e3ccc6047cbdc4f43090e3b26f707835090ce4b81d1a6c7ff05fcc7687a64932432949d5a86bae99980289a776c9d01ad1405d5643fae3b753f32ccc46bfe0fe3bf6654b0c88c91908f8e40cd0874ae6dc394b1f0ca0a5f64123f0bc5e267ba2e6bb6eb3a939977be2a5a36d124543e971536dd6ffef613d5e93930272", + "secret": "14a2eaeab05a24a8b7815658cc06dfc3a765fb935eae8dec25e871b08dd5309005f948f832aadb6aad58576ff5a6f7c30b4b34b2c0949dc137da3d5ec6294fa5a64932432949d5a86bae99980289a776c9d01ad1405d5643fae3b753f32ccc4683bcce707019d21fadaa74431ca7c1709721f31b1c18a1f2758d641d906bc7e709912389428d6a460dc27ce3578bc0ce5596f53f542cc0f22e58722d67bac807267ba2e6bb6eb3a939977be2a5a36d124543e971536dd6ffef613d5e93930272" + }, + { + "public": "938760a69afe2f09f94c9a0fa8216b103b8100fcc606a5e91742b0aac8253799f9982abfefd5f837f2fce4a4179772f97627f61811cd4c70d183f53ab995e610bf061e6661dd7a28bfbef01b256c002f447172e533051671163525cd9a63fc6400946a3c9fb979bb0954722ed29bb6b165de4de3bdbbe12432f694ccb60a3f72", + "secret": "f1bf4472c057b526d7a1810e4d818a7d75a31519170e04128c8db7de603de7a4e84fd84ebf9fd65252ebc38904076ab14ec630b9f5feee9ff9f6a96cc77a0b19f9982abfefd5f837f2fce4a4179772f97627f61811cd4c70d183f53ab995e6101a3af5fdf4cfeffd0d3f4fbd7b5f2d2819faad5208c9567e23870619e9a048d719cb3f5c3326aa19d56656b5252b55c12da68229ff67fcbbfd6725c149d59ded00946a3c9fb979bb0954722ed29bb6b165de4de3bdbbe12432f694ccb60a3f72" + }, + { + "public": "404eb9a4dd44439198591de9973fcb8f6869cc6d443f87dd72d50704fb766040dbf3921cd1f784499fd49809d8b0169570f09a4665ec5984bb410ab28b2e5e7484f27df9f3474d3a9a66a14d7a95fed07773205b5e6c7a544ee63ac2f4d6504d764a1fef7cbe4fcc9e1c03d59edc7090e500b372367229265b77ad6d0586fa54", + "secret": "202cfa79636dedc2743c9e16c49f04a03f546300a5197b5ab5dd4fe6a99d269c200a3d6db8a790f6e4afd4f44244f596e26d87a359f5fe7a3f6180a0621c5e05dbf3921cd1f784499fd49809d8b0169570f09a4665ec5984bb410ab28b2e5e74bd1fb07bc8c20a51d78eee0c44647ef4e5839e3e0e15acc0d69a047219dc4fa64f352e35ba695e8803d2105b84ae0aef42c8f3b232c3cefa9f8927846e054e23764a1fef7cbe4fcc9e1c03d59edc7090e500b372367229265b77ad6d0586fa54" + }, + { + "public": "58399bbc4caa3265cdcf11dcdc9e540bd16f2647ebd184a43513a83e5c5683138c7e7dfd3ca6ee551ea094e9feef61e2ab3b8c9c6e00ef51c85f2112edfab44808d09a915414c3d9e676f6a225348d8f892649fc8028a4e49af57a39b9c95fdb7008c2acb04fe5b2e62f6753a27f3742c3c0d847be42d1ea0c49fd030b0a0f0b", + "secret": "fef9cb6bbe02a72b51e032a01f18f6ab3f727ca9326f31c701cae408588eecf0226c860ef412633c59a09963e0d1f0c70da57f57c0085a17e47e5bcc7a3e40b38c7e7dfd3ca6ee551ea094e9feef61e2ab3b8c9c6e00ef51c85f2112edfab448d1a198fcf5108d0377bb7fc3d38ee78a40844e5692ff999d823e1bcf08cccaf21c438f85ba9cabff30869c95440691630d8419c5375bfc952136c449a85711107008c2acb04fe5b2e62f6753a27f3742c3c0d847be42d1ea0c49fd030b0a0f0b" + }, + { + "public": "f70899758f6a835b28012799396c49f82337485f29c5f49e698d18073cba32e575be9607ac7e60476389f76b51e71fae6b9cc04c5e298211178b00e4596bedb36c590d3b30b5b91bccf47adef1660eb7234b6bb46c60b504a48510b6685a9bc14515a5b00536de24cf843d6bcc4d9ff81dd36984a50beaa29cfae70ae5a3d42b", + "secret": "bdea9f83e4490934e9618d5ffae69bd447528ff33751e59d93ceb194501da625f33c323b3d4b59038775d126d25f28403d6cff13ac397ed61b96bf082b87844f75be9607ac7e60476389f76b51e71fae6b9cc04c5e298211178b00e4596bedb31f6b0cd6526761e9308fe29449503926134dab65d21c767948a0b985b2dd7492679308ada09b6fb0cd3a33aab7732f372f7647322408827463e15dfcd81e317d4515a5b00536de24cf843d6bcc4d9ff81dd36984a50beaa29cfae70ae5a3d42b" + }, + { + "public": "171728517511c5adc08012c023b1e3f2fccb370f64d73cd54023075e43c331d6c05870ea7cde07df9b6d0ef19ad2b9fcd4c2605f61fa68b395d24a176c42d120bbdcf4fef93e3ff2f854795e7c9d593ff7ff3f1deec3b2c760e5bc969c37e417844364c736d774aa7178116021247a5b9c4a18351f172c19f397c18c1ed75a45", + "secret": "335c48ba4c84df91cc656d723e1a76f08cc2ddec27845bc59763da156f08413abec8d24b3fc240634fb7f2af727f2d3662864d2ee8e9c570046ef971257ada28c05870ea7cde07df9b6d0ef19ad2b9fcd4c2605f61fa68b395d24a176c42d1207355357dd98180aba5b070a3a4d391c2a4149329582fdd647ffc1f16250ee9d75bd667ccb64e06adaf4cdfa3be8a81285cf33250ec653ebad007ea219b9a9d38844364c736d774aa7178116021247a5b9c4a18351f172c19f397c18c1ed75a45" + }, + { + "public": "fad5f4e099d5266f7e872ad5b7697d4e2e7fbc8ed1b717b1203ef03ee196a350cbbfe67fe711690ad52aa7063b0ba21e743dc8b90eef0c524af2c6c4a4db1f40dbb78c74c42a5875b90afd65454a944a60524edb80cd367103ecba0bd9d27ea59c7fea0490857f2b6978861cbe55ec23aabb2ab054d7172e050964fff154be8d", + "secret": "da7de9b53081a544d922ac97b63545f7eaa84d8868e362d578ab27c633aef35c8dd497b8d7d7fb71cd73ceb04448143f5da427e3df574377e1c7d94e1dfd2e28cbbfe67fe711690ad52aa7063b0ba21e743dc8b90eef0c524af2c6c4a4db1f40a72eccd39602eb1e6d00310256c1df6a2a62ceeb8153a25bb643b6fc799f666f566ff0aa62bc1360242d59ac0d22a5621b677e06074b6ed79da8b5c406b84a219c7fea0490857f2b6978861cbe55ec23aabb2ab054d7172e050964fff154be8d" + }, + { + "public": "21df657462542106308e2685da8f17409b8a3d619b65de9d6dcd27114722fc90d29b1c690a1265fbfd69f27f9e38729f5e79d4bc4b18c2833a3755321bc5e38e822efb36acd64ce5f5e6b898b7b222a9f4152b82f7a36a7d480c5e961f394a4f7e03e5edef7839582b6ca0304758b4ac844660f9842d6a33cdbb634b5d859fd7", + "secret": "40fddf63b8f7031c69ab8e217b548467ea6c9a150439b5c6e949ec9a5536d38384fff4a375e1032370f8b2eaaec7a5d42b59411f90b43ac07e783f2e461ca639d29b1c690a1265fbfd69f27f9e38729f5e79d4bc4b18c2833a3755321bc5e38e580daff60a2074e65d0ae92b41da077faab89458a4702aed9935b72c591cf056a893ee88168169f23439810f9d465d03e7fb26e98bf12c11b99a8ce2c80ff0be7e03e5edef7839582b6ca0304758b4ac844660f9842d6a33cdbb634b5d859fd7" + }, + { + "public": "4670fb49ac8284cce293b5d458c7a0e0c38cb3ec9a599902c86b44418a15876201c25283bfd2c23cc4d7950f1c94ae864eb249df62203d82a297068f58ba6fe35a439e45fc8d10313162962291510b344870645d182307c50f9d5f7a545436c1ab2829ce765c51974c506ae84cd6d7fc22c992d5b565aab0c11b5b84c279f6ea", + "secret": "acadf9457fe9c416623ad185aba5ee57f8c7d8be554fb8e3a875f47d8cdf36323c8caf64cd6d8ac807d1f09cc7b73f67a7687be4827e01252f96014cc6a07a9501c25283bfd2c23cc4d7950f1c94ae864eb249df62203d82a297068f58ba6fe378e635f2555d8bb2f5d6f6cb87cd749e2d1e2100c82a97e2f71929d7f163e4500c26e42d1750315735667b0885ca31c25a6b1f9bb0488cac5ed9415a98e57fb5ab2829ce765c51974c506ae84cd6d7fc22c992d5b565aab0c11b5b84c279f6ea" + }, + { + "public": "9d3a71a4e195eb29cbcdea3a063f9125cf5f1d89a6acbf59c939211a75b89b3c984f834c90dd1bea8b61c71952c9906fee8cd79eabd771eb04cac244247e1972b31fdcc15bdaf574e1414ea736ec6ab0a9ecc3adc73d7dd4764e0f9df0cfe51280e77d6f7bb04144252e51f74773a34c71d6f0bb3ebd1d3e3e4858ce564c82ec", + "secret": "22f248a2a3d484f935a8d6dd754ea5e5316dc5ce2ba3e471ed791264152a8203345d6a84711b8594b86a52207d31a9e0a12aaf6738bbde5ab432a6a6e12f7419984f834c90dd1bea8b61c71952c9906fee8cd79eabd771eb04cac244247e19726fb985d33174f963d19bcaffc17d0bc2c15e3d8c85da3220541e2d688475ea9302db54242bd970e22d031576fba5e41e466cffa87f1ad4b20c7d46b04e342eff80e77d6f7bb04144252e51f74773a34c71d6f0bb3ebd1d3e3e4858ce564c82ec" + }, + { + "public": "de1425f55531a1df354f4b9995174ba588cc1ee6e16cce72e43b6a4317ae928c0d117fdb5b26cabe37e5809c3b1b6813aa6bebedf870fcf0582e8b6fe6359a750a33211a618474ee02ebf8db16c178bd9bd958c897a731c0f0d2208644bbe5164698d3064b0e3cc966c28e63218ca956cbb8db2b7afea1862adb1f45f73225c8", + "secret": "947f94b323449fb08b52075c6aa1efc957fb0d86540dc3a88acc6d2479b018cce6013b6bb28e418344982d573589c28ac6d7ee27ed8fdc82ec22c1c2967203470d117fdb5b26cabe37e5809c3b1b6813aa6bebedf870fcf0582e8b6fe6359a7510b3f1f55910fc9fb1fc54f9c6d6215416a19b3670a419863806cf5a7eada3d63a20f5c43b3dcc05f83803a7d1e9d277a68624ee9eb7c869d31c920baf4c67174698d3064b0e3cc966c28e63218ca956cbb8db2b7afea1862adb1f45f73225c8" + }, + { + "public": "c0379e831761780ee6797632de6974fbc721e27cee066f2dd553e1eee7e385db289c796a1fa8eca16a7db0440571767aaedf3ef612493fe669e61e94c2ed22cc669554ad85e56bbe20991fab600abb9a068f40ea3f841444beabec0aa5946a895236cb1ea819ad943fd98ebdf3c1cc1a08d065c3762d0ab43ec481806f069133", + "secret": "7cddd56b1ecfc8865177ae8936b044aea89d24c676af375e3373be2de53ac94b2e452da46090c910b2920ca888b934749cfc78922f5f1cbfa3c5df126fc4d0ad289c796a1fa8eca16a7db0440571767aaedf3ef612493fe669e61e94c2ed22ccc7d63188f6611848f61fd02be75c0ff4576ff8711e67c8ea4002803800a737cbb2b4a68ed4746ac28b352fb837600bd4bac352e7c0b4fee6fd93f79811f190975236cb1ea819ad943fd98ebdf3c1cc1a08d065c3762d0ab43ec481806f069133" + }, + { + "public": "263a9576730d8b6c5c3cd80840ca0fa831b6af9ecd8b38e8916d6c1a05004c514976afa53b3e92ba0772ee0f846c63ded8e1de5c81b9fcdab12af0483027596e09a955ac0025ae9954dba602e2cda9e5b99700208660cbb450879b0d6e781c3cbf7263c553586d247400a90395f3c11560150010082d85c37338fb928bd1fc6a", + "secret": "8181b24f6159db6dea1d079bdcf1395fa5d751bcdb58f8031fd42a6aa71d54f2cd6a90a11663fd7c7e909186e9d73aa3b4dbd08827ae0f436b22c6eded48363f4976afa53b3e92ba0772ee0f846c63ded8e1de5c81b9fcdab12af0483027596ea2009f7ea7e6e543fb7cceda523c8b9781a1744247b4a093f7d6d06f4db95845b50b8edd92959c706c550b2d643bf2feceb2a2e1f094e5f1f8dfa1ebf5e532e0bf7263c553586d247400a90395f3c11560150010082d85c37338fb928bd1fc6a" + }, + { + "public": "a91f847a1c2248a63fcae2e5952c78d0e4f3f7c2453eedccfc966d8d473080124a950bed2176e7c7a0ed849c4685ec42038f4b53b127e70551ecc83328f61e0c50b37cda9a2d63495fe59fff1e137585e9f2928bc1058dbb2b77242949166b102017f5b70a6958f14c57b639b2aafecefda3b7629820a11abef4a14df88b7bdd", + "secret": "c2d9798b467e7f49478ed73b3e457c1858b02523b8ba5a6d820985e47a8a3448e287dc50a3be723078b9fc44c9b3b96452e06db6a1cfd56f7dd9b357642b3fa24a950bed2176e7c7a0ed849c4685ec42038f4b53b127e70551ecc83328f61e0cdef57398c3dbfd7aaf18cbf6baf815d236363640dadc7426ba662939855e9f9ca3ed4d39119fd600c66d01e604554cf6678174108da3be7be76e52a54de0fe3f2017f5b70a6958f14c57b639b2aafecefda3b7629820a11abef4a14df88b7bdd" + }, + { + "public": "b9d0996c2feef7e9f9861d366e227ad15dbdadfd024755c71093d5ded5be2dd61480b321a933dde96a3f2248eb4f1d2461b3fdd140ae6e765e5084f7a7b3faf4ee3dc7921f6b454fb75e17fd88df1f0b368ac11b2963084680e84f1df9137d01befbf2fda270ba9d658176218b5c0f59556890109d5cda32ca1bbf94886dc514", + "secret": "8516cddb4f8a720fdca5f98eb6627691b7d847249ecc7d44846c81202ca1239fe485abb155046922a8fde840859771647a27b0de6ce6700761cbbf1ce9f2cc501480b321a933dde96a3f2248eb4f1d2461b3fdd140ae6e765e5084f7a7b3faf49b14f3e2b7890bfc077df9cc945a705b5ee46386c28e0112561b9d1e2720d81b33d2e81e2920bf059d5db1701125b7a96076d5273e5e4247b9e0e3bcf38e557cbefbf2fda270ba9d658176218b5c0f59556890109d5cda32ca1bbf94886dc514" + }, + { + "public": "a32f8ec9c19f07872121aa74cb9c2540f97dd3927753f0ac52a18988da3b65f23a2d6942ffb35b528ddfb56ad50e88f4ae12276c0495c4233c8a64cd65ce58f7ed6321ea0ad594698147171d25e366ab4d1e27c32470f932bcca740f0e94e3c99f8e42a964b0c59265c2d55f20bcdaa866b8de739f1cc89bbb1160b2cbc88eb2", + "secret": "56d62f999a871137e1a69948cd92868420e93e3afe404a8bd6e2917f1809287aa910b35f05e1b0b6d7c69b9bd9b7d4644bfb62da4d0e6c74f6c03945427473b13a2d6942ffb35b528ddfb56ad50e88f4ae12276c0495c4233c8a64cd65ce58f76f52eb8896c0449d60574ba88fa7d044962cd6a04563026dd7d3570c3baf331dd87bff34fcf4e804500b0ce18a9d3358f3f6d1c78b821f05114062f6224309649f8e42a964b0c59265c2d55f20bcdaa866b8de739f1cc89bbb1160b2cbc88eb2" + }, + { + "public": "053cdb830bc0f4fc244062666127ec55362dbfc615f8b0317d0e87f5b617b0cba3998e902035142fe1ffd876d31dc3b887eb56e8c2914a3ad39191b24503c139b5e84bdf0adf0bb0ae1121081eacbf1a44bae9dc1e60bfeb899c345f74bbe94df682b8923e390afd0f02e4c2473a9ca1df35fe82edf068bc48fa59f7ae558544", + "secret": "e8961fb8953ce90f02abe433c8a806b2f986449bd2854b4f60811c4040409f5459e3cfa4af7b5ca48aa044526af3b2f0968015319786c686db0e114e018dd39ca3998e902035142fe1ffd876d31dc3b887eb56e8c2914a3ad39191b24503c139fcc851a2a2b8742932642662137065c9a4dfb4e0806255f9bd88c6d8d3100e556ebb381cf7c1d1b2ebe1b13e2e4e92f47817dd5afaf6b92f9be8832aea894952f682b8923e390afd0f02e4c2473a9ca1df35fe82edf068bc48fa59f7ae558544" + }, + { + "public": "dda001821efa5fd7f78968fcb12359113d4781b6c9a030ffbe0275947fed1f9ce022e1b1b681c7b49bd565cf5baef492ad29b9a2453694f557c8752093a933a346a565ffd0c0ada461653d9f08298e59e3be5a4dea42c043f54dce8436e047e534558f18fe9ded1dcf6ab1f9069bb94302f9b416a599600a23313561684086e3", + "secret": "815336d159b926f967b49608f06deeb377ac27c4edbf5c42f02fee911218e2a121857696a424488af4056fe5efd5569598b413e2c49a4c248d1cbe44991e8aefe022e1b1b681c7b49bd565cf5baef492ad29b9a2453694f557c8752093a933a38b10253fc85e90858be0bf37f42502de46856299ba6d39aa8b6ce0061f19982dcb4683436084da79234e92a29284ba82c80fb9dfeedafd14a59605a14e8c3fb634558f18fe9ded1dcf6ab1f9069bb94302f9b416a599600a23313561684086e3" + } +] \ No newline at end of file diff --git a/rust/tw_keypair/tests/ed25519_extended_cardano_sign.json b/rust/tw_keypair/tests/ed25519_extended_cardano_sign.json new file mode 100644 index 00000000000..b0731ed70c5 --- /dev/null +++ b/rust/tw_keypair/tests/ed25519_extended_cardano_sign.json @@ -0,0 +1,1252 @@ +[ + { + "msg": "", + "secret": "ed250655864a1e9a06213389c54b4c613ac81ec41b43f50d2e21839fa881535f1f2925bdb9bf244094d2884590971c126b11e10d0c91f49a34b9b90e54bd12defb71871ead7ec2cbd005010e66dcc4dcd9fe34a01b34a1c6d26027d09846ecaa5b5a54df16af9d9679b1f861da12aef0993f10bd1855670b2ca7933e34efb46fea3148c50dc26bd9f3b72ea41a1f0b058c1da97f0a7a156833fda32512f11a88cc58e676ade7c158a54b87fe39593b141a07405c031d71b71afbe792e35fbe76", + "signature": "9ff22239581685c79a6b041ea7bb92e6a863d6fbe49f40e28ce3e1ac86038c0352da29ceb85bd76cd49486f1ec103367163c3a91127ce05734933b6c33d9290b" + }, + { + "msg": "dc", + "secret": "20c43540308293cf22324a2ff21504012cda04358f4b7f24e047da942ada8547e0ebfa3b9d9a35bf2289587c96e2c84c9c86cd994e4103f4f69ae8f47ba7acc03cb5fd5de2e9fc2605595486a7cdd15eb5bb21cf77b79b9a1b07b794e4815d7ea89489377e9fbe1d5106bcf8123c0113f0f1e56196c0afa3f3a97caa7b362f9b021bc9be635fabffad8623bde997b17c00c39f3e99fab838d4e7d75bff04b90fab12207ff62f97832dcbc474ffc797dce91f62bff247ca838f54f6a856b69155", + "signature": "130d0fe92ed86b8583a41da94c747eb1210d92c1dcedddb192f71d201c92298131019d46c1cc4dfaced0047118a2e9b04c7cb90726b0c05fb5d51cacd5181b04" + }, + { + "msg": "e370", + "secret": "b959eef5999541074b8e457ba1d8624c2a019c022b12a57dc67c3cd3a321c5e22f309e8a60f5f1ef45488720a379c84884134531489b38195a5ba2aea9119e9d30a3d2d4ebb89bb98d8bae5163a66db7eca6cb56a8aa52d459b4737c4ba9ad7c2261a545afb37e6b4d63e425426e492e1521421593925659cd54be7acf880457af67fe08d0a7e65aa4fd1b32aa34f762913a69be4a886bbde1c437778f6969d22d68a36c150d28e3da816626fe0d329fe479f23f4cec1c3a28e1725e7deb37c7", + "signature": "a2ae583450dc797f5a71b6286e1348ebc1578ff4bda1f78ec14d8f515069313e3757f1f52765042afd72baec4d5d24541cb186f1b219ad920143eb2264021103" + }, + { + "msg": "1de039", + "secret": "77168f24b6ffa37133365177eca5712c7c7e24778b88dfe4a90be9b521437e46a011bbcb46e82f89b1d14d5233da1ae29007efc7c220a8e768133421506ef811c2996bdc9170bb0d1005e8b3832343c11958f06fb46fee2f9129de834deaf1b78c24648e360f1774160945bffe4dc72b981eb7b0eff602099d23b53fb11b0791d38d3a14b94023548a3fc7815b12ea985896e40561c5bd242e927fbc6407486618a3348038cef8feae53d7f097434b61b926dc5098debbba71ac9090f62b8bcc", + "signature": "f50c99805aeab3c47043a9eb5a330219ee8f186cf7d755ff4828a271422e64a83cf497ab29580f377d5b1d98199f316c3f4d239f829e2fb7d853d3e6e4bf480c" + }, + { + "msg": "0f835c4a", + "secret": "de00e0faec7d8e604e7ad2c42e07257af1eeda664ce634224470f96aa2279454cea1e161bba165d1482160738aa1850718881b8093cd1a72939893496f834de4a731b84b298da463431db29e2e96b0bf949cb5294e7a1b79b2c0f5e6d0ac0d11c267d014a51ce01bb3ba3b5db9ab5a2e9e2b61025d784016a619cdfb6081dd9c479ed89e9acd5611ae1854127dc15d9711becfef3087144cf264325e722125deb8406e7aa64f60f8e25fe564e9815d4bfa32493b5da6dae1b4aa18e32e662ec7", + "signature": "18fdeae3b0e813eb33552c4a53e9eda5e082134a5c4154bb839d1be39cd5f344c12b9bc24417dbbe6f1150f37b277750cc0425f51fd7d1935797cef708f97d0b" + }, + { + "msg": "d47af195e5", + "secret": "99673a0f794f2c426e69d0ebab6d808d7fca935648e36b96297704225127b3f3e3eed9615cd442d34bdecfbdf464ec688804ebc09156ba7b204181dcfb8020c9bd6c2e8b37dea2ab102ec8d9fbc788b68dfc368d8c26076944d3104ab2132de863c0d68c265147f30c9d53d2c515397482ba4f520515e6b4ba25a41071fa306ef6dced9b349a0cdf78e6156a86a7552b7ee873ae3b98bb20ca09cf26710236618ef684f4aa2f40692fc436d521bac30f16e4f51ab8de67ea52e36dd543c60a1d", + "signature": "b6368152bf4e0110b49cadf0e828739949b2e4c7067e9166bdae4d62da755812e54dfc10406274a0b68b20849a0f58f76425648165128321773871ca4c3f1b09" + }, + { + "msg": "096bd2b3c0e5", + "secret": "ec8a337d9e26f9a8656abba3b66c82ea8202395b4e779acd49bc24926ee98d799156791421ed50daa4ad71d75ad827a9ca2eb1de26d7ef8bc5f5872b89daa4d0adf098bd596673608529d2984858f158afa112a670293fea010e7209e3f9e44805b71a4c5edd507c3cf5dbf3a6234facadc1704395c08b4bb396a04ab9c71c4de4b19a50eaca45439e6792cc9a8c76f8f3529a2215cb345f5dd2263530aa9a5d69de2408c7da83185c9b11fc5b6dd76d3e3d10f5bf96d46fc05457f10a22275e", + "signature": "a68f4ad5b4a1961b9251cca0ae96ef6b4c3b37e00775850ae7b84529c2a738e19890ca666210efdee2fc830f2f47e3a8434100c81e71a9f6ceb02ea15a25f303" + }, + { + "msg": "1c892685ef88b4", + "secret": "ab2c9494e05a05eef0aed319ebc2509aaa8eab3dbbc9ab501a5d54bda11dd62667f186fc0a8d5c7e7fbd79810ba5fbba26324c09c059d930b2df331028af55410163c416cc69abd2b61bf2af90a16cfcc01e0cca8ff462a227037653ee1185bb03a401370bdc4f7dc52131e0984f220c37ccfce3b83554742086bffcc51ec907f0808d4a3141d2de64ec2e9d3caa87380afcc4fc611bd87783a766979066b4660fd2ef0de90cc4c7d4a8566ffb1c159cd91761536a54e2318ada8dfaf364a85a", + "signature": "dc186be3f06a52adc45157d7aa7dccccc71806dce080207669cf407961252efea3cc0f9424bc1f59376e4543c0c0665d8f7cb52a6b49f55b6e5a2efb8828b108" + }, + { + "msg": "31435f5af8c7d632", + "secret": "1efd4e59c42907ebe96b80a009c600c7b443a65b1d0819f75669cfacc4370db9cc91d5b8689f5756fd0e0d4d03fb69142400d314516cb5e2c7dd894125bacddebec7832a95e7f9bc125434754163af7de42a1c51bf531d07b4417f66d91583d241c0badbc65e75b6245e3f85b5cd10badef127f6af970782bcb94f485365e798ce0785c397f267adeb0aa2e0f1c53ce5c6d6189e804c29bf1baf312d3461c016aebfe9f86d4d974daa444cd667ebc2da12b485e40eb448e9090eabc93087edbe", + "signature": "6b56429e4f956a62a914d7dde1d91925239a3abab5b09eeb118cf27b1d483679b1196726c3a6ada658a2c6e29a6c577ba291d64e0ff39b194450ce2179f5a10b" + }, + { + "msg": "91c4334bc8b30d6e53", + "secret": "0f82c1ed2e6cc831d757a1d813dbf52545a3fa0c6e0447ce4f15bcf477189329c9757cc7d5af7222e0b85dec3866a5075816be5fd95d9e47ce01b89be89547956c2634b82c682bc78321583333d5a9178e2bde87614dd6d6799bb26b2d1d098f240a25694bb3edbd4d727237561daed4de8cd3e5726dcfaab83ed5909fe29e9878e380babfedb4a586728e14c5455b9614d13ed8f0cf4c997da6795c02337dcf188d2f8e173cd0036f229995d0871003eadbde82611c6d700a727d9637fa0c55", + "signature": "d13f2dee2bdc8562f9de9c7be7dc288a0283f553cd2f47da13a3bd758976c0a76a2640cabe1eeae57e26a21d2da6126156a0dd44ea8627270b83f1394adf3100" + }, + { + "msg": "baa303b23e270d368b70", + "secret": "fcf8c766baf7d8e28125903102b7f63e8a99674df87f6e5e45c1bcd6607d03026ad93b4254e2da29e7a61e752933031498b909bc8d2d414d89694b70e5179076f8cf273f8b0c00ef2875fa8bcb8816c699b51d2d9bc88c96131bb80471f568f35e711884a8caa9612a61557e611b56ceb678b377244faef401e2c989dd686f3827c4b8a589aedb765764ca259aac27a44bac0b38d519ed3d10fc76027e93b760d05b89784127a4ac5969b0563fb3414cb460939973bd7f36f183519938bfb699", + "signature": "4f235ddd8faa0c2c420054bfc71f9dfacf89f35e3837174ce125da428f154972a9ed12936952cb87b6484141bde6abc2f7693dd29892abd1cfd3161261e69402" + }, + { + "msg": "1cd07d035f88df29d1c2a7", + "secret": "c58977ee94fc4095fc75930b5391003977670cfc57f93e6b51a97b052419905e06b00ba580e5191114e4e41fe47918392011c3bb0952c83f8b0669afbb44abf202bdb041587038b003e0b1a7ac4841d63423871ece9ed647c4dc59c3ef17cea432a417ac90e03b3441e0a1b5cfd74c7cc0a477d79a825d8ee5032be29abf7cd544e4cea319e1099bb4e875f782bacb29efdf29bdf599fc65840d4c1f79e0f2977fac12fa137c831ecd41fb54d14e95f0626bb53e2b8326222d002724f6f9c87d", + "signature": "9c3780381d5d59f6d00e8aa5f29b5910b371ec74475fe8de7fe36ec68ac09f4ad7a50dd742db1cd4145603521c320047e7c469dc5d695dbc538c65a9187c3905" + }, + { + "msg": "08825f35ccbfa94568a1053e", + "secret": "92718c088ab4f58e74db26f0e9989d9008bd04127547bb6a1d42f526c0d68db55af70a09256432b10078595475b18b881ed403023ad9478a13e1d45a33ff43afe4175dfcc44c993a481e2c11b5c547d5be8c1de1c4a577dbac79db0511ac9226f052a4aad68be2fe30d32351aad674894ca8854ad48c2fb4d1e04a3a7317355e3c26738e41f12e265c1ba93b2516b8a89a4ebb3d04559fa08c9f5d25d5068906c6298df2280337ce6250649991ff331a38ad3f57037eef54f7b684a8ff76d9c0", + "signature": "d407e2f0a25deace7e93c03f5fe4138da6e6082c886d4b6919e25a43963ddd9238052bcfc3535848edb1a8844083d73158ec7cbec0874b9eb1a3ffcb9db75e0f" + }, + { + "msg": "1e1effaf1e26fd98297018bbaf", + "secret": "858713b5710311becad715b07720fd7c60fbccd6bccc9105ec721df9cd2d1df1ba39aea58ba766604b9ad2348501fe9bdf131d7b44fc76ac06e07e047a8f9dda823cb6d3143c3450dee3f6a9c689d6b26f917bb45841d3e97e4960d11d811f8ab4156f2370400f1c2e045baa9b939ebc50255819a154054c1c7347043ddbb80132c1039d70bcff25c5012e885b3b83191da21a58f0d21fbfd7f79839502ab5fcd4152131b692d882c7c06a84132141f50051a2f250d96307099c9636f3121333", + "signature": "7fb8e4de26c7e0ea1f649658d96074bafce3dc29ca1f11301a13c8d4e3cd2e0546a75b92b0aa8e2f5ffc1e174e612dfc2c596932cc97a3518e0ac4b36b93000b" + }, + { + "msg": "b10d1c3b7638ecc5aad8125a07bb", + "secret": "c3a7471feb887f06a308333b2e32992823f6fe21456c7a14ee5d7b5f6847ddb00812c16c85a37e042e18c03e9884156b2f5a9cbe187e7e95cc1bed76fc2c88aec2bb774b77303b78b0c17f3e87f7bc036a1a680f66fc34c2d32c915808f24bbeccc5261ea66abcd316925f5b9e7a76efef1b39394adb3801aa3ff23f975af7730e34253ef99fd54e21d389caec44cc701b66640a8564b680eebc6084324f3605367ed46df56a6aa097306ec47abc92799d8bfe4f2c52bb5c0cf3796d9a7f8d8a", + "signature": "8f4901cb39e22964e9c79908c1ada05d86a8566a9cdc3ae97a57185faa9f769958f23043a2b31dfc1a309398b193719a03f7d8e214bc5519b7ac6e365a68530a" + }, + { + "msg": "d4b325b88ab1b06acb8cc0ac9c4c07", + "secret": "fa6c2bcf013529cdbe5edeec6c992a8cca30e71207fb5936224e8cace0647694340084658b3c438ffc01bdbfdd44311ead0ed85f8c4d9d9b943072ef9173c4d811d484a685b18eb57735ab5b2f2e8f576a2f5b95741e312db16c3683b8b98d2619b9fe58a57403a72d7e08da11872a72e55ad26d0f255ec6b029d2f20feec88ef3f6aec57cce1c65d6ecf891ee3d94a536160d67e5119abe780e5e70ea54218ed10a98861a2f17db790ef7d5826929c44807984dfa5ee448a17914fd868bb5fd", + "signature": "f91e0ea5e8ffa52291a2fab38c264b15f38faa9d95fab1af3b52b6500702172b775c10dc75acbdbcbb2583cc40ba3c3bf30ddd0e80b8e428e63b14820d68c90e" + }, + { + "msg": "5c986263ff281b8f06f09b3c52ad12c2", + "secret": "0a9c99b8229af131119656e4d0953989ad4277c3d3c4e00ef719291986d0f6ede2029566a55d5be92a4878776e0e5212f9be306d06ef9e082743eeae1159dfb5cb4e8d68837f5e07c088affa09b21997e57c5bd68824791d2e554c094efd0a9649e92b1a729e297676a3197581ca70d16297911b8388e7478b30cd74c068321ebe71d1ea8a34a4e50c4502b9ffa35f3f09e33b6f9ef8fb03be809a1d0f93c29ada81c1008189cf976cf9713c5d752523f679ce139162219e4edec79901190d2d", + "signature": "593f57b86df9ee51f13ec48cb8ed517e6887d4f98e0eb7ddbc8974d335d6d824c2c0a41bcfcd53ea70cdc2efa48dbb78e73f3b523aac919cd4bd49ed00f1b00e" + }, + { + "msg": "1a1c597139d69cf78ee1ee720c5b3f9cb8", + "secret": "559dbefeada3bb0370b491edfad2e7c86f169396c489a5880e63f773491463323d3ed9e6097b488986f8fd450c01aecb2c7637bcd8a54a312c12d52a26ee8372e1290704807e65b67ce4de3096edba2bee12b7101f71cc4634f6904a93ae4d8d6c5f15c01ec72bef4bdd4f6755dbb2dc9312c9602abf3f75974bcb414e75ffd9a3dd1799031d1014b0be5102efcd0d6ecebea16edbfd77d23de3d8be2ca0c56b5f25c050751f95bccef6132501f66d15a7607478c93300f76e929d41b3124f82", + "signature": "77e49f290ef40e131427b375743479b62d2c20fff1455bef12f702ddfe75f13e34d0303e6d0d5e778753dd04541509534b3ad2e370e511c95727c47865716207" + }, + { + "msg": "10e14f3ff62252285230732146660d40e59f", + "secret": "c3c0c07a5300dbfb89a30bfbcdde4c901cf0d70794045739aaddd1a2e70dbc26bdad37fde7b2691fc28367234cb17c068a83844c333d50b8db7253a7db9d3fc1b25d56cf7a7f63a6a46df2849547092e0fa2fb613a65e9d50ecdc916e52d3c44d28e02c98bc455a4695934c567d991c2bdd1a878770557d3345d1516ebcb5c1c75ff51121cb00ff3998055db42b6bd05b29894e436900c9abfdaffc8ab8d35d58c68e89e455ce0b758bb3a27bfb91a4b786d60ca553f922877e32c022a0c858d", + "signature": "838b5c25d1c6239f44faa69e378021009847a9b26905ef62f48b64c3c9d6e57f1b123044059b51dc586f7371729f83acd298e86085638c503d82c01970664204" + }, + { + "msg": "0dfa9197d7d90da1a0552713cb2de033bae3d6", + "secret": "bf9a92b7f37e017c8fa8b21dd5c7968ded44225a24e907e6ecd8488ea55c787fff9befe57ab309b81182a724d15218749ec6f92687a2b4745d17d6b111140270c0f64afd4c39343453e9cb2771d6a3e71a7572a1aa4930ac286fc73647b4581b0a5c581b6e445e21c506453fb4bc0f90867031b76e00ff3626c3f0252284c75ee71857274f3300bf953d4b3cf02ac9959dc84a118732a0fddbdb4e219ac6d3947d1ff84722d726b5998f8ebf5b97efe5945e6fd5193ed8d4ac9b9f4e4e70d54f", + "signature": "2d0fca2b11f0ddbda8ecc212c036714ae9abe7c60badc4f9bb2a766c447518dec1e986d7bbe73e6aeb9c210db39516823d2ac0d825d6d05f6ef029c673edef05" + }, + { + "msg": "57f799a203b76beeecd0d9002b3e0f14d140dbd7", + "secret": "05ca57183afd83681287fdc2ae4b7b8b2007b154ed5ebf90aa38e714fa6bdcb66364de3a1e0a9e3ccd175ba36c2764e96ef9f9efaf4f6a635c3fa157363a718391c7ec841aad76aaf49e22a4186d9b9a9a8fab98b246dbe57954d5aacb11ed319e1859e2accfbdf3525a67fbe45254415721dfbc767d126a5d3399583e32724b11b42c1230141df7c878f5c506df6ee07838effa0d5ec4f8cffe8317889c58b240b09be743a5ca8281dd7520ae245fa1966ef302700199d161faba90ded800ca", + "signature": "3ab59f591e4db95f2fb5b6fe31bc98312b645f587980cdd8f47322f031c95d8796c9fe1b1a65175010ce2ac39ba8d6a33e5e757d9a69fab6c834ed3139241b00" + }, + { + "msg": "61c33b635867f2db1e3ac6f71dba0ebb1415b1f240", + "secret": "7d0b6eab990f3c7743a5728bb67c5b77847ef6cb823e856376237ef00b6972d30b8a885c3f8343432e15784d0b88486eb952776886165ffe94f1dc7712a9d86dd28f3a4640962a4da2b331cc7a780ef6c276ecdce03f8f815e2c3e05fe8b5d5c99ab844a2391467ee52d51b6d8cdfa9732154ef8d8df4c13ee2d80584cdf768ce0acf8e60e63d3575f6958badc039129f17633d013a395cfeae908b8d097c54c9dd54a274bb23a109484365423afa49de45adfcfbe2a2c359fce76772fcb794d", + "signature": "4728108cb24ca1b9a8d525084edd872b8d6b66c0296273de8083d482a551b49b878b6d8d2ead9f7c9dc4a56e5b3d289b5c211597fb4ee0035bab1fbb9781ca0d" + }, + { + "msg": "0af943aa1ba947c18566b216164fab0ad1fd5bbb3857", + "secret": "de5724550429379115f1d5d4d9b245ab1ed60112053acaa32b76128b9f9b16291053a9031226fc21792925879e41e2d9b859a37cd28572a068c1e5b2fc6ddf1e3bd5c1352b63147220399a64ae78dc6b3f64c5c0a2033e7af7c1c0b1032c691f9e89477b71cacecde58701df95cd292cc8fc4a8b2be7cd9dde8cd5d17ef738b515b5fa86a602709abd97666ca90b5146c20d46e94b71c87e060cfa1ebbd2230a7835b8f72581f1c2e9008072a8817b975f2dd52a22d6c71c7217f72ace0670c3", + "signature": "9dc5909e9467547c26b56aed4fce436528f74fdceb3169a3e9bb1b7d6be88f95620bf4f019298dc220d693c24881c69eb195aedbef85541200470fa784fc0904" + }, + { + "msg": "5c65a398a56200872c0ca45c45132cdf9a3bd8b817dd18", + "secret": "4431e3f3c02f2ffeb04f3b12d821309e0c56f6e07e8e05cc4e852350ad2c3b3497844c7afc1b4438780ad506780b03827489ad6cc6185c24373759901d70a84e961308da9c154f2486efb30bb1de155d4668f467be08467a7e6ab377e16ae0c8ef64adac1677ac9962899a873193a438c38e33b8abe47431af7256b910c6f38a62192d8d6ece4b10fc57be46e8439ea4858c375ce64d95b89017b35b938874d0b2917d0bf920cc0a41f8c8c189135a83e88ef09fb7f5dc3dd03a5808e04e254b", + "signature": "8d27fe427bee08325ae404e67d6e895ed76b076cc99ab5d5b63fbb1c24bb30565a9daed01a1dc71ceedf02ea0748c147cbdf252b7ad438474de321d160fa1103" + }, + { + "msg": "32ea9c5bc2b35be4326209e63f92a615fdf9f4fd0b8d9ab4", + "secret": "b9d3a544018c3d1f79e7edbf10898ba9077eafe822484cfb1bcac01d6f39a78608ba8aa3f763d3ca6c4926273198ce2e22f8228bc38bccedbc764d536ef946394adc30ac9dd8fcbcc3ccb7de9f8752e1ea2ad35e29fbded3ed3fc3c82b9509ac8dc1739a532b165cbc25134e9e9600fa118e4243a5d0e0d2825d403f4ef0bb9bce34d4afd995250000cb4ee210e688bd759464b28209973ac3c1f1acce3201f923e49953c91ba3b1ea2224bdc08207dd1db076211ca8b978a03b686219003278", + "signature": "9a91d8cad661fbebd4916797da631658619bcbc9f1bafbbc3fedb02209afb2efca054d4dcdb02b71ebf3f6e4450e6a8897033bb66bec8524c532076a0a51ab08" + }, + { + "msg": "f1f00c976756033bc1eb9eb9aec39e686553e3f1e8aaf24b0c", + "secret": "8bd90a0de469940511531fc0807ac4b2668f580c04cdee065504ebd8111c9ae476cbd821999b609f39d493f7f6c3af240d94eea78b9c5f3a90db780bab73fcad84c274aa65206af7f14fdeb18bdd4d14e30fda5fba28ee5df6c177c4aeb858872f5b796d9ae4d71327f9db7a86e75440849358d2f00f7f89c08ac0f7baf82e8b3da65d564f7f7aa6121115fbd0eed6b40a68e717f7a7383a6cccc9905ac100908e58d127d9d52a3aa3153f4c5f6db588984024aea2728710a16f121f65456e1d", + "signature": "4d188f011793923eb01923573248446ccdab67a10705b86f7232f0a56f40ba4d37936a90cc8f3fe6ddb1d3aa921d33c78197d6a46a19c90fae3a4c21694a2702" + }, + { + "msg": "a750a49c41f53114180cc8afebfd522bcb139f5e59d7c63fc3b3", + "secret": "172caf30126c6ddf1028851374c5ec7a66f3be82ef8729fbf58c9f0e68fd81cf54f0c4a711cc7931275a21a6824ea72965da13a3e0109496e5c5045ef368fff56b25c38dc04ffe5242852c4d957f5bdbb5a43d131e1cc456bceeb1b4a8fea1135a5268ef2f07395c397d187036ddbe8703eff542d35480a97570d5813116b70f265e93ceb6bd59a51eede574adf98fe80e80d5f57290472c42dbdbf7effd48172ee4c52a99cbe0fea50851fbe7e752034b75372deca98e5857ed01fdb5420de9", + "signature": "d0a0bb96e092b05382a29c5f61f2c8170b502d93285e7e9da680e0aa88ff622522e365e14bbd584dcc512ea7cb36ffeab3f66f49c5fc98a6d05d8a8868f4f30f" + }, + { + "msg": "377335219ab373789c834914e61f4132fe3add7fd7f8045014c906", + "secret": "9f4eb59f9be1c6c4a14aaed985616df658ac15c9aa2eeb9a6e5ac1752595fb3e58ad0b06ee1583c0296dc547c34e67bc691e593dfac76966c7edbc720e390d31c7ecb62dd1dfb9f9ea5103ff0f67c74c5fc0644ecbbc36dacd30791a98e361d0658f684741f4dec829960cc600920ba752222721f13a53a4a24a090b6fabbb48a85f0ec72c573b08d26817e7393fbd8e73b9c1b6bf02c938491ff3f11bab8b09266fd907a2c44aafabca8cada70210d760bf456c7627232869cea28a2d13af63", + "signature": "a37b5a990faba13742863c143c6c033a84a722cf8d56a854aebd6224f3f0b15a9f0a4d6b4cccaf93a14e6df108865a319bdf045e23ad691993e0d712d61bc305" + }, + { + "msg": "00ed421cf550f588517472901845132d027f2d3e589223b853fc8f3f", + "secret": "ac872d842b74d2a9236d3eac9b6c4a93399ebce7e705d6b1815d2a71adcabe1e14afeac95eab0a538fbeed352ce5ee4bab8f023811c287a8fbe6e4d81eb924f709c580299a0a88bc825232350ba18e9146967fa729348fc0af69e1fabdfdf170b4b7c19f85d18e8b098872559b4696bc32bfe8de00bc0319c3f2f298f3ea90f59a522045a736c1db1aad77792255df64415db1ab3ca817d30e0105285b9f0bbb585cfb0b4148abd582b2d0dbb7803f9b435d306d6ac4997c74545704326229d8", + "signature": "93c388e528126e805cf0bc87f39217ab4a6399f3d24ed9a0f9c807515d5015e8304d6da9797af3e508059027cd5c186d3beb5ea11850754ed2f71a1e25dff50a" + }, + { + "msg": "a83579ddd3fab7b1f1dabc2afb9e0dd2058669864f81032cdf7b187f0d", + "secret": "8e41adf63c83d986313a879c7db81b4852f0291543f410cc6619b3233f3702e7b549e73abe52c917c419e0b44357d46b466ac813bd3cb0f874b6915e4f1fa701e04608e2a25da95d69970a3cba0caf50f46ae13115defeef36a51114f118235b47ed56f1a71ff6b97aca5c63688d7e0b3a67050db74a84f1373001c38d6df83049d6d212a72afb434acfcba26383fdfa8b1a44836fccd227a2b5b1c82699e21479a0e75ddbfb41a8abcb43ad0b08ce1736186c655cfde7be978aac09c9ae52e3", + "signature": "7ebcbfd9bf8287c98d25bcc19f37031cce6eded1e2ebae3bcbe621fa0bf93673091cf49deb9af0cc22ab35c13f35e22b0b4dae5716c4b72d95bb26e133f23e06" + }, + { + "msg": "8e916afaf7a91c0d1699fb87aa1d155664b267c9a6b6bece533b8b0b00f3", + "secret": "eee00b0c665a6f3ec110575ae7581f0a52e28a6eb680ba346027d41db6dbd3cdc9d2420b5899ccd2f9570160f4db0f7b96fbcde967c1a611c74da1207dd6aea6d7c3d0f8a049cfa29895c8bdc0b517a7b0dc45c79762b7632130966ff9bafb264775f071bdfb4218e05694a5caf1ccde0538f560c2a74e751ce9fe4a718b6817627183981e46627d9788825eb5a20b13f07a7329b931a7e43dbfbc4f7ddba74ecc25ef9e902ef9137357cb973372f1bcf55c0d4b883bb8a95361d5d591abf656", + "signature": "81b55efff88064c91a4e7b6e53b2fa9fdfbf4bb4d667ccf372b9397f097680810ff1cce6596c87727f5ff612488733a2e1e2e475f6325546d2e9ee8d77a43d0b" + }, + { + "msg": "0d828225943c42f8f4c52884ffdd66d945389add7c6650f43ce9eec1d01191", + "secret": "c2e19e318dd755d2b6b7cbb3a945cdc94cadc0a3bfe21e61a8730e7cab9b66787a396adb47125e778da24b45a9d5bf012343bcb989db4f21eb7c40b7f6e366294b901e898519a5c533781c82904bb1deb62a6f74482b1c1cb68b514bf264b0170b20c0137125618f789a66fae2406edd11ffc81e12a892abb19dc399c68ad9a9656221dda5a768af0b62ca1dffef438003a8bc6ea729d70cb33a9d41eb3ea973845a0eaeb4ee726df7a0c02b78b88c608e7ad8024244f64995dd9b02515149f8", + "signature": "17a51659742b15aeda331198a23fd734de661c0c24a74fc7a37b646ce4ac3d9adff4520c1b2d557fbc9b8e55e4a13ef362977e04b48a59fdd71bb70a672fe10a" + }, + { + "msg": "84ca29cac060bbbe0b06d3289fa11e2d876038b3652241bf7c54af40065550a4", + "secret": "e2a9bed604160f6f5db3e27399ee7d2c48431832a553e71da3b774b52b84909ad9e9cef64c201f2a5f5865824b4c18be4636129899c386a68639fdd2dff36e538bfeb994c2ad42b0ab92d1a464115f9d69c7d388ae8f3f93d449292fc11f5d9cf0c816bc2c061e6e0d80d367be5a28caee0eb45beb8e32e8f5fea84e7212d4847a3685a83e6298a5f9d3958ac18bb65f99a717a86e88ffe0d9a4477868da849c9461b4d54c8f799899dc3ccc8217098a1b2c681e7079130c1bca8339e605ce6b", + "signature": "1980b9735515ae71f223d45b14a245acaa007ada96f9c006086e345fbf22a038caeda66aadd6ab49bdf2633beeebd7e316983b8758249fac5d4d3ad1bcbf0d0b" + }, + { + "msg": "77cdb7cc037550f00298b09046c74014cee6025720754060bdc57162a101201906", + "secret": "be479f71db1c4efa3bd54fba1df6542d49d1e01ca8373806203bb8179791334a5bcfe55e2d35987fe906836d6be7dcf75e8001c5b925662e180a793fd299bf2f3bacdf5f51d4fa7e36fe01d67a359cd7c8aa505454ddb8f1fc04788f90fc7e37e27a424fdc04dd368d13412546902bac28fbf32ea244b9f6a98bb7c9fe37bfd5ed8de0ec272b4a88752f50c0361e511fb099520a36dbd910b50fa709cd56c18f71d582e26d197170a11f2711b564c4cec2ee2d71208c7fcfd1fe7aa5b59c2aa5", + "signature": "f96d996eca83d78db952a00cf938dbed47d86bdcf14b3db8563bf15055320c001d287111a8d5195f33d58f74e05c0d254aaac3988a621a13d717a8b961630a0e" + }, + { + "msg": "6058fdb0cdb8dc26d8d14eaa20472357654ad5539525c6d78e4c21970cc84f6c0dce", + "secret": "3415ad3114f75c0a292674f30104d8280d89264262f8ec4a1ec8010a81f622bc54893f72aa9c5e09ad411867b0c7c2101b29f457c4f2f0d56c5963b1e0bde5a2aa4e1690b6fbb966bf568cd82ca672fd9a3e1f23c2c66ca2295dc30466d0da44b283735ecf9b9f6a7e92323733e4cbbaf02f6c5788f2ad0b15ae2a397167a5c1198cfa7c2ba75a23cf2c37f1e3dc1e83f89235257032300a1f53f06eb2a8ea7bac97979c82d26517300ba2152feb9cabc82ff5bbbac13dde141788b467eff404", + "signature": "3bd4bf8deefaf5c0c507a7051ae9e5fcd3d00af4f7127a7d97c33e442fc7feb308c64dee7aba2b10fa7881713fe27be8b21151888d9ca68ee264fba045c36c06" + }, + { + "msg": "540dba0bb10071253c1134a67643009e9b25843fa0ade8367ee00859ce0e9871dac412", + "secret": "06f845c19f26e8f0f51944efda5bb412a2a076bd8a9c49aee30a4959161a7c75659af522fa1e4a2ca64527c57501faf1c3faade692a33e9f43cbafbf98d003e0ae370e48989f362cf4eb7f8741d4e93dae478f93f0dd3fc565c702a56748679121755c2fe9cb2560fd3ebdcbd98e1d4ae9580259d32f14a97cd260bdf33a67d53fcd8f633f4fd5aed85701350bd56c9f722fb27b3b2c1dab5972fc2ae24a0565a3d439826304cb776df12fd662aada1e9e6fc8d08434b5a51cfef7e31601072c", + "signature": "82ec709d67b05f26513d40e1dad2afaf7e649dc50ad88a51cb7cdffbb09a42090e1490eec56791ca6387ecfdbdfd53632a55adfd70bd5b3f11449eb859ea380a" + }, + { + "msg": "3dcd91e937259856842629e72958e7087af405f9fc9b38e9e3a6c4853cbdbe482ebd2e0e", + "secret": "09a4c1a3d127715364884e99cf10682c0e601cf7b5d29662f3819696ab4bc8d676af39c949b9d8edccd986ecf5df24623dad8515b88a1243114c481a23b538e0c0cac744fba3a13b68cb42ab5a9ac7ac13beb85d1227037e5809cb55105ed2bbd0305a4006db452aa78cf22e750467c861d9549e252e76df8f2a737db224b98f588825d1b36dc9f830371e74893c024dff874f4aaea57b23fafd0887bcbc410585e554950ff503f754f3cb3613f7b76da6fcbf6352a210e8dbe34468fa7acafc", + "signature": "0e8677030bd08b533ceeebc82866903036aff78993090c1f82d46cb5a4e0643c969098db7cb5a35948f4f5a07389142065b8304ff2e3e035ed1cf1e0f3f34d04" + }, + { + "msg": "98c786b8fb6a341975c36e52d5db2be27f0c37bde3a1c954b23ad50a46b8730575c6e21ca5", + "secret": "113012250e06d0f5b124368f3dda49691e2d5f131091035b94d2f11fcc2c5f8af82fd82d1642e18c57a00b33d82787433a6ccb0f9fa070e5d2b503bb4830428a6ef64ee6234963b9e2dacda022c221c0a30d1828f989ca96635d4b12d07a174a2ab47cebbe833a0ae787a24eb81a956e9506f1a1acf114600741cac4caf2d19b2e1ebea05275f5b9443caf877cfa2701c8577b6a30cb81383805a383a34b07860fb440088254f0d63b247510a052b449d78b786e3ff73d90952df4de7ba41ade", + "signature": "27ac3aa7a64b07e47fa4c1f029bd4edf3f00c22efc7dd2877f2d1fdf963e95feb7ba3e7152b83fb87e4aa16915059746b2129cf6366324ec980618a042795e0b" + }, + { + "msg": "11a9eb5bd6423b92867c2990465c0d5fff23b38a402945892a13a4c083151c94c46de061547f", + "secret": "f68af0ffa72c8ee63283cd73218affadab28ebc63bfab74d71e4a807fc3f8c19b7203a2e2eafdc541f00e43a3e151153b347bd81a05278ab2163161f3eb58653ac18490c3852124d64c86019521fb7116b49059a19015a1027b4da6ae59ec145a5e23e19ff34fc6464f671618c414adf576433a710cf72ca462fe30e9687bc36265e11a943e4444d26ab22c5c4f79f0a0b28d57cc02e9a7808d360504cf4b009da784cff7400830aad210cd054d90f0567a29e59ea8ae0188be4a0a7b1052db5", + "signature": "db64286c0449f321a135716d35507b57ddce158f33910d0f82b1e869dbae1bdef84bf48d8753c8272ae9dbace0e89ff844c5186d421cdd749cd4f4ccc278b101" + }, + { + "msg": "ffdb912456cab3e000e920c0dcaf3a1f8af720727949fe682fde2344ba3c732fd1179ff3ad0d8c", + "secret": "f6f260d3fa72bee5a39e42aa3df87f22bad96b23e2b722f936071aff255e7e4c607688663af30461e42bafc67fbf04b67e0afabb4b2816a8ac255bd8020549e57e36932b6d1588b549c05debe1d89e97d5bf0b226610e68d66691c82bca29fd4e98cfbfd8dec2eed000ca82672c5431304a6e5c7f4f83a213d17d2dcc0dd3b22ee85e7376c2fc25bf49eee9308fcc890c8ec684fc26624481ef1fa99612f890d6cd5fb4039d2e36f40d8e14a3076a6c6e59e7cb0a578d27de774ac8b36f7dabd", + "signature": "df9c9b2054fac67d7a7eda78633d59772b05233c1c08b42ae4a3e20ef445e9b6a39561689ddbf33bf2c535480f629acd5082cce72020d250aec182922d302604" + }, + { + "msg": "3cccd89faeafcd07d321496ca75c5e4056c6615611d831032b95881a4c38adf0c0908d0c11f8d742", + "secret": "d9b95acb359da608f433a72ed3a7fabcb5c6f3e416880faf030e7cf0188990ef21742e8be22f883e7882a2e0443086ca050dfa246811f0bee602f2c409cb2148cdcbbec35ca1b9c8206f58b7d7fc1736d81b164af2fc06046c90a7ff6523ed9917ed244e176f0a8b32016566db1a04e1279b536ffee6200db00234127adee37bcc8e42f3196c60b627677c064028bfe20f5bce3a8c3c5253db629f0e9db3a0a78c7147a38a540b720890a92dac68882d23c100701dc662beb60759c018320945", + "signature": "0840f821fcdddfcd2bcd72bf265c512fe3b305bef8a37630e1fd1e0731f8e9e182096babb9f237774573b1e4b30dbd043ec06f0ef1682f767b22e80fce80c309" + }, + { + "msg": "e38c34ef8d008e16029fa0a45b8157a9f122645fb102edc7c9d6e3a6edc6b516bbff2dbcca7732d0ff", + "secret": "93260e0cd490e4e19ed583490c8e4791dc8f73765cf4856b39b2886f07583a6090a813dd83233ec996dfc79f70d24c1275c4ad10c268769e926d7ef09d6fa9cffbc07699f31aa14cb434a4d22272d85169cb05a1acd276fd7c9392752d475957ae13f289856f55935483c156742f9604694dde232af09d1a214f21fe996077670e9a12d5b792b5a64554142c1a0b6e2cb746d03f033fc6e0b24850392f68ffd276dd07945b2ea5565443b350e09f5c22359bee9ef590eeb28451a0ee9e8f6ec1", + "signature": "9d1072cdf5f7d4ae2d9deb7595f61949b51e00e13362656b574d24b044edd3f6f3cd30c892dd4591543fac20211b0daf14e8769c7c57a4c5a5599de45d7a5e00" + }, + { + "msg": "f97320053eecf1c5ed25e879aef482c2fc250cc5f73f28a07f9eb63ba646b116aad2e4933612ab471b26", + "secret": "dfbc0a6aabe54e60a03e8ad270abca79e77b02e578bc3a6efdac59d3d0e26f5bd5c87074970149a6900f012a8a401128416b9dfc171ec773ce788c2fd59d6977723fdf774589620ae999045aec39ddaa81d2b5fe102c500affc6ec7be7d3dea673958de6809ad821dc206a157224047cb160f6983503621572f3dd4c586a175f4f222a38090f4b80b066c03ad4df7d1c524c9285bc0ebdf52dcbf65173a5727848de231c6c38f2cc7df68ab4724f93af8c54d635bfdbacdc38f765f9eae37563", + "signature": "22bfd4267c82688d7fe4df0a5415bf4ee1915a7d645230d991274f28c75ffe092e649e99700be55ceb2556431f4f556e151c13132483291ba99a42925a983901" + }, + { + "msg": "82cc1dc5100b550145332081b14a0c0a818971099a9ada9081c63e6b97e37b837aa6b1dd1bf64984e12fea", + "secret": "8b760380b168746c45b98c8746f87b76bc5b6a3e1d47e2a9c6a29943ae779758e8def748c32f0741bfb989c352eb82e40ad210c26bdb3124b5402368021e9a541ea5d295cb51e80bfd2b571d5ecaa4af3e8cbbc3133c2df10d5d342c8cde0bdcca48ec6d55e3a496edf3d668e3abfdf2eeb581e9a843e7fc2e79d8e23a60e3d7aba6b5ed2e43f198f551cd16d37c338c9dcbb22f7419d1ed27d90a54bd135506c0bdbbef03c0ee64990f6b9d2f142127fdcffad026da20f1c6ba5161c7c8a1c5", + "signature": "19a64cf1f6d10d79e16a46c4ab6a1efa826559ca22eccda281591feec08103d73e81ae77bd8faf0f6335abfc39c40462651a02d08f9ed9ad215f5b3f9ecef909" + }, + { + "msg": "b3321056c477b29d01d33c4ecb7bdc7eb4e0465ce5baf893cd567f2e5e6903109ef2ff5d9dc3f72a09f33d60", + "secret": "50f14d305b5e2cce21bbfdc1e80d2c692ec3bd1fdf44c227cb54402ea2346257173984a5073e671842badb5feca063b89bea303ba38163a153a28e34dcc00aee670143dd5485b8fd238d0435ca8ac3e4beb5376a3521c8599bcbcdac8ab7f52fa494ddb204a33781c5aada6f7826a4b4387cedabb2fed36ce1d8a95f392a55224c4ab90a50d409fa6b56c82bd3f568bfdc70bc68495611bb6c1cf03bdcfa0dabdaa50e75c6e1c5a1892e1d11b52bffe1a354ca235d2c6237e49455ddabc8e877", + "signature": "354f0691195a4d3a4e4fc25ff9bb8bf9adeefafa401f4f169357fbc6c38465d4ad20d390381bc56d5c9c989b52bf4584e134d69b3dd657b0d994f0cd94553404" + }, + { + "msg": "953ea314478c90cd3011113e67b1bfe2e97d595822756805d61452aafa4c37da2063c0dffca27e53a3ade5dcf5", + "secret": "878ebb00d13d5ac9bc7aa832cdb1f7da676323631fc06da6f5c241cbb0da9ea2cba0a45835bf322c07f43a366bc15d4edb667b290c5e02c311f6f452f96b6d72a1fcc4c0f0dbc93bfb8104a1816066c0800eee867a0c1bc6c6e218e4427aa3acbcc891a8a4a14f38ec9071e324588aee40fc76328f3848f701347b90c64baace691dd613fe867e6e6acf756fdcf0a03347df6e5b1514dfb4673a5c67ffcb0f83aa8a18ebb3ac67e574c7acb2e980f806af4c5a0afa6b1c1e4681ba3050441cfd", + "signature": "029a77d2933cd8b4e787c61e0c5338ae9100d4b563355d44c840ac59eb3eb8904bb5061499ffc7b3edbb20bd8b1001c23b19a5c3a2f2ac2c8ae95ef3e78aed00" + }, + { + "msg": "8efa8f6a4ce297ee6736bbf315b08350cd4c301f8dc99b7fb1f33b9e6ab28de0343aeb60e802f8864794e2207312", + "secret": "cffb85b122672c792af8654935723236f937ee1950131132cbced90ef6217fe28778cd4053c4a9ee32424caffdbc04bc7a39f8185c4d4e55ba41063e9bfcdba304983c4c4c121373a4c0ef94ff51758545450804da495baffcd8ef54c63443cc5df775f4fbde59ade0206fdf350bfc4a2f9ae33040f0623b4f427c27ace1835b2f2a4d2756e254d6e409cd6c586511e2cb319333041fd4084f40fcdeb74c82ea554b756ad1d657a495d12f89a1947cf4bc09c461d4d5797a018a75bde6e51388", + "signature": "ea64399283c2599a0269b19f898d73ed86e6932744f4ba79c816500c40e9ea9d71e75500f482bf7dfebd0e47588d3fd5c848186eaa6a56ab7ba332136f3af205" + }, + { + "msg": "df41b95d5e4843e0a34cc0c435a7c053e29f5ec26da0c67bd73018376aa55a71703347d19d3da0dd2fc9a8a08055d3", + "secret": "67e863a99f3d5dab91e241f7a8fe2d0de4539c7fdcaf03493597d657aac346e414ce9c3001336940dd48fec7bbab7aec1ad5c6a468bd3d18d5f743303de609cd74a6e166f8ffd6506f2c0f57b82c8b4b4756d964dd4cb518bf3069aa164e63fb6646e0db56dc000bf9fac8951369a39c9680ae7cd7ea212498577469f869049a4556d6cb8b971cc56c1b342f3527dca4fb437fc8dad78ab08a296faec512004529fa98f899671e3980673e86eab5df9a745d134582c6a1b5ad360d37c92fbe02", + "signature": "6d498c401d600a0c7a982192d643dea411f3a604607d522001c2a427184a7c4574cb9a3fce5a7c09750ece5f920d6f74616ae7c04bff7d3737a9bc0cff474309" + }, + { + "msg": "f3af2cb265f829835ff1369f2ed88183feac528dbccb3aad4c01d1aa704780ebaa6c653a91fb691449540fd643b89585", + "secret": "a362637f88c3574ab95b0fd2c6a88362aa192ba54b5db894df8911043c37889a3d1e061bdfd7f0dc5d7653093220c65b07ea2221fca145e99e47163655afd9100a7a4603b9f38cfb87f436e22aef5cbc3ffdc7e01715b6bec645ad92e70be6aca41d6aac3a8a1af43ff5d08caf3ef3d86a92497d73f9a83f66739088eceac14ebbe8323083e4778a5b3e9a90e2ff6d84eec159d987fe452ddfb1ec07ca61c474cc7c0c698cf6115aaed40938ea3c9ba23b80637e90b1a47b7daa79e06e5f0053", + "signature": "7b8a7e8df6dd09345c8ab5beefa4ed18f2dfa92662c10f857a77e48e0e1e01d813bdbc836547101690a7b45220fb5a65eb9e3acefc1160b9723932619f952a0f" + }, + { + "msg": "74c22a98fd5f4bbf713cfc702d9e112a0d2c2b1055f40f5b644c23f4755a49881b9a4af7099f63abbd860860f793e68ecc", + "secret": "0c2d2a4e2bfa0b22573092d57825d11db58cfcd4c7a57bf1f164bd6dc0dfdcc6c6a310acdf4dee06a09adac5b61f4206cfc2d04c964f26d98910437ad42c3a86576202010d59771f259fed7cf403ed0aca0ffc2a46f9c033ed8a7cc3a735e071c9183ffd7d33db1664e2c08db0e5b941c4ba53bd49f5734332582c2559f387bd6cabe13a1ce39917f38fb7a797215181435383a5661153166f096179947eaf2c212e3452aca225528b7b88114615aa8b9541afd068e73fca29d069b68b70614d", + "signature": "88d76a83c602995016d499f31c921388683cde0db444eed5ed400eda29f7bd6e8009358a528607f967c699391466ae53ffc26025b2f665335dd69bc931e7e101" + }, + { + "msg": "0d621fc2fff96d2844a9509500a0005c315d41d436f748ac13172e0c87095db54d16a6ff07b05df8266ceec421a433d16c8a", + "secret": "7749512e04192e7755aa5b72600baeabd1d7ad13318dc149a17a7055e9220b12fcb669f5597762d7ba1a3cfb4a6059b71d1690004052e3e72d35db20104c26311e4097bf373f42b2c09797f27ec59fea8f23c97696a62c00cd71bcd41d6ee8c73f2360b894153249484841125fcdfaf09af2d8dfb5bc41c2a4c9167796a3a0664c8e0e397edf862acd504bd3b91d0bd3f54f390836b5dee402f9b90c1d398b77315d0a2941d07581d4671b409f737964078bb759210853bc9514e2ce63cf381a", + "signature": "2e30b313ddd6add786f83a30c52f78c8d25cbd6125cf1397754025d4973547cbb91222611d5af727b71f63d4cfc7ce0ceb0a35eda738392a15e48beea8050605" + }, + { + "msg": "71", + "secret": "c2db8c7775e602050d524c59b2778e12cf5c0d4f763fa758889bdf21578a4055b1b9870630575b6effa9b2c3b6041147059de34584a65e2c90ccbc50831bc9d3fac4c6b730c610feb350606e63a479370cb3883dce85ead0727060ca86acb48712573327cfe541290dd1015851edf75ff8a5eb5e2c5f5058d3d1dc15ddd22412c6035c596ec6995469727d0d3b7045186037e72b21f5e3943151ac40683724e3aa6764bba9001dd38a63582857f19498c1eeef573ae21b0f9ffcdb184923d218", + "signature": "a9d4238318ce5ef9de09d8a40495f806408d0c77d915dcf934b22e46c9b14bd40f286ed5cb14b9142fa1c0167749dee11b637fe29526a2c923bd1bcb2318050a" + }, + { + "msg": "7739", + "secret": "83edfaf292311889d226969f102923171badfeefae36e8e20908d41f025074b95b2a320d6bef56736825f3c36522ef1c98f7979d290482d2ebe16d7357d1049105e4f5bcfec5fcaf668d0c7bf6c870fd7f95bb31d06e7d15dcc9445011b2562437d2cc031d91cc1966849111f26f5c82ddf00a08bd381e45cebb86d40a0172b4743d3fe77c9db7d20198ca873c29063c7749d71fb2ae47337010766046d6e9fc4249ace4a5b727b5a25b9d8cbbf5e29359ea3c57377fbcb60b346c5b1d1bdf92", + "signature": "efbf431e7d13deb4a9f5daad9074daa9b0bc3cb4de66b954bec87eaa095164fdb6084adf3d8632ffe787c0dc280ef228360ef92a539e2bd893857537b1d6ab00" + }, + { + "msg": "8a2b08", + "secret": "c5d64486a09074ab356dfee80338285d376ee5a6b1b82dabf10f08849b13e71f709e20409d6016193dd91af939dde920c14a9879cbb15c4496a7d2282743d32611da427cdd4fb875ecc463d09f023ea8589ffa12028f778c4f21fd712f05039aaec8a2ba453d5500edbb12f70a9171025505dbc9ba8205184c512ef0e411eeb351c6851a40973d9292ceb36be15a737835ee4359430bf32f3404fe9bbf3cc0acd82b469677a6e749d5c31e1c0014b260cc858d41b9e27df21683fdd3f12c7843", + "signature": "6bfa1e0f39bbc23d9e8eb0beb2577523b626a0156c920e44efafa2f85b7c49a2bbbd559dff8edf777cb3ebbfd6a25e87e2f8585e99a45a5de37c73318a5bdd0c" + }, + { + "msg": "1d4497e6", + "secret": "4612daa53de0ed3b27ca80b5af41f6dde86b66f9b48c6d11f67ffa3fa36aac651e066fe699540ef43cfcc36f982cf3885974d06cf6fd52385d31d0eef42a1e0b9e078cc5c11d8038883b0e1e917c4d809d962585c1ae694c321b5b915831d4e3f546a16e29e9d9ff5ccaa270c98f9052be132ffac8c0910ab6cadbebc65e64f01526f69d632da7543a5e7cee6de38fdfbc2ddb77f971799315a04b7e94c6b963097b2abc1d6112efdd5219a505cde990bc41252f866501dc8a407e2356eba4ae", + "signature": "30e7f36a1ed0706ad312b4b3a02d8847f834665472fffb0966277b4614361fa48ba7f9f4c6c79bd933f208366b1995c8ba4eb56b9d658d25a08adfc049e4620d" + }, + { + "msg": "4538573594", + "secret": "4f54ab932b4fc92791beffab44df12436b368d8581cba3804272c342d10c85a74e64a5128486b2f834b0a1a5c091a6a53447fa83fe279b6bc6709be520f8470d3f880036e308ba62b1e5f62efd6034b56d52a3ac886e6c42f87d8aff858a601ee6234b1b2ec999bce25b7f92db3c6ecc3f8aa943045843291483e7233be074acd2e16905940b9473380025197446897c60b102029c14df69106a5e6f73d08c3f1f22a2119e4a3e9123c3c508cb16b18041703148a4bf9865275b802d1c2aab36", + "signature": "60d683b609cee528c0564391a5f823f6da8f5f6c0825260cdc1e551dc437afd543c3d9eb41d86120f25124bd73b9eee8f597cfb666913a1035ec717311471509" + }, + { + "msg": "5a95c9c22742", + "secret": "59b7cf6cf1ce659398773a69c5d29603c414a4b82c72b48d4d23e7e0589cda9dff4cca3dfeedd3608d45c0347fe5eba19a4fa7cd7c6e5a204070a9b3398ec09f62cf3796b1edf380eefecf480a14cf9d44268a2e9ebf16f989b07e0a636bce5cd660e556f6fecd2bbfd0b714bd058b885b134ad0ecc1011985e998eac2937411416ba68e2e96e64a47a9d437fb5a8187cd363be1f20a7a61edcbde51214209d2ce9338363243acfd6f280907c128fc1917d406d20a5c2e0aec693e8d6524ad00", + "signature": "cd5e515306653a1082202a34df46bb0287bc8dcc2f4cfe9fe30c5abfcfcce555d139d706ce9ef82e82c2bf6dface6b2c66cfaad411d95f8cda6cefae74476505" + }, + { + "msg": "ae5c6259f8547a", + "secret": "f7b3b6a5fdd7744654af193cf2f63e6faa6468af348e2fb8f30e2a9d4926966c0fb4aa160aff1bbd3737ce6886167cc0181d81418ae0fa247a8ca491d61338982129244a333c1c6a60db4a290e83218fe4da5090518685dfcf57ea0cc4c887e84b608e736a74ac7679bb5285f45fdc332a296aa9955bbbc03a95d01c704b98a0bf3ca2fed707c09745957522b6beb7088a0ee7983d271bb73d7918d3d1be2d9a3192f94817fd5fe6e3d4e1bf3d0fa72622b90b04c326ec890a8cee8b6d5497a6", + "signature": "f944b9be4dfd5d23a44feac6d3b860b46c28e4e89d1242f5924d154955c57127c7356a29abb29a7b70749d2ab0045f226527ccb6615c37b9e50f180e45b7d503" + }, + { + "msg": "556304c69e120dc8", + "secret": "2ff8a14c01c534654bb3d20a9d104e0458b1f5a710fc26cf6e2cb54df6c7720dc280a5e38bcebaa6de0e84d96839844ed3185f1f6d42dc7ab82865b850009f8edf95e410d4af063e6e703033ccf614fb5b7d3af81071267b697d94f21ae41d761fb51355ad1d804f3363f4f8df17d9acafaa07441c20a8266debf5f528b7c0bdf1202094e1e3fe051cc5bf1937de009724a57c48622473840948f375109718da74a643e52078447c20e8efccd297631a7892b29d091cb37ead0c521d45c04836", + "signature": "34412b82b463d1fb00da90c19f8deeb827a0fe546d64a8fbc969badae07bd8026e16025edfb09e5347918d843a302731a9ec5f8d6cebfa015b688045dc583c0f" + }, + { + "msg": "ce94455c0831ee1bfc", + "secret": "192fd643b0cd48524ba4ddd29cc2a492a4a478a4c565a2d5b349a1b28300e3b440e7042490e106a0f2b8e3811a0a748e8e255c82579e257d6f5cc0b0570187ae3fbbf65b21764097d2ebed24cba4bb89efb56dd14e6a0b640b77c7e6ea5c620e30a0d2caec37fe828188536e8d0a404c712c8a587f091e4f70af7e1b71ea9020f02fc8dcb111d51b84e211a0ea6b5401c673c2cde7f174c6bffd571d0acffaf8157b52960e61da98e117280f5df2272b635768076b3d06a2bab96a6d6845c6bb", + "signature": "e3c932b02268c17f68644f8a5f93816728eaf7df696991452f588518b178fae37caf28df7811412334ae368481411544abe628d66b479814dd5072feb74aa10e" + }, + { + "msg": "43318fd3af81be1bc766", + "secret": "f0a69d79a1cfca50e133882a58d22e56e857a658dcf76e413b25bac2ae33c9086af546dc6b942d46b4862fe7f4c917b94cdabfc162e4902f714374d72ee44c7931acd09a890a5883037bbffdcfc5aba5affea68576d9f6de355dda9abc20883be1797933ff1466e405b67a2726cfa4eace574712a364588c9315243397fb66f3670406880ffecf0545db84a440a15beb6e696dd11d798711913da54a70e78aa32647718902e03cd843e148c710f54f747e43584fb0f10eee7fe2800e9533d207", + "signature": "57d78cbcbb22124d79c7f092fd99f7130dfceb1e1747a650b3f13b2031d83e762e90ff55bad5e882f940f33406572a473c61e126227cc549ccbb9ecfae0ef406" + }, + { + "msg": "91b97994f7d3f12880308e", + "secret": "c145875d318659aff4586fcc91a68abc82b3ce35f5921e7b1420a8735460a0ab46a7806e933cd256f235a8ca2bbd81c63c0023c2f4f2e35e07de7e0c021c093dc9b7cdea54ee519809eb0199c6e8c6f481aa5d932efb867bb300867f91d9800c0d1ec59b3359d64fd7df3b60c2e3667a4f66b76cee5b5bd21909ce32a0b905cc29c01710578a4e79c62497dc2f9e1b78fd3272398403abc1b0846a084a5417805a95a69d5308284b5a63063abccc757199d1d51a65634495e84accf5a2b900b5", + "signature": "76ce479e0039cddb072f21202436ef8de1ebd24bfd2048c3e5f674af53e236b8a343a6a104544451d12bdf2c31c4f8856edc96e8a5afd78b6f3f87e34492170d" + }, + { + "msg": "2fd35849e7291d9b5fd86d09", + "secret": "c6ab6ca0a73cb406318e2585ae859fe0ef58edd0ad3a2c546ae8ac7a6bbd738d83fda6aacd67ee63e2b8aa9c173f87d5ca779dc3da22f1935dd828425010db99423d10bc0c8c88010a230e0b7b1f5f1d794b3eae968daa693efedd0db8403129c33ca776c458ebbf532e1a7541ba6fa341983321c6118db8a8609b45c62d09bca20a31a4e56226d304adda8609ad70a2089841489eba0f87f3ea61de363b2fcae48478a373d4765fab38b3ba9d61b162aad332ded8c2708d841ca0ada8ff999d", + "signature": "7b995c7ab14667215bd96a4ed4495f18a9445c0d7818e3830b512204887d2a101dd1f714b8d91cd7433165b51e182f7424501c5732a7df0555a81701621a9402" + }, + { + "msg": "d48a2a925d7e24b4f8de65ef69", + "secret": "3730e65c56365ca52a6ee12fb82417582dcc8c27be55b529923fcd62b9dbd600db8c794a7bc2aafd8c113cf023c156b103ac19c5f075298f51108b223828b658f978e677a48ff9fcb24b543b4563c317252641bc7e18be57a69fdec7c541e4d8dded9ec11f43539e3ebab51441756c25f0d56ff71b03d8d4ce3c4bbfac8dc241baaa315dd2771b38f13df0bef09ba02e416d39f92ed3732116223779f74f635c7c0556fd14999af25f4079727792250412c473b8b158b6950779753d5e1862ba", + "signature": "5f8970fd922505a93aca9685e0dd6e791b114af6ee9bea92de085e11a797b9d7992b1f81f775769d24d26b7f42c5dce5ccb18dfd8a3bbbfae172365be21bd805" + }, + { + "msg": "10e4b47bc4117e1c4f4ec327b346", + "secret": "0ccdb33c86e08332cc0f3b6a9e78bee88f080cc56b4bfa51f3ce6166d1246776c523507c3632e59d4844adaef3ebf3621cb7f656005a829ebd184827cf69b1c5ea8515c9336f495dbd3ee4f0ad204ae8f50ba9bcad0de08a75754749dc4c589289156a7fd6fca3479135f8cc85f790d95e65f6d2ab403a1c32d4ccd34a0ee0d903cb155b7971f54ac4e68f00c5611b00d904425a99d524944f19aebaaa95f5ffbcc24f896e9a9837ca6a6a4c3754dacd0e3c8d7ba67ba1beccb44d70822e460d", + "signature": "ca636a9bb6a86d400b4b8b3c2b79303f17bc6ee77f8ee0317a892db1a25f09b85d229c3a00c44e040acd2975b6baf3d9b5e879b0deec0fd47a6cc59904627f07" + }, + { + "msg": "b5443507a1409355a720d4b4c03c44", + "secret": "a5b268e351cc2323a52a0e2be6c5726c2e511194c86366a9cc71cab71e716af72d588987d76bc8ea7647f7a874d812a3457e55ee92a3a3b65941756cefbecb637a3021f82f5308a6c3b657ca6ac56f4a314cada55ec7178a6ee922914f6a3e11baaa7cc944542bc6ab658b1a174e8369011b694f30ee4fcbd7141c5f6764c9f25cd5a55870175aef2a5fbda36f152b04f2b2995d5c6a4bec99335c0d7830c52e2246f4a9c4d660e937fd728855df93c7ea6e71ce2c80cfc492174d6ba9016c43", + "signature": "1edd65b799c7a9b0f76429c5934c6d8b71b8e8cca9058a59bfcae7849c11e6d3ff8fe13d4327d17e244d35d4eaa4dbdaa1d2797a6847445bec3ff306949d2704" + }, + { + "msg": "555463c79fc5de710b2389a40437c542", + "secret": "547f16dfce04d0b3fb157de1b997216b371821d131fdb53160da51f66700d2478d12752dff3290f5811bb01a5b3ccdd7d1cdb7b9390de5ee1c7154793e3efd28b3d5b16d51718258234f3fa5c217ad794b309f2d5902f141440f6c0359eba9d29a840dd4d4b8281a50f37c9d66bc93d30ab9440c805c31f0b81463e8ff9d10380d46720a9fab9daca208e4c9263c3bbaa202f986899ff408f0471dd965e8378c60b3313ecac307e68d7a8bbd0660003cc265c7c6d26fe44f63a9cc981651d045", + "signature": "56392632b6446f4178f105012efd53cb352270cadab1df5bc0e0e2e470454872cdbd64a59593ff7fa939e9392ae2cc2ac1b212aa1103acf83095c7c8a9d1ae0d" + }, + { + "msg": "fbfe4c28dd3d6dc626b81ba454caeba957", + "secret": "df2d9d1a11e6df8b3fe7419235702f59e0f5b80a837883b2e3f1d368313e6072824c1c98dbefb3ee6881c1ccec9e3ec0146e401c75a3cb1cff0f0f394f3518467fda3c79de356ff66f7827ccd248062a59c7dab94b2c627e58a7c6dd00aa71e4751e03444e5faf9f27be24f69fcf2fec32439bf994e2e715acdd5943d44696f10196afb7dc0e70b7c28856559c55a7fc6a5dad5a962166392395905a6490f7737282ec3e14e7f72d0e96f83a8e6a9f7736d1c9fb8ae5edd93ec6b9dbf073256f", + "signature": "68f676676877568557f304a518e05585359008264dc0bf2e2aa3e1f5e2f12f307d9546849d8a29f26c9bf45657721e27d9df96b3d38fd7ca8dcd6b44198b2a0e" + }, + { + "msg": "355495c6f1fe5a79bcb9783a13bce1850b96", + "secret": "9a04bcb408e2ead9f04387fb0acb7573b1e545a24d7d22b15ad1183e7014f211ca1968f5880daad337e45182205014cce9f31de4a4e000e881d03fff5c9e4ea2d3865b07f886ae3a9907ee9929338c11c390bb4903e5e6df8b92d6d1dcecff6dae9dfdf8ed85b1f614e08c3f6c8678336021fcaf92e478eaffdb3ba53868e3d2c71ae8947f926b5e32d82de0acf4deda4b67e77f24b5782e7b61e9c1420df396b2389a62c104a87136ba674ffdfbc3411d9adf7b4e8013d433b7a0973106989f", + "signature": "cef6f4194b5556512c6fdc7f038f7348fe820f377c19e3a5889395e956f30a29dba5dfa9910832c83f0eef65c25ae257f6a226adbf8b2543c9d69bd34ec55007" + }, + { + "msg": "ba2b818a9f2adafc8eb094c044518f6fa09549", + "secret": "0edc30262b8665b373424cc11e1b33476d1854b747900743edf22042048530acf54b48966caf7847a691b6dd42e4314ea5e6de6ebbf247b97a3ea3937154cd42c23842dd1f8fa68018329e634cf4275d874bc78348cc301985151011f5210d13581ecf1892c7f5ef76fcb0eb1e1a4094dc62a9c0b42b389fa23ad4a1c7966b60c6efed844c954d9ac2ebe6d918ec4ec24418e9c1d64e5ac4553350dab3e3380839b186433c7eda36f8773e3c41cbb4379154870f727f47e6a4231f6017fb99de", + "signature": "8d496d9f01e0723296362a7128f523e7aceaba327e7568b50e36f696fb51e7ba465a5c3c93592edb99ded2bb1a7aef2bb8500eb423d0e71f218018056acf8008" + }, + { + "msg": "77ef81d099c4759cf87a12c2ab04f01ef76c386a", + "secret": "d10968bc5395a1a0be444a652163c9f4cdf6b98261572eb907dfc85a1cde26cf76b77eb143ba48542a16f688fd060bc3627fb14f3ce47ee6887eaac3a753f2af95f6d7388c55158c3dc13b394603604ba98c7b23e3f04067f226e8bd6786054df1e7755940f11eb254ee7395cd65fe40361e7702078a41fcd676acd2df7a6078dd99a6d3377b83416b8241a9055d2a40f9e6a6b77358f9b54b9a5102a1c12a57cf23e59787f97d64786a4fe00b7a97050ece0920b7c76f9fcbb3860e9e1d9e41", + "signature": "1c36555ee80f469c8f8011c34a1b01d5afb6d805b0243cfe966b692ea291880af0d7d2355af206dae81dc27d484af3fa7b2205a7d42b6b8a710ec8b94f871c0b" + }, + { + "msg": "868fbd177d9aeb1fb3e91a9bec9d8435fa19aa472b", + "secret": "855fcf0c08494020d4bfa988bb7ba5c6b04ac18831f02e2a78eab82c8b58f76ddef71b6caf6778741d56ef1b28786b8f13ce6de9b202576d5c9a93e9354d095730474169c50fc49d8e60283916cf5e2bfb1113081c10a0b036fda98c8684fe564b826aecce147ce08ad3e284a9d601997205468fb0b0d126211716c835644a05571364071187f175e4f9de0c24b4016cf1ea14ab6e180b09d8a9ddb773cf15ef0cd4217f9a774ee5a739c4cd9fa1fbf8ea0029a4a24dd4ac4b8ee07d0d14b79b", + "signature": "4401075c99c0cbef08418a722ff3904b884b6097f08c964ff48bc9a4a16f41342c2daa614976be3d2a113fe8b339a55d30fcd8a8c543956226f3d55b8977ff05" + }, + { + "msg": "c478614224585c4c6ffb63bee114b292f55165cbe067", + "secret": "f3bf74d75989a2583199392c8b7c2b309911220bbc4ea522499a916cee362f81ec4a3082a91e0839ec4aa7e42b9a77b90a906ec6e793a1a6a960ced3fd012aa4c260c32ce7c49da22710ac375e3b13bcadc6985e2c005030520afcefb592962f861c190bdc335332925383934d9acd10b1aa3070cbe486a263ae1ec7b492b4d118692e59f65333a93e823289b78a93af9839527af11c3a7f5b1996419fa698923e68a36e95bb4e34985734dff39e8bbc0f4dad8f3c55175ec9b93ce9746dc7ba", + "signature": "a75ac258e7ef4615b3dbde5464ee27acef9a3cd4f2cacd0f1755a700957adceedd08963f6d32ca276b8555e583d8119b7e0b62102acc67ec73ff08c2936d2403" + }, + { + "msg": "f18dfa7bdbed965156cea46d836779681119939d8e6d9a", + "secret": "0f7239bbd47d0a61a20c640187ebf1b5ac0f1584ffd86f497ac1ce5109c9ba8dd320885dccd4588aa699b068a8c7ab017e036c55d793dba2c725e07a74eb91529ccda6d961a36fdddf1480a2611256d34f26eb4aef50b3d52be6361abf8e615568d29ca6b9f331ae5f8bdf05a7cbab637429daa2145592bfe0b3d22561a77302b42a92ad9121002a9d5894481b1b29db2d844c9e5b00e8c4adf4dfd970b6622a37268746059393799aa76309c5090c88c655e8bb1ff349deadf2e11290e91c99", + "signature": "d287033614ee807b40a921ae1241d21c506361ab46b4cfa158c6b45bf85af7aa4b6021e9ca73b2bf549d6aa28898678979a0eb2c5d9964bdd49274e303828903" + }, + { + "msg": "1a1f2d1f0ea12d494f64d2121bf4b5fd904c881446826ccd", + "secret": "8e8942fbe6cdbfcb67e190672bfadddc327b33b7a8bce886b1bca70850bbadc5098b663543258908b01a7423b3765f23ebbc6790583c9500feb104f900cc84d35cc0f12e3c7598a1d3731883f742b8e3aed0ad15e8578b3c60acc663c34b3363c3ee915fb586d61f1af135fc0a9eb347d36542e31cd69b94c263231a7b4c495a333b7b5b38fcf8c45012437d275e3c90ae4b0a1d68cfdbe19b686bfb9f3b160a8ef0bd13dafbd39ce06ff4cd6392604c3890600f39aa080af31f6985807fca4a", + "signature": "f8361f79d140a9ce2807bcdcb1817e289107217d73561415cfbb605b912e638331e7231bd77c134f499cc7aee71f17e7340d930b1c09efa4bb642f8b250b8809" + }, + { + "msg": "991567b764ccdc9acb6c2de20ea285e805fad514d7c5b69e50", + "secret": "2d8f9a6969541c3706cfdd002a9e76e5a0645c34faa168cb01eae7b8caeac0efb3739081c2e10d7ed17551dc94c9bd5e502ddcc897c95f652da784b80764acb233e5dfebdf96ac8c4230272df4ef0e546e1416c0088c10ac883aaaf744b2d8175a5f3e6d3ed603aaa526d510029ab7e6a4e286accfba125d0a0c8995228bd006092985b78bd8070746eb9b7731f01b4619202e5503658beb99db3a1a2fca90748deac6c6a4947d1cc34b8aea5a34bfc23c6c0cce9da17201386be45133a6256a", + "signature": "cca43a039fd0d3a134f7e8bca4f344fe9fbfed929fa602407bd565c42eaa9ea50a308ee21b482bafefaf9784199f5187ab7ea5c0f1a0f62523a40042c53f3d00" + }, + { + "msg": "b6eec00c87f71a8005b24460ff8e07f6ef43045cb365ca654842", + "secret": "c003feff4d6586198a2818a547e6c6badc7a1f42b701051dd85f70bf39a880ef7c90345c28b358c56469aaa0d812e65429bf8424da5f791e08c9e5b4a038b616660321820474934f17a4e614a6de475cc5ccb0733fca7afabc45ab39357f09520dba7acd3cf6bdc6ca8f6b38271df765a0a0a29fc2d9b72e1a4bef2536455f75571e8118087180ee598cca9d0f7c697bb580181b13226104474ab073e3d3399c7675cbb55db14ab635415679eb7e32ee60647c8a9aa27a8ce139eec52ee15810", + "signature": "9d7704e3ab815270ad4cf6664d1643a04c4312d87445113d63a342649a9b368964240c1f11bb2018acc1451a44a3d5234ee142fd305af17a413a34ddaf822001" + }, + { + "msg": "c3be3dbc4e1ff501f5d08efa5608d0ef9bd034ac6a27b3e29affed", + "secret": "17d1a76bb0711e19cf90ef8b552f7c857cdfc8be54c38c57917d11a7ec7811c5c204c62b6948a2268fffc21671079aed59691d250b9796f33ba6bd883ec760d7c74957ceba98d9e5b5c370bff8966c45ad0a290b14e6be90a2b2499a0270501b5778e147a83ec279e0e0c8202f4928cc94046d99a5280d89bc2b5073b1d26f44604c9896c6f2179a9cf66502c7de868cee3d2b94fd6b53b32365fd57f6fe238ed3f7ef4153a3d6ebf42d5fdfa2dbd17a94ba8c3c96b9ee88853acf4f60a7614c", + "signature": "246bc94d67f130660c591a9c92aab3bbc3465d249dca256e27c38ef0b22c142fdbf838b550534d7228d22064ef163524731bd5290bc6e0dd3324e7942693ea07" + }, + { + "msg": "dd62aec4340f6779c1a02ad847a7f9f9e083bf66e9b06571f74130a7", + "secret": "d2445c9e947e13bcf56465628a3a848c134e06a0eec34f4a354596112f9400a4c386878660bb53b71bfa8173eb14fb863ea4477acf69efa036ac6bec3507a2ec070012da66b1d9d1a30d08a1977b971ca8d0e10c974ed3f45e75207e0cfeb4d8d64b2d13cfdb9a1bcb691c760dec71881c2c4a143c9991b9c15eff6887951ab32f27b9f1890335fd7d5a15edba0233bc24e8541591c90c32a7c752f2f7ce2f2281d984073096e222a3c7b6d7dbf0c39c21aba88b3bdd1493b18f1a814c4dc836", + "signature": "d9c4a50eb53653ef3868cc152613f0ccd18d6c8df2105acb92d26bdc6ad93df53f46bb484d8c601cd05e212d6d54fdd679176712f62477b3809e01d7d1888d0b" + }, + { + "msg": "de7baa6efa3c463e75c619a1fb90dbe620a1f05b51417f1435994cb103", + "secret": "9b908f9b898743de60f1ac698325d7752450e3b9064fef83eb61b4d1cb676c25c63afa471d9ac819cbdde6b5d99a0fed0f3f1e5f20ff847bc13e32f966685f1248488aadf9aa32ed75a57d1917e1b86641455d0f0698acf3323c533fbf87c36b95e174ab94c927ea11df635f8a9ad6792c21dbaa4c51103f19d0cdfce07262c7ac4aefc307b50e662e75dd14bed9a964bc585922ae6528f822b8294b752e3c96613bfa5c59294a183ebc492c6ad1a2a7cd711d9ccc34346aa7fb1d5e1dc5652a", + "signature": "66357c114dcb010dda61641fa35e4a330eb77e5a3edbf03c88a0ed5c77bf952651a3d1d3b4acc500b4eefccfee0fb5ba66cc2fa57795e90bacd1f6992ec1c805" + }, + { + "msg": "4c5d094a612b4fcb49a0f60c6468545af57743d0559fc8015cbacddfd663", + "secret": "e423dedf05abbde705f61e70cba7c19098938a6ea5c8b9cd6b9edbb7ff62484183e3a58c19d3841a6c3ea1c098bfc236d70403007b08393fe4cd605771c2e82874cbc919cc4023c0fe690bdee375a51cd71630671d7c64468138b4bc7349c1f9d98e4547d0ee518ea0e4a47f6493776ef7c72b3482beea61325cb2a0ba8be35512d7e08f70ab106a16f02f52340544aae0f5f4457e7c3ee0f9ec85b98cdfcc41fd2d1009e2f4615aba69f49f4b029207331568fa9e1e32faa9863897c44ffbab", + "signature": "e81104693a674b62a749e1c6a844ab67a18e6f3e44bb468fba312960083d07c9e720d34d42c33211b6108010cfc8979b960bc362023f703fadb07b321e4f3b02" + }, + { + "msg": "c0458fde9616e6f9fbb7762995c04804c9d88d1fcdc2d509e19cbdb6935c9e", + "secret": "732f157190fa07497b6d244a8a06c3d32812359c731e64bb7084b8e6e1c9f890b4c11a9f8ffbb4af292de3212a4ed430df552c54f6bfc392b77f622e8d158410e0d0da9c481ceeca42ecda458b60270271f61fa1f61f83c8f18b81171d20c533566fb5ad2d89a888d0e28cdd106552f85bf2e82ad527b33473874b186d4f0f17cd4d31d7460e78b63655620f4bd9b2079150c50e59dfefc753478720f6f987064ac20c735adc8b91f90819f203c10ac7ba17b075ad4b6be31a111dcbfa14096d", + "signature": "04af494efaccbb631ec0247063cee85d164243826e0ac9b367cdc00d7e7c187522e8873349a2550545ce0cc9db606ab1e1db4810283d62c842a85debc42bc601" + }, + { + "msg": "97f5efab9a3f1c26db8208cd53caab196411faba4cd972545f6f5963a88eb37e", + "secret": "ba48c5b8e83c2fd8cc9db8d97508c41d705e1fd0cb743ad201fece6930228cabeee06d435b098b0217971f12e09262bbc6c9f41239d628387b6a4fd1fde31446c2c10121358e76846a1f56e9494793e34846d274f9724afa4081eefa9844c670aac43081772d2504b3b08ead9a1e129f6ab27524218d8ec1d0eb93694aafccfde60a43a7c34f06b15f5da1f44bbf20d246f2eacfa8c61687288757c24635b8439eef35b409a58df0a500d871c75646ebb63c06d507a1f909ea9ea1d9c86650ae", + "signature": "fa4384d32c8e2e785abe874c8cfb7aa685586e1be02e175b34581170b4ced8e67b990f7279bf70b4fb12ea9d699013b726d5e0a9ac6152c927901ec94a359603" + }, + { + "msg": "8bf9aa5ccfcac8c5c75d9a1b8bee05e2ad561567e05d840872efb1e04e5d19484a", + "secret": "665c4b87611e8e325c78f6515a3a7e03c4ba0c9c5ef7fec6f2bfe60abc3bbd1aff1da367d9e05acc7e22b606a4ebe87d25af5e242ddacf1dec4ac4ab1165f3e042270255cdb925d39d6bdc695a8aa92a8f305b2ab99fedb8fcf9233dce6bc84f16a23699eb89f24ac378e743247113c4afcec5ad0b14c48fa4d23ef6e262b17b91d74109c1cb648c92b765ef45f5e494ab4380e8e1e872762c3660462f490f8f1329048988cac6ea3216034e7012fde7825c9d4175600641b0c262ebd3449f7b", + "signature": "eab978bf84856f158de470fc6d8207ada951ad2130757c20b0e5b5867d47a867b1ba3e8f5eaf5879b3a8ab93cbb8770de2d2399b55451779ffbeec1c8a07890f" + }, + { + "msg": "0cdbd7ec251c3d6011dba0eb769f2e43f5234c3c86476eb07e84a021cbccd1d13170", + "secret": "719cc0f5352d8797b1db01bf320c65042a2f48a7ba74502bc71086a81670c77f4574bf0bf8bfe91115744d542ea103d004900455464f2e67cb2f160f67c896b90b1d615ea9b69b0495b4590c26894cc4632896fa1db1fbe78faebc9728e446b85054d6e71efd6cc32c98abb08d9009a7fede52822c30a4d243e83935cb2448fd59a3e53ba140747a94bd6d3911998fde180fd899fab5ca4945aa1946f54750c4dfb89312c0b08c4acdddff0b77e306d5fcc3fe3183053ee0de0acacddd530aeb", + "signature": "94f0c4b85626a33fd1eab1ea81f60b3f77b47d80b3af9a12409bf289cff787cb32e8123c7b1eb615a00dcd85e9dd5be70f6fa2c2ecbbbb257d1fe2842aee850d" + }, + { + "msg": "e0384662f0619bcba4b7b2116d603bd4082286e859ff8d06c1fd8634fb6188970e8b0e", + "secret": "797ecb6380722135d822224052909d25fe1d66a06658b092fc1c00d6aa74b91bbd90897656285b9b06ffefd10dd5642ff23b9383b1470a28b772927255746cc078ac4c429f422485d67ffa9904a63a867eee8af898a81a12366b22a8b2a61b9ea3861cb98e24e235735fece3c517751a1137e873b0b56b7ac3ec88814dcbe1ca6f231869cc9fcea72526e4d1e63fe3fd70d8d812bb95bb74cc396f6d2f03eb90e1105affd39503e606107d8341e1ff2678ddbfa1d4287b57ba28469c412d738a", + "signature": "45d19d29d8eb9e5980d5eb43514aa0c497692463fa060d5891856b621d43b8e2c46cb8ad05bd0e70978b759c612e36a836aff94b0c9ea262bd2aa64ae4f92101" + }, + { + "msg": "17976f71f0e43017f6bb06c5d5fbf3317c7d6681b81ea455b2cfe4d600a60fa22e9aa037", + "secret": "c43f28e47d2916cbb36acc5de80be6a3827c070826718c99b266852593051a9ac21a3e769a86ce0d00e257dacbb31af970ea1e2d3c8144af823b94e0e3965ccad701c40a1b23d047a5acdeada6cd9fa9437fa770b7aaf1a4461cf780d9ef1914e71bdfdcc180cce5fec2cfe92483a6c905fb2ad1c7c929c1f6636e011b85e7ae7325ec32d50e3100d514792febb4a6558b98d761d6deabfada1272796e353f0e050d5b357fc214c538ac2db30c00ef1e86685a3b4bd992250987fc0f95b7365e", + "signature": "933da18435becfe9fcbb0467b6677380eb9278515176686b05bdcc50eb7522f913bd19f5f6176fc102383afc2736809d8e930575425ab9d7ca8de4c7961e6109" + }, + { + "msg": "039c7594dd090b98dc126ec6fe1aca8e0dee43594a77cb8ee6a7e618e5ea05a84a1c9aaaf6", + "secret": "e782dd57e4bf3cb4e0a117a7138b7efb9820fbaa87182404e673a084c2b0b4875356ee6bdcba2a6f247e5bc6f6f4385b9693052291eda65210bf6ac73bfa4933c6fabb3e29870947c8c902683a7c8e1c45efc3abbae466b3904ab9bad4878433db691575831515df573c0579fd2ba293b85348ff7506a042a87f55f34914f4abf862ab6de32df23a426a3ab03ab96141cac06562b0b025c9c7030bfded26104c99056c3249e6a40cd85d0787f226215a020f4743edadb5e6642b6ba8da68976f", + "signature": "3435c309a326c4025c85f64824d36a5c6f6d0475f91c773f90f9ea9f820bb5039eb223159f37f928380fd5acbd5350eda00142e06a3c15106ca46f1d7f06d501" + }, + { + "msg": "6942bd46b6b19086a3a1dde3a95f45519f54147f92676bcf9cc4390e7b0dbfa732809af990c0", + "secret": "a798ddc0b7dd65902a64fd6ee1c6106fbc2813246a8d0a88ec5fa0edb117457ce3aa5cbed8ba26af5f9aacc4a0a09323a5f1fdef9812d382c4cc46401c55d5642dcb23700bd687ff52a1c0d5e6e17d5cdbec742fa2dd0ff16ddc2812b27af0c976b7dbe91bf9c9e3183669912300b7cddbb0d72cde2de9bc1d35e46f67e4783fc35fc683720286bc850648d766dfaa2f8c046279772781b9a875ceb85747d75389ce078c1b738d463b221fb3e3c337f332e327c292d745c8193c3a5bb9e5aa38", + "signature": "274094d3662eeffb57937974ba171f5fc1149ee404d4a894654755e9f76b8e8f7802754cce6d92756d970eb9fd11dde19515a763adfd6ad5a8f0ab88b5a15300" + }, + { + "msg": "702da967b4783e2616e962e5cdddea716003f6f26d8a8d51f7eae513a0fc489c8714637e7b2843", + "secret": "2d215cbef93b45050abe47a04e41820ea882e15478c80a61700dfe6629e000ed4b5d5f8c30309ef836d50d3153f1abc5acb70692dd95141532e0d9cb76979b761b3d5675471782b7eb5a4ca6ecf2a78333dd7d2525755c7c67bdd84e71a035fd59c884d6c9671930bbf6b7f49758701c1647e167e614dfb78cc1eb5b4e5cae1013e60aa37a9382e6d3028d3566c123cf94707dd28097632f4d381d6d92c9181a6bed954d413b671b4e98f0e962186e312a9f2af63194f6db679c2f6946bc0752", + "signature": "e18f78a9133e29091d77e7734d5c72484686d3b881aec092abfe9ed9a515c975e8eb7bf6361a61b846eedc7d1909000fc0c7b30f035d95d807965794ae0f4f05" + }, + { + "msg": "1df0b454e43327764553d41fdb38c49dd0b6aba7a3a34eca48ac9b51d27723acdfbd2e53a11fc0cb", + "secret": "f29c5e214301a80456e0fdc1ce1057d1c64559140071405c9962efea0c41b4df9ed1165f29b5efd046a24c9cc9f1847858e56ae5713af4427a3dcee9e4e1bd15daca2a02d50fbf13c7b85f6da6f6516446c1e1588a45e5bbbda1df5e5491b9c278a9c11e16dfa316fad872dd85e253253477531e04fae81eb7cb32777ed41bb7f691cdc8f739424e7a562d1a65a159d69dbdc9b5de56830690ee9cd4c74e36421f66f66077b7e991beef93a04a0d4dea009253cbcc7f131ac2b3bf95a112fb87", + "signature": "56f102fc039d0ba0874d0a0dc5fa2e59db387a1f7bb08bd185b2a4f02fe184bf9f8f056cea8fa01bb823ae3870bbead77e0b985d9dfcdd6d3346f66730d7d900" + }, + { + "msg": "6730edea852eb70f034b21ee848e286a5171895b4389a6322221098b42b8dd9725e10b5fb5caa5539b", + "secret": "67e981670154aa18d3399ca9aff90d7fb43fd188e3afb111416f2104e92476afd764e354fa634c3005bedacaaa4e4dabed52b691ad4530613740f5635366e9610b6f5ada70de3601ab9d026323fd00548b080c8c79ea5df26621bfa52634c4e618d514ea6d17ee9e1e94c822a0281134d1f840bc43599f9e6201b2ba2adfede0a9f8084d35b9c640219b35ed253f9b126f001878a942e5bf9ad90f46df9517f7ecaadb10f445c8135f2ff2086ce338c1b3a047b91918833cb430c133b0c73608", + "signature": "a90a69c46d05a10f3c5f6e4c5acac8e905f435b235224a75be3144e166d5bb814e4b70c8ddad099ff6a94cae2280df8f9e79f4d441798eb111d2793887a63d0b" + }, + { + "msg": "c9ab90f5898d54c9d404c116129ea40bf2ef25f4b04ef579d8e682de5c6689ecc50a15069618a5d62d42", + "secret": "9c5be1acdb17e577c995c03e9d5003f5c36fd00d55f379484a28bd7eb1d5d2f76caf507f443b1fbfc1cce6c75d6eca2241a835352e1986f1571b07b9b5c32188fb70571745da40aaccb40c73e28f48ecad299afa81662468f066c21bc6d29e98b66ca73aa5583e0027d588ccdae4045aa821cbc8ebb2d91237c50b46f6401210eb6cfad5f9af177fe05b84a12dd97d9f27dc12213bb025726e5a186956b4d8da767fa378aa4c94dc9e1c722e3abc056af3e5c563d8fe551731ad9e4507e7cc53", + "signature": "a7209f24303da3a436a1cec37742743f4f6a7ec8dc165410452e81dd92ddf3379ba10fd1231f9e30a7752daa285a475e8553614379cee67caa798f1f78a98707" + }, + { + "msg": "5bbcde7382836a1ec2800d6ff099d819073c7de4b1b335047f767a8314e85e613356c8181d9b7070108782", + "secret": "fcd5f264c3ef6ce0daabf14d246a7f2ea9c0cd42557054455ee16e5a2828deb5db0058319d014522d80b62b8c8905947086758c36bf43676d900d1955445002cbc0766071c438963a16b06c185c6b3e15a20c734d131dd794411b92d6a1af13ca20b8c26c0b789c54bfd8dc787ad0ce8d1b8ca95549f52317165d944e665b6c81e0cb062bf848cfb67ac2e7339309ef655de49227ff9c21db3563be10fde1f510e8dfa92c37b7f3ae1630b8d6cb8e45e1a045192afbb6cb4098b0c1aaf67d4ab", + "signature": "00a076b7f9922bee936b34a5401b867869c8857a16154afd30af2d46c49232285716ddf1d6ff55badb6d2373228caea1f6cbec05c5f85a8a6393c51db2557801" + }, + { + "msg": "55c95b1c2341d92648a0292cc8ca91272897d880d049e3910021cfdb3fa93ab9edca4a33e90cd8792aa099f9", + "secret": "164b1ef0092a6cb908444ca14615a9f6d927f05e384e0f2300dba1ee710ea054f9ba6937e0b1ec9f4df1bc443b2c1d0d2ab4f4e101ec3c9852b47ba275be7abd93b75a9bb03fcafc39cd5fdc5e57d354496616d467ca58f4537d48005601198d56181236fc326e5b819b167f5083e45977ed3b5063b3bf5997b4533182f84488e1c80130dd74736c1f7ab39ebb96723bfca8892d4d0a81acba5c0a5663634517c91a1e70e1b9a62c4fcd41bcff71c140a37a6541b5e093f96605eeed64adcb18", + "signature": "a9cdf4e2c3cb174f11f4be60a26763c5136771c05aff665b4bdeda10b134b2c92d554a9ce930e8721e6fc313944023695203583221b01718073aa5fab2f4de0f" + }, + { + "msg": "149bcd7323c0af5994d9f7f7e688d2a12551ebfdf4f747ad00d9a7a3a8de3a9072d3bf2cef800289e882c065ed", + "secret": "6e74b30c68e04365d044af24d65f27f1119aac5eb6218d37109a61388d1f0d5bda118261a1f08b194455454f92040887b49e1257a0495c9ddd892e246389d45fa0d970ea2900ea229650dbb452bf952a09bbf0bf0379a4561126045115b5c8495cd074599ec22b13fe84af63fe478c415611be4ec08c893df96d25a489cc8786c90ebc1c1b375936151b74cadd130df2b250dbf319a046e8da2da361a1a0ea99a23aa72b9603bdc6ab72c2707a1f79414a4fc8647d60bc33a3cd5109684f25b7", + "signature": "7c9a9fab3e76ec10aa4526e603d6370bc4e7fc3ea6e5b089d5f5f899f0bb8bd10f5aa29d4b76a7e987de5070c946a231d55d3de30bda32c48c4444879b09a204" + }, + { + "msg": "07b841a0fe2b2723a0d1584bd74c3ddf2d9cdf0290c2b4973e8bcd31c49ed1f016ce2059d48dc425040cd53e2f0f", + "secret": "71b6e612883220962fa675e3302669f422e523d24b3aa9f84325b0a5888f0c3b8b785206865dde39d541fd899afb3453172f75e67893a6447f5ad03849d385d6ef216457556755ae299dae2b05e021b5bd3b0c86b75b192e66adeab8ffd2a1f48d55b504ad86c7af9c486ff1d9a1ad18f3e52a6dc0a23e494325c6bfdfa2a02f359ae87faa2bc67bdffa590b487e283ecca47e4ef763f1e5e358e268fdc809abc21b3c25fa865248e8724a452913328fc96a77d4727eaaca0d8c259e676d9ddd", + "signature": "ddfdd27c206b8a518813dde99dc9804ab9c37875a83a7f7fe426a12f06cdd4f1de49581eac0ab24cedd551b11928366c9996e1d1962fe880946a79c2db5d9d05" + }, + { + "msg": "10f9c3223dab949dc0524ac80b1fbc30b5cfdd79a501199d51a3401af2685cc21fe75895f0e82dce5dd5cdf3a136fd", + "secret": "aed3ce91f957577ac7f38bce9fa83e624593e8222e43be96167c9404c6753a192643433c94ae29c33f204c74e74261a766ae54369160f20920ffa9d5839d285f0665f278c9718415c9eea3ad73fc767907d0e444eec2acb2afda3dbffc7eb42f845ac5cf6a0d27dd6ccd4ddab6f4c751ab799b7c9cfa5b382d880c66c9ba6dcbd67e1e555b143a5601dc4ffafe5eb9b6c4905a0109643ad27819de166b68b67d1c6810b0a6662bbc79390d3c896a0d7e004a78160a27b73446f06a240a5665f3", + "signature": "bd0312703cfb7980ce4877adc25f6503130db317ecf77fc2dafd27a115acf90b0e825d8613eea5cf8c8e6c766101ab7ab04abf7b300ba66810d630dceb42c608" + }, + { + "msg": "35718573e877ca1e155e82a06ee0cd4392ea8c6ac7a1c1d2edf5bd2340c2d7704d475d21af86b02660371b95b590d05e", + "secret": "b94af5f87f946957046feef4b21b9ac798877af7b815c87c9d9efef55671204c3bd65ae6e608f74cbbb1067c99dd5c2fd319b9040c9f85e70d2c7aec7ffb7cf5f97bcc873dca0f75f414b5e4cf79d60abf61cd3e21b68e5c75444d6b6fe5653dd195149e4a5e23f427f1e30c91b369f1cda937e3c14217e7492159577648f726636c4fdccaa1d00b3d2a2f45bf3bc50b97576406c456e4de7168d577f8098962e1fe889ca7356fedd671e61bd4f88fb2f4ca9489476b5fd621be9cac219b3a5c", + "signature": "286fcb5add84493a16e770739f546eef81d45736d12d94a5aeba13e402ff7e7c63176f768b4279d740d621eecfda8464b58a877e939b1838b2d8e8a8bf4cae0a" + }, + { + "msg": "ba4d9062d9f378d573ffd34ea72962bc6b9e85addbf6c5a5fcf12e8c0567bb20a07c64d7b8f6f0b09bb1bdfb1e11ceaf8a", + "secret": "95d5c46763f8f22fb6d168dd629c726ee3259ea19da14acb96b9755587b902d35e6df94c1e0777f5a3b730ba3f37cf49cfc7493cb2a10bbb368fccc3ffe4c072ceea80673b844f6c9ae8558d6056411e29bf6df7d63330cba79d9f17e143bc68c427be10588b965ea478a61d81d70e52aae0f7b63d87a2eb94e11eff07195aa57b00586c44b9927a4aaecff6fde6630c55995de13d4d075ae9603bbaca615bcafc070ca0c30d45a7971af5c9f1847f8eba688425b2e19ae4b87e298de1026500", + "signature": "4998e0dc515311ca2a20b04880bf2b77c2284d5a08636b449a0073d50fb4b5ab74226f207dfc7181b4b23b2278decd08e75e081346e4c23deab8aa978c5bd70c" + }, + { + "msg": "", + "secret": "3ba0ea5db2ed03ae9634c61f014b842812e7e96f4c256ba05ddb83f5e83f0ed22ded2f38a935e6db3891c5ad98639afe0505a3b4ba525893e72d6ded7445ab0f50bb103fce9a4a533dd23287cb738fca81008d0995fd916f809589f76e9608c26e7f29bffdec661df3ae4815da4d09015638f57d1cbe36ad43cc84f54993a34e517362082e52ee08bb79be5ff113d24b341eb364caf9ba538adf8091fc3aa4e269819a516d26315a7a6bada532f6957a9b2d28aff2eeeccd578dffe63b4772af", + "signature": "2e38872da2b2e85eec400c9445e5ce4714657791a9dd032de609e6167beff1d16d22c93196469ab8fb31d00df3ae8eccec5d3443d9257b112976effc80c6e002" + }, + { + "msg": "b2", + "secret": "a195420fecfabb5ad5a2223d588b3fcfb3c6fc5a1ffdb2073fb5b7f6c948a33ac3bc2981e3d1cb41bd63213298bda719353fa597fd611840b8390b792411416e14edca67a9f2a464667dfa45e9f075eff48204ae81a322c958b44dc86e68a979f389f46cee3b0277b45094a4ee9ce512a65aa06a0134f25a4b64af2e14e678746cef118a327766829c92185014745d1212604ad3220dc46fe2a3d25509eea96ed4ddf3ecf247e19fa04f798c5034ea762da57befc527119a964289d610b4d64c", + "signature": "d1e663325a65080b16e3dd99a25b8b2b1168ded8240a6bcc68da9b60770df6afff8b2e9e0eddeaa6f44005e1677b4d0fff57eeda28baf4b5762877600d0f2606" + }, + { + "msg": "07d3", + "secret": "c5ebd2a7623954696aee29be91f0ec4043d5b63fb4dfd78c283b4c454efff1f7b360c914fc5850761f5670c43684214f419bd9ba38d8ea68a001a8cd84f7a39af1e88b40a58c8d2105c66dfd767fd2d9c0a7ee1b713170311e15239abeb72a73fb4af82f50bffc53e952a4551049c6aa1b981d0a41ee318bf81b458c3fe39d7c458fc086fab52e66c18615597c2273a5786bf0b1231838e66c5be19906c7a9c71568ba415a3c64adcc07a13d61d50ced31bc5438aa3f574ec7a5c1b762bd9316", + "signature": "2fa1b6d7133354ed5891586395824327ef9eaa5b0378afa312f06c89aef3b04914ec9879a1b0247df5dcd1cea9265ef2b86ddaf4899ea0f7213780aa7a9da102" + }, + { + "msg": "02d111", + "secret": "88dcbfc5d67e414d5bd7509c5bbef65ec0b82b9d20007dd4973e146711c9deec3708b0a6bbeafbc8db485b91ac8a6a46493ac9c031083905de7cfce6d5295df3d3f5d3a7782d159fcaea9ca17959b8af1b086f9c163ac618a0dea5b369cf387254ea8da56769cc7a7831119c7c8b2585d21fcf131b7cb158324f75a5af3280ff37f4e15491753f6df12857bbed383b0555a1f7b44066a06451cc4e312c8eb04bee131c0ca6bc82e88b0415f54bc17adfb006d9941ac44997a3b3ffe3d7da6eb2", + "signature": "a0f4964745e93bfc307d3754bb7a4f41d865a82f87898203d23af0d3441eee550560f478c455d1d0ea4a21ddf09f9468c0795d81440bc16f457b91ffbe8f4807" + }, + { + "msg": "9917eb08", + "secret": "27692eb4a3cdc9b03fc64d6ef0bb55a5c98fda73e1bd83c7155c10c246564352e5dea27f3f15fc75521cafb8c134d211a995abeebca035f9d9c2a1dd974e068a7bfdcee3095b90139a6a7bf3aec3b9c1e73083a4bd36c3cbedfc4f975dc4592d7a71d232acd21a2b0047c5dab4171a7bced3ca6af0e50e0090037ebcaf79272eed1d1fb2e2d5f2ba7f72cbdb7365196a01a604b61ac2d066f903e7d9f75ee14264477ba95be025f155b040632fbe92a5f30d3f1eb7069de0aa497db5b10ba2f9", + "signature": "89c0a87371f4b766a81652019687845e8067ef04daeb60a41e783e34d4ad6d6a57589dd0686e508001b9b721229ce2dc6ebf2b20358956d323f10574d22ba103" + }, + { + "msg": "a93ae44df9", + "secret": "07492586f31d55776b660c5970575aea38d5702da28d66115b55629f290dfaf9f62a29a0d8ba8a1985c1268eed0d60e6c7a5d71fdc0a193bb8dfc8e2c405809fc2ccc5dbaf25e19a6bc2a6dd610ef4fcb8115e218b5b877f7bad472cbe764c0f5c4caf61db3662c69cd38daee329bde899fb3b789c7461cb252ab5dfc79daeac6bdd2ccf00e1c169368b1fe460737d2d2fe5b54ea89abb1dfe6bd4f042bccb0a08a360c3984e529207b37319f7330fee3e9c682f66c9738dd563515a7d7474f4", + "signature": "2a5fc751f5db366baec833bf218d7cbd4e66fb1310017ed9b1b7dd7d7ffefda62440e43178eab965565c3a5218197ad8fdd686420a3653de6959297f04b5fd04" + }, + { + "msg": "55665bd054b8", + "secret": "fd3132f16b52d30859be067ba567a12dfda779391b99d608879faaf877c9f08d8b02d3df44a0732b33e4767b667b4274955d917e1c958e4ff913015ae248a61e9be8fbd50a12d7b22cf5735466520f8609cb87bd506d91ad838ae11ea05de32d10a783feba4c0a45c56b39dd816ec18820fb22ff212d26d6219b45693c3357bfc485e549c2bdecef8b756c6fd96bd0177f09443a867d006bf808aed80a80df4d815de889035a3cddf544601c641d2ee3ce845f45e17292e8c6d18d3feb34679b", + "signature": "2afaa08c15f4ce36062651d7b8a2ef7dfa1d33f197632dc83f9b3d75aef82d26ed3652684b6009ba9eb545e6b93df5f1f645479d194b924b376b7c1697ea340d" + }, + { + "msg": "bf90d69cbee512", + "secret": "17771729e55f28ec2f0e25ff2225d04dedd03a72d8847ee4a1198bffc51a400dda3b5d6a7708e7d15b4429feb9d80d5360d88e0c8fda2ec2370fc696110ef394ae3c2cd76c871ed4c5e4baaf03fed268430347ade00eff9ab6c98a5de1ec7596f1b27a58ddfa40ea1be1952174ad42ffd3c5807538665b7a711b2da78dae7ff69baeabcd680a8bdbfcbf4d8529d11a8dd49e0b1f1eb151f64dc4e48e56d285494468a852efe353903234b64eefdc87c3be3efff0b2fa22fc676b7638c39de46f", + "signature": "91dcc09a1f5587308acf11c2d33956f34643eb4b5102bbd9ce59772cb13195771c06076650ff5c1ca6170f3008c7c2aad270a87e03c7a96c5e359664a509fd0e" + }, + { + "msg": "a40e4085e038665b", + "secret": "826f9635ddff35bf34703aab952e592017bfe202fc05fadc61e983bed92edeee281c218451cfa5ac583ee244c26861cb3aa8b47d5e9de5799ee301856acc5b20507f72313b7764973fb62c91727154afbb054fd4eb8ac6416363d962e589de345aa1533808fa4e959b4668523beac083e785b3cdc62050c67f9cf694d0643f6b07e488d582afc333d3e41f39c8bcae3ff70b00d33b9d3241760792ebdddbf5610849f7de700e8a617b2ec2a400a6470561fe41b935d73f04dd0a4e82c6954d4f", + "signature": "343abb2275c8264c3913f85665c4e1fa0450b60940159e62c61cea0f803c8fac0ffe08b1e5a829a21d620229cb34747f9b929ccbb8071b1828c523edb44def04" + }, + { + "msg": "1113c602387e2333f2", + "secret": "7d06abb11feb792c1d27920b46df9159fa51b5642ef41fe2e74b653b9acfce66e1a84ccf09b7a9e3f2488366de8472c92df0bec21200fbe39aa4ac1debdab131452ceced1ed7f216dfe8eb19fbb47d7380c71fe85cad485c608679b4adb0f5ac1407f1def67cf149297979ddba6acc3a6cd40c11bf6e681e281a07ee0bc3b1b0627d72681deec3496fee7a3e70007ef03227556d4aae0bd228ca8e72cfb23e4c167b879d0121da43a3cb7b11dbeb1f21dcd46ef8cdaa42788226cfcec8bc0422", + "signature": "4061bb04c99c2a5d22f077f492b7395fa6222509d597cc957310c81fe214f0a6c1fb37bf3ad14da0839b3fbab399b23854598bc4619642edeaf333fa9e639c02" + }, + { + "msg": "04e97bc384df9ba92581", + "secret": "fbdfe4d74ba65537a7e875b8af768ee17009fe1d962fc82360e5304ed8e117d1e4d95db7cdb4beab99278e9c8d31b4a3edf208f824fc30c92d970f3a5e2ed9debef8ea7c8b57ef5d1c58f89d70fb29473cbafd3dab3a9820991a66a642affb942be42192117ef779729a775aaf99bb64dc5d60a6b5c4dcd34ddbe2413fc9b350e3d3417f4053e1690847c12e244245f8ca0573d209e1209dee50e420f3dc8888e4696e3f4645f62dc5b259b656b51908938ae9bf3373b7e2d7cd203a63e87a54", + "signature": "5995054ff52b238c6e10523f5f5d258758e3a1423e6599cd99f64c6a7e93cca89a38b13ac1a9db40c670915aa629aa97e690b59231cbbd9c45b8bb6969cdb90b" + }, + { + "msg": "24c55e2e9956ceb3413553", + "secret": "ddfd0f60b652452c0426d7214f84cc68dc17ab385664f968a464728facf6d49116801942002ffdcf7510b3da05ff99a3f2095e45a585e8e78585e2c5ffcdee9d35367af9bae9da16222acc625f18dfe8bc5ba880d6958b6544c2fe47fe028e3a7941d55f49e4a31338d2f1915d66f5f3758e56a777cb29f399dafa2a757da7ec52d2f3fd3952398ffd22d8f0974aa5e04f6484bc5701b668d8a9f14e654b716a0d6329a9e72687f812544f4b0096e22a2df9c17dab9e981642cb1ebe84a35844", + "signature": "7ff8ae0b047e5495f3c0e30de704f80561ffdeaeed4046f231d3a40b93de00edbb11fd00a482e514369e9e76be26bb0a69dbc273b8bde3c786703cc543c39102" + }, + { + "msg": "ef5c818d975ee4162d46442d", + "secret": "2a4dd4335294f9d3b23b3f489d46db4a6b4ecaaa025c4416ed1ff99a7ec5946c2f45ad8563979103a9855e43a5a7324028e6c1202991a3f9b5cc655d28ec4940732b045fdb7b96b1c25fc2ee25dc943003819fea0d16800c4afb90354400a44c5bd294ddca43c656c7966771bf9bd52c2c878640835d6a865b9276c320875c05692c7a20ebeef369c716a268725d926662cd3c4d6acd504cb4467476d1534f8e1147280d0b8abb06a16b73282c783d2367f6276deb3a9c4d702e50fb756e09d4", + "signature": "35618bd9283522e36720c11730dc0a4b903bd3b0867b0cf086716edab7ff5df3c0ea824870b2ef5c5cea3ed2a2f577e86e6884c7ca29241c3edaca4ddfc8330c" + }, + { + "msg": "8df88409ca197f5bf4f292cbee", + "secret": "91f3c1ab651c961d1e176679a370d629900098efc731cbe0c81b925cd3983bd1c43f7858a88544cda15c27628cf1a47c49f3775bf73a01391f99c46a4366b14060b968d091d0bc5597772c38533ec4372b0ad55ddbd4434e554277a4659a8ca99ed9ebd7e9a64f92ba50d83a77b69eacdc304697052349e1ebc0bb6d03adc0e18a1771e0a6007a6761b48c6050ab880304b6d79ea80f50b4a4b9ffcf94292a73d2f363a8d38d8ca57fb35f6b7601d9252618a52129fa7edff4d35bd152da9fd8", + "signature": "a93c30fed4b28117993f4531e9b316f75adc2cd592dc92a794fd1727b1c86330f9fbc6711e90d14bd63cf01957675934b6986725967ffb6e542320ce99f2b900" + }, + { + "msg": "7218c3e5c16a8478eabe83e29b58", + "secret": "ecbf3547d2b98a558b7eb2c417b174b4a75fbc17ded2b1f28457b3a8b15d413f31c996352a8b7461d59a27c76b914da045a0bd0efb2be44baefa331a38198a38c11d9736c2b59854ee3d41074d2aa810385ddff1eef3aa9a3856c9b3558f8c46d70061ba08c0ca3adbda33d9067d24d241e7b20c6939d1cc9c1be66620865268712c325610ce1a3b30655fced7f438df57e67200959cf9c6b602d68ffeacedc480e2fd845d31a770ac3432bef7bdc1d780830fa9992fbae98e101a2963db6d75", + "signature": "4ee9c581b8784a945eb7bd93744f86867344cdf135d2991c98f5764231747b6dfcb8b773dc9c71154981b78fa721ebfb9aea03cf84dfc680c155c9e118805d09" + }, + { + "msg": "f4a625a1a31e3f61df2c15d7d1cf9c", + "secret": "c74d2838927971cbcdaed3a935aa4d72ba5d5551e04895c93313642f0d9f56a500ef72424a253d864466c0c3984032e646d1ce9ed1e1eda87fc1301246c151483bd2ee4925f4ee9a5159ba8324ddc1cb6cd782c79b59360f729fc90e3b5b485fece3b592317c11f95a9f4e6feb0b0a46716c22ad1c6d0058fca8abc6ee360983adf5972da7387f964a8746fb65f9c27d46d49db153839fc419c1317df54c1e5c19aa856da962f3c0b11d65246b1c3bacf1c60f30c1e86b2a716f87bdb3d7fa75", + "signature": "3b596cd6757f0242ebcb7aa14fd229c791ab653e0d25afd810055eb5f5a7c9268df4a27350f5c1406dbc8fa331d31bb471ee8aa54254825efe3aa271f8d3c208" + }, + { + "msg": "548e5b96ff60025a3d5186f7075c3e2b", + "secret": "4e6bfdcb3be7119860d5b93eca0edd2cc89ecd64d5a7f558c228d217f0f8724bd13ae7c4e61018bd0e88c31d7fa9447646e673a64afc6dd31df4fac587b0c987de8e89bb26c9acf72f528d53f5145aa42b0c1ec2f18afed9950e3ef397babb912455fab3fc93f9584e5fcc10639e78c9269161a5db3356c221a8541bf9bba8dc10df9ce0e28d6c73c682f70cc2bd5fe6f22ca0057ed95f76695585bea94a18de16539d427d14aef6fa8f8902f90eb799bd7bcca7a6eb83c1e26469b75c5990c0", + "signature": "55ff55d13b3a9e966233c9a8b48116e9e2472e5310c29c8f0c33145d0cfd635783848031176270d155016c21c1e22ed79600e1d5aab52b3fc68249f4b39fcf0e" + }, + { + "msg": "24282ea90f0552e31142a7a83124364dd0", + "secret": "be10978a583b4a2c5f0c3f6428ba938c07ece0eca8e3d728634b81f062b9b3fa22d9c0e65e719ea07428a92e79901fe28e9e6617b73ec8b23d85c605dcdf02b41ef0cd0bce01cacd35281e75db139ce83124de2ca13e015441dc5aef28b1b55db77ab8b24b9d2e162d61fcb0ad9363615d6c8bb053b90f3d97d08ee6063c5a91825aebc0fd2e46471a05c620e4147082dc6bdedaa6e4e9c435753bfb94bc481783f3f91c59b14119f2e034ae3583d628be69b608207b053762eaa5ee42a652af", + "signature": "a3e3125eed5a7e895b153826d7950170dd4a0f4ae45464910bc97dcf73602eaa92d0f231cecf1952072c8407ef192279b76df9ce8a4fa36cbbdf1eea86c8670a" + }, + { + "msg": "86fabb9b4a134db1c204231c70ea38094815", + "secret": "0aed5a4799bda40892b4d471d4b0c087c7fc5d7a560f9dd3df750fcfa7823a93e778414d133d3ff0ffa434898ce5bc18e449e3b1fb9a27b44c6804965eb4d36a753fe1bfc7048d6abaa5d05e2e811020cfb05bf301f95b0ee265201bb77b2787bf117b2771b1d1faad04cf9f43ffce32a59f2ba1885411d7e80c8e255a4fae0764f1b9ae911286d2e8fbdb9571b06063847c6caaa83db9656555a079a9ebc8b95daeb2b94ef1d91c7c965546b7a1687e5a9e2932f4630b7c27490c04ffddbea7", + "signature": "2725ce0b82d1bedb3aeefff5aaa764f0b31b98895a132ef8e7d53f5062eea2c07dc1d12e1c13abbbcae50590dfef99bb9e687f142547a0360c71d1ad30083705" + }, + { + "msg": "acd604e7f62a177a3d18a9534a85804ed8123e", + "secret": "737f29e9e0377c2b314b2f26c697d43b3969dd05cada1ccd44d8cf1e8e674472c28eebdec075efc5a119a5a44aed4be06dabd91bc172a3099cfcd461ed957005cf75ae9a5b342de6c16ec4a08cd7846558bc738c2e9a58a27cac144d7fbb1072d98cd616446757c367f868ecc354b3ad8706ba55522505eefe5ca2f6bc0a5ec3bbf63abe2bb3b0ca510a7e8b4867dc4df6bd20cc4bf505ba975d481a6c4fc8bf69daab5bfc3cc06367e09baa4bada40a7f9ef7cc79f1ddbfd7d2eea0b0ad3852", + "signature": "f57f645f672a17878c5e828aa067c74a818640e4b6e431e68de5dc6127d72947e5c02fda851f4a8ee364d53e1394ce570cf7ddbce1eb160e8f9e6d97bf6bd307" + }, + { + "msg": "689d82c0a2e1db2be2e7011614db37293fe7898e", + "secret": "b87fda13a159a7d8a9475d87bc58065303ebc2e83287c47170cdb7e2f7fbdc88f18fe8782d32c2094abb3a37d662ddf33b4d9ee4f658d36cbb17c964283e373c37438314ec888dfe0e443415fbfa011f657c3f74896450d654b7996a5ce43213b3972911df0e36e8d2da5a6634cd2456ba93792d68517cb21b2f78a4f04020d52c353552891c3fd2f9f7a13d0af18ce578d22fb0655629accc1040f2d67ffb289bdc55125ba4d1e77224e9a6da2dfd55f80826526a7ea93f908db7ece4d15ac5", + "signature": "6f4e279836c911d295c1874a1fd6a49bd5bb68639ec0a94feffb532cf557429e4942f572b30aaa2146f3c66d1d0ccab820fc4ea48bb4c5ce7a7a744c0398ea05" + }, + { + "msg": "9f066adb48675af8eb8d67bced6955633cf74c0f73", + "secret": "731dff33128bd0835dfdf8dc20ebb947af98843ccfe98472e6f5a02bd319c95a48d9d8032c3176301acab12acc237deb510da175a9e11cd3955786a73dfb9c50832d1afd4c6faa9683e769708009e73c2a2e91aab5ba9f7e5c08f9bb641769ef86b3c3fb88be766be32f60d2dc780a1f7dfe0821ec8671622a6117e0b730e6be00083162aac177f5c9528373296058b588b7da4e8a07e6b62a0011263ccbf787718366b8eb5dd3dee01635c5eb7c085d4a031035f527435c4887c3ed28af2118", + "signature": "a040955625362599498043cf6fead1ed39e2821953c04fad46cbf2cc6b05999014e6e9bb440b7edb09fb1e0be60211c7942d14a05d6152c5429c156400bb5c0f" + }, + { + "msg": "e3383a02c47e8e72890ffcb24cd211018f69f9ab8c49", + "secret": "66cf25a5782b57aaaa99b20429c1145d9de562ed37fbbae5e4fcfcb1725d2db23514eaca76e737964c21537bfdb33754aa9910f605de9183482720d6030a265e62c84ab62d7a294803dbab41d15e3bb10446be65617540b490258facc4d1e74c9c23525d55f272774029af2ff12a0b6d8b8ec0c803cb849b40418a8ac00a51620e3c0b157d0e5330123f51536348576c993023ba6798377325110caa87c004aa74b32a1c28226be9b850cb5cfe26241c4c1450b04446f9c2e2b161fa1ffbdc2f", + "signature": "4642b353931d31cc8e3192b6f4815741e418e76af32b2d6f8707742e359e3780ac5f1e501daf9cba82158f35c810b11242ccb747700d1533683464d847815d0f" + }, + { + "msg": "bd3828fcf6081ae1e063cb58c7771df439a75e4bb8952b", + "secret": "d5a62933530213a0ccb01dcf3d508bf048e2998cb8fa071afbb935e516e09d7c5e12830d5c0c2c300d887eae7cb422fd97ecac4bd8e6dbca68f75c7ac4f1696768bda06fc9358954235b47139fa62f3fb5c75dc332d54484a634dcc40ec311250c85b8f298391aa3dbfe295ff01fb378dff9ee3433370553781f01678ebe89360b4c7136321abe5814f918fe73a6623389418398f9d725bcfb22794934247edb9519aba807411e889c50838323de4e6aaa875db375b70576a1b906680eed5b4f", + "signature": "0e2bdee200b0016d1ab4d81f83d3fa62e3eb51399d2f7a134cf9def503b7c10716ca994d8b36c9acc0721d08c04518b2497a3f5440c1af914f44eac8e44ee10a" + }, + { + "msg": "511d5e7857c70f1dfdb8a194bea846071e15031f702e47df", + "secret": "f53708340d2ac758b59cb7d6538fd9c860043ff3443f8cb27e6a1e70946c322a354ec27915bfab3897884a3c536f59274ca627d5dd21fe1e4841f12a31c6437a4e42525edbb11b40ddd685cdc415596b177d2594834b7b11fbb35c3b2c1de751fed3eec10f11f49e99de7ce72797cd47298530d89cec0d192d0d105d71506aca110a933ee15883edba920ed42cdfe42a88788c67c0e8a0b4fc2cf56865b59b785a68f6a2cc8d515d20f5b338d1df775f536a05544fea469685db4b8f7b2f950d", + "signature": "eb2210e0e85ab7226f37190116295b0bcec7e34583dc649b8715721a004281d0acbe9b282ab86beb0b6df2dd41e2aa8de9131150f2201da496d1caed45c38306" + }, + { + "msg": "ffb0824d125bc5480b6e13a0350620e0a3cdfc3a9b199a95a8", + "secret": "1bdb38e077a9a5b7efe378edbc2c75cdf3b24055f38d724333d9bce20f4fb0ee4a7d7f4bf474929bbaec893d5ff8c4609048f3d92cf7c9a0999c54ae7819c0e3af997a14451d42df3878e9ccf1b28b6db07c7cda7b0d244943d0b6b86a60cea10476fdb5517ae909672fcd1ac44b8de8a8c69d17db0b6f7ead054d9671ddf85e3adc44102c0bb5c5fc090b49ba2385125d2c9968c880fd7ff3adaf3cf5f33d7ce4c9d7f6d91a24f08b839d10004e5268a5968dff0a92555c67701e274266b6d3", + "signature": "69398e981ae88a415310160c2735021c1ede52ae905a7f999373ef05abdb674c56b7747506b27b7c9b14775c703851a488b74d254b943f1d7fcba5797b4b3809" + }, + { + "msg": "0a8eecde4e3c49a08197b5d4cf28b09d909025d960e5bf7f00cd", + "secret": "4dc77ebc9bc2619ac096eb094b4181b328914131f92c109fc0645c215c1dbc3e8a4945c6ca6150536b99d59d8fcad095e92c5b8a165cfb4fe0f4a31b8197e96ffada6eb106bb2419fe43c5be9be8e49c1733fe3f505c55482f33c42cbea5605119715515bc85748fd7a3057105ca6e022b958abeebe54cd603c28cc17232be9a23fee76ac701a634b186a1cdfeade860ed6a40072109953900eae878f6906cf5916bd7f0e705e80d171dd5b9b416b98cb2fd9a0e28962afb5cede82a5e1333db", + "signature": "c0396cc3de336bdd093754ae2cc250bc7c19a4f8c36f919111385db5810243ced023847e93cc97cd2ee99ede370a5e408a1e73c2cb4b110318493b592405e302" + }, + { + "msg": "60afde19423e42cde9d69ee1f0d23d1d8621147ba6c06aabc58f82", + "secret": "e9aedf36869273dabc258b1ca1acfb4aa8c7de64f68c63db930d4e00dafd7989a4709acdbed8c2491ac0a653b1fc01a154a694cdd3bfbb06a1b4bf1684da2c6d925493ba79a249c132ff27a49abeba367839cadaa30ca2834cf6128b4d2696f9b72999a06c4b668de383fa5080f22b315856641ce4c15307786ef577b618caabcc655bd04d2aabc2331da51be8f10ddccdd4a105fa021cc9a3b8bb2b0135fd321e1ada1ce667e72f922d87f4652c9f1a1082ee87370e4d12f2feeaaa599d507a", + "signature": "7f274f68075feaf3b4627bbf078558a72848bbfd31610554913ca6dc26ac11b2e8856680d40f26561ca50c7c42804ba67f66a4a979f949baf96655c6b63c630e" + }, + { + "msg": "46f0078e145ee40f6f37cc158c783b3947f237b31b65eab547c910f1", + "secret": "d1b579f4e4c4e53af4abad4709b5b4d15befe3017c6ced6a8961b55f0be9e4a6c77b905da44bb22632dbbe80e7544fc3fcd73ece17004d5e519b6d5c90bc847d95db378e113713d3b31b3e2c62aec9c77d72a907b5cae4ae9ae0b4653d8ad29fe270d04a79ed343c63506b91dcf5cb5f217a398adf06fc71839a319e0a14447251546cff21fc24d14f950aa6537b2dfcf99701a11f10d9527ae602c06f401622ca4f211d822e71af8f576b0e2fea289b2c09d09c99f2fac7880b67a1baf009f4", + "signature": "031215b491cfdaa056a892bada94ca0d8d03fdaca26cf6689a904e7b9b8099b321ad046c8f83cda13fce79e2ad83f5876765c941c5aa0156253ef1cf4d7ed001" + }, + { + "msg": "39fd3596038d2bed3f46d85b23d51560ac1b39dbb14b33763a2fa60d6f", + "secret": "cc15a5e9b92de9227605cd5477e3ddc3131fcff1ba667052cae9baecbd1ef59c3163e987555016e11e1a05636aad1d0a3cb76e050bfd68e3246f8e5b817ad1c344e3681761e48a3a9ec868c0d8110c429298eb2b7b7469cfa2994e175c802f6442f51f6d3dc38a9f7d4e5cf02bc5c4d0dfde227fe381d0b419d6b2d2dd7955de558907b3b983f4c0ffd290844b59615ba86c6a225f8ea5badc27890db2022599c59f4e924c01124e0b502df3309c2e6bec8b8f888a87fd7c75e9701cad7dccac", + "signature": "35af05ed1d553f8bcad186f05dabfb2d3c4eb2761ae8f18b5f180338b59223d4e30d1b97828fe0b0aeef959b658f6f5561d76b201fe1132ccf3f16a9e29c1b0e" + }, + { + "msg": "1c99a7764739f8e0c9aad738d682324634ef2537d7cd8b6ea7f851e18d6c", + "secret": "732a8a082a9519fd3dc9ffd40ee588c294464f17bbb1d7fa28b0366a9d050bbbc65ec86b3a980dc76c0fc5f0cb430a4482daac3632f424de772ced54b7cf35bda4162d4a6aeb43b0641d999f5bf4d9e00205b507b737f72d650c1a220b19039ce6d688ec9edd5aa75fb7e2e6aa15f43a79e511866f9af846119d5c37982cea8943a481064eb29eab2dc54111586f0262b0b2f792adfde5dcee321de8f3ebc84cf363e432069465b1b96da66fcd3be94bec1345608098c2304c8c324fe346d7db", + "signature": "42a14ef6a3cdfec49fd6c5cb20e951137aa765d2c83500115285880afb2a30439add3f5ff434cfcd3ddf93e78ee38daeba8c2ad0b7cdc0b248d2fbc457244c0f" + }, + { + "msg": "64929e10df00f1aa90f9d0eda61e3e22debe6377a8be35157f85b7b0aee0c8", + "secret": "70719c9cdf2ac2394d78eb22ad181db2232238ec93742ffd44d4714bee563e6529bcd455297e28351ece33e1c06afa89f51812990280d4f28653c96a79575a6011dd84f75e32db5ea12daae4f6a02261c7a99f361b87ec8719d538c3e0bcb54361077895b7db8925b154e15d97830b6321d7bd62cb7af434de3459e624817355b5cba770be83ff783e4c7f99b7065e5bcff04a6a93c575781feecf6ba1b0d66209c92f008d726ca659dfe319f710a03001c7269442d4e96bd0e16856f5f58c0c", + "signature": "3bc1cd00cb2bb3187beb801f15eebe92740c90ffb11da86480ee2abb7fed56223c44630a814748324ba68c12ad376c898b9ae17f5bad33ee507052e8d83afb0c" + }, + { + "msg": "af545990fd89df7049da9f3e8a1e2ad3482779f3499b6b86e978fe05061beaee", + "secret": "c14183eb17bf2772e365bed0085578aa08e1040b5f75b904ddab071506553653b09a7ccfdca66ae98ca14be37cab1df8de5abf83751fff7f5cdea7a1fc2f89cb4473295e48795beab231551ad47b55684cd3708146e2252ac285e8496a6a2d522e476c6dd77f920bb149a83780b945da50960154ce7e4f7167f0003a4f43d2278b2fb513f519a7992184301adc866e8a3d79e8b026bbbc843ea90aa1f335c599fcdbf2733abb8531aa293b2ca02ec88fe80936fb96de111c259c665a20a46199", + "signature": "ea39dd36920a0c806028baf6743ba2d35c5dde025ee52e25500cd1aebe7d627977b98d48f09b4953354897cc6d6a47b549e2b7ddf3387af9735ae5714c911405" + }, + { + "msg": "a84f75e8afe27678f98db708390a51220837a4c7ee32b274e228445a8cb3956aef", + "secret": "99bd6256f2a98bd7cd331438bc0e6838803fffbf9f551848e7a724ace8eec0598e35c9c479af6a7a988ab9322a0c419af3190966241015755e041e8f6c00ddaf0004e1c634d566d71bbcc3d38567280972fe4ca57a67fa3abcc320dbca810aefe9a3d9ec8bff13b0e1dba87b44c90424a5c1f3ffc2b79090ba14c9b6d5b483bf528817b199bc59b1c615f6656e3d11f18f3ed14bdd4c78a961fe72ca12d49059f066c7c7ad0da7564ccdae34d173b1575e9dedd107bfdaa5e59eae98087a3f2e", + "signature": "4a9236c04919bf848480690b4588889b07149536e5da423df41d9cb953ba5cd8fbb8b60fb0f96de56995647eb3d752173d82559e48b09cc75dd6f7179bf24c01" + }, + { + "msg": "b2069a1814a8bf1d317c5aaa80fe52e4574dcaa33d52e875fb04cc2dcf1e68d3e40e", + "secret": "42375e4b32bbd1a12ad31564576000ba77d961be083aad33d98bccedc754820a72b33f27b258abe636c72fbf1474ed6a587fbfc9b64d4b9123b4e3384a4914ef96359c927fcdf408e5d80273f0b474a1c73c67da072f214e7b325f35c432b47399c19159e3966b603c58819cdc394d149303bb1fd95794e3455381621b6a13136c567d6cdbadb6d95f8f43eff33b943424c7738ee53b0f5829c727b2396df6e61a1363c94ff0e62107ef8b0955cd6bc40e495a6aa1e1ca08276b9e703eea670c", + "signature": "b63207d5660dfb131dcd521c5894526f2828ba60a20ad3cca6f75f94800d2592d75fc58fd0ba3d81876683fdf941e9ac3d3248faddb20fc96f5be4888fdce505" + }, + { + "msg": "ac48ea03b563a904b1267ed01ed1129de6588955cbab48690cc07b9d92db9c2412e379", + "secret": "6b3af10ad9b126f48ef9e673605755309233a556e2cf948df1e6113a4bdb90c056a85ef4c1e1fa90ece46820f8b2df3c107bee9920218ea0ef6b58de791943a2fea9ee8de42ab5d1ba39265b95f0a7523878ddc2166ebf864d6a2de8cf80b2a7a80a9fa45c0362c58c36d51248935b36a4a8495908a7235465ac71e8ef2d0b1022b3f21d6c7fd58e0f6f42a7f1b36346c77539760f61ee1b78ead10bb5e58084b3604e64f44dc4f96b162d853c1a27b0ac042698f33fd99ccac89097ba09da51", + "signature": "d9c4a2694db3a096fec94896bcca8277f32092d51b85d3bb441d180350896018681e78e26cd0fbb0857398238ac14c859cd05ea1ccdaba499d937e120ca83c0f" + }, + { + "msg": "4b1433b262da3a22b52054d6cabb33becd556a3e27235db655b34141b87b39c4d779f5da", + "secret": "19813194683d3a28cc8658a62e911f82e8f132cfb259ef0b0a8288b2ece4a6dec3fa679cfb293966e5e964c97079cb0bafcec9b47390cd3a6b098c0ec0116cc351f85860a4c84bb9568983f864a0614fae9bc2fd83f97cf174f626952904aed3fdd1ef4f2b26436f6c99937e51ad94667b6b1e2d13fbe04ed7ef3c31879df21fe6ff5de3232e6f64dc24a8b8a28bb20d2280e80e85119e4884d151b27cba62fffc076cc03cd91c4bbe5107e9026e06384199c3bcac0e10a3ed71044cdcab9148", + "signature": "2f145af16a3d907f85f4b8d8093fff281463ee822a2c0c7c684a91e66a4fd073866442aab89ec4f50ca6cc5719d562149335f617ad4116a0c8bcf1fc56e33404" + }, + { + "msg": "73cec893a58cb9b28315313d7545468099a13d9c02dc19b359cd05076dda945f43cc46e243", + "secret": "17dd82667de964450d6f709c4f750de26bfb7baa98bcb216159fb8d3943816c682da2a2c55ebbc2e55cda8ced6c937bd26134ed7bade58b70e9c591461b14a33ac723544627848c679be8f38149a45ffabc05b849b1e5636e251c937738754d2e9dde3148480e9bbcc8a2174639178da3400850a3a744d89630d49c1032bbddbd1c50cebe24a42a81a79e60b186afe1e0fc008d2ba2a5b33ad293d66bd6d2ba2d7938395eb9c1f40900567421b67027589db038066235e53225d7e72229fff05", + "signature": "80703df7bc962ebd021d99742c55fff0f47dd51203d6b0d76da6dffc50818a0cd85dd97c43006fda3f6620be5918b40a53066db9644a7dd907e37e222733ce01" + }, + { + "msg": "6b06b11a97f37a65c4909555e3d70597a77a98441bc03cac1c52f48896ce0d77588c081889b4", + "secret": "aec065a55012a4c9650eeca7e273821289dfb76c7213a1a9a18b2cebdda4fb8bc4e8838c772bd3ef338649c96eae967645c67d8252899c1728ebf64081fbbffda94e5f1d9d4eb40baf2ecc5b6e81aedf7d24f42fc514f4fae90e28de72fb027f256e4ee792a16c80583a648b9544595a6539e409ea8488c23be5dbee2ebaf6a0e9dbcaf3778a84bed11d1807bdc1846edf58bb6f0f13b7f02094a3cf800293eb840d9a6f3895743b5d973f3f5c01924382c62324990989ef7e0be3f3d805179e", + "signature": "c58292e9cefd8512cbd5462bbc9a6aba3ac16d068f270b0639fc44f06a796f67c9ddf4e90b504c0a9caa5bd0328ace759f24372d266a14570851ccdeae0f4002" + }, + { + "msg": "2da9dd67a5c4904167b44fef0440609ab9a88c145179d1e8b8867182f6779d94310ae2d3965997", + "secret": "39e31b24866c55b48c3a9a4121b000513f61da42c5147f17a38e7b1f6c97390eaae71fecf6184667bbad56581ea2752d7e21552f2c5680a6884d3f01e768f4d232d971cfac40bcc186003cc0485df6f0117bdde0311200cdd191f4a739a2ecc8d05a35f9f70bb92ae4c5f8a28bbbfd476b8eda3641cd72a85e1b97245e77fed1b6758023a1b95746436d58f877d867203a2a5a8750a31eadbb2023b9c6bfb7e9e4bc3d57db6023c0e4e33548ddba68fadcccb8e46218c1afc105d819782ff404", + "signature": "69f9d3ff05251995a64404f7dc45ad95eccfb796e7d2a945303d3f02dec0dd923049a8d51f2a013cd7b4d293deb4fc764128c01681d9081af0e5cf03360f9b01" + }, + { + "msg": "282779a570a02a1cdedfc661b7eed3e62a571c582a7d945aa01f9974abafcd81869d7368c68b6311", + "secret": "07e539281f7a316bce5ab69c3baa1b7ddb2d9ffa10ca6d20e5be1a535be5f473f8bc5ccd98726bfa258d9f1b244cfa22f6682f5bcff19d012eae10dc8586a7a21268207a47273a642e726ade2add288d0e176d2858c3cf7c7b9d98124205bceb87a94db639b660379bc2d074755729838ce8b986146d0067329fde3898cfbdee9f3df1490de129ccaf0ead2faa1099cf6c9947743a215de01e89129c7a755b2777515eab14ba40e15e67483d1079fc9007a5ef02a217915af23368b8a952e153", + "signature": "bfa7bce02cf799d2ab552ad548a061e5f7e56a1253b1e6785c1b862957b76d1d5ff03dae21ae8c4ba774a114a446a8cc197746f56fbefd93294cc24b2e72130d" + }, + { + "msg": "b0e53cd8e580a10690b806cd8bb1d9c1b3febdd7b7cbbc9dce6fe888808cff3336f0d06bd927175189", + "secret": "0a8f0041326083d62cd3487a18bcdf325c1d38353006c2e6ae37c3eb356d42e318c3bb551034b0eaa28aa9415f0a2c9fb265f2d62cb9e082844b295400bf953820b2f78cdcc77d2b48aefec01532f599891c3e1892b5f3d87fec6a400be734adedca0c2bcc6961023eeec08a31c5fb5cced39bec4c0b23c6c9ebc0e051a5d37937276d74d5a980cda990f3a8de847ea53c320fcc2130a3473bf4e934463f264b3e8b9401bfc60ce1e080ed277af3a353e9a089793d40106043cb8af2fcb62e82", + "signature": "fe71a2e9d5887ec180ae72cf937cad427cabaff09a59c5c8f26841c0d8d40706e5f69b952118736715658263da6a834ed9740ec944d0ea8f625f3954381edd07" + }, + { + "msg": "2287b517352afc6e1c27a4279681aec462b63260c06e89ffa5a24305bd3ac653c6c9da7129b419a402d9", + "secret": "0c9f7abb2cc8e68ae0ff5a1708e921d7070763c207d6800477e6c8ef15338af1a3cfb868db8e7083cc6fe21e03ae8dd11e78a4db6927a8a99ca0ce198091cf21371f35ed07d4f3576ecd78e850284ebbb26b44b416826b5829f728248ca8b51c52d8018d0cd9469b9b4d22c0b6433525771edb180714b783484b72614e55e8ec00fcf8ad415b6f7af7ae99f04c8778d89e227d359df7619ec527d83e63a1a54fce2bbbe44e7b68196da78a5ad9e19e100dce8f90e502a76bc87097cd451354ca", + "signature": "a6b4951efc130b915fc0bb074f790765753a5bb136614c1777800035763071832685a30a4b886904ebb11c595fd7d9e1fe9b2a7126c31442762930f841ff220c" + }, + { + "msg": "0072222efa9cff32c083f2abd8365863f36230718e51056e9fb8a45c0f4bbe0b08f38902500c564b97f974", + "secret": "93a1f51c5fd34c61570892656cb4f77ed03f1a377a299ab1a36167db5c521c6c84fa4529760f629bafded8648aa9b797241ff87547e077708e044b89e54be5eb757d46714f4d5a2705d0991d81015f6d66b3ef0ccb46c80edbdf2d57233e7b8bbd39939eaae8eb59e064ae7603e2795ef6cebececa592bcf932f9f90b429e4655c49787bf34b1df5f34da503a9820ba96d5d9f8c4d12c4de012723230e378db7506d349cca171d9a95d01a475f3196492cd49139b9db7a85d8811b0c88f2583e", + "signature": "36432a9abe725c277ac5e9786f25e55cde3b04975a774674c26cfa9a8c6f7cb5790324fd23d5c950e59e571a4f4041fc2ef9593b093443754fe80629887de507" + }, + { + "msg": "f2c102343e1ca54f2b898345b43ad901e90afed6966ccd7c59ff65acf3b740de6e6b9022f3e8841bb86a61aa", + "secret": "0e8a7fb9697260a110f57272394150ade4ab01ae3663bad97c86f7d76fcda3142bf3466b48aeb38abab074e04d914d67e219c6bb54212c941c69cd82400ba14d24856e6156ea3a4f6974c133addb27ebefaaf41a759eb53fcb58dddfd93673739b48942b154110d2cb31a1de757cc870ad4e2098d5df5c08fe3bc5c2d5f9de25eff6769c0ec0a72373e760680d396695c57106915429934fcef3542a31e4331abd69a4540538a5bb15b8151f22cb376d052978bf893e58c2e600f828dd066d0d", + "signature": "566e5e2d249638300e0a51f00744e0ed5910a47726d7ddfb9912bc548b178afbcde25712827d7e76ce5bc7cf2e580c8ddf8492f1ad81bdcf0e5701d7b1c3cc0f" + }, + { + "msg": "ee45fd7e94eadc778e69b35b10c718d973c942b3f6019db4d530186f86062532eca9fd15e94b4f2fc6bf52cf8e", + "secret": "7e34a377ee5b1e368d549b45e3844971e3ebce3541d8ddf9cead2f387e16da6dadb89079fec6cd1bdecacfa2d5fa17437c9584c2f6fb95310090b2f4545d28dd48d05d47f4743529dad67ea9952974ec26e9b0f314d2fd11b019baf8c1625d758974a2da253445c4ec240535b52676fea303058a17675798d7aaf0e66cc4b18aa24c11f92ba5ba643a3e614edbbebffca02cc3f651227d7296576d8fa19cb9c2902e13c9f7236b08327b4b13741e9bcd63428f55a550fffd1447b518ef2a7d51", + "signature": "7a45698aa21cf911ffa138db8ce83c331bcadd8d00bb02d01e2a1a8ef45a3d5caabfbb8c66f154cdd9aa51ec6c0acfc5131048022aecc74f679f073de0306601" + }, + { + "msg": "ecbb0a52d487566ae56cfbe8718085140dfb63f3813b0647562fbdbda818cc0d14270e6134d448b38a64f5d97b7d", + "secret": "c4bd8743d9232e8fa2afd0273c6824f012e7b000f71c4a67874bfe822b680246e618f2744d30cf613d3186ab576c56207184b76bd7d7eab2c8eb63df0ad7bcb13bdd066a953c2e77a4e475f9b831ff9c8079b3d9bcc56f52c1e83b27024c91ea7b2bbc95fc769224c193f154c044cb21fd73d9a613dee6b940792f5693ca7b8fa994bd9b0c0235c2deb9191407f320a72d0335ee20d17dab935b389ff3d0d2f8f9a5ca75829d45b1dce76f8a5a00b06b9d859c211db3988695785569e08aed90", + "signature": "2d019062c9122bfe1669eb6316888fae604d75174ca351dc22f05fcd36c40f44d62fa672bf0185cafde175106c7812b9f66bcf43f16abccc2dd51efb9ce54605" + }, + { + "msg": "77b7dbad2eec336deb6b65629ea532f0fcc89a709a69b94c2b7ee587e05c8d8b324b2f5be26b0f02012798cbc55e80", + "secret": "e124bb57d44a54ca7d3d7ed0be641893f6c06a99d71b6b0d79e3514924a3a6885940215ccce86de419bf76dcb730e5429b9e89273d5a3a1bdb321b15868693c1f72bae8d77ba185d3c9e7c2f7148a24471df3d09690462d8d4151506fcf2878cf497753140fa3c2c8b2c092578a3ec903452dee7ddf8914cf0f427c5b01ceaa170b295342ed3d82c4c6577ab17551d2c359861b3072e454bf6b9fd9191b92a8421ab782b434726e28ddcc3d063ac2c8730ae1cbb97a3627cbff3e8c2cf6b3877", + "signature": "c0804d9dc2642f8dbc6e68efbeb72e8f1daa9bf24308041476643462c9c4cb30af2846c44df9daf8cf3f57c0b369dbdf4e57dbfa817868f6c84ec24bc82ede04" + }, + { + "msg": "b6128985e64c7b1bc3cd565f34559ee4157143eb4f68efa2fbd8611777a668cd7d42e4f002d73e64ac0c57c06514d7af", + "secret": "18abcae23dde9fc1a88f666fc0b00f6f77651863e776a4fe401d09afdd1f0f8fd03d1b7d3522a3d2393916717580e2ca013e181e1a51f2d629d7ba2178e601d0c6f32e8f59d86cb178ea682362ac1f5da6f3248a3b6846cf1bb021b7f50985a8f5b39aa5f2176c9357b022ccc2ae6a6c383ac1d1e021199203a5cd625bd49012954ca64ea4842549458bdc9b95fe63834f049e595ea8c2934bbf7c3482ed54b8f80c8498a607c9b1cb66f5a770b0f762eeeee2b4121df542f04ea9d4ad4abb8e", + "signature": "7140db908a8244bc5a4992e020df191679fde7d0d8a6ff691c6784e78d7e967120c371d217676a7f76259a6b765823dddac71914b8fe77882d55387fc94cd206" + }, + { + "msg": "69fda1ca0ebeffcd9e33676e8fc3871c40f4272ac24bbb164d146bca138e2ebe9b6cf192d504c07260e92eb04e67f73bf8", + "secret": "3115f4a637963fc0164293c6eaa13c0f7fb8a33499cea3813814f9e3dea1a71fd7d3fd6c47201f75f716371ab1868a044b2d9e0f0ce07179ea8564f5dabb7bb6751a883a7f1047670397f0fc9d1dd46bd6e0877f12427c0e44687a59201c73a2a9094d28a73eaa99d98585649cb24378bfe1a3c0a296bb98a3d7dc748eee37ef475be9423a50aef2701e777e16126a6c8164d3960431494d0c478b468f9792d6e970a305015ad13f25818f5c9f30456ab1696210f50c66e5996c2e9efb228cd9", + "signature": "0aaf683aaab00187987b38a3a06d2a49310a7cd1e8c8e4bb3ea7455d99aa86cfb9935e605cf50babd4407579066e6ee830278fece828b8674c7f111887eafe07" + }, + { + "msg": "", + "secret": "2e0aa5c85786add6b93988ec78178f8f3edb81043eb597682aec64c9daba491ef8c8124474dbc8fe6b0ec1ade58c9a8be5815666206b04f51a991ab8a3909cb79ff63a0ac6b217ec451aebda887fe10053f70b4c231e96b575f477429eb5cb1e5a0de55e3d06b3273ded396d28f10260a102b0b8eecdd9105209ad29c205eba07e80b9681aae204450cbf39f430eb1ba928bfa3b4826b3508b05c8544d61d00b856a6f8f12a7ea8981301b9a50e11a02d77057de5e4808fd6906c7d0040f5b90", + "signature": "6370200a73d3af797143fe7c755effe8e9a2c4e552273f10702adcc386df3b85c3b171beade6ed10054579c943d18f8ebc02ed87566cbb0fae8279c273822b01" + }, + { + "msg": "c9", + "secret": "762fdc98538a23946913848b58ffb49e5eead4d955e2a194f3f7cf0c4e9bcbfb01996dbc652c8be86ac95710603558f872fa29e7296492fda9e9af4c4518113b52cc2acc2e582c41f2a4a6c5ef833a6bef6a87f17b1d99d122e69002ceb90d7d152a182dd98b43ceb424bb075eac8215f9359e1637285d2e7ffbe99ce45d2fd4193b2c66174c5ce96259f44a129f67541f6086d6b526aba03468cf2d63e763d5fc29f25512f649817c4d5aca3d4dd7dd5b6a40aeeeada01c39b680d747cddb38", + "signature": "8310ed56cb71c4d8b4c31f54f8ad5ba801df58be2ac68315cef7b5bb111da97cf95e32a9f8b43d5f80b09f208c2338f874f294d914ea4536acb338d18b1cc50e" + }, + { + "msg": "b095", + "secret": "9dc9c56a4d97776593e98374cfb55f38b0936c09446a34d1eb9ed9b30408df7d806c3599b40d89d9cfefed6e17b2e5d92d1732a39f20743c1e5552ac83ef148e767a242aa3eff3d6aa8c5592056e410694e94b9e24fb8a76a5eae49fa4e5de6cb0f06019e00f6ababd62529403e64604e2baa603b01f2cd83138e38a61c06bb33fc11115caba8526e664a6d9be4718cb8e719b83829312bd2d5f5205c73a89c334b1b257f8bd7d2a43817596de784200a472b1f0786157e169afcbc7ef2de34a", + "signature": "7868d66a2d4b85010c6bf09d723166e72aed4ea7d44a7475c556b58a6141347302202c464e3f0cfec79d1ed6c440e647d5e9da63e5e2fd0b2823287c2ac8b701" + }, + { + "msg": "196ae3", + "secret": "3841ea8e144bab3578c56b98fadc0be59e218d334fffd6d2ac96525692651d1d955e6e522da67f9156864534d76ea0e7a934d837708fad63a0d53abd3b6e30d64d55b7a103bbaa0000037916507830876c4989de7447829c446f7f9b7de17cdb4ed593160ed739403de39c5d178cd158fb6292e8138257420fa7dd36784390457cfe9ab15e98292bfeafc0678ab61770a200b1bf99db8989221eb66c5e5f433a37f47e2f9d8635e2ee169d249f38c501cb1877e8477bc1dda34f70a24a397ee3", + "signature": "4276f2732b59a075e87af55ae83dca4a371a37829e95d2b83eb413d17e2fc62199da50a59288cf39b9212559651893091d70fe73b75fc3d054b8cc86f094c20d" + }, + { + "msg": "5d24a95c", + "secret": "6c9bb1a35d5c5b63a3e64ec07fe0195b012d644ed86bd88308ee9b7bcd2d4a7e8192fe5e107541554d45016f39bb42cf8c4af348d0fd07e53055af6f7e2c2ddafd5429f5bba4eda12ea0c3b3b20a3d2f0bb56c7f86d79a0d7dcf29b4f17f0ac5375af8a4ebf4ae378975504dbda95fd0b7b4a1dc1106ab5a1a624c22ce145fbb30bd36d6f37bcd7983d7ae111b3ea0f7e0b076ba65ce300fd354ab5dcf611b27fa489cbe170647095896755f51b164e196daa875723756529da503f31e5ccb2a", + "signature": "dd6fd7ea61050ef82703702a9cbeae39174978f4ee1a95ffc3d0a595a5f237a3614fcad04a61664276f2f949c62ef236f306137dfc1b3901a895aa69ae2dff05" + }, + { + "msg": "b0c01450dc", + "secret": "f55c7d29adcd4ad3b719b60bbaee3b9d7f0c3aaf6584681ea1627390cd777d715e51541f1af9cfdfcef377c4902ea7006bef7246963ff989a903fc7c677b908a32f6f43de1fa010bd2653d8465211a969d62c51b9a126d827917bc979c8ad62b1ba68a1687b36e761d4cbcb48561ba900f6f27b0abc63cb1b82b6b7e5ba0e9bd9b8e4c4e2d239e4d1d67481307ec781b7c86f4166a138541942a3334003b862143afb71af929aaf3fb65113ba72bb3c9c40de73363aba1af00bc7d2e8cbfa55b", + "signature": "db0bcef857e31e69ec3c292ea4be5d38b19cf8873283bee237c1f421d058d1b20a66aca7f2b4258717ea2a461e4f59c95d7ba9278bdb3020b50b39925aaa2900" + }, + { + "msg": "674b18fc5837", + "secret": "157fb34ea768cd649dca0f10a08c05abdcef0828896bb2599735bd1a8888b9ef86ef6eb8c75cc972bd84f3a83992e61a6737c689fed9239f31c892254b884e40fb52167b7b7d6c1a9f3eb86b392a0710ce672aa660fc232ef5079de0fee39d408696798c60ffdb725a64db825f2cbefd6fb2157e32ab30a762093e4049be25aa46ad0b7c108be7af7c3b0ac885436333365381cac181b4b3604d9644eb155baa52ea23b87a6747fd91dd09e166ae8baa7e58429e19a7f78bbc3e533a5ecd8c02", + "signature": "7e57845e6f8a2dc86183b1be0b1eb90bf59b95eaaa11f6fb3298fe451d221cd1817d860ef4f24eb397768f3670ffb026037f6e8eaa583bc6dd07b68d2356b205" + }, + { + "msg": "584e50320bf724", + "secret": "ce329c2e233482d03a789f6ae58474ce4f8fb215947830c067a73ce8adf7f0d98e7aa38ecbc231dac6decc8bc86be59117b8bcbe7e859ee32c04b283a7ab83a4af75b08f2fdc938bb9b819ca1d74b27be29f2924316049cae54b4c4ed99d8bc6764ec9b9ceb18fdb02299781bef208632ec387dd675f60c8e641b23a5e67b7b3ce1eac3cd7a8b3077c6c88de7963ad06d20a0d2452c78a102d00adcacccbb2c72a0c35f9c90c5d52d1ce2cc6955559b67e41c7e7e3d86753e1acfd55b4a736a0", + "signature": "c0bf3679679cc840e19d8ad818f87c61009296260a58b6a9e90e90c863fa5ca36850ed0c2de90516c096d64dd2ddd71836049cbe9131b79f1dea6cd3a93c0300" + }, + { + "msg": "8db6281949f6a9c4", + "secret": "cfe15ebfc10118b3a3f7eb7175f6bcbd3aca8ba7072908ac7f3d5b5b4d834edeb9e71578e41041286ce7306896fdc5e6d9563f6e51fbc5a46114d8349d858622b83cf079dd7e3ccf3d6d0e1d47b246c0440ff6d3932a15e32eaa4abf171123280b8e486e7661d0a3699328ff69208431ebc5f52b3c57fba1db41f011aba3896a8900d3a23422fc91996a4b18545100398c247a9cd1d79ede9f208b320212cf9903a78ccd3961e14306c0fb4d4c05dcd8a0e436041a275084feb850b756ef53b4", + "signature": "9ab234b6202cc4fde7253df116e68c707f0077ebd81bd858ced148d58ebae3110286b99f0b41d9d2ca218439c2b38afb7495ca85e803c1e36f60814295a37f0f" + }, + { + "msg": "f29584baede793d105", + "secret": "bf9fcf6a399541f839d4320b00654fe95c74b9fc38a0f43f5a28cec9ed279f7637cbd6ef617a25d67336595b9ac0e83002d2c60bf5765a9580956300bdb83510e56a823ba7d7aa7b5974cf103b6236d45303bed7f021c1698b623af7f6ff8f9bd16f026fd93b2e2e310952509ec60d12e4fa918e8ca8be84b09331ee32d656a65dad3b8b4bda7f54d348e1f05ff041660d8bf6301b2a42c12ba6d2a9f642f0c491c2f7de7850894c703f6fc5e13420f5dba2acae7ee6a4fe3bdbf5fe8808b5c7", + "signature": "033f0829b98ebe8b1caf780f69535db0a8c147e7a11bf86b94726edf7051deb7e042b828c2ba5ec3cd9978a4038f66ef6232ca0f7099d38d70db2693fc377a07" + }, + { + "msg": "dc99e47882a0f98da6b7", + "secret": "8536b1ce8b286a8b162da04a5780cfdf2914b736c06fef127911ef2cdf4500166019b2c636d43f33fdfa28f79c5e70e00163c1cc65a3aab05a16ee869d6edbd20b55b4bb2a82d26393a211c317d83eb1d007607f1364a970184e4ee4afd783cec7970ca36652f162ac1e75cb1b33345f05defdda40b563504afa7a6d93d9ab4a1568dcc19c0a58128eb7c44ad2a33491b7e61d4a4051ea42d9455fd9e10f7fbe33d823f38b4f3e711fa2b1d1334791c7c20b2b37514db14c44877ca4801d0c70", + "signature": "88bbc5ca9095d882d961e3a46f0f174327037e939d018b12f212f82df339c7101d96777c15c8609d3d45548064ef2393046f0e850ac1a47a66a1c8992dfaad08" + }, + { + "msg": "fafb8f67fc6ca154c90c55", + "secret": "0b09c8f3077a2f44e7ab865ff97ede8f9a7fb2f274d659f9c2433d2da2f48a682643e4a5a6a49796777e32fbdf09d814695aaa244a6407436de9cbc0db9e592c43d8ab933477aa32580820e16a32db7e928370a0b6a9d42a5d62441d34416e903df6e0b991008a946d509a2d7f5f56331f8c62e3faa91494a3b5564c5984c42d4980816138cc612db4f35a892ed129c4219b5b94958b94d593d022685638bbdcb5aafadb5d9653fd5b5ee8a6dd1b4487d038fbc331e4700da8eeb25704e14630", + "signature": "e8fe6184aee965cda44b14732cb5685138c37b71b2a479cf7aa8e5fea0f0f631540d35ee4215fd6437ea20f780542b41036f2df05b0cd31a0fa6c7acb280f007" + }, + { + "msg": "b8b8f2844602fef97b7b8279", + "secret": "fe438f5e81d634fad43d95185f751e97c28c10e2efdfeda9c17603eb653a9f4823557675b9e4a3bc6e82e338b3ac3de3f3d981cb58bba86339e6db21444c3186aadde1247cd7481922da1773cf26eeebc850ca1b5f648f9b60f9ee480a5c66e075770cd1831c255978d6f3e987fac70ef69616beb1c2134d28feaf987540c07d4f35240e64fdee50d0fa7513ee7d93117db734154e31408a8aa4f22491596143363b0e763932714c84c9a50ac4c25ab8ceebc9e4fcab43207694e710af1282ae", + "signature": "b9ba258747884685dc3d742286560176d0e156202154715baa5b329dca166e429db133a351a3b81afd4b96241e877979ea8b5fcb283931ed15ffe4d06943ad01" + }, + { + "msg": "e5b17cf871d01be0f13dac0ed7", + "secret": "f504830132b2408be522fbb0358ac94d93a675fc96d42dacbe75024c0a112af63b5a849928f1e7c34bacbe390dc2cfb705a55f4b9c079a6dc14a68095c41a03cc0d66986625a5b42ce5063d3b89e64a6d996348e3543765114bb450e35a11f0bddb87ecf01a219c49dc878fe1ac0787eba986be29bd421e1a4f83e9e787017e22206ae96825b190c2b7bb06809d22b992e224cb10735796f02151a5b301f7bfb189ed147b7d6633987f5d082d0092f12813529516054a9b6e4971db870923d10", + "signature": "c38f2a1e25aa2c2a8e6ff47355b46cadd24c8b65909d253855a5d1bf9129c969383983edc3b1ffb41c2c4c6611b38f3ef0333dc6399452f1136ae775f2e71d04" + }, + { + "msg": "a6165a07db77f23f596e16a66b9e", + "secret": "43923ac886bc99e799924038a698b5255b1e0978c9a300c67cb7bcae9c0e2c687c53511312ad80850dd6803e1afb2390ed5079d0531a689d921fb18325518a5265399d7e35523a4265510d4b18fcb41639aa55780cb45dedfe66fac077bf4df642076ae3ac1cf50ac0257f055a4ef5bd063dbb6ad3558994c83f6c68d70b7e0786f902fac6e8b6b3218d5458bda2daa30c94fb1b9782176754a1014e5404f81f1f58c49d81ca708c553b7b4ae507d711696cfcb44f76d3ee857863db47cb2e1e", + "signature": "d622c4eb02d492f4fe6f974e47777d6225942673ff128b6bc1514c1821fd4eb4f94e6d4bc5c53fe7969247f7994ed8da231b7cadd77c8a9f040a28009e4cae03" + }, + { + "msg": "ccb64d1176faa56baf75cec8835bfd", + "secret": "f967f5eb8ebe18de5398488632243c1b96b0116b713dbf4f59b1303fc66a1890a0c476466669a6424511074b515d8d3e190c4ab18e7921790d29fb4521d7fefe9034290c793f2941f2c27fd546e783fef67061ee07cd4dff9ec7b5184de30474093a06ac5a68a1515361dc3f2ea6d48c52b66ef2f774a021539ee560570fdf92e127ceb50788d089980cc2607fe4f902aaf1a074cea64e824b7a61a4688a23648f8771e9700481fa669ad86a456adbda89a604cfe0f66707e9b360af5ccd37b4", + "signature": "acf17007ae1a3bdf0e29b41a47a56d9e8330092181243b3d2095c82b93b6941389d07ffa24bce9747210beeb2ff77ce80a1f8c58c62f6f26b975e618922abd05" + }, + { + "msg": "d7536271fcdca65fc7d4b12b383b5b80", + "secret": "7ff7eafbc67ff90b8d5083375e494854e79c3e64739fbb04debd25942c6e9ddbfafcf366e42e52b49625989b0de1298ba5075e5ca2e6175feba72a48fcbc9d4d252a7ac3926b41c25eb0ab50a9126783a199e21271457857a486fb1aba1b522be922e2a15762978abb41c5e44c6e2848f2a7a1959c254d75b449efe0bb43a9a7249764f9a3d7d813726acddfd7c5a6333c42557fd488ffc2ff28e209b164d9342a7e602a1926752186623bf4d20b9dc9b2b37aad9676caa85c6549dc6b17ae7c", + "signature": "52af0bbb44329b2e22193ff599f9955f373c2523b20159ea23183d553ab1249c48e453bb73d3f223e4c44afda33ead095f14871d33e5c34e977d347e26cd6205" + }, + { + "msg": "c9019d4f4c6ad1939500cdafe12adc7eb2", + "secret": "cb74adc08de414a19f7667f13de5dca5f56d3a9d0e5ab08916a2408641d3ff0fb305477d84407732b17550c958866b48a7de5d9670460f0c23c79c7ad961b6953d5dc0a0180de80604696c1aeeaf323e465f220fd0ade040675658b5c518ccdcf3ef9cc90b958d239f65a37900feb91469615c4b888f2d66eb214a41c425b3b84b32d729f2298bb3ce8c176bd8642b19c90134fd2fba31980cc06d23713b17c7d1204e7221c5a16c8cce5da1b8d3d021a7c75467a74fd43c4eb37b4ce07f9fd7", + "signature": "fa9429fd50d474ccd39ab6c75eafbcd9d6c11aa2fe4f0cf65003db216d8444da46c4348aa1bbca2decad648bafddd65fbfebe57fb509eda2a92c539774eb4d0e" + }, + { + "msg": "ed862b5506367aa330f24a2afc4067efd9b5", + "secret": "f9c1cd80c01ed6a5ee54d94afd9643ced7f5de174123a73b9837a7d14fa8df7716865424c3d402d9fb50b3ead8fa89bc0ce41c60bdbf3a91dbb15620049f186b31ae45bdffa68f8ffb86ed89679d7ed431d9b017ff52a50e85df2cbf12cb35ed1ce1513435cbb8b3ca6c2f91f1db138d68c44c8264fc2d5f44f0df563e68cefb65ae9947a9fa72411cc8ecdafd42a5c65ce8ee9e364588979cfa5d130809b80c48252034c921a36dfeca9b1d8701b82261bfee45969d444bc690e714caeefb11", + "signature": "0b4cba15b1371bea85bffdf96fe27c16c10ac8e1e3c6c716b5b60781b05a4db45fb8fe481e8201e05aaefc69e4e2ba7f563edfbc8976bcdfb043ebc020e7210a" + }, + { + "msg": "e810792e33f6fd36def8e8cc2e0836076a68d4", + "secret": "c32f41f50dc215528af2ba0f1f45fd2881b94b2d345a067c437da3053727fef9fe268ce0dc4f4234edaff993d5d55309bdd28dce037118893426beada98dbbd748bb586dbb4c9e3ecd91d839cf8e22ab443769ad9b41d143d84c59b1bcace87a6c0298b94cb466e47d0de4f83b4ce0d8a9602c3d43f2e4560a53ae812613dd1ff9f84ef624ba9aaa0baaf618342b9a1f1f19255856ae65f13e1239d0853493ffda1e26d0fa9c5a98eea81c32293ef0ef1a9773afba2806e36c238b9f11556217", + "signature": "089ea0e70356eb61fc80c9a198e6f15ac54b2561bb2cf1e70cf36101a92d273462087c8973d65122474bd12e9cbbfc0bf5681e1a9ce202bab28f3cecc097b80f" + }, + { + "msg": "1e833124a6ea8fbdf454393e5793ab9149a76403", + "secret": "e437fda4026cb5adaa5e7ee9310c480c5faeeb068c4411650bcdfc57ba5aa3639ddedf87ff83d72197f8432ec47ecc3d2e2cc7d4da5f5d7cf00ef8a4f60d045b992e13236108dc627144a06c8085805bfd3b85e3434f676f470c85792eb899366d615f49cebdbcffd793a1d6dbbe6e745fa7d896b5e6b34d2b0357601a194809fc4d58839978d0ce30d19400e21a7bc4fdec53ccfcf43cbd67ef4436cb941beb18429a157b79737262f336d674edebf255bda8013e7e86dd27c96d37ed2dd4be", + "signature": "f0a988f89e2d8861653c99bfb7a392cc08a849772fefb38d3dc93e36f1c6b124b31a14ff4f55d6ce53898122e0acdda11904203f8af63defe50fe4dd1c8f2e0c" + }, + { + "msg": "b27d5cc84e2ae4045b1663e58f305b3a49f6c862e3", + "secret": "ca596e2610328a4f5b7175f415c31dedc5f8df17050122fbbc7949c3d4bd2371447647a35ea924c37c496a1b614c965b3c07119b9ed04a8c4143e7b6a131fb531c379bb5b9c3fca3a7f0436ef91990e3d45d87fc995164e33621beb5299cb8be635c87a50177e9ab5b0ad7a1d3b9c9a60a6ec5faf03b7fa15a14f2268a706f3a694cd1bcaec8cfc676428ea6cc05bf29fc8e6346d912a1aa79ed159f7bd8b7477d6ad7d67afbf0e35d0cb5db863c0f541188b2b7bb8f48d9ead2bd37155a17b9", + "signature": "839962e5545c7cf5d9d5fdddc067561b17cc49c30b2ee25db717763e5fca70227633c126a026493cc375deeafb55be046224883a6a233c9c0103cd45b8eae70a" + }, + { + "msg": "a5d9f5791c26dec2badfe42144d3f95dbc1231bdde64", + "secret": "9fe167ebc41b6ea2a6849a8b73da87d9a366cb0352d858259738c89e7f5d9bfb49026bde944f9bed4bc7246addea0c2fdcd68e0a105d3d967e74ff63ad047d20a0f197a1791931e893534ccaebe75fc8c6a6e51b142232ede4e4c26e77e129f631700a98410f7c743530b981f2958afb060a2aeabccc6c6567db62a88c23528681da2870b8062f834fde4fcef579e6eed2c2f0739f4bdf8c91cdec13351638db721bfa22ac71cee5c60b0883b4c9e6c2f4ed490f19b76f01e2e8629dd88a90ee", + "signature": "2374f5ed0f7569bd9847ce102a43eacee341a31e55840a823bb9de517ee88da2ed6e178dbe3a788c78be1af5c533a1c518b3414ab0a2d366de5224323a524306" + }, + { + "msg": "87579e5c092eddbd1f91f32f0cc5b89e0f6eaf2cfd51dd", + "secret": "e0ec8367d19cd4e18502ab98878e8d68ace73eaab54cf55d9efc064830639365a09705f8f4805c8bcb342b455bef32fc5e4f7601a1c6b27f7159c05b22bd0cc046dbd898e6ede915816a394e3623139ff6317afc9a5aa092103b07d93a3854bbebf036193daa4169efa71247ce4b80e4fff70859c2ca6fafb19a216085746ad6a477056567837086af49665172fdb58e84b9f44fa4f938b464345cda58715043b67038b4cc6a66f99b47f4e32ca12eed9ba3d227e10c8bf720a46b33441ddc56", + "signature": "161c1f4c000093ab623a3bde7e6e40955645c8afc281c3997fc1a65eb037a24c4af541bbbc6293aba31c1074d9023d79a2110fa53b653fcd3eb0cd25a6315b05" + }, + { + "msg": "11235f9a1bcb7984dfae6ec02770b1784ee46867ce0681bd", + "secret": "54daba1de7bcf7e81219d3952338254c74b45cd16279ac90fd6392cda88bda0724d72fb65a5496a584c77ebe9f8f31d5660fccd5d4e082d802b6e2ad212abb12c9608567837f1181829bf991d99b50cfb4234054d7b31773cae0204e68caa02349e35ace09493af3ee1ed30813aac85bb68165ca0f2659b1d74074b41ffeacd71c1c8c20f1f9b69847e2766c1a8bdb9288737c43d4b169ae745dfae9e92600387634241ae315f3703523f824452ae3299499ae34112cff300691063bd5c278e1", + "signature": "6497a69c115107759b35c8c03c0cd777776c805b47720e2b658e316ffb1316f3653d201305d80e1c1216c618a39ff34df973792b68f3b8d165a72cb8d4b4a908" + }, + { + "msg": "751a67143b94db7e9953a1e40b253907a9d09c2d974ec0dccb", + "secret": "6edee25cde32d7c8d58c88ff5cbcee19d29591ae7d535415db076ef778adbafca0aa2e11e4d650f75d3ea15f7439f47a02d2b2efc008a211e5f7df0c4c9e51ca845b1ea9363a59bb881afdf134dd07019943e0bb67dbbb7e839cf5071639c23360410cf507f48231ce00dfad0b3bfafd174ee73be3efaf617e83150174cb0333b50f185382a77dd887aa2c4eab6a7b301000667dcbe993142160b40cf942d7853af2ef3a6915a954acf52da49794d2654e9d589ae1942e701a392c4b0bc12318", + "signature": "d15c08fd709a6990cc8c732e5995b5af4ad65a4a188704fe766f507351f5b464964f3f460a1bad46fc58231afedb469cc56e28d626b6f7d475841748d2fd8101" + }, + { + "msg": "2aafe3554262ef9b2a39edc349d37dcd8e72fa7acf6ddf2d41d4", + "secret": "b27254eb11d1c5ffa03e1e2289a6e9d284086444b8d8c3b503a0de9af8fe475468bfdbdb365def8b5325beb27c63220b06b1880b9dabd83dc18d9cbaa2dca76aacbeb2ae90fa8b30b0fc57cd3de2a249c1acfe91640e5b47c33d4ef85e7f8c35bae3a76d32272ffcf5a03c955a5e4a8efe134540e7d95241b543bdb2d90ff6ec90964071c4903aa17cbfa87cb68b9da781d4535cfaa4aa2619bceb4f50230f1fca0a20a6fa36539768f090064563449c0af5a2486b25dd96857729c327d238d0", + "signature": "8c467daeec122a3039d3956729ae266854af12abb002721c1423e1bea56a8d886d2044754c6fef671fe1f39c9a361497844b8573a2b63d547312f59ffed44300" + }, + { + "msg": "9bad5208a067223ffa12832ef6e069d9f7f69219d5cc702717ba1f", + "secret": "e8d64ae77805cc78e4a1029a31818773c698ef21a85fbc663b6799194c1dc33ecc18ae633d9a77bbb55184ecadb3beb40d81fffbf4f91680cba75e28dbd4a820a36f1c8d242d4ed271a44c8365e8a12555594b82c608d690c3366fbacab93e2d801dba3046bca4ce9d13b3ee00b4971280567736af8dd6b7fb148f16149c1e79f6534d7b6461208bd022ae4e0c5704a9951a99f8d1074930cabd821d59e46a798ea3936aca33b3262910d7a556471589918d650681e623d0dc7ba2837ce67e5a", + "signature": "621a11cd80d47b3edd352fc1950fba70242cf760da50da93d57371ee1767b7758013d29874b6722ee2b01e5aba525fc1799f7c5f13c15e355e307c9b3a9b7905" + }, + { + "msg": "4e8d7a10d6e534303d1d67b38f75c3e2e395e98ec9a99b72c989a090", + "secret": "d5c08cb59a17f5e0b6aa7b7af6489d2aea1e4a1ff541bb37a1461631cc56b582b165f67e5fab2f07093949726757afe67473d54ef08219e69d2eadc1fe95378a85fa916b697c7b14bb85a15ee62ae7ed5dfba4a4037c8932f5743ba1ca17d49e71bb17cf6208fb157bd2d4e324770275f05e7eb33176d4931f4783f59fa96a1804df326fbff2be7a268cae0c89fc9acd77f2c369d640e18b8840c7fe2aa0f86e51c23fd506bbccb96a2728f9ecad43f3614af018faa4392b0ad5349bd7688e2e", + "signature": "c5b5690e5245811bc7dfa05750bd39dc37e768ac19a20b803e632fe3f41082110abe4c71e063a47208604a433a80c05655201d33fae12b83c67807ee19d48908" + }, + { + "msg": "768bae838f0bc46a18def9affb16950d3788476489f56ec4346fc57919", + "secret": "220a02a36805d67eb0715beea873db984901eb47835da6b0c88a4d7efea92d5cf16e31cd4a5538c319cdd6ce2e92e7000a3185da198e6faa1eaa8ba22b36aec9bfbca6b4dd22fc13e13d01deef2d0f00287f310ab22f7bb1b9a7ff01fbc7183ebfc40150a624e02b5529ff99c0cc90558fd313e113d97e06b85355a37ea5c57d09cbb66359c26d3db1f2c74ae7e8f072a84257bfc00b905a2c9597eed0cb70b7c9f26d6e05859dfc6f38469fb0f95bca85b1ebeacb2804adc61291b971b8bf51", + "signature": "275ea8df4eff1604ac40db59bb9f45555503d7781debfff17eae590038de57dd21b9866a2fab7a382028426c0d52bfa0d80ca976bf42f6de9b49cf0494bc5f0a" + }, + { + "msg": "a6c8359ae7d62a737e4d310d312738f11515e08efb7370c6e3e2ce740202", + "secret": "91bffb1f56f5465654360d8bfc65adfe4239b45f83a8905cbf1d38acb84fb7d1977713ceb848d6a5b9138b539227462cc05abbaa49b1c9b41f071d43ef079c1f589cdaac10bcec9d046545e6e8f43b792a3c97d0f8156a8dd271bae3bc4331d7e6be5000725f4b808a2328628f960d2d5e18c1f0a505274ceac7977efc5901284594104fe848f3625645399d2179e41938e237368ca8f577560461a8c053b770fd1a58851ee1f55e6d8c92f35d65f1396678bdcd5ee4b177c7ac46115ab62a4b", + "signature": "b9d9ac4bd686935a79a41288b92cfe8285271d36d9fbecc292528570a1b1028280fc44ff4932f206d77fe202c5bab49ec8f4d87ccaac4dd7e1d425686b83000b" + }, + { + "msg": "3f8612623bae5380254c3ca2caed112b46e709c5d76071b040666679aeac43", + "secret": "4912c5d1db4dfb010238656cbfde9e36f4b8c184e590c80a87f44c8c134c70b78965bdbc3b0242f7b11e4354a58b9438901d1b091e2ac516733603c66c025812c691f37985dae241ad4ba1d0f08fd5523d55c917ef645f826d09349cb98687cfd9c48a65703c9b185c3e10bcc91a191ec0ac245b1a709826b33e65b9b64e7c74e13a5476b7edc943650fa81fa1f4d86ab4a67c9677aaf0a0ceab2b8eb5443c78af1dc82736981f3a10cdb29a7ae9c669a2a98c9e06c50c858afd8339adcc2833", + "signature": "0ed6171d3fb2357bb71437e5f9644f6fce5ac78c8f03a3c6b29ba0e0147b6fca4a0b9942e2b5d19c32b1afbe73f6bf4a2c9aabdd12694cd9e92f1b543974460b" + }, + { + "msg": "b56ee57a284369590ab822eeb03d00f761bfa0d10a8a367d086dfadaf382e302", + "secret": "a961f4516f24121f0b32c6b1dac809ccc2884fa10a1be34ac7acbdbeeb0e99476ecf8b35448692d96214fe02a64bb7605b240bd54cc3dba9d53d562f5e5d5f3fb17f5820c8cebde3c65f564269ba7088b19b3ef7f4f8c256cce683233e30e131e3479b30ff50c2e5d092eef3067225dc4016a0aa798eec36587a5f358853b955d3cd1b7c0f697b2f0caa241f20e7d72233e60f0917de37342c63668717bfaa38bf19868c91fc3ecd3be9e0b973741f31179b2ccd6a2956889ad1157fa8e6e312", + "signature": "91d0bbb4a9df4f9c23e8066dc4ffe8e6aefbdead93cf6ce8066224ca47bfab55b9db6134736a61e3ee5b8282de3eb5606cfacbd48e0cc985c508b07ad9c85508" + }, + { + "msg": "7856f8717163ff349c2872607c45dcfcc4a3b1540d23462fb018d6608c737d0eb3", + "secret": "572d7d5e452a7f4d25d941aae38b922e40908ff1b4217f2bb3bddcc9c1fd4fd73a3b1bc526af7ac98ca0bd6e05d0ac88893cdd36c4f9c6fa17bc934faee8ba36d7f679d9208ca4692794efb6e9ee957767ca08ac72fb21668de5c1440888914869d4002e204e78053e4749da7c9d817777b70d0b3d20e4b3d5ad84ca3bd6b7ee8f7aa65b68eefebcb0be7738be62ddb41dee83daabfac15ee05d2121f15007a67fa4ea41909362eeb61d223fc1bafe20de6f298d5e2952560fb294a290383825", + "signature": "09111de12109584f4b177511fd928f3766214fd0efc12ac1c970f4a48971cf4d153e6d293e991de384eeefab161dc3ed369262812bdf16043b5c77dd782bd204" + }, + { + "msg": "53b9e1d1535137cd0407bdaf7c46358ccf50195959bbd1fc2f3521061fa2ba61e893", + "secret": "58b15ce561663637eccac501a74896ed6e1ed9d72eaf4af1d80296295c83af83c22bda769c519434390293c4340e2461eb7b830fe48431fff8010d0daa8f639a55e0faae82ebaddb4753138bc0e40a3b0ee747ac93bfa8cd12755ee9e8fbaac75591e72923485117925da924eabd73893ee7034cffc58ce31e75ceb4be9cfc89606d2aafd98dd8674db03027ed70e0468ee7bd03b602293f0c4b0ca297b37305c7751abb0047b0dd7702e65d8288aed41f2bf6b906515dbf272520b2c9c230d8", + "signature": "bfe8d910e153d7b5679f95d13107c52d6f04e7b1aacd7890870111dc01cdf08a856216f6d6bcff504de320c64408bf743398fdb27c857b57533305383f83af01" + }, + { + "msg": "8b99bb4b8ac57cd70124ad463729dedb0902a254a0c3bb67bafb4d8f3fe1b98930cb7c", + "secret": "c3848a872d07d465bef5d24168954ea6c259be10dae4144883286181718ade82a6cc71a6f3ebeb5cdd29dfa60373603c4e36f9dfe8d915633b29fe81e666949a84f6929877248624b21022ce04e578442741a7bd9c011d360e3edc2b4a00a1368252ae301cb89e115b08cf8aeaf0e6ad3ee9b1f01803b165e494d663545bf7c99c0909d6c671612c3620ae1bee7ee8f5fc29c04792726a3c4ee852da5df805999de49dcb1a7a304695eb15374fb514cb62caf28ca60b4e8d4ec4f44bb5698e9f", + "signature": "97c4b15e5d8afa06dba52513bba2631c929d00227dc162260008ec9e6bca8d0046caa322bf882f83ce5a6dc5816021829566f888d0ae8ff70d5f9cc28da8ad03" + }, + { + "msg": "f2c0f46efc1c29b555ad337093959e9cc654618d7ed5661af441cb7a55ed04cf08bb1a7f", + "secret": "015038b0f31cf1019211557198de1ab761789c0307b9318028040345dee89c2a54d00b7c9cdd9ce3fe738380b02badb1d18412e192dfed526131394e6f33d8e180193e7d56c5937160d91d1985e11ee2239d148bc130e51042d82ccd286ed6fefe0322149d215a847583ae73add9c991ca17a3e299d19371e0a61c4a1eef250a2df923ad92780ed2c414ecd41a7e11bb8adffa228399b57da2a870843eff1390ececa0146ce09bd3169a3c2e218d67a760235b8c7d7301826ff931f49234a737", + "signature": "9afbdc8f242ce2e75898b1c42e66493cc69799ddde503efd6b2c2213084b562ee42f1feb7e264d4151951984c03b4f95a9522418033b49dfd91d21230e3b0802" + }, + { + "msg": "108b2dab73389271270335e1b4d90f53de0d8bbfa348c945580cfd41f821a0d293cc59b14d", + "secret": "ff822c5219fcc3bc6f8382ff1102d7ff8a2cf54f7cf54a6040e42dd054abb9a31130e89f5934c6aa463b6f7b81c8c4ed7e17d7cdf1281be298f2a9c7ff9bf92c879a37083101a0ff7386638e8373129d27b9bc6198ba3dc97a68e1f14d38041ae042c6d5c4912ca08fc0ec723f0a1eea32d7cd546f6b1ac5aec6bb86830b8a9f5e13440af2d78de2f5d0308a76fdd83564e9076ca9561354124d53473a07f572f3d11bbdc9b40e15449110867310e489bef13a1399d8d9f19da3374b859677ce", + "signature": "c0707c7ffa8ed24b268d1ab89317497cc8074e2ad63cb7fc1b13d8f91fb838ad924bbe88139e1d0bde735cd4999d1d20bafd16dc716b495b77f8573ed263d40b" + }, + { + "msg": "46e879732ade4e2c332f808da6e50b27033554bcdf97063a947ea186274741918a9d3fc92e46", + "secret": "37c497476f7182a2ad19284576ac787566c4e68ae5327d318969b318c22734a35e60dd73dcf1456b5dae81395d9857ff65286e7f7931ba5d90105cf7aed6e9c45c60cb0e1c449a4a9ed82ba228f7bf655bdf236ce134f331cd2be2b034126d146516979cd5f5a067eb8c01272db9bc982db325a3794e51c3c9363262dac32f9f8a28edfccea80c0fc149c2fb3ee9804b81897b06d58c82a4e662e9f0f4290e824a2e1537dd944458d93e9ae9a4aafb1e55a45cf5f1bf9e04a61282c4d421f51b", + "signature": "f4ef72e195402528bf7d77eb727dab27d7d5b34c561c5ddb76698c7df62251eecc875c09faa360db2fa2c168ebb7d0b062cca3a0d6450f14e6d231052ead0d06" + }, + { + "msg": "07887316bad2036fbf7a9adcae32290b8ab291efb9f41874a4699889b01783df4db42f8975923a", + "secret": "2a3db38560c60fe66eef269fb5d99caa36460bc14846606c754e3afefff38fd9f7a81aee17b52fe284adbeb3382ff24472690f8e69bc358f3103fa4bff11421f5f717da5dee652c9a8360a37cc0bdf633cf05da4b25de390f866acb850314c1c04823cc03cffb5aba018e27465736914b6bf47e82b89fe379b271030b86279162310e53c99aa84e9336f09a18300d9de900780c33e9736b502b93c6c0227e30bd21e7ea7ff7cbb29cbfed444bd63f8c42ee2e4c113c93c5369095ccb5e442b12", + "signature": "e8f6f4c29cbe6b5f256e06a333b0ca9c5ecd42fc19e4dec92bc482d88803c9a3992f2a36b52436a990cce98bf30a8e7e030f4ef30281127b5ca60a28c9e6ea09" + }, + { + "msg": "3818a8e3cf632304fb93c982285e2ff5658aab0558767afa09b26338dace35f28e3fa36b33627cfa", + "secret": "b023d033d53a5813b7000b050dcece766d20a822391c4d6c65930681c83cd1b01f564012738761f01ce9e9133e27cd35b8cade7ff5457bae2fba85dce440c693ee76fe8e67c32767c151400e7bffd2ac4fc6a8adb99214215a18de761980a46d1420358b031d3cd7a97dc3673598f38680cfa63089a2f84272c25c23936b934cc7fc9b0ac1afe4ddfbc2a3db2807ec3a2f0fd23d5d9e7830bb4101d2c5bea3626c9b1ea466a5ccdbb316656c846eb4cf2027977c64118184f2e3e1d72669de06", + "signature": "7d7055882337f67899390b66c98cffcae4981d5890f5043bf2e4813e3f5b35a75f5d88a0e91cb15488a2ecb94d163cab82a2860fede7a20c4e5273fde18d7204" + }, + { + "msg": "0fe04c64f078105907e72030d237ad885f62e8d818b04b4948eb30e06aa2e23253a6412fa63c177e41", + "secret": "d21708a11d62efca1b4d826bac250ce6128c0ed063ca23ce74d6c69442a9c0529dc0e0366b1cbf7f3363e582e603fb63730735c7cec509cb65ffc2d93e72e7aac2c59e1d6ae40c7765907db2efa694eece3ae093cda6fdf9104cb138557f8ec6f36ccf1e02d652aa1493db60290189aab5295cb3f4ea9672da8bfd6677057097cce31da2fb5e115619154c2c4b15eb77572869b3c0c9905a34bd5e7020b77232a57b05cf0c515421e33ed2f5a5ea861724db76bae296cc09feb6920f4177ede6", + "signature": "c030a2213bee079328169f09da34190559666bb83ac8d8a3846fd777aa56166edf9f99507d21b85966374a0cf709c8912addf403ffd056c22f0436e7418bb807" + }, + { + "msg": "4c641d9841835e1fc2cc1024e084eebd9e9297f6bfdb244033a97cbd66df1d9ddcae75619abb1fb5872a", + "secret": "137123357aab6c25c1c1d49e339047abe50c995cf7a7e2f29ef4be9f80340bd7fb7b1f8c8bb75b9a8c1180ef9af6877cbf1d10aad19a8f2a7c6a3cb067375ef46c77d2e5ea80bd5e5800bc1506333678e3fae746c9285a5a25e967817ea9dae4b36aff7012aa84ca4938a426aabd460dd20ee8e6dbfe9b6a465bffb40a22c04a6f6898b9d2cb1770aaed84ca505da527d078daacf5e74730ae38067f03963a125e817a645090c5c5ed3e63883ac09c41ff8f632a6d06d5e45d7b5be0acb34980", + "signature": "e55d4d724b582a83c3a0b57d469355d3d7ff1aeab31ed4711d5f66dd8f988f213908c1ab1cff483daf1ed9ccc2192b76116c6ef390311e8a8c4891984323ec0a" + }, + { + "msg": "55562cca8bf1a6f8a107900b0abad5cdafb6b37be4320003aa98007d392380d964d572c43af60f44b73c44", + "secret": "c6d15f49c920acf9dfa007973c11294534ce1a0b011a68f6041c9396858db0fe013bb210b9c395a75674b57c9cf68130ff95150bc746708df37ff001a7aaafc52f857434e6631519c3a9ceb88d8c163e84aa1788772009cb7d9ac3d91543195df35f2f7b48d1d04d82c8885c2f89d51c24c3327d6c063e22ac029af3820d53821a5fc9afc442ad6755ccb77dc0f6d02d18dee7085d43dfa22943808c655d81059a35b55196db3dc46cbf72dc28537e70e2817ede65d1434c3e00034271b1affe", + "signature": "0332eb7101def2b58f0d59593cde54b36d1a64654404603f656706fd2acc23a622d186d129b96fcc202eaf9f27cc5cab81e2120adb7782655c505a6ff81f3409" + }, + { + "msg": "5a2fdf7563200996500a37dab893ff2ea7095984877310d301b07ade72b746ebb9e02a41a740d7111b8f591b", + "secret": "30788c4f50f21b2cce6a75738e7f48c286d15df2d300f0183b0d3c1eb15e66d7d0a6fe7d715335f98e5f7a682a8472688bd2376eef5bf1758f8b8288336e507cb785b786cab5c7762305a9759bdd66827b1632e2f36f5aac41c9d4b18dd0f301c74baaf5f49cd02a0b5059b3130167d3ae8d23785a99b1f770d6ec05731e02aa1353247d381100f9e55e58a9c2fc048f8435d4a6866da2b8f49ac02931fd48a55417cfdad8d423e09a997f094448c4ffadd269e9f831a11114a6a3794eeee491", + "signature": "16803aadeef8eb7fe0127ea2a212646ca774f1065a6a81e495188c0a14c2f48cb8b043a0b90a6be54500cf0dce2d4115ab19ba4b54545f21bb17008c54c2d508" + }, + { + "msg": "4b680720b1a0595ee2be72e5de4c50633390dcf77eec07f1d4e472166cf33af9e78506f8b6644021a225792456", + "secret": "504ab3fa5fd0929da987044f182bbec693e2c3e36afffd92c148a1318326f98ab39369f343d903189a7fcf1c6e78968ba26412593f3d9530499432c30581e81356e1f3a74cbb6c55a8a1bd4a235f761db7533418f15f90922a7045637e8baa42ee9e18fdc809236ed4fbca499e5f3e1ba9ca9665fb184ec92caee61bc24df3fd3cc8c078ea382e2fe7d31b59911777d287c8b346a9354390f7f16ad48d7467ca84f07b99983347077880cfd9ead567026cdb48a797e7e34a03c9798c1ebeaaa7", + "signature": "1e45ab548e871480dd09c9f8da63156e2b0111a562b1364eed490646c843d127af8ae26c6c57996644d92aaddca9d463adcf0c9f9e5e8688b5ddf0f920312e00" + }, + { + "msg": "23b6820724d6baf31b7d3d63ee2b1ec25a015db6c5a31b540315d6f87f99ef39239f3ed39f9ed51bb6e1452e00fd", + "secret": "f5ed74b5ac5b3f6b4bbbd0e3dfbf84bd359ac8327ead8dde776996040de6dc893193df5f480a3784279c38220a4cd3eaac13447a96cb60d154b58632331e355f039a39db42af711a8132ade75d3035293c9921a8bad471cd04e1b7ea31f1fe474910e1eda214449c60aeca8fe1908702a574dde5981ecf02c0473e37b913b14fc6d77df452b99d0981c0fcbf7a9b9772627ba9e9c8fbc76281889d86988a8ae7c28558fdd9a4174cbf9a5cae26a24e5e5f9a52b29288270153c14ced1cc79962", + "signature": "29e428123aacbacdbd2630f423656f15d6f8676b84e3661f7d77fc399c056c78289da0570eb6ef8d78fcd5d6a9822678d1f0728238b06cfaba50b5bc2d06cf0a" + }, + { + "msg": "6ccdb49f0b759ad8991465406ab943e497bb0fd9abfac4709c703b98b18e3e114eff01e4455f6be76f5f100fa0af8c", + "secret": "cb7d348bf471045822e6a5c51afaf9b758ba021320bca8d52cc2117d99cda103a98d497a17cfd4f4b076e7b09d496507a31389f61da8cb9f262015fbfaae7c9ac964185695323879656360a97f2b9fa2dcc2fb93b3b3a0f7cc92d52701cc2a3135f37cdd64d5a04882c5607238a041848a6fe0cbfab9ea4b0404d463a2145c8833e52912dceb6af540daf7ecc36eefd6601768712663ec8e05da8a1148376dc080d90f5a165b0a1fef0ee4fc7e9e0b1899fc093201b6709ba785e4b0a204b020", + "signature": "4b559efde2e70cf4cd01260830c957353ed1ef75b97dbb52d07ca41f79aadbe3234bd7740997494eaec426e2a133ed9b6b1f9dbd3f7a38adb8a961f1f2bdb009" + }, + { + "msg": "32d82beefcde2b010f6288c05de8ded0513eb5d8726e4237eb1385067cebd844a5ac72fde64c5b5c341aa6b738b66ffc", + "secret": "6da1fb40bde4e86a3e86feee59b3fc1208fa85b69c8d22914bcc54293e8ccfa9d25140a3ad0201e41ab94b7fe43353b760373e637bcebacd658df5bfd12634f02671f6264aaeb4703e8fd99a587ade60cf00d75400eb4b81bde5fe69c501a3f1e5176f9c43d8bbca604672460b49a1622e1fbb3ddafe18fd9b147207bf0ee1574d557e60206731141a3818692c05eb1d408d30844100c5d48f502216c374fa359c74cbfa7942608ef4e65c4c11cb5d462ef757dd9676f8b24a5e58f1d59f4bff", + "signature": "9abb210a7a2e5dedb84cba728686a8035c33dc1069ebd4609e6ca9b3d23d70e002e839e58cd05841222e2c66a0a5bd558c3933dab12f4bf2f15211add719900b" + }, + { + "msg": "df0b1b4bf17a300d8ac3743e710fa7cdfb4d37c8c9caccc870e232e96b87fb661f586f542aed5c885c71d6874711b458bb", + "secret": "bbde4e1f485eb562641f8bb1623ed9905ab11a208991953ad9fce1490763bd267edd302b84cf10799561dd1fccba59d66be9f02141a0465de17baf6768741e905f1dc35d65b262f4fbc416c6db33de19d1071c7889f700b057461775544dfb7cb3670a0719dbbc7cc42e4ee509d1a4515ab5e4259878f05b6bb5c1205e7bc34841c36d52f17fecc916748a6879751370b2b1c5cf5d5d04592fc48a7dd8068135aefcd174719c6ba720cc0714c599b6ab6e3c8724152b14c7ea2ea37c51db5349", + "signature": "ca19678146db4221b3a5978c0b3898192b6e209ee9122f05bbf13955283dbec1b774f367ba23a20223d8f27b419f84b21a7b298840c8a72a96c995d1d7109b0f" + }, + { + "msg": "", + "secret": "a8d01cfb7876d15f5b62c0a4766f9c6380d28ab5e47fea14c1b6908eb73311202f32d503d7cdbb32bf9201dea3ec544ceaa0e4b53fbd855686b0512ca4a8aef02f3ed741a78fa20f0e4e93db03b98047924cc8fbd1061f9f60eaa7393811ba790442e1716dd85e3e8fdd894a2fc88b31cd47a86e2a538a66d56827be604d07aee4c9cac544d72c1c52c51545a895035d89f9894e1817aedaf363d52ccc30cef31e7c35b9368c430434cb222ec1cd2354da920b2478dff532904b3f9482da55e0", + "signature": "be9d4325caae49d7b5f7682b1a2024d458d010918b1bf445a528fabbb81ad8ec5130cecf14f5c7eceae68811751d33335b93cc4d19eb05fec509b7173b1a6c0f" + }, + { + "msg": "a9", + "secret": "20b1801f0f5838e764079bdee6a6215ded32534492a9361275d0e2bc8278c4a373c853d301bdeefaca3f1a3f62330e52011bd3ae47d34efa271ed48fe1c934e9022ffb02650e14c30e83293251a0c13ecb22524813577d2c480cce49a56021be57a243211cbf7aaa9369a39b19ebec7e2f8528f31d87b51cbd01b1e4ae1f999fece3bb8a01f95e5d3f1254dc68ec414744b62b80be4db298c63642cfb13847dee35d454b709343ea54db679b816ea735e801d414a9fd5c6b96a5a2593530eaca", + "signature": "ec302a75a5618fd6b35314a774dc41031f069bf3c8d4a3bce75cbeba8894de3e4f0cb72d65db4e82c56ed8e099846e781cd07ea6924d1713d261906373693903" + }, + { + "msg": "c97c", + "secret": "7236c9db5f02bb3c1b5c248ba9ec9eeb1d28430df8e8ac65c82666840284bf06b1ad8bf10ac63db6b7b8cb51b25593e5f3cb0132fe10c915e1043ff1a16f2073ca497510ee0195ccf8afd8657b7bb21419a6acf3d9de7e1e2e40274b90d635a80029368d6ef79f6e78176da0b5d25a763f242e5066ac6fd9672e1ecebc0e9f3a3a39e5f083cddf497aeff540f736b71d26cd0adb827169699869057444b58eecdc68ecdbda98228858623af51ddc575136ac70963365a8bfe753d1d90553548f", + "signature": "a407d8eed252ac5b351a2d5f726d637bac6ab400d8513f1ef1d1e70f9fe5fec07719d18fd02a52497c88282b9f04c49143b09db6b5ae8a3de246d57d7513d70a" + }, + { + "msg": "4cab09", + "secret": "0d6a6e065691b3d83150b1ead638f7605d166dd3ceab9dc641134529c58f57d63f469dc4efde9d0f4e881863858208747178c5bf4cf135f16a41df9531b449525083cebbdf2d084db7d11f1d53ec07aaed45b88c959a19d1c039a86c3cc6f22fdda09228bb78a59c4ee393821f7c2fdd63eda40f7450bed8a52a9389d4afbf44ce8cb6b9a8c7adce6d93e1cd738e5b43791ea4df8fd649f5d2a38c79f13c218706ea9118a921960a9a6c64f9d3b3ee719a0aaf7e276938bac654561d12985aeb", + "signature": "02573562c88c156d0c6fa236d72673465940eda787019ba398ab8a5991f4a7d38da2648da60ef9e5bdb18c39cd1a5f491195321940b3a13f7d60a973c803f900" + }, + { + "msg": "8b931b7c", + "secret": "bfa7fc6ad48ec458d713dee4650de23b5b4ad2f96fa109c3e934a4c911810ab82dbb5725b8d6aa7acdb8ce5b1a157dea015c903789a66d13023fb2e7fbf055448cb9ccdd48d36773e40cb5fdb5a1a4a3eb5fe1f5bfb7137f3ad50942c661562b221b2d68c116f7ad5d21058d898c6aa03415a1224b95002a9d6459ebd25d788aa9a9af508e0167495e14d51d2f780cd28c29a18447a882c872cabde8f01c7ac24afdd16c7ac6ec6edfa6cb738090c164f75f7d7b8b61624077685a184c92dd7a", + "signature": "e780eb3d712a960229164143723c1d1438fb46ac81e7fa2bca52f8035dd68aa88e1ab7d9017f4d04c7d13a73708c9255faeaf26883b664f3d3e155e2c85ec30e" + }, + { + "msg": "86f790be7a", + "secret": "36fb426b68453982cba6c20c8a228c438821a235ca5968854a9f8ed7fe2725e03c73c9bdc1eb08954072b5f58b2ab364ef4f3183a3f421ec2b2036c8e1599a95c8c642e9f460d15ac8c4817d6bce832916af99e163effe51cefb056225e02f9996f29d0292111caf372ec14467c2bbf561ae5198c577820ca8a61210e4d7018afa7bbcec396c0494c14d3f11f49e0758b2345fb0aba90a6d291d320cc073a4827d714a6702d4c281492998aec1749853bd2023c473b4d85b40e8d1b84501e062", + "signature": "830a2ada61d86ecb37d2c94af017249a6518a624948513a47cb69a6bde67ef30c1fd19a3e505f94cb14af1b9d3552a19005b93c8b6877be8e4e8c72be3e7ad0c" + }, + { + "msg": "bea477732cf8", + "secret": "2337f9fabf72d2aa6aa739f26aa02ae98d642d2357af13d13878d11ffce779225633c671d0f2290d8779545d2db7d6f23b05333e0fe526c6cbe8b4e0f2b13d554766016a9385f76d22b5df1ced288e61caa1293bd4c52fdad263ba36e25ad12c54ac282d39ab94b7632db4bbd8bf944438a258fb8334958fe619a4b18f3b515e2c0c27a39744972438a0952d7eb9c2f82ed348757387349c00e3b0196efa7bd9dc75a169a11f00fe9fba0b7a1db841f0505f66d2d47c777c40a51804d007888d", + "signature": "2c782fb7284bdc04c7cbffb92809ff9135a037a98d04e30f47ec53842a85add630889c9a2aa93df7ece8c5f17be9d4d3339e61197506535354dac187c23aec03" + }, + { + "msg": "f010c6ebae848d", + "secret": "ab43923fc2459dac5b457d29ff690d318b6f4c5ff1529d643c9949deaa9d3b846705bdded2cea28cbbedcc5b97cb9ff0d5f91134cfbc89a178cb783ef9e0cb8b2b3f5971dbbcf81c68304e75bb7a4dc7d0368d00f49180bc738aecff0a78175193c2394005e8c5730437b72f68b4a8e0c76185cc380053afedce381c3c908513c650989da1ca4bfb65ad80550f5225b407974e28fefd15ebbfed2f33e68f109a0cc8a2509a0387214a53b7927a7446203a7a81997065f4b7cdcc5c0484fd9acb", + "signature": "91906afc1e7e69c87a4f5ae0290b0317db33514eacf3b5d9a792b5d53883168dae8713f285018e27eaae82a1b9b3e96c99866c6ee8a7f1dea787f5652d49e502" + }, + { + "msg": "922eaa2ae657dba0", + "secret": "c9e6c5248932f306efac7711ae0852a924324bc03350bc6545c49b8a8a5021b3b4427e8fc5923b54ff29f0d53bfc0982d30b73a7063584243b6acb1c73d1246f40e63fd72525c7b873bccb6f0a4fc7751d6663dd4fe5624c12f2d55d10dfb6481735813eda95c6ea7ddc675deabd5eb0732bee0aa38f590e95ffc8a605c5202162b114e9455a02e30590141bf6fbf41127286659d13b0b308e3089090b067709cbb1740003e02294107ebbd9e12f9012056a73008fcf6d7858c9b4dad892a40d", + "signature": "319f65b371305a0a0f175f0782cc51abf0930c079a64f026a1a4a50f25699f41c3741ab6769ebf60c91b117268cf96fd0d562ec7d2cd5e803716f6db113dc60a" + }, + { + "msg": "7850f57cf9095c58b6", + "secret": "77d6f071e2273f5d43b17174ef803cbfb2c448353121bb8dba819b3fbbb2c13a1df356b7cf340baf54f8b79085ffc7239551c9be6a941cdf2ddc15c1e9a8489a91814f3eb0c45b9e6cc3c09e2580c46addffbd0832162a2afc55ca6166951810917aa2233b37e2934ae2388d95fad91f1d33e75b1c991bfadd559f1731e97678c517a4c29b896d98d308599beaf011b7d4d7abb7ef5cbe982bc50987cbc117b78088eae2f711da4baaa9b9fca394756b8fe801a7c8b61039a17d61214ab915af", + "signature": "7406da5caa09b4b676c810fa01542b09cc27113ae45612e2cca9d7a283aa78915d5542639bba11b4829b981e606ce8a95cfeff93e5cb04e92feea386d0c66801" + }, + { + "msg": "91fdf398ccfc9df4ebf4", + "secret": "5e48201d43df5027ae155d9c5433c5f133fc537307d5c09fcf8ecb5d0ad6c6c136a2ad282f6d09b33abecc09d046cbe8eeb15aa122b99933e3d287f5bfb9a57b51c79331578d3f5ead9eb7c9050c3284beacb4a4292e6d13207211a8dca5ea197f143d37bc0c0be1943eafbc0b9f5c412a82dd92d2a3a2b163af5270e3dbf1dbfe6c9b606bc0c16b841a9dcfbc8bc2e1f41ca1cd9ca28aace9af34330513926dc06ecceadfa0f8ed023ceb82cd268b64d8c82d91f8c3ac13fe32d1c5888f1539", + "signature": "8261793320fe8fa4b4b14a5b8811215984d99aa6d8e05ad43a12a6026621afecfafbed7b9885c254fa34a2f59824c06c373f1fe5fbd8202c4e1080d4ad2d810f" + }, + { + "msg": "f8a2cd15d1c62a3848fd1e", + "secret": "9291e14b71689fdefe68c8638a6a83e00e28682b332af6df6272f80d0fbbbf5c9ecb82fbc7588b5a1096bd07c69b01bcdaa48c6621ef94779e3212737593b56e9b47cc26a99bf36890b865d3c291d3fc3d373c830781857b31d8e72e438ea3b4281fd8856118f3a7c1cfe53bd5939eb09278cfb055133abfbc290598187736fd49b59ebc17abc21d639cc8394ff290a7bc156f42f9efb71b7d1d4a50ce1a699534e85b57fa7930dab81f9a260a5b5aac74238b2df884e4d3dd4094c536d059c9", + "signature": "9bd1fa4386d646e1a4d7f55bbc0d7acae355d2487704667a12a11f221fb23eed70cdedf5d171e9081562dd776ee36af89a46ed8d330df45b8a12e9dbf02fa00b" + }, + { + "msg": "bcf513ce24c7ffc7d384edc0", + "secret": "cb544bd2a37241387f94839de3e001e0a1e3b214713123b55748a47ad6c73b5e0aa9222a2029158fd950053e12cdfbeb4f29b6abe69f2f941d9e58b5da76917ebc9e1aac756f288afc60c7a60ee3819c46e0724bea072de769f8a748fdf553249619966046112085a8f50da3c56827bde3712e105b4493545142a2347c957fd243a10f0d977a6232d6e39058218431517c47daf43ceeea1da1c959c984bbd195cd90a0990f7b89360f60b8288069e8a73a48fa6df5851851271bfac68e17812f", + "signature": "f3119a323126e33d04dce0418ca1a092607102a6b7280b9ca18578911bf9f57546a0c0370a0586bb9a0a4e6dcff1e47d63dc69e62564745782006b3920fbe400" + }, + { + "msg": "fbe55fdef48015bd8d33f6daf9", + "secret": "d3f054d148b6fd2e1b26a747c380dd0f26af9cc793763d32e4b42c980e7b6a91975c58b117c54ee48681edc69910a0f611b49e97e77c4dbd4cc3f9e3890cddf807158424c06cdbe48f2fbe9aee503c233d5072950669133f55f40d7a3ced9551101c220d1fee227307039cb929ca8c5cf673d5a2ddeb22893451b06e9efc0f91e8927f9a62a1b7f200ad19c598a25d08ecaf56c8597d711befde8d2c66f19045f279a6132cd50db577addb284c483fb70a9f9436e59c22cc1238fd5869b8c58a", + "signature": "081c392eabbfed9fd8867c03e6dc750d1cbccb0ba6af60635ae2b913e73e3a03635462b5918347a352c3fdf8d392ebc06921143b16097a4a1688703878552e07" + }, + { + "msg": "e851d8bf9d41d441e3db9c66c094", + "secret": "a101446e8105299d72de305a371dae7ee83ccda6b28458d8f40c2987c3ea60c3b5e5207b2a02a267234b9d90f7cd84ee4126280d3fc1a21332a167d64d0bce7f488d4edef4a83c06262832042615872d4e080f76e6902767fd4a3a542e696f3288670bd60e1f1083e9b956b6762b04c493d95c75cc6661e3d5380fdba03425cb6892fc601ba613f302c8947757b541d90e9f2bd08390289af794073d5e11f401c4aaa0bb71f60c509af5162e06ac8606ad111321c523d6433f25a64d80d568ab", + "signature": "60f2b6d7b7034a6ca7abc289fa286aa7c16d746cddf2f11296cd2923992cb648a835cd9df24db7d25419c4cd50d1e2ea4f79da6d2b24dc73fc69d0f08b2cab00" + }, + { + "msg": "780812fae55fdcfe2635fbc367cb69", + "secret": "917c05ed4dc708284b50e024e930a94f2e8a7264d9ec615607c179fc77864910ad2b9bc5efcb6731279a0a20f5f7f341e9f22bc79ad2fc3f6aadce3f9d6a523649496a5c03c2cc1ebd0bc6135aa17f73ba0ed6c14e94f3929b8179ea7b2e8ea3d7035b255dff6afe8738f86109e2dbe8353062b2f23bf398f15b2159ecc3aa737246b0eb5729e626371a6425ff3c130e87c36cd2f4936de91700fdc5b24f0cfc998a9f292e0fc44be62a38afcfd201ffc8a152f88223ce5aa82e1c7c129f5e38", + "signature": "d7b2ebb5a2a840aa56f0e3d8c8b1966c64bcf38723d8db4350b92877cfa158447b084b4508c53eed9f08cfb2e39ec818225b744736b5b114291fbfae7da33c01" + }, + { + "msg": "107165ff738bb612af6921c65232aac3", + "secret": "2d65ddeb2c0e1b4a7367f48a75558a1f845aa3a56db721e89d1adf55470a909bcff160c3cece6e51d6a320a286985c666eb52586c2c8adc0e83551748f3ae1406b9608ac34a13fc8aba6c0ea8e03fb7539c5e19ca9b27747f179210e1fd112e9fd37c5fce2c177ed19013b29b0bb1cc2fbd19071a33131cf19d1b778f0840187f5fce23ae74e362b254f78e97b1e4160b81a4a6971ea27b54a3ebdd5989ede7fabe98b073c47e2f98b9792d7aa1b8dd71e0f0263ff31085574a5042eb3d88229", + "signature": "746a5521def37b51cbcefd830443df40ca41423964895ef2a94992d02e28c9aaaf89d46a0ebaea6e16fc334f1a5a237db95104b8b60daca833e3bbe1f435e303" + }, + { + "msg": "550a1cbea975f16127b5934aa11888a939", + "secret": "829baaf00005756d581137e5313526cc7040c66b7885dacf503f8a13c0d40170b3ce8f6dcb7beafb8d26319a0ae16551f773a8bc7bd9340c8f1c017ac92ae775ec2a2479249c35401b5dfd48fd9828e783c08c8febfa81c266d3d56efe9a711735c44b02bf9bd8973d1e21939bfb4131f0fdad9557581a0181613681c3ad864bc1c9ee237dfbd60ee1e16bd13a58bf481f4ce7157345ae98b37aad777d926960aee6aa69c0f0feecbf878d50b29949e60e9fd67f699dd89c5632439d121dac10", + "signature": "7173ae4ee1b71ca289ee2ed3bc8db884074da413449d32473eda8189b26b67f76a8ca1a331d32695070c25e98906ad7cdc607e80ab1487acd0b9b251a6d0420a" + }, + { + "msg": "d50afd74b12fd0f92b33b6b10288e2e5be49", + "secret": "f0938ae2badd1574a707181cb24eaea9d7bc90837c602a5cabcd2201d1f56a02f45e375c07aecfb140e20a47221706ddc0c89af357a3862da6e0e4f732e0d7100cd3e0a2b89ed88341893f3a2830ebb1d3b1fc2d9dd230de49bf1bdfc8e5aa9ba53bff80da56af146fbdf7633190247ef443aad8dcb101feb5d40bcfc9e8cf636ffb25dcb9fcb01099be23cf703247560e5af865ca0c385f80849f7336766f7a9c234c92ddf49e7bf61636b735e59683a4755a893a0cfb9b55e0486e7c4b872d", + "signature": "7ecade303cb3ebd2f5b3cc7e7d0ba186a96b1c4c8778b8bcfbf69f735f48f0c5b70eed98075adc76c888cdf22efde5052caf29c635dc767bd33188d084d00f0a" + }, + { + "msg": "0856facdcaae8d0a1910d88573616e5dcceed7", + "secret": "d5780692f494d450e5dd93bd9719d68e47445e0b36b6e1e3b3c6d823e2b71effa6e73d6c6e90810485cfe919bb837bc90e84dc6b605656870fa12d4bc052fece6d0a12dcdf206e90886439435991c4cb345785e3fd5cd2384d125e65665d5a3589ecbc40086ab208448bd92155c0c768403a99ed2d5caf23fa6eb3838fa9a4c7ec9b71c2085cf522531b7da5eb43dd98171c6927b9f1d071ca98f4fcb2999dcb06cc6cdb9703e8c33db2b2658cde41f94c7ec2cc139f7b615b258f3d4a963d6f", + "signature": "aaca14a602ea801c3dbbc78461153aa6e0f2324a3c71023fa27bf1366a4018e9ea8d67eb1fc801fa993d965f0730a9cd8e3650a23f56daf3d6c20c26ea24ec06" + }, + { + "msg": "5b9e415a91fab5323909b1e261554e6c47f77c34", + "secret": "1c968b1c87f566d5885e126cb78ca9c03f3bfde0097454bdbdde3f694e1372f6f443d26f9e037e4627b7a20343e0f302d0652bff824a9f08f1e1f9736f282aad3c4c86d707c641f768600457d06c6b37b8527397777ae1332d4ec94f9ada39ef04e1a5895a951a7cd3ddf380e7d34306ae238f8eefcabafb7e90243e677f94c4ea6601f52bafab2dd3fd896c39d1cdbad5069739ff853a64d2fdbfd705a5eda3ea6b50c2a5d4f72329fff52343eda89ac435553bff5a876ea485fed5ebecc129", + "signature": "1c4f6a59d38022c71b81b8e460f6c40a880ecb474f973ab366453da652134e507290031a8147d71c511e38d98054bcf3d159e5592914340d81f41b44134fd30d" + }, + { + "msg": "668174e03bbafdbb94401a377028fdc56c1bbe6194", + "secret": "f15b45a1d326a9e4414f83fda3434e8381dce9bb9277fbf38d3fc456dd79a57d92d53acbb388c99b2596d31445df3d67952439f37657a0062bcd6281b569c1f36f5215109b55aab25bbe6afce9ced642d752aa14a94ba32339d829824674aa873384ce0ed511bb1da0894e64ffa8424b2cbf22f536d61cb2d74c658103e3a80322f3da88f839b6129091e8b5a645085726674ab61731620452aeb1216e568d84bb4da5fbda065f30878e4143d369dd46adedf9321fd4dd18eed41f395b260c4a", + "signature": "04b853d023894e8602de90da189966674e959fa01501957da58d24c86eafa39b688d9c66712c1eccdfa8ddea5d2b6cc5d461fabc9bc356d2bd7822d0115ee507" + }, + { + "msg": "d8f1931591cd7b1e14caae6b3993753c0dba71b2818a", + "secret": "eb5097d43af0b624e99ed26dbace0ea17b1c92e151400f2c041a3a296b0106615b80a808bc7986cc3027971be32ab1f61c779fbc039c7567093d1ac0c3b5037b44bda634d98dd07cc1ac1dccfa4378db343ef9fc905faf4b610601429a019d554dca3142afe4fb300e03d0b26d66ed6eb53c8514533220acf5b827c2bede1d51c64d17131b0e7465d41437cb46e72b5b9f1906110fb89190fd735cb9bc83a0863121c9a54a76a3531c074c23a6b26e14116d6513d57fdbfd7faaa87f08e7ae6d", + "signature": "ca5d633736ad577357a5f82123b95430bf735f9dfea3a64aafe4180c83b11cf6db2cf0129db51fc9221621f4b793a73f2fe240a2b2a985972279304785edc50a" + }, + { + "msg": "6a438d306b1988bc913c97ca06d9dc271c17cc6a33531b", + "secret": "f326b716af7d10e3cc64a327c2742e1ac1fcc195734d8f84ddad73738c9cdb545c8a5744bc582fe3112fe3f1bd42882278e39dfa6c09231a7ab2589b77d74e56fb65f7bf95ec9ce63260628d6b4dd14aa65616c1ac60fa8c3d2307542ee05677eeea392c7e2539ab18f4d6b0f065064f59da35b8e5ff0b62eee5417adb1750747a7faa9a88547b10729f4f4c348e44b295bd86f2771e6d05ca139167a3f94d0ba961304fc3444d6f038041560eba68fe749a4b80620dc61c3a5027d124e9c911", + "signature": "b46c519f9d241f938c6baed47b8cc6097a998f561826607d910e5c91a7ae4122aa6554b992b7edc88902268ac207c69b741d336fc2bfbac630006f262c7f040e" + }, + { + "msg": "04a45dde4c81dc636978196820b935443a45451cad41e827", + "secret": "7353d18f2d635a640a861532405ed589e2ca16163e6a4696622f937d6724fe6924c370ef8b86e836b5c0b4c3338472959faa5cb83bcd0abab427fca8da6fb07de2c01a8b72ec9cd01437beb54880c5cc581fc344de59e9deeec0128c5845f74c23f8ca3ab2c9d1c4398dc494b7d74121f92842684c84179b6916c9930215dcc110ae3768494dbcb10512b216410af02144c2b2caf7ccc11c129dc1ec7f18e86d96c397ca4ae24fab9e4b098eb67366d92ad86770922f29990fddab21072d28aa", + "signature": "46b667bbb3c33475835bb2a05d6c11033461d4812d3dae6426c214cd9d55a2bf1e3a6dd2496631c0c18457bf55f675ab2aa5000c1ba2aa92ced2e279d3fdd60b" + }, + { + "msg": "1cda2d715263cfd20ee44aa5b3b118e1cc0f10442d7cc86067", + "secret": "3bd820d166a42fd7b74e7b8a5ce8a368758eece8a46054a1a3c6004c5a7545f2a99e942d38f1e928f35070a4036807df867fd0c9116c5ab14c37486516c038622e8bb532096b1b1c39c28297b82c89d4182caba6a285e4ff9c1a3f73b9efc391df14a2c56ebd2858f9e6340e3653f56e1f83e57637d36318f6e16a935c2fe96bb44198fe2ec4db6b6461c41f87f6ffe7b854951045cc9bcb24381029a8d357b78eb52d2b2c594c716d4c0b4b6416da353d818bafc2c114f137a44dfa932a615a", + "signature": "aa5ead9f659f9aa6266c4b63f1e5e48aebf1a158d851bc5b50ac7697a43e70d36b1b512166b1e105c888a85982c4601d017e78f3db53e2f356921d0075cb9803" + }, + { + "msg": "ae3ff11bf8d155641f0964050866f0dd1ae4b2231c55267a7391", + "secret": "d24545096331e08049a4043583aa8adce08a9bc8973bef0719ea0342bf29abac37404255b20d12e86a0dc096600fc1bc5b6e2d755d8818b6a6041c7a09cdf9214f142f1eb8d2814dd9146fe10e25ad23bae60bacf02c3003e2e772a1f7dd848d2d0d9e8f36d3cf10e6014ee1355d544f79c33f96afe0f3dbe3d2d13d7b8104bc5b45e03bda8ed5dbc5ac64f5ba10edd314229143814483dc611fb3a39f5d526dbf5ad29b4ec9062052737cd617c7bafc524fb85422021fafe114e25e21193c81", + "signature": "8e9cc29bc290cbcf92f801814238132d17c79eea7924e2f93493ce07ecfeffdf545f1cd61bbdd4fea43f2af323dc83da8ed73abb275ccc69c06c7b146700d30a" + }, + { + "msg": "b3d0bfcbadd7faa75fc7612a4be8519ef4d85beff169a8e9b902f1", + "secret": "846c5e198fde1d9fceb81781c1a126f2e93a9173a80ac245843e58e2d3b886d13749f59c4de2122b2a175f5b0f7be33723556250e078170b83fce2569f6bc86d4967672645312e0f915e6ad43dd892ab15b237c7946b1fcad7b38e9f3469ba0297a4f5a877114f184df580475ec17d04dec5b57a37c65ff044c3fc4ba58a230068e3cd848a5cc1e0235a764346f9e5c41eb2ac6fbe9341bc054dc19bf35ba72c441eb69c61a93fdf36bce5f98f0ae7c9644e7c5c3ad6bcb7ac214adf4f44a6b7", + "signature": "aa448ffde51cdda827c5222a650af2608033e924ff76b66cfbfeee65a1fbf9de85eb09e91d69077607b0e7755b479b0080859ef97540f5ad841e113c0c6fcf08" + }, + { + "msg": "2b164d2e3bd95010bd8cb4b61d944b0d022f80a57a70fa84be97e399", + "secret": "0dcac71efec2df84bca76e49ce3531c79990bed5953e1fe13e99911ed6bde6e371ba9cb6910322e911816168700ac8e2f6250ea31ab9947cc8b11f8a603077b491a95f2aae23a43aacb2efef0d9e40161953afa8ab6284345b540e8775c59747667aa1a699e0ce073ffde284307b78540da8e559fdb8628967f45462f54eea45e38b453f2a61f2856b1e47bbd204844d18c57c3864a7e08b9397957448d22578dfbc1455e5ab909c511b23380c85559bb57358fa962ee11015c94374a5b4162e", + "signature": "fd2cd3678f4c42787bba16bc63af7c08d3058c0f5b16a35091e77691e546d5480b83d4b31b36627eba65ae45b6a1c38ed42761b7f39ad16e9e535e2acd610d07" + }, + { + "msg": "18cee04e7c03d58bab320dc472ac7c89d70d5490a225f3d4fd73198dea", + "secret": "0d92d1bc9a72e314fff026c909aab6feadd0357c4f357c611acdc39d671af8688ccfeefa8665079ac1a1c310a70db3ef0b23c8e63f6f778ba9dccfbb2baaea526b7720a0ee8b9fb4c61dcb0cb74fd023d9b59681122f5fdd38020a139c569c5778598f54e89d07ccaf33a89c36dec1ab7f228acc51b79f0fbef045f63693d5cc287fbe88ff676af9d2242322fd06ac8c088accd191f28f700e12f741c393b91f381dd07d011a557177434ccff06555c3a767d2287f68de8578ba2302f78344dc", + "signature": "8a1616806a7f08b74c58418f2921a373069d3c5198e792c2c2ad86f2c2939e56237bad18c1657f597f15680857959168854f333af7456caa127fe5201d2a4e06" + }, + { + "msg": "979a541b9b73f458d7a9b8819e331042e5f740f12f659070db86e4f1faba", + "secret": "88854fdc0c64075e124d15eea6ecdbdaefdb9a74436cf9c0ca7263b030d2387b8daeacfcd5fb911acf094b53b251d83539ba4cedff71a863f756c79aa6cae1df328ac58be1be7450bf82a699eed8701b062c7350e842ea7f25daf4326a5ff0cca93dc7730d89e7a5757574288999e89386ee927e6080ee46f7a31017de0b7db7d53f0cc0475b33a7c0d0512b114afb263af8e1f291886c7137789fda631a6408d196f91d61be9c8355fa90244fdeb799d7b048d40fa606fae22cc0a5b3c26aac", + "signature": "f3ee8537a465192e4249e3abc16addd85039c728a1f97e9480c089f9b79e5ee9c5bf1a8505e3e4ebe9c7baa195db919ba1395f6054399bf462f56381469e6a07" + }, + { + "msg": "db4adf1dfbab008ecbd2212faf4acf81a59af197015c63863278cc5af2aabd", + "secret": "ed1fcfbba4cb10ab59db6a97776136c6bd7f8cb8f96984ea64738766cc741338678c9d93e82eaff5ed2508d97dee2234108f130636c6e6ca7488cc96dd57dc17e5f0761d728b03e9bc60e66d3238ab80e5e453cd4bf86e2fc65a3fe0957135da8769c2d3c01e0b506bc4cf36b5abd5ae25c50f73cb578cf37b713ce6aabf3a70f850701c12121d0f9e80abe3114a8c3dc0d06fb730bf907a230c37eb27ca8a6b5aad53a8019263f3985fa3026edcdca96c7dd0f5da26aafbfc6167a920c989c5", + "signature": "bc70822e169d830089d3194871f6718c7dd1886c41452bc8b1e815ee5a2397200bfe8bfe6b606582b7b7639ee88b852555d102531d990847db56524770032c0c" + }, + { + "msg": "e01ae21265ddee0cdcd4dc350dbcee1d1fb8d3fbf545d0c5bee96e0668825c24", + "secret": "c45a677946f1511df8c01eef00884c53fffdf3de367146328531ea842bc9748a2affc42ffa29fed0501a6f22076f011dfc8ac058710e166c5805113c3e0e4dd523e814389a277f61e78141daf1d68bb4d024d7271d907b8f104360b532ad3c175ff0e34f7fe3f14258dbd36cff6c122d961385ae6c1457c3d6dedfa3e9bb7f994e1ea54934c9a3c9b6235f8bf49cb30e843da8457a194c692e8887702c3fa9e9e62dfa906af7c768f9fe9ae9b271d5c2cb6a53a9772694beff237c611de77342", + "signature": "90a89769fab368f723261fcbfe6f933e96a73b036e4f8f5880e0d474506936516c0a60879b49716af7897345c5b1a6e8a59c1bc41a8d9eac68e2d7f67b7a3b03" + }, + { + "msg": "043e93ae76068e1185e4c0a4e2e60ac8d3e3bc686d05c47cc1d50b59600702f3ee", + "secret": "e8cd9ed7294fddd856a5b9cd6b1819a82e40bbb7125128c2018f515697d7725e597ec769b00edd11bb55b834bf825324335b0be6e2661399dc6334c0c441f749af83ea81f83c02d2a6d9c06dfe50bb198dba92f13e78b9022a51cb9aa67ef9bdba7c5cf3d27d92ad56bb08ef3204776b5be78224b7475158f0f8cd5b09d915cc4ee2e09f75b0b0847f66fe0e16b2841d6b6e3de36382695f54a4d9780bad43bd0d3be66a6bb4786cd2ec19f490c1fb165823b2e04e0721053c702d3dfbf975cd", + "signature": "0e228dbdf47fdbf663434a176603290e8a630154afa881dcc75240fc2b922c134711b110bdec5f2e810d02844d762a87aa9064a91da4cddf2a9e5c539cff0600" + }, + { + "msg": "7eb387bf995b0fed1fde8d0af12d4ba6977a41bcd9bf979f5a2b03df86232994a0d3", + "secret": "64cab97eb0004cdce967216e1de2ec05470c90c70b854aaf9e858fffa617b3debad9710b2221831e1d920ca4a0d7a2fe5384cb364105209100feec9c3fb41d08b01b6697efb9811bc624ec9abab0b11038734364f5c91c97c02a292bec56e43ea6c9d3541af9ada3f452e1a18522a96d31d59813443bf405e36be3b8828a6f3818f2bf116ab65ffb96d4fdb040a643a01785de83088a5a8870ff81af59560106c737ee75a3da531393eb08d6009a21ed094647a7f7ee17c99625e7ebedc1828e", + "signature": "e7de90deb56181ac51b78b46a2a674a0f225cd88afd1e206ddaa2057df9d5b0a6c1ba826b7b1c3bfc42ee2898304399834f0c102a2a47ba5461f1ae01bcc430b" + }, + { + "msg": "b580081385829a440a2cfb79125c4d47a06790bbcf6647b8749f994ca4e23d4a208267", + "secret": "ebd5559f57f4daf5077ff5b313154fd8904b4f41c0b1970dd92b6d29200ac8002eae20953bda520b2313a178e88a369e06ab400385e0e4f19a16fe8870cb08355b887632d6d08a09ad21a281bd8921003caeba1856051069d14010edc91289e5c132d537caf403d126b3c55b6f4cd255f4965d6c9fe4148dc5c3d949eb268d9e2421cc2c443b3b3c2a3cb64164ee4bf0a912f39466702f12224e4d492632974e351b763420811aaf13de2e005259deefa6f8c2128570393c2394a0c9ea9808cb", + "signature": "7fdbafbba0b703d0e4f2dc85874c184264416d9b409641ea6d3ddb501fcf5ee856ae2b7cb758bba5f6c97f3171f99969db05f3878a35153aaf5857b13c70350b" + }, + { + "msg": "0eea76f04acb8c78ef3328dbf1cf8afa9764ac3677cb4c786ea4b9f23e43d5fabf5dda66", + "secret": "6d1419b45f23d1e9a07bb66ad2cfbb1fd6dff4b21902d94519ed47bab554149f27c9b10c5e062b5983a02814e91fe5552cdf810e1572e508db8f6a5839f2dae4d35d93c837208272a849c5b5a0a04c2898a2a2266584ee17950e15bb731e8b91844989ca79604d8eddad2c1d5dace0394c98e0d80264735b9c2b54eb4356ad51e066a7364e52d23caf59f83058beeda4b15d4d5ecaf9688aae7f5f55811997d97d39df88fc8b8ec83da0b6f1f439d1f290b97fd17f27611bdf33b83eace5f7d0", + "signature": "b7f0cf3af1af92cd69b95b2834f6e9fa4750af6705f3ad34282893f35878a53ec695155477f2d0a08f5992b56846ef16abe165935be7cf476433f053d85e2802" + }, + { + "msg": "75839af085afb5285a5eda48e72bf10c91e4c1950888c11c7204466b67e828bed514a51b43", + "secret": "df348c3e6a72b2e05ec4504e326c62c0daab0d6ac56321bdfab9fbe6427fd4e53dda192df54c1c93a7716606f26827cd40d1f90edb5a7f72a5242f2ba7b32fc3e89e367e8113e903c762fb0d08a6e2b1609b0536177b5434fba923958786a462884e4d5465a04800eff6b1245c94b2bbad7f4ed736bcd3346fde2eae177efda8877fab7b80a68e97f03dae47194c4499cc07293ac90805fdc830dbbc820e5de0eb1c7b8c28aae533262231899cab18b032ec32b6da709bba39a76683dbab8af5", + "signature": "c26f562f1450f6de94ed97f3e712ff7695a0f8937a89f8e4abdff4c2ae3bdd5722be4eb0128c311719b2671fbfd46b9ae7ed017ed26fb9f3c024505a2175c209" + }, + { + "msg": "4cce4a8af441f44557f9d95270ee6d51401f5aac363413e41bd2b6389421a0744e35755ce5d7", + "secret": "1744a6ac25c9abacf29b04743a9e15ba845fefd3884b1c60cb83700317fa9cb6a62de467fff5164cb617acacf9b8961769ecd7c1e91936d43a308a53f56eeaed1e17e92b04fa979c408e49bd300505cc4db5e4ae64d855d4b0d792d2c215588a72dd193be2df5e40cffba835395a218d7551f9f9550d63eb16bcb8a2250f59ac1c87601338eda6efe000aeed7fc16294b26a91dbe4525741af75f7b3545d12dfb6359ad11302f0d3ea4c2a67f091ab1db70c214c86e09a7b1de409baf76d849a", + "signature": "2bfe6c0966a045794fb528805bac5001b3085a2f4ba010ea68cd81cba28203f157238d3880761ff2b6ffbc21712a8642117b1eb80a755a4fbdf730be38a0ba01" + }, + { + "msg": "c30d798c0352809e0453b2f55fc856b137ffdf36c043ecbf419427313229a5d67dcee42bd5724e", + "secret": "af63581ec85c6da16dbb9a2a5ff4611444141249737471e0b562df46752b9dbd220a9597cf8cb374a82a5dd22340c917c31dc2b4a18bad1b93d27d44ea1dae6ec6772136742f94bb34188614c1f98498448636500237adffc42578fa3f01d38168203a8c11ac40bbf973b5eeca8780e1b5186ea41ad28b8a7faf32a9dcb577ffad512df13ca7c318bb9f63cfa8d90718dfde97680169b4e33b9f01f9b827222bc2216ef7101f832058d01604dd6f83f65200d305547002db7c4f605b8f50e084", + "signature": "ebc44d960393c080e4e89cf899c3cdbcbb779ad6a9f074e4dccb943a05c4eb4d38e728703ea312d5c5bfbfe0d51297718b66ad11711410f6f8d4d6ef9051ec02" + }, + { + "msg": "72c740ac0b2e7691747231d8c074fa1f166b648efda4c2286f382471561fc148a36cb892e384e4ec", + "secret": "bea36fe00a35e50c7a8f8292ba730d10cea9cb1d5f4863f9ce43ec57906173cc6fb5e6f68c0cc78ee9762d869ef40dd795588ee26ba5f78375d075194e72d66dfe6c641bc4c10e13fc0e4a62c8242bcd28d573aa146c85738f788f0c0414a1c317427209520cfa3f3727e8e3c09d5247bba75fb08fa43f77fc101849fb8bd29d85f66fb7b95dbf1ea1e985b67273cd8b9ede0dadc93e850bbed3cc965a4a1844cf9a128f849daa9ae9ff2f4794a64e5b5c69a9eefd2a3d88385969ac2fe8ba15", + "signature": "8b087947b837e2e40aee95aa16dc3520ea116215d66273cec2aed5ef5a7adc2c30ea50e698c8c8eae391d552bdbb3d0958911e9583badc88faf52073ee8a930e" + }, + { + "msg": "928244fb8abfeaa716ae0e70a744caf4c9b769b2dbd2f0da4b25409d6e76fe4e6240aea0f175f6f9e2", + "secret": "0dc810122e52f727b5e51a0490a6ed94d4842a4053b1aff17e9c9c126d63c302684c27ca62583a5825b9c74f9ea4c1c0841264d65ff3b2532d892ac734bf6decf8d8af5aad4bd9890525590f2251123ee7d4637acee995aa08e649826adab7e02cc85a030be85bd99382c8cd43191cd073d586aa1b82530c5d0e2f14c051b128724b66fbbfa76340321a387628209f9c1f74f603b0ccc417073b191d12197585814a51117e57c3119b526c4421f2287f62d239157c1193c9177dfe70a5d4eacb", + "signature": "58916991017f8c73f350a1412161046a069963e419d019857db3410b4f610655db044586d341b22903e11838bba9616c83bb74f9341980c6bf84178a74753b09" + }, + { + "msg": "62f9d109ee0baea4d31235eea68ccd276319fbda7ae94c7ffa1294a48b99f42a414720b146fa79f42ae8", + "secret": "0cdf6066a7274c735785ec3abf083f980f03fef1e76214191da99bfafba436318436a7e8b1952ca8a969e580d3cf6663fa8347b4e2574abd1899b9ac285a85f7894449fbef7dbbbf8e05b926e03ab38e8564626229d379d8386d14f56b92623fdf4c06fef3af9d7be22f5587e9aad6eea2573aaafb33956d76cb5fdc705dcf5964fc896931b85770c8c8676b10f3f79b97635468b5f3056cc84a332ad1a3c221d6796d37f85c3c511a89f7eb99b33903ab268a09ad0d82d2a475a031c63d0d5a", + "signature": "82cadc3b349ed1e95be4fe2f8f7516b775a125c95102015d0030b94dff5cd93963ef4fbb38ba415aa9377a59471d95bc4b3c9986e80c92d77d1b97458c29e40e" + }, + { + "msg": "e4894c7009d533615e465b1a47ba59a0bf4c82cbc48843b6166740046dca27b9b983af09e5807fc6acfd6c", + "secret": "f3ac5bc39c6fcb6a06e468ce95c01370bf89f53ec3b3d754dbf9445d557176b0dc4752a458b2570f9c515b4e7fc985d8a105b00bd28bfe1f438ae0832e732baff94631846f4b745ef42a5fad2bab881568a4a29cf6632f2a17dfd791668ca71e666f5a1cb51f1213399c8197fdb631d2f977166e85acc2b1e4afe9190c41d9db5fd83e29d9eec336abc371ef87511a52801eb00587e4b4891049b2bd7cf3b160dc7d25b9838a8863ea3a0eebec7397514de93907854799bfeb508ccabe6c6419", + "signature": "533151b35349871991bc92ecda7ed46c9e938a36b131787168e7ba573edf8d25fb1574fdfb3607ed8c2fabf9d4427e5967ec2c14b3211f66e868b7aa5447960c" + }, + { + "msg": "1cb95296ec68adc4a8f1e0a1e790750eff5caa1878b1c3b03230f7998605d350bc7ebeae8f0fdfe083a7a75e", + "secret": "5364f25c6067d44dbfc527795ee2323dc03581b2f612b22454236862833fa33dccea9188bb31deb0cbdf7e550ada6d813cf6baf88bd5b5710351760104d5066814e30c15a78c8beb5d74c2295ff1017f630e9c04fe032b66388e0881793c2ef9693b9bf9ff2e30ed0dcc71e9998b08600c6c6e240c45c0ca14f7fe0545ce168cc431409bc1114715bc12629add84b2669da3aa03fd547c0f4755427d3e062a072006a6ae6d45268dc353a844c89215287dd665215c66c1bf5792d353f40c6b64", + "signature": "bc729413fa8be229dc3b0a82c4311f0a65c462cd3cb40b66b8e376f01a84dea193dbccffaf102a53aedebbee0505012e22cd0b2ad4df7c868a84b2a8f2943d0c" + }, + { + "msg": "486de8ffcb1496d7a0848535b66733ca6fec32dc318a3bed742877bb67cf482fe7d8c3a798deef7b1a856337bd", + "secret": "ac55d350d614cbe05f30fafd3f67e48b04ddb5361de4aac721488ae76e186c2af028c4b4ce6e997dec9dbffb66e289739a0a4e05db26519fa13f20b044615d74e7f665a815aebb81cd78f2dcee48cef23c5cf264dc41df8edab9e80f5c52261c1e3741ad8cdfd3256c5d4678aa853b936585e186b8dbc1f43dca9bdfff9811fd49589a1489c01269f6cedc55fdfb2bc192d2c7165703a8152d8e81fc5a4cb12a01edf66660d93ef9a904471c6e1af19e04958aaada6cf299a32fe98fe7b17071", + "signature": "8360ec7dba4771353ccf56fb2fd07680ef8b26f4ab3cf06c8d13d6fb1b46edde3a290832697f4b639660e3723ee4cbe0c7ca89673f7d905259cc2d406d6ad003" + }, + { + "msg": "387aa45863af7e6cb00ac445c16458401456e722744dc6b8f28c9eac0d372b5421daf52a61b3594cc3f212ca606b", + "secret": "4b8e9316c2a489526aef496b5e65de5f98f9117f7f7fef0fa0e125ea8ee0832c763da8108a928aea39ce3d9dd08ef6cfad823147eac1c72b95d5845e028569a7847af74bb9e239b0d941591a0ca814d75a7ad742d9804b93285581fa141b8db849ea11fad7e531599c6c34c3688608260b198cde0084037e7f625f6e349c527763b55b1ce3e7556cf1b0ff150c96b02a701eee21eba18745a26d9ba3d24a3d465e3468646f7b183c8df2dc62db2a3539152a2df24cc1a877f4ab7fd3e5b2fb2f", + "signature": "0eda03cc37e1da479d0e39b42683f455d7dea89113b1547da682121640c217a25e424a67a84d6afcef5c55c820ab3cc436e68ff447e619bb91c3aca3781bf603" + }, + { + "msg": "1098ec2a9b4c53757de09788b588aa9e4cc8b0c0bc201a287e98c933b1c1814eb75ceff13177f169685bf527863691", + "secret": "3ea317d8215d8aac9d60b0d5c4674ac3546c0e6a697d1e3ca417d04599a89aa892d379b6cdcef1c57a99991f62854f3b94a9f7837b9be3f9b9c10731804a0594a7ac3f89296c85b9293b16965574dadef97d481763d306090bfa4febb828e0c0783289468455124e813af3875e347d3252dfd884393665bf8977c777498b668d9d0c77190fe85a587155bb23bc479e33cae02c37088e9ac17dff8fb5432175ccb6262f9428cfb6cf2041a763d133b254f33fa3805a493cc8a9e3a09f950cd989", + "signature": "0da64030dce5a62c600ee0f6cf1c351d1d89bb25647c70d547897b7187718787c3816ff40c02eea713924fb30a741bb1370ee35d802b55819ad4c645f7017704" + }, + { + "msg": "c98fadb1d5ca93cf2c8e42af954cb059fe3799f77d850d7cd631ba9a367e9dbef0482fa6e0c21e3ff439d1a5ad465db6", + "secret": "b4a75ff8eb908d096f7383766f0f2818d2235108c24e736ee450877919a3686189b7b0269201430b2bd50334d6a8784f7d346aeb017e695c0877a5c76f16e4652fb8383bc5be757834abf7d2d394ec643babdbf57603e252f51d2c78af32dd0077a9f736d927f5df646eade8558308bae859f86979d53298c32b398b18eefacc7b2e725846f827dfdab4071cf79c1474b3a9e8010498cbff891c78fd27d2df4ad28c0984ba6a27c2d6187d314c1aec13773b5e91d3d78d379cbb7ea95f54636b", + "signature": "334220c39b4f834d206df406d2c665b15040f4e456029a82c6532491b714aed8aa833bf6ce7dd54214d24683461e363d743a9f52bee1b9dc266649455efbec06" + }, + { + "msg": "95510f10e9a15c261468bd3a4bd30d738844eeeafc10eb4b3f41255be4f666c37e43d0b6f1b8ed6ef2a8e124963ca06b63", + "secret": "17cee45611d1f0b86d0e758de69a256bb2bd84e7fca241006123b84a086bf18c1ebc58fc075b36e1b630c048d0a47b7ec4b75ed3dc4e2bca6cc7b8857cb183666a9eafb209faabb631221903f46c1d1ffd27e664474c1feb5f68e470f07e2eba8ce547a80843a6deed5db0ad97009c37684fa4e7e2a2646e67c156e5462f3a4d5e0391cdc02f4df15e53838b1ea39dea013ac01832ed5b082c414b3cf628be18be1d2360707ce78dc9611a12c4f5d7d58720ef00e5edbf83f6140fea47decb45", + "signature": "e705118a48a41e10271067295c9194a25a0dc0aea85813144ed4ae5ae42ad378ece5f2d317d1aacf4c776b118b54b8e3ac358b3371baa595e5227fc7f9987f05" + } +] \ No newline at end of file diff --git a/rust/tw_keypair/tests/ed25519_extended_cardano_tests.rs b/rust/tw_keypair/tests/ed25519_extended_cardano_tests.rs new file mode 100644 index 00000000000..818b5ef208c --- /dev/null +++ b/rust/tw_keypair/tests/ed25519_extended_cardano_tests.rs @@ -0,0 +1,62 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde::Deserialize; +use tw_encoding::hex; +use tw_hash::H512; +use tw_keypair::ed25519::cardano::ExtendedKeyPair; +use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait, VerifyingKeyTrait}; +use tw_misc::traits::{ToBytesVec, ToBytesZeroizing}; + +/// The tests were generated in C++ using the `trezor-crypto` library. +const ED25519_EXTENDED_CARDANO_SIGN: &str = include_str!("ed25519_extended_cardano_sign.json"); +const ED25519_EXTENDED_CARDANO_PRIV_TO_PUB: &str = + include_str!("ed25519_extended_cardano_priv_to_pub.json"); + +#[derive(Deserialize)] +struct Ed255191ExtendedCardanoSignTest { + secret: String, + msg: String, + signature: H512, +} + +#[derive(Deserialize)] +struct Ed255191ExtendedCardanoPrivToPub { + secret: String, + public: String, +} + +#[test] +fn test_ed25519_extended_cardano_sign() { + let tests: Vec = + serde_json::from_str(ED25519_EXTENDED_CARDANO_SIGN).unwrap(); + for test in tests.into_iter() { + let msg = hex::decode(&test.msg).unwrap(); + let secret = hex::decode(&test.secret).unwrap(); + + let keypair = ExtendedKeyPair::try_from(secret.as_slice()).unwrap(); + let actual = keypair.sign(msg.clone()).unwrap(); + assert_eq!(actual.to_bytes(), test.signature); + + assert!(keypair.verify(actual, msg)); + } +} + +#[test] +fn test_ed25519_extended_cardano_priv_to_pub() { + let tests: Vec = + serde_json::from_str(ED25519_EXTENDED_CARDANO_PRIV_TO_PUB).unwrap(); + + for test in tests.into_iter() { + let secret = hex::decode(&test.secret).unwrap(); + + let keypair = ExtendedKeyPair::try_from(secret.as_slice()).unwrap(); + assert_eq!(keypair.private().to_zeroizing_vec().as_slice(), secret); + + let public = keypair.public(); + assert_eq!(hex::encode(public.to_vec(), false), test.public); + } +} diff --git a/rust/tw_keypair/tests/ed25519_priv_to_pub.json b/rust/tw_keypair/tests/ed25519_priv_to_pub.json new file mode 100644 index 00000000000..f0cdcdfff09 --- /dev/null +++ b/rust/tw_keypair/tests/ed25519_priv_to_pub.json @@ -0,0 +1,2002 @@ +[ + { + "public": "84f3555539fd7a0287c45f6f6273249a736b268955874ccdf6b4bcf52e2a20de", + "secret": "f400c297a83a759bfc7ff89a079c04ab755dc90a51e32db794386ff7e14f3495" + }, + { + "public": "5bf8e02201c8c08eea75616b1a9b26c0c254510aeff0f54385e5924ee6777bbc", + "secret": "aec5de8c25da6f177d4c8077912e06c793db1e23d62f37db39ca3738ebc0bb91" + }, + { + "public": "aadc3f989f965876e545bac9db582bbe7eb636e8dced276e6f4866fde438e8d2", + "secret": "2b30069121856a5f510e0bd539f5e459a03ad2885d2e9607a9a38eb12af18f06" + }, + { + "public": "d8121013f8852749a6fcfeef50312ba04e3f071e88b6b4462736c84238e029c1", + "secret": "35a6c3d0381e2e9cb38a7c21b912f4460e8e0144fb93cddecd5e76f9de63482a" + }, + { + "public": "c28fd3c20493169bb8bbb5d46a58e993f62d618b73e526bf9e05a26f30ffd0e4", + "secret": "c5edfc7ce883bd7e47e616d1c8588283023459d3f374c0986f75e768bfb849af" + }, + { + "public": "000d5f5a70a9f4b47d35167036d7e17d2e383287949eab3bce1d29fa107f180f", + "secret": "22eddd4711bbdaa2fd5329bba15d846db3b666b7d9d846277d362eb9e4551b4e" + }, + { + "public": "7fc7d2f0246c036058bee1b6e6f43f47cca9a3eb3197da70f04fe27903644623", + "secret": "4d8c3c1816ec3ea58e7c6b6e10a337a69d75aa5dc48f6b0011d2258bd3c2523b" + }, + { + "public": "5eeab191591df74d2111ad3aa5a79e70a2ea74cfae39bc182b72bad60d26b7d9", + "secret": "418585aece0df8119481adf9920775b1610a0ab52c9fae3f041e12653adb0d90" + }, + { + "public": "74216e14110b646383b6477351645d6f0a97ac13d829dd0f689868669a110686", + "secret": "9d2f692df35683e94dde273fbf56a06ba76ecaf3b10c09fdeb8f9bdb0cc060c6" + }, + { + "public": "7519d8de67fb297d881723958283c690a67af1ac6add4b21503b95b90dae19ec", + "secret": "527f9f6463a3d0b9a6c6b5b48d2909353766dc13423b685fe85c3d54377b734f" + }, + { + "public": "ad1a584b9e7664f05582c130385a97d5ac73388643fa73150f0c99e288c37db7", + "secret": "5cb52219b1edbad73b2df614189267ecc41fa770ccd6bcc57b5b7b7809ef6701" + }, + { + "public": "0b47ea97fccbc101ffab8f2b720dd1df5f8b4b91dbdcdf5ad35c1a7d3ca619df", + "secret": "9777eca69734023424c7ea8b21d9ae4159697fd8b5f40fdf3dc1434f4fbe3830" + }, + { + "public": "d6d3058b0f53e0be3309213cf56522c439a6ba416d45a5f683802fd8d2ac7bd4", + "secret": "31be1707207b0a7c3025790bf950d686173479ed7dad7630c2dc132cfdcfa616" + }, + { + "public": "4caf3948eed55c36f764529567d3cbb9e21d3f2bb29e83694be371044a2857a7", + "secret": "dff344b72d7455d225ddc7433d37ae9ba1b128877c00aad6b68cdbf15bbfc71c" + }, + { + "public": "f7c607ae3f486f506573352c91bb9ec5839245bea2625dbf313f842a95aec569", + "secret": "fbaa0f3546b4871cd03ca1d95a1665b954b668d1854b734272cf4f6166695eaf" + }, + { + "public": "21f01adb3b4b9dd201ff74a255e606bff1899ef51d49ff44bd8437b79b2ec642", + "secret": "951702b80e35c6d37f80da18eb4b643857922f88291adb7161f26789f3eb2229" + }, + { + "public": "90c23fae5aa1e8618d73227031b6e19b053f166c5bc4a2c8321f6f9d464586ce", + "secret": "514cee2dea9c882080ae6e3a21cb0ac5b69b73c45c7eca59ed6cbef9e62facde" + }, + { + "public": "27a4e0c809205a8da2a88493de35fb7d4375fb7a78ce5cba9b80c70705d831b5", + "secret": "862f65e095c7dca826c15e3e5895465b7baf656f004fb707bc4e016246385b4a" + }, + { + "public": "e9bc0d472181d424c13f1edbfd2d6f1f52ad49ddc573627e7acb4c2de6bbdeed", + "secret": "7a0a37c98b89b92a640b85a094f37380e18a8b4d861305e1c25ea3e652254e56" + }, + { + "public": "bdacb49fcd1c3f6fadada5a08d283ad710c72215ab33ce444ec15304634e1e1a", + "secret": "68c3fdc1ed94cd9a00442245893aadd614afb2e8583124034779c86126cfbef5" + }, + { + "public": "efe1d4fef1f66242b0eee77287785bbb7cf3661c3139d65264b8ebc87376c9f5", + "secret": "1d5f19e050e9801d1ee1752deea21cf790c0ed518f5b4dd4eed18090e1faa063" + }, + { + "public": "b2c87b7d663689a9e90d9de4314c17bb824af893167e596494e62953ab4f1ff0", + "secret": "4239ac869f7cd0421d611912d3afbd4a5bb0edd6fd77e0e2eb0e7c83aedbfb39" + }, + { + "public": "4d36143eb4b5f244e099431078ada5256898b12269ebdeecd42f9158f1faa3c6", + "secret": "fe895bff5f431941abf6427d5ae03968c5a6bdfb49d8c802e729e06d5f508b13" + }, + { + "public": "b2b7140c309f2eae5f4462f650ced07c07aa830f1b422c9b9f0fbab8ca445242", + "secret": "d7107c5878b34bbaecf7a1b3059a7da5618c4c26eccb39bab445894b7587ef4c" + }, + { + "public": "ab87d8de2556d13567832f413ba5af5314a8cebfa8973ec5fa14b4170abd6ce9", + "secret": "4b7cfc999c4784fa894e685133f83a2beb48e058bbea3fa23f15b16e98dca2d0" + }, + { + "public": "4b6797afb41a78e00e90a6eca1eb7ad0fcbffa61119150985c62e1fe0f2e0f8a", + "secret": "973021d2dc1c8949a397d05ac99caf8d745c721c054b9d59fb2252101bf6263e" + }, + { + "public": "27acc7bfdd9beb9e37af70121893d9bca4669b79a2eb6d9aeb9cbe30c693019b", + "secret": "a86fe317f2087969cf622b87f3294d7a312340a0682989d4a8e1453eaae11a6b" + }, + { + "public": "a193742ec16ccfcce9da1b2daf0827ea5e413b187944c3a346f426348c46a044", + "secret": "5bfa98563cc3eaf39c40806f271a0e8605683e35a9eb0e9f8f3d9e98657bdba0" + }, + { + "public": "93c9fa3727c88a9fd64961642430c368afa8aa5212dce0e1781c1893ae72412c", + "secret": "a2779bb8a177aeffa87f1e507e0908c99f2f12d8236e3fa08f30f4f52caa5e7b" + }, + { + "public": "0d3707fd6dc6f1e8e187b88a6f3ccfcd7bfc2464688a974f7034e1d24d67752a", + "secret": "896ffcb173d3abc1bae5ebd86a119fb996172cf9a70f46f9f0e0a6462f96147b" + }, + { + "public": "3111251e9167235fc781522c914d79098d6df14112098e60c2660c0d7cc63c39", + "secret": "5f5528c40976dcd9406860812bd58901b219767763d4646e3a0c987d1031f714" + }, + { + "public": "acc678dfa68cb3ee0c726f917b4465fd15000d45c240c410de8be36b0364965c", + "secret": "90768a5a1c5689965dbd38bb3c15f67a2f32b9b206a87a2aeed3323830727807" + }, + { + "public": "f037e2b145ac7dfc26c191a451dcb4955fe59777db404a807ed9196bbb2c5f09", + "secret": "a43824ba5cc60fcec62c72d7dd10bded98e6f8e0cc2f8b75ace7e2f948964851" + }, + { + "public": "860e0b15de1e8aa43e69ad7216e9764f32ef72798ad1b5dc65b8558a28ae336d", + "secret": "1c5e55460c753dbf1a5ddc5991fe6a5afc80141f5a0c4583e6aa764d45ec705a" + }, + { + "public": "f390028b17f26a896fedf1a8bd8d12af67814f979b5d3adf662e666d236c7700", + "secret": "8043d6c98be18e5dfac47df5703b475bdb6ee429f6abd7e5c55e0327576f11e4" + }, + { + "public": "72033bd969c6fd459944930ddb89c940cffa6153cef9ca0b0f05ebcb9f06ce04", + "secret": "55daf915e823ad4e8b564d9d54db970149fcba7acabc4e449bde3da8cdad7ce3" + }, + { + "public": "68dd695c21397c1c76bdb8a1b9469be6310ec0e550cd99884cf48307c59c2098", + "secret": "b0598f4bfdc623290045ba9edbed613b54dff424495e37caacc1c9f513359196" + }, + { + "public": "4d0cca88b31ca19cb380499062993f968ef7ad7c17d7db31204eb407b2f55a3c", + "secret": "eb8a8dd07904496e59ba2106a699808b8256ec15c04977563729911a21d66001" + }, + { + "public": "d1ea442550480cde96282c934f010defc0f5a774b2ca0c4abbde5f24827b5d10", + "secret": "958c2cc0627b8df283e5b75936c717c87f19dfec3bad5d7702ed7413e16266dd" + }, + { + "public": "635222508ac740a545ae3a2e1b531ae834dbc3b9ba2fb69a675e2835fac09e59", + "secret": "cb82a5aa6f02921cfe5997a7d8fbb9ac04d0123026ea02080461656283936fd0" + }, + { + "public": "36ffc1a12dbe8d3b67c1285c33c1a60e0ef4feed15b23d5ece1c23f08997d381", + "secret": "ad3b1870bb1420b869740ccf8b5d872b9da79072c0dbcf637cff2fb12405668c" + }, + { + "public": "a88b824107cbb11922171d1acc9018a432185c4a3e9c629c7ecf7ed0453751e1", + "secret": "fdb260e4086296c76b8dc27db3f844d67eff0e2c482b6fc4988b08db364f2b56" + }, + { + "public": "a0b7d6353da9a7a176e0513fdbe428a7a08965141384f4210119be5e2de35cac", + "secret": "0abcc2ca67f3cf3bba047e5956b54db7c887fa546f039aa37b2b5f677029adfa" + }, + { + "public": "5858d39e19433849c95be07fe9273b683c56198ba6e396f7b549a98ea8aaea4c", + "secret": "29527782410ce88624e2ceffec8dbdcfc135e04b228406a127d94968aa146858" + }, + { + "public": "31ddfcda0ab66ba5d515238e2bd0cea57b73e9d36f9996e5b9671a59e66fb47a", + "secret": "9cde91a16cc82527b108304b9a45563f524d1ea1ff8c6db0849ebacecca9cf20" + }, + { + "public": "64f1704e9a0d3526d1812df2cd90e7e9b54960d7389873eec48884d1a9c4fdbc", + "secret": "beb922414cbd021a4df2e9ae5635f37c96139dd9fdcdbe03042167aac21d6f7e" + }, + { + "public": "992faed57684e050c325598f46bfe8c00521d61c1bfd1fec2a44a706105c16da", + "secret": "e40b9a5c9c6157fd55219a521ba076fdaba65db61bda660c03fc924423a0f94c" + }, + { + "public": "309ee14771171e677d5e240d88bcb9d1c80920a0ad048aee25878f98a5075d05", + "secret": "98dc24e2cde873a8f3cfe3723191deea7f69b06f57879ef196154c7e350625ac" + }, + { + "public": "dd228455a96281ee87e8d37a74afa232ea2652dde5b8edf0e60584b26a792b30", + "secret": "74a1eb437e170c0b5421bc0caa5919a2ffd5b636586f56ce749d17f2aba827dc" + }, + { + "public": "2767c2f30b66791a352e168e85b900fc4322f6ff962c269ea61d38c13a5d2605", + "secret": "aa88a461c6231ec22f5d81e820d5f8966216a30670e54c92cf7701271be488a7" + }, + { + "public": "b6ce0609ac4f3414193a3737621358b005c0271ab1f30a5144cd919f122a238b", + "secret": "d029db3e85761b79d76782dd5a206461b7632bebc4d1bf1807dad95670dc476c" + }, + { + "public": "ee0a91723535b1e665f3622e85481e58fc0f2694944f3af03f9ddf73490cdff1", + "secret": "f806e9b7ed27e629a08ad81857e45edd29437f45e7371b0015165550dbc3445e" + }, + { + "public": "210e1696b11739debe4e0d2a67c70eb9466f423dcb7bdf263a565a3fe5fd7db4", + "secret": "5d9875d708b104b55d4d00bca0c3d62eac4aaf81686c71d4e4e9886f120ea0b1" + }, + { + "public": "205a1476a451baaf356e3d1749b1b965d2bc12039ed9594e1b0b52d75c52d562", + "secret": "b80e4082a35e55474fc925d2bde375c7c02e6c8c5d1e9656520526da75d8cedf" + }, + { + "public": "824a592ec3ad92c21e06745adebe8d9788cf5751c98177b8ef842f9e4d151968", + "secret": "f904158d81fd1c5c14de708b92041ad611ea323e5400d6928234b3a919251121" + }, + { + "public": "d92420a8118b141ff942b04791e6feb156aee7de9afa8b2ba7d55a0d39d31650", + "secret": "835f1e9898f4f6ef8933f91e25d3c58df24f04045d9e40626caed09b813e37c4" + }, + { + "public": "8a18aecd24e50be11d9a71cc0d0ca5a7a0acc15035cf4bb4fb17f4f6a8104761", + "secret": "fd8e3072758d683f4d704892aff1213a70d2f533ba9414e47aa11670fe4aa934" + }, + { + "public": "ad13b24afc50c5de27ddd0ec44a5f05f437da4cc7beff60755b48fe294e0683c", + "secret": "d5ef06ff7c86a0fe26744480474af9ed23a41b946c28e8997649139f959a4822" + }, + { + "public": "ea616e17a8b7305ace349db45f22dbefef27cd9f8d22a715e7ac526230a4021f", + "secret": "84f39e92aeabba8dfebbc75aeb679fe64e054c542728c158306c60b7304e154d" + }, + { + "public": "f65fdeaeace9bde8f7ebc87ee55af205309eb8add9a1ce71ff5f04d6bfda05f0", + "secret": "03d89f00868c68efc8d6366deda83f1b2e88eabba91e693797c3e72a8f88f48c" + }, + { + "public": "51a5b62bf0bfb06dc3d1c80d585f8b7cd5cf08369777bcd3c2d3501de2a3e9c2", + "secret": "a3f7274b4e581f5046c9f7e2d4c6ff6df95b5cb158e874a9b7e27810a8c9f239" + }, + { + "public": "30fb3953c75933098dd457f23e131dcec1790bdcd14d3756c7bbf7010d4398cb", + "secret": "fe2da0666f35787c11f0a13f560828a31813ce0c693ce1f94536556bd24064af" + }, + { + "public": "08c61d07885bb0b40012c1055140a61fcd793c2f28621586e88d77241872c2d0", + "secret": "979c6563227739b56351cc34dd726f6ff5288ea1cbd2c62b970d3408c41c2e64" + }, + { + "public": "8fb74969499cf2b2e45e835cd15f465e347b60bb63b4ea3884353254e8795cac", + "secret": "552c0bb73536e51ba48dd06a70a367b1b4aea90f0ea9a7cecfa8871101d7a048" + }, + { + "public": "7c6d8a910269a1adb45ab69073597c04486adb033c4129e1f65d8ec85dd5f40b", + "secret": "d2f2130652e5aae6140d27f05c19cde4f157ed5740c5d41342846365bb6057cd" + }, + { + "public": "55d54e1e3f2f6f143b68e0342d70812a41c143a1ff221494741b364adecca4cf", + "secret": "acc98122a7bd03146c971a6f42d2637e1df9a2719955917c218cedb8ca156a67" + }, + { + "public": "f2417acc7db550e45419d1e7bcbc64e159fe74a388d0cba0b3b9db7147506f6e", + "secret": "ee683019e064e95379f391aa1a8c3bb03af0238d57f86d1669b8df334e962bdd" + }, + { + "public": "0d006ab03e70a5921f8c62799ed5e690a0034ee6bc07e50df0a135eb846a1d6d", + "secret": "434868143c2fbd71e3e06dc1d71883766442d6382da6a24791fdf329a0ad9b72" + }, + { + "public": "4d03079cc959e97f55c250ca80cef3973c307852bb22a9930668298350cbaccf", + "secret": "5874231e3a6e0a3ca29acfbc4d8b88fad5f15566412e9285a96a47bed9acaa50" + }, + { + "public": "7ed28b2e6897e0246bab7ccf771e1cbc2dd716d94eda2c70c48c537239a0866b", + "secret": "bbbc68b2b2d564c0a713566e331757f5fa3d08563431279892f5ae4ac8f64387" + }, + { + "public": "78e0a77470d5b61aee66eff5b831fd5f6b4eecc4a895928ffcebb1229b5c7dd0", + "secret": "47f979075df1bf0b944e827cb5d9b4718853dfae4eb7a9cd0fa228022f09f8e3" + }, + { + "public": "6c9d6d3442c6a5982ffd9b96b2ec828ee85191c95b7b773e1b67f3e1857e8925", + "secret": "9ea903004bccf379b819177e6ca56779817340f8957ef6f9403300047f2aa822" + }, + { + "public": "b026903d283d251236046643e695b782f3daf9a4d7bbea4b007567a2c2ceea6d", + "secret": "00512e0f6b6f4f8d7674407de53fa6b1e307412c4b36cd2594a2f15583707f3f" + }, + { + "public": "f6584c29a31812d35b180156acb3e35b94721ee9aec71fac524dda3927f90848", + "secret": "113a58cb3ee6e951f65d3cddc4ecf79b83eb7252ea3d270f841d66cccd774f73" + }, + { + "public": "c12c0ff07322ab294966b064361efe71772a3c90153f4a5fbcb0c76276074c94", + "secret": "d82bfd183553b32723ad988b0f00cf258bd78e29aabe38508cf3144c4cada63c" + }, + { + "public": "9535384e1f5ac1e1bb9195ba7b1d42d0a1c65772b87aa7c81f526ab7699336f6", + "secret": "4315f753db1eee9158c4d403a62b3f2de5b56783b88ec99acb2a96e501d5d791" + }, + { + "public": "86ea8574b55ac5e5449feec55bc625ab51b709324c6c1d873818f12026535af3", + "secret": "37d346987531c9580239c039adf1e5e3d41090e9a8eff8c6f2088df23507edba" + }, + { + "public": "5e35cb913be094450566058fe5c5a61b181e9c2a0892e1bd2d831c93ef470b21", + "secret": "73c6689863032902f456ed9854750085798b4ee963dacb34219bd6b8307ebe84" + }, + { + "public": "48e3d1fa5ddd4ef7df94475e0d2696fbeb5018fc5b3527f8f0347c3159a0c962", + "secret": "894825346dd6202a48475157967c5406d1bf0dc3400e1caee006e01a76c04566" + }, + { + "public": "37c316dc53458dab34d6a54b21b7a9c82f783a614e2905b8d5cda5a426f4afcc", + "secret": "d61efde38f048ad5d634daec05b4a6c58988a3c32c4e380d096c181b0229e818" + }, + { + "public": "d689984485436dcb7a159286a7d934beb706faeeb088e2e016af5c70cecc0a51", + "secret": "9ee3258f7525a6f812e754208571cb3adcd096679f8d8ab1230de74ac7b5b7c3" + }, + { + "public": "1a74de65f689599432202dd28d7a1bccc0131629f61ffba16ac3c915beb3a201", + "secret": "15c58cffd7409065d7bfc9bcc42eda2dcf184e64ad799df3da81260961388558" + }, + { + "public": "077660db09fe8f5633cd5259b2f0501750bf4afbe6fa690216da69c20b6c3b71", + "secret": "45f0e40b32cec47dc43ee1c62a5f490a1e2b73ee580ab37c6c769cd8282ac707" + }, + { + "public": "afddd125f720d9eea185a52a0d8f182b571d2a5930763079c7501fa6cfe79b8c", + "secret": "1d79ecd13af75bdf98e86bdf3ae8b40d93efe3e2921b440981b837d85530ae61" + }, + { + "public": "b06975ecf22d4819d445f62793cefd36f794f5499c47dfc11d9d6a3718ed98f7", + "secret": "42ee2757b2f9105e7b7bdcb0593be3cea4fa6cb2a91952663d732f17e5e60c40" + }, + { + "public": "ba30e3fc9b02f222413e6d3d86f6e4d2c2aeb5595ad1ce9820453d955a390bee", + "secret": "4ae24666ee1c7eda9c262fc554bece6956eb733a3024a2992247703fe21ccbf9" + }, + { + "public": "22c4d789d8999b6a0b25b39d0bee3d201ed50c52483f0683517640d7205a2f73", + "secret": "822f1047ff121976e5fea52605a0f9a94294106090b7b81319a93e546fc27edf" + }, + { + "public": "3a77674342e4b72694fb35c1467ace0ccc83220ca49971fe2f56b197faa0ecb9", + "secret": "2038010528766ffa20c27d4ceac7d74b5e496670a14049ace05f4e831f7dcd7c" + }, + { + "public": "89e74aa5387de4ab4639885233fc1c5eba82c986f41b0249680a1b31755fed08", + "secret": "7c222c2d5e36cce49e21b52ddd36ff73889318846f2f69096c9be2ce5680952b" + }, + { + "public": "7c0c6b57a30316f325cf0846593923961dbc6e6336ae2e9003dd2260a8ccb834", + "secret": "cd5a74e9e861f62e05f447bd8342efdda403ce0c5047101c2ba89f70c21808c7" + }, + { + "public": "5f8734cac055b3174b720f5fb0488e95bb8d30c914c53d2e9c7cbc359e78cc9d", + "secret": "46304ad4c56bc7fc7a4a55cbcd7677fcd5dfd968a61ef2d1f1f7b13e517a1feb" + }, + { + "public": "5d6c0447eb19b81e9fb8293911ae6f910a75939e70e2b800b51233930cdeaa99", + "secret": "ff314af9ebb0fa59c0f8cb0670da269247ae793c79402b1f1a6f4b722c34155e" + }, + { + "public": "2945b1e68ec9a9a129e7fa40a70ab3168cbc74d51d533d41bf09e889c87550cb", + "secret": "6327be47b7f2ab7f3e25d84b9b8cb0d173509d355760fd52c0c5de14d7027d6a" + }, + { + "public": "3eb1832356a0eec3cf4da7a94f5a132789045edbb119d674434985c9f6d40462", + "secret": "32cb8055716b97082dc0208c7596159b8394bcf32659d61d742bfb0ffb4865d6" + }, + { + "public": "6da67c23d20e87434830fdad59d9bab8b296684eaf69d82f68a3efe170d4f714", + "secret": "78994a0fae2bb2c2ad8ab795b34fe139c1aa00532593d07ca25e29002d51e868" + }, + { + "public": "8a6bd5ccd2d01920b72f83cab25e9c6dfc591aa607b3672c93faf79b0c176af6", + "secret": "8fc6c4f0d7772a1361dbc2fb02be22e8507311b63431ec2799106043a81b01a2" + }, + { + "public": "8ceabfe70b487633bdcd4ab3cde7129bc193bc32f699911f10f1ef9ed393bc86", + "secret": "186c826e2581f0d5932094f1211bcbe43532976ade9200e8f4d5694582f0a054" + }, + { + "public": "1a8f79d3e38c3c91a67ca4959f84b6f1416cb0667bb7128f81c9191baa9de0bc", + "secret": "4e80a45f57b66608951dcb9a18d0c31ba8ba8c691904b58b26b040646e7eb8ce" + }, + { + "public": "2d9aa629d138396fdd85cdfbf54fa9a03d1d4d9c7362b8d5dc8f613acd6a1e08", + "secret": "eb51c594fc1e3e2108ae0e1c35d7f2515f2f77377e3f05a9bd19c7b73ac1076f" + }, + { + "public": "f5a2699aced98ca20675a386a3b23c51baea941d6e73588e5e9cd8be55252101", + "secret": "6ea021cdb91a051562d909de3a626878363758058fcf4b088a857a6b54bc08c2" + }, + { + "public": "983f5486af410b230abe73d3ff30bb6a61aa4a9c7fd0f5ecd88ec731734adfd8", + "secret": "67d2b516bbb0b74530d1fc42b7f647c5c65d08617274c0172352aa7570cc6598" + }, + { + "public": "a763d6977e93aa1fdd522cf529153f98be4e92a5627f7ddf93a777889cda9764", + "secret": "95008e91e514ec3270a915d7e4193569a0f60fd7c879781b9a3de8fb70a63840" + }, + { + "public": "67a1527c699de15c96a6d3ebda2f26df275d70da91167b341a3b90672703ad65", + "secret": "4d81db4fe699e8af2424b8de0aa3978d3a1e79852d26127451ce528d8eeb2b34" + }, + { + "public": "c3339e06699f44b8f7557da81b119720da94d640a05223e44bbeec639de357f8", + "secret": "852090448cc70d5f85b20ad418b4ad3fa9b09be3b53f115bc3827035e6888369" + }, + { + "public": "41086c085f17961e30ca951c5f0b84aa12320ce216919e7a7c8e0f03e4251d84", + "secret": "e2bdb1cf7e6c70cdeb3cca86db1c1469ab9afc9f325d2db8264d66bec54190e7" + }, + { + "public": "19b1627dfd4e1ecf7692cc39899c32bf1f4ec245194053b30326eb1f94bbfa8c", + "secret": "b742739b3f71e1a2fab131ab45f60d66c761b10219c8ed1b390fab37b2482b59" + }, + { + "public": "53abfcc91bbc7814bcc959cb0e6d756e2bfa2161bac411d34b9577bb471d1756", + "secret": "5e0ce24dd053515dbeaa1878b44f42236dd473b9eb9e63d86dcb2d9460c45341" + }, + { + "public": "ee95fbab3ec78caec349e5e643b7f2ed332af01626bdbe47f59ccb67ff315593", + "secret": "adeb85ac33201e21a3984656b7be9e01bd23ab9abb4acbcd6e7638b11202b689" + }, + { + "public": "57850e5c08687662693942c6d421182e769f453216f6a21a3deb46d72aee0530", + "secret": "625a4f14479b8be40d9ab08edad23c18b55d23c1d0ae4fc93751a20c110e8c47" + }, + { + "public": "94c7e9bcad48d4d7c9af0658011672c187e3bffabc0c1e8bb295900cebf3aa42", + "secret": "4001db50c3a9affee761f2bf2a5915e80797ea80da2f6de7ad9f40d650bc6542" + }, + { + "public": "7bf756c31296975924daba47fb5c112452f6e1ce502bca9f4e2438a0189beee2", + "secret": "637bc973557303ee32e585fd420828f4850307c7db0a608aa76db2b0e44d95ad" + }, + { + "public": "e6c82e8618f5cbb96a28723d4fb93cebc98d31166bd099b0c81d36bd52709df0", + "secret": "e6c3824eb48e58544f93e09a7eb21f2185680b6067d336b9f720f07402a0f07b" + }, + { + "public": "b52d61b8c8bf194a583d2d5483c36a3829a7c415190fe8a17719db1dba895634", + "secret": "dd001fcbc596f38af088cc7ed6859b36f1b423e1c9b8803e5a47b3ef6f5d7c94" + }, + { + "public": "96f33b90b1ad6dd7e67e26fd8830c48e4cf055b1cf73c02d90114256d2a76650", + "secret": "e3b75abd96734abb36e8631e0004369c05a2ef21e5e2b3064466e1be376c22c5" + }, + { + "public": "87da366d523c91a072c0ad288fab3981499c0c54cf5ce5304e76f30393d6f8ef", + "secret": "6f01fe42df2001e5478d542c8fb58b1d83a91a716faa9c5cf709d3796f3fb0fc" + }, + { + "public": "7bd1b46df87e8dd298cf983bfc0d0a59ea69883cbb319f6f86342349cc6ce83c", + "secret": "40aa01a315ca34decfd4ee6aea159e28ee2f9df33c0109a8f10b93762e9e38fa" + }, + { + "public": "43d704b0f1e3c8c773d36b589366e7e535fbd3767431e8cfe9ae90177e857506", + "secret": "75369e2d4cebe8baeeef67f2c45945f70e9d6b6b2789e9ff68f18c41c0c39aec" + }, + { + "public": "057ae7396ad478e798807498a283c38f7dbecf319fdef2fcb3e3c439ee2f54ea", + "secret": "1ea015f99d700ffa4322def1cf7528ec5395eb7c27e3bb000e34a2f4d248d14d" + }, + { + "public": "591a0960727c16ae7c5a5fca343cb795555c2c44463c59f2dc05830478a6c51e", + "secret": "51bc74a870c2e9da8461fd4dd2d89222e5f97f29bcb414b4e0e9fc705fb5a48c" + }, + { + "public": "9b5417d424302f973dcda1db9e35012c688f93af4440a0c6260b7f8894566de9", + "secret": "54b30150cc1d8a7a02e443bc0b5df80a8defad0d8d35d04d77d3bc3f948469f3" + }, + { + "public": "c86278b1e4e768e1ba0accadee1932e55cf49fd7efdaed521ce8cbfe1a341a10", + "secret": "d17e4fd6be883dcb59a5de8681758459ac38a0aa1a14a4b2a1b21fd417512785" + }, + { + "public": "0abf4a96c3ec75bd72734045f6b8c5431231a271166a09d7797cfede60993f39", + "secret": "f230791d9fa71b25a63faa0b184921187196ab6766b08e86e7df67bdd5f06349" + }, + { + "public": "048250b263aff93aea4c3808c140f443cff094bb7fc8f9684b2b48951ebb8cb5", + "secret": "12cfe01fea4c12d67bf265f5230281751a731811cfe13d4cc0988221aa03227a" + }, + { + "public": "ddc95ffba741677cc3d4f7f8d8fac35985356a90591695ae198b5fbbb0b4f727", + "secret": "28685af2dc1ffcdd2fc79e38660d162d5a5e5cb4d80cc2cb34952ce97e190534" + }, + { + "public": "0fce9d961492e624ca2d4cc2d576a17325cae409ae21970e3a7b74dcdec94614", + "secret": "e2a3da34ddd8ce1a6bf86263cab4717d190dde5a4f19d29790d6a1e075fb6cdb" + }, + { + "public": "ef19db113a4277575dfc95bb3ed188e6acefe3d4c88fa7e5236313bbe937152e", + "secret": "c7e6325e02350efb0c9d7eaefcc452020574d955844d35f4d78b37891942010a" + }, + { + "public": "5b802bd732bf0ed0d98cfb32ec40ca4631997a66e75b2e160f2207f9748e12a6", + "secret": "8770b482a5bb6d2a031efc9de6d262f44cb1029a3307dfa1376f5408ba06f3a6" + }, + { + "public": "120e33eb146b2f923db3f189b762ef0f4d9140d488e963347147c5578d31732d", + "secret": "75986569e290131acbf0fddc8a3b5f43ff641aff4810e91191612e189f0bb974" + }, + { + "public": "acad50c1996590abb86142326fafc550cd9acd9c4f4770e44aebf5bff37002ef", + "secret": "04c96b627e91e5aa137f22d8ab05f05bd5a4a366a00bba579d34f71436fa8c5d" + }, + { + "public": "aa3869cfb327cdc86d500c54e9fa8a6291cf3524cb17f5db75336120811112c4", + "secret": "284d5f59be2810274296b1b7f7a04bcb745b23993617fd923537c7753ce3bef7" + }, + { + "public": "400fc613204daf2ab083c0f16e6b24f4f862be3d4f5912d2662a7d5cbd2d6a61", + "secret": "21c72972cd929c4cf8e5f24c0cf043b2903646eec04ef986d6a73dbf1a152ea0" + }, + { + "public": "a2cbad8ed72f4f450e738ac4893cc40980f1a076a9b63bea78d10202b2743214", + "secret": "c2119be02acfc779cdf76ad5a820e433878c5bdbf5573233cd2689c18546cca8" + }, + { + "public": "00e31d97420b0da03e7d510dc20c7fa539cb8ad31f81f01eb8da2f06e8662bda", + "secret": "7b060d03dd03ab52e268dab04ce9dd3e5a31351d11d8e841e3beee562a4c15ae" + }, + { + "public": "2f2c64df73fb8c21e21f6866fa46283d6d48e1d6284b20f8c8b9434e7d05f50c", + "secret": "ed230e2ced38caaac4fae98075d35cb0bb8959daa2c371ebc2f42342e439cad0" + }, + { + "public": "824bdcb6f88c35a124a7074ce5790961c47245a78e07342450a8588378a4f5eb", + "secret": "0e78a90e210e18870c21f057d253178375a182a40d2452934ee8cfdcfbc9afb6" + }, + { + "public": "d793349ef0b5bc0404658706c014fffe49256dd83bd297ffda10e7b0dc650050", + "secret": "33ca66d1db746f709fd7a5538f1045d0633ea41e8a007fe31cb6d4bb2a595f0f" + }, + { + "public": "d514f360d2060a08fcc5b9f185ef77df2eb0e677cd1d3a509ac811f286b11870", + "secret": "a99d1cab98448ed7ff1754c4eee2272de5585446746841620ac6b9870929285d" + }, + { + "public": "78d778406068d98e8c2e726151e0d834cdace5e10dd1bbd7e37690a29fd51041", + "secret": "a4309c020262f11016cea49a6abb80e1f3c98580862676698a4366e7ffc14e4a" + }, + { + "public": "6ba2ae3a8f27f1f7d2ad927825f1db4d71846534d679d4035ee1cfc81b21e61b", + "secret": "9cae8aeef9a0ef5775e4682c0a544545f0ee7835d71addb5394a43097feb4627" + }, + { + "public": "ad6f9324f571dd3f3ee5565b48fd8a1118e1ca437881794f8606974e5457d810", + "secret": "7579fd8c239d03e7cf2d26fc032058c11f4a2f599e5a1f134a2fb5fa6af5c0e3" + }, + { + "public": "85f88b42183e14d07a049c7d172258741a96376e648b2b44f5721348ba798d7e", + "secret": "5c1a94f456e5213c34edf44278992100d82568ff875f5a2f9ed94abce96fde0c" + }, + { + "public": "0e5e25e9a46f00a3bf91e4a2162bf2c36e83a621d83297fc01ec1c419017d2ba", + "secret": "eaa714fcc14e804968e748c98883262d0d899032dd9579be6cb1a3f1f1324eca" + }, + { + "public": "36bf532425a003d1720b39353094c9d7bd1c6592a7a64d4774fb3793fedb36d2", + "secret": "a976add6afb11fb96788fd8431d1d973bb8a02dc211942b0192f360407d7c60a" + }, + { + "public": "634d9f2fe5a7086f6b498e1a5e0569a9dd05e93ea6923555960568f51599bdcf", + "secret": "0249977a3d9e61e27990dc69d2042e08dc1684d642abbe0f2d7a5e441d127d2c" + }, + { + "public": "a6f61540bb2516934c275e8c9c35fba7cf2db95aeb98052a647def13dfeb1789", + "secret": "0b46f03c5230ba7001b10da617ef00f52d4eaa30b207fb9d3cd9085587e29cb4" + }, + { + "public": "9fc37e05528017d01838a199f51789dace693de7696eab23b288bd64dba15b60", + "secret": "f25953d06d2a442a5b9bbbd33f58ef76bdec8af43f9d17f33589eb30ff99c062" + }, + { + "public": "25cf2ba3a63568b67b008ca939768f77c4e6620a3dc9030ce76f5057f77084ae", + "secret": "f32f49b1ab1f6770d393e035dfa4625400b9ca17d25d98c64607927ef70840e1" + }, + { + "public": "3a887e859deaaaacfc9ff7034ac8c235dd76dabc12d08e37a281a4d115f03f5c", + "secret": "469b215a43147de7b3a7715d78a3a3c1a2c407b7c3b822130104cfdae02dc39e" + }, + { + "public": "1a3b2a6b43566639914a38d7bc95a557e999acb2d03202f2c3d4f7fd8fa93a23", + "secret": "e2468f03021978cb814568b515f294558bf124e261168c32a5925baf214939eb" + }, + { + "public": "558384341aee91cf9b3ef0488928f747dcb167e63222700f0fccca4fc2e7eb8c", + "secret": "23370a4877fb453aa48ab833653e8bf27e6a974f67267b71d752294ab459c762" + }, + { + "public": "c2fcb49b7730b577b08238a42407327436bb5ff1812602c6439f187708b46d1f", + "secret": "b5f5065f3e6e657d8e7ae8f5a7cbbb7129552741d0c435d141d68c881da3af68" + }, + { + "public": "ae7a555cf333e642d303fbdbf03815123ecd6705d97811d3e4e674efc8343846", + "secret": "d1d62631d5728bc22c631cbce44b47a29df07757e59760a13723bb940b088422" + }, + { + "public": "4def94818656a8c080dabd06a72a3741ab4a4e716d1ed5a6625526ad5a266f53", + "secret": "e4452c65ac06aa06ceea30eff7a6db8276e809dfc0d3c5453cfab9dc301396d9" + }, + { + "public": "97a805d3b12649acbf9c1db69ed91e7f20b669cb917200826e91d1ee3d2bf47f", + "secret": "eda240f74f23d56ec1cb3e0158ec94f19eeb86e6050f751b0c4a32c2ad4f8234" + }, + { + "public": "447d1d75b850c2c7f02058ef38c868ffe494df3f2fb00fd67eb7f38ba7d776f5", + "secret": "e183b14c9b6c3250c7ae892c21513bde73819443deff2c72f81cbd71a5c8a4f4" + }, + { + "public": "f01ff315c36ba40d32616da88659aca0c43c52aa69cb9b4a1cce2894f781db21", + "secret": "8be6cfa6ebf4f73b4148ca36bfe904e930001ff7e2e06b5ba9ed71733aca7467" + }, + { + "public": "2b211f5ef3526b0395a793a7f7b7917f7fb20958a95e54d0f53e197d882528c9", + "secret": "2e9fdfdfa885c5acd2411e0f0f86ff70e693d41af60b85aa28f0b73a665334c1" + }, + { + "public": "132230301b41467209c3d9272e5b2019c65d6cd392886f01ed20cce131d283cd", + "secret": "c66cc2f557cc33e185b154496ac2bc2bff696fbf317b6e00254fe23fae3cd297" + }, + { + "public": "8213f91ef030df32815deb30ddb9c01beaf8909176754ba77da2a5d6bce1d5fc", + "secret": "74e64481d61a7e59cf6e82b558abe69e83494bd39b9f86410f4cd2540eb5efe9" + }, + { + "public": "d14214184e172154353d2d93fa3d6b226afff9c1912b40b72c7deedfe3c449ac", + "secret": "2344182b9f37c2c42342984ac5b8f8881debf2e19eb0ee7dcfa9986c8f814d0c" + }, + { + "public": "7dffec32d1e4c56caa3c300498a5e934b8a210ac781b3734eb7caf47bfef4240", + "secret": "792547f88f4cb70eace09bb5c7b2b5bf49bccc8ad6f6755772032c886d294a35" + }, + { + "public": "9fe266d2c934871500bb08dd3eea440c1c803d2759211993bf109ba2462b1189", + "secret": "2671bbd49bebdfa23c3a9d8ecacafb42988dec55a03d86a88f25107c86f3f651" + }, + { + "public": "eb2fe1f4f76a6ed5725597342a7572c84e60587ce3f072ac403c149945fed10f", + "secret": "d3fbde35d3a6be5e5788a4f014f4d0cc2013a1105cb54cdea7a887a92bcc6e03" + }, + { + "public": "bee7e5b9c22a51dcb65ac7ef717670152c38ed4b34a3ca47cfdcbc0f4679ede7", + "secret": "7aaffa3eb13da5baa30a4fe7ac3c1bec161393e50ae146ac7851af3628459109" + }, + { + "public": "dacbed0fbd4f5bffffa6be6b650991a0351791034f3faf586eefcb3bc921cd7d", + "secret": "9e66966a404afde2f4c20747928f55d2831acd9d72019904afa9fbf9c5342068" + }, + { + "public": "28d7821b3a6727d7449a80f471a4977c148e57e50a378bda32d45c72d7752c9c", + "secret": "538e34a667b7bfd00247d80cdad4a7de6472cacc890005c5503f940f185ed774" + }, + { + "public": "373c2ac636467bc378a6346a1509459f06c0408bcc6129cdc0c6d0f397d9e1f1", + "secret": "d372c764f5319a2744da40dcfcc30b9a1dfa6f834832c4dcd6c4e166303bd977" + }, + { + "public": "ad156eef21e06d1b281ec2f9fcdd393e0abf58573691f4993dc0166a9f9ac46c", + "secret": "124f23e95836085c389002af07bc5edc76f229a3cccd13cb2d5209c95f690de0" + }, + { + "public": "b6370d0f1e56be7dd38e9eb587068e1d34a2cf8dbc138b53864604e8e5094c34", + "secret": "56fde18c6bfdf0a1b097e93d786257130dedd3a82f203fb77218b1256f482bfa" + }, + { + "public": "3b9bba6e86205e128879ec09973af4494783597ad2941d838b66822879111d13", + "secret": "e77404b19332e7821f353497b2cc7af0e2130c674d4b82d74bfcfa408adfa9d2" + }, + { + "public": "e7a7651f5cf13c46abd9775c3000dc51a2f39760ebc01075b06ddb4b5d83df59", + "secret": "5002745c441abc76d31f3fc90ebcde150986246ce6faa51af49d49331756df3d" + }, + { + "public": "18ebffac8e39ca00ead3eb6c91769f56ed473c3c3f64685042c11a70538b3ea3", + "secret": "3a59bc523bdc002ce8b263bb7132bf0902f7c1b69e2101adbf198226a59bb180" + }, + { + "public": "31d6df214c6244d944534b751c973555a63bceecb832a21eaf91f607d1322e27", + "secret": "60661d2351f05e9fd6b827df3034b8844a9b123d0a949b21bcb766bdae742141" + }, + { + "public": "d85eaf57d00db006e571c1a3f24c53a3c8c4efc8698eda48f80a937358e519cf", + "secret": "65462b84a66b9899a04e2db4753acf7b84d35957336f43e72fc4a389a9a19ac2" + }, + { + "public": "6138af0e8ec5042e5fca8ba8107224d6e8fc490285c779881c15440b813f8a0f", + "secret": "ae215bc331aaf9a9bf3079ca74d234079d45fc194b6e6fd5eb2ef0a5555d1f45" + }, + { + "public": "2697cc15cb923cede7bdc396469cba1726bce780d9957dacde152a419eed6054", + "secret": "67f90959b2a6336c39573a170878e9a8545aa968bb2b3daf95e250243742494f" + }, + { + "public": "ee52ac2741a8fd71dd0a281e4a59ab15bedfe2fd061c2dab268fd3a14f261780", + "secret": "3341891b2725236c33054dcad9060b5acdd4a3beb72f1f688b2eff2e5858a955" + }, + { + "public": "38ccf066d0516c99a0f9c516b25873a3c45ae5a28c0b4ef5b0615ceeb2c689f6", + "secret": "f06d6baacff05f0f5bde61f9b7d4f1656f1ebf0d4f1e84e13e1b3cbcd12cf010" + }, + { + "public": "d3c8e270a639970842c0014c371b44b6d09c723a64ae42fd6db66e9856fe2d6c", + "secret": "2b2d0a4764ef784f8a8040aea9bee908a54d7d325d496a887b7bb384bfd52edc" + }, + { + "public": "0d4adae5ab23aea35c43ade2da1de0a5ec468c437608c432616748f7615a7585", + "secret": "c2a9d1aeb664dd70de3018932fe3bc94f67c27e6515e092fda191eff7a65898e" + }, + { + "public": "9f60716a627a065ca68e9b262b8fdd0dd1dbae0e360e254514631b680c1060d6", + "secret": "dfedad30b5402125e14d50d1868b22212c93a0330e2e25647a10603acfe87ed4" + }, + { + "public": "ed0cb6fc572386232c10dad68a480811e78b73b6f7c489a8a88e574f06790f71", + "secret": "748694b9b3607680e7e1fa09a2d17a3a8c21911a9c6e5c734bb54d835d5f6c56" + }, + { + "public": "f173b30fa8784e006925b0a3a58efaecc08fe5bcb6c1f009f14986156d766aab", + "secret": "271e2e8e13f5767bec74a4bdb6e6f707ac841917a3c3396463048803abc4c481" + }, + { + "public": "c70e6db7a78148d4bbdb164d5f212049f020980edfae8ccde7ecb984459fc977", + "secret": "baf1550d2ce100bc9c62992c61c37455f15b596216c30b24d92bca4ad26ec8c0" + }, + { + "public": "35da7c7fd7e297fad2992c4151a41d2d7c78d4cb42b94389f55a0c53daad65ae", + "secret": "6d59306d9cf83ad3099254a805686ef429c3b8e5607ccd40852936a329612aa2" + }, + { + "public": "4c7d78df12c3fbf0688ee25ad685dbdc9d704250b23d5bd1c8f38e87cb59e276", + "secret": "e6a00bc388abffba63f10be2c02f2f9ae52baaae54e8bfaba491171b0dd203f1" + }, + { + "public": "e7dbb340d0e18e555a7c59d40109280daafad40aa4f8547d51c2c6540eb68bce", + "secret": "1814cc7e982a46970d9701c9746665de5169b97550b4b42344205b5ded696097" + }, + { + "public": "7423f7279af918eed9199d366e5e5c2e49c8729b8f6128648a7b9bf7ab1fd1c5", + "secret": "5247298819104301d628ce09061371198e2b1b66024f2da83cee2870a5485570" + }, + { + "public": "1667a59051edc3e646fc75f5ad6db0d415471820f289d99dc8610835e8d8fcf8", + "secret": "78d01cbfbb52ddebeebb2000c72ae37ad873302e845cf4da442947c8ba16a723" + }, + { + "public": "e1265bfb04ef05a30bdca580593a26318ed688e6e29d382a3ef6dc421b225e51", + "secret": "1468c77a70daf4baf22ac1b1eb02b388fa5de8232d22934a7a87d373889ac0dc" + }, + { + "public": "516f78ca725241f5447dabe1f34f58415ccced4bf721a8ee16c62b16b13d94d8", + "secret": "da877a57fa764ffc92acc5a3d7c6076bcdb6d82003410e2a800f0e2acc588176" + }, + { + "public": "402e58ea563a3d085817603915b4222af11355678de231b0633026d51e1fef7c", + "secret": "6834344429bc6fb308d8b16f1e7f19d90aab1e80ad5beeeceb76394e62b02f10" + }, + { + "public": "7ce0caf21aca02e21507dd3efefd906d68a9d4fe6e239a76064c8961bf46ef41", + "secret": "f406a50d4dd68c8cff6cf494c86687e3640a735bb3d06595bdd293fe90940db6" + }, + { + "public": "9f2aad5b2add509e3318445a9b9385a5c1c0a647fb364b5820012759201e919e", + "secret": "506c0e1925dd5011d3d8e7f1e64159364c3aa5efb02e3d0a910e7c67d299d242" + }, + { + "public": "09428832a3c0c2b977dc01698a7c657d1190c0de5ef43ba7206d8598b9cd5402", + "secret": "c4201d56481f8eec017e2c84bb24f328ccf89f38cdeca4356831dd34c5c870e4" + }, + { + "public": "dddbe6eb344ddbdb5b60815de7f69ca367b2666e341ad02c5238a46eda04291a", + "secret": "c30cfe188d325b1a92f9e7ce0c951e0b6a068ac23f0fa2c6a602f6c386c9c964" + }, + { + "public": "e3642b4e1ca24da5cb16e17c8bb89c6c05b219db9f25ea283be039dcdd63bed9", + "secret": "95eb9218e4879b514c33b7e869fae42658a04ec7e63ca771fd04479c66e6f99c" + }, + { + "public": "5a3fb4e516e81bded9fea3b9f006c7f9fb13bf0d4c19b194315f7462c0faf3cc", + "secret": "af1a1cf89ebf817d7ec833e7313fd9eea947520f8ad407fbac61209a96ce9d39" + }, + { + "public": "16de4cfb4c9e68578a03493ad66b12aa1f0ccac6edc1905872aad49706478226", + "secret": "99362261f0ac8557423b6f00406be0f25dff1b843ff4c95a21f9bc447b1fb4f9" + }, + { + "public": "8993fffd074302047af1509ccb42759c2492508795cf511d064d9ab972ea9ff6", + "secret": "1972505da5cbb6442131f7c5a5818c9a59611145bc5266ac68de02e63917b3f0" + }, + { + "public": "18ceba81b155310d6169d3a9ab49edcc212f0d8bd93ee97a542f6a097d382b1c", + "secret": "641b5ddbb40cdea25d28ea37e2eb18a21f5419c648a532ada23e2afcbfe4e6ce" + }, + { + "public": "c6c50337a056d60a7fa9c75f4da3a456ce828cb3e4d8b9bc6ddbd2db2e0076ec", + "secret": "867b7709c14325aeac55f546f977829e886cd0b03f06eba629efbb7e9352ffef" + }, + { + "public": "317d2c77afcbf2c8501614f04b7879599df0da7d14a2bbd223840eb4b0c4fcb4", + "secret": "7eed86bb9b3b6f9a351eaeb550b87ad09b2ce639d7b51cf042e66db216cd475c" + }, + { + "public": "d56140b8655564de89cc5d781710258ba6d95a8c0abb3bf27f06e50b9506aed9", + "secret": "18e208ad29461dfa6eeb3c779080c0d1c7494f66e87758b3010edb0e58ce02da" + }, + { + "public": "c87aa520a5d90ace080ccce15ccc34f76a680f78a349cff58dac21e0a8d3fb18", + "secret": "f440602261e9312c70460a57d81cbd6e88ab1d582b2b00847083aab2f92bdded" + }, + { + "public": "99d0bbe1b1e6bbadd8412880b062c9ea08a508f2a02e68185971e467f1020518", + "secret": "f42517fee107adeac3a582b11c96fe35fb579f750d77d2c9f19d9a414efa4242" + }, + { + "public": "114f2f9eda2f99f53540a9f70011e3e94769c3afea73668dbe6bc73d3c9e0f30", + "secret": "ed8d154ad87d0dedac8455d749651bca0d47260798778453b934cffd072ea4a5" + }, + { + "public": "25ffd0cbd71564f731adf80459dd338854295e0c02d71348d754bf287736676d", + "secret": "7aaa1e190026d32628c48e662e7f3080a61f99f2e7988b255683f2e92be76e74" + }, + { + "public": "8d024712ebccc2c332e5138a0803514d5e7663b24c7d387e8a57b73c9cf24a50", + "secret": "ddaf235b0c0d42927536d02f2d48ba47f5c26c546ffd7884d697ac4b88d111ca" + }, + { + "public": "3b653ab6d944ca26e46bb7425a90dc26406f0c4f096831f649aae9745798c16a", + "secret": "d156418fe6e1c06617c90b6d7a2709622f9a1827dd5f3fa03913898cf65b9866" + }, + { + "public": "71af084cc1e8c378a7956aaf770ca9f25da3cf225adf8aca58eb25501404742f", + "secret": "79c0ca726c41380d5945a308839cb0c70a12dc6dbe180f107bd1b7beb7bede78" + }, + { + "public": "62c5dd699019628b6ca3fd9f7bda1bede4d44752335adc5c75c190579a66e7c2", + "secret": "fc30a39b8a88a5b79fdf904a43e7c181f01418f6cfe7fad14700f0293c5fa1b3" + }, + { + "public": "7dbb48ad1d91afdbd119a141660a801686ff52ba6a61d5e37e3ab016adac74b4", + "secret": "bfc2a182920503713783ac2a73fe8f443baac7a8649e18e01a57e3593b3638f7" + }, + { + "public": "2bc6827cf9dddbc5ca3074cce1d590d08f3d6de9c2e7bfde4bdc23ed854a0e2b", + "secret": "92ab80d16180f29b84d5076130c527a2551b54bfc6c3d884b71f106a682d6092" + }, + { + "public": "17e8f51360445938f02441cc09e7ce7d3a9c6278643d68c1ffd446228bdc9aac", + "secret": "8fe20d8d6dff9c90515d268c8ee9a7cbcee70d1146dd2e9b3eb02d86362b2272" + }, + { + "public": "3b4c5b8e662c3024248f19721ed19abe10c10b7eb1d8ef19f5a32d5634830ea0", + "secret": "27f8e6b04da3fee0a522cdd9761a31dcfb7f58fee470cf387010c5daa1a36183" + }, + { + "public": "524e5a7181b41bb76797ff4bdd16a3850244080fc1a36ecee7950225b62d797f", + "secret": "d22b5b6e173a1121c832e9093240251066b33d18bc106f1284d27b561f58cfd3" + }, + { + "public": "a1d458360090a16a8f43036a5e1c1e0774e75f906ee9f53afda140056abfebec", + "secret": "740418c953c6301e77071026fcf2ffa42407eb82fec80b514b2e13ae96eb5420" + }, + { + "public": "aa688df90590dce696b84072074e1be9b6de33aac71123af0c60424f3c13eefa", + "secret": "8c070a6e6f217f0bf0e87c7b190475b7a5227ff0439b7962570a687bb57d87a4" + }, + { + "public": "04c630ed2816c28b70ea87a5d164bddb4af81b4fb03c845abaf4f6d6970e3b59", + "secret": "23daa24d3bfb234f6198502ee8dbd0592137024e6b04183a781903b362cf9982" + }, + { + "public": "715787e51b5bae42f7cf2990a0386a12f2d91cd02ef89207ad280a542ac6c9a5", + "secret": "51fdf3b693ae28feed83914ff4c1ee746d28738de191a40d13fd4e88708c7b49" + }, + { + "public": "ab8322504c1f8c9fd3e70d180a66c13d6dd5cf10693e195eae6339545baf3316", + "secret": "180fc0ed844c79893a839bb122945534f26578d788f2a6b84ac7b9280c92ebe3" + }, + { + "public": "8fe3769c35c4b63a5cc087f43cd401c8fb00959c8d454371b8de3f07795c2859", + "secret": "d16312938d77ed68377c30649cdff719a9b89cff1c1f0be12d2086041dd099b4" + }, + { + "public": "4d2a4ec8635a13be9ed8b7de349ec40bca6a17a78582121d616949a070aa8b04", + "secret": "7e4f9be376dcbe4fc85e4ad55a874ce720a62e5423dee8f3474dd71329fb1d53" + }, + { + "public": "920a458e17ad78f74dac7bc4ac3fa4bc8e1ef1e695d6e109128d657e49a0d619", + "secret": "6c7edca17f1e189d48d90b65572ecd63f43a3086c758c3bbf2e2aa228f5ca06b" + }, + { + "public": "bd4a4d9b36ad4be451f1e4adcc46877e4cb5561ce9502fdcda94f9082e37ecd2", + "secret": "c90200b2f0aabe656007f58f665792439d467c7bffa244cfc76e015b21e16b7f" + }, + { + "public": "b14b684ccbd84a93b10bd19ad693261de4b1baf56b3ba6860f885da2d2fbb9fc", + "secret": "69fe8aaa522e120c908cc60936ebe58e2f0727f72320afd334d4d71451fe632d" + }, + { + "public": "c593ce514c50fc4fb277c565863ee1afeb2fb5ebaecd249e1b0679babaa459c9", + "secret": "a3463822104c9b9573a9403955eed1cc8309147b73d29f582410ef7351a31692" + }, + { + "public": "21dcfd4c20a9824532f0f1ed27ff380946ef4378b0775f25f4449d23c6cf1fd6", + "secret": "48d50682911774b7fef95584af3a9560a68494a122b7ef2d97a96c1c6602585d" + }, + { + "public": "25cf5d62b7d871b81d1111f853494d7ec822e86899e80f6917d083aec1484c9f", + "secret": "61b1157529b8e68e3195132aa30cdb636d7ac5af7381ef1830b19a2547a059ad" + }, + { + "public": "4505d7ce2ae54e394aae10a1d49883166b5f80c59d865b561054d897473ff369", + "secret": "6ee6d6037a507752e3c453e8f06f0e3ab6646829711750727ca84b09e97f1a59" + }, + { + "public": "9133e4ce8bdfdedf59d055808fd50a69e2a0dc69575fb13ecb4e5c245861dbc8", + "secret": "a768890be5cf76413108c760f398f08a41173d4108bb6f9a5f86f5da38b0c09d" + }, + { + "public": "f0a4ea8fe6204fcb7be3e07f9c4dee94569807594f6c2d7396f4f3aa70132d27", + "secret": "9e186e47a8c11a2cb84006a48a4a469771aa846e0502d5330edb0f4c87e2da44" + }, + { + "public": "16bb5c190cd009559020f9a35ee0a569cd934c4a18b14656559292c35b29a2f2", + "secret": "5629910b4799ac429aff93512462c6fe9e2f73d5fc5882dbad34b2bb90f599cb" + }, + { + "public": "985106d3966ab9616620dad9d72cf0d57b0f3e4a7ecab51b16bdaf54e01cb59e", + "secret": "c73ec8267ee884a754b16ec368c45fb028d5e3b761167440e9948a8c93312dac" + }, + { + "public": "c5dbe23a2f91fe93feae57ab60dd604af802254b2d8636197a0d0d0e0cebc0ed", + "secret": "d89932efd5254a105795c879f98fe657ce5fb689dd92e544739f7d27e61d8e0c" + }, + { + "public": "1fde65fe328dd6ebb61d139548f465ce1de0ec5161c64bd32dac4d12a625a6e0", + "secret": "52721f640b55a539dfc7ef36126523cbc99a720a596024309ebccc9848a75bf4" + }, + { + "public": "2b51d861a5dbfbe8b3aa566342f2391c3349a7a45da8d1df3e8109f774251e76", + "secret": "ab077f222429b2e62720fd43b471d727bb2a5b9a974f686d3bc97cf3b3dfc453" + }, + { + "public": "f9f18b9eb56be945cd17149e9a95f9578daab1771d24ce7d96daf9343ee01bb0", + "secret": "0106d344362098cccc5396bd0f62b6b6719ae2973da3213882e915295ae2285d" + }, + { + "public": "8d6db5e59cd29a8468ffedc544ad4aa98205a18ee2764ad3a03e6d8ac35bb624", + "secret": "89bafb93eee78ae1a01f67ad0701ca1ab51fbcccb5db62c11c0d3972e2a63cb2" + }, + { + "public": "12076f7e321a2c37bd4622ac5c96733e45df983f5cdec136e755eb97d0504f6c", + "secret": "3b1cbbe4f041c7514caf2e62e3ac112b900c63eaf63580b20ff7029fa6ffbdd4" + }, + { + "public": "7a993733184be08185226fd08432cabf7689fb065611bcc961ab1d8409d4930b", + "secret": "249bc0476998e9b9a1b176ff216aa750602b03e3433b98fe2f9b98dff3d389b9" + }, + { + "public": "e448cb68dac457fa2970f89100ce00069b258f6c71867ea64123e316447eb2e8", + "secret": "ab390f49c1358cc201f49bada8cf8ea9079ee6df117849b40050e78c8cc79e4d" + }, + { + "public": "2df97543f8c2546b9ec854be5f7f2e5518a153d6a0af739c9c987f411573c098", + "secret": "593292995510a4b2b3c5ee3df9ed6acc7c968ad52916d2d7193bdc825be32ea2" + }, + { + "public": "7f84571e7dcafa9a062b91048821fb839739c46472a0a4583acc9834de49f939", + "secret": "125975aab2dbb1a3907d7bbc0a72b434fb658886a51e86b23aae57f148edfa28" + }, + { + "public": "e39deee109f2c43fb80d028c50a70f77ae3fc00679df1c8144dce4fe95b7f157", + "secret": "7cc504de1eeab29e0cb891f2e0387874833fdb2a6a2e31113237effb6c91c85a" + }, + { + "public": "bb5f2f5dacf0d1316d4b1a71d2818a165ad6dbb5afa7a1c40afa2f6f951769c6", + "secret": "ad7a7eb98d0bf9e2c9f780125b21c397c3d0e6ca0fabc52f4dbbac3eb8a605f9" + }, + { + "public": "f9a043c6d20db193fbdb42e5cee3b79e8571f0c83ec62ddeb40dd0e8537b6b97", + "secret": "0ba287f9665b8e46ae129f718f25a2ba05c94ec038c13e2fc81cec953b477277" + }, + { + "public": "e4cefffb2675b9e60b976af71ad775ec951e2ba94c613266bd61a809c1363e76", + "secret": "2c8280a87797e86d7196a9e56a214b538bee0e5ef22c694d2d7fb040f65aadcc" + }, + { + "public": "0d3622a499ba3335184b68a271ec78f2599fb6d2edfa4dfd7b1f703a8c745402", + "secret": "0360ae4552301b295ce899d38602ae750052697fb900cecf8e99312bf113bca8" + }, + { + "public": "dc36963d8cbbb99de07a86253c084ecb4be0ac0554e2d4b37d0eee43027b87ae", + "secret": "8138a79a9100ce550b41f77dfcf93dbaac26db94ac052b396c33bcf69891c190" + }, + { + "public": "9ce33ac203f9a20c11cab1b054722886df325a9fa40717bed452b12d8ce561c5", + "secret": "0a1139a866885e681c73fa4bf320ca3db08e7cbdc8e70be3d395dfff90cd6c81" + }, + { + "public": "6a4c30efa3318e23b712c6b641b7c6314d3856ae465a7c34c296cf76654b8dfc", + "secret": "fc241685830e450de334e644f6589d0efea6d1b59c5e6c70783146728550b112" + }, + { + "public": "15fd03fdb91b2b2e5522eabe49f4178483630045ac5dc1096d38fac6a0216941", + "secret": "40038258cf50cc27c535a18ca0774db01fbcf56da5095f4f047cd64131b72def" + }, + { + "public": "632dc8276dedf3b9b5e2353637ecfc69f6c450a72b0f196970fb0e44ad31afbd", + "secret": "201c51f68aa46d01bb06b6422028464faa063c789624c85df263b9c9ce33b0be" + }, + { + "public": "9b8ace5447d582ce799635f8398d5afbaf3ed4b18ca4f87487a53407e149e7bf", + "secret": "2030d4dee699461bafd90028eb7c7a2a589ccf0fd27222ab5a5cff0b065f5251" + }, + { + "public": "1b8e88cba351f15b29c8087c5d693ddaffa18972939970d77de2ade5882bea32", + "secret": "01afc4eb1fcef0dda07fdad833771ec708b60cdc115902d7162a2f3c4cbc6933" + }, + { + "public": "c873b1e67fbf90fbead47faf86bb3b8eb2a3953c33339f1646b2dc7696eee51b", + "secret": "f686b88039bf48f07563d9441437ea4c63df333c0bd09e2b745f99b10cd33c1e" + }, + { + "public": "33aad64b2ed6f8e5ea96245c88d78d342878d75463ed8891f37390cb6e48977f", + "secret": "e49f1acd8cdf7e96bcc509cd6fe1cfc151b9f2d54be5b047a23260807f4ac315" + }, + { + "public": "592c2547b711047da89892e7423f293d8389d0bddc796436bab48f854b553084", + "secret": "34f73faa5d6219e4efa92020be4a914a2e963398972ebc985a4fc5731321ebe0" + }, + { + "public": "6573f68aa7a06d2d70de392e13474e9f81d0a91a589a3d27b21426a0de8225b2", + "secret": "0bf78dbb40b68f2ac3095792ff12681627e432df3672413d30f0ca350cb040aa" + }, + { + "public": "69131d5322b58fd023c3955057e68bfecf6ec758223906aeb8020ff5ca4bf9b4", + "secret": "672d7c4cab6a10af1aeec49a90ab1e8ca78d42bc3d36211c2cfa8d0bd73fcd94" + }, + { + "public": "3ada6236e05f3b92526a8a45685d737d1b22ad551f96d32d80562917feb2fa24", + "secret": "3c8ec6d12dfb03cd9797d1e2b9d9981c9451fc2b280e4d846fb5856242f476ab" + }, + { + "public": "384707d0e33cb614edb057200cb85c805eafad81ef0c879ca993da27df20e8af", + "secret": "009dcb17503d25a3f0b9334b87b280f14c5acd69f68aac2f7e42399526ab1025" + }, + { + "public": "f61cc3c3ba9e91350bbf1fcc7cbefa56bcd4554ef46bd99ffa7b71b98f707d03", + "secret": "969017d4bb2f91a3009f6156ac43485524f78ecd1ccdfff33e470825e28fca72" + }, + { + "public": "73aa7fda4687aa9397d3bf1d3fcd050734e5e30934a8587a8bf75c25ecce6849", + "secret": "37978e52bc8b7247d40390d07cb77e47cae8f88cdb6ce43c228b32b3f8113112" + }, + { + "public": "6efb662d33ea0d347c6f2679d9976d92453d5c168e90477f57c654fd173da8b9", + "secret": "aec2ff545b6dfdbfb5e7fe98567652ca6032278b395068fdb15422aa82f3783c" + }, + { + "public": "1832553df6c596990dead79409861ab3e428a45f6ec23eb39a3a1ead3a39bd3d", + "secret": "b8559cfa4a253ed6d08f9cd563587627d6c9696eb1ed2e5b23af8745dc0959e7" + }, + { + "public": "0b2a803982a55bd02ecce3d32821b20b622c5ade803048d51f0df831589bb9a7", + "secret": "ab3fc4f30d6da9caeca5ec7bec1ac87661e982b066fa671c35056305b02fb4d1" + }, + { + "public": "b7144bf18f85e69981ecb6243a27e25e8a10f79179ed04d1f67fab2f1ebd87ae", + "secret": "b1bd56e41045e0bead491e990c2db563349e615ca84122c2880c1a1da41b7c3a" + }, + { + "public": "5fc5b6ad9cbe68eb522f080a634e4417fc48908c466001d84b725a976ef27764", + "secret": "ff9b1cd83392940ed2734d5fd0c1eeef30745fc365abd25aa7956a8f70819ad8" + }, + { + "public": "3e81bf7f9b9e787f8e0e7142fd7be9a4cc732079f79d49bdfab735cf55d1e27c", + "secret": "fcd367665b9e3f05a56dfb67d9ae7d107cd5f68ffa2fdfcd222bc639bc6af959" + }, + { + "public": "b8ba75a68a63bfd503e6fd2f4f83c232ace4bb37ad373bdb9353e24aa7705173", + "secret": "7606170a9a1fbb000ffba7257ce8903dd477621305fca1ccb84deda626570481" + }, + { + "public": "6ad6eef3e6a471247a09047a7c977cb366c2263d90ed8fb55e5e2440f2d337cc", + "secret": "f7e4bb73e177325571d1151b9d791216294b69bdfad075b462a3a931dabd5bee" + }, + { + "public": "77a0065d754c203bcc1fc83ed4efdc3afd680994a4dd713d9055c7046adfb1ef", + "secret": "becf3bed33187b9ae3a05c3f4dda8756e6d0246a5cff67c94d430daef768d910" + }, + { + "public": "18137b3017ec776ec700e79c94c994344e603cd12ea216464b6f7dd474bc8b01", + "secret": "a3ff2cafb4bfd1265e83f8a7c2a344ea2db4b36bef4f244a7587c99302ed4b38" + }, + { + "public": "ba61930621cf04e0a07cfffc49f6281d65dd8f5cabbb183e0c7cadc906b8fb09", + "secret": "4d0fc2a9daff402fe046b028cc7904b60705e41dcb9e34980bc2ec0663033466" + }, + { + "public": "c418d4d95c972063acf51cd2bf89ab4b7d2df46d060517de0d81a2e7444f6af5", + "secret": "b497d108c70078f2bf2a4b56406d67141721f298f3488f6758c7caf00692da17" + }, + { + "public": "95401ce76a364f9a6380c27fc16928dce9a3d4672aaffa6fd5c144c0c15c82b0", + "secret": "e69d584025ac87cbf7d81ae45910a669caf38917892d434647ef6fb018f21bb6" + }, + { + "public": "99fa2e7c4205ac501548d147b623801cc194dc49ba4e512e9f833cee17001fd0", + "secret": "de4b12e94dc76f6316ca4cc344b7bc2b23f69ffd787195102d01d9310a06a7e3" + }, + { + "public": "1cac9bd35b5a23ebbbc4201289c15cca2c437555002c9899aa1cd1e2b4bcc204", + "secret": "cbfc70c77012dc2a7f07260399ad8133d113063190457633c393c091cb606983" + }, + { + "public": "7f47c5a09c679e00d7efcfe6f3d8d7dc27f2ed365c2ee905bf9879a0b02bde04", + "secret": "090b86b598882051b4123c262cb69c3b560a9e836c41827e6aecb92934306ec5" + }, + { + "public": "2db06504fbcb7185f5476653d26108b91eeafe53563795a2e5f04d7f975c2689", + "secret": "d709f8a8858297de4c014a7b8a1143d93d3e9754c6db274ec67bedf1ecaef09c" + }, + { + "public": "62891267f89c34d20939d0bf5f977cd0b12641677252126d1f6daf7d34f5bbfb", + "secret": "0bb037adb88f5e7f70fefd7546395038d9866c8ad6a7afb45f6aedbf302e244c" + }, + { + "public": "c4a62613c2818aed6ffefb1115493f47786d71a5f6c0e13f4f5fbd2f2025ef95", + "secret": "5869846fe2dd0652a466650f0449373b3cbac9497d76bf0981f9958af05b5ea0" + }, + { + "public": "bcbe517b5ca9250eb97f4b6111fff2e4c4ff023ec4eebca36c7b6ccecbef6039", + "secret": "5d2da9990b10c1bba3aed940246061d32d5923a076740d2f3195c754758d66b0" + }, + { + "public": "b18b89236d5ed30f79b423ff03b1eaa3ef5754c6f0a6eb2027c3f6cdeeeb8d92", + "secret": "f2bc7855c992a72e83ff631065ced58a11384e8f22b5fe1d67867c76276ca9d2" + }, + { + "public": "5e653eb275cf899cd1c32bffcc81f7e6e487c41c39dd2aafa6cdbf9d54fbde73", + "secret": "c2da33a82e88ca2a0a2243a791b21423136189efbbe77aa8ee46e893ce7a2061" + }, + { + "public": "b8eb68b72cbebbc7f6b2a37791f4d6efa33ac6bf690e1e4164850ee215600688", + "secret": "b30541e29054e9573a573ada37414f06e84530276ef7407dbde033e54a9a1b0b" + }, + { + "public": "9b30a715a1f80c1744d2391d6866437a155eb5615db02dc2aeb28c9c7298a65d", + "secret": "f07d46bccdebf7460a67a9a879b7254cb535fa641a98f58938abf52d1e81c8ae" + }, + { + "public": "649b05fba0e159b5ffbff381e29e36a4222659664971f3b5b8d454aa4d8744c9", + "secret": "0489931b7dd8803dc974cd35ca092bfd8e2e0bb85bf88cec7b3d92e59100fb04" + }, + { + "public": "069b9f8d37c8cfba2c7194e1eb366dce9fb103bb42eb630c6bc87b1f11146c50", + "secret": "a8d94d0f1032670fc995390c6c221033c50742bd715921853efc0c0800a015dd" + }, + { + "public": "1a4f71ab1d3bc19e2398726de56c1e79e3600ae714266f517a89ca15d61a5478", + "secret": "ddae22e6453e3e7bcec475f72f412c28b79300f51e75f2609c375ea133c205b8" + }, + { + "public": "67c05dad419879ccb18d42cad0f47dc4fd55aa567954fbe4231482a296f2a487", + "secret": "0a2588d17a432d575defbb484e2fed7c60ec9dbbdfefe516d9b2f16150d3a5bb" + }, + { + "public": "caaa4731e4b800e187c87a9e9554cec86dd10dd7a7e9ef423208aa4a674ba6e3", + "secret": "29ad964e0e4c92c83b622651274b2e2d9e5cb7b4bc5d3e5886fb7ab2a27ecc63" + }, + { + "public": "25d4e4d16d9ce1bf6a4cba50ef6e7544c34ef989628d63550199ebb5d0f5b6a3", + "secret": "30d2db194bf9b07a0dd0e8e4bd1e1ae7c44d936c1b0afd6f651b68ea4bd5f586" + }, + { + "public": "f1a8cbed753f83a3cb3f3d261f58d62d08395cd60f3ea243919ad6207cdee44c", + "secret": "867282881315b031dcb910e56350ea18bf01e0705133c103695ea3b0fc189e05" + }, + { + "public": "c5c62663210abbf436ef190afd7494f213d9ebd7059e0546eec0af4cf28d2db3", + "secret": "e5f55a19fb539dd37c86ffd63c8211ece504d64cfc751bfba6a9953c6a2c10f9" + }, + { + "public": "e485234a319b7a12b16f61f120d79966ea7f6ccc755f787f85952dcbd4b87510", + "secret": "62d5934a91d32b0985ef7c3a1b9c75f84052fca55da0f8ae74f11023d4101eb7" + }, + { + "public": "accdfb9ce40e7e5e8daa7cfeb768810eb1f1445bca1cc8ec26b28e72ec1f3eaf", + "secret": "88bd96b4dfbb2e2a4848e859056f21c4175437f41de749d1764d04d002596af4" + }, + { + "public": "0e20b9d5309dc1310da053d550af3a8cdbaaadfda7db83180ecaaeca8361d8a9", + "secret": "3cd5067c2dab34e91b3c5d26d2028e45fe94f45c28b2847c4a100db3b9415476" + }, + { + "public": "3f16dc2d08df21180663c083ccfb9ef74b1ff890aa11c838b44e0a483ea08693", + "secret": "a59785103d367ddb3b2f03bbe24637242ea7f1409d4d7be454816a0a59b1dbfc" + }, + { + "public": "a3b3d38bbc8156559d0d534a6030fcf3e60ed721b1edaa14ce70916146203efa", + "secret": "f355cde485825e2d0ee771daa9cde8da584ffd09a62b7299b0523e7e7cbd857d" + }, + { + "public": "229567a1730a8aeb5f01562c46f4e0d5bfb38329cefa3fe1f15f93a48010bd9f", + "secret": "b608f262ead0aa509a4a23ff760d5a51652931883973bee10c711921b9114423" + }, + { + "public": "24a9a03b3e67fd479513ad9c2e610ed7909537130c05fc74cb2cb51732e2aec6", + "secret": "6de6966b730baa4480b26931c92063d675cbd13a8c5e7adc343fe3452de55e72" + }, + { + "public": "5e36e18e7795e388f8109a01e2f8755c710000f08f4a20391c921b0ecaed0e81", + "secret": "c884e52f898baef4c64b6be05f88b768932ba3cbfa703f02538dd0a01910d44f" + }, + { + "public": "f42b0d0bd6baa9b5cee60d91397ebc536df5d0f587b472163b96d00c45eaef54", + "secret": "c2ae4d1abbda1a7faf9c6eca50d3ed9b5dde173f48202cb831639902c0bffcf0" + }, + { + "public": "99feec1184922c5125a5b04192a8bfffa9aa3934d3094961845044541e700b80", + "secret": "6a7e4af1b84823bffebf0589bc992d9404fa29a666ffd8ac548518cccd802375" + }, + { + "public": "9c7a1ea3116379c3878d45d36918ad3bfbd17043f717bd6ef070beedb3bdd6d5", + "secret": "03d8fb7884232a6d074adc9bf69fe5c2d7e0b396ccef4a7bf81dea7ea9ffeb6c" + }, + { + "public": "fb15fce868eb34a666f6f6e3f7df3c0cba3bb8243a327eb1eb7ed4b1a50a2306", + "secret": "9797b9ae444926aff4f4268193f4c5e650046d59e7b41e8997cd46f4da02d642" + }, + { + "public": "59bcb84c869fe1a4f7056a72a9452774a4e4329d71b79daf25e561cc2e0a7012", + "secret": "816e88786a55ce7dc3b716b9de16c75b5d5c0f60bd3786b22d470407b3a04d03" + }, + { + "public": "c14cf76ff42eb7546a618c10d6f982ffe16988992b420c0afb750a61b8f15137", + "secret": "56e4db4860ae4e25ba27c841f4de3179fe31befe8a716311d423db3e4f081070" + }, + { + "public": "70248044a78c7e971885d193c0aef604307d81dd1aec9a9aff4a64ded4170aad", + "secret": "d4fb6d2277b6dd9be427e37fd17f8a0b6174bb709a7c4278d0c68ee2c648df1a" + }, + { + "public": "8ecbd7aeb3d4c13d6c13db2c1d20d443b752fab510b84c67da285bc333ca43ce", + "secret": "2a1a49adaa428aa013ed78793310c52d32f6453211a3852b729ab54e383389c2" + }, + { + "public": "f36f9ead2aa2a27ec8568d39cc601cb4c4bc6b4d85d7c12ea3eb0aa191fa13dd", + "secret": "be78469027e2720b5b13250535c616b4017306f7c94d085cbf00ea5941e59108" + }, + { + "public": "bd84ebddad55176cbaf0a08edc54c6d57db77aec7ad54ca3f8c7e143fe7e9915", + "secret": "b23606554dd43a9569409c4b9b61a4c9f69347b82bb0b1355dc4b8345c7ba60a" + }, + { + "public": "2c3a248a9453394bd4c7ac079133073c30b14bf6894bca0e4ecae72b88e105d8", + "secret": "b6fd1903ac7870e7fa19166cd04a79e6c58ad2f57a0907eff0e3f74e26fe8da3" + }, + { + "public": "503694f324b6b44fb149eeeb8356c02bcc8dcbf6ef009a5c29d4c3ca9bddfa47", + "secret": "ec9339bd7e01c42a501c80181b537523f4deeab946e8d4efa0235f8a14dae8f0" + }, + { + "public": "7902af447e250886d068313a63b41b661d7318b99558658112cf08693b74fe16", + "secret": "c4e53ac84088bb58ba05369ef811cae70d01b99c0108c33b3da6f786cf295d24" + }, + { + "public": "c41c6be6b4d6d9f291e6ecce6ab7809582083b1bc1ebd7ad7b1dfd4394dad4f0", + "secret": "569d86d046480a0df1629add7179113dfc9f3889ccf72e697890b0f5c3bf400c" + }, + { + "public": "a02c199004ba615cbd0c4f77263fb8b6cda9dbc1c24383fb4138ef39dc0a97b0", + "secret": "f74324aa36568253b771205de391d6cfa476cfb147ba3933b74126c104a4d9f1" + }, + { + "public": "d0a8fe3b76432f15ca329ccbe1c9b0d4c58bebd2189c471459d810357cb4efd4", + "secret": "f2ef3158a6e49ebff0a4140e3a80c97b0085ec39c876a46e5b39c7522fc97d64" + }, + { + "public": "e2a7631047223a3521210ce90537db258a7010be7494862d70a43d6a956bc005", + "secret": "c411b989dbd95cd371cfc7b332f585a48e9e0bd780d358b35c57163ac416d7a9" + }, + { + "public": "ade328febc4105d21b4e5febe877dfebe6d9fc52f77c704fe4d81262ff45f3db", + "secret": "4bf73f355743152a0cb720c7e701c049900daf3cfae0f1f4443edfefa5fe0c90" + }, + { + "public": "a3f1b75157a408a073d79300cf65679db1b928b902480e5768b2e8e79a03aea3", + "secret": "4b53fd422005ed4b5928583f125f1cd3a559832029ae8dd2a80e0ecec58d65f0" + }, + { + "public": "650230d064dbceb328f798fa65804f29964beca010c3453129ded1eec8438503", + "secret": "d8287f21a4351065b8ab93691669360355049fd664b660630fa0311f8a262988" + }, + { + "public": "661f3698409184c344bd7827deb6d307d6f9d353eb4934f46be974ee016212b4", + "secret": "e6d77209ea3b1663156c1b0bf17ab807354a4dc476301f8c739b274ec886e4d1" + }, + { + "public": "f17f14feb44b525e046e9bda922d57fa86e1a84e8d2238c2b61b242d433bdf39", + "secret": "6972da9451a59222c6530bcfb0646758a3f59711c9220cce49535f63d518a7c6" + }, + { + "public": "5b473895f24bab06d6ceb3383bedb42b9886bc193e2635f7f3efb6fd33c14cef", + "secret": "4e2a607cb334233052b856d441aafe608314526efbbfd41dde967e912c195ed3" + }, + { + "public": "9711a56ac5c6097a71edbe13612d07e44fe671af31edfc4f7891d814dca49997", + "secret": "2cb88f5d63f3a2839d110e5171619f6d8ff4d1e0c51c2c7eca7138d646968764" + }, + { + "public": "216fbb0e63aba856d85bf94a8d10c06f56066e9719aec860b2710fe7caac3163", + "secret": "a6cb5c8271d285b425e9fc208a14ab3338fa285155f6022def10f34072850b23" + }, + { + "public": "05dc1ed27679e0417ada244adc262847be09f46010dd6fe3cb166373f8363716", + "secret": "912fc21c4291628006acb903716d0c87190ff4d32fb1115a78d0abb3403820c0" + }, + { + "public": "de8546e1e80051d5db299d854529a01ee4f69734991efccb9713858485d39988", + "secret": "9eca54d7effa2d0554b0286bb14502e36d6b6503bd491f39e8eee2916723cb28" + }, + { + "public": "941f191fae823dd1c242998d4c5db40c8afb49f32632f5081ea6e1e1e75177c7", + "secret": "994b29b96c8900fdf128b471e378201e835b70c45842941deefe4554a2c39e9e" + }, + { + "public": "7f804cc37e31520038abceeb9cc3021b67374854e117d61af27532f7bb2fca6f", + "secret": "1d9672eb9d907e581932e90b56fb00db757ffaf26377c3ee6c7a0e1a4112f9bd" + }, + { + "public": "ed1cd8e28140b0b54acb895832a360526be675f27905dee4b2748807d3087f1e", + "secret": "b733b37267a3851b0b07faedfda52c8f7c2c43662165356e63c92687a3de0da4" + }, + { + "public": "916485b3eef865acf818bb9345fda749f7875245a7dc6f8e91cb0b71e090e0f4", + "secret": "6ee65789b0cbf0db72d925df0c95836a2c48475f916e56a5646e0a00d66ce92d" + }, + { + "public": "26542938ed069a269a9aeace3d307e35358d292b32625dd254e3d210cd8f6047", + "secret": "bab61e87306722c5c2eee3af69f9fdba701c4c2d2ff98587b3f3949cae22f5a4" + }, + { + "public": "20ddd63f58d86369330ad4f3f51ff50cc45d550eb3b1577ac350b4cb1e39d4a7", + "secret": "af8ecd3c99c1ceb26e53ef2355c771ee7326941a68971bea6d95e11be4ec30f4" + }, + { + "public": "ab8e0beee090ba4ddb44d43b639ffc10941031d0388d184b42d47478d265f33c", + "secret": "3d9a15dddd6f69627c396341ede923679d665a84d979a9e4ddfe5da2530f1efc" + }, + { + "public": "0fd27324574ef684a4529317fed63d64f18890251343a5f6a38876b955ddafa8", + "secret": "6856fe2b15e0a89315461b22b72835fd59a5b3015ea032d16a8c1f7a513073c0" + }, + { + "public": "baada2d1bdbb5a1042e6440a7a13f66549763c35ceeefd502d05448fdd3bdbbf", + "secret": "85a21b5a8ad44365e83fadced92c89e2408c2ada17df17aed55371ddd1045ca4" + }, + { + "public": "6af227a4dc2ac0d3c178f29f22395df46632f0d4036d01e122653a4014bae92b", + "secret": "a452af611572c509414df39e76259d05d40e4979e70e30b7d16fe1cf812c4b28" + }, + { + "public": "adc6dafaaa23e975d763194d483f0359299e96bcb70bd44fe92a1801112e187b", + "secret": "8aa9e563c01a8251b8eacbc7f9bdc3c22a97b41c77a23cc08fe5b64fe9e6f663" + }, + { + "public": "bc6c77ee3f0a625fbfb1012442305fa575a4d52ee0fe82e3ccbcfc1aa97bc917", + "secret": "9eff385b8907e357419c3bd49f2e6ba94c67fc53ca00ec9ad85c573d83a68005" + }, + { + "public": "0c1d220e688c84238e55c6d166f4debe9a5edb26c8e784b3c18af273a51aaea6", + "secret": "5899192504ffe085bbca2562dc0cc18386da793cea45eedcd1d8d2f004ddc817" + }, + { + "public": "3d8bdf8162d642140f9870cb84fd2c82cc706e83f5100123e31169381bae5a8c", + "secret": "ac40fef174c84f2cfb93bf7f59b9123c3ed2b2ec819b7554a7531984b8d2949d" + }, + { + "public": "a3b38619c0982ef40b03a5da4d77411ae7532abb75133c184b3ac99ea58d46d6", + "secret": "d6ecb90b0f6400e20dce9c3bf7e704d0e4b0f64d89c77af27fff62bc43865d56" + }, + { + "public": "219eaf6364ed6face38727b6eefd50336ae8b1c9f5817db20487b53dca1f50bf", + "secret": "df939a62031e205a510a33b3eb86e3af51b6bc7dd986b9eca4aa9f344eb94e3b" + }, + { + "public": "691d869dbdf9c792cea1587b9b58155a68606f91f78f98003460cf103bcb6d5e", + "secret": "81b185d84419f263fa50d079badab8030febc3c7b7622c24977e166e3164c477" + }, + { + "public": "9e34be21592e9260798252507d7f3cf27c57d1978cc432c25a7a8b8025758840", + "secret": "471244b7b426f1f4d4c4871e81d822c1a6177fbe6c477f6b72757699d21bc580" + }, + { + "public": "efc778f02d2ab0e9932bcd4b9a30d439aa6764db066ac87ada8f096857d23f73", + "secret": "a8f28e766e3a0b61bcff3ea7f823b90e56549a8bf3dabd7157548e529c1fcc90" + }, + { + "public": "3d4f4d61d3abff3e929098776160165bbaf966d15cb485457afdc2e32c6023a7", + "secret": "76bcbec85f0cc44d7cc0abe0c7f179770653ea15692516e4ab453e4676470951" + }, + { + "public": "6052442f41bed488c2898579b86732b7d5a0aff77553bc2d45cf81e43f7194e0", + "secret": "1e463ab3315e8976e94b6e9459af6b803e1218e6b5c39a49d732ec37b8493403" + }, + { + "public": "e472a6b87b00cb064838ffb4ebecdeeca0cd5e02e6fbe0c048945d86857bf583", + "secret": "316de265eb21cf51f04ac8490c23edd1d6140660b0192ed9dff2f4db50085d45" + }, + { + "public": "2273f214bf665ab5ee37261530ccdc555f7325f9014ed8b4dffeafdda9859a7a", + "secret": "88c9a40af22e126d8943984b351d87024e33702381de16e3c10318a00a906923" + }, + { + "public": "42a415be42e665a76fa0743bccddb8c916a7b0e06665f397a1be37c0e0520dd9", + "secret": "dc5b1998d9a67e7de464ff4a12b8e19102e5003cef3db5eae315eec416439348" + }, + { + "public": "92649af7eac7639edc56bae7be728ee52e70c7586acceeefd57b3de9cd25e5d9", + "secret": "5a12a5249bccb4cacdc9a5f25d9d5b6205f2f6b80d312d4c25b18f690e8a7d45" + }, + { + "public": "c4265d4031952b681a3c1a585b062b8b53a7cc496cd73fb638a59d2a434bf414", + "secret": "36c6adfc208da1534d915a458050a33aa428f50f73e4063b3beb4436d37bb612" + }, + { + "public": "0a23bc24e4c7a372b369d8f70563a55a3f08a95c09e95d461bf10404d998ce37", + "secret": "bdb6595db6e28882b2bf6b3ebb5da67c95e8a1870ce7a36f79e3d3efbe349bbc" + }, + { + "public": "dc9c0004e2c130e376420aa751fffcb379059f02df572f23b34e6868937408c8", + "secret": "88bd15ace8a28cec402892cf8276835be2e543ca32d7482d771e9c47dc99df8f" + }, + { + "public": "3bd55135521f62c900b412be71d74012e92aba32ce4d13e15f66324d33e20951", + "secret": "8f3d49f968feaebff1fce647db5c854f6b08f5886c3d4e20dab250b40504c92c" + }, + { + "public": "a187cf67ef7cfb12b4a395271e4391074ff8bfa3719a706514d0d56e8ba896ec", + "secret": "c2ec3e1c9bc9cbde66241aeed92122b71996ab5697603c390f7513dcf7b0574c" + }, + { + "public": "0c0b9aeb2a9a5b3acacbc5d61621124cfc16855b353d747b3c7bf28eb87cbce1", + "secret": "05a39f10c36bf83d820951dd0e5b8eb9ee5ffae0f5e4687792afb44b5b8843fa" + }, + { + "public": "d13e5d5da9b499ea62eef6c7034933a0fa53a231ae4cdd32e708d2520614fde6", + "secret": "4a43dcf616dff641f189e41b1390acd2309b99a950adef70a0777021e5ded448" + }, + { + "public": "725197c7f63d344d8c1652817a3aa02be0a6de9d7a6309c4e3178761831f89cb", + "secret": "694439eb266485f424c90014d82d8ebb6ae166a1e9f33f2c7bbafea038a8fca6" + }, + { + "public": "749c7c430527675e897c09a1fdb0bc4074476091f4532cf845fcaf031281cd52", + "secret": "33506e72242f4a773512475bce43228a5c50208fddb91aa41f423f46c513b481" + }, + { + "public": "2e2198dc0ec511f983dcf1b85c725bc93c03d9c2c31381e693e1e8fe345740ed", + "secret": "4db9dcd015d5c5fd462edf8f02cae68e6c0c1c151bb47e1cfe3e351f6e982789" + }, + { + "public": "cad95d73c528ad101398e30c01af612b08d4b432e6587be82da11ce02878fde3", + "secret": "e9864f1d63ca2c4063f99dbb23a29e808faf967b9bc3153ac451fc8ecfbadd49" + }, + { + "public": "8854855d3e1f9908095554f16ec7f66c25a1623d28e0cc2d97a9c0732d084c12", + "secret": "247eda425f8ddd54e615e709422d4fb6f2eb60e80c6c8f5f208d705f917ff138" + }, + { + "public": "7e38868aa49dfd9eff9d5a1aa2d679571271d92c306bd3c4daf5e2e1f97db212", + "secret": "7ef5f4d8d3c53d60983922c9e25f9c3b200f5e07fc624a6ed60133588e663abd" + }, + { + "public": "3f7db925730d342c2f731ed1fbe0631952629c39ce2fac2fec2a566399e9e9a7", + "secret": "e7a1479a3b082e26ae12b6a1cdd6ebf373493955707ba6f80e6d346688ca1560" + }, + { + "public": "8e53f71115d5e09ce8260412f5c16dad0c5fb0dbec251b96202540f6f013e25e", + "secret": "309ebb1527aa45a1f25b1b13dced51b6b85896f96ddd11c5ca34af7cdee67bd8" + }, + { + "public": "08673563b3767053dc2e65e58dab959813af5c4801b9810bef27bbee95d471ba", + "secret": "8637903955725beb0aadd21a35014321b6014bf42704bcb9d5e67de9d7b13364" + }, + { + "public": "10be59337f7508e5efd854e5f335591ea4d51a6393193f626607b71fad009df9", + "secret": "0b37ae35fe0d943e51f753b35daf3cfd1d63e56a37f2ed124947fd79a4cd77e2" + }, + { + "public": "d9bbd9338603b8812d09c21adc7c0c9387515d92d70187831adc22e8f0555797", + "secret": "fbb0869bcaf70dc9aaebd2ec58e26a39ee21abff118f714d87e3fa483d34a853" + }, + { + "public": "dec676e8e948f19990ebf4a4166c17ca89d2e08cc7d61e8e2641a5c929509cbe", + "secret": "26d96cf5df2939cbbbdadc2ffe25abe903770d99be789da6e751415c35abe981" + }, + { + "public": "cdc790038d304cd52fddf9deddadf918c3f15dc2e64854cd761fc76e1b87738d", + "secret": "f557f040edf96277f1422933859cb5f2da6452c0c4af062dce804604243b843f" + }, + { + "public": "3a15f9f968186c54daad69d6b270252d4c6e164301d82103a636a3e7f4070a6e", + "secret": "e65eb12789920d93f36b489656f063a955280f28f998fbad5ac3bf82f7735c35" + }, + { + "public": "612cab795cb054b435b775461a5bba6e1081a25f7ab449c01cb5d97a2ccb19d3", + "secret": "c315e82e0cccc6f37574ded32e071409e77e6d849ca756995d78c01184f59a65" + }, + { + "public": "ec66eb8d31950fa7f41a8d244a552e3f5fc57642cb838ffa200f8c0b6f3f9e84", + "secret": "750c6d0d0728cad57ca686b439729ac4237298c240bafd4060e0e4050f46a7c0" + }, + { + "public": "174a3d6427661e491cd4be6060c884160de8bb759431996a20ac9a468bb6ab65", + "secret": "a3aee9bfebb11cbb1322155b1fa3529317a14ef24083a40ba6015f37d02cebff" + }, + { + "public": "53dcca690add64af228d173545e74623423c65c08402f3fbb5c64b33f8c239c5", + "secret": "dd5c041ce81d9f243914b631a24ed4c016f7000a1b1cc4950c724302b0087d78" + }, + { + "public": "3a15574985d9c060e023dca8592865f511db3d3fe8279591113e431340fb0474", + "secret": "1290044224e4f2dd35969b6beeb7d514a77b66c961c8885b8842a5df002f9835" + }, + { + "public": "385cd08332f594078a7d970bd0700f9b1889e37c827cc3dea520ab4c4b8c4bd9", + "secret": "27d402cf67593c9021b391678c3bbb306f234c2cad2239ced2109ca29851161b" + }, + { + "public": "2061291d9a0c41e4261dedd9a695b5a90d4b172bd1c0ab9f7f74caf6f75eb705", + "secret": "efbb306fb1f5855cb230718103118de1b6422ae36c3ffd2e1ef58323bd920e7b" + }, + { + "public": "9a8e483f46224aaa0bf97975663b64e6c3c25c6e1f6712a408804067fe24363c", + "secret": "b27fa2c64fb388bb0dc824b758c4b6119bd1eaf117c7e4bdd621e6a7565c362e" + }, + { + "public": "9492790b21ccb957ec1b5ddb2d4be27febc05dbf9c389bc2bc279287d91a19dc", + "secret": "5ba0c29a61cd2780201cc3dc4adad4b88f697f165397d40cab4478be23db103d" + }, + { + "public": "5e0eaa499ad1e2425fd7cda4283adfc84f50aa7d474985597536689efcdece21", + "secret": "0db699da1da5c518850ba078cc2ad9c3d1d561f251fea7ffc1b30a40185be5bd" + }, + { + "public": "e54faf50a0caf1d363247a45352182b516f6a126b584f024dfde8ab65b6716b4", + "secret": "e188fe3a436f9ad1dc58da31266778fac8f1db611578c200e2d32ddc0ba5db4c" + }, + { + "public": "762de634fff79b996fdd726ec1d7a207d5298c151d83cb417d90ecce08b10e0e", + "secret": "6167236dfb7fdbcd67ad47f7819a315a3dbfdfa70215a8283b344cd4f58d814e" + }, + { + "public": "657dae0d11b4c7694e4ba35a2fb0245afa0fa508bf9420272778379759c9fe53", + "secret": "3490e4a468f501778103196d564c04127b8a4aa7bf364afedd227e4b17a12fd9" + }, + { + "public": "d5e4e34a002e3db3ee3f1c3cb9db7ce1350422c7744e310cb18b755b38e622f4", + "secret": "5ccb0dc602621510540c142e7a8f0d8def5cc41668c08f3a38fa5e31da182e3b" + }, + { + "public": "6632fe5081b8d046b80017501bbd792db7c2eeeb746c1c8cce9120a1146b6c96", + "secret": "56f9cc1ea1f9e4d3c56cddcc6d06ad4527d80685ae3cdb29111b2a298642c92d" + }, + { + "public": "a641986fbdfe5d14ffc507e15290d41dc5c6eedf39ab4751a1a65e4df4501da0", + "secret": "fc619ef5f19cc0eae6430c1fd1901854692f7b1933c448a01f014bca58bd8c2c" + }, + { + "public": "955b4eaf92da8a354190d861a6c91d9ef5fdeca8353ce2cbdda853f517a79523", + "secret": "a7cc30d14fd676c7c2d7a76dde52203d212358f14f4b9d18b39103756e9452e4" + }, + { + "public": "79aa94afbc52874a65c01dd8e66df803258e61d706c083fab0e1cb5f0be26377", + "secret": "fdc55dc028b1c0b507734c4cdc407d12f395c356f5f2f802661a026b42c01d6a" + }, + { + "public": "c349eb27006d0ce9dd2d5df17cd8ddb6820d62689b3d1a888a1e48349366e3b4", + "secret": "dee7f34f55ba8c124bf2b7b9e2c2fb9c0600163b668a417511cbcc29fe620ed6" + }, + { + "public": "f0631a6b85c7f40cfc2bd94f9f688618b4abb689cc7f6842cbcf08fdde669a34", + "secret": "2d5612093bde516e6b5262df51d42097d201bac9ce033318fc7b1a0c32c95b3f" + }, + { + "public": "659cd6506da0ac4df50aee674bd776daf8363801f5da7e9bb512e609fb7dfb9d", + "secret": "572667ef63e0a13386fd8b5f5a25e654ae987c87900fef5e65ecb169ae5c958c" + }, + { + "public": "269c4a800a1d818e125d63c5c498f6d1071e6304de1ca007e6549dc89e723c00", + "secret": "62dee6bff6a1c62a01cf8f260e47432c26b08c3c66188761770c844515f44477" + }, + { + "public": "590b06990dbdf8421d7916fea9f1f4a438cbedbadc64ead8c92d3b332c32221a", + "secret": "37ff0d01480976a6775a366b2a1643c926ab497d27b2433520ce5f8159f4cd1a" + }, + { + "public": "23a8872845111e9285acba5c69de5c6f53b16f982aa73e7a9e88c3a6c7199d86", + "secret": "5b62ab858fdd26ff73822b2e5a76d3d256ee03ccfc5ef72a7036d0e5e418be18" + }, + { + "public": "5f13dd1dbcaac90a1fd9689afb7b54fe13ae87bc554bea89676248f7a36ad58c", + "secret": "87c2b1d348c6ba3576d1901ed1b86b086426db2314fd029948a9d8f25ac0f6e0" + }, + { + "public": "623aaa8c4469e5e142f69404c371db31d0dd4275616c227d36651081eb83f981", + "secret": "f92e4d1d9e8037664430d8b05f3b2b0cf3f61f9104b551654a299f1f5e288182" + }, + { + "public": "b37b5179f36731bd48fd20626d31b9d14622670e6a98a526a300a9cacfc81faf", + "secret": "4d18c1733a974d9e32c1cbeb2c2baf7ac8213e7a18d89ca7c6a77aa10a5d9ab0" + }, + { + "public": "ff24838729e164a7a6e9282d4ddc7f8f9345b9a929e7b8b3df2d93dd455f7626", + "secret": "f8cd0f5c3a33fe6b84f1edda8c4d039a01861321981c002e95413069d1df06a5" + }, + { + "public": "0e61cd26bc5b1fdf6d5b75dc8c3694cfe08df7b737b42a5fab00651fc95a2c35", + "secret": "70e36aca7e399bd2f8dccf1319637922e80a8a2d84893c5dc105ebfd680e825f" + }, + { + "public": "9cd0d5f9b88560423c66867c3d832e0e82f708c893eca7a27b3488f65b9f2a9a", + "secret": "75bbc56f911a2dc0e31d96dd56c026c7986ee091f730099e4458ef24859a2582" + }, + { + "public": "7e8c1eb6fa3c14d77d5967fe3d5bcdba5a9e7a6aa3411056a7593494cfd62166", + "secret": "7aee0e45ca6373522df27884ca79e00639829e40d3c039abedda019f8e0e441b" + }, + { + "public": "65de9b14aad24e47dd8e5656b99cc207d23805ec1b2a594755b1a85620ca59aa", + "secret": "458c4599a5df8893d71932ab362faae0d2b3f737811c83e337c1aabef7b91f80" + }, + { + "public": "89d9003228431cf666cd018a6a960eefe0222ff97114f532ad397530f38a5e78", + "secret": "a8adb637aaa6904c740a5c16cac4245e2a527245b94d45edad22a740d28f0b7a" + }, + { + "public": "1073400df9f97549be6831c132e023112f611ce950b2fe9e16426abcbb351ad8", + "secret": "ea3d86cd44651aa2112ba6a789e12d60b35890c5e454cd59236e984277f8d8c8" + }, + { + "public": "86dc06618c6a255d06fe2a48e6762a01aa2bef66ec6133c27a63e44ec7751121", + "secret": "529298fc9e0b84fdfbb47027721d35ca1cc943eadef1524fe16cc70a06098afb" + }, + { + "public": "fcc74ddda3e6f256a2c9b12d1c8f373fb45601f6b05e417da966c798463524b1", + "secret": "76d01cfdf95d427f4b391d893a8c78267314754bc1755f8809e507c910db1dd3" + }, + { + "public": "d48d626693774b0cea47f83abce88dc42286375a19c32fe71d7a9fafbacf7471", + "secret": "df84812be98a2e1804c61b8e472517615f67bdacd7685eb8c8c323c0dce93f40" + }, + { + "public": "fe9e17edca76c43c0e7ecfb264dcdeb1fe97446d8b96f177f693d143f5302cc9", + "secret": "f26a662f0c70648334a8c841810c8f3b3fc95fe019a84d5bc88fb82a56f45b20" + }, + { + "public": "cc5dc005be3e65ca700f45b7ca893749c35041f661f1845f1bfca29298ed0214", + "secret": "98dcfcf43648adb156b72307f63f9a54acf538272eb877e3e52a9fd867bbdf82" + }, + { + "public": "0f2d5446416aa793cbc92ac25df1f7eb65b36ec3c0ac168eaba08c88488fcab1", + "secret": "1d7c38777560d42f8409436f961a69e3cb6aa858a0619750dedc3a123941f569" + }, + { + "public": "215c881736c1fda44ff0995b9400af93f79b34cd11c70702651282c214c6b189", + "secret": "7cb77240ca7ef84e004af650868472c2faf405c79b4a3ef4f5eaabdede980671" + }, + { + "public": "ecadbd70cecd262156606bf672188a703267ad0325482648be4891867b220826", + "secret": "98a5d709bf169fd1aa04e3b756188d7726726525bfe047b8cbff9b26114ba522" + }, + { + "public": "bb61bd676e8a5d4c4b69b43de6b5e3bf716299ffa97d227ed1ea787c68c2293a", + "secret": "50182b75c8a930a452894856a741441454df515b21eb599ed99b623b56a941d7" + }, + { + "public": "5c05ce4b0bbf47a909b48ad5001feeaebcea6238e5956a9a1eb9c886b68b8b72", + "secret": "5582ba833697b947735a104b06776bf8d32f19ff3b6ae693d5bdaa07359cfbdc" + }, + { + "public": "a19c851db427a7aee055d633383178cc825fe005330361f74d42a85501fd3e88", + "secret": "e0f1be1213cbecd33a6131fb1b3cb392c18f30db59e9db99cd4e9534f5d0c1bf" + }, + { + "public": "c1710ad6a09e3343526ca83084d5111af2ac89c965325782a2e4df7aa88f3c75", + "secret": "ff36b642c084749f123322cb3aa77e95f09a07bf968669de8edc38cb8eda52a9" + }, + { + "public": "7f9e7de517cda6ef53f1ddcdecfbe5d6e04c4dbfa97515f6aa9c6fc2d3054280", + "secret": "67ab25c2476f1ab7cca69a8cc80380d7a78a54b4c2f19e4b0268a01e0d2a70be" + }, + { + "public": "72b016a933e2b62a870f56778a277d6810f02a752bce7fe134e0c9840d9a37fa", + "secret": "034d74ebffc97db58c8c7e64439dc9b4142fb629bd4199456db885c719ddb67b" + }, + { + "public": "07616e2c2eadc9be597ed9a1c823fb11f6fc4c59513cea0f414b4348b995d971", + "secret": "737d7614d0e2d2b5ad3f140d471c5fec21a779fe76c52c85bbf622e5e538cfc0" + }, + { + "public": "b5214b37f2d3e055dcf72eb97fdee249e6db923f9f2317f280f7339b672ff0a4", + "secret": "535ac9dedbf881cae53ed59362140ad05c8297f0695f67cdc305ea8e87d725b5" + }, + { + "public": "d4589033c919d9e04dc2d2e3e140c98f794136ff44c97006bd299b2b70508269", + "secret": "d55fe333822691cc6277ae49e70f19ef0c37ea6352a6417105574959f291231f" + }, + { + "public": "bdd539a53e6d894be01cadbcefa28b0ae953768542e4f076573005bdd884b1ed", + "secret": "1f1b4690bb6d1896ba0e3bf2682a3555a42163fac5eb3f1415071a9a3745a4ed" + }, + { + "public": "7729c6729fc864309b8d5d9cb80b3ce8eebfd48df349c52acb52bac1722da756", + "secret": "816a637d72df8636abf613969c16bf7593c7c1d91d5f0f2d616cc52114fb4dc2" + }, + { + "public": "4c457da03f3522db8c1692884e0c49a79c51e5dcd685525dee5fa2c2cde6470e", + "secret": "a0ae1ced08213ef4ea6b758b1879f1eba4672a0162c3442e2d852807083607c3" + }, + { + "public": "81bbd9552c7ef759d05a295fc4a1c9a3c863605737e34b038d937bef09f91416", + "secret": "b54370543db068d55e5b3764291ce888e24d24263e06669c56a1827e4e236892" + }, + { + "public": "143e8929a85aa4e2864b15657115be9f930a34cc67a983833335a7dcac4185f0", + "secret": "a8fa71c66d3b4e2868a7b4f95bace9642563c30cf62560d930ce5fc752b3ec2d" + }, + { + "public": "32f7d06090ab43bcf577722ea58b824df72f6c9ab79ac89e42232994654e506a", + "secret": "fdbcb3ebb07c872b992571a2c37d4d1db67b78860a5a630f41420eba58ced932" + }, + { + "public": "84a9fa5405f6701df920e0e7d4e55504492661c41d901dab50471e047356a2fa", + "secret": "81f51462e0b1d5af397fee4c6cb432f063239b9276770fcab09d724a0e858167" + }, + { + "public": "53517a27ac7bd44f02e3a374c4d0b8cc9d948b5def4f8c46de5c2545717a3171", + "secret": "37d0b134925c9a4eb102c796e56d032af95c986e76be421119324098c85b5efd" + }, + { + "public": "8aad015568568fb4ddb0fe49e8b65f104397e353cab65ceee5148439fa1d5cfe", + "secret": "b971a1839a4cf5671f3e7eabd00c45f0d1c0de1f11127afa695c495c97678228" + }, + { + "public": "e6fc3ac2a6b28ef75848ff53a84b3d0afc85f7d6c40aecf176469346b51aaa58", + "secret": "5a9dada3cdff05d10503128d29c0e827493b015c14a0fc792e91c9cafbe070a5" + }, + { + "public": "b6ff7eb76a42ae86beaee6188fc3932aa231e67b49dda95016e411c996ca55f2", + "secret": "6e7fde96a0923df9ca412ecba5ba658d955639d64b9a8096e9c3b7229e7fd88d" + }, + { + "public": "7a8a061e9d2fd6aafb1af68d7329d85242a5235ab32d3a26cf5b6fcd222e10f5", + "secret": "545e59e1edf5a931ff7353f393f0fd57ca9f4c76cb1171f09876a92737f5fc7b" + }, + { + "public": "29754531b26081e612e3d427fcac713f328b3932eb1d6574a5ddc3c5f5ba6143", + "secret": "2b06680e7bec811659e93e5a0fcd62d39cafcd744fecc20e164d34c9c67a0c3f" + }, + { + "public": "0a8a1b53025a6eea20b5d38cef87643f69a907f766c8397683ab96ac3da10bf4", + "secret": "f6d5fddf3399947b2b8b2f09065cde767eb4170af1f41335ce6b2311dda9ca76" + }, + { + "public": "27a663691bbfea3745af32ac5d86c663f4256662cf9da72ded3c5d00915e53ea", + "secret": "b93a2749d4f51fc21fc53a03daf7ef0f0b8b24491e3e8887f2de2e5c429a992f" + }, + { + "public": "54a4ea8c08bbdd2c835ff578e4971f9f910eddb4469392d8f45e8b09c459e8cb", + "secret": "26247e136924cc13743a59e702f264bf7e2edc107122e15f262d770ff5443ae1" + }, + { + "public": "1d586be202b68595bbf506202c3a6982a97cb0a4b677be338af197f7ff9048e8", + "secret": "65bfb8e8c4f732076a2c28ca456b72faa43b14709f178270ff34cd7fa6c8dc5a" + }, + { + "public": "e9eb4f552668f35c3a9851029b18b9fc531c2359fc802fd93e35e395387d7179", + "secret": "7f11095a5e5fd02b1c26c1472a286f54c2d3ee7d4cd831313c7f74d76dca0f7b" + }, + { + "public": "a5faa44e1561305f9847b90b8fb8c939885733182fb6d726e6a7cd1c27afe968", + "secret": "552b7328447e74d07f2bb14f47f8f084164ef25e7845734c54da52f4a9d11aff" + }, + { + "public": "93b9bc50d87e3acfba8590f55ed58533582481820e282dd7826be649aa4ba6f0", + "secret": "96b7d0fb4c5b7491602324f9ee0e669c36994edb9ecdfd9fc56318744d93fe9f" + }, + { + "public": "427859a789f46bfc4824154b49ce4d15b1742b9175ad4fb5ceb6ae5ce631ced0", + "secret": "9cea6ae10d152ed554383915ee168a41f1a3b31e7857ee34c58e606928adeeee" + }, + { + "public": "af53a7255dd5d1e10f7e51af077af640ceee32a24b027121e8493bdf30e7fd6e", + "secret": "157bd78ebbbae3e94ffb79d3b8a93a241a968b42345e8b180b6d4deb53d372ef" + }, + { + "public": "321bc403a607bb4842c07bb78377ae8a2330ec0c39255762f146ed1ed7a62804", + "secret": "a1e2c8dd68a05925730ed382046933557588e14ea89648b80ad18b121b182b2c" + }, + { + "public": "19c194a1a535aba2e9551bbb869f7359141a6c855f2d8183828af1b46153844f", + "secret": "0fcb84a191f5856632fcefbd709b4d5399fffcef97c480146b69ccfe9b88a8e4" + }, + { + "public": "312f9319753a6fe544065ed1ca032d82bd655d63ed475f0ea3b83d1a06dae5dc", + "secret": "46239685c86b67b34e7dca94bf31a1fb974ae0c798f2976689df57ed4868c76e" + }, + { + "public": "68a09f0a0dc98562e729ba456f8b4fd2611c3e9f96f3d45d5d77bb1578be02d7", + "secret": "535fd8b156af1ae4d0ac541b8fbb185f5c21389bc525154acc1f8062b6dbd71f" + }, + { + "public": "90ffe82d532aceb9120d7e914e3bc86e6fa6b6172b24eb16ff36b2c61f826a39", + "secret": "4bdf76d07e424ded8d6e1b47e967a174eb03cc1b57d375d2679ef51923510a72" + }, + { + "public": "b18ef56b9a7a56e975f708195243827eff3461552bc577c5e242eebd8aa56c84", + "secret": "296cf25998b7e71de5d949031f4937c62d84318ebe4d52e36b578ab100dbcfbb" + }, + { + "public": "b0385b247948549ade2f48d9951c3356a1409d3e3a4a3cc8ad5476b72f3cbd20", + "secret": "f5808c0578c7af26f99f6ba2e184b5581b445fc05d01ba095275fe8aa9bee815" + }, + { + "public": "1726faf7b152e20bbb82b7bfd9a9cabf31e936e9f07187fe4c9193e66258f5f8", + "secret": "c3e09f4a97f474464a1e40555189190cbc06902a17615028e82380728bdfd518" + }, + { + "public": "6eb477cbe8ad162d1aa37ef2a8c980df69b8bfd6673ef075ec7f89a04c200e8f", + "secret": "564bf0d44703456742438b3fd6fafbe318891fdc387aa13c8cb316d0fe245be2" + }, + { + "public": "24ae039379d51892227b045c9e9291899a577601a6fff5e61e7f400026e57b8d", + "secret": "46cdbc0de3be944cc06de5ea656a1768c2aea7bf80a91d1ae98f2dfd368a1117" + }, + { + "public": "864d8bb3053f9df9d87b01c87a718a60ffdc90457176653c8961cfe948c3debf", + "secret": "8d2847c5fb86c059207143fcd5ab3245c2a22107ae023f9a720659f6524d176d" + }, + { + "public": "15acd3556e6793867e51195de9edd612e9732f5215482c92d485124a49b20828", + "secret": "8e501459e7cf2cf4d14f6b210875273c20eeb21bcf56037c9f60d267c9f1e0c1" + }, + { + "public": "42b5febf065ecfae1cf0a8a3344a31c1cbae5508de5e0b708bb958b1c592db98", + "secret": "0e6a9ae145bd5468526b4def913ede0d9a08e765274ce136e1b4cbfdf76e3ba1" + }, + { + "public": "3ff8640d2f2c5842cdd5aa4d5726a1d30880957db814d61a11fe655ed416cf68", + "secret": "70ced9095b0b12bfdca34625325de783b00b0517ccbdf82f6f71ecc2afac092b" + }, + { + "public": "87d56d004cb2baca4e5e643def333433fb21ddb363b8040fe909b9ee208734b8", + "secret": "54264e2b9a0e8096677d75dc9e5fe0def60c9340d18b59ff0bb931dbc166cbb5" + }, + { + "public": "5ba509f00e4306961601a6a698a4debd4786d440f551cd4f1c594c351c57a6fc", + "secret": "bf139c257498393d6ce33d2f7a34baf1261b67ccc0633867c8d6f9a6c9fbad67" + }, + { + "public": "82f1f0d2c125b35710a5c395b68db455063f3774a9ec67905e2c7f0c791258e2", + "secret": "836f14ecd683aae2ff76043ab36263f16e27b78fd9d55804ad024665b2aa21be" + }, + { + "public": "fc0bad12b139600eb1fe6b84ca66f6162af37f563c4f6a58f197e9c6e9286acf", + "secret": "1784eef782477d186815c05417912aba81494610c9318873abfae265b039ee35" + }, + { + "public": "dc70349adae6910642e54375f67531763cca61255a52301e9dc131d45e6bcce6", + "secret": "21a8f8f60e888b5c2f14f41ded7b2d38cc3b7c3f95eefb67effa489f72b1cad2" + }, + { + "public": "9837fd2b707204b712bd586489b7355e65851064e19a3818bba695d4e836bb8b", + "secret": "7445b8aaf8a6838f068ad738f3ac1a1c74d3f34967c4fb61ffdc944ea25bad47" + }, + { + "public": "f9ad6193ef3e28388b2b08b3a22eeff8bc559a7d4a94eefa0924e078b375a84d", + "secret": "c5da3bc44aa37ae74af5b7a8bf520c5dd7bd00655a1a3687bed3d2c1597973cf" + }, + { + "public": "4992d3db99569861f1f914f2a138f6e86affa43253fffb4565bba9f88d53ad76", + "secret": "710edcab75608186050aa12a69f1af7036ec7f6a422fde8cd44f29ec6364c745" + }, + { + "public": "b4000dfdcd793ec85d8deb3da7362b104506a8d378c72ec5af8690e22d071395", + "secret": "c79cb97b15480ed699de6081733c70842d87712d28564c70dce1d0c786ddbdf4" + }, + { + "public": "b2198b51a665dc64ee9d8e9d2d5b1f27dc22eb9860fbab0a2073fd4b74ca0da4", + "secret": "a32b2f730c60f4ff11f67d6d07c1b34ff3f5c26ff18d7ff4cca451e972c64c4c" + }, + { + "public": "fd9f10b09bdb1ad666d8a19806d802532d78c24f9c257a26a4aa6f2bebc63c9a", + "secret": "e343f1d9ca01685a4d2ae490852dbd658b11d399aa10affa16520647b222842f" + }, + { + "public": "d4acd5d34f2e77aa6eb82c6f6afe064e6a0fe66bc00738f3add069a7890d28de", + "secret": "c4dd7bfeda011e6f08880909755c2096b500c3fa1c6793bcca6028c67c0a68a5" + }, + { + "public": "bf6d5439446097c2a0642edb904bfdf57765f247677cc46f96b34e1665b1b62f", + "secret": "b32adad3f7faaddf4da5de27acffe2016a1aa8b839e96af5162742b360a7d045" + }, + { + "public": "425b648cd939b6c43105a6bd55cc5266733feb925d90a51a839160d0e9711f72", + "secret": "7e0e4826fd82d78e75138187fef31793234feb7f799ff5bcd13b86c870218731" + }, + { + "public": "1f7d3a2dfa397e800f84524f1181a9c5f5269e498219502373625a18de448680", + "secret": "9c4462d2c9129c7a102e88f44a46ba0c56cc3dc26680f43adb219ae5c31744ba" + }, + { + "public": "1aea40445636b16d573d713458c0923f64d231c756257feff4efbc8fd3c3f196", + "secret": "80cec854516a2ea3880e36dbe539814fcefa1f1149e312faff84dbbec101261d" + }, + { + "public": "1bf289a1ca52688365caa40828f035a155a0737c2da8c254412d411b0a219792", + "secret": "a701fd6f481db7ea75623097e9f01d936937cf5d400a161357a0188700037438" + }, + { + "public": "2d8806d3878ed2dd8f1fd4503888a75fba89b33caedd8d4048734f011a3669e6", + "secret": "6c73d33182b04ff8268327eccae137dd4ded374ba26299047f2dad847b75bee7" + }, + { + "public": "f152f38dcbe93d6c0c9a378555fb1d72ceae98e2c436c8de77aa50215df23362", + "secret": "07773316025c80ddd81d6f0ebdaf63add12bb6646329c87b866ba0f63a054f04" + }, + { + "public": "883254f5c4fdd34accc4f84a093f5a871447d11e641ad1ba9ac0be35cc496abd", + "secret": "db3035c8594adc1dfccb3b23a33ed5168ef3efe3ec9878841a71ef2936592674" + }, + { + "public": "402603e48ffca1449493b02ba742ead31935ed8007bae9a1ab1d7c96b474fe57", + "secret": "60083a3f4b5efb0e06bd80be5a7c202202320ef046f5326bd97a915e0ebdea8c" + }, + { + "public": "7f49b5b27befd46acb865f93fe490a8210ba65efb1040884429bd65896af3ce2", + "secret": "9d600e73ed6844d701bfcf8173cb31615c6e5eb736d05c5530d8b3e2d509d597" + }, + { + "public": "ace5807ee11b9b324d5ccebcf0e6098db465dad427481581ad9303767163eb41", + "secret": "3258fdd5e648ee3ba55ca34df3b8319244d43b2ab62e1f7436624d46a8c5bc2f" + }, + { + "public": "ede389d3d2b1f3ec6160237d86e97ee73b7cda760211c98edfabfea64a11a7a7", + "secret": "891f2436fdeff49eea25bb6efe5c4feed7bbf3dc9da50ccefce112974cd8648f" + }, + { + "public": "8b3338195c71962f67f5067adb4190cb1b569b501a841b43ac39720a0a918add", + "secret": "fe42e37aaa11ab4b4ad6f0b38b84b95517266e00340699a0778a38f628bdbb0d" + }, + { + "public": "fd1b496101413f52b7b18c69430b4c5f2ef883c4010acbce9cde74861968628c", + "secret": "ab725fc0fa61e672d215603e017cdd181d8d770e03ad2ee040abdeda452e9e0e" + }, + { + "public": "2ba11d1edc7e18f79a58aced050ec3e4e2f536be4cd6289fa96f3058b5c8c7a2", + "secret": "1d01a184af987ddc8d45bce815edbc5a879c3e76effcdfd90c1c50f1567da343" + }, + { + "public": "0d3fd2da74378ddc5204fa69acf203415d5cff2948ab650d47149c7e561931b6", + "secret": "f56075df62cd4600799cb1ad00292edddbc234d921945dc8f8ed180d01ea9d25" + }, + { + "public": "e0497ba379ca7fa0cd941d22b950f72e5471e1c03344e2c6b68bb78277e535e5", + "secret": "015dbb59bcff87b25c50e53c1b0c4a8790ca537f016fb876fb96ea1361a8595e" + }, + { + "public": "ec885c6d757a0020fd5e03604998930eb4e5a781642f05b8d8368568c4d806f9", + "secret": "ebdba5ca4e9ce4bdd15cb38af79ff4077defa95a121bb92c2b4d5ef51090a626" + }, + { + "public": "d9aa7e0fa9c7a3eeeec430976d1479ef31e6103a95245ee70089a338921626f6", + "secret": "a5ebdf2b20d6590982fa029df4f90ef527e7a2db100a54ae5b78fc0bf06851a6" + }, + { + "public": "7b489e641ba959acb50bcb5dbb850a7722ab019177472132fb7a63bb83b2f71c", + "secret": "1cc1f25a812968353d6dd73ecdad4dac5f87f65b4136269ca7a04a1cf0388677" + }, + { + "public": "95ee6338b6d4abcf5aee72fd691c4d9d1f422ad960bc9dacedfb2fe8ed6dd0de", + "secret": "3e06be7a28ff8c209e515d9cd8d738d3d89b167ecf70c11f79d4bc12cfce8ab8" + }, + { + "public": "62383a166f76c0f20e69b1bc4a813cfa7e2e83f51a788e66f866bb967de4267e", + "secret": "6aad85049a0f0acef95419d32d432ed06e8f32befb21d161660ce91edbb054e7" + } +] \ No newline at end of file diff --git a/rust/tw_keypair/tests/ed25519_sign.json b/rust/tw_keypair/tests/ed25519_sign.json new file mode 100644 index 00000000000..e621c8b71d5 --- /dev/null +++ b/rust/tw_keypair/tests/ed25519_sign.json @@ -0,0 +1,1002 @@ +[ + { + "msg": "", + "secret": "8dd6ef33da06e96c722b1713d8fc37c433757f0a8e0473298e3b47626d7b6484", + "signature": "2a95145e7a7e1a8770ff6dd1afa0d022a5778e63ab456d3f43cc51e124208bdbbc8558dc882e31987b7c92cdea56e3844d6034fd7b8a6d1db066f931ab0f9e00" + }, + { + "msg": "c6", + "secret": "cd2441869d0ec2861fbe9d4655c941d9f2af83d53397a12f48342d2e6edc9f6a", + "signature": "9ec01c5a24fcbdd97c1984493c5da4593efb8c70e1e404b44c7f7a5a6b6ada26f42a94d33dff3fad900520093001a00b2057acfe8f4ef946ee13830e62dd7a08" + }, + { + "msg": "d016", + "secret": "04b6ea7d9bff177901a0cd4bb942d7b60de4eee9bf7eb3e90b954c8baa189c59", + "signature": "93cbc208ab2cabcf74e3582fcd0a2c1b6c0e65291a031ecf155ce66323130dc4b7159160a6af95d2d8036e30d7bed56726dfc9872f283fe23a0fd7076ee2e50c" + }, + { + "msg": "25ea67", + "secret": "5b756ef685ebb266b8d12416cf7fb2b4e7059fe58c4acab14f350dc77edf0b69", + "signature": "88eba1f7022b62b18d796ac76f88747ca689871153f1889aa1058ce85a8c1def71e1f4239dcf235e126e5225deec1c348b120a6f9c9c93c7da287f7114e4a905" + }, + { + "msg": "82b059da", + "secret": "e29fb6866116f07c05498b16498ce46f27a95498fa0d39c18f437abf413cf852", + "signature": "cc55ca7d0348a0f6262764fa2fae852825dccbe2e2b84bec21d81397a90f914c35f62ff3e02da08946114498fe5e12a4e26a0afa8ea12f09938f9c0eb1d45b0f" + }, + { + "msg": "0a990a846e", + "secret": "7567121d546f346f1fe93af5bc07c0d49f3395227b09e745d96fe4d2c22f90db", + "signature": "80df17bed6eab0f86c7fce7cf1c5fa9e43175effb9a13433d7f5d5d39ca94f127ffb4f000f50f77b06a05e8b7170df7c0dc2054214438cfbe9bcb1c76125d602" + }, + { + "msg": "f0ce777de23b", + "secret": "a5f7954b491df49c06360c409dbfc05026cbe8f07818ce55213ece305724a628", + "signature": "2a9905b9b1be17f2be27a66f78867dadede437ac878cef5dabddd8c52e1f5cf2a8fe7626935f2353cfa610fd3d46289b83cb7f87ce2abafdbe83de7c10f91407" + }, + { + "msg": "62ee5f3e85697d", + "secret": "f4daa2ef4a3a3a9179b92fb707a0675bfa3a3791c0594159bdfa6040d7085dd7", + "signature": "ffe772729a0862b575368f48710b1b22d7936b9764f101ea3e236db236ba227169e808b758155bceeb3f1e8d96673bd8adfd0ff49b0447a54330059501816c0e" + }, + { + "msg": "6c8859c7c376c1b0", + "secret": "64a5c2122e53f543318493133fd94fabc8e0892065c4a4f998c368386ab7f15b", + "signature": "b259e9a976ef5057e1336e0a20a01f8dbaf973844b40498a7ee3fc51296faf2c106c93cdb8528c6f78b49ae5c8a9789aa74b84a58105e49036599e29693c7f03" + }, + { + "msg": "5c839bcb6e1d5554c8", + "secret": "319c5f0e55aeb30d0db40209a17cebea0b85e6e673dc3df48fbeeb0344c423a1", + "signature": "a6670b2fd23c712dcb2930b269cf8e2e0eb9c75c818cbf31c4ed08cf0a979a5d509939eddb7741606cc0d6ac8ccb5aa9370939837c10da1a16b44751a0663c02" + }, + { + "msg": "0d2e1753ef5fd124abf8", + "secret": "63ba7c1fb49733d076f71ae349c985885c89cfd94bb8db238c35ffb4c0ebfe68", + "signature": "fa4a3a9e5ebcea816d91a94e303815af987601f8cd9e5e8310a1757c22233f21f3096327087f3a58931c98f34125646075f39e976880b6f5db29203163315a00" + }, + { + "msg": "875f76161142c2b5f34a59", + "secret": "227ca4d42a516e15219f532f963d7ddeae54af93376c4834be00c26b9633af23", + "signature": "01927eebf7dd6726a010cb97ae7970b4e305e1375dba190a5b3e0c21553dcf348dc1a4706e94ce13d7ddb44a6e200cb6143ed4721400622137c9e215a2c0ff09" + }, + { + "msg": "f750ad069452b4c14e2bc1e5", + "secret": "840b1abe5c185b7c2dc0a203a382b8c9053974984a106861a250add2082813ec", + "signature": "b05d29217e1b3d7269bfd3d64495ed613c59e11b2c17235cef77f26cea51ae862322a62491dc5f6213a1ed0b07d4b065b4ac1b86be79b56745691f55a2f1d70e" + }, + { + "msg": "93c7676450a44f8524ff66ac7c", + "secret": "c3966cd28f6355945e663fbb24331ef6dd380db78118e183bd99107b7e3f755a", + "signature": "fb7855e6fd07ed15ad897fca00efeaf00469de5e48a02d7034ae945313323c7142405cbe53ffed871de7fc034e79694ddf6de9cb8fc7e54d53d4e7c856826a09" + }, + { + "msg": "5a6d4d3bd03e43d28eca2890a930", + "secret": "51088d26617f8c64e39f14a5df91e15514099193995096a5ccdf0e83ecf5fc3e", + "signature": "b70af6f932f8daa0a0090d1228c5f7da23a185d233f8c6642d59ded06d0a5bba7a94851763ae2b9c9c6ac333a9e4e030f0c5489001ffea58008d73ff5069df0f" + }, + { + "msg": "bf8ba4db0cb4af150912ea82711887", + "secret": "cdbe89fd5d96cee4ce468a103d9843dd8183aaebeac7d43b6fa872e1850086fa", + "signature": "0cff7eecf88b890368c5565d57c3d481f00f7744702bd517936423b297ce84713571d8032e41f3660eb8e0f032c369efab9ee61e6cba976952a112048ddf4101" + }, + { + "msg": "f142f7ba1a7ed1de48e13ef40f30f192", + "secret": "82794fb0711321f9127d14e12c9e32cd28184fba3133c20fea858f8e3587e5ba", + "signature": "3bc71a6f81e9757ee19abf288478424c2fce64c31a517e4cb30eae98bb48dfd14ef437e11817ba5fa420d88f50372a02a36919f819c190e0ec8985348f5afd03" + }, + { + "msg": "667b4898efef75fddf6d655ffcfadba0f4", + "secret": "bcf84795c05f515c4ff06605d9e28775c21ae83eccca11e80198de65fc54c0ad", + "signature": "cc1a8d06f196bd3c3bebaef63992b5dabc265114e92492e3d2b43da542ed9e9b620fc5948f1bd13a77ad364c9437b2df4fa34b9efcfa99a058030dc94ec40806" + }, + { + "msg": "6b918baf8a8b3dc33be3c46a107e2c9aa12b", + "secret": "081a0a2fd3f63ee8a2f73f25965995cadd85d0972910f8e35136e709f24e2c86", + "signature": "92cd1e04aebe877577cff27218eb4f7a1b0c8e2ea5c6e794e0968bb79be4771bd71d0056c6d369e53fa063115be313ff9e6c05fd263c9e4059b1edd4a980fd00" + }, + { + "msg": "4bf4b9ee844a1dcd330621a6a059312bce6a9a", + "secret": "e47b480a81a6c417ef4600f5fe34618f168db7a1c9fb1b6d5b08c02bac6ddc6d", + "signature": "af7aefc983c3c538bd831f85fba47266154c360ba05c7f4829f9c6bdbdbda78704f6a668c1ba14c96310ed06bb752d87ac2d994bef77b89ecbf9af478359f403" + }, + { + "msg": "7df06dfbe5ab9f88fb522a1c7afed1659e88835f", + "secret": "0a9e7cb7f9eba40a4a6b439166ddd14bdf66a8072391cd3c7a44acb4597fa8cc", + "signature": "f45b24a08e445b67f2f69f1d0e7d287dc3076780b53b3d494f00549c645f7ebc640bf7b2663608604a19d1ee94da5a0aec031cb6078c576de91cceee72919206" + }, + { + "msg": "2a51abf6178066a9260cf62342bf870fa73588286c", + "secret": "256652f61917d25d7d5724feadb1110c9bfddcdab3487e683ba1fc2dec4d1a1e", + "signature": "63a27b4dd63c5ef94dd44737e0d782c4fb58a4db68d3b4a966bc27eb72f493b4b529cb89f67145f98f4672b4746c9736fc8eefa853315b8383f6433f78dc260e" + }, + { + "msg": "81683774ac57f8b8b03ca9d8683e328d3460ba8715d0", + "secret": "71c5b75467ae24601ad94a524e8d0c4ff13516a8e9f05bedb968872e897402d8", + "signature": "f5b27b056cef1db2fe3cc7c24799c38502d97fce0526d2abc0d5009003683d0e998b0594dba11f9a5d2e327716648c1ff215eeb84312c1da40ec4b4cd00ffe0a" + }, + { + "msg": "eeba6aae127be29b546d39439a579058f64e0174a6430e", + "secret": "9fa9bcf42eb2c356bc961370505564bd1a4e918a642921c4e23af582c12dd341", + "signature": "c8a6bcf0384aa050b9f1b67ec08c5e8553beff7d6e512388c20e891878ecad23a1d29b90de4047d918d43379e6d3a6d93974ad2c0c8958fb9d2e52416f4bcc07" + }, + { + "msg": "c0da7aad36b7216e9935d1e9658ff69495d34ade2a8c5849", + "secret": "2605d1a0339841eb2900c700376ce747a90d1768bf8ae1d577c5f10e807dd413", + "signature": "329887e0efe871cf73f79c1dd75404053c173196bab76d9e62cb070a0891ec34550f5d63e92a033358974a241353e1dfd1df8c3877bef31a4c5c46e06658a60d" + }, + { + "msg": "8f6629b7de27da091032757831ae3dda4968e3b040bace8a87", + "secret": "3629760163454b0a0c04184e4c0d2f8a2cf25b60715e57593c240caae4cfa399", + "signature": "5a9a72a1d1cd467911afc9330769460fa6a8e586db33e7f10733b19c73ef0bc98a12dd29d719ea1101adc7f951ca2d255c7742b6656ec4b9b3e9d99edc7dab03" + }, + { + "msg": "8942095c16e6c7bab7e4e5e61d07c72e79384169e0ae9c36bc6a", + "secret": "b365dc7797c0f0728509f33988b8ed9e25cb5a00355354b41eb9efa71a3497ef", + "signature": "e717114f89d3046c90fa87e4615216a8b7b7c0b9c472f259bb3d02cfd3487777b3cf6200d72e98ea7c54f18f2324e84a89ecbfdf51fa6964593c4fab540a610e" + }, + { + "msg": "16d608210763c7bb39d3724cd35ff4b8fac44ed368403e5c6801be", + "secret": "0a295916f24c097a6b5d64e4269820a1b7ad1dad661d98d61095570c2db23ba9", + "signature": "b9690f5c96f1b7129ef9f472d99455c453f6398c5e5fe62f5a63942548dc55fe63a75070afa95addd001eaa15bddd34de7b20a83c4f2908b49d8fd359f152f0f" + }, + { + "msg": "2ebfa288a5ec465ce7cd1b52a999620bc08998e1f0db12b5c68d7612", + "secret": "6ea5f0bf609a40f579893334dd028ce212cec192553834ab6ccf24abf6af205e", + "signature": "3358467a8ae6aa8b4c3c0dd7032a2bb1d0b245363fa1de5b06f24d87eb74ea287686060eb423b4fc8e710c3c0239d67e0b48302b05b2d094720291552108b605" + }, + { + "msg": "7695f72447610e340471b13f65ee3b7d3b9c885d02ac7abcf3de551144", + "secret": "537ee6ccfabe4733404fb328e70c0088414a021b8972e3e6fb6656bc8dd7457c", + "signature": "35294b50523d9e5a2d67f56fa626ef91a5d19e376c2dbd5de5f7d3a61bc0045a02f2a5954040293b399b8125748919b1c3e78a239284b23701dc45655230b60c" + }, + { + "msg": "0828b5242506ee27fc35d15401651429c47b77514908c51b3860eeb7f75e", + "secret": "376e36bb1901ad83d3c56f2406b8e1b0989522fbfc37b52c63e59149d0c3a5f9", + "signature": "8dc3b15ab6a95106ad66eb15990146498ee5acc648a5e2c3a2c36f97e5653e28ba833e97d2eedecabd5b85d10c22029029c82fb3378136b6a2ba0dc5f0a7110e" + }, + { + "msg": "1cb41cc3b2afea37a6d2eece7e165a2637645465eab95621da83ed5bc2a55a", + "secret": "f3da9f295dc166e8e1246cff3433701de5a3a43f166935e474ccc9df008c4b72", + "signature": "b59b731561b0703826291e5d373c0eb19b65812d19866370defed730eb7b76b105061dcf3f2a92b4b7f70906d0f8278d4fac5f4df5a21246c7d0fa1e37bd790e" + }, + { + "msg": "06414d0c258bcd528dbf1b94d2d5c3eb74835f5bcb7c76ec266a7e4a73fafa5e", + "secret": "3a489cabea2821fe32fb7e742fa17969c08ee19863b799e6468441c9f9172eef", + "signature": "47ef5b0e4ffca143df7cbc49a6d408140dc6f19ebef6f87f5b33738c4600468c121a3ced5f721b68cf57929a28d8857a082add82e66ad41aa1ab60c7a272d90f" + }, + { + "msg": "8c0d0a5045dec601f8b43cdb01aab66a439b6061c8e4379aa160c54c5e67006285", + "secret": "2b2949d066b71c54e08a405f3600bd149bf7770ba938ea0e06f409e067b5fbd1", + "signature": "d2e4579a7eaf6620e36f789d521dc9c840a8326041a1b8c0d8db8892f8a25d8785f6e79ef867435314f28c2701d36a04ff75fb02ce15fb57589d602c5f0e8d00" + }, + { + "msg": "bcb13904e8e67309d0a9d3323228c4c9401616c3039b7a68c438b7a67de5dda186be", + "secret": "85bf6f184e7fd19cae6e732c096fe15163c88c7f81763db971fdc98c2f9236e4", + "signature": "efaf4ea3e499a822f00d89873ca5040efb432b07120a3ff54438e685f8c1e9c0a6ac4309a21da90339fcfc5b32a0f08636b5df6c6f9e61aaabb34cf82af8750c" + }, + { + "msg": "781bdaf0a6c9e715f92b89cdaa0452afaba3893196257eba17de9842eac2d7d8957e58", + "secret": "f97d5c2e8ee8504d1cc832d117bccb1551937bb154ce19e7ff58a7a9f56163da", + "signature": "22cf8194e6372b34aa0074edfb7cd615e25bc9e836a0485ad64520a1f377ce804aa7d37f5eb2cb90de065cbb5f453135da5eb2ed6735dce066d6a5ee888f6806" + }, + { + "msg": "37e2f56df27983a2dd29a985807c91cf95c2214acee163baeadcafbfda4ffee2d819b278", + "secret": "3cb50f9f5b533c9704e63e405e1628ed44c121cd460729d33916a799072bed86", + "signature": "4ba2165c827288a2c5240dc441a47dda5b2e647a85665d291551012ecf4c5b3fab15303401ae0633b53e55e7a29f52b6f36aff0d5b7cdd7bf12038e22f84120b" + }, + { + "msg": "73c5a4bfe16271284a47b824bbb3bdc6335b07e39129f2067be262a9386fa2b814bfeac0f0", + "secret": "fa3c3499a0302f5102ace36bedb0dbb0ddfd219a4dc4ab15d2e40c2db1249c95", + "signature": "8da03159ee33c146d2ab53d3d43772e888d4fa48f757894889ed3e1f72d822b093bfb03adb5b6c9ae24bfbb6df7f6164cdbe590e49f91c32f31b8df223b96c00" + }, + { + "msg": "41caa617505f42c1fe1c176872eee594842ea18e56736eaa456f1a4c10c0dc48e9c6b01c3856", + "secret": "05b20a53f66f57c09afcab2ee77fa847b09c7412c411c9861bc8a2658174e6b6", + "signature": "c4e8dc971bbe6acb940585e3e5710cea371ced6ec5944aaf0d63f59d982658fc5f913f3db3d3f2b9acf4992271bbb18f5a726172d39471fc6de35dda8cc55d0d" + }, + { + "msg": "011b110540b5fe4139111ad6ec80609c066f6cb5bda86edc90b0405afb90445368da7d93cdb556", + "secret": "18cf6cbc093a428f45ba21d1dd84895aa8a006cd81120e8fbb8268a535a44013", + "signature": "bd1a5f7826751f6aaa5988941f8b24c51a17411ad9f214ddf7430b0e7f4bc06ca88afc9fb6124384b4823a66d1b590519dbd96ed01fa2527da4064c97389770f" + }, + { + "msg": "2280f940fa3977a43a0ec79962c54abda3c385c53092fa96473ddec16135b1e95276ee83d7eff433", + "secret": "ca4040dff4fdeaf19227e5a8dac943ed903e82403fb34cefdcb5f22f92299ba3", + "signature": "67a7f3cc6bc85d9f6f8a9ab098e636bf67db25a6b332ee34b844fcf78fa3d36b5190d3ee1d8abe5bbc779746c874f3160057b5c8a92d4e35ad3a4a8845b97405" + }, + { + "msg": "c8a687a4c16b3f7ceb409ddb23266fb4e2b01c83fc749f46167a5b92060b13c96df5f210c9f1cf3018", + "secret": "a360f9fe7246ee3a7d77fb374d1354f09237ce7be187783b02c433fddada023f", + "signature": "9ca7fd0d092db2e787d3f1b1980cdaa76fd7c3b1bdc00d255bd5abb9e322a5eef97ff8a66089b4e52246ce8d253f5cf5e0dc79828f14b6da9650e733ca6ed00d" + }, + { + "msg": "66bcda10b96fadf1d5b296d61791822830255bd46733c2036fd97270054da098cfeb65bb706c9ddd7756", + "secret": "68dc1dea4daff83e3fa7fe391fc641c0c97877a7069ae429af1c099164a913b9", + "signature": "eec7efba92fbe8048413b1d30da8f1be96f5175f95384f384ba92458777484576f7e0f01ea0ccc94ca743a85c89d7dff158e1a768ef0765fc2ebba4298ac0200" + }, + { + "msg": "78098fae674987675e3cb14d8f99c1bc1be8337fddacc7beae03cd4afe8d096d6d507e12e4d1378d85e7df", + "secret": "8cf7fe80c7b47bf7314cb37758ecbae100e025accb727d0f3c13b0b4da857901", + "signature": "a3bc01f31c07d93fe5d9f83e9c1eac7b1c515c6f317b89bc8cf20809efa3539fc38325b11b4956e80a2c04018f44db5814ea3aa7f089b5633b8dd9b714d0170c" + }, + { + "msg": "7db45788457232e289926f233075fd58a9cc6259c869d9b7c8901c17ade364246b2b20c35084b67fade6974f", + "secret": "318c522d5d435e45697197ed84c370b6d736f64099d0799db226629f6825b099", + "signature": "28753cab1a0696e1d69aac69a506c61daee218bda03e68192456b4f2230ff5f6149d421b74a5cdc72733d4fa42e4eaad56dd956100e60dd13d5f5c933e02f407" + }, + { + "msg": "151ebf6e4db9ce55dbf57202be2336aa846927ea551659a8ebef2f558d47c8d6466255b21bc1345f1b0e6fea81", + "secret": "4b7208db8825c304c3c1f4b880321c8d71108fc1b959044caa626b75c8ad1274", + "signature": "ec867e996da0e0e75474b5e6e765786b05b0a735d00fbec0c3152f373f683064d8673a3fa48a2d87f1b5a24f1e269a9eb1b4982ea892b09855fcc2f1f306b50d" + }, + { + "msg": "c246712c79cc804213faab3942f7d82d800f6d987c1be494ce0da4a0e978ba7f828dbce039527c41dc4b48a644ec", + "secret": "39ece05d86a5f1afa50013fe7b8a4fd2a106d4bdb32225afa8d629f79a316fd8", + "signature": "125256f67663cee6e5abf2bb8735a0673f7730ea576922f014556a20d35f3ae87c2fac84edced17d0d59120c6f2490b6ae10228c0d5c8672f9ce78d05dc21301" + }, + { + "msg": "38badc92e0ad6d1f5e8e8ab18ba94bd780b241f732796df585f1845f53261840a8dcdcdc6563e8356f008209acf1e5", + "secret": "48f799a31d8ca682f716ca3761b85d778dab145e40b0dc0858f62f1ae7b6f6f5", + "signature": "09641c50f14e5485a843068159fae0ecf5bf12d0370309a2b1b173fc486004393f4b91229a14d8a359b1d14931d6219f487ceac38846982b0cb1222a8abced0d" + }, + { + "msg": "db31b85435d449c945f31c79e62a7aa411e1b3a8e689d5fea52c0a3031db5e97cffaae524475f787f9d2f57c0d4297d4", + "secret": "b55fde1435d14c89734d2fe337c1ed7e59bf1c9bc86ca427a7eba44b9a7eb1a9", + "signature": "d7498c00aacdf16cc650ed728c2f2264f38c16f2425b455a60a9edc866b3bafce2cc6c1b28600235919454a70cbe0c6da079e7420a6e6d89d0b3fd6295cf7a0b" + }, + { + "msg": "85c2d8d5cc2c2af471a43e4e748c1a101ff1b6304bd5ca9300b3f25a9745e4cb5975a954cb5867f759b1ae1aaf02b409e9", + "secret": "c3f0f18bc37abec83d8bd9440c214c87e550556c27f64e3a7a0d7c2153a979c5", + "signature": "8beb901d635fc81964858f9332294d42ed25da989024ba80f7e55e98c11a590fdfebd1827b0fcd3c9fb2636e71d2bd317c6c1fbf1468edfe7448d06db702020b" + }, + { + "msg": "75ccf68a025bfe06077b60736ea0e9e0d98db383b08ed73c31e27a0476e81ca5a683e7a24465aaffb5b1d18cc66729388c54", + "secret": "678be044bfbca878fe43ac3e4dea0a265c27b1c178a6f398ae1e5ebd31c0dbe2", + "signature": "cb67d9700e610851425b360ac4ad78db451d3cc90601a73ab5f897aed9cc41e4cbfbef7756c0d80b77105416efd4e75247e9f7764f54eae01932ea65e1a56004" + }, + { + "msg": "43", + "secret": "54d89ff3223995d76d1d798e50a81003d2c7dcc3c5e9bf508f1b77cd39e0e6a8", + "signature": "f0adb50c013eb43e44adc62bcd77a90951c7cdcabec08c51dc69477357fd98e372086ee3e5801a24b231a0e65cdeacf3ac7b609497b6dd68e0d3de06a7b57401" + }, + { + "msg": "0364", + "secret": "23416d7d29e191cf20c2dd470193708bf55824e595079cc45e0ed9d2cd450191", + "signature": "bcb7351cabe1eabc978929ea77b70d575cd1fc120dd3ad78461d37b651807a25c0a6e9e60a8e35194582fcf93e2ad63b0e3d5ae0d8f6b5e25a06a50342194206" + }, + { + "msg": "ca4bce", + "secret": "b63e2616056ae9b44f70e901acb2c4307e2f262c098102bd7a4713db856996b0", + "signature": "041d0864b49107406b0a5ce194260467098b69f04edfd23792f62ec02376d794d45b5adcc99f37a9fc269d182947350c3d5b8d05df1e101b616540324aaa1400" + }, + { + "msg": "94c48be2", + "secret": "694acd5f627f2d855340c52074a10ba2c8c7c53e75868cf52db85f437625b052", + "signature": "13a3c48af63d023cf8937385af036f8f3730b622f77a97cc4a71452d27932b6c62f710c34c5161773090cf5269ee8cf515f8b0f34e1952f8f624380e0c3f850b" + }, + { + "msg": "bfb861b909", + "secret": "a25499e66880ca20512ff445a6ca3cf16acd369b630196078cc75cf8e6cc9fe3", + "signature": "2bfbaea94bc1dee8a967a09df9e3fea87aab5ac1115ce1fd9a3890d5c2f3ce75359761b5740bf00bf613a42ca7b0cd93bf89dff9291e1bcbc1170103631dd501" + }, + { + "msg": "9b3dccac151e", + "secret": "3466d9e35bc3b1135a563892624d08b3f6ee86b8461bb1209651634612e752e4", + "signature": "8c2272bea4c80e68a2daa33e636c353eb5e11b2f6c7b32ba7163755b5c7a07ccbdf36cb35e8c058f69a17a0d0203137aef38f3f5af61417fcfb48fefc30d7103" + }, + { + "msg": "21e978916632c8", + "secret": "f2d9eef29244041afdea72c745d0ef18a440e4c7dc1b011a8532af0c701fbbb3", + "signature": "3509a8d235fa4c7160e439cfe7cf70f4b49a618a39902b49e6e19dc724bc7b3dae06e71ccfb72857e4461243d05b495fcc89cdac806c40d007bea9109bf6d60a" + }, + { + "msg": "25fe15e7c3690a7f", + "secret": "a1249e96eaffc4befe10161e6c74f86c7fbb3a8f534f26adc4ed99e28a8b0950", + "signature": "fcdaf8b18ef85b3d0a38eb211ebd10cc70d8e7a41eba00c1db856ff6daddf570c85d5a55eeceed45de90b2e40d9f1544c6b77f964b845c6c3cc9856a84357e01" + }, + { + "msg": "2dc63f311fd602e106", + "secret": "f8dd1ce11564789c3590560a7d48c01dd623ad5da81c19af2f2363fb75f9de5e", + "signature": "79b6db105a3190bc2c59c3c2c8a0cb035b68695667f71f7a660a8318d81eedd3d0680ad7ba0f14f1039dbfc3dfc3cfc4725bc061446464a560d9c091d7a1df06" + }, + { + "msg": "4a0f07c45df858eb4a1d", + "secret": "a2447f54efb6e2849dd26335a4596bfd8eaa796a281c59ec0d3ab5906437e33c", + "signature": "62b659006e7c99c05963942585bdf2cdd0dbb55237ca97eb64938d3a7f93c521b0267ee8213047095957ade55f064305f150980833f49dcd6230830c373d2c0d" + }, + { + "msg": "2a27f24b7df8a0a9a717dd", + "secret": "97d09512c54ab27a0164ee3a6b7f16280b1922b37a974cb5c247c71b549ad90e", + "signature": "aefcec59d7d79265e40ed9c8d039435b1e9562b986f03fa150d77bdde70ee820db5a1cd3ab781cf28aee95a57ddd183148e61d0c5830b998f2e13906929d1e01" + }, + { + "msg": "74a38e91890d5fb09f11a29a", + "secret": "79b323adaca0bc86c1f1725f3b9da970c419fbc40f4ef8cc1d4de7cb1415653c", + "signature": "528956fe25ce87f4b2b91d4d44e7f19a8402d75aa8863a80c66dec01eaa92350a3fb4ae5006e46de527ecb6383d772d44dd1a49572592948a80ec21d76c9a903" + }, + { + "msg": "709e7a14f9a3f4297adb78b6c1", + "secret": "011a262a6bd202363dad9f3fb901cdd474bce7c459b7c10c41330c35380e8612", + "signature": "5bcf214b4b18f08d9655d0f3a6daf708b28a18a68f882c52e85a9a13b3af26291acdda316dfb9c977007b648dfdbfd8675ebe41ac9e2ee01a3dcd1b15612e206" + }, + { + "msg": "ae2f4fe6a5f6d374665f7f3b3fb8", + "secret": "3b1a8d1f2c0fe6baf87217b5d8d7be2efdb3190507c32da0c2789b48c2df31a1", + "signature": "407dac6b76b005d7cd637f16cc6feae4d0a18db2de05c024a03cde993d7b8fe8f97dca27f7a6c7d93589fe0385650ea5487f34092735f124becb1b417e9fd901" + }, + { + "msg": "97c4063cb4ce9b3c09b780b8dbefbb", + "secret": "0c82fde63e277c4aa195779e372428d80bdab2f6a1f7bbd25cce0415f1530b9c", + "signature": "048597cff9613c1489b5276e2f84fc6e76a97a0be22d73a183ba61a5b089e11f578de0af29d638d44f52ed5e43dce4dbe52e2c3dfe9268700eae32f2003fcf0c" + }, + { + "msg": "5cdcc41bb1149f30e2b74243fccb38b8", + "secret": "a67c59e72448b689d70af45c67409bd8a3b6ad8e969f516b437b33009d851c0f", + "signature": "7781bf3546c1744fa4c45f58afdff98635acb4b7eaf7e7aad67702b026079f3a16100bcdead6ceb6bc35ef521c5280527345bd8e4bbfdb1b54119e79e435e50e" + }, + { + "msg": "c3c18779101e4eef980563fb6de790a391", + "secret": "44be64902fd6a48e6acabdc0a7c63a01a2a39835532be92e010dd07ab37dda7a", + "signature": "9dd86307d0babaa8c3a062ab9027d7f1a8443773428b554357cf618951738acd4e4cd373399b29d3efa9f8180bebe2484ae2184a2f70cd3da9af05c899742809" + }, + { + "msg": "1d855ea0f6a73dc545b6bc7203ac25bd7ffb", + "secret": "35a2c0b046a97dcabaedc46e5cfdf8e02cfa6c17fd09460e99488e3bd090ee6a", + "signature": "86235249830c540e618c4f477283542133b05596023b648000ed56d73fd96aeec42adb644b3b2defcb46d48492373accecbcb24656eea9f0b6232f71a05f2405" + }, + { + "msg": "a290dde5b399805ce944f23e19bda7296afdae", + "secret": "566c88432aa96d9418523afcde9522eb1cf6075e8fdb406ccf70247678951a73", + "signature": "e525d95a8cd4c8c75f78aae91d91c2c328137c044cb85d41715058186a89a0a090d76066d08d54a7f9f73d41ad3fe30df7425227a36b5aceb556a36180e55e00" + }, + { + "msg": "2241151f49e6bbfa60fce184fbb437143906b8fe", + "secret": "bba27895cf0a248dc8f5348da5c4fe6ba24b22e2097e76b6b8b2d6481a69b0ca", + "signature": "a4ec13e88d48c960640c2460e8e6806a6f746506452150883c1f53be4b6e3719280cbb5b959cfad48fac808af68210adc269349c5a03feaebce1ed798aad1903" + }, + { + "msg": "1b6c387dc3fab3a92e2355e202d901289d820a0876", + "secret": "08ffd4eafbbeedfd7e56132c3b7731f7808bf0cf622709427ec9c2c6509395fa", + "signature": "a4c02b88f62c81af9a60fa490a1c68cd06eb4e03a89c0b78d72d4bbf4766650662e05ac72f66ca62dfc1fb8246c0737f901453688e7566aef81ac8de7040ed0d" + }, + { + "msg": "0d82a964cd3f161473bebb3d8655dbde6c8aecf1c23c", + "secret": "91979109d10fe33d89f0d2b978b3e0b387bc5aa2ffba452e59a7a817a14a7a6f", + "signature": "7de8f085adfe9b97d7520cc9b37f5a2658ddefa0b634320c0610587ea0ac5c429f9f086122fc0307cf287a4f5f1dced5c10fb6e26b859cc34ce4a3232e48fb02" + }, + { + "msg": "1e1d6923cf04c20ac5a7860e8ca8a7116ed463f641be87", + "secret": "4b10236006c09a9d265d883c2f898afff749ec5ac9c9ac72196b5795f25d595e", + "signature": "a107b46ee77143259d1c39c8605376a37c12ed6282aed2e528dde2bad10ea690f48014a7fe1577a54f276e5d10288babba9989b7fe3eb4566e1642cee348560d" + }, + { + "msg": "17ce2985c575c34c23b40b1382b921f7e6479b687174cb47", + "secret": "6302d80aeb5a49ce7d94e9fc1b889a38be53e6acd3aff95e18cc6175a8797aa3", + "signature": "46ff959a44b2b94c6728107369e13b4c0a1b29e68a99f3a4a7aee52853caa3ac3aaf15079d811233ab0145b5c4cce2e7c840a5ad713932b681d8fdb4450d400b" + }, + { + "msg": "1649fc9f0aa3da92539b651fa296ecc2b6120a73cd1ec223eb", + "secret": "2ae3ab00387b456d7f60c59e9804346448b24d41628b268c817da82f67fdae17", + "signature": "8ebdf5eb775f604f58e5301c2b16e860b7283ec20d9d246ebedc024ffc6ab2f2e49901e29aefec015f23ba3db817b6a841c71ebfcff7c77ea94d8620b83ef80c" + }, + { + "msg": "d75f6e80e1769db59e2bacad0fc81220e129b1e42b33327acb39", + "secret": "b5c669157b5b429a84ede94c3a75a0b90ec983cccc2f853f7e6364f53be8c506", + "signature": "9b5ef713706aeb97b9db048ab0e74092eda548a39693b103e5e635997a084f15b61113119ea62d984f403c60b3b422b847c0fe32e961e4bd109e2f845204de05" + }, + { + "msg": "31abe8abcb2a78d6cf5517a51286274d391c988f181d11073686f0", + "secret": "f51c6e3008b764eb025de90e7882332c76dc69842af9e629c2e1afdfe64804ee", + "signature": "ed3d153c540b2d35bd6e64346e770ddd401adc634c1b953b4fdecad5fedcb2f79f0d7991e9d8e1d9698ae2e495ccb9f135df433b448a17f57aa5852382b9710d" + }, + { + "msg": "c8ba60396421e4e3d1d7612974bbe1b3269beecb4c5a4275a69687e9", + "secret": "aa8b83d0b25819dfab81eaab6bd70600e435a877c82157ffe8f3c6e54e516cd2", + "signature": "0813bfb057df53d3fab832c7be7176bacd0d9adc6286ac69b5df6f767215de303206bd1e3b08f120e988fdd3798ec31a096b4673bc328f8858a7ee9e41b3480f" + }, + { + "msg": "683dceca1a3c0344e8774c81011ddf069e1da61e45ac1fefc28f1ff1f3", + "secret": "18facbfc852be15e6aff08ea7f93457338c7413b4b943b42c5cb682b87760fbf", + "signature": "52227a344e114ad9ddae069a17236182ed52070979635d21063df95d69a204a8279bc9ac9887720ad219343896ba541ffc2a037e60a218c0a9735dc9ae74e702" + }, + { + "msg": "6084879360af5aebb199d482159fd9a3d2150e2b381ec15cf9db8a4c5972", + "secret": "f32d51b3634fe9000ba4e2ab90ab16d153f1005a520e040c4a51a7027ca84cf2", + "signature": "50335df038d2812a40c9e528576c661069fac654e84a6e5c3181d8eb33e027c60611a182fd90d22dbe7638147af0fe9209bb06f1f761234b08e8b5637740d30e" + }, + { + "msg": "4e20838ba703bf1502c7475e26ec14878797ef717ffb2d210353418bc8591a", + "secret": "14effd602de27a66a74ff7db1f6318f95dd75cae60f1fd93aaf83130d3c169cf", + "signature": "1f7b52f16f3d4195a266c50812048cd210d52846a3cbe81612f68e580b3e750075744df32ae436f2aab0c4a48f605d5ed69c7a1149d4df9a0ea4e6359c374007" + }, + { + "msg": "35584956de0e934f616168725330b58e5fbded673ef6712e1e903f1a7b035c35", + "secret": "3a8bb3c33f88b0cd7591febb01cd479ae9fb28da0962e7484cb04cdd1c5c8631", + "signature": "4b7c2420f4596dfc04392d3ef1978ae3b5c3eb16fd1edface32501e14554a7af48b31cd28e18dccaa5d6920e6aed500bd4bd6727999ff62080f9feb1eaf72e00" + }, + { + "msg": "1212a4547e1d6b339723cd767dcc8a0418a23fecd760e817c875702abe63511811", + "secret": "329973cd9426ec5600c2726449f49f9044555d20deb0d445e22e7afc6e45d6df", + "signature": "3d34d3f2b683c6ec21827168853aebd4edeadbf6b96a088436c6f3d373f292462aca7d40c5b3f32c488a4185e6afacebc9f277cd0208f7aa419421d516a99900" + }, + { + "msg": "4a5b6cc4be03b76dc1cd1c30191e37b20656c4b3928d699474c7fcd7a477f74edc7a", + "secret": "bc6d545751e23f3ea638b8b09026513bd528f751eca0f2275cabeb9321bc32b1", + "signature": "8dde04efd3963222e82ca08af356f290fa3f64ae17fdde5cee4edd48a9891291f96d092a4504f1ee3d70dcaa90b3f22f19b4b8e7d0694cd77163092579207f05" + }, + { + "msg": "5cc9a368fa85ac86f7acefcba10d101e9599d7500c952fce14000c698ff270ac16751c", + "secret": "2d03128f2c834a4a0d511acc63e262aba71da4a60a1ac5e21d7f5135bda323f2", + "signature": "901f95738693b10560c28801b1d8e53735f9379b1700adb3547cd9d3d5a0b7239c4d56bfcda5b198dda6112f4697f6013fe4e24de680ac8332ba25570e3bdd06" + }, + { + "msg": "e5fac091939c429e9e0d6b0b512b5224e0bd041f33d5b643466f1a5daa0f04c0a75ea2a9", + "secret": "609963efe10c2607e597161da11611316be1549373a3c536b5c3105d45dd22e4", + "signature": "275cb11e15dffce85958c60537a3c2b77647f56322b5e5a37d3127978082c4765eb074d3399616ce8380f5bb2101dd4706970bf2308695ee9675b87d724f4407" + }, + { + "msg": "8f5e527444f6178502f208038085a94fb2b5fbd1ff2080779ed75a6cba2eb00c4a38d90d8e", + "secret": "e2c35f95b722d20ecfe06d7ab354135d157f28674348808176b0f49e1e5f9c5c", + "signature": "611c756206df97e19f0a59c1baa3932488405fdff87a72927c03d48aa6ca87005220fd16d19b7d228926daabbdd0aa9c672027a14cee9ed33965175242fdde05" + }, + { + "msg": "933d46836b7c3054e19831f8f5d06f8fbac86ff4a1a382f74aafffa99ad83927268942df956f", + "secret": "ce2566156ccf317f0c4785448157be7a1c236206607c134b4bb4c9a782ed530e", + "signature": "375771c2439fc8ade1955f39a3d371a21f9192bd61b7df8bb2eeece0d7e16097f1cf0c9f1007b90f8a8a1278bdf017c90a9313648e5ee9f8b6e793bdc6956f03" + }, + { + "msg": "8892e4c591c168c0dc15b68e22448ebd39307f02e54b1069de807676f1e4df3c2b3faca58ee0c4", + "secret": "6eb5ca71acb24e7efa601ff2d4da6d717ac3c7b5198e31439ace31c51eba62a7", + "signature": "ef2465777d2409a8a34a7af709338956ca604a0c266b777a486a4f8f1786d1c56cb8732e83e9346ab8c42a7e4856020ee9d1d2d6e0c576bdb6f5be7c73f5ea03" + }, + { + "msg": "10c607f95a8a3ffa8279333795759b6968893f22c5111d09426b9d2677961657f4e8e48cd2409bb9", + "secret": "6a7ab95b292ebbbf46b4d8b48773cb5d3c0c3e504b765e96c160c316450e038c", + "signature": "78780903535fe0ec5e8ee5a238f51938df251d0d048ead166ae271862debdb8c13e9db174a7a73090ceef86d243998566b5818cb435c912f3fde123abb50ad0b" + }, + { + "msg": "b4f2255e6deafe7b9e1fcbc2876f76d8f620c245124d2624dc9d754cc631b2e173731f0c2a17d22b80", + "secret": "b7f1c7aabe8c8fb12038e2ab337ac1bac7fde354f80f68e55b2b6afde104913d", + "signature": "820ef0d1f67e7b103140f82f907d1095fc4ee1d48eb2fd273e66f8a8621daba538e0e9b4284b7cacf076f0e0b5cf9c44a7d4b8d732240a5bff5bc28244dea00e" + }, + { + "msg": "0d1200b034bc24d0899aee8435ab1de854ffd9463153db6c5a8b7773595d12ad4a9d0587a276412974ca", + "secret": "ef3ad04e9b994d7b1c8abb014a7f62299261759a0bfc9e4d5fab4baa3f146e0c", + "signature": "a8c0008d15926efa804940dd6a4abf5617c82d8d79c4fe2035a5d3c7e585385eb88e7e8377d0fa41c3da6645278840e737c1b160a29ee072eaef0678d887bf0f" + }, + { + "msg": "2a2b090f9ccdef34f35466e2bffa2a80a5a3b954775768710881bf69fcabd84a1ff0da325af2544d2ad101", + "secret": "9248804811f7610aa06dbae369f2dc4860c898ef53addff51b7eaea37f609551", + "signature": "379360498de7975a6d4f66dc2545d4018a2e358d7d869fc6386474d64645739888ceac07010e301b509d6b0976a4a1233db2bdaf280ad7153c8e8fe28ec20904" + }, + { + "msg": "40de0c774355a0adff3f588b131e0abc9e5f5442dd9d8ee94cf9a85fe4fef07497de3026bc9bf832527632b5", + "secret": "4bd28661270ac2a4e78e20585c639a16ff6503afaa7e131ebc96cc6d239ae711", + "signature": "26c98cde5036dc23d24be0e7530796b5e2119759f8479ad3c145c2890199e26a9fdfa598e6102500d5576ca2cd2394680919479b0643cd607717d726c41ec309" + }, + { + "msg": "49949d4610cdac321f71ebd62080edaa60ac4aecb2cfd84e0ccb584e1dd82ba4ea6b9591057e2460568c7aeb7f", + "secret": "874f509bfd0443301b8a23de7ac443d63273fcb87013d270aed8d1348fb04719", + "signature": "7745afadd5bca6150bf761c58ebd6bcd631bf9ad0e2043d785e430e68c4e5027a971e917e443875af78efac099a66f4e178ae733d7ab4332cd15852617fb2e05" + }, + { + "msg": "85e26518fef4429847aa8046c26b062abe0b8a7d0449285f4deada0bc4bf91a2ae734fbdef83c4d48f1ab3980eab", + "secret": "aeb4112a121d60eb2a206434fbed7ab9e12f782a1bb324779093bf1cae68d581", + "signature": "56b02332c1ba9ca513299115f6b4c8730025a19cea3f961045e6d642a3f9da0c9ec47f645071c1ce243b96196c3114a8bea91ec98843e1d20e218763d2d43506" + }, + { + "msg": "1ebe467f4ce2422a5bc59dd7ef90448715931f0ba7a82a95ebce48fe1009643dc468bb65a8723dfaa0b6926aad7185", + "secret": "58efc6b766abf761d7ab3bd30786f1ec6c6cdee534052c0bba38cff1339543a8", + "signature": "3e4e80ef141c1faf0898142dcd0a6d4e9b0bf9371ef9c7db48eaa80b8d7624d3f9330833c71476384890cbcd2c4e29d7cad67f16a772ccf33268d94cb6a4f40b" + }, + { + "msg": "408082da564a04a5876cc8cf70568f3301687153c67c634bfef6b807d6dc59b84eaee895a089fcbc922220219a0b57c9", + "secret": "39997c7cbf0f532851b2e71a8f1ec42507b711d51935f266dee533d131960816", + "signature": "5223dfcec31f8c1321a08610e5d4a4d361aa4ffaf03c6f0716753f2c6c50d1aff204df2623f277e901ebb23b832fd34ea733ae35d9c2584ad7fcdab9f1479c0d" + }, + { + "msg": "2170ded7931a6e696c76933e2425b6ec0132562425f7f237ea5c136903bc57b258830fd0631c981125eaa1dac38586c085", + "secret": "7bc957c383d940bfa3cb463c482c14561b74a8d653f89d3eb6d4b1bd3ef75919", + "signature": "f224aadc94343287db510698003466c8cf49d987ad5311670f23ad7c9b5938f396716157c22d4e43ee31f0b685364f053033808a717c2b6485b4f8795e823d04" + }, + { + "msg": "", + "secret": "111e14994c181857e462c8f5efd16017936ab20717b995719ac6047a00312871", + "signature": "eee7b35bf5bea2402f0fe88bcc1b9eae9c0356f3c3104b164d0b1d88cd32afdbba15da591cdd4f3572c7c6aee09cc00f9711da0023b0b3f63f4019c6f9109107" + }, + { + "msg": "b3", + "secret": "2e472b55c494ec05252562780ed642110e3317c59b04b18a325201bf28b97b05", + "signature": "ede536e013fc776896d185a95b6ce04bd6f523a7b46d78c4ca5253fe63028d6432678bb5e6cac6e8b2012676e6b1d6098c2f302838deee1fd19b18ff4aa51c0c" + }, + { + "msg": "c31a", + "secret": "6a7e8bd46e216820b10ee964a6019fa3a02563202bd3fe7364141165d7de61ec", + "signature": "9e776316dbead6692cdb10e77ae8430473f467acaaea5efc59731d1f5c92ea683571dfb5a4a88e9b44620d12ca89fc04bc5c05e008c872d96802b6769026d400" + }, + { + "msg": "dc61a1", + "secret": "dd2a8e9fb85e64271be4dd102d83d156c13225486ab92b1062af7929663f9113", + "signature": "1a7ff2756f51f4e040ae872526e628089da27bc5bd32fa23f9cf1a1546223c7f0e0bb0d8e209d569323d8ff31cea2b075b7ef19ed79789109d9d93e2eb2ff203" + }, + { + "msg": "3cf0d640", + "secret": "361c35ec4a355b8b2b31e26cc9f9fad3a4ee1ec4fdeaf5febe25ee6eeceef90c", + "signature": "93302aeb60c46cbd6933dc525d32a07144f7dffd94958dcf63991f922da2d89269a4efce92334bbbae24f8389270d653a3b92f851f9772592b84b9839efd4004" + }, + { + "msg": "d5b286e05a", + "secret": "577ce81849e0a697a83ee42107ee4243aaaa9f74aec2d5212711691d56e7ad3b", + "signature": "14ba3df8bad6285cc537e9f00e0463184bf6c523c0a4d0acf9c5fd89d516b59df7828957b97a5ef3b6b63ab9b7ee93388a08d50b33a52f1f6f1f970a46bb3c02" + }, + { + "msg": "2015dde9f8aa", + "secret": "99bdadf9698c7bcb053ca93aea3a495ca0ab258ccec6ccf1dd9702bafac612e8", + "signature": "839acadd26832838c4142c51fcea397ae43b2d0b7f3d1a9b571ddfde8ee430d99c35159fe2e0109d6b83dd3b4d414d3fab17f697cc3a5bb80a885ba7a4132d0c" + }, + { + "msg": "5aa1d370b4467e", + "secret": "682681f66af334085580a37467da20d83db3a904198a49f9f2ab527c5fca9bf5", + "signature": "ac7124dee38434c18a2d260bc0f2e88ba8a78a6ca82cae42ef8767c08bedaa666335c2bc78c11a66591babccead2e7937550cc759766ea57620c28ef1b325707" + }, + { + "msg": "1dc9596a692509b3", + "secret": "8d9c723772ebb871270c13b2fc48e30fb8391b16be03668f1e42cfbfea0ee082", + "signature": "cf197346d7c91b348627954c3e8904b496280e1cb2804e0c80aee544912aab8a523d87d7752ec49c623fe12d67fdccc7ee2e9660d1b8fc38c56df2b88962d80f" + }, + { + "msg": "adbb0c8eba5e1173ad", + "secret": "9ac5cc9df83e1aaa570e88fc70982b110a682d1e1ab45bc66242b9c789e96ade", + "signature": "f1916f31dfe5c06b61b4e22933cd8f1f2b1d65ae35f3e39c309647ee4520c0be94f0df9c9ea030b75447e02a6fed57299c01211a49096a60cd89cd7ae4f2c50d" + }, + { + "msg": "7bf9ed806e428f67bf9e", + "secret": "75cd64eb36030f6d518a4e56c2a3b5f1d2eb2b672011a3ed7b64f31852b730af", + "signature": "47586780ad3eef0b04270b4eb53c9d08890dd05a907d7f797e8eb378c7fb8b931f3f10e320335df91c727383424a2fd6fefec8a1460c9db71512e881d8460e08" + }, + { + "msg": "adefd075373818315bf8f8", + "secret": "5ec4a9d64464a98981e4ab957b07adc674961aee8da90656113e8234f1ade04f", + "signature": "f1abbd66d9beeb32b9e7b29f981af6dae339bb21d7223d0bacaccd63eb5a8d38940ba301243aa06baf631f355f2ab5423996cfac7a9dec86cc18329f243e8b03" + }, + { + "msg": "b6a59787c1b80e10c6001736", + "secret": "46e3dada84c8cbf57d53bc2e5dc1710599678f212e991cf69a927ec117b723ad", + "signature": "ef51abd3e57f5606cfc9f341dd54f8aaaa333c8e6e85b3f543cdcb0ea53feaecf6a484097b552c0fd86b1371ade1b7052d31f947321c4769b6575d7ad7637b0f" + }, + { + "msg": "735920eccd1ddd338c66862984", + "secret": "73a8e7f6c3666d9eef053f9f2f4951120e7c99e1ac455063fe5fc868bad96531", + "signature": "4380d306e2e78d3e2bd71c57c82533e58d456c901d0d092f78f21f191e3b2e695a2f81ce2fef0bcd17ee43c515871a55664c13ba1679558a3f042bb1b5777a00" + }, + { + "msg": "196b7d1c2778399044eccd241561", + "secret": "a974df2aba456d5e4074d2862a5ca0e0acfb06d74635c9ac375561bc85113eee", + "signature": "752890822e69026dec43c90f47fa5b09b1fdfe53903e6201a846cdd73509f6aa0a46b3d814c5061e5164e9fdfc5ff8ed704efd1adbffc8b609571164e17ed703" + }, + { + "msg": "fc4d37d6168e9bbd3ce1544bdcc9db", + "secret": "1a500a5562e709f78e01703fb486d24d6a0fb7ad7bc040d10087001840cead40", + "signature": "8e0c35ebdb270657224f6904e05856a537b908399642f0d869f54933be77cf0c7568f82b7292d7881c942c12e02c9d357063e1bbff2d98d8610a5e9b1d172b07" + }, + { + "msg": "fe8a6977369c57648ec679ea67702fa9", + "secret": "dbc2d1331b041e067b020298b0ee0fa14f6b64bd0bc56a67c428d8b954f221f0", + "signature": "8b7636eea4300e9203e38cc4ea5534803a4c21f3ee5f12cac5dae1a42207687102eb34a16b6cc375fce9e5d1753d2399d1392e17cbb8d8c76f76512b6bcdaf0e" + }, + { + "msg": "dc1f80b8761b435dacc1ffbbee6ad63a7c", + "secret": "1640b5cc5d85366d766e32ccf7745f8f33354fbd520d120d186bc07eb5c4663c", + "signature": "76eba95e2ce46b5e3291282883f547e0272e15786810857f58163b1c323c67de7417bb64f3d7d1f14b513e60527459ce47fadbb98aef5d20ff80c9fa2e659d0a" + }, + { + "msg": "0c6905b4879ac416e4da55c41aeeba83dbb3", + "secret": "d507980ff35132c96d39e39807844540fd22514e7b5d804f8b136b5bc0f663fc", + "signature": "373b42ee13becf2dbc8967648d760b76c8b39762153892a20afe278da26fbbf68e984c0937a59cbe62366aea9384e23e8ff99b7c36431860b36743247917360a" + }, + { + "msg": "66f715c904548ab07ff06408b55c1d1897460d", + "secret": "1c2ca1b98f5c0c78adc9b4b484d70ec13f51e51b936083501f73da9bb99fd0ce", + "signature": "0aa99f30dffea160a36c72f9a7018869958d6368bfedd40765d3b3dbff512d7811df838166532f76b82655fcccce2ee841da07ff076a824c2c269ec225f29305" + }, + { + "msg": "4be6355582da270d6531e0b87a143aeb5b3ab5ff", + "secret": "0cc2c49260d8497cff7d6dcecc454b08e4640e0831895be02bab1a63f885b222", + "signature": "8abcd55c90bffe2d6f95c86a69f53e89bc6d4a14a3866a13dce9060cc90e31b07e76066872fa7b0fa03461fd976c68e9ccad4835d3f0b2edb0b5d2ddb7563e0f" + }, + { + "msg": "5399e0c3585b53d6c99f19f5c9c0ac77a796cc86b2", + "secret": "f46f925de45b73214b4534d5bff2f2b5c46e8564cfb0f4e5d677567c18215bb5", + "signature": "78ef2ee42f36efd98485965b527b5b2d16703c730ab93b2bc1365e27f8574c3052db1cfa9f7aacad5707951783ffebb6c00a2ee1b964429cf4f44fa5192b5b0c" + }, + { + "msg": "cfbc784d980669c638a8cd1642f9661e42512fda9915", + "secret": "f7ccbf0edd0c8a872cee1d553abf10e9bdf6ee54f0911857b99f2b7b5dea5fc6", + "signature": "637028c23d2d1b4e6b019e7d3093a258c92ba4f3493825778e9c19be0ef551b1f65bacc1c5e95bfc02bb76528af0b3bd1feac755bc064383a511a2aa58cdfd01" + }, + { + "msg": "61e0b553b971c5d87994a451c5bc9934e0ba5f533e0f48", + "secret": "7c55c2aee1efa4373acfb1167bffb78f52d429718b974a8b105273f4be47a447", + "signature": "8b599eee24c9b80d8dead3f40b9130d3afb3d882154355f3dc00ee04f47c65397847b9ab288ca1bf3fbe9eb00b80d7a251a51617bd565223f4f0ad89b8b66904" + }, + { + "msg": "13881c81f68a32fd66f1935ddaccc757b094b4ae8ae20f06", + "secret": "69bad9aa8d10be8e104a8790dd4ac920a10a0e2bd98cebfea0d646eba203de16", + "signature": "da1de039ee6708d1f6e37b8401bfe9beb42f7c40cefaf9818ae2299c0a64cc19a849ae0fd45dcc461e980fcb9124dee5bad55ca4749897961b6ffbe058cd5002" + }, + { + "msg": "666b892681ea26b171dd8f42031a479c58a36789da03a7f292", + "secret": "b069a90b16d3bb83e6173ede0516d041e0c82573d6d854b70b16b334923a5ecb", + "signature": "80fac0d7bc15a44db4a60d3286d7b07f9798867063a13f65fc90350cf42f36102ee40c998b660f482b1aa67b2b962efd03bc9b003694d01e2d963134bb03a504" + }, + { + "msg": "36df157bc571ebe7458cf9b32b2afe651bcfc7f660a154cac618", + "secret": "81a847292e5500af0e3c12794224523a8a7084508e942300ac1501dcf570c10f", + "signature": "4f2997106951dafb31a5151fbcb48db043799515769e3c08433afa2f199abbe7a887d419f9002db74d92a63f50ca655302491e23d68a22a315ac584404332503" + }, + { + "msg": "307964ba0ecd089e65ab4b9c1ba389e480d70f21ba1abaa1505464", + "secret": "ff58c9c70f2645cfecbca7d8dd660f3ee96ac3d451e287c316a82b54ccc18f17", + "signature": "c9a8ad88afbca425d2cd7a320bb6eecac87b916ab4b81f6db367a37a9eb2da9ad23691252c6c7a5431a31a9530c05b8d8f853335d9ca3ae8c12126ddb86c7e0c" + }, + { + "msg": "d6f5a521cd9301abadb5615c00a14a9b6aa8719a078d801499e67929", + "secret": "652eb6de06b731750e1e27f06380346e8056e8df85f81af644b76f4e66ba4711", + "signature": "f7461c65b3029658ba566200f1d84f6d3a15949d3a8beac77b4b39b64fb1abd11d8351b4aefcd48f77ac50dd62cd34750ecb5ff584d9cedf2078aeb3f819ae0e" + }, + { + "msg": "25e6e178a86a8cfa1c1739343506873a070bf278509c0a326a632d1bdc", + "secret": "1f208ea1d3ea5da84c9055c646a796f8d143d0d0b10ca2ad2594677925403502", + "signature": "8d333f1a9bc22c9d0568e329e40c47ff8e05461fde1bf47f8f5ccc427fda9027e7da8abb7bebe752d2e78c0137cce21d03b7f4be9e647c61dd82ff5e4edc5b05" + }, + { + "msg": "a091210ee30aaf258738ce291d3b7569a45bff032e1b7739135917ca7ece", + "secret": "963a11cfb394accdad675920a83d0e372f2444b7b588cb80b8a929f65b0c23ba", + "signature": "b1d419f1c56996293354687bcec39cc014ed01853d53dc763511ce7e309f15d6c7ebb609b3c0c5a1437921896cc8a8775124e21f0f3b70ca23838bb6f7aa4f09" + }, + { + "msg": "8ee9f305d279c383e0eab81d25f816e77fb80fcb483aa637886093bf50e0b0", + "secret": "823d54aba73a9107f78ee65612639dfd7f6a33aab532340a3e5b0420f474df89", + "signature": "2c0a7a8ff3df926029d64df06620f05d5a57df3b2b1b9d140aed8879d1c2e4c20b1ef08ab103adc7bfc4af9dd9885b1a0ca2f146dcb0471b593d9a73c3628d01" + }, + { + "msg": "b0b135f62c6e9012da8e721ab188d2381c5ff6f6b17eb1d33e69a86e16572630", + "secret": "dd040fa05f52fab66a0000836a882bae9fe86216fb94f25830508fa8c9d1a0a9", + "signature": "6131074ae1626d7e072de76c727a01cdc56373d18522997fdc9e339308732eb3892ad8663071efccc627e3bebbad971378ada7e65fb2206be3b5f559ddf0f505" + }, + { + "msg": "b4c1485f42a28a996d2dfc16dbe2c6cfb3a5c2632f7a9ef9a961fecfe5cfb8e186", + "secret": "fd1a92681065c38a59895b3ff2c42d68a0da48741711eece4edc66c5185bfdbf", + "signature": "88581ef9758ca9514130411ff91a26e7b1d3c74e9e766f1919db93cad08c00ec20dc2f22e1d999c86f16cf005f6c45bbd0db2c47af988c42dfc94d74ba2a7700" + }, + { + "msg": "9aa03dcef50c6a3a09df89c2a8fa8104136072465b682162fde936b1e9e17698929e", + "secret": "31d97c437aa1831cd9a070d68441d572635d3705ee4e02cc233cd426ba535807", + "signature": "c55766e0291795615fc0a1e34c4441f0bb74200b959489b221cea714cc03bf050821e2ff81da9ab6397f93b89b4b89e75e2cc2d63039a890656b977e81873000" + }, + { + "msg": "7d72d3fab5e9438b0416ce6dfb99648215d52b8f7eab2354334870ef76972ff3d06de9", + "secret": "5e77c0b86b9e5fbd5a087585a2646ae609ef98c5919f11827cfe73a57534b557", + "signature": "ca5e1f1c81eb63b8cea12b2f02d7dde18342674ecf8010f154738a80b620e808aab5d1a592ff2a5299da59e219a417cc204dc63b5cac01ef2167046a2e034d04" + }, + { + "msg": "00b1699880865c453d82771f5c1eca56426ea0358312ed0f71f466531d4f2d813707e413", + "secret": "bffec493b33ede55e56fcccad0d870f4bf3355e5e7958771c975fe9ebf4d89c3", + "signature": "3411ed44c392bfe443eda5d00bec5172ef1dc95b471997e7c165297b8a95efc0d3fa5716873f16a7dc7c029c846a439a7675ede0eff6e102084019ecc01de30d" + }, + { + "msg": "c7674b2c3c05100af92e7ebc8177c1e0c38ac62f7950827779825585e46ae62366bb29c0ce", + "secret": "2766270285e01312cad1d6ad7abbdb4ed354b8d4cc2df3c9cc47a20e8ef9b041", + "signature": "058d85b408d6070aff0a36749a5c21c1a5828bc61cc6218a047c437bd1e7992f55bbccecd70a5e32aad6ff43a244e72009707ac99d337c43fe576e5367ed360d" + }, + { + "msg": "588fbfbe17c6dc18299f576822501827be4697329c35146c1c0d3b8775e14745b245f54bbef8", + "secret": "4687cbff444dcabd6a14256c7a7b56fa4db89acbbdbd53b453a8f470af8e06b0", + "signature": "88ac052b7cca7d474940f2537785882ed2a6f5222f1781e9c9cfd13bdc4d9a4dfa2bdbac5dd38ed214a4df124011f0e5b5cb6fa64f69824b3b6de92e66eeb30e" + }, + { + "msg": "1ad4bf84ffff40d90e74832abd1c163065d30f34862e7f844f2d730f67701fa1b0786922b93f54", + "secret": "3ff4d569d8b4370aa3ba00d6d106a4f84e5def4237ee7fe03cd6849d6986ecfa", + "signature": "5001f4f392aaf4a6458ea285b2bb6b9e90166fcb00ea097346bb4c50df73db788a934f9b8db4002705ace0a0b688974f5b769761f897554554e153327251730f" + }, + { + "msg": "8ab4450fdb5db74e67780f5528d3f8ab1d22cbeccd3f9cab5f08a6557fbf6fe726fb4082aef95640", + "secret": "cc1952f8ba1b227aa5b06f160c84de61756d21a170f49e90c19136e01f4ed8a6", + "signature": "099c53ac3d1dfb17ef5f0e5354f15da48009a2f6885e44a8ee9a1bc7c8a1a3bc0d8a267413731f989c9ef3aa43529c2485145839c42a60ac8939fb602540da0c" + }, + { + "msg": "a722206e79e288325083c6cabcec7103223311774378f9274b11bf314cc655b145ec126d33f2447ea9", + "secret": "531bd060f089f4a40b3e40d8104398dbd044c2eb1ccfa1eb53d20143e7a10eb8", + "signature": "764ff0fcf0e65389e3194531f2e30c274cbfab823f0533dab8b1043b64f7759ad0db0f27e3acbcb82a723d4ac26a85f4cb58a8eb31001b62d7348eeb9b4c3506" + }, + { + "msg": "e011eb12ee7cb9a10cc75af21e34cfdbe4617d7fe44105135a627c77cc4003f406d43e966aff1c904704", + "secret": "7969fe890c3b6c7633dfcd6c58cbe58a93013223d63453b1c8cac09ed23645ca", + "signature": "54065c1f261818f8f074a0f7d39e4d17f36a99ce861b77ada519dab4f9bb00f7dce800f6ed68077c718538dcc7111d0bb0b1e70f71ae5f7a03dc764d806a3900" + }, + { + "msg": "61d8da8b378a60f63fc0c398930492ad3a9d3995f4d7f7e95e91eb0741bb2e6e87d21988346a15b1a2640f", + "secret": "9711015a87a991d86d44995e176e29e76a31446695cd7b2c506f350bb0d3c19f", + "signature": "a29af183c2fa12c18496ab44d49b35b59461b3bc27e1bb3e85315f3957615ff0433410d5190723df1848f3e388f41673951ff4ec16743f863f218d9945eae701" + }, + { + "msg": "31366906a392df9ec8b9d31862916b1b134db23a5163310be29387174b0c302d7b9b5a3e3a0272fec4e45604", + "secret": "63bc40363ba057c49c10040f2a87e168c7d20c0fefddf576d614205c434287eb", + "signature": "64cd5fef6e677099b1c3a90be88c02a040624191e6ffa028d9f0a2bb4be69e8994b1b43abb66f2c2c74b3287dee9684075a717cd9ec7f12f8fa6d6b19e17b707" + }, + { + "msg": "e7f57d6f86d25186c37a33916c027aced8d7932f5c432125ce5d8519596c8da1b2dbe75e8d1b657a35e378d63a", + "secret": "a3e326b025a97d80fdc9c249ac2f14a7e2ffc8720b3293a2afb39d18acae9c8e", + "signature": "64819a237a51dae5b0ec1a9625a9008a7e408c8bb599163b0ecadd70f2048109708f6fcd0546f95428cdfe7cb6421612f4ecb25d570ae0f73c2fa5263aa7ac09" + }, + { + "msg": "7d3adaf3d27e5218e8ebfca79210ec2e651f4642375a172f0c4a0b389caae31b113a278608c9252c54c0dfe6a0e4", + "secret": "039422ef16ac0b06d51c15b5a178c16c1ce58ea4ce7508a24fb9072a36297501", + "signature": "d3d6370628d22efea31ade188cf2f58e13abc2eb43b31b05458b52ccfaa8b049d52d83af56982fcf22ebaf26cc5c0e1e46354f4430bbe279630955f84e2ab605" + }, + { + "msg": "aefaf5b2f7b797d2a7131a7b283d4dd8b22f5f21b92fa008c1cf7b75b63881a01ef01f0b4120da6a7d407f94218c1a", + "secret": "883f4dd9f31760e4ecd40240b0c5ddd31029960ae818b9c7896a5fe7f3ad38ff", + "signature": "aa299650226349efd2be147a684fe2791cff75cbfe4dd4bad7d85a370fc125f908da68ef3ca579f7bea47722280a95871b01d81369bc5e3ef7e8e3299a693f0d" + }, + { + "msg": "75339838acdb16c412b46739a7cd6c0ac6be73da1bee9058b90a1cc5bb60a09ded06aa479c8c2e799dafbd163f27cae3", + "secret": "bc23a39a0c811210e0d28aacf90f34ef71f686bf91d2c4ce55d89d9b6826145d", + "signature": "8acaf1a395d37d8179802fc4b59efd5548260e0678c79f53cadbc881f7236615e5d25262e33a313d0fec220ab519a1cbbce91e049f4f1ea12aa100c992aae003" + }, + { + "msg": "ceea301f4e4080cdde74bc22719fc8aa77026666efd4584c92c7f48317b4f7d46cd89dd683d61611f4c0a5d9c3b3f4607c", + "secret": "90bea9d16c860df2df45a2bcfe81a9af66c34871ff267de7ef907d7a9031b4bc", + "signature": "4f95738a5d93ab0965f4fa17a904a25e7af5e1a1903f3fef3263c6c08ef3a48be9f43030fe8eac29cb437ac7f4043fb9a0df50f734d3d123ccbae0e0ae86850e" + }, + { + "msg": "", + "secret": "c9a533078f0feef1272995854b6050eae263a0c6ee3675600f75ef612309031b", + "signature": "810de26b679f450151db09da62e0548178dcc0c2d98f3a21282403ba6f40e903586aa2e480194f2edfbf5092fbd6b9faf40ef289cc278f2b89443a85cffe4304" + }, + { + "msg": "8c", + "secret": "ce8b954763c8863201e4c7c5349082f4c601c0265c2e68d6acb35896764f7b95", + "signature": "cab7597b757154c22876a8ab557b1cd588352288cdf304ce7e71637bc9f1bfb7b22820f2f62340c74ff0b6eac95389a6f823a88f69c09e2526a67358f3f62e0d" + }, + { + "msg": "0683", + "secret": "d9e94b7976b9543104e33755133eb00b4ecb761c9a5e842a95f359b810e4d37a", + "signature": "864ed88bee12f3023ce17e2264104e5a8e9711c1088fc7af60d392625a265076ce4b6e2fd9e7887e6f816c82398de9e13f58d2e7604b1034e619157a4339b40d" + }, + { + "msg": "4e61f0", + "secret": "f53c664992310b11e8bb25e360937c525c9411f78c0a3cbd95c49e0cb7ec8cd4", + "signature": "cd75eec6ae69b093fc55d27e38e280fd2b73b661a83c1696a408eee8c984c23f4c2f31a49bbfcc0833d3253f27b63b1e66865762b05c7aac825bd6aa061d1f01" + }, + { + "msg": "ac4eba7a", + "secret": "200bc205694e85392e246c9f46c238fb2e1afea3dc10c889a7fb47a383269c18", + "signature": "7f1a7409ebcddc887b45d986ad8b2b0833d8f80e18bc5e4090ec5a6c3cefa3aacb3ff1f3172e2eb678a0f881f7ed5c0f6c5131fd54ba20e22bfdcfb7192a0b01" + }, + { + "msg": "62664890d0", + "secret": "6161f2beac0fe1d0e18709c46d4662228c5b8ea82185aa9e39a63dc0cbdde0ba", + "signature": "6ab711f7d7bfa2fd52838ec90fd9b766b61a49a1731980c804a704feee917d1e428612be0b9cb6e7771bf101558b9b7794b7d720a371fc9bb69d38d102973d0a" + }, + { + "msg": "07e566631ab3", + "secret": "3fd16dbd92bb951cf2f154710d32349f07076de1f0565ace7e62fa128c1b5e60", + "signature": "2b79b752bfb19e57fc7a6f7bf052718256c6fea4e1d4ff96cf1c46c36495fdf98ab7686314d44a9d491d3a3ad30df139bf81dcbd9fbc012dffd861bdc9bb1c05" + }, + { + "msg": "7f5aef1c8a729e", + "secret": "142b85a13da51034d2b377530bcdc8c5e68589f0e0fae1f91425bdfcfef84131", + "signature": "0865193203f895e7f053711839fa32214bc4b2c4e1860bc684574b8c2ab3b38b4a492ce202be6befdc870660c46479223805b9fbe61feba69b6f62a76e159d05" + }, + { + "msg": "4f3b634a3e060b1b", + "secret": "ae4513a092524ecc9486a3be8cf8d64d2799cb77528424a08e9f5beb5ef19269", + "signature": "bc10de1ba91a260f99a371bf435d8105830e3d70fea355e14cc6a9d22cbb28e779c431fda41eb855f2e25c51f41c2f41196d83e4c4100bd9059d882e52a5e20a" + }, + { + "msg": "ed00689338abea8824", + "secret": "46c7d6beb6b8d4c120ea85576b724812ed5f4bd79e55e51f93cace50177f2b64", + "signature": "47fd9343d17d937cb38539600f21437ab07989f0af345a9a0a073a70b6e37e5475be14c8d53d5227754f306c331066ae501de00793fa3871ec9342c286f5e107" + }, + { + "msg": "846e4d1d3785d9838680", + "secret": "957ff5758a213fd0bfe220490b5ebf487ce3c031119627d3371def432966a5c2", + "signature": "088b45c9d66a9158f2fe0eba4e0fdee4dce49e7da005b2aa3b5fb1e1f7100ea9073e4fa3a7443b4d5e21260f114ceddcdf5649e729daf861649840c5b009b403" + }, + { + "msg": "cf19050e08644291b50b2a", + "secret": "8e30c7290b0ae41d45da6339bb9f2fb443dcf347f4b92ffcb0b5c06d48a3fb3e", + "signature": "58f5998ca160a290edd16acad29056b402584c6c1a780a86e6dd2c7730e2443a32b395786af201087b98a9298f162519c8a33f37cb16624a619e40cfc9735a09" + }, + { + "msg": "e6ea80d07a7a675cfae2c267", + "secret": "b3ddae3790c128d309394904753cffb2a258adfec7c011f996989dec7a764224", + "signature": "989a28d607643b253ec51afe5997040f54a762915b568fa2951908f7622a1f53bd3977343675a4054f7080e4b2426fb2692955c0b61b4e7ca45d8222f7a3b90c" + }, + { + "msg": "4f9afff1da2d299b5a3d90a621", + "secret": "9369214336e9400cad431ac03b399e8654991b2f2a9ae06836e014c963005be5", + "signature": "495995f4f80c1d8ddc0925087564895e3fc6d81e02a8503d6a4f7534d5c00a82ac8e7a96711809662462ac944b56768de58834a16e79959f4d5686d92e3a9705" + }, + { + "msg": "6006656d9e019e7cdbe7ce1d8897", + "secret": "b099666bcbdc2c35aef052d7ecaa1b0e7ad1bb92b2909e91a81a34b49d286b31", + "signature": "5175ef09a0941502847ab4d5cb924758e4651886f1639dfbc4910f9f8f9d157538d1d17f40626355866fa33d58e8cfb72ace00288ca22608f7fac3fa3e50fb0f" + }, + { + "msg": "0434d9839209e3254afdb8d4c8b485", + "secret": "edb48f8e825ef2f98ad400d51254c15b73382d744444ac36a50acd8d924c8a75", + "signature": "e4c32a57f95fca87815a526d44ba7920a18fce8f2d7deec60f0068d765f1816c562c596766b7f22bd9ad65cbf724ce16db7b29db6ced1429845df528c55a2407" + }, + { + "msg": "fe8cc4ca2c69163bad3fded90fc8fbaa", + "secret": "fa4f5465f5f8bd36b609fb189a2712c9756589fd006847d3344c6981da00ea39", + "signature": "f7dee912efda0a5951c60b28fbe699ca3d75e4869431e9ccbbd827ecc8260c1b9bd47b5e4b557e5654038fadda45aed600aedbd628bf1cb92cfb97be7c826301" + }, + { + "msg": "71b4153374f6be9399697c9203570a3594", + "secret": "8f187d85e74a50166f52838df9a498d277f06b87977bc59b52cc5c4adb7c8678", + "signature": "02ff749205f81df9371ed24905835d264fe495508baca1e265494d3ba6b198fb4aba9ee09b03c2cab548c4fb9329f279902ce61834e3649b594b66f90517d30b" + }, + { + "msg": "06add6ab8000496b120476920f8b9bd24271", + "secret": "57e1be3f303a65a8ee9ea0b6d8c8ecace5598f2216ce477bcfb04b82f326fb13", + "signature": "9fbacb8128308e0f7a885d25f0cf449f8632b5807c452ec597f11136d669ffe1f3130247bc06ce3e9e689f97c9d6706963d7bb8cef7ab397ca225b9a39636505" + }, + { + "msg": "f05508bf660818e16f27aae5e0d61cade7238e", + "secret": "f7837de18068ba71896b1553d9c007b5abdf21b0091933dbf78eaf27b4c8e5c7", + "signature": "e1e3bca4a1dcfeb131e088bac0b24bf28069487e507d590ec943edb0924609558cdfb9dc5acfdbfb2848db164f7b656c85f32be71fc601a055ffdd25d646210a" + }, + { + "msg": "75fb4e7116da17cdc6c2ca0c770db4d958b1ed40", + "secret": "d9fcf91c92bff0838066f5d26e771b39cbe41d9f3db61fc2ed787a4fa3db2b5b", + "signature": "9db85ce4111a02ec06f180c9d20ce7a992b9925a4d6f70963c28890d8f3f4ba6d0c00fd462137967af9c7152e842a553737f0efcaca3727af21af2254e13700c" + }, + { + "msg": "07d177e86695059453c1d3b40f66270e38ac7cc9b7", + "secret": "7e6d753bf98fb728df4f6b811bba8c284d4f66941301fe6002e7479f182d8e4a", + "signature": "5fff8aa92cc2d046f28861dc2499accfc81ff5f75158e1c2ee2966d397f7892cabe4efc1e7b43978919be0f0febadd1c3089abbf3aa34bf4205cffbf59b1740f" + }, + { + "msg": "d82d088e7706a702b2563abecb48cef9c01745321bb4", + "secret": "5a90c03b7c4aec9c678da8fbf25e2a8428f8a9d238fc101f4eb411285feffee7", + "signature": "3f6a004f452f038bfc4ce503e23acdd4b5226da84f0a1ca593ac8e2b3e6ac7bf55be8d50c30f9bdc495eb02a4b0ab77c7e28b008ff106e8c9d2e0b74e0885106" + }, + { + "msg": "469c3f2b87ef6923cd3a83ef45ea97aa57f023bf6beee1", + "secret": "8086ca354b63792aedb65b954715fbb2af8280c379225f00fb5f8e8c897e5d7b", + "signature": "15d33c638c42713ff9291a7d463191f5adb9737ae03230f3ee26eb9b173c9a4c5916b6735f456fa094c75308bed6b73536fbc92b74b3ea9b8a982f2a6c529303" + }, + { + "msg": "bbdb03ac8d6e13cd0d2a92222525fe2128dec0d14337ee66", + "secret": "9baeea7a4032550ce57a7df7294c2f2d2e9e12d93720253de5f59f21773e87da", + "signature": "6bf42e4c9e39d32f3c4af82a39b4107fd488d8ecd97cc9d060ee5f2c1202d44f5f8b2d2d6fccad03e401027ad43060557bc557a6ee8400136959a48e14baee03" + }, + { + "msg": "f08b166c884da02b3a8b6c9cede812eaeb1286fcca9edd307c", + "secret": "3ba248f2589fc77874a5ec05b747a35cb378f1fb0c76724a0d755772354b638a", + "signature": "1e6b3c2a4dc28ef262076478dfb88b31471a973d1433d612684b3b5970ecb801a9f347ca091f62f0befc2d2709e1668df00179e204d8d70e06b8c859adb9d20e" + }, + { + "msg": "e4aeaf535b143a0007cfa8b79fc4faa07f5971415bccbe1c7957", + "secret": "bffc28defcb2ca0b3956fa12deb025fe4ccb2a75234b6275714c3945bf20c4ac", + "signature": "132a2dc4b34558d4ffcd2bc5d152494c1ea36f119bdedecf801ba7e0f8b491c92baa39d2e64ac24b9ddf71894c867ce561167cf571bc04ad1a16a95ba4ca9c0f" + }, + { + "msg": "6324af23179976ffe1322d915ec3778a888b1ccbfab233c2900456", + "secret": "895c7185f5822c0fee159a3c7a023caabf66f2b0c9d285da67f98dd565bb5c4e", + "signature": "4c0d00e8eb7c65b55d6d5e168bbddec40a52c684975dd8d71114d8006c0e0604d521a7b63c8d9e7c721e522225542d6c9faee1836cf6c79ef9eb0d768b511d00" + }, + { + "msg": "b3701905bd835b886adf475938f530ff62d8a181b713f20118462680", + "secret": "08f2c0404a20bb5b785a95105fdea1b5b9ca2e707a79d4f2ee8bce72cb14284c", + "signature": "4530dbefb413ec7c331868db99cc20b0030bac55fd213b7c31d91222a26237898641a5c6aa48907f64819e9a887ebe80abbbaf79657bbef2e1a1d4b446f62b0b" + }, + { + "msg": "f184f0833ecf1f949989653745ace851a331ead8087f83fb4992e2f795", + "secret": "efc4f432eb88cd31b87d1150da5bc44ee2aaee4b6978455818a8cb767b3b3a81", + "signature": "10bdc9a6bcdc4af71ec2757c9f7ddc5b48d4df50f2fef7e3657dcae13eb1f15951c6c899dcfef75cc824cfdaf6814dfbc12187258b408453dad6b13485c2040f" + }, + { + "msg": "fa52f74dcb1fe1c0d0777ce76afe4a2b4ddd26c3313766264ffb9584ca66", + "secret": "b856d51a510bfa009750fedd49768d24b98b7b78c158b461979bb2e34cd81849", + "signature": "7c6aa5e93c6b89e6fa774b2756de09d0816da50aab7ad5d1d5b3b4e7c80b573bc24bf0753cf78b2a25d8360eb9968cf247cc70f4f0887bae4b2c58f8bb44fe01" + }, + { + "msg": "a30fd00aa7313429a22a0fee7479527637aecc35828c154f2c10eb6dee789b", + "secret": "b03f0e36a6186276ac256a1a2f492251d5ba9f32189846c7df81703a9cc0c06d", + "signature": "6de719462d3a008c01dc23ba000254c3a6e960a577c428a100a5463010fa38f2a7faf4097dd04bf711e46d31fa038256089a8ef9a406e0c4178b76ac08d0a10b" + }, + { + "msg": "965454bd800ea7c1591a51b20448914db80ff8b926b00e2a94ce5882c7415f8b", + "secret": "49dcbc967ffd4e14ee5a1a4cf85d027866c16b0b32980a0e293247f886c87063", + "signature": "a9ee29c16d4d17566c0946cb4f3ad801ae45f7aeb5af5c4fe647ac0212d606406c84f8e29216489e9095c1578f4b6e6b29c3a6a6c294aa3edb6a29cbb3f6b307" + }, + { + "msg": "b12dfaeb884ffd8e2dcdf591a50b8ac5a1a305813924e5368a8a7b650ece600431", + "secret": "bf03d64b69634d5eedd4117e37e040280a6f10f1473bbdd72f742896bfe66492", + "signature": "2b3044f1ea12f52312710552c981f0468fdb686a7b7057a386220f5745b5d5b9409ed6b939d36c2816dcbb1dc3a47130708d6b04d4cc54efefc2f56e34143807" + }, + { + "msg": "2bfdb07a4e1bcb7d5b426d91ed42af4e462ba86bd96283954a09bcb646ed48c0eb05", + "secret": "604cdce3a1ed456a4715eb0d98a8145dba3c3713d28d7f24e38e1d704fd1a828", + "signature": "66ecaa62e1e67b4cb2e3c3cfe8263cd9cb6661c13d8747db466adff93e2ba5ebc99a0f3c37fd9198aa071fee6619edd4ba1a7f6fb6c63bb5e6a50c04f159a80c" + }, + { + "msg": "933de808cc094d50045ed34edf235bf3b3e9a4bbb790156e9e019c069e7a1321b65c0f", + "secret": "02c53f2f16b113f9b22410d45763ef5d472332ad05dbc12786348ccf53031cce", + "signature": "7c0cce151cce98ee6b445959b4e236669faf16494c353d456cafe7717d2f335eae392d5c1cdf63a95d51fc4138f2c2f4d123dafb44d70d50bae978788c22bc01" + }, + { + "msg": "93d6eb620c36032d711424f6110a64ef3f274314bcbd35d2185154b1cd1f5c87d0719e93", + "secret": "c376522be00c8b8ab009c39bbceaf00c4ddffccc4ebe63d05c7524bb4f2064af", + "signature": "531e205f462f73521171022beeb58ee26296617843607869755d9093d24dbc5c3c4b23272f1d5560c72451e88c131aa1cc4b548c40430b8ecc3683e93c4fa204" + }, + { + "msg": "eeba85768776b204335c36076f6b70eb204631dffb8dcb943d01d37fa890430ec50c0dbd8c", + "secret": "013946cd3d18c5b99791b357c79f69c2cf9e30789a2f31daa56da40e30b84a15", + "signature": "44c06aca33b853b7497fae449635c731b98b0872ca8c58679fa4c04f13af673d77a99e76205ae7b66e0de415417de6e31b417da4a1146cb60204bdd9e67dd701" + }, + { + "msg": "3557700c2ef949c94c0e1de2c2ed4603f5d6e656a8d7b815cd37d3e44653e4d3dd769a4281ca", + "secret": "0cd4c07105059feb9fc4ec07a465c197878bc3e2e36e401bd386c2684c569933", + "signature": "7f56faba502580670d7c471ef0265d298b27e3de2972a0e10eaa21a677e79ae4548e31ad7d1e15e209f72f7b023e88d283d9d0fcb175722d0fad117f79702107" + }, + { + "msg": "aceb03ee7e7047780bc3e7609a8df343b64dd60bf03540c23441831059807bd54d54b53aa7e7f7", + "secret": "68d8cfeeab816d61e22701dfc82efe9ccc176a25cda855d0190adec3fe4907f5", + "signature": "714c1d21cc511178ff5456141f3255f6de25d0e660ad5c87406e276356dacdbb58bff0e4361eaa0a2ca9dbc6a208308e9f778f6b4ab9507ec9fa457e36189f02" + }, + { + "msg": "b77cc43e4e7fa8702a54efff56aa2cda0f91d12a243b0ac1ecdd256e30d0010892013e264c4a3c9c", + "secret": "7362ef7f879954d4c4bf84191ebe3c7d5a97fbeff14554724058e633f1ad08cb", + "signature": "8d8f61f1c233a347f24e9ecbfa24e96a77bbdf0a36ea0b9de163e0de0c3dfad5611428580bf05475e701ae9eb65ccaa9a8a86888fd682c7c3cb50debf0a5ea05" + }, + { + "msg": "7bc681e6a5e413006963acfe0ee44f3001c4fc5aec726dff661542ae694e3783d4adc11b5551a4caca", + "secret": "0d89c70d537b96ecab7f88c6b5d75142e044cee54c5501a4c8dc07986f5fe0f4", + "signature": "3bdf04e326f8307876e88dcd816ce4723fb4b9c71b81cba82fc56d67f94de82e90f00e74d3b624cae611f727c7c5132abed7acdb31916a3b881893248bfe9003" + }, + { + "msg": "4cd5516fb67fd4d0d0000b38150602ca204d533931d4b21487248c745893daa8943794307579eef4d94d", + "secret": "b1496b69c3a012cdbab3d5b5d5e2b326157af8bee763b79868345d03fa916ffe", + "signature": "543b9db48257a34ef85fa2bb842eb37e85e420df62c92d76692dae8b13b951e7eedbc564066dcc41eb0702ef73658966f99f288710fd348399d021a4f200c507" + }, + { + "msg": "f899801f0dfaa82308b528012f4059129c1b6216202a2268dd9709a1d397de21dec766e978f1d1f446f012", + "secret": "98ba785268f53a49489b70a9084960344b2fac80df9c2a14e71d71430b94ed04", + "signature": "4f4476eab8fcf78fe6844dc42d4e43d6f368e0eb64353df66e73d05a3c331f0e1f3dc4afd3efe0ff0ed96c93b971f525ec99c62550bbd2a675377677fe1f7409" + }, + { + "msg": "626349b5445f511da6a85c9accd8946f49ffab1949060a4a101c4484ae51541c4f1bc00c2a04d9f473f68332", + "secret": "56057dc5541f68373c75f22c30a4d2e26d9f84d962b84fadc51229b212234284", + "signature": "d126de60beb7cf1947c8d141c0dfddf56545ee96c4464bfd3ff90f91715488fae0d96b40d8bc89018fbaad5ad0f1664a5fd279f15c26416796ee1f94cba7ab0e" + }, + { + "msg": "c42f493a8c96b2b4ea843eeac55597e339417aea1e6cef24ee12ea41a5abf97b55886025ce86f08040e2c51307", + "secret": "96afe869637a4b605b7cebf09db3f38bacb419457089e14e77ca002a1c14736c", + "signature": "7e3b0a8621c97e052570631238a6492900880ca46441ce9a2137ccda07d26194ffcf7f1e474b4cf7dc340ee64648eee03cd25ff74a9bd542d6e7112362b4350d" + }, + { + "msg": "66f5a28905101ffb5228b70048c78af8122d0077c4064d9872406de572aff5617ab5a5ad8efce63cafab776890c3", + "secret": "2a4d7e0bc00e4c901c323c09b7f794dbd54b8d6ad82dc930c0d444b9005356c2", + "signature": "0fed47c6194540ab444796b72d89fcc6897ee80fe758a95106e892211769e0361e07804955268aac79df51b4bc2357a0c3024730d6cf9fb4393bc1331b896b07" + }, + { + "msg": "64429319761032b34106b791f14b058fc95c386fe7292933b28e54591427eeeca77af43c71d29a6c8ab7c48899d06d", + "secret": "1ddefdbdd61f993799453467f39f650771898ff66c7f5e7c2be2d79486dd8380", + "signature": "6a28c677d63f940eac150f54fe6ed3a6b74bb0be9923a0d1cb8f1b4f042c32f658e87acd042291c043ea39ff3314d42737c3a4a14068eaa1916f604e600ef406" + }, + { + "msg": "b9a098de375566b892911f61382cc4889fb49bb097852368f54b6907152cd3b3b45f9a943cdb11af9a137dca98df9f20", + "secret": "ad17bf8fa2fd4eb62c5aceb625860a266c6d5464cf39b0103d3af595d924db51", + "signature": "616e227b2e8b65d237286a004ec9ea619f3bbbbb59adb241fb8908aa8166503848414794a8e39a7e92f610b4ced4e9d831d24771a7fbfd514950574d317ed603" + }, + { + "msg": "ded69414478078da94a0741416eeeead31308861ea18525ad8a5d81fe74dfbb8a25bc4390f366aa1f571f47efe3697cd90", + "secret": "e12553785d6e060ae57abad3f359b1bb14f378c16695d409b142dcaa0e13a202", + "signature": "b4b83f5e6ea1b3cde23fb63a09c58e6f8d24355854f3df5a6bdd73958b14198727ae3c1a60c82a0656399d30c8c2cee99657af3ff64e0e559502489110e14c0d" + } +] diff --git a/rust/tw_keypair/tests/ed25519_tests.rs b/rust/tw_keypair/tests/ed25519_tests.rs new file mode 100644 index 00000000000..de8d494b915 --- /dev/null +++ b/rust/tw_keypair/tests/ed25519_tests.rs @@ -0,0 +1,58 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde::Deserialize; +use tw_encoding::hex; +use tw_hash::{H256, H512}; +use tw_keypair::ed25519::sha512::KeyPair; +use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait, VerifyingKeyTrait}; +use tw_misc::traits::ToBytesZeroizing; + +/// The tests were generated in C++ using the `trezor-crypto` library. +const ED25519_SIGN: &str = include_str!("ed25519_sign.json"); +const ED25519_PRIV_TO_PUB: &str = include_str!("ed25519_priv_to_pub.json"); + +#[derive(Deserialize)] +struct Ed255191SignTest { + secret: H256, + msg: String, + signature: H512, +} + +#[derive(Deserialize)] +struct Ed255191PrivToPubTest { + secret: H256, + public: H256, +} + +#[test] +fn test_ed25519_sign_verify() { + let tests: Vec = serde_json::from_str(ED25519_SIGN).unwrap(); + for test in tests { + let msg = hex::decode(&test.msg).unwrap(); + + let keypair = KeyPair::try_from(test.secret.as_slice()).unwrap(); + let actual = keypair.sign(msg.clone()).unwrap(); + assert_eq!(actual.to_bytes(), test.signature); + + assert!(keypair.verify(actual, msg)); + } +} + +#[test] +fn test_ed25519_priv_to_pub() { + let tests: Vec = serde_json::from_str(ED25519_PRIV_TO_PUB).unwrap(); + for test in tests { + let keypair = KeyPair::try_from(test.secret.as_slice()).unwrap(); + assert_eq!( + keypair.private().to_zeroizing_vec().as_slice(), + test.secret.as_slice() + ); + + let public = keypair.public(); + assert_eq!(public.to_bytes(), test.public); + } +} diff --git a/rust/tw_keypair/tests/ed25519_waves_priv_to_pub.json b/rust/tw_keypair/tests/ed25519_waves_priv_to_pub.json new file mode 100644 index 00000000000..40f25d9b378 --- /dev/null +++ b/rust/tw_keypair/tests/ed25519_waves_priv_to_pub.json @@ -0,0 +1,2002 @@ +[ + { + "public": "6994de65574377e0dc1c8812065ece52c540d67d1f7cab456f6220250f012b48", + "secret": "4a63e14a607a8a1d47b943b5979945d07241a45dd40406adf8ea2282148aba9e" + }, + { + "public": "eb031e07e28e28a4ea1a6cc39cc98fba52e1521636f208aadca0053944983468", + "secret": "146d40891effd3b8280899d032a958627d9160e8cc08896a1e6b40964618591d" + }, + { + "public": "b60076cc30ffff5c29c65af9a13ce01c3affc231d09fccbcd1277319c7911634", + "secret": "0778c7bf255eebced7a2e4fb0711f1c069ff4de1b888cc7274217d4aba93ac23" + }, + { + "public": "00080f30de13a7ae491abb680c6260aca51aeb1ae0b57638f866366d53439959", + "secret": "dd2d31ce9ee8110c2c5de4915326fd8c4a01a85043b4940a17c41414b956f072" + }, + { + "public": "d879de2b6efaf97f5df282ffc70eb4137b82830479d4371cf9d8de9870a4ba5a", + "secret": "f1177a06f3b1286dfc56cf5f3773b2bcadac8c059d9f609b6b25ad30233a6ddc" + }, + { + "public": "3555525c9928e4b21e77d88c3aff9f97b8c036b9ca2ce00f46893b73314d7d1a", + "secret": "b31bcb19f19e195ace8aeea14fdb36ef21528a01c0c44755993038a75aa41a2c" + }, + { + "public": "2364fac68900b071ba419e8ad166927ba2d213ee00e4ac909879453032530a5a", + "secret": "1eff1b1f7da431102d7630c4bebe8e27a8ce246d950f526ab0a13904035586cb" + }, + { + "public": "c4611be6d5aa861e81719a97788723ba02390a9d45933c40a50d26f83ede5679", + "secret": "756a71c86d56256a2e5429a4ac2049fb2cfe1a3113f359dca09a62e0b92e787a" + }, + { + "public": "a41a61a6535cd5159a859567520bd200c2bfa4a61a5c7e6d61c6ba43e7d52775", + "secret": "e5259d670828e56c62fcad7e227d98552e8b72e0de0e701013c1360217716200" + }, + { + "public": "8f25ee46a015c582d29b6cee37c8e920548c542a107b01568fb601704bd15458", + "secret": "0a61418013eb7b7a6deb3153d3310509a40bd427b1e51f85afa987617649665d" + }, + { + "public": "e071bc94bfba35614be26587a638f7d3fbd446a6f2505ca22b3e9ea015fa2107", + "secret": "327f248eb44940b3c3d6ea6c336718789554a25bb1a19d6d59b35083d151a1b2" + }, + { + "public": "f3331bb6813827c6b4c710d8f54b1f5b8ad049e9ee5543556c7b76648ad5ad67", + "secret": "217327f9e1a353f5a1a2719df337e65e2dff5e1c3750c112f6cc407cc13c1e51" + }, + { + "public": "e21fbe88b06356950f6a36ee015ea6442c00395d34b5c98e45b0b8143576891b", + "secret": "48ae7ed9b9d9224c6482f45b6c8bb0df6ad84d4b6c6dbd8abd7a52ca24c12b9c" + }, + { + "public": "c52011c7ce403a4240314d99ef2e0bf738b4d479bc534f3d3f09fc99e21b245e", + "secret": "12161bb407ab94f151cec53b807e95e83f7e51cb81c5db2f92a1ca783b7e3daa" + }, + { + "public": "378c133ee53dafb1938086a91399f4e500c575d9302d2a5d2ab88abe3bf59e0f", + "secret": "d844f6b866f95442c849809d5458b2f1ec87c45ae8bf1590aa857b72bd5f631c" + }, + { + "public": "22af2609904da3b40cbdf7996ae2dafb2654b976646301695ce325e4f36d6850", + "secret": "6717d8125252add4aea6868f028f06d803352675aeac9e93df26d6687cd2e594" + }, + { + "public": "a39a7e9e37e89090ac07e04c4d50072df9ab637a93e5d192cdb4dca5e9bae77d", + "secret": "7f7048ebb23194a2087bbb157e593bb9f6af60ef787a5d89287afe165b3332d8" + }, + { + "public": "b4faab7ded2c3b0c7ce91e9862df23055d2bc26025506da643d919aed4e22b04", + "secret": "7e78a13ae3882b2f9dad87ff0bc193365a2068d656c6f037e10d5a7a6dcc9b87" + }, + { + "public": "07f9d642dfc018fa2d91f5903ddf9a16e1b7f09a4d856496dcfae8d6ace4ce15", + "secret": "2fdbb4b3420104753e7589d11c7309f792cfc6b8cf4e9c9a04f0960d15868d7f" + }, + { + "public": "3222dfc6d20a9bd692f7e461977c13b78c613674cc20af92833c999392721400", + "secret": "9a155f1ef3d654091707feb8902e712c5e7c652d91a04a730daf1b72e6a19a6e" + }, + { + "public": "0fdc953e7980334d24a64afbf0a8a2d3005fdaf0233a2a4f3ce62c3e2ecb7e18", + "secret": "15cbe82dcb60918e0e1528fa13371cb39e908d3ab0d6b18652e7f246ab29a4bc" + }, + { + "public": "6e000c3a8b47691cc72ec6d78f62fd18a2a7b6509f2715d7f611b96e55969019", + "secret": "a091b34a3aa6284b40a2cebf126e545489b221e4524b2f4db76ef397d0bbb114" + }, + { + "public": "1b06509ea4abd77d40774c74741e529165ee1c036f0daf49edc91cbce31f1a28", + "secret": "a924417b82e6956a79bd5c51cfc5dd25615af884beb6c5a57c01a8e573918fb9" + }, + { + "public": "d533a0e3379f5725212561037abb2b1fa7cb912c10f9e446d1b834be38266338", + "secret": "08b5c72057d8ec47da8b1d961a1b848d6f2cd410798cad9be6c640c4df2d35ed" + }, + { + "public": "ef0b4b4893a58d595606328807888e7f76090e25a099b69eeafd126b073dd674", + "secret": "f80d075e27bbad3f2ceffb937fe8de5c1e594e5dbf21401fd6050f16386cc6fb" + }, + { + "public": "359ad6381ad14c3957f3f6a124206b0d9667ca940f61c39551b8fb2c548c0d72", + "secret": "8e62b4cb288fed2bc4a6d3c64a854e9f2809c274247e6005cf6028d24d1f240e" + }, + { + "public": "25ce79a6b088366d6967c10666d434699ce3990ccb06fceb5364bec1f3691926", + "secret": "5ea106e68780fbd264b1bf136fc2ec95f2c8cf1075907072133f1b5cefe51891" + }, + { + "public": "1b53425f9151fd2bb1a7850c534059b49b0d220783bd1e8bd254c2b503d31e74", + "secret": "869fe363275776ce4b5df236c7563dc7cf237db74afe84babf1f8ef83d548858" + }, + { + "public": "baef1c25a7190fa98ebd23cd4b359ba2246cfc2ab03bb88a885e7e9601ed9e14", + "secret": "d0fae9281ea5beb59108786398f8217b584a4d97b06b38b2fa340007a3f853cd" + }, + { + "public": "43d37cc7e03f61b4e71a8afdb339d62d31c1b5c7b48a25bbbe15a9925cfe4e76", + "secret": "5670f149475f48790df6e0eba38a6252529bb71dfbfeb0f940389d9cf8fdc62d" + }, + { + "public": "6080127306f6fa30ab8b027a1b66e8c87930303aea56dc18b8a72ae9b341ba36", + "secret": "5e2c241cf3cb68e8c8174e7b709ddc6728190d7506143da9af0a4468cd2930f2" + }, + { + "public": "570b4211b5a40b9ade1f1b4d97614ad7646794482ffd2f9b7f251a44a890a003", + "secret": "be8914df5f3b353ac60c6afe19d5b91baab869f34550df053ff41372fe6ccaef" + }, + { + "public": "c2729d6a22350ddf132224fc95e296bdc9c3e67a2434ec0669451e1d7870f200", + "secret": "0439d1e7e5a81c50d81b9ca935a9f68aa065abcaad4f285e8f5763f2c73af7fe" + }, + { + "public": "d3a43c727fa5205ec1d9e97f8b3c7af93b671f59e6ca9620c918f9c310e6a36b", + "secret": "68a93a0bfadb63c68390ca92cdff51d96b221c93a7eec06432b007515facc1c9" + }, + { + "public": "08cb5bc15edbdef8ebd108275193b32335d7ea217976449ebcb5f878cc950d2f", + "secret": "cde5864cfc4e494c582eae6410c156485d5e51469a27b0f0706b00ed19db277b" + }, + { + "public": "fa6c653085fc1cf740e03419e9524fea51b237e822a56838d50c524f7abe1559", + "secret": "c32984d99bc2e1ef591f2e35197c0716628f2a0b994862f766188a83a04c3e2b" + }, + { + "public": "5375f02b1a8643ff50dbb2f64ef29747e1ba1e19dd859459faac65c67854db37", + "secret": "5347ed6e4e9ca752f79126fb5f7097ace9f35bc11c4109e8ea677f44dfd0c5b8" + }, + { + "public": "f7cf051dfb23d652ca73d5b25dba5a46e98534bdee51ad149a27f6e851754a22", + "secret": "0e3b3ae617869422fa58470cbfe7d24a1a055a064bec56d45e85aab6d31bc298" + }, + { + "public": "f9e5953ecb3e231488f8580de0cf5d7f0191c2dd8a3eee9ee48ca63b174afa7d", + "secret": "dd34761880e0178c9e15c12b132dbe5779363b4a32a9aa40f1d6e29e9244a790" + }, + { + "public": "056ca43fb3957363f6c8fbd4b76cb824af102bacadad4e247adba92409268937", + "secret": "c48fe13d525b3e3fd4f301d638230465300bcc97dc1b9fb7a5edf2ea5b6759a9" + }, + { + "public": "fb8af5ac9f47ffed680d37d5a6ff17066d21eb5a5b08090ee6519600e00edc13", + "secret": "31a193031d5be1bb0a2a315d9d75712a9e65128d2869a7ec047df7a20431fec7" + }, + { + "public": "c177a05bb3083acd81c2d6647c9fdad2788f32ae7d112fa44e5c808eaf54ec7a", + "secret": "64525189a50db2b9e83e6228dbf4d4425e8522063f3abda2c794f77ed5ba1304" + }, + { + "public": "b8eea5eefe7f26cc8a99ce4d154119ef0c9526e5b60188d4fbdc8eaf4227e671", + "secret": "acf65da635b934e4e11e54d2123e85cf495e4eb1d80b51840ac93114e78d818f" + }, + { + "public": "ca6a003239df6258f06be9a0b46474a6fc28d87a4722f97c98d275edadfab66e", + "secret": "b527b314156dbbe7feb67da76bfbbae1359def0ec9283487c0c59df0e66862aa" + }, + { + "public": "4a23451208a2c9ed82c5fac741a75bf1395c401386ebb1adff36a72902f83873", + "secret": "d034964780b37264860ff98bc78fd6542e2cc1e9c8ce8ba8d4b83c6383696d6c" + }, + { + "public": "59398ece5bc9a03267f218fa0849202426b4be1c185e95c3cebf5858bc1c042a", + "secret": "f13f614624dd1547df9127dc8c1d2c31ad81557eac71fb87d3a4878d7c1d4f90" + }, + { + "public": "db32fd9235e3dcfd26d375b4462c3a76973db17c11fc5324f400118a79ad2a01", + "secret": "92da4b9bd167233323952ea95644ab0bd6c20e33c94da14c8c81a8e3ba27fd2b" + }, + { + "public": "ab0428e769186de40ac24745ff56e8496cb77ba8c41ddf30924bd711b8cdd151", + "secret": "cc17419f1cf7ea1db93c338e3c061329e19de882479b0505703794af83d23587" + }, + { + "public": "e6eca9ef0ce8d4f8306ddc2b513c40a72e38b5c6c0438a2dd4cf96765e42be03", + "secret": "443e7d230bae77f7ec9b3645ce79f5cbf5c1989aa33dfa83960ce593bf60ae67" + }, + { + "public": "20174615d596e89d28be204a7f2dc6eb0b03e0abfc0f30ca2646f5d432aeb72f", + "secret": "a9b8cb1118ab181be9601aa9055e9223a09f43670639f0c401c21102fb6b3292" + }, + { + "public": "05098ec559b11e4deee227e96d356b3104072cf62212c669c434dd988d1e8a5c", + "secret": "b12681e6a607b842cd2588e4ea0ad7efc7337192a8ea4ff8e06078d30fb2235c" + }, + { + "public": "61124880383c36dd02352d14451965740a1fdae33e3df0dc073de133a7d6737b", + "secret": "76d5004df0acac33e1e2fe9807a9efcdbee48c7a61dab8fb10e2248083c4f75e" + }, + { + "public": "2846826a02e01ff6903efbe47733a5d718b7293fba201a533fb7308793953b54", + "secret": "a4c5df999bc6bc4a8e077bce3b1aaaf48485a329b381b23820bfd0ca19fe87d7" + }, + { + "public": "d4963b557f32d189507e26ad48b0f2f837dacdcfd67fffbf2af0928a5d5df447", + "secret": "0e3ef340811ec1d14f9a7ce8b4d4aa58144f2ebceddb58d97dfafa1621f0302e" + }, + { + "public": "1d57a18696da3a8e9bcd580e2bb6098df79fd6cb89a3ba8b1d60cb9959d49027", + "secret": "8a70dba215618735397e62b4b0187b989e45ed4b43bb44b0a0a03b8e376b613b" + }, + { + "public": "a59d0c2270a6eafa455e2b7bffbfbd49066d3c1959f26149a7edb41679b1cb0d", + "secret": "4ce2b583a2ba7c2acd59e236eac959a45eb137140eb9365ddc56fe060998fe8d" + }, + { + "public": "7343ddc3a9d5de18513fd23fccd0c325805797c3d0bd648e83c97bbd99270a15", + "secret": "1dd790c74a9309502a5fe40e98009aff321b719f2972f57d3389c400b8ae143c" + }, + { + "public": "d5d7f74b2881b7e8b2d96ff32e7576f56264bb83e22bd1e6f5d42940d5a29c12", + "secret": "132ee003a80ea46b698a0e26e6ff316ba5a516daba2694b52c87935fd44dc064" + }, + { + "public": "5015354d15bde26e3255fee0b691fdf908908198412098f841a467587abfe93b", + "secret": "035d47b3461653309da0e10c4e8d5355b13ea946de9a7bfe9c8dd456583a2f1e" + }, + { + "public": "e41b542dbed9cae38ff49f8318a35c19eae3473809563677cd07929f57284774", + "secret": "965f939df073ff64d393ea08654ad87c851adf5aebe790056bbc2d6c963cff19" + }, + { + "public": "7d901e0e3c04a5f43c798bdf3f7ade4937caa3dba1de47926c70979b5ce3f12d", + "secret": "24e53c34a6c58ac662624aa64754fdcf8bbdc7c02faeb4067dcf534c35e97006" + }, + { + "public": "adaa357d9536187bf7fb240ab05c4be28fdb07dd6cfebcf09b0840a853272b43", + "secret": "18096a25180f01fedd68f2925446d81af327cdf92781b7c5e073fc4e3c52a421" + }, + { + "public": "f96a2cc551ab9829b8d18823a059fe433d194a28b6ca8305abd41f9022697e42", + "secret": "872e2d7a638c4c6d0bf35581cce8877cf6a44b7e2fab912f1719d56fc33fd5d4" + }, + { + "public": "1bd5d20d362c845dcd84fbacdaa8accaab50b1683143b312d183b8a445766b14", + "secret": "db2a2b37b879401117f31584e8570089a6c0e50c94aaf6993cdd6e9dba8f6f66" + }, + { + "public": "bf1df3934a0ca7b07e545c7669d9b6fe9dad431afab8826327dc422bd2cb7e5b", + "secret": "e0a661cfa2da179af2c9d217f99d92ace8f23e25d349a53a0e0603fc0b9e2357" + }, + { + "public": "d6eae08d4e0c706a134c99ac66c09e5ae895a680349f7bf7248f240d3bddbe7d", + "secret": "0bc188e3d253c6ab33e850a4891d181ef781f43e84b6ce668fc67f8858b9ee3f" + }, + { + "public": "cb3384aeb26dbeaacd1d342e34260e5e6bce02ed411ced681ee24bb9a581b322", + "secret": "2bae4828adfa1289916b9cc46b76d198ed18344e71d19fb8e54e328a307148da" + }, + { + "public": "55a533610c16951c2859cb333ae19a011928183b996780e904f2540684d2660e", + "secret": "4605c62aa16d47dc96030435217e707be59b13181447171f5b2181d241009683" + }, + { + "public": "20f84bd24dbf0546a07836a54a1d1262539793d27a423e6e122721054768d738", + "secret": "34e2de953abf5d6269ab686adce78dabf245de8d0cf9d368c35394c824885eec" + }, + { + "public": "14ca2cd9f41da25069352068f8436d5619f3b0db0336c2fbf102a3abd158df24", + "secret": "7e41f3f288b809a204ca1845c78f6ca33ab806445b2181b30094bf8662121d0b" + }, + { + "public": "0ca2a8c6e1204624f59b76a6fc126f1aa1007a62b9f7c2feadb08695c0d24265", + "secret": "2778f0e3ede0848807cf1bb4d002cbe31a063e6687f7325c3c9de9cb9d153156" + }, + { + "public": "6d3709af567dea674296f4390d8b641801e5daaa0835f8983f9f22657bc4186b", + "secret": "3196df92dc5598113678f1699543c3aef7aef299a4bc8fb261b62e0aa5b15774" + }, + { + "public": "4f32c9f4353ef981c7c5a9607cdba44e1e9aae3ac910c4c4471cf3fa2f0c0941", + "secret": "8b82fd5f8c72d0774bc2eba80466f9f07fc86d31948c9d3da5b37dd979d8c37b" + }, + { + "public": "a3693d7b0e475cf1593da3d62ed047cec9f34e3f0ef42b9322dc77f96c7e1d73", + "secret": "ed830539c84deafea948832de39940a1335244b5e6887126d74ccbadb260b8ae" + }, + { + "public": "02d4bf680cdf16bb26d91e0a6f666746e9222ba24f27735396f9831441d2b74a", + "secret": "6d9fd49aeac72b20a582089d24b901267e429338101efa67674ec4b0d41b0e27" + }, + { + "public": "078c7216244ebe0a724bb3690d12a5af8e53ccc70e975484928916b9ddf7fe79", + "secret": "6240b9c4464fb01f3a4d3b1ca94d834deabaa02940584985680056d39c43fed2" + }, + { + "public": "aba1f1255abc39f08d2748363bc3d8da7c678d9bba35d4c03ebf5553c2989811", + "secret": "6ca1ef32bb69e837a98e93fdb87b01ed3369bad33216dcdf68c874f38c9f648c" + }, + { + "public": "cbff7bf5854ac68e3919dedbc39d964023c56ea85cecee1be8760d5033d07d2a", + "secret": "f1f025401266988bb31997bf0a84b2166526d19118fbe3986f01d3aff7ad1c30" + }, + { + "public": "ef759fc660e65264b7dc1d8d179e29d556caf7285aa9156c3333679965ae565d", + "secret": "6f2a1c2fa336fa19c1089144c2dae8208277809675d46337e8b9d9e742c67436" + }, + { + "public": "3709227e6374adca96a5e076866bf61cb1e6f3741cc3d125f770cad596875917", + "secret": "f6a163f9a3f9754e02e4418bb5bd5de0842709bb1af22d571e3a63cd258a73f5" + }, + { + "public": "f0ed71083ce9c7d35576d74ad5236eed2e5f3924f364b4473a6f05b8b8fb5715", + "secret": "e6d6341893a1418e2f4dcd0664b5b95308f7a907592339bbb719c1c2db0ed819" + }, + { + "public": "6a710148dbac02889cbb205805fc4ac59bd5bab0d03d86b3dc0705a4a26afd55", + "secret": "5a2ac4d1cb7797f493f621e6f3edd3bbfc47178c272598a6e206e553b5c6b4cf" + }, + { + "public": "7a0c4baabcf16c4ef380bebb539f73431f4917aa62c2ac4fc655c9d4efeb7f07", + "secret": "f03ebafff8587eb9c153d6091263a40e9c459001af0a673bf337afba65931c96" + }, + { + "public": "d8f9398472d39682c1940c2d0d6a6d5c50f82c1ba8e7f28cf015b24c95811b32", + "secret": "2dead0874da930142b011a1f8497d9630d8c5041ab9e93f558f29b44dc92f9b3" + }, + { + "public": "e9a33d7a3162a388c91217880784da105c19cf1ec3fd6b39b029ef0179895833", + "secret": "db459e207f3d8fbe4690eff956530d4b85790dd11b0b86fdf20d08a8d2603939" + }, + { + "public": "784a6e8727582e2968c1acaaad46df379622d2b03f182907ef847c39dfb2510b", + "secret": "ca7536f68f252fbc00086d768f19f621d8f14bf28b372c95815a5d76845d034f" + }, + { + "public": "0fbe08ab1bb21d9b22c6040c07e01e73fd5b358dc75316e4b9ac6c688e55ab2a", + "secret": "667cd2f0cce04f241a427684911ce6384cf9d7b9aea4d0b1f4ab21a952584bec" + }, + { + "public": "a55bedf307ff63ea89e8fe72c5e72b82e915bdd32720e5e5bdb65414ca398d0a", + "secret": "5330fd596c78f1c848b4735a506afe2c7fee89b78bae655d7159cb2188e25211" + }, + { + "public": "6ea3b0f2730527007011343073cbd6ae137ca4c877db4a4b6734b2629095b446", + "secret": "741124ee85eeca84e90162011561ced350aa9213830578618c5088c30e6754c4" + }, + { + "public": "f8f25013a3d96a5558f10e17fb3a621ab2fc4fdb5534ac96ad52c0c7eafd8031", + "secret": "17b5b505440e76edc6bf29d4f59b84654693add66b7532e538dc16e7a67bffa0" + }, + { + "public": "38c63f26ecb6d76477d839436ac30c71273263752f5775807a21ea9aeb772710", + "secret": "5689a0beadb4e91f37c6fe3fc2faa08afc2b9e64dc8e7bd6ed07cd86e9179b4b" + }, + { + "public": "1944b544c3a7b6908621d808668a93079d4f6359b643e40fcd84b6f35452b36e", + "secret": "ea4a221ef36a4dbc9e6e1ceb9ebdd72db36fe4d51b6557b1f11d982286ae97ae" + }, + { + "public": "e41245f7ec196cb67d1fa3e50ed55cc522c04fd306274831f506c8f3540afe2e", + "secret": "e98611926124c5eb1564e889abdce5518b21ed7a0800ae463973812d395c5c21" + }, + { + "public": "42fe2b9845b8f7ad1157231febf65c913ae321f952d66f4fa26351474b5c5d40", + "secret": "620830af6fbe28c6175aaf94e6f6337df5010f5e573104eea2845b39ba1d471e" + }, + { + "public": "d0a3f7d5879f84edabcd2f6e4b0f1fac82f502fdcf4c56bb52815c6d1895af18", + "secret": "55a96821c2b96632f14f422231c56a8a106bcdec85001a43bb8245cea579268a" + }, + { + "public": "a6380a7f0723e21ccae40932f44c28e5b26a8d695a0c68fd1466975d26d26f27", + "secret": "a25d23d4dedb46e9b03078fceb74627ffaf31ce86c065d41bb6e38d6f2bfe6d2" + }, + { + "public": "ce974854024dfc1c9aaf2563740e20da0b552a451377583a74e647dec3db8e11", + "secret": "9882e4380f9f45603e7cadb01f93cc3a601920c461b5d93dfcebe4636270280e" + }, + { + "public": "6bfb5e6b3a79b2accc9915083a68c9cf7065c2e992e1b71b9bee3ff5fe041e0f", + "secret": "d9c52a54ba1f93aae63638d9ef831d8dd991a85790f21998c2d2674f678d982c" + }, + { + "public": "1c1e03d9d5702ad3f3112a29a84ba8d8706fcd5887de2cde340a7163f5ebad66", + "secret": "94b00bd613059399075f6b947b83dbcb6090723ddcbe720ee63457185a0a52ea" + }, + { + "public": "7ae4b96dd87d9963ed17ffeb6dd1223c216271f6e7204f2457aa04f677750640", + "secret": "cd68d5cf9e99b93b5e43074a55c6e23e4340aab9f73caeaf720cb8e03600f95e" + }, + { + "public": "0bb9a66e96bd6b8c0f21846c2c7bb5f9cf4527293f9cf097a59f73734e32fd09", + "secret": "78fadac3449567c0e4e4b05563c0be5dcccbb0d718f8925342db5383c6266eef" + }, + { + "public": "72ff000fcc97869858c2e8fa765f14d06cb1f603e9f0081e01a08fababdac436", + "secret": "a5cdef938311f0c32142443e791b20c127a11c48144169427e6cda2f5b3af1b0" + }, + { + "public": "4e2deb3c8a0f92933b5531704f7e715f6e34885ad370229deb9c34b7a9ebe61b", + "secret": "1075a77ee9f3e22cace2ff88eb56d93df2bcaefae2bdb40278c0b47ad2200507" + }, + { + "public": "5669066eb3e11fce08d151e92b3e279bef5f3b7e0a24e01e1ee995947bcb3817", + "secret": "3de102544d8b1ff850809421a9a35a9f0739dfd8f89c8048c5b1c145e4e4a47b" + }, + { + "public": "0ea3d7f905fcb20067871274e575fc5718bd804c50a7dadf098b7c2d3dd7a113", + "secret": "e88f380d8648bb007101e4430e50034c03ec3dd18c9e4efec9a8506ba72d99aa" + }, + { + "public": "5eff5941e0cc179bd70ed01de103e8a7bb6f86b4852503e8cbf225933075e42a", + "secret": "4f0df8e1bdaa02bd03c8801b3c06bf8997b5753a5ba2bd7f9b4f93f65f492b5d" + }, + { + "public": "bbf5a7586463d369589bf1ee6f02a041452e7d05a44342cfb0783be6cc4b0f1a", + "secret": "327375899539b7d4dd85bd39cd5f7ca5861ecef75c4e04c4faff34f06c2cba9d" + }, + { + "public": "27e022b1d485bb59ca24745eeeb8eef94fb6778e2934e59eb3f3afc6dac6732f", + "secret": "4f3a4bff2c25580b7327d7c06c729e4936e58802a98cd6c7e986206e3b6df2ed" + }, + { + "public": "ce0b75b43f3c7c30e28223393a929e6c254dd5e9b7d573d9f5ff01993b4b4257", + "secret": "2c783effce1639254c4057c1f7563af5a1d72e8dc55d6dfb7a184e290cadedfa" + }, + { + "public": "c5a845918819aedb25d4b612250a139b35e43fa6240edc2569b4669531193336", + "secret": "4c273af47fbf8b08db2535b27013c8d05a646ef2f3ddeb0e11f97dfd3b291f7f" + }, + { + "public": "0ff0eaf436dcfb69c3c6aa6d049c4c3fd2c38f2cc5658a5b556d54d5a8b3bd0e", + "secret": "557b8e1aa98d706653edca72562fa4f449caec8f977fe093f53643b278732141" + }, + { + "public": "99657f18974ee55fb332b08fdcb0c33215187db27af681424f2a7f2718df0b18", + "secret": "49ab9030e9f9eb0e2c41e407a62fc708cc59959a669829b666e72cbca2042a1a" + }, + { + "public": "1423b22532c6399218e67b4ed870720e2a8c6d2c2adb1b95fa60b5a67b40b014", + "secret": "caca0a38932751097012bcbd21634357c0d4ca8b1c7dcd2780c0b09d7651fe52" + }, + { + "public": "3d8c29bd72a6a455242451daf30ee7cbf7888f179cb9cb245b72ded887995437", + "secret": "841dba6d20314bbea8ee049ba809dd14407ba3496b7e0c47642c09acb2267a15" + }, + { + "public": "ae0c049c808f6509402d8dec8a9a5eda276006e3854ef415efd013b0a24b5935", + "secret": "e77f2842e8562ded1e740bf6616a5888db180fe7321fdc9b171392cdd94b2608" + }, + { + "public": "a340b1c44d20e8678effc92d5732da2521024f3ac898448d54cb235c33535b3a", + "secret": "add5c4f7e57f65f6ec2ae40171250ed278b23d2ffc41868b82a7f782e1783108" + }, + { + "public": "6ac0494a7571b3375016029a5a927eef9f223c3898d4bcafda6c36e480daf569", + "secret": "a39db787574e1410a539865227947418c184d3ccf30c95533a7438250893c8da" + }, + { + "public": "3a6ea8cdd339f346672e81d3b7607241774b460eb71e5da90bb39225910a6c16", + "secret": "631f00e1b8ddc743c35915297e22a734661c650b75963fb4ea2198e6f3064718" + }, + { + "public": "99ba6e97e7fba9b94cf85a8ee74969ba7e4e92b7204b173ec51ee1b163a0ba01", + "secret": "770aed7fac0e99d525766f2c959270998aeb35bd32cb5ffcb4ed96e71b7f1490" + }, + { + "public": "f233c7fe6665dd524e2a18a628feece7bf370f99d075fd6d232d2c55269e8740", + "secret": "5883f1fca5e21322b6aeca51fe487de159a9935960d93b1f10ee4fedfab4f254" + }, + { + "public": "0f24e6b97afa293d3d1a9d3bb1bd421117661d4aee6069453e465734ff535d7f", + "secret": "5c6dca2b8f7fd0c4cd22f4d40d8a9ac68cf72f56de9b6a8dd8028f09cd28f5fe" + }, + { + "public": "94512c7a7381ee7b49760a173d1969dbb1bafcade35f677434b57f6399817660", + "secret": "73c0870018173c7cb556467d80007000d2318acf8c069a713b537ed090459852" + }, + { + "public": "0fdcda990c8fd31559be0f8e5242b670a612ea83ba5ccae058475ab1afe1b462", + "secret": "0f2816f8d64dc4c60a9126279c43cead073bd845788436501f721d84f4928b96" + }, + { + "public": "b12c450bdf7eb33d5f2a7f6927eb842a934c4b8fd2c9fad5da9c72bc4e900653", + "secret": "859a842ec9fafbf52962e55737a3eb5a93b1cfb2046442ff5708b6c03eb47599" + }, + { + "public": "f779e0fd223fd3d080936152f9bc461ad6f96a41739c5ab3a761dd2e99ee197d", + "secret": "5412ce0afaf1a19eadc753fdcf0a3f5fe1f5b84f16ae0f2543c1d35e4c78e22b" + }, + { + "public": "8e69b69ad08e1a155d563d6c61cebfdd8f60c4d0b9c1225053abf42023b8147d", + "secret": "ea16cb502797f5405606f5eb601cb47833e41d4603f780d67dee3cd89535da08" + }, + { + "public": "81cc04f358b68531ae4be700454878675e7993e459e98634b1b0f85b4258921a", + "secret": "e7b2a429a65e0943df1a03e719b08e564ef6c27d8a0f3f4640b161a6ca3612ac" + }, + { + "public": "43ca5dc394792d7609bd9173b14384d531a943cd11b86609f0a9a8a549b68e02", + "secret": "32936ccbc3dd27314bf6e272874b6a4a9e2b9586f40ebb3999fec15db26d88da" + }, + { + "public": "0a149c5f2fd5477cd6cb85263faed3249c4d2ef4f39e54d26f7f8121bfdbb40c", + "secret": "4bb47cdca486cd97c59042016cb315f15cc3750302f34ae6d3c080aed2e1b94b" + }, + { + "public": "765a0208ea8eeb2bb93d0f0d619111de7802951293b1bf73d312e422b3396c17", + "secret": "beb27638effe9784fb1ec62cd9cc4bc440a2f6b8838b9f97ff64f118c8e62040" + }, + { + "public": "db856e8b163711e7a81729111ab1846ab6b30a601113475c136c370ca161ed79", + "secret": "c871f61c23561a6d5ade21f410694f1d63185942b991e2cc1035e3a66b608228" + }, + { + "public": "1616a26e0d460fc9cf6e8c4a0a4fe1ccf2e42106d6bb69b9a78745978e29f415", + "secret": "a70b88316568db7b2bd7824b097a39db2cede20f118fe7bbeff4c1db4dd2d8dc" + }, + { + "public": "c1a4d23e0410ddd90435358939cd1b77eb0dd6c925cfc310518110797190892c", + "secret": "fbda72a1a62b144fcb34e73fc0fd3f527c2e439b53c1d58b337584869d40be64" + }, + { + "public": "5b100ae4253253850b2461471957cfdc414dab98fdae9b93f0aa7b1de8bee02c", + "secret": "20743c8f9120c97494a92122d65e67b7ed9445545fdeb436f4b26dcc311387ad" + }, + { + "public": "fb22aac2b73c30e25489ba597d4e61100f1221465b43c373cffa5bf51a562a13", + "secret": "55b936f4a6947482e6f7d0f328c2614f0e50d930836296eec08075b504e75970" + }, + { + "public": "3be71e616be62d226fe5330d14d4b7c44563ea48a0210105e008514d47f7df16", + "secret": "8221dd80f55d26db1a4b05bb846b5cb4a8f1e10fca3dc4aed9a3ae877a4f216f" + }, + { + "public": "b57dfde4681c9d5bdf866a0b29840a62e4c142ae6734cbdd098c2663419afc66", + "secret": "df75bc00d9c2b8a4c0ff824c6a7c72f2867129675fabf9e76db2c76bdf786f5e" + }, + { + "public": "fb456d54be21c49326392b5d94ff3239536438400fe5db48bde6554eda32473c", + "secret": "a2ed62555aba3d26a4b70bcfb867f8cd15788079935420a2f596c8c511a4b3d4" + }, + { + "public": "4c17f3eae951c7a404f0a90eb4e61b3355c13a0e3ae45602183f8343a7ee0112", + "secret": "b838cc1490b1f0e8f1ebf9d282f7d7cad68bd3cc4f3eff1a57bd8edf1592374f" + }, + { + "public": "6d43fe1fcd19ea1438ab7d9aae68f301236a02ab6ae12b2dfd658d25e34c4b14", + "secret": "e91b3c1da135e2e274896b2c709d9cd7e8b032679c6d70ae3c996fba5ac96382" + }, + { + "public": "10dbb44ccfa38cb69346cba7ae28568c0e95917477bae57c35641122a916506d", + "secret": "9c235931bbacfe1adc4823faf65a41884745dde11b3b7728a8e16f52eb2d0fd8" + }, + { + "public": "e70b009adb587f2ccdd59512f0dae96c9e9f15398aad88770f5c9406c9ca0d34", + "secret": "71874e6d2597864f06275760477f1bcf15635d6e231ec4b913a9a0fc1f75365e" + }, + { + "public": "242b73fa8f7710506060036e4d0140f2f25f88527fa060414339d9e048b49963", + "secret": "eeff5d88eeba478a0c0f56519126d6b1ebd74df11dc78ba1e3f62ef910641014" + }, + { + "public": "01fe9541c7f81f53d64e9993fad435b9ee29129051279ec6106f220b5988c451", + "secret": "bcff27c8bf20290b605b3aa4a07b707ed4d5c69182516899e7d675ca5c5f8464" + }, + { + "public": "fb1e78bd678107e9cb8f9bc857adb6bca3bcfe772c660c62b984b09d07223c6a", + "secret": "5b25399139cd4e64f8a6286cddbfe607e02848df82c2b28b897263c0eeec3791" + }, + { + "public": "4f0452a4c9e64e0bfbadedf8c52d68c74086becc7e7117f69a1d005caea83e17", + "secret": "566127f946c88e87a5f638d13e88361aa74c71258ef7cd7c4ea7d88edbb24cb3" + }, + { + "public": "d4c7d8698ebd7a33a6af3eedb11f869bc60a4937f5209725db3720ceec959d1c", + "secret": "109064280670b364fa161105740388614afbca92832a955254246a5395693df4" + }, + { + "public": "0557c44b7497b21987a20e13bac7d5aeb62be58f309dae8dfb03ed99fe173b7a", + "secret": "b429742f66e440d41860606be72b1c8cafba11c6ad18517e18849e187f1bad28" + }, + { + "public": "fed8b07486f7ae733f154058c7b9939b7772e73504a86f49c3a27149adc16912", + "secret": "d333106729caedfb0a1f402f670a6559ef6c27ae7b7bff1a1a03939d6065e1da" + }, + { + "public": "7461d91c29403eb29d1783f16ae8b4f73a04c057e8ec0bfe80bbab1fbef78b20", + "secret": "a6878097445d8da1c6b11fd0c94b7be6df441c13afaa3eb161a99001b363b4da" + }, + { + "public": "81ccc8efa64d4201b75c07f66553f961cfedc96ac634708b918eca5bb7d7c32d", + "secret": "51ea675cfb7183af2840fa32c5cd9bbd8c96517b0cb9ce1289f49d09b76e7380" + }, + { + "public": "084f5eea0eec63398fa0e050af41e3afb70b143c0791ec1b23ae2d69c7881774", + "secret": "d4cd00de03c8ca01809bf943c6e118783af6c5777c8c4df0779c198784b829e1" + }, + { + "public": "a5814079094de679f4382ab5cd6d1ce4a2efa5ea07733f483c8695c2e75f4f30", + "secret": "34feda2f8b44825f4d7f01d156459d189f5f73d26d4fdbe078d311b22a47dd9f" + }, + { + "public": "014c39493e244ea935735b1884f49f54fb5cc4f0848d9f65c543133af02f2827", + "secret": "dfd897b8bc7c39d8a702322fc430ac148be9f7e221b79da74eed11b2eaf85ead" + }, + { + "public": "78366780afebd8555074024844e28e29dde9428c0723de5acb9eb9941003ee6e", + "secret": "c43334b46bc4794d9ac465abd73b00bef3b6f834848a8d6efb7a7092016f6052" + }, + { + "public": "af2da2cefb1baf8d53bea4b9696aa56a1aa2ac6bd3df73a114f1ad127e392b74", + "secret": "84e303cd760df55e529d7fa60660305810ad7e4400c383f99b48e454b58958bd" + }, + { + "public": "43c940b742b9a1a7dfef2ebea65b878e2a4e8a7b4a32004cc02242f9d49a983b", + "secret": "c1f497a832171bb71382bf182036c3554befadaa94bad5a065c3b75050f3ab46" + }, + { + "public": "8d7e9e71653d6f96f900a1d890c8732f8ae66200ee2ce57079e693aadee0e164", + "secret": "eab3a84609111ca3072fbab3f59011cf575d66b46e802900fc78c84fd8b71e19" + }, + { + "public": "d50b6655910afdab1ba2cef6d5184bfa8153e49f6119cf0aec65c9c9d4d20267", + "secret": "1802c190553935aa81a800147fc65c16f67da28d199a248f570528fbcfac5ba2" + }, + { + "public": "0dcd9a8fd40cc007608844326f400bd897e8aad098bc1a010f7d4736383d5f3c", + "secret": "51fc61ef00cd01e11af7f2170376eacc7a66e5e0b4ba04d21f66defe2143ffdb" + }, + { + "public": "98cba0836efeba5a1921b84339a9c2c4b52ea5f9269b15f27e2a1413f44e3221", + "secret": "5051705d39c7a52e407baecfa7a64acf3342f8cb08c8e4de7e58adb579eef9ac" + }, + { + "public": "ad415c620b33e469d279e4052336eadf9601d934e7991a9e7ef41e5cf718d34c", + "secret": "6e46460f407256601064b0bd0a7918db5737613fc107fe9b67f82ffea844e911" + }, + { + "public": "23943e772b4d1f00cc9a80d3125dd2b8146b8545a45499385ec939ac4de85c5e", + "secret": "ec55e8bb406f0f821045dc649c73de8346c8d7a87f4b236d0a83ab0b95d6628e" + }, + { + "public": "b64c6380626fb775623acd604196159f8ac2b0b9a8d666022965186093b9de1e", + "secret": "da5a7876575cf8fec83b8f23600829037f09c55272effe24f5824a82ac7dcca7" + }, + { + "public": "74df2a44fed9e23d8805c5cc84b99609975e4d445149d7029bbfc6a35bf76f22", + "secret": "d3ea6ceb22a23b1b05957ce3bef8918b8929246ba6e681e84cb8e02347ed181b" + }, + { + "public": "d21a5e87b7b6f5b787783775ff7ceb7b176859eb4f29f5cf53ab42c085845373", + "secret": "871b8a1882464969b198a84e87b85d3c9c747691719333be097abdcfd547ae77" + }, + { + "public": "17b7c7ce3ba2618acf496ab3005a59f7eea378e931933de2f304326abd7f0e5d", + "secret": "b5f0d78358600218ea1b69f150c29f7811bbb023bfe29c822ddb3b0415acd8f7" + }, + { + "public": "81b438fd7e69d8c3e827c96aa6d1ebdc963dda816c7e89443ab675a18a211165", + "secret": "8e2fe876756355a6c7668f6a4f033adcf19ca89dbff0ff07bdc119b6ec574116" + }, + { + "public": "489effc9ceb13319cbb8ddb75956ec1ec9a569d397d53b1709babb2819d4c96b", + "secret": "3cd6cfca013cabe84650233c85503de9937fdc324f89edd3ca5302d20f1947ae" + }, + { + "public": "3f541ba487d34a312a7a9dde375eca9b00357afa56d1ead9cd0b2c4ff9302f59", + "secret": "9d9d60072b9ea339d723508c3dcc516c8c7803029531fc403b344e18ead6f09d" + }, + { + "public": "ffe57c58512d8aa71206b80eebf8d80aa12b1a8e4fa19a6396fa003504ad2576", + "secret": "a69c78c08dba93ce290e1408dfc17127ef6049f8cc51c89d4f6a5cb023b64d0f" + }, + { + "public": "fc33042293cad32006e3ac50117c73e6f7e128c24a68931f67d96a264f88427b", + "secret": "d219929f85c3f91916aa4a934d01a4dd996038e39645b23b90bee60d7d59556b" + }, + { + "public": "46c5a9937776f685b15ec8a0576dae93273381432ce40e66e3b9fcd9fd713873", + "secret": "8af7d5d79e7b89f1e6cffbd65e122ca7eaec2c8812bd037901daa5d797ae3105" + }, + { + "public": "920e10d29b86828f2a2566c942555e900c761e4228bbd5473eaca4ebae2a8237", + "secret": "4e9e160a35e3cb087e71576dc58073837512ad957a11c06df5d111d410dee6d8" + }, + { + "public": "3aaa7c0d4879f9abd7b890bf2d10b5b2390ea66bcf6e6f6089587400fdf6fc6c", + "secret": "532802fba4c2a547a5ac567c17e629b209f8e849751b21b394520cc71a099045" + }, + { + "public": "2b9260191e81149023e9ea899c18c2a677d58dbf50d24355b0d6d135eaed841b", + "secret": "b007f29defa29d462c21dafc280eaac8fbf8a115afba4fe688b647f58a9f3f8a" + }, + { + "public": "e7fe900b47df3892b7bcfcaf4ac91ebdeddcdcc15eec0789d2c0a9644908b83e", + "secret": "f86b1eabc8157d9b38a8051600c35acce269b27b76a7ef254067463ca9905e93" + }, + { + "public": "8ba5f7bd38311d8fd47481c2ca921d53736c5875e696aa7691edc1a0cd8ed63a", + "secret": "4ad8e7c2ecad61002d9f8954a8a9ca905ca52e7e6699497bd89e88a3f011b380" + }, + { + "public": "f4789a2ad6ed1bf5b5cd84e76693435a8753c74997bbabefa6ae0da594fb3860", + "secret": "0ef09023a43bf6a02aefd3ec45f2fda3b040096aa668fe41abed6ed9d4ba1a11" + }, + { + "public": "1cf2b06f55322e4112c4a783becec9d45d4b6a3cac772bc2dc0ea18cdfce595c", + "secret": "a9b282da6b74cd0ab0897e33d1e8d0c39a555f9d565e98c654743099dbdd508f" + }, + { + "public": "5ce201879dab59872ac37de2a6a855cf739e197811ceeebd279ab13b877f7e1a", + "secret": "e0a127c2ee4fd27c552f3ebfd8ccaccdf26ce43686e5c4c11801fd06b4e58fbd" + }, + { + "public": "b9d727f412851721e31f614816df3fe6edceed547479221646b2c5757868152b", + "secret": "83e5410bc0416e134810d24a243d213c8cd92de14027774df52a70a7ee7bdbd2" + }, + { + "public": "70ebd9e8b827619207df4dd9fdc0f01b5e691df4675e09cbf2acb36f7bffb17d", + "secret": "927b2190e69e181545fda5606175b01653b0cdfa6237f7592b8289d3021f3820" + }, + { + "public": "57bbdb2d7dee0999ea5d743098391e96cc7983a801e814966e4b949c5fcec455", + "secret": "5e25e6fcdbbc92f41167a574a4f75bf44e3111d01815f810bd1b6a20f7e8f1d0" + }, + { + "public": "8d7cea0f0ab63fc0f6be354c305bb7448baf5f5037a0ce00e3ffd785131e2839", + "secret": "09647b21eceb7d83b67ef8748176d2a5ef3ce95de331aa73bfdd691571834608" + }, + { + "public": "a287de77e069e222a3841dfe71711a2535939e62b1b27f70a5b45a0a3fa3ec69", + "secret": "4339beae000fb7283f94d4321213d1526d791985fe2c466f3ad55463915f6ec4" + }, + { + "public": "855748f871778910b3086358acd24933fe63cb1317518fd8d69c46ce4bfeab4d", + "secret": "14141d5e085e64d2591b49ecc9b025a854bd6aa129a69d9859a3a923e77b5222" + }, + { + "public": "9d6417f45af03d422345f1e325e5a0b7082ddd7e01baaabab9dba40f357ed210", + "secret": "34651ff9c9b965d34765c5f63a42caff1ca8996b0bed534514f61a9b89e19f44" + }, + { + "public": "7eed3386950ae1576bbcb8212a4a3e06d0bff27ac18a53f98679f7845d96a524", + "secret": "27616f9a7b5bf9f708eb68f2aeb0a4f526cc19690e383cc6bd7a984c86fc483a" + }, + { + "public": "1235e3b2333756341047dd619266118184195429b63ae22bc11b44df43b39400", + "secret": "f748209bd72275bc14e07b6a6cde7aeca7d716e0c52f65aaa029ebe6e8b71f6f" + }, + { + "public": "73d04aad12479ee79a0c80be62c9b305bae9f2cf6f64eb0711964a985b44ec54", + "secret": "65b41595baf7952650ffd631bc7fedf27bd7df2212be42a07953b98dff646ee5" + }, + { + "public": "40881125518dd9ec9c57c238baa168eaee4f7bdb4ae29732ab50adb87c1cd72e", + "secret": "9c5e81766bdeae41d1c0376461069bfa4b4ee93bd72b8cfef3088cb67dc460ef" + }, + { + "public": "eaf8f42f98cbd3d1f93f6e772754c20bc6a7f0b85b317fc3418bc772da98d37a", + "secret": "9d15236267f9614b03a6a6cf3e7447fcf9de7a645285f56daa348e0fc10894f7" + }, + { + "public": "621d8c9a3ac7c2313d0529d1fd55e3a98d5827b74ee22295a48d4cbb59c17a0a", + "secret": "35d5815a0a8dab2c3aa3fa83e68d7f626135ddb0c94e96af98432145373f5b11" + }, + { + "public": "f95e915fd3bda2cc4c1d51cbbc5a05b6f304e282ec936e4ed8f4e17f3905c87e", + "secret": "177040b621b4ffc60bccb5aa4d4e5501b2d3a55cdd80cc5d64b93ca7a83db3e2" + }, + { + "public": "6775f6c7985768c2ec722e816e1d9ff195aa41dfefeca2ea6f604addcbb8c726", + "secret": "06dec3c72a18b48a8a5617d030461009cfca45d5b8670dd1baf8c429aaa2c81e" + }, + { + "public": "fc44ce8adaea08e06cb18791f686498910068f1efbaf71e7aa396d68ff0abc28", + "secret": "f1ca54696e925e0073523550a2198a49d81590df0aa5778ced19e486295289f1" + }, + { + "public": "06ba36aa46827cf5aeeea0bf56e2e9e04d76248b0b33b5b72077ee1c36f7ba14", + "secret": "fcc53eaa920eda73d6d1c813022fc30ee472b3dc5ab2c0051e8866cb95dfc079" + }, + { + "public": "86aef10342ed1e14b2fcc72c93866c6ad4d0424da9a83172b6c308b33116c07f", + "secret": "13fea5d1989ae32701d1dd27090ab2c5647ee9ca38f15d846fcc33391566d58f" + }, + { + "public": "5d3aac592d5cd50d43fa263884704e2f2997f4d9e68bb597937fd243b716584e", + "secret": "4ef59312e76e1cc2923685c1c8b0dff7de5ea40f030cd03517286f1e22227935" + }, + { + "public": "7b8392f148dd47623c4ecb9dd4685a105f282986735b4ea615b75ea19182c611", + "secret": "9681fa6951c9fec62b285eab591cee02e5c865d08e24f67c9ef72cf0c1e4421b" + }, + { + "public": "e00624b93e378541d897b78c4331cde253986dc6e39a5c1b8d8574e89e055114", + "secret": "25aea5401695276d6e38dde3fb550e7f73120e4dc2dbc055bbe92487ce95fb13" + }, + { + "public": "077fc3c568eb72dac6067ffd5806b6dffe34e580e07a754cc2e763c0d347270a", + "secret": "440cf93ccf20294225337611a11552558c10c4db14d9bb2945d3c75ebbba76b0" + }, + { + "public": "7c7f46b58497c86edcfd39c6882445f6c827cfaade35b1ebe38fb75fdffdd046", + "secret": "ba07fb49a777788a62f16d3a1fad293d61301735498f4a73524a1e180cb5ec6a" + }, + { + "public": "7c5a5d8e114ad5bed1618791ca5b22d93f872cee234c0c74e5dbbca0ca84650e", + "secret": "d952957ffdabbeb6253693291c47f72778785f17aa91cf9d464a3652da48a5fd" + }, + { + "public": "b6360d66f4cb38903224fcb1cd0426dee32ac6c5eff3c677008a3f806c834c38", + "secret": "e7717faa9d3ce741cc3b2a145967b13be9edd66b61fde14e306a035755ddd076" + }, + { + "public": "144bb206422fb10dcccffbf056e7285549055d7c0953faeb84b11690cff56274", + "secret": "73671261e735aa33f884d5c437e736f468fef0252d24e2c36cf26764da01a595" + }, + { + "public": "37352d82aa24aee2f5637fd4defa9ebae87641c928e6bad3e16327c3c761b55f", + "secret": "8dc3e677569c8d0b071b7e583c2d0f0a5e2db379d630c27bdea65cff6f42cf29" + }, + { + "public": "60309e7715dbe13b9fa25786911db79375597029fca24ef3f433cea450fa292a", + "secret": "dbd3bd2482be6ba69a9a345b576be08ac8462c01894478798970f8179604a1d9" + }, + { + "public": "2f61a13cec9ce5f7e6aec3fc840bee39f438f4cc48ffc0b9db661d4086cb073a", + "secret": "b3a425442e99aa622f12bae44afb6a3cb8671613a5a6385e891dcf9f3bd95f66" + }, + { + "public": "d5d5b9b3d64506f08f74ff741f521aa8e05e1d5c22b31f122b1eeb6020a44222", + "secret": "e11063fa7c740e6eb4430245e6d52700cb3029a0212b6b93aaf93bc908396ab1" + }, + { + "public": "ee8735cb13f42a2fdd99c647e6d90c51211c06f6ae64793eddad67bd0f8c0322", + "secret": "f6681d46a6239978562e3e9b7c794f17ca87fbe31f80bf855e7c8a9fc0fbee2b" + }, + { + "public": "b05bf971722ada9345b8108abc2b32e2ebd63558f2c375161a38f82658f95503", + "secret": "b8ae4de15ab23b467cd839bff0f3da8580dbfefbea534205085d50d7e12709b4" + }, + { + "public": "d136bf21b6ce288dbb86b7ddddcf3a9ade093466f2a4f71620049840d22d7552", + "secret": "d5ea08ddd3b332a1f6c702d2d5aa25710fee1000f00a220d0b4ce5fcaac8ee25" + }, + { + "public": "13888549083b332860888441f98ee5a8a65814ffaa7c30784c8206d743cb713c", + "secret": "bc823320946b4c507948249b4ac16b21cce121ce812ad51e131be91380c0c7c9" + }, + { + "public": "4265d75743837a5b1e5f0c773ba82cdd0937aab39803c0e4374411cc921f4a44", + "secret": "8066428e1789edba02a8b4322f8af310313fc222176b12663e615d96bcab0472" + }, + { + "public": "6c2711b6cbed33ee3323c7cde0ed30b2e9493a3468da4bd5018083c18390b40d", + "secret": "6b9dfa924b93df1c90f8ee52877552f6f0b60359dc1d9ac6cfa778c31d431ae3" + }, + { + "public": "6a6f0bd1c60db7bff73744a1214e77cba95f7ab651673016cbb2124e9ee71b0a", + "secret": "ef6464b359b31c511b12d58d13969758fa4f0b2382860e93e8d2f82b152f0fec" + }, + { + "public": "d57c72bf91228167bcded2e7ab20948f923815ae07c3b51ee0dd63f21392fd49", + "secret": "fda528da59a45ec48bdb6de1172a320c756bd8d8be1902c383d7f6977b6c9c8d" + }, + { + "public": "804aa4d46d3c910069ebeb86ce8b535aed43b9dbf70a04cc0285c9c1f613f827", + "secret": "cb6ab59edca42fd9dfdb140a9ce346bef738142d2e7c09dfac0426ff0b7443e6" + }, + { + "public": "04ceae37c29b70b10924e9cf541dadde24671a7a5094d0905e2883312ace6d3e", + "secret": "769df69b8da603d3ae75cda76d5b6b0d04b71f0415caa9742e5e8803540386ea" + }, + { + "public": "6d1a6d6b626a513466c38f269a6285eab5e69feca09e1cfd3bb2d8244ba7c834", + "secret": "7dd9d9d2ec18ad181c28d161f75a38ac1c219427689d0fb512b0e55de71ddb2e" + }, + { + "public": "a5f7d1bcaa7e336c6907cfff05d979374ff8deafe9587fda55950017d5acbd3e", + "secret": "b0b9d1cfcd08fd5780549c18caaa8ce3f2817912103e196038a561d921035562" + }, + { + "public": "fda4d2451ce631121677efc088a0540655f1ce15d3d9d9910a9a0197ad20b538", + "secret": "0c7747cb369389baddc5b83a4a277b7afbe25083c5d25cdce7b8d2f9f0aea114" + }, + { + "public": "589f85095f3273b1de05db1e00c53d2a34c47b57533c6f89657ee90ba8a8d478", + "secret": "fd9c1f185348a862c07da47d0c5aca5dceaa4c81bf4ffc7487800f2e12bde71c" + }, + { + "public": "15aa7cef57863b0245069f553893cee448ec9b61a25c4a563cb28b36b239ce52", + "secret": "2bfa956b219b4a9ab578089509eda1168f2196f02f7a7760bd6e519a21cdbc26" + }, + { + "public": "147255923e1a1982b73ddf032d8d51ba484f7653249df47510ef2f001cf99e42", + "secret": "68e9ea014fb450a6d81a7eb7ba43f309253861a61097dae59247ff608d9a7305" + }, + { + "public": "6e21a15e1a9fa04504e02a2111e0d8f0d2fd4ad3f8a0bd3606dddcebd2969b79", + "secret": "26f26a0a0c0b573afa0e77f1413f805f5825d9a3c730abc7597569f9da311f9e" + }, + { + "public": "7734cbd1d586bc77e867eb14b219f63974e9724b8ac37818c2d17add5603f83a", + "secret": "998bb150fb14d68ddc6e2c9467b8141b08ca1bb59a7a184052bc5d737c69b648" + }, + { + "public": "3413300f46d28a7fd6492fef510a18df6ed6031346ac41c8250766c6d0544136", + "secret": "e3dd37744d595596f03204295682b73fcd24096d6c0832543407443b9f7337ae" + }, + { + "public": "6cdcb0b4d73ec666845899e8500f4bb25795e2657abee79c434c8d3b8628cc01", + "secret": "6cb3d041b8978b878da9ecea6bf6b680272e292a1f9ec4f2f210bc68389ab104" + }, + { + "public": "12db7f8c5c6db8c77d7b5682430d3d06a6e739b58b6e45e34639758d89276700", + "secret": "93784879b17b1184c65dd918dd06aae9cfcdd047e509e70b7749f9e11121d5c4" + }, + { + "public": "69245c5247282b7845d6dd7c6841beb8136efb58a4b5891dc2d5641977a4ee68", + "secret": "19ebbc890d8601d2e7e0a044da4b03a13834ed4fed419c621004ebd4b3afcb85" + }, + { + "public": "aeb014810c0f1bcc438f5a1169504e8935feca342f9dc8b5cba9c3884917a220", + "secret": "cbf28cc8663de65ea78e56af5f1353c25e77175b338059d814ee6591321afe38" + }, + { + "public": "56975e687bce30b4417bcb0724cbdd345bf5f44140035ac55a7e8d25b1ed8e55", + "secret": "70caca8fa29c031c7784b5674cf09d330543392083b20bc6304cfc8f2743a16a" + }, + { + "public": "54be305e1fda37224a58a5301fb04594e09adf1f4824121a170ea947ac5d9c05", + "secret": "4fb076d283c04f9a6e6d19aab3cefeffe7edca3556db179befa162ad36b9f043" + }, + { + "public": "942caa62a801f1ffe906b994be8b186438133506069dcb70365d7b9812feaa26", + "secret": "5d9b1a1c1e468a881df1cb0754306821754963ca57abfaefc63d618bcbf03b0d" + }, + { + "public": "bc3fd42006effeb4d66a6f15c403faba0ea5ef7090051badd7859d8bb0477f01", + "secret": "543e94729d0ca5085b4c25e1c760962fa81b5316d3d674f5108e3c85fc7d5489" + }, + { + "public": "a80d0830960bc33e7ea8ae8239e5df686b6528b4628e21af8477e44453cc0a52", + "secret": "b3201630e31dc7ad5ba73017b0dc4171296fe72e4016e95c81853f775ae8bf9f" + }, + { + "public": "f5f5cb181f046ea4c17ecd1b5eda992318a7194b3560bece88bb2de12d78a863", + "secret": "e6643b1f08f9287e91edc2b4c010a19f278cedb9aa127d9873c56859484962bf" + }, + { + "public": "60eb4e500825774aff861e8d1ade23130a20e18246e2d6c443d987fddd272f31", + "secret": "f487bb3c271c48e9600538f7048ae95406efac16dd622669008ca91e741a7595" + }, + { + "public": "5e7db7913618ab264e6e84604f77876ee5b54ed35cc5926a826773410a155f2b", + "secret": "bd544b317920e18a73ab7652684b57dad813da598a8c4d5429452e372f423b62" + }, + { + "public": "5514e5e9bcfba7116cd4aa5b47c5071f29f0292915ef8da2c7020f353741221c", + "secret": "9334d4e82d6bd8fee304aa0543337e371beb0e36e6f2bd76a0f900f9c540765a" + }, + { + "public": "dd2084da1ac4d31bd2aabc354544bf20364e597af5a9540f1b23302d8bed2d45", + "secret": "3d4bd068f26ac5156b9ce740c39b7e08641c32aac54a3532aea001cbb38acd49" + }, + { + "public": "45e854af2994cf247cc8b60c6280d4f13c08f64217e028afa464883c57876650", + "secret": "9474425fb983085b9f437dfbc0af3c1714be0e41165d3120cb527bafed55c42d" + }, + { + "public": "e860439e3e9084309857c6f3256cb320f004fd94d36ea39a5e3311514d108850", + "secret": "d560d0c796e682e61de7ee1d368a09956258041fad7d6c7dd6bb2688f19ba2e4" + }, + { + "public": "9eeed3fe087ab3d3261d75a6cd82155ea1b63e9dc244fd7bc00e37de49e4a840", + "secret": "3e9f65e499ec39b91a68407e932c88280351c9c81861caa04d6688783333d5c2" + }, + { + "public": "0ce81007dcefb2ed31954079b6eed84a273cd7d3f4b02be7dd2f4cf4356d4103", + "secret": "da41504e4dc1782cfe9e34a7c7f2c49b99f397ce182b0a4fd71cf7c295c1f474" + }, + { + "public": "fa961ebfe7558c40a574338020be7891d7cd49a508d5fc69db694ebaf7148c6c", + "secret": "6692d3e6f708db945ab4f72c853416355e0a5738db8b59c89f402d3119414c3a" + }, + { + "public": "241130a3eed3a7fdad6669716c84ee2e7a2047438ad6307930de3e5e96b92461", + "secret": "3ae9ced7b9529e8c6072be5caeee1f4110c1f30dcb1bb879a27639c8890fee73" + }, + { + "public": "1d6e45f2a4fdfb3dae3d9acdafb2e591b693bfdfe4f58af46dadd822c7214924", + "secret": "386f99bacb84e2e5b460e5e49a02cff64053ccb4db3763bca3b78e8d45974790" + }, + { + "public": "d1e1e13c7c71b33a525f8b37382b86c238a21cc8a02fe0bfa20b007be35f085a", + "secret": "5f20aa8e513eb3145f7d535f06a9b77ab2a8589e601fd556e772512f2823a8e9" + }, + { + "public": "fc239a4ba46c68778dc874be3fa9e4fb54c340917528bf8585fc17af1b414c7a", + "secret": "1ab804f852495f5a1bcadf709a5cb9336e665e5f753afe51c05053681ebe46b7" + }, + { + "public": "2c4e0874bbe36a11c45a2a7246fb15e8c95e085bf5590df560fb883ed5f34432", + "secret": "a83da2d8c0389b7c2c1191c57a802ec5b2f9677327148f1c03cbbfbbb8518d93" + }, + { + "public": "c69204da6cea251e49a971e3794a7224ba66881ff20e07790c960b7cf8f99659", + "secret": "fb29b177210a02aa0ed2dba630371c971171e97d3377c9a1824466ee0c913fc5" + }, + { + "public": "f06a73a35def28d22a1da371b6fab270dc747b29b0ecaa02a6b3b74113c6872d", + "secret": "9db5c9e0fe6216f1202534cb28c93a77db6e210a55f023d1fdb8dca3550d9c6f" + }, + { + "public": "ea6461069da077c33a9fde81f1460ab3e9827fbd718b737d7ff18ed216d2094f", + "secret": "4ad71da558e79c3e4073bd8c54a6e0d14715f9fe85a591dc53f4304cf87e6e67" + }, + { + "public": "006e26532f7785cab6a74c3fe93570e158282dccf7fb1a7468d9c66b686f5f54", + "secret": "ecd7c60c25dcf6e90bd85d08b4549874c530286f2a6c5c5275444e663de5dea2" + }, + { + "public": "32c43164e1f1539f3b085a18deae0508d628cc578666cf9ef5952ad54aea1f5b", + "secret": "ac40bd5d5db20f54890bc8471c73aa6f969c210e08acdca317bf401d52641fb7" + }, + { + "public": "55c2e656b6cdb4e023c1e298a09add41993dd2f88a70e98d586da9f0fa58ab47", + "secret": "05f2c35758acb44072eac62935ebcd6671716eca52b7c3b6ec776588c3c545cd" + }, + { + "public": "8c365d2bc5ad8d2747c95cde82e662b4e3061b1c05c5b89308fb37d7a34a3217", + "secret": "5cbbf9eb991b81aa9ed94ea1993da93cdad786891955609188507e82d1cb40c4" + }, + { + "public": "fb3493a292e45107df55a848cf47ea44f36342c4716700f0d48afba906965272", + "secret": "44b743c79839434987f4e2f92796cbec499de5764671ee2f9557d19267de30fe" + }, + { + "public": "41569f7142dba53949e99a8ec51a09bd50fb8e37a34e2dee0aa8755659c5da5a", + "secret": "7498f97e5ad531056ad0f30be9c989c59d9da670a77ebdbcbab48f19248dc65c" + }, + { + "public": "4bef3f7a31d21488f27efc67de130e3eb6c67ae96743b8bf80283fa599661318", + "secret": "01f0954a3c2b94c37e8715ef7e42d65340afe5633b3dcae729674798684e8f80" + }, + { + "public": "e23769c2aede700f41d0c04922aa91b64b6280ed8aeb73c2c85ce9f3759fbc18", + "secret": "59d1318513703241a921dd1923a74d1864845ef3fac585c85228b475a45d1a5d" + }, + { + "public": "c9b264dfa0e4f7e0fc20744d881267dd76d7dc118c600cec152aa2cd9d73594c", + "secret": "99edc62de7d9b4d979a33165d85e0787af48a5840481031869f40bce2527db84" + }, + { + "public": "73f5aab6e4345bc67a32f1fdee50227c9a6ae0d1c506b845adb6c99042f3b64d", + "secret": "17a780c8099ceb98d50304dfa9381c7c27a1619236e8c6caab18c4a3004213e7" + }, + { + "public": "64a3405b0d6af938271fefe89b1fa8ec24bf416eb3deb0dd9b7287bff9773e16", + "secret": "6e8bf7080a46adba9908d0f1b38e2bc2f17d6b95c5e392c7df53458f81abe6e6" + }, + { + "public": "03fdb6d182903a68616f54342a39654717f078091613e9498f301268193e0a68", + "secret": "c320836d137c77b46442f90fcc6b493c00460c4fc380f384772f8de65b92b943" + }, + { + "public": "21dca878a554dc6e27d9c9d39dccb6182dc89e9be3cbecf49701890da733af7c", + "secret": "52047454477994c320b17a4400c284f17aaba88029b942620c22ff4da9476a49" + }, + { + "public": "042c0e805d7cc5cfaf959d8d68912715d4a06a4b975fa0d514348f81ccdcff67", + "secret": "5ae57e19a9274df2d69144a4b30bcd9e4244c8082057fcf67ea229d0c8caf75f" + }, + { + "public": "a9122043318310e035f6aadc5d973f88107fa9178742ac6174691055931ff746", + "secret": "545a481334aa8814509c5c5eea83496aa1ec5a0f33257a1b0b30697431bfbecb" + }, + { + "public": "6fc1619d2f950668e4f9326710f52363d09812e5acc89f002ac6e54a52243f13", + "secret": "e2f51c22ba8a50149286459c0884448d03e52cd00f448604b1e08ae0aba6790a" + }, + { + "public": "acd15029368b32d118037f232a7e9a916d1c3bcd27a61f4cae7b3f7744ace571", + "secret": "6ffeafce4e6c453f6c15972f15b1ba2f1868b429b2372fafd48af32701eaf5b5" + }, + { + "public": "5b3bcb49938b08872b9232e2257038d3e408201663ac284dcd3277a0f9290c5e", + "secret": "18e4ac7eab66ef92f604c839374d7ce1b0cef7722954d9455e735a69c0062348" + }, + { + "public": "bbb5a0b5bd3ccd5f778730c5b680a9c0e8a2ee6528357b8cd36a4f6af3d19d04", + "secret": "f4fa6c58de0f0df421ad52fd171b966a2c26d258552a425c1c97b9fdb5c3073a" + }, + { + "public": "8e44e590910df387d324684e0d8fae291e6b9ef85d6954b70dbcb3745890b160", + "secret": "12e7d34ebf29a60b0424be890a748b7611f226751062af79321c0fcf53e48178" + }, + { + "public": "9b9619d756cbcd3afa8f369006b7646995ca35f77951268e8320b5ae14b2037a", + "secret": "3d2c9f37b8ad1de44a5fada4c872d56c4e73a67eb1dc5076d52e1ac2b68882ad" + }, + { + "public": "5c30e2ef7b8fe46f5f8e2a0d1c5fa6563549fbd0846c4fd7c38b001bb62b535e", + "secret": "39cf0a6340d17a00767e3eafc44ac195fce1f6d8e6d8d7fd237c17f41e3b9bfd" + }, + { + "public": "b51ac8562328c85a52fa20de7de6473fd5e5228e7cee8e466bf32dbe9c13647f", + "secret": "5d904793db3db3b4f56f61e1bf33aa99dc0f72e4edad40e35a867f0f7323b50b" + }, + { + "public": "46b906a54f357d0a5c74a29e52be99eb980be9aad69c1542aa5d755a29e0ed5a", + "secret": "dc21e747658180205df09b53d7dfdf879771f3c9ef170d0bed2737b0e55d4ad6" + }, + { + "public": "b653af87351e38a1ce4c58086e77aedf3c804ab1cd023c7d4b1d923011eede7c", + "secret": "ff4a000a18004271777d589422019d9d23778f3cbedc571a4d3af7eb9fa5c11a" + }, + { + "public": "256c147671512c27369979d034b0cb9687e3532df253bf76d74f7133df9d6174", + "secret": "50cb6583cc112ae4fa210d4b0522711dccf16690bffcb0e9541b132fb8c5b8a7" + }, + { + "public": "513906968b1002d2e64a82298c577a9c70fada420b87ed3599c93b25c2c22403", + "secret": "97b83bf153af4a0f435e2abe8684f379db34aba4be888f90ded9024eeb10ffbf" + }, + { + "public": "db785ffd57270c22604cd943bb890b8c82f43e915db0befe98cd81c42df5135c", + "secret": "61455e486ccf1f3c40be2567f94f5dedc16922d1a760644baf2c371007418b41" + }, + { + "public": "d8ad195616c5e825c4a2e3604c6e5acaca2137b4b51e05800edd9ab02c56fa73", + "secret": "8147d2e374cbb7e2f6fd35f4e6fb9a0b68ad79a1435d54dfcada5d56bf8dbe60" + }, + { + "public": "88d7d389b6e05bea5255a7fb9ff2c3c26a9331563445a380e7f4216dea4c6260", + "secret": "07e98119bd6f7fa7ae2332e3533848c405925b8fe41c0878eede6c8f56f37752" + }, + { + "public": "e91fbc64f4fcbd92ee01505e9be7d03364cb29f4f55b0a7d3ac105f451eb8d1c", + "secret": "9e0190fa55c6c9ef8bc83adc8241d1f6b8ebeec2d823239db46191d4131b7310" + }, + { + "public": "ec44b62974b5f4ca0e2639423f008584dc0d662fb7ddebdf75e8f5a4e7eb2336", + "secret": "8fcd65341d62e0ee00f403715bc191b0ae55df7fd16c8db371088ee715c1ca3d" + }, + { + "public": "b62d9236922b3083ac6b1fbf82e56378969f29c01c4369d5029a987c6bfc1811", + "secret": "285a74af5cd7ed5b06f7de50e7b5e07bddedfdb59a3771c966d4d6e3eaa54f21" + }, + { + "public": "952350b6349d34990da4bd988afb47a1963c50685b0d66732c39e98ae169ba45", + "secret": "7ef3a3669f7a7cbc3f9c024710700aeab9ede21107ebc34199ae64377707835e" + }, + { + "public": "c5c377d03b2ebe3134c2e2ac8a7250794f37a232f7f309e65daee8c1b9a2f734", + "secret": "a1bb4132f6bb7c86bab0780fb9f82b1fc80e8ab9b67eca387e9c76304940f557" + }, + { + "public": "afdc9e47b50ffde40e0614291acf3ca59e548ebe5183448444f9b77661a1f32a", + "secret": "e279d2043d1ef090f05f60a9a5556a513d52e48a7aa7ea89bf8fe85ed986924a" + }, + { + "public": "0164ff434ecad8ed5c2beaba9ce4c610c895b1ce54fc8293a954a0817407e109", + "secret": "8fc46f49ead6b7675f5f4018610b142957426b9f8f4ac9043d8216504546ca11" + }, + { + "public": "17c05bb31a287d5cbb3b4c95c879bceacdf39152eee303f745bbcbc5ae1a1240", + "secret": "aa88473b318d1fc49037f2ef9cc9dbcc3dd18e545555c9c3349deebcdb54cdca" + }, + { + "public": "2e725a8d05e1229b37efae480de4fba6191348fff88f98bcecb9c8dfda1ec81c", + "secret": "a7ec0952a486288276909bb235ae789e477437d32b70325b8db40f8f74e699c2" + }, + { + "public": "5e9962807d967df7758f6bf1bc10edecf209927a203b98bfbc6087eebff99f1b", + "secret": "0d3becbcdf96933948f59f33395bc1012fe699cf1c7f98cd596d5ffd3ba3dc0e" + }, + { + "public": "d89e97d6ee033b3272e25267ad552ef56c7b6745c734b15d6d1d7141701ad243", + "secret": "93bf0780ccd4714338b0cadeaf8d241fdc3acd9f346bf67ae58a7088c647ca88" + }, + { + "public": "3718e5b8178d00e829f9d4fc9f9798a3eeff9d151c01c35bc3f49b81f7184265", + "secret": "4a7d9ccdd6de04ca37add1671630a438f5c06a14350cb5254ee93e5d6a76b551" + }, + { + "public": "3668eab4ae67487312809487799be727363f0b685727c1c1f7fee315470fb778", + "secret": "9a4edd4171ddc2cd22f40992888fad9afa5b5eace9c0203cc191c7664a9a4571" + }, + { + "public": "a73a9fb6fdcb788c30d14948b46420764e848b2a997989f5a4733649ff505677", + "secret": "e49d821d0f021fad289626bb88016d399af174e953ccce281c92504750ecf2ab" + }, + { + "public": "59c39a45ba768fd47ad8d174b4e05c6e92379174bb18bd0b40d35d63818dda63", + "secret": "7943c5a4cc161198ce51e013c172971f981c16744ad2fc464f2637480b821e85" + }, + { + "public": "aa0a94f992214a680f8b9b36796f32c29e788f45ec8dd8512155a2c0f304b926", + "secret": "49db0b7eac043592b6392b4db91e1cebfbce4cfc875d031a9666a5d309f3991a" + }, + { + "public": "7bc1c24a2ba6c58b9186495ecb63c6400a86be099f379fb73b4bd9ce47636458", + "secret": "cdbf0e871d56f5ba22a8dad7d9b6b71a76ae537fafc07c2366c72a7a3949f2f6" + }, + { + "public": "bb206715c5000425a202490adf30401d9d6bc4786b5ca3bdc69edce3cae74b5a", + "secret": "fbab6a6477e250d1eedeb7903af503a125f5d9e1ae1aa9dc1d9dde2793641ab3" + }, + { + "public": "443cc2a2f9cc6107fb22f416fe7cfcc952c3a6dc9d08c1c81ae7615eb9ab5872", + "secret": "208cb7c796debcb14b1c3487e0a9ba6cb842850069ef13d735c627359e651dde" + }, + { + "public": "a82ef97f2ec0489309e830070f7ce16b2df30237ce5eb6dce080eb3cad64f258", + "secret": "5fa525280d766b6a46464dfe5e905af3cbc2c2da323fb24fafe85604e4ddd2a2" + }, + { + "public": "adf84eac414fbb87cfadf7cefad334a0ba370fe1ce73c5f8abda29ec1ce29f2c", + "secret": "68261af16d221e2eab1b46cb9894d23cdc3540d8a958e64521d246e1d8766ee7" + }, + { + "public": "b58f8c4221db669518e849be76ebea6d2b6827918d365cb6be30fbb2c8bc762f", + "secret": "07169d161bb5695fe11edbcc86e9f1e07385908230bc9e8a8f7029963be5f445" + }, + { + "public": "87983f5574fd51d9159fe3dcc16d12a90686f6eda4a69a48f72660d71a436455", + "secret": "670861eba4e12707984378081c62c50b79e98b1c322ce11835d274abb0a19103" + }, + { + "public": "337c86409a95ff937dadec8aea07387933a399242e3fd2af3f4698dc68716c4b", + "secret": "d6b783445522a54e9c67535465309f3bcd2717226d26ccb53fcc749812645e88" + }, + { + "public": "ed1f185c7dde509650da2406ee5f6f3959f391cd04a48d8d52a733c371f0337e", + "secret": "f40fcd14e8c26b07fd45c6493ae70d38deeece672f31ba9c30510bbe41bda71f" + }, + { + "public": "fd57a224a89b1a63a6e414e0af940d59d9b9ccd7fc5298c2010d1a7b7b61d06f", + "secret": "94d8c086e2f3aacd18a895e18718b489e907e969885e54c828addc92ab04deb3" + }, + { + "public": "d5d75fbe46e6f169992ade9f36e6362cadb2e57eb8efd8ee7d1d1531a12af703", + "secret": "e15a5593b1187da516162d294fe9bead3a519c7f5b2616c5208f60a5f8308f88" + }, + { + "public": "1d90f6af69473c19cb9c4515c88dafe5f104acdcb8d62105c885bf32070eb133", + "secret": "e97e4a7a86be0a40861ec76ac8ae7ba4e0a90e53fa4cedf3d8c860b67905897f" + }, + { + "public": "5842d29ee6a9f752bc0b5bdf7b614d65c5839b99dd20e532d7311addf75ce769", + "secret": "4941852b877820239d3899629ac728aa218ec2bf4a82cde477462381541ba134" + }, + { + "public": "4b153687e29978f67ccf00c5f97f72de79c2c21a111be8e2f4e8d1e0461afd40", + "secret": "3061e7ead725f74ef42e286372eb06af700942d4a9b28f9faddbe438e48c39f6" + }, + { + "public": "5431a01e0b625d9ac8a607919d286e2ff7b91627bbd94f591d4fb291459d2b72", + "secret": "3f13ff8ba6e19ef59bec324547dd2fac32ec73782ef1768eeab13822c4fc9f2f" + }, + { + "public": "b6428323816d31873ec8d36d043d86f4bcd987e9f848a2014c360f9f60fe604e", + "secret": "b0c26c2ea1dd78368ca104f0c83d23293128e866900312f95339cf00425c6c6e" + }, + { + "public": "f2f5427d404ec59790f4f254f773fffc84fc1073887faad0b8aaa624219b1d2c", + "secret": "d9b6992800e4ef14281c8f53fefb813028ba4a7e69a6f9a5afe468fbd206b7d4" + }, + { + "public": "fa93bc82ade9dd90c45a27f4954a82914767d9a01625c140530ed0183805487a", + "secret": "73139ab0e5dede0cb93776c5e0cb9c5b2f15d92dc5d6ea068e8d5a13f1a17d3a" + }, + { + "public": "51405ccac428d954ea9e0b61e924b54ed40c1519df1e21f205f65565c0550366", + "secret": "d192b23eb8cf49a02533935fdab6bee4aedff2e8f216d26aeb238569ef3fc20c" + }, + { + "public": "dd24eab8c5e73ae9f3dc014baa9f078c11fa4fcb8cad695df54f6dacd42b8a6e", + "secret": "3ae90e576577db68926c6afae1a1cced00eb7df41bd916f245ad0fc8751d9015" + }, + { + "public": "4efca589d1ea7a5222710bdfb9415ffc9b79be741a40681db47774fc7d3f9169", + "secret": "9c5b9c3bc4671763a005c6ffe2720eec9c90dab24d1e9ba9c2651ffdf152a11d" + }, + { + "public": "2f9808bdde54bfe18f3916634890d2ca24a26abadcef024c3c0db6d5d70d513a", + "secret": "476e50f2f5938a878226d6399ee1c7cf99177bb1b5e92da27f890d32df05107e" + }, + { + "public": "60e264ad2832ef69d29a2c4ae8da92b70e0617587bdb393440156ce25dd58a25", + "secret": "77184905738d29784b1f690fa2dec543990c7f95d8ac0d90b725730f58a3034c" + }, + { + "public": "f1986d3ea6978424e9a5ad8d3ed90f4fd5f86af3c4c9dbf10c48757c7dccc745", + "secret": "45511fc17563ea10e6bc206591a09b085dd4def4bda9a4f3466ae192c3d16dbc" + }, + { + "public": "72bfba6808c6c7d1f7f40a461e7d17f2f5f90f5f256d850a8a6ff28a04ec2262", + "secret": "c52b6544ec9dfbf3106b2edc72b87646cc6569b0ce687040bb0a88bd90e12a35" + }, + { + "public": "906f5a2fcac3e5f3580b68bd748e8040a4c0a0c435a0f8857511146a9d03b355", + "secret": "72419c4b8c507a611d934869ceece3216364cb8b86bdb8362bd0e308caf3ab49" + }, + { + "public": "9e91a7317669369c336d47e3870f9995a4a4b6d5492bd231f4b16963d6ea275f", + "secret": "48c7a65d250af497a7413a845faef1ff1561d0156f634975fc32d925aeb9a7d9" + }, + { + "public": "94b965e06279730ef69f6a008af363b504e777c49e0ded42550ba5b1db4dad09", + "secret": "d9a2aa66b37f83345ed1d85eb7688a3cdde84af9ca13d61d89fe9d710d6626ee" + }, + { + "public": "d9adac287709f3b58613007ec828266647e0754ca786b1edd62964cf351e694e", + "secret": "482d57571ebbafcd8419d90a6047e5d2e1db7092d19ee1463ff1490682ccca05" + }, + { + "public": "1029e34ef3e8e922910d12ae0082367d55e7cac4b757fa63255f60a5bd769340", + "secret": "de0581a8322f28fe56b8b1aca7ddd0d56f1206e4e13f7dd74a9481eb212c6b97" + }, + { + "public": "9794fedf8864bd663fde25678be0c7b667b733099f19e10e4239921defd75e18", + "secret": "66a748a24b7912b460d392665cc0774c99f164ee8dd66244be4e5e6450f2070f" + }, + { + "public": "5c6da8a06ce7862738dea924a18c0091519b45a5c24fc55d73a6c8ea02af0059", + "secret": "c3c7d6ea3dbb5edb539336d7f8cfdbf514831571713da64941cb4920a535cc2e" + }, + { + "public": "b8870ab38555f283da833ee9de07632f616aa3b9dab8df6267b67a771540b21a", + "secret": "1e110ee77cb361edd11fd0269a6e27e1929e57d68c902512062e8602ca8bf9d1" + }, + { + "public": "a3c3468537ab8beeb2a136e17a6f809016da0c58533d9826318f56e95003a22d", + "secret": "37daebdd73568562cb81b9f30a53eb6ad1c5d6d8c9b1dcb47ae41ef3e084404b" + }, + { + "public": "d9fcff74f709d373b84f968ad56a8acc030a937f8b115b9afd7b4c163a30e575", + "secret": "84063f7869c726f354104e4dba361bb47ae1284f6118fe2027df396856421dd1" + }, + { + "public": "9cb54d794a31b7e250ed2df50093945f0dce5fb1541ff490938a57e28449750b", + "secret": "8f9a6531ebd8ff457dd64be69c10387ccf7f0b817456d6e15e6a2cf8a38a7914" + }, + { + "public": "7a605856e745fb604b460cdf28e26f0899e37cf63c0a1e961c56e337ef8a504c", + "secret": "d0d9c11152fccec8338809115d30733d6ddde853f558ab9ce50548b741ff7bae" + }, + { + "public": "05bb3de29cd75663ce49d7e84b5cb7e5f6acfe4d20428ff4e9055d7556b9520f", + "secret": "78b9cdbca8e3c9c55f9a17391fdd795dcb28b9b3d7513edc027c9b12c7b63f04" + }, + { + "public": "dac1d230e8acd02f42e20a88434145f767a64298fef8ebc13151b121327af20a", + "secret": "5a3c8242ade73a8f11133c1ff70c69495fb348871ab017eaf7c7d5733c1709e5" + }, + { + "public": "46f640a93512efc604f89d595f7be6c10eaa396ef284daabc898e0301882656b", + "secret": "c49b9931f0d9bbba1251716fd9a20b716bfbe9e1a1ef55a95550d7b530d33438" + }, + { + "public": "df8325ebbfc599fba6a002fc93571d39e30eea179720e1bf5753dd54387f394e", + "secret": "980a2acbf3a9fa462409b7dbfe02eb1683fc0c40d1f3c9f148d30f45880c9024" + }, + { + "public": "b7c65c98c3fe5f5f77e8f0ef21917660540a702438302672582a6e6fe2e1521f", + "secret": "a4f7c717f710a2a02921abb2b08226a2f475d8511a56bcb1be295e85c733f519" + }, + { + "public": "cf12a1381a8a94399feaf3204cdd6e060da2dcc479bd35443c8b57981934423c", + "secret": "58bc0630b357bd415304a49fc4d7d80d911765b773c5e8a5335ef47d71e305cb" + }, + { + "public": "b91d65ee431aa2028ac1b6d24a138049d6c7e9e6f86c3f0363472b50d5220c05", + "secret": "6c5ee9ae9f443968419e3e149f56005f3afe2bba8007ad2c409939693917d430" + }, + { + "public": "f2be5b6c277268c2d1a28a892c30eb1cb243e881c19b85d7d0aeac22df9db747", + "secret": "e30af0127fa8c8742fa40faa9a17bd942f76cecdd892d5199c2f9032c8b10a61" + }, + { + "public": "7d98d6b55413f8fb84bc0b3f97913670c8a8313932570c026d0f1d8d022cb353", + "secret": "b2112852f87cfead905aa6386606f7596e5cb94f606373b9e436602a29fa98f4" + }, + { + "public": "f5bb951b2532646d7a71e01f6a30871fdbd3be0d70c89fbd71242a6758f86617", + "secret": "65454655f6d948611b7da33841943b42c9cd8c5222ad21d8dd9de5ea047f5a8d" + }, + { + "public": "19e83555b4e23b389e1678acb9e659fe0bfb4fdb4a9e9120fd18caa4ea3ffc6b", + "secret": "8c84005843776c116487681768a8a2d3615747a0a044d38fc463dd0815a49418" + }, + { + "public": "3e78938a089f0e58cfd5c40c4bedf9c7500327ac8df4558d56ca6e53bb73cd62", + "secret": "4d868e43c6052f78663834503f32a5047fe9cb73a7f86c67aa8e52025214ec03" + }, + { + "public": "9b7dccbd2f93d2f234bd47d545e29a6d53c07c44eb500151a27ee1dd7b94e163", + "secret": "9f9d1fbaa0ef53d9634611baacc14caac02095b158352dcee6f0e7bd2831ec85" + }, + { + "public": "fc95209f25f84abc4bb2fd389e2e007f3e9e6bae8092c203912f132937a5e76f", + "secret": "d2a2ef641ef26d612eb389a3347b2d5320e9f95a18737bed9932de73f41ee28e" + }, + { + "public": "d7e699c2b49500e9ee1bbe11395ef5d6c9c929ee0b113f7c3e56f8879226c56e", + "secret": "c1fecbf17305da7977d401d8d80ce5dac896b262ac406fe88b578fb05437b72c" + }, + { + "public": "e3a91c1d3012192ae10c141af64021444b1006c6b7fce45d55fcc815e8e6c210", + "secret": "b42abe02a85fe7912c0cf0f28ff04a3e32c17df39cebaa76d3b3b81446fd27a7" + }, + { + "public": "380017297e099f40856bda6d44af723ecc67f9c691957913d7b71cfc1de50153", + "secret": "6d2f182df7c89e49f51f6a7363f1a95d4b3f582c91229842636e7e5ca242fd1a" + }, + { + "public": "2ed1fa1246c0d6c19392d2cbd53b381718229732a08b73491df8f518981c3875", + "secret": "36ca45fdd5bfe34d4e72f525da9d11cee58ff1f3bb6f4aba6a32c88bd2091f02" + }, + { + "public": "6e7e17e3bb4c9c50e9cc30818f880f0afb6ea4ed70b16ed39203084cccfed250", + "secret": "e68e37ecff39d2fa685572142fd465c394836d2f9b6a9f935653df5aa7553f5d" + }, + { + "public": "15b82f4a5b4678e6bfc2ceefd663d5a1303425ffdacd3189b153801a75e5ff52", + "secret": "e876a8c0ccae06b31213c6c6468e21df58e307e73f3be004ca60d60894076f76" + }, + { + "public": "9fbb1be82725e25b2ef5adcc85e1012e67866fe11523110cf269e1227c978378", + "secret": "4752c932669a323b0e920357c7aa9e4e41e753a944938e484885ba3a39ec4ed2" + }, + { + "public": "33f0d32144def3c968930293c47b0cfb89a9a0e5342c1917c0e8471cdfbe323c", + "secret": "dbcc2c6b9872d42b25a47e4e80c850f14c0833df227dd1e98c02779bc1014804" + }, + { + "public": "9339a978e8d1493d4bebcc4fe9fdc2789e91656e7d4e1c4142554da3e262d35a", + "secret": "b9245ff83f78c0d826c79517870f7f4f3211fe681bc9fa0669ccee9e663a7665" + }, + { + "public": "c0c3bf1337fefdf7b379cfd79ad81b9f0adbe2106d3bbfe210e8cb27e98ada40", + "secret": "e6fe410ffcec3eb487513823e1d8165f11e4e50cb1a2b82ea78dd2d4352ba054" + }, + { + "public": "ea8fa6cf1a5b6960d46ab00c63a3ae868f960640457e06dd38217da66d69b56c", + "secret": "91bd5135582279a4fa5e841db3a301589e4c090f1eefcd893d7267748e6c84ea" + }, + { + "public": "cc1598d231b4495b770c3e69b009a935ab0df75a37f16dba65f96ffc69100032", + "secret": "f0b0b4d68360b86f18bacfed1ab41177ecef22e9a8b5a426d23ccdd653dc2b38" + }, + { + "public": "127aa7ea814aef98c7a43c974e5b835b65ce806d7b83761356632c7e08c7ed2d", + "secret": "a4eae4b8b1e06ffe9d5d46c8faae741154b16f56ba843e357f18c5c145d145ce" + }, + { + "public": "2dbc99e9d736a866f8b00a08264546d0a06810d2c338c98d7e32f4ca5611396f", + "secret": "83bfd4e9f89f624b8694a30c69cd52eabac3521b0e87375f744efd579ef0976b" + }, + { + "public": "6cb16a4828736b751c4662ac02acf7e66a05145f4ceb93c2be597d87faf7db01", + "secret": "acdd322e2eeebe0670253d75c8d6afcb92d3035d5ee76d5400341e6e5bd3b1de" + }, + { + "public": "43e1d1ef680f27ea9ab62b7581e7c302610a19578c63e4572b0625014aa6a56f", + "secret": "2d0a455bebe733e35f25a801892831862a67a036cd24d0eb825ee18d62723f26" + }, + { + "public": "19fec283fdc037e1ea3fe3f0f18c82f4e7dff3afbad282e780604f6a0b3e022e", + "secret": "5ba08bfa044b6dfdb82955e01dcba5f87c33882364956f4c8cc62f05848c1b65" + }, + { + "public": "8dede7dc25a230f57ba104d2c00a1ad792492b1762a968b3c5b17ebae8eb6a5b", + "secret": "25ca3c37e6e054b3735593d157fa050ae9f83e53fccd6aa4d0bce6fff604b105" + }, + { + "public": "aded83353b2a45251ce75c38c8e5e0c711c0ddab6e705b1875e3d01b2ce6d750", + "secret": "4a4dd68dc6eabef00645825bb65211c2f77f8271beabf0535aa7a054b24d7bed" + }, + { + "public": "a7b49cf00150f7f1b94aa54f4687e92ae0b4c1f5a0ef699c1a0733cbc87e013e", + "secret": "5269173273c866d1cb1f76bcb92c0d192c86a555da0a7f12dbb3fffe2a2b2922" + }, + { + "public": "6f8d4f7f0abc9e6f6294886961a1f142fec8b6bff826bbe036331ae125c94a17", + "secret": "6f059622ecc8319a6f2b3bf507b45677baa321a9da92041f2adbd5e3e6e0e1dd" + }, + { + "public": "2916c291c0ed597a7dd1e125fdaa06f5785b07a18b5a17af77668829f158657d", + "secret": "84a43d4c7d413a5ad762b130308b72ebeecaa72dbd90a02c1c04bc426a4ea469" + }, + { + "public": "b07879b955cb2f948eb5af823a59c80ca36900d70f6b902781453bb878451341", + "secret": "7321df37797a88a6d9c724420d4befe80883b0225da9a42a03d260aa3eb75d81" + }, + { + "public": "972a552fe6c313360b2f4b0990b9e3f499806dec7ba4a27a1d05b898c989bc1a", + "secret": "e16bd75ce73db2157c3063df0d3c624f09dc5dc07d07dc667a984526fed344f8" + }, + { + "public": "bfc61d1978306a82bd6fb50e186ebf49e1dd61d96d9cf996d8fa288d2514ee02", + "secret": "cd02c7bf721f411c8668958e275f335a2b322f851cca93bd0c3e93456dd89234" + }, + { + "public": "1dcb2f10db9495a42df898631eaf1f902b1659fc9b9ca6520ba46427c2ff6673", + "secret": "926d67b8b071168a115680887f9bc54fff8ae81c19c7b2f39714196aa5241dfe" + }, + { + "public": "2e4ca61a73c5f7aa3f892470104a177701e98b4d926a0dc74eb47d923789a766", + "secret": "553016d7b1f3be8fa75053b7699d8fd6c96ad5679075b200b6bbacce0a5ea885" + }, + { + "public": "34a41497a31e3156da3fd22149125f5c0c7e38d517504ccca0a77ff1a23f3a0a", + "secret": "e7ca8c2760ee6adc5964069471972304157ab9208ad72119634989832b6516a9" + }, + { + "public": "977c274ddc7f3c87b18320e420e732f294430c8ed72b48ccff9cf5a8aa55e50c", + "secret": "5a338cbfce50b825600fdd47ea82414be2ccb01530379d416c43b869bcd3ca17" + }, + { + "public": "816477abad3b19c4e47a775224eac2a9f96c8a4854fac6afa269b5ae4704ae25", + "secret": "a3d7b710396b26028c10c9a57aa4984e05bf722365a34dc54fe0f699d7c99492" + }, + { + "public": "15d3d7f96c00f6778276b78a94b9526fd21d83c946ad97b0435ac2641c459867", + "secret": "27a8ffc3acf57b6dc491fada4e5099b3e49ef5614d9ca620435fd12b83e8a2ec" + }, + { + "public": "077496fc1c9e2f0e1141bcf04784dae401bbad46b6257ffdb619ea373070427c", + "secret": "40e03ba9a01516ab728fd9491f4fb9743ff7ab3b5800c0d8138e913ee8dc63b4" + }, + { + "public": "44212559b1051be914f8b7defe4032197d4c761db7decf0cee7c1730dd07fd6a", + "secret": "bb9fb5c30ca9a075fa3107a0fdb78f51ac975ef07c3b0dd7151c9256887b153d" + }, + { + "public": "7fc995ff6bf288c68f1cc01153d4851520ab388ce23df5036deb8736f0584425", + "secret": "ee98f636de12ea9d6c93716225df3e1c1e0bdfbbca78d43668bb05f1cd766c11" + }, + { + "public": "27ce8debe8fc65fc7103b4f833eecdc4238bddf75c364d046f03fa38410e003a", + "secret": "d7438279e505d88e631cbf19eecc51180e9509e34ad951e2cc77476bc124f6f4" + }, + { + "public": "608c4146a9b5fc79904b11a8a9414582eb03d89a801983d387943e88ef3f0d0c", + "secret": "f79730968d0173d6c73c59915aef84212e3c5ca8558d17039eef9ac49ddc33d5" + }, + { + "public": "03600982fae408dfa890afaf3de93947bf418e565a1308250d9ba86fc21e385c", + "secret": "737bbc8f730406aef5e383f75214a0ce2e9a3af6256432e1973837c96097e4eb" + }, + { + "public": "7ba30408cae8222741f9d185dd4ef0d4d4e4bd5bbf10821611af5938aec68d0a", + "secret": "a23e479551895c4afe083f3989ec80cd52101e4e9fdbca856a0da8cb905cfab7" + }, + { + "public": "643ad97b453e99b31e9175a612b78a783b0bcb3f871abb8acc22a892c7d16622", + "secret": "195a8d5f13e30541153626dde98e6f964cb0289c4dc669c0bdc84dca294d3546" + }, + { + "public": "f43626eefc8bd1b8bd20bdc78bc5cd4df636a3bbe5650c9047061591f540280f", + "secret": "9f3c6b71b062bcaa3804c19b815d499d6947ea1d8787f99e8b7cfb92e0e198be" + }, + { + "public": "254c6f56653a00437d9d83b795c7f44a997c94b36b9e460e08a5b71cdf9cbf74", + "secret": "9fa49810b1ac5bc521b2c022be2bc6de5c4e2830cb9469c6a553082f398d76c0" + }, + { + "public": "4dcee0f9ce63856c44aecd7bad52d2e47fdfa2c7a89239929ed32c3b1ba70930", + "secret": "60ca466b4c3cea9d7691edbd4637fba4180f90d3067c74c6f08b0181369ce14a" + }, + { + "public": "5da3ea208a65ec8c48ae8b737b86c5d0b4e2076b57f2aa3a6a2b4c63f913fe03", + "secret": "be313a7e8f825e4fdbd88509748c97236a512401c0101c0e1fa519df19f1c5cf" + }, + { + "public": "c527f4807345363a4e992925b9f0714029a709e1d905389ac1d872995155b233", + "secret": "ffacf512e8ef8b85ceee04ff5d4320820276aa0de089cd5115242d56a8b77891" + }, + { + "public": "40b3851710d0747e9c47ae5180fe55e5da0ae8c577b789201804846685c8680b", + "secret": "7239da93bafa6e565c9a288a9468793595befa703a735c23c08248301dd14186" + }, + { + "public": "6dfa78e6b7de144e8ce48532b3d6867c6a66cb4bdb9fd72059808f17be3d6b0b", + "secret": "e03428f92ab389afa1faafd3187268d90cd9d59fbcabb34a5bb7c215ec3738ff" + }, + { + "public": "79b8ab3e75d7b4c2d3bbbd7b11ad3775e160968a90263d2af37df542a39c6a5c", + "secret": "6054ddfd27163f621a07b727545e7bc79e9c10ea41fbcedb308883dfbcdfc9a9" + }, + { + "public": "ab454823285ee2a51ce80bd8e487236e0effe950339f6531377e0d4733f3924c", + "secret": "70eb09cfc1275fdcfc603e115c5ce7e01037944f1767c0b19a2b97480cf04887" + }, + { + "public": "5f4a41eab1b11cda023e4ba701c2804defe8ab20007c02f68b2e0f9135d88169", + "secret": "d598bcbc4660548870a0aecbf5db9f15ff40bd6d85c9256e6c883870ae583278" + }, + { + "public": "8bd0e5f32de301933de6b2f2382fabdbef2251133f4f69e6af6c1b9f89749525", + "secret": "4c62778abdc39c18f872fe475d663a131aecd79dfe75eb069750eaebbdd3feab" + }, + { + "public": "4779f06e7ffa740370f2214b091c7c9750b1167c6f418763155a04ec9db34664", + "secret": "cd432fb9445bd8eb844ae76b577d9a67f07f8275bf7c756bae905a5be84cc558" + }, + { + "public": "54c4629d5fee8cc0ade21239184ee07b053d0711139a7ec820232baf0cf3ee3a", + "secret": "490fe7a8568ec6b8703f97c0a7596e5abede057844e1628a28407f96e34acea1" + }, + { + "public": "bae4914c5d0b64069883015429e136f8881501dba8b1420a0c83678d7afda525", + "secret": "5bc78476150e4926be88b87825b83d1791351bf347f60cdf48bdad53bc540f14" + }, + { + "public": "62b4977d6533be9cfa8e68c584e704aae8abeda1ae19c5c7642e7f7395b99e6a", + "secret": "30873e27fc3d2b4d1dc8c0cc95425fe9cf11f9582a3b2721e4766b910ac49526" + }, + { + "public": "012134650df003167b7fee8df53bb8e706abbdbd7b7182825288975931f33208", + "secret": "06c6dcc0b74a548a536f7786a5f86f6cbaabd44aae71342bc760499dbec326b0" + }, + { + "public": "707bc6108075be77306d693808eb1458f7f5c4a6601fadbd159a1a6da8aa9e46", + "secret": "cf083fa5da24fbebbb83d78d0a64624d65c4eb5e387b7de1c83d658fa470bae5" + }, + { + "public": "a5925c286ac7f427327f4f21d16b5f8be08b7d49a24109bf7fb1b81a39951602", + "secret": "75fd69b011b1bc4442ff5b2bd041b91b3b9d223fffd77515110df2d1093180fa" + }, + { + "public": "c0a3b673acac44661e18df2830c0f21209b013c7292d26a28343f02955371c68", + "secret": "6400f69185f075b480d59db51dba57c577fa150becbfd0d12266b3ce767e4bc5" + }, + { + "public": "b5faa6e9e191e183b9e02522784726a60dc6dd58801b607ee80550dd5d7c9065", + "secret": "3185cfe81297d3723d6a6df4da388bc3dd38615a27e096688541a40aa353348c" + }, + { + "public": "860df4af1d395dcad8e5a82035c34f20e259afcc7299c65924b250d00a4b9925", + "secret": "eb32f30301ba1480eb802604f10cdda5f44252b9fcf489526291ad99bc46779a" + }, + { + "public": "e64f4bb5bd753902442c97d605abf678c3f4c008c837ce00ee2ddc574a0e8e6f", + "secret": "7e149e3fccac7f0ba8088e6ab36104379df0fc383d3a0341b224c2f73adc3d7f" + }, + { + "public": "c1b75f0eed14f2dd6e293c55b4042c5d19ef7395af2674749f250cd79ec50531", + "secret": "63f598bf196d25beeb1b9f2bcdef375de126138d797876fcfe934354e43ebd26" + }, + { + "public": "ae2487dc8c1add14fd3d231fb00ae77f8599a0fd611137434dbdde0f9d8b2c67", + "secret": "bc44104c0e70687d191dcd056578a5e7aec371cae50c0dd4c56ecdd2168f6c22" + }, + { + "public": "1e16b7ddcd4159208ebfc7cb19db547065d911dbca06e1daeedbb715f5876221", + "secret": "0b128ba56abccc42ffbc11e0e2d84f8b7b624abf5783690bc4e6a876cb4519ce" + }, + { + "public": "11f0ccaa27c32643aaf723b07ce167f5bc28f4f5e1a5864a7bc3738bfaea441c", + "secret": "0e8572277d6ce569b96390ae0793ac41df39f2265fe5b9eb5ec074a0062ee548" + }, + { + "public": "7c8259bf772212f8084af8f4e728f83d3464b2bcc8a02c61ce3064d834b63230", + "secret": "6a3df55f5db4f76b5f7fd433400c4f9b181bc612cf00d335c063bba6803789e0" + }, + { + "public": "9673e6d28e57053de66ad70f8585505b46850903501f1ae341e03928e1fdd935", + "secret": "e70a9952fad27963c89e3c7c09adc5d45730f9245a4b2bd60df9d321062a2be8" + }, + { + "public": "e3d35bef7b962add6f34c76a36d90e2bc59477ae5afb62b8b16ce104f6cdc74a", + "secret": "48cdb84def8b157e74387755b901e7b11d0592bd8826a107b4e5226a3b699c5e" + }, + { + "public": "058f1f58166cc3e5453bd30c8e6a169db262b3f389acade9ef9fd7e1b0f30d02", + "secret": "85cc52fba84054ee997394282624ba27d346b7e7ee9dc04ddeee32373144fe16" + }, + { + "public": "342bfb842c2b0cfc58fa46f75c23fc0e16a4f2ee51e17da1ae44995ba481b34c", + "secret": "02862bf4116b187edcb77c9d167bc0ebb262a8c7266cf45858409b64671c2b93" + }, + { + "public": "176fb80b6b8592e785c14b8427daf1c6f46f82904bf316e756ed44d468e97c01", + "secret": "c554fa7258e6d0a01cac988f8fbf575e31aaae236adb79c9a6fd2521ea26233a" + }, + { + "public": "6418b8c20fb70e9c87a55860759847cd39d9881b0afd021d42d8d8d9b3b11409", + "secret": "47d57f9173fbe16717ebc301abb59826f2264b406c4620de5e57a99b2823e259" + }, + { + "public": "c7e9a984635e22aefe918f9f00919db404f84934ec10a4ab0be2af56f532b920", + "secret": "dd910ffefcbf40c158b38737ac54f328707029c62a3d025a9978389da0e7cea0" + }, + { + "public": "78524e2394c057ab62192d3ab62a55d40833448698ede9b90af6bfb546fdea3a", + "secret": "cbc4eae0c34c2a196e0bcef5fdfd7d86c0784f210cc0423a4d7169dca07e830f" + }, + { + "public": "e6ee7da63fac3dbc9db63a48e5fbf9896e384609e46edbdd48d85da8120ade1c", + "secret": "c83254df25dfafc13f5d05e1af16b04a89fe8a7629f181ec563b9b6daabe6144" + }, + { + "public": "1337ae0ec7c42dfe27331ad05630899976485d4665f7e0bcbaee263653896e7b", + "secret": "7e246512a5ccc6b975e2b1f4b5d1ab559fe10e881692aeab7efa0422895c93c5" + }, + { + "public": "1aec21b387fc596d14957a6045fec2cc288e4a9b4723ef800c62a899f2976040", + "secret": "129d990952be3a18c8b9a3bda06d0d7e27ce30ba161ec8cc3c2ed4748b383c63" + }, + { + "public": "9ddc810f7bfb741c7b3339b4f1445eadface2cf99abb4e687da10511c4fcab01", + "secret": "c01605ec43306219bca8ad845360e7bd18b3bbf1ef8ac6444121e0e3e110904e" + }, + { + "public": "9e199620ae24fca08b1a0f2d003cae1751b21e60f028615cac2c0a59a3fe9c6b", + "secret": "eeb69a1966cf44260816b05d2ae2af00aa6f3276dd15aaedbd6bca2ba091473a" + }, + { + "public": "504f2d3278600bbe525bc3a218c0c5e59764b2d931e6005f6f19d0bb6880b572", + "secret": "049c99d5550ea8e519c1109876e2902a128500eee8dd560ecface44fa4f61857" + }, + { + "public": "396ff515283373dec40da680cd740cf94f7f112ba9a49ba2add93ee19e468e32", + "secret": "70d0a04961fa3b0a1beed3fe68634c28655c8dce7dc53acc96b09a8d730b6a82" + }, + { + "public": "279e89806f469d79688b609050c4d73ba22f680d553c409d9db41fff5a9abc48", + "secret": "bacaf51740344af73b72858daa905714775dca302ca774557d25ef26b06aa624" + }, + { + "public": "4dfa2d81bd123dda962b7c9b22c45d6d7c16e64d8410596d09674b3fae7f1e18", + "secret": "391a74bcb3c6dacc196bc149e8bba186eeb6f0b11bea79d335b1d1e95c1a1bb2" + }, + { + "public": "87f0be75f3e6bb74bbda8657a0d6b52f25c772156e9d7ed190f3f638fa95a53c", + "secret": "535d269283e74c83551dd5cb51ac164bb03028650b103037032ab72f9502720d" + }, + { + "public": "c72f543e512a7d8e21f520736e27bc4e5b2c34ff5c194de6d51d4c9b37897f6b", + "secret": "8db9111c44f91d501983cbf15534530b86b2f49463e67fcb888d3aeec47acfe6" + }, + { + "public": "44c4f3465439b4dabf6dcc37dfc1febdaeef184703ce7b98b6432ae93dddd766", + "secret": "5d9fdfccdb29ed7c74ba112575630d6c3eee08d059fc881df36c43984aafc782" + }, + { + "public": "8ed02b823d847bba459c6ef656d6e16a34ab8469997e8ed9569167a25a790567", + "secret": "ef682320078125cc6b56351fe25f3db45365322c6d6d69b39c90f5e52c539e24" + }, + { + "public": "5065cc57755a699b05738a75ae29d16f67a80d786f3c7ab3dab06612ff331d30", + "secret": "51bec180043fe37985d3d1834296208b9e61b25a3e489555c285390023767714" + }, + { + "public": "3c57418525d3a80da3e21a3696c6fe3c80b947d5e7845b53d36e183aae68bb36", + "secret": "d0a70772225e7e1e18d38761f5984be155a88294a5c8b7e826a8545f4f1e79e6" + }, + { + "public": "f41cff8ea29ff89457756c6d6309c4412bee6941d578d89573b2d2917f7b1b5a", + "secret": "8428cc8f885fed2da8380885b7133e3eb95e8cbe028659b398a7a30d14b3cc81" + }, + { + "public": "9fa244ce0d0569ff2771d7f95ef41ae7fb9f4a63652b8d453420f155fe571317", + "secret": "7f31dec347e847220170e3165a218d1b036806023b193f501d7a3d691829c2b2" + }, + { + "public": "46c96b3960a11d3350158b0ea8ac7674dad503cb3e6372ae2d839f16e6c10e5e", + "secret": "062e876469d049b6d98c6160db989f90d4f08ebc267445036c5795289fe70676" + }, + { + "public": "7abddca6ba53311a83d9211be1a0d4fab139344b7215d4b2eabfcc93829b2931", + "secret": "8e90259c907f2eac854075ecd2584caaa0a655be120e7f4028c17f3923cf37bf" + }, + { + "public": "b0b9155f43cd802a0ff2a9106a06ff5cdc71f5479eee1490cb994f671400fc38", + "secret": "93cc064d51207e5dba18a93e8715e35d259006b49ce51446d4b6ed5c82b0b519" + }, + { + "public": "b0d839cbff18a3e33f55c306b6ebc2b4e012de1b7507c67cb90133a11f2bcb53", + "secret": "5f18a84635a2b8d769ecf539e0ff439861f91604e2669e550732af546619017a" + }, + { + "public": "c645784fbd63329594ab419f49e0ba5471cae3fdcaf4c6215b8232cae2764673", + "secret": "dba27fb75402f155485a3f2f665b29aff409671792668a2961f3ef753b6617da" + }, + { + "public": "b0aedef77b0e090ab806c548564ba4e5660644d841c6fce4baa31f7a1df7b249", + "secret": "c99ae5c3f239cb7cbdac39e28a562e81bd89954f927cc1bb7cd5dc8e96f37a82" + }, + { + "public": "ad3960c5d3d3ce598c0cfd20c6a43149edb55672b83be197f1f241de1102f07a", + "secret": "6e7801804001329e2893329c35b447debbb38819401fa1d66c87422b200117d7" + }, + { + "public": "29546d58fc20e75463ce01a637de688083c6d40179beddbf1f5964621b4c5e23", + "secret": "f83dd4e54d2ee25a03ac86b5cabc625a4a409dea9a11dc0d3d8cbcf9746a3a42" + }, + { + "public": "97842d246628673dd8584bf2ad998b352f517cb14d6d8063384bbdcc4e78c653", + "secret": "797ea9854898609eaeb32d523aa3996b99dc8fb65cbf54137bae7efe84d1cb3e" + }, + { + "public": "4c2d282b50c1ef9e548e53e9fb1528e15757cf2dab1bc80a84aeea0547fce858", + "secret": "aaea5a10671ffb0fcb7f62d3b405fb6f9e0a557aaaf967ce073d4ffe4f1ad4e9" + }, + { + "public": "4192fc263434cf7a5a5904d094977c2e9b0e16651ca3af7da4758bce0215cb06", + "secret": "5e669a2f59c2df795a9ead9fce0d7a27586ab8a0a55211ae2dfbdfce47dfba06" + }, + { + "public": "7cdf481fe82029be654097a5e4518354a18d13736474324e2c927f5638569d6a", + "secret": "a21d44f9dd7f8ab2b5837a1f1eb102015d99f3a340032821a311ee782fd3920b" + }, + { + "public": "4555f0ea6fdb9a743fa003b8c9b1d354fb369b53bf383002b6210db0de5a1a2d", + "secret": "b8a7c31984138bb9bc5b733f2b4c5d3e48d99e8dece2d19dadd3d1a0ce130dec" + }, + { + "public": "9fc4119264e5a024d32d95e199cdc9178b4181ba2666711cd9e30644efc4a244", + "secret": "74c3378e38b5fe64386e8152001ac04be768ec9813cd9568b7b3c939cf970a6b" + }, + { + "public": "28dd5103956b2d2381d384f977e1dd943d3a3c44306c7a569dfba744c0787e0b", + "secret": "285931d0e5acf76f9fa22972c002a2aa259c9b2ba2c3ee2b0ff2dc700f701a52" + }, + { + "public": "09be14895739d859f0012a3bc927859172b83ad1e8277733aaaf1c96a6ffca27", + "secret": "7c81297ee997ca94df1d9d6a4715a46bb26177267c06b884bae510291677773c" + }, + { + "public": "5619002b67246207c45706e0988c0fe4a2a0656fcdd1940c6904cc31f43f632c", + "secret": "b55b6d43bad1e204726dd272c82e54eb3cb84d679b7fd18a825c9f736c5379cc" + }, + { + "public": "f8c07a47fcf089ef1b0ff7826b475628223dd1f6cfba99faa659528f1dfcb50f", + "secret": "c6a718225fa7eb1db796896922dede2257e7b89174b9e382b53d177880144207" + }, + { + "public": "6ba6eb48233138bbe0ba3c4f805d3e8541ee60386e31285de5db43087288c51f", + "secret": "458c1248840e1c2615c734ba927e1a51c5d79a29d38b1101724b574c3492abbc" + }, + { + "public": "fd05f84f98f40a6ed713213ad46abe0df251777c1c2982469c6d569db339fa13", + "secret": "47bffc1f4a57105971652d2b593c26e8171af4b6c8c67876155efc8a73209a5b" + }, + { + "public": "8a3fe3e6f6cbca2c6434943a4a0be77534b5bc17a1700b4ae9baba52ee02452a", + "secret": "d484891ab5ef102b8dfb8866746ff39c9e1e295eaf2f7fe792c09c73f914711f" + }, + { + "public": "a449a9e34aa7c21dc0911e80876a74e71357c72dd53c94453b6c1ed422acd56e", + "secret": "6b179bbd46696c2524c95586a273b797ed7aae84168de3d8e4157e9e82972073" + }, + { + "public": "053a845e71df2ae3b709f57f7e4d5cf17e37268f92826a39d72a708090472974", + "secret": "af33513baf0664a7dce05bcfc55c156b70d976e135f94018ed837823e7152e97" + }, + { + "public": "afecf8fb2283fc515e6505c3df847a7ac510fa005e24295db50c9bf3789a5b17", + "secret": "2ff1610a7e00f130eb2ae27dcbc6dfefa7f10614d4708a747a42803e06cb3358" + }, + { + "public": "1e33a54984238b17d8b3f100d02284e2c1906c5f4dfb976e786aca4411530c52", + "secret": "d681aa827f10410d39fd3ca9f67e5ed7a20313f3a9a13b08419e93dbd6a3d644" + }, + { + "public": "046451f6a092d7a6c51e017ffb4f139c4545ab6b33075a5833f11b982273525c", + "secret": "304cc5cbe39b90d4949d2d288e8ff9e578304ae46bb9f382503fe2e9f05bce0a" + }, + { + "public": "64ab0aac56e192bb278634f9641df204bb6abfb5280b7aad66ef3a97628e4d1a", + "secret": "9814e635824ccae06de42264ee96cb5ef6a6a11bfc9c13647ff17685c3b5d207" + }, + { + "public": "ba902fb04abb8e4f919dc696abbbc3e957521e05bd2f1e19a83bcbdf4d5ed75e", + "secret": "e9c6fc66f3c1da6018fd560d830ee95a8af88131c7db57d08e8141598cefbb4a" + }, + { + "public": "c19b2cd9d15dd52d43d21968be7d8d6b6f3fcd8b0e40f656e4e823f126ae9759", + "secret": "2e5992082bd7aecbf34d9d7dce2c63a7c9725351e518277c39687174d1a1a518" + }, + { + "public": "55a0ec68968fe771bebc634fc79b3fd3193e275bd2b3cde64529a46c864a1a4a", + "secret": "18cbfea946a844440e5a206955f251a13a00b073384803c7a9bcf98427f5d52c" + }, + { + "public": "43f211b1880edcd9a76e60f67198ea38adf7127797d44e8466c01d4299bd865c", + "secret": "5729b0a143553034317e85e23ceef5a97be749621bc9d9d160bf9fd8fa5b87f0" + }, + { + "public": "f8cf39fe5015d256f1692110584c82f449191e9273fa28f26d1c8bd1241c2407", + "secret": "80cfe70467f7995743e32421c93d5969ec317d4ae157ed2e3e735339bbbe141a" + }, + { + "public": "b459d9e26a00314cd306bf70c3dd09fde56a75e99cd9d935d9fc25585c9c7027", + "secret": "79db1a2819d05a8f2e46284229c5365e070708f7a1073740f1f9325fba14d48c" + }, + { + "public": "75b7ab71c120bdcdb2fd3243949204aaab7a4ae579e22c543be12822fea6d470", + "secret": "0dcaf98d5592da3392ec0259e6b7ecb07914ec359b107b8b2f9585d76a2577a1" + }, + { + "public": "a03561c73194e1bbccdf29d0cbb4fefb40b0af1c536b3a46aed7443d5cdc9849", + "secret": "75b041ec699ca8aa2ffe963f2ef490f5f219dc0176ca9acee6390263563e4fb7" + }, + { + "public": "0aea4dae7a7045f26ea7ec2dc9970fbc3f8a0df8bf7be67dd8ffa8e95195527c", + "secret": "fe9d576d8125d7b23efceee7d506b10b1665342b2160cf878fb99c8bb19dcaf9" + }, + { + "public": "599c9c032d1e412bb4113d2ca06249bf256172d470cd0dfb80e09427fca9f256", + "secret": "33601324e9d0e3a2c8777a26d65e00500ba5895c5ca03aa3b0983b7ab2cd6e79" + }, + { + "public": "36c278af300f2010a61d255dc7da0e3784f7879e2e3270fa020c83107882eb56", + "secret": "7bdbf01112a9742ea6d2f3bf7e9d311fde0200b1361a18ca06779b634e7be9c5" + }, + { + "public": "8ed5d4c9a6f8ea362fe9e53471d599bfad18948e22804ecee7a06fca5c9b6b1e", + "secret": "3ff532fddcc015e5e9ba659159ee5bdc6564c2c4205800bb3877812da50cf3e3" + }, + { + "public": "9985fc97f38ab764496e7f5803add0bcbb21dee53ba2de854e2cc28252554c58", + "secret": "8a3e801842210c96fe3f3b3e41be6867e91c810ecfe523190493ed52b7697713" + }, + { + "public": "a85bed7485f23a330638cb56fff6a29b397dcc1879f4d39605394b5f10ad7566", + "secret": "cf86927d106a1fb2351250b9f60347daf60d5e3000d23daa615b79aeea3869f3" + }, + { + "public": "fe1100a320ecb7ae707cd98212ee65f11e79775696f0ee043b6a371d7420e960", + "secret": "f4d61a86913d9afa3e699ded37ca51252cc46ebccbe449abd8367898be28f58c" + }, + { + "public": "3b0a86e212ad877767d8b95424d06c9b7365438e6785e2bc6f54d92039eade10", + "secret": "01009cf1d93a970dca2034f839fe3868e3c2e6a157f55734c7b2ba68a76e6a21" + }, + { + "public": "38b0ef4ffb7d2f66f41b83410df46b36de515537aa23bfd3ff86fabea43a9e02", + "secret": "b02935049a8250e80d190ec19810b6d03bd18713604ce8576500fb2b5933d79a" + }, + { + "public": "bb056df37f48b26dcbd3ba804471635c82ed28acf9300a6f69f61c130347070a", + "secret": "b0153f90fdac2915923addb54592539863dbf96698eb56c95880f26cfa4122d5" + }, + { + "public": "d778e9887d9cfdb4b331175904ac2b4ae5fec562f852d317514e76b13f6a1731", + "secret": "cf23efe160804c1dc1c24200d7ef88b682b5a7ba8ef43f40de43f36cd8ab35c4" + }, + { + "public": "3567471b762737d860b07ee703ce4fd5ac6d95f60b66d15f1725a0e3ad212b3e", + "secret": "9688589b820b3e1d25c00a7fb2589d5b10428b982ed29b2085afdf41e158beea" + }, + { + "public": "e5a98ac5799aa7f72b4be4d00a7cefef3325f6a919a874981eabd951dce92770", + "secret": "fb2aa3144f3aaf90f15118d3aadbac340fdb6f12dc82149b3249d58ed39f62c0" + }, + { + "public": "6b11067cd69386c19c5b9f81346decb3f6913912b68a2b1f86e3576b110c211b", + "secret": "4b04c5e9f30ed5e1b3966d0cb8e40d3373cd74c4d1efa22f95701399c1c0501e" + }, + { + "public": "bb38399d3c48397affa05527a5a481186377f43b5682248bf4d32134b70b247a", + "secret": "8cea48f704a5e33b093f46e9bccfb36f6f7dd44e922da4e0229aad1e7d1c72a1" + }, + { + "public": "3dd6f381118ac5bf5f73098fdd31e77b2f7b7871e9d1ea08ac74b37fc4797175", + "secret": "dd223c41cd08dc7ed3761988f03bdb67a62b132907f12825369a61e7207ebc90" + }, + { + "public": "d0d625bc67db6223843fb0b2e87d0409935f644eba159c024dab9325b0537f44", + "secret": "c8bac55d865d835913bd31008f788a766f54081df68314db32dd27322e5cf2f2" + }, + { + "public": "4c5eba3f44b6a4aaf81b29f580dace3202708675745aaad3b45b6d0f3ab3d443", + "secret": "42bdd80006aaa7eca9c2ad89add60f104d52f2c0863daa316e9f744fbe81fcdc" + }, + { + "public": "2062223114c38af310bf4dd9105cf4cf5cb1a34552af04d9107efd704c2cc37c", + "secret": "2fb90f2b26b0ed3c0bf4ba6f7aaf94298e82a07722f702d241a61102c2ab32cb" + }, + { + "public": "74bbc65de7f2be199a6da9e1fac3314873205755a5b810d2e2fe10a29835d469", + "secret": "4b0314b5ef22858627c086e8cf0ec0c0b77744e33da3e77e76e86ff4324a5973" + } +] diff --git a/rust/tw_keypair/tests/ed25519_waves_sign.json b/rust/tw_keypair/tests/ed25519_waves_sign.json new file mode 100644 index 00000000000..159ff07db78 --- /dev/null +++ b/rust/tw_keypair/tests/ed25519_waves_sign.json @@ -0,0 +1,1002 @@ +[ + { + "msg": "", + "secret": "29232a6d56e00bd21adc7ee8a12af33eea1da85e40510097cd9bdc6d0cd78dbb", + "signature": "14e0aa80a8b29ab3410ae8076a74fc55c355855bae3ebf1ba8a29589d412c59d5a5678ca6d194b0d7882affce54d8dfac7ae7347d19a67aa225bd6c6bbad7f07" + }, + { + "msg": "fa", + "secret": "9f30057164620aca54ad586d63555861de9d7bdd1b694bb67de6f60c3aa4de29", + "signature": "099f28b68cc7b7605b4f98f63bb5aedc9127b760a3350d724c12148a4d74b786d6cb94d74f61403d0b2d09ec9c8ace493d7975da79e7fe41e11d1b5e36373189" + }, + { + "msg": "9f9f", + "secret": "49f4a75001195504c907a35b9ab64d6920c1170e59117d7a6bf9364230d1e0aa", + "signature": "a98b0b5330c07f188f4683da961c345b4009aa0373a1dd9a5e205bf6a17645108f1928b3b31c6c1fc7477b5facb25d7a93907a5ee06b8ef7d650e2ce41011489" + }, + { + "msg": "e6167d", + "secret": "c45d1ba60a5d929d228d1b69a8f91bd256262498e81d32b6411d6dac9a60ed3b", + "signature": "05db2483b8107187448e7f3d5581e48380a83338d53fe70c59e88b281b995216085518e5d331a2b698f2d0d387529dd94437157df88cb14be8d1925afbb6e80d" + }, + { + "msg": "650b72d9", + "secret": "ec48169ce269fc98df8b721803f6498cfc813ee2d073bee23881295839443ac9", + "signature": "dcb846e1b571dd5ac6563a53320254efaa9b071e58391cb6b9ecaec50fb63896da4949cc1a3573576f7455ca5c6f27a8e46c8e8e2b7a455067883aa800eb4383" + }, + { + "msg": "d60c3bc46a", + "secret": "1638f2146bfd99c8e0e67d21c7d3e411202e90377380fd85b846dfc3dd21aa36", + "signature": "7d3ca61e03bd298f2903efc365ce51e7809c43d7d1e8e0a135aa620d970a8ef0ed74223303b0282cd428a8e825db4cb3bdccfa85bda20854f1400d2e9106698e" + }, + { + "msg": "96691bee184c", + "secret": "3c79edf897d7685b5382a1ecf54b4dea41b9066844f7afc7d8beac4b7f908782", + "signature": "5bdbf5816b7ff64e5ee2d7aa6fc27b7ec468c3417439e77fb49e0e9d82e64db497237da265b324d429a282e9a90af157a94ce5f891d70f2e2e0d8a7992d9e38c" + }, + { + "msg": "c31191964e60ab", + "secret": "d1188370e98d2b1f35f6eddbd111f8144557ffe526f07a18af59cbd1601d53dd", + "signature": "2725204e2d214a2f2554867d150b097d79f7a5741e1c9328762dfc546f1c4e73f0fed5f2e2c76c3b43eadc5984da906270c27b0168afe2cd7514d8554919d384" + }, + { + "msg": "780f3653c04c28eb", + "secret": "b0cb7c3f15ecf4462021c16b9bf7e4aad0edd1a00d52d517b1443aecd0f9067a", + "signature": "ed884b0d446911b75542218571e06ee09751bc637e7877a25aca3c3d06d0e88dcc78c11fc97e65c6fa91d1860fa8ca20a6afc216a4c6930dcb11988b65b74388" + }, + { + "msg": "a231b7b9ab4bfffa24", + "secret": "f765ae39e8b990f7d59e08bca241f8d2851c6edaad9933d6cd27a769e22ee7fd", + "signature": "93f98182d1648ad37c156d529329d30f54b9f833878506ce7fa3dd3b545e3564c741c7b46fae6b8244c69def092d22d17e3666cc86cc42c231271a905ad2fe09" + }, + { + "msg": "11bbf3d0be76b57ee344", + "secret": "fb5b2ea31515875641ee839611b9448b9632ce9cfa6c3d414289947138f70835", + "signature": "3195d0c694cbd150509dfd3071b35041496514b814287527ff248d0815659fe29622d098462f2a207de1ef60569dcace06fcb35133e2a71cfa798b788db7158d" + }, + { + "msg": "44692e2d3f50b02e34ee54", + "secret": "4d82f350b75db7712615b783f6a8ef28b71fa87c3e7aeed9185ff36f88f7c28c", + "signature": "5d8dd314a9ba10a346dfe0bb5485924048f52402b32e794116fb91071b186d84e26ba7f6a04b0996e34d136d5d9c77bdfe78cd6538332a217a29998de412da0b" + }, + { + "msg": "fb5cf6d2d1b4b1367ea88171", + "secret": "fd3882b4e8f253b02478c14f40644994a1df21d4b051185a49c27d940e63de05", + "signature": "6037e6d413250cb749f53bbd15560a447cc0fb781a41f083ca33f9951ee57b4a26e82deba91e8db04de07dc7344cc50e031b7d9e7f72c14580ff0d03cbe41f06" + }, + { + "msg": "d0a7221b077d1a214208f37712", + "secret": "c877b864bccac3f12bf72369c736a6ea7d0771d769f8ed8d0c27141b774ae1a1", + "signature": "9499c3b4c093897890bf5f0446eeaa1d14fca3488cf2e6e30fd78f0cf1f6bae5ca8367ac186a5ac4ea1764b876d1ba945cf6162ba3ea79f57d14bceeffb6b400" + }, + { + "msg": "a2243e5b69c71e58fc16644b3226", + "secret": "0b6cb29e64a1c3ac0cbc37b146a9462935dbad36dc23404288dbe4bda3f60a58", + "signature": "efa97a7fe683df736ca814b0b041e04c9329f560044f704e66360df449b5442f8137615c1cb1763063fbb803e515f20571cc9cff54cfc8a2b9613b468b9a5988" + }, + { + "msg": "5a3be6c17021a8eaef9ad0d360e846", + "secret": "d552e0fbad11849488fd9c65491468c9c85c3b2d7d25f62d3f29498d9d783a6f", + "signature": "c33b72eff7cb6c86eef1405639d749c374273a2ff661c284f65a2bbb324c490a5be5358070f21ce39aead3e847d2ffc7a4e5b24d0f987cf0d2bd16fe78650e8e" + }, + { + "msg": "898bc102c615e776d5f5f28c857b5072", + "secret": "df3424af05f797adcd90bdde28fb8b25141886749f5389aca801515c437faf95", + "signature": "f89b588decb88c6f87b9241bb118e4612b86dd4a0c0ec55b89421f954a0c9378d5838c7b0b5d87d0a416ac20dd702c37666392eee984206ebb7fc0eac7242402" + }, + { + "msg": "199af157cb2e5ecd3dea9a169731209119", + "secret": "fb0bbfce58eee7b210812663ac16f46c77108772a0c8243844f606fffed5a5fa", + "signature": "e88b6111bc4a523427241c022dd675d26f68f4c143ba01b92158d4d9d4626cebb7ac6afac64e0e0be55b91c7c0e64903489b803d6c9690455a98543e0e0f3003" + }, + { + "msg": "a31b4eecdc290e623bd1b8f29a78e557aff7", + "secret": "200462d51de53fbdfdce72fb0ff61d01b6a7c02b452ba8e2426d5234a2578989", + "signature": "0b4ff700df6b197e05141bc049547c969c5186b1ea646b972c9073b6cfa909c10d850b9f4d7e40dce35697012fb170c0988ed78723e85aa71b6f72293d323c8d" + }, + { + "msg": "32b605977d59e9c7cd329589308cfba24b4987", + "secret": "a03784763e4ac33b29541730bd345cb98537795b7cfb31e2da89ba5fb230b937", + "signature": "82c0fed9c5b6bea4594139061a1fbb2a3d51642b612f90ee5e7ff5f7b4511603f8beba98dd946dbbdaf6376e4475378ce7c19f8e33e36388fe63c640f120ba82" + }, + { + "msg": "a3c80a4b549f8ab9d8fbc3e3419660ca9c960ac8", + "secret": "c5aae1ddbdd4b738b92f810964990deb046cd665980b0cda313a8b6679dc99a0", + "signature": "28cc2ca611a33075c0f47cc744835d1221c711ca8a18403f4fb0278dbc962295754dc4d9a2e56cb2bd488119302cc37d29c3db8420c8cc47917c7b9f955c3385" + }, + { + "msg": "23da4e835d454b2d942a2bedd2f8309bf06e9427aa", + "secret": "c80f63b32d8fe520c0a8b5afc5468d10f2b1644e6c366fd75710f8ed7552ce64", + "signature": "bbb7b9930f3d78670a43e38ac11bafb4e8850334d5a0a1ab607c9e10faffb7016ba347f60d1ab3037a613a8fd12f1d8f0aaa5294468385dc5ac19bd12680c500" + }, + { + "msg": "995236037d1fc630e71b591dc5d9806d354d15f4591f", + "secret": "4037ab6879c0663d689589ebbe166e311b0f9e7ced54a99c5eb31d1ed805de29", + "signature": "1962e19a89a633c7e4aac0ccbbc63fc8d678c00c0da1d853527e140b18e63003703cff6e0cd3d7f5d0a67788354a1ba44c320f5e6c4acfb27d808f8c50b31582" + }, + { + "msg": "5dd4c4bf64c159f27dd540be5b92bbe484c115ce9b265d", + "secret": "83df49e55453d3d0157f9675d0cf565bcaccd81a0f502fbf59cc556046502afb", + "signature": "19030afe2778fd92f4448adc97720b82b6ccf53b5958cf3c7ecf4bfaa1af2baeac6303f66f5e36a26943c76a2bd3e215d1015fce246f1041c0275207c49e7d0c" + }, + { + "msg": "141fbf43ad3c9c937ee42cc6d7a379229c2b0580077e58e3", + "secret": "a047e28819e1bbe14ac006e0780ef6e69cc98f444d8a477a67b5dca4f3783a40", + "signature": "caef435a8a86764205235cec12a9e486bab2201d97682b48c280423b3d7e1a1bb5db7dc8e917cd9b8390b76f66f79ad89d59de03bbe06a0e8a99e2b8b666380a" + }, + { + "msg": "1ee7aac8a07217e4cc226493f632711a75fc0ba44a79c1b30c", + "secret": "c12adcf996321dfe739d23b99d2546984e987dac4293a40b95ae8a59e71ffae2", + "signature": "7e1cb2b09c19310c2f87dfdcebe4d9cf8573cd806321bd083c18c7a71c2bc6356a42b0f071843503156f93f3fde709570f27a518b06892cf07e5ebb012481f0a" + }, + { + "msg": "67ee20282a6c742059a7b7ae6cf01d50c454ea862276917d4e40", + "secret": "14a1eb6a5e2a12896deb99ed7ff63ca3b3e281b22ced567181be1ef970ae7ce9", + "signature": "76689ddddaa29f2143f9ad977b78ca55fb6f1a6aeee0a1f817902f9f565cd2cfa70aafab024a69f33a327886b4b7b9a9a7930765b39acac5b3e55e01d7c1f48c" + }, + { + "msg": "bf217ae33b3ac20bec59fe343960cf8f6d5d173ca44832d9cc7364", + "secret": "b35cd84a3dc9123da2db79cc8fb37a1831fceb9a74cc3758255a5584b6ceb444", + "signature": "c0ce9cea86bdd756adca3a83db287bc2627e113dd7d1b18eda632ac8a11f085bbf07cfba666a3a9a435b4f4b6648a7fc3b51896d5709a5b7fcae61859eb39887" + }, + { + "msg": "87449231eca450270a8d5b6b23be9e96e0a8952b116d9bd542b897ab", + "secret": "0ddd8bef156a4d804255da0ee705d3daa5aff1d374a473d6dd8e045042d63718", + "signature": "7909006534bd1f28de6c45d37d89295b2b788822d54a4dfd4102db0f399b99735df7cd2c511270807b08b1598ed85ddb4024293d962d84e12b088d74fd08be06" + }, + { + "msg": "be8409225754b6c6dcfea6dedce44ae915a86cce07ce29d4363cd0309d", + "secret": "d796f00143d75f1a334a04c6490c9741782df209eabd5c6fb4c94161e8f03528", + "signature": "44ed443508d6c65fd5c1c1aada1b8592bda8caff9f6752997bb6e3b6a5364c8329a15cb02e51a7eb8cdc7ef93b2c48128282d348ebf7486ebd107f6e598fe485" + }, + { + "msg": "b40b5fd7ca7eec6db6f80ecb72779b5338b96b7f60e8d1d1d628e0a9a7eb", + "secret": "0cb259f53a29913679225f3e8ee7189c8410bfae0279432f83fe7c96f36870ad", + "signature": "28e0aaf249eb5cd75f1c65608d2f2d8fc92e6d5fd9cfe6c8c7f4b6c35424571d966549958968c936a5e2bd1f3ce0329c91763d3c01545205784cc3cef7a0a486" + }, + { + "msg": "1816f85bd01eb88bf833581c09cc8e188872b724b0354176d85b2654c656b4", + "secret": "fe688111153ad1b2eacbd98c161badf96d87c408ebe0937d52be3aff79beeb3f", + "signature": "eeec0a323175d54043fe75539c4300befbfbb6e70e350b30cac944a7bc078a6fe37e1fb6880179f0de60eeed33faf45b6e357f224c8b25aa62ac27d39168698a" + }, + { + "msg": "dc095ff38e58492168ea37b8ba801971724447f0dc33b66dda5c5be89c42b126", + "secret": "20f1047fcb297f02d54b189e82d6f532d096c08c25c06da291c064463ff1df93", + "signature": "8c20477e4dad15d916f980ae1ee7cf037b7552442c4989246617e8f8f21e74abeb0cf940691cbb2d413b96e40a9f2a022e61cf98221253c60f720b7ed5129f01" + }, + { + "msg": "2d49e9073db150967803fd570e5ee1d08f4136b840a287dfaa16d89d34ae7229bc", + "secret": "bf4a3d37a2d5368b0fa67f2311cb9640a4931348c8d279665c919471468f12e0", + "signature": "f49b855b8a457cc9a5c5d8289be8fb42a6faee4b88d56c2455370fb2b1080ca62386d3f16686d555b66d04c6d48960ab1b69fc6c159e218b07fec08cf71d2f0b" + }, + { + "msg": "da0be27dea4c2ee3a8f08a456912e8c2867df53f3e662a673df76d1edc506b1530c5", + "secret": "d621d80a6a245226f47cd2684bc388ceed6732df1177c993ef1cc655b9105a9d", + "signature": "1a4111e7e1d14b5a7197035ee174023aee3955b7894fa83b9002c7db883607c6b26bce7df70de182bbc539df5cc49090cfdcb8712bff07f0f3fdbb109c160203" + }, + { + "msg": "094a5561470d408f5978fc85ef18d2b4ab99ff78ba703df07b1d29947b65ebcb22162d", + "secret": "a1e8254850e7d53e33afc0130d9522af4974768831cc09f6d940272d47bf7422", + "signature": "e00602b5f36c542bb13e4b094f12e72f860ddeecf0f328a4c91bfa0cea1ea99991d4b9ca9386c5b3213f97299ed3387075ab5a08eb6a47775ef6df9186835607" + }, + { + "msg": "5da30598f66844ab37032b5278f1ac196b9b9765fff0850ef237e31858c0c5aec6061fd4", + "secret": "0fc36a96280502fd9e89126e59aee354dd05f08fe8d281c05491abee83b84f3b", + "signature": "5beee46711e4c37fa173b15d2d1db445e3fc9bce09886e7de495ae17963ad832076d89f4671d4c027ab8af1251f923ffd0bd9f75c9ccd93850c581c7ef817208" + }, + { + "msg": "32640e1999269491ee4b46fe9e52af94653cfbd8ab63bfb34db4faace4fa7caf9765e54347", + "secret": "81425fe5a3cf38f8515eb7a52dd3fd01cd507e43e44fe49fabbf1aa940dd4181", + "signature": "77d19c5fffe0ac8d1770e1dd3bafb65cc0d02244fdcd14f4266baad35182213b6ae2ecfcb610247cefa6eb0d38bb21d0de4425c9c55d2acf1dd91d9786559486" + }, + { + "msg": "c0e00344d4ac446b72f4558165f208faf3ac173ac0d1dd731e8a2026c6c6d49df5c0897081e4", + "secret": "fe7297cf33e937ebd86cb31c0c6d42a376bb30120b848e3db0ef99fe4883d50e", + "signature": "99672e0f4e6a73a0a1bb6fade8ec6b8463ab71cc862156d1873cc8a01e65e321497a30740e321f223eeb884572c274c278e09423e7017f04956e84faec874d88" + }, + { + "msg": "28fea738b342151461ce38a2b87bb178146c7aad663aa38e54f361b59a04e4335ce9a430244ea1", + "secret": "b6f5b1eb55fc3c7ba29adfef7bca50573acea4fdb203ce1f9be464c877c796cd", + "signature": "034492706b5157fefc825d43e251cc10d6fd4e22bd54482102026e7847f89f329e437f2dc5bcb9bd40f08436bb1a7bbbafcb966a61ef505673976be87e531489" + }, + { + "msg": "5dff6da4086f6b533c19519f683fcf870bab20f3bd316da6df00c47023dde0de892acf76c4c163b5", + "secret": "21f9a003f845177b92835c0d764b0645a24716dff82a477008a3c99e73df3ae3", + "signature": "060beef7c25cbaad23be604fee10a1bec3f23005d02d237ca182b5341623bd7430afbbe0ffc01667dbd3437f0aa2927c433ca9094cffe72222a9d9ff1731b60f" + }, + { + "msg": "4262a49ec4f480d508830499600f26ee1db77c7da5a00b0b3fa89d4ac58395ccc47f16393eeda1ad32", + "secret": "1347f3ff46b55dbf3aa9e3131054c4e01821469c46cb6333c4869494789c70f8", + "signature": "2b52c8c1033e780b6dc99dfdbe298fcbd8407820620964bf21078bc926747754ad143840e6d7b27256fc7a8056017f872ffa499deb5ec5726155c2123a9b8407" + }, + { + "msg": "e4ebc6c1101daca21c181e9d95234db4aa927157b54f875b807974c3cd25e195790d79db6f848eb0bdf5", + "secret": "a8588d8140a5b01861f766cbd0af31d8a9aa2dfb32cfefcf3cfdd40a35b5bb59", + "signature": "f919d9ef90aa5dc8518fad92b4fcd80cae87dff76e1c7302a06d2e8fc2236eab68b9401f14f9400467f259af0377ac9e09afde2772f5a5665677969f6c166900" + }, + { + "msg": "9b8c48268a95b0077cd1adf51b2c32c14bee6e8e68941d88c0f942bfdc5cd53fd6b848db4e565c3ca8704f", + "secret": "b90773e6c695196318fbf118c9dc377aa1e1de9fdb1eefe416a26a1f67471969", + "signature": "282899ed07c0f7bf965f6e4e8dd1c992c6f102a52f8ba4e9a586936dea461bdbbf6bff600d7d22fadfaf6704fea9cda9322e9093832ea42b6d3979cfa3ca8789" + }, + { + "msg": "19116e3ec707dfdb484fd78850e5fdd4e18fe5b4cb07fe1e2f5ed5366bfb47274256182f8e92c9aea4b3bff0", + "secret": "ec2b1e5fc2a271a21fe51499b216a2e0a5b92958a1d77a52a40f2cb1a42ab178", + "signature": "3520211e967619a9142d84ab856e11e9af5be9d9a6b5175327c42b613a2f449a7d60b6834c9bea983a228770307eb8a04872352cd4e2dc4754b672925301298c" + }, + { + "msg": "c216d06b5326b6d83febb7b927c2bdace9a816a1ab854e0403e52b4a1e31dfcf66621f238f806d2dfa44146adc", + "secret": "67e3cd7bc2bc04c1b1519f8004bd80c28da52b98a62af2a50279034e4273a168", + "signature": "8f83a46786b97239d03f0fc01c055e369b96965fa70e986bc0f44ac63fe052b6bc2fb88f95cb3feb3eafcffc30f4ccf2cea6bc53ce7d4ba542106a41ae125880" + }, + { + "msg": "175d9bd6990d02db4412d0790dfe98fa17b2e5584c2c118525bc62078df71d496729bc2065c05f4189e35073b52a", + "secret": "8913c40b979c45af98a94b2bf42e7f0e89bda7d426f724279c5c6c0295182116", + "signature": "a5a1c3f9c816f24747770c288a83e06641deeb74aff8c76b201a22be9ddffdcbc490ade6965973a115c97a0ac264ca007e0420e0d1fa1af88fd43f836f333f8b" + }, + { + "msg": "b60a18de7914ca143f6ffcf555da83c78a901fbfc4144efb2983b5070bd79440c31d89f721cafbea88f0bd9c555d58", + "secret": "fbda11b1076b94ee9cb03eca996226d5b0d0839b58369d12217b97850c996783", + "signature": "bb441458a9d790bb6e0e42b8066b3fcb14e17d516dade21e174375e379fef676a50741c27fd854c87e984af78953a22ad4cffd43196c91fef7689ecfb531a10d" + }, + { + "msg": "33cbbb77a6e088bb922e4bb2042a21b54043c34e1736cbd75e9a9a7f86e6c4ce091c75046872f608fb44d8272665e796", + "secret": "9e47d4a4b0c1e6f6db876b0a67329e3a42e2727cc720d9c230e0ec5983861d50", + "signature": "06706f933e4f2892467ebd0da31394259416693ad0baeecadd49362d15d0dabc182e479234bab4dcc6815b594d59de6e158b8d8f459dad8c736ba7f17bfd5703" + }, + { + "msg": "e9e0ebb3abfbea86731d8e5225aca6b1c2dd5f73824ce5d0b7f4ee99839c1f2becb2ba686893258bb8ffdc0b41bb954c80", + "secret": "925336eafcea668d49fa14d50640ebae7b3cc1bb05f6644234618086ba6dd5d8", + "signature": "c5c73de0523421f8c1858fd225799b3147c35367051ab3660beb0ef5b86753086810c74743ea3fda36e73e50f229745554645f2d6d1e5438317523d4c8542f8c" + }, + { + "msg": "a14e2a8f543c3c5190d25a282af781335089296238ad754f29c9a08aaa7c71229aaa868199efa9f8911b203da6f4f0a6bd0d", + "secret": "9bad136270ac39145ee10fccf54da42d22d7de6ebc1d0bde5467f8fdf83c70af", + "signature": "54a66045d85c1429277357671e3fbec8fc5c77a97d1306d1ba9c74820ccf662e4358e5c015a2387b0fc55bfea61e337852ad6d48ee46070e29b1812c8d57670b" + }, + { + "msg": "7a", + "secret": "1c5ed3920c40d967f065055cf6988c66520446f5df354da1b8ff87f64d6644c7", + "signature": "5a220ebe499bc12c3feb8184d258e7f07380b5c7ebac4c6378f56ab20aaf84abd7d53fcaefc5efb73dd1f863ba58931f87580e5bde14ca4a5ccf623d1bb30b80" + }, + { + "msg": "db8d", + "secret": "9ee0f7ba52f241682cfdfbe5cf00e12651a7a94be24aed12910cdd21f34947e5", + "signature": "2303bd957a62b2ceaad2bba616c1eb2475eee033c57f32e7e2124a9dca45ca272195321c5e27f16c6d42d1c7b39b5f8a60330560569a969cbd5fe304ca097989" + }, + { + "msg": "e61196", + "secret": "516ef0f80a479a93c6861f78e8fbff04b01ef8d6c65ab2d77d55b912257a4e0a", + "signature": "980147e841c8b3fd32b9d5b7c0a743850178133118b49c58b385648dd58814410f7f0fea0116bdf88bc6231cf7bce09ae46c2b5498f8c0f4a18b2af1d3c39f0f" + }, + { + "msg": "b7adde6b", + "secret": "206f1ce13948ca499d1bdf13bc6811c2d154f055f9bd18f0f924f2d1719913e7", + "signature": "51fd7161e9356ffc4e73d4793e37afcd4583044292a3ec8ed76d2f3897d760ea303b564bff854b2e84763c76587dffa1242a53d38f1567e4cc60d606b1a1570e" + }, + { + "msg": "42478f7914", + "secret": "c3b8feb9306753bd4037bea0b288b15facceb40dc565b377fc68ac38618e665c", + "signature": "a70c8daa236ab91823a7412ac15b7731a1398f52e2393447aabfebb7cc43736836d38a94100919d2e75f76ac2cb7485bf2fe6459b9f0922333882c105d1bef00" + }, + { + "msg": "a8cee83b4840", + "secret": "3bf7e516d8c99f1efb437c0acb2bc44e86eaa6b44468ad9a6c56f5ff2474c153", + "signature": "540c2aa0466707a015fa7e9ede994beff92b41aee867b10b44a395303234ffe7894d8e7231509abd4185e4915a3482c0790bbfcf3b1618152be5314261cb8f86" + }, + { + "msg": "5677cdc41f4522", + "secret": "ffa9ff1ab63e86df6c06196b3682b06987a42b5d815d40055157ae5040aab74b", + "signature": "4f87b42b96a1f78b85bead7b6c1c8f982f98f3cc71b02b6a1b68f52f2d22ebf95f0924328fcea5e388cfae3768ebf6e3b731b7984efb8d7a4b18e36fd6e26f83" + }, + { + "msg": "4d4946dec740deea", + "secret": "3b335a4555a6dbe35642aa872d2c486d19f2f162e0d1bde235e1d7b48ab4311a", + "signature": "5892f36bfa7025373b372d596c73813dd048b021149126180e01865ac9ba3a5af7eaa535429c2b7a77beaf6ad339b6e39ad6f84ad1679a5d488f1a1e0a872083" + }, + { + "msg": "736617d4b1403645c3", + "secret": "0331ab69b783b006008e363389bf361dd20c50a0028f046dcc1d0a93060498c3", + "signature": "7f205dcb3ab2c7ad36c4bd551863a6cc4a41a5c269b1dadf358e3bb9ba549073b019d82619214724cdc3ae8be39e0fb2a54f364c4a3f0f71cfd1d29a994e1e00" + }, + { + "msg": "5606ab63e7a4a06af1c6", + "secret": "7e5c2c3bdca1593a92798a3e4c80c9ef3aa378a7b5c9e2163daa3bb62639dccf", + "signature": "5f076443ed0157aa4b7adc656aae8720d6e6403449700b3cbf64834eba15dfcbe3b4761710fa3aef0d41b42402f1d6ef5bdc095a5cdc1969c9951f9735162284" + }, + { + "msg": "31fab344acf2280728e49b", + "secret": "41cff320af7721375acd59cf42075ae148f71e7da1f33dab28539e9deb03d62c", + "signature": "56cd2381d6f0c04407da267f5c63f9c47e5cf2d512f704f8a10405137b48c329cfe4ef12efedee980e51f4776178b90144bafe4919e950728612174562b4bf01" + }, + { + "msg": "6362934904b564e6d3ae510e", + "secret": "092beb36a60e8dddb614fdb11ceb08b7bcc188c127fbd5e8466b63e646541a2e", + "signature": "c2b9b93d2165c0501e1e15304d7c269b3e19d0de108d911a5eba5575d30807a70ac23c61e68292c547c337fb3b970e0d6d2402446992ebd08c7151300f0e910d" + }, + { + "msg": "7a35787ee60627af6da56ece24", + "secret": "413d16ef46977702171367684a1d63dcfe7f233e6376b3e64a5502c2d59ae718", + "signature": "fec51e61a508ecdaea7b919fc9ab3096c48268bc357dee9f8ea3062109a4f76ca1f04d16190c83642813d32adce0bca3fef0589b2f5d7d5f158b70529b2f080e" + }, + { + "msg": "cbdbbae6cee1011b869f37c26b5b", + "secret": "ed621119723edfd74090e210b31f3a61fe488ecdbec55cb7558ccf3fbbb016e0", + "signature": "ffdd3f7db648ec9be5ecda745018eb5863c909cb20ff85f36ae2899875cd98c496a09a66eae1d2123d27b5e74d08177571767b4d16c8793227ea609a02630a82" + }, + { + "msg": "27af0cd5b23facca51db775f222bca", + "secret": "716b80c33331d94650a248cddfa4eb9a1ff1f8024de5e525c29eef54ecf3d976", + "signature": "2b1f48ad2a3f2529364a54faf160cb7a07116a554e96a6f3ccb857952c49a1fff14fc469b79cf093dbe470c0b5b71bda839920229a92fea6aec90b5df58c3f88" + }, + { + "msg": "795a0bf42e946278f2c98a16e43c52ec", + "secret": "6662a15a336d3fe6febd16732f2cbf9e4efe3fb6865fb6c6bac5710012db70e9", + "signature": "702b32b9ced97024c2e5583851caa75532b1e4400a7299045560a7a0e7c05478483d829dd006f4ae17830e924417b9cb6f6ab40d5d52c2e58fd0ce146ac4550b" + }, + { + "msg": "b6c76b124b3c43ff7aae7e36df90a4db46", + "secret": "0d9686abc47d520acbd14d1641bee90ca4b2f1fad28393abad08870b59ac4e40", + "signature": "bfab672ede0ece424e96899ac8f8114ed654472ab576c2ed1abf06ed6945b28ab47bef95d94e0ab26f7655c82581f1deb38acda28bf67f0ec65deecac4f34c8b" + }, + { + "msg": "c881c86343efe607a976948dfea1419a7272", + "secret": "67d84a7e211702040e945fd77799bf71650356f6d460146c3ed9641a1efe2e22", + "signature": "0bef6a4e326c7824b136c422aad12dddaf12f8ff5ec2a448d40fa16ef5566d3c69d4208efae3d2a0f78c27b271f66e7ab35ca94036b373c8fbce96a64b9df60b" + }, + { + "msg": "bba6886e5f19f8e057b01ce75d72134fd1a2f8", + "secret": "9342c68c95be12a76fa43f8cc432250daa76eea5db5d79b011eb289613ac4685", + "signature": "33662e95cef048739b17687ced53e00913f3a425529d9e771b7a03b630aa4bde50eee98f7cd5af138a7f5c4920af484280eb4fa06bc6afa26e77137d663ea80a" + }, + { + "msg": "261ba2bdbb29ad98f68964760d8d20262ffbab5c", + "secret": "2fcde88be6c346eac8e141e98d85839ad7663c4b4737d34195d23518d693ca8f", + "signature": "2e0570bdc9d93f5d67b046ef77657152bb6b06afe300b9c28e1f05094d1434429a275119d8bb5b4a366d27aed0de6f553abfb9d87083100664a59a538639da8a" + }, + { + "msg": "500bb6a61350b268afd1a6dcdd1c4a06ed787644ec", + "secret": "af6891ab223baed9e45a0434512db31bcbd05e893771fd9e6bec33861b6f19b2", + "signature": "37ba5008f0c8a9f61ef464578f8cb588442f5fcac5c581a5adf448be6905be608e9db1530a5989646c71ce66a12580ba6eebe4431bad1975e003c26c32f18b07" + }, + { + "msg": "39f44d6827306d6dded32773df8f3ac4ab9026a90cc4", + "secret": "6583ad059810997d37673725d4aa818edd7493c4adb8432e7591ed0856d9524d", + "signature": "e88e87f093aac536294c95ced2ae2707eec554c11ec218dcd34411d407ad8ba88ac8dcf332373a2a6caaee70e81d6f9755aebcedd57128182aedd2f2c094ba81" + }, + { + "msg": "c37f9d3c4ddb8feceb09530d6cf7c94b63034d23dce9be", + "secret": "93fed124bf53258f0c11dfd04b8465abd917f67b745156b1a49cc83a9c28d904", + "signature": "01029f0f12015b395807b40b7dbcd6469b4b764d41c03b9c081768e896db09c539f6dfae09f5201f59ced1904a2d25234878238f2c2981b30682dac59220590a" + }, + { + "msg": "f6f4ced6ef76c134006c2009846be5c58b5f077e66e1a302", + "secret": "e3fa72004f4bfb20cff8441dd74b277b37f60b849d0daaf57989b1b333b5d7e2", + "signature": "af7da63ccf78fb50610f114eebfee68ecc41fa87be32ac99395b71a3477c848302da6614284bb53e1eba90d6281d6d2ef993cfc4a23120b054522d297b49460e" + }, + { + "msg": "af73146d2624fc49667ea43724fae9f56b5423d333009eba88", + "secret": "a2dbe25ca25accf875deb3890f7a616dfc8c54059562cac191f7c1eeb50e99bf", + "signature": "ca7b86d7356d803049400d9a40893045b604a7665c5c440bade874dbe6c747740bdf7e6b80359bfc46f79379fb540b83c266d035f11b2b34ba883cac7efa7482" + }, + { + "msg": "776c62ea6bbe3e9cb8cbc0be1e0cf7b4963860eb4f30a096627b", + "secret": "012ebc002673ff2bbfe043dc18e5733883011406e57ba191e00a58bd36dc2eab", + "signature": "ac97e77f76562a31cfbcdba0d46118b3baf5dda4f22c3d79cc8309c6dfcd6db3b86f4bfa32f9121cf6f340aa3ee50f94ebbe7476f5a812115a4d48764920eb02" + }, + { + "msg": "b20886bf163f4eb135f81e130504061fd2b3972f2d772ded86a47a", + "secret": "5efe8749fcccd48ae962276330ed9c0cf3358d15cfcfda639a4d0bd1e1d3da1f", + "signature": "87411311aa8eed98ab16fcaa214f80eb1db4b43f16202c97ab2175d8958e5f34de51f55ae2afc36f6c02d00d128f98f362116150b7408231de9ec7868140218f" + }, + { + "msg": "f4ef2b3758f55b29144daa6473157cb99538468fc69e828249b193f5", + "secret": "45a318c6f1a5e4647f8e1938879e2c54c6fa6c1b974c7d911568ed947ea9d991", + "signature": "5c6e73b15166247c38a999ec3d1a5f1d89cc1f75be5b1baee2ca0f9bd4bdf08c8dc2f24f53ab7ca31e55ab62341220bf760f98119df50b7d1714d3c3aaee3d08" + }, + { + "msg": "ffba6948f62a4e780e3189801d8f9030a5fa6502ddc2f52a9e4a041d66", + "secret": "58252fe77c2200ae306479e145cfcca042af6bff9a0461c3f3ae8271d1144247", + "signature": "197bff2fe14d3213aaee483579fe875a34144523d316665fc3d3ecc8cb9889dc84ba4d3f4ec847572624a2c8bdd305e34c62826e077133643f33b3bbc5d37007" + }, + { + "msg": "5b4fcd1db10d48522b254d7f4fa5b02f816f85fc70dd41a699e957f89f4d", + "secret": "926a9c29047abbd916a5749f5362ed943b146256abebdcc5678cb402bd3331e1", + "signature": "ebc5471989dc9da3c9b4c49b2bed85d0455c8709c2ca0ab31a8b30d6a1268a65f222f1e237a8276d4884e82e7aa4cd2bd99411e4f9775a032c38d794a7495382" + }, + { + "msg": "ee491c2d0d7d2e1f4da088ce5cb98697d29599779a44c608be42fe835795f6", + "secret": "9674b1850323130e3836969633edc8da34e9c432d77ece9c37318f34c6234496", + "signature": "6d8d8dd33738b3acac5c695c13414085728792c97946c6e3f0d08ab5876cae32baeebdf44465898eb82fc2836f3b2f0c1afae970ec96d93bc7ae8b78ee46e902" + }, + { + "msg": "b319b6531a784c6cf505f33ab2bd7873e5bbfd6aa632a88ab1f6064e0b80b5e8", + "secret": "97dbddbcbfa4c4bcbe1eb49a03340da5812a31872da90c0781992d03500d626d", + "signature": "cc07e2723b9e0eb7acf1ce67cf862c7937328909354ccbd2db731ea20d7e0490b344945f787b1d8509de239601c6826d8a8c2e09dd8ba810dd27e9076c0a4906" + }, + { + "msg": "fb47a9b0c3e6405736ed83d42b36918edd64c15b870ca8d296811dda0ce6a2851b", + "secret": "8a82d435f56908faa99ad6ee93e54df675b06298301c26ba8fe89a24b57ae994", + "signature": "04b60df2e2782dfbcb8704373500d5e082f73a01f257cb97880d093b8ae37af6eda97d94c20b76443cc7edaaf02ecae791e97704ea3e7b77947644ccf7165207" + }, + { + "msg": "f36b0ee3f97410d6d6e8a0bec21ddf2b8e808241df8d1a0191e361b3d05e69accb22", + "secret": "d7335e194b7ac094bbf3e6cdde02b8ea1188d1b1aeb5a3dab0627eeb58d62231", + "signature": "a56cd9a5f1b9299e077e691f04323e275182972e76c886f0eb3f93a72dd7ad63f68d846f8090aee3b286ba3c13e51b552ccf65437f0679e6339f645411771388" + }, + { + "msg": "91515014b066cd6c50322d63b00645ab0b52f9551703438e8755f221ca926f0b717dd3", + "secret": "55bc4a1c699e034fd1e00cbfa5b2d2ea622eef4bbb43e5dc0cae99338ce1a3a7", + "signature": "40fb7c229bd9591565a7b1e18a13aa36045991178f314b6c19c84f5c8750b4350d2556a027c85bcf024ea4af032df2c79d549874657791c497d77942f1e90c83" + }, + { + "msg": "93bc1907ec72eb3a6d0a5cf5fc8e93ba10452a8145e1ddf2e86a9c1e4577a72dba36ba48", + "secret": "af69e89cde8c9486252f319a7d7bc88fb3364f5f585f7d629851fc759600ce0a", + "signature": "a794b8279f9fdf1dab2a88049f0381941c98c57548f5133c81a79d45c29887ed109c6e512e37f3d990f14781c789bf35cc971129b7d404e357946f0e06ca9b0a" + }, + { + "msg": "1abb81edf8931d6b8085d424c9b74e1a2715c2ea5ec99a55cc14ba09ee17cb1007ee27f19a", + "secret": "773e6165372224d0eb686c0c5540accfbbf6ece8233417a7c4204d92ebb1ba52", + "signature": "00a00b71d6d65bcd3b95f175055c5f09c3bed7ccc3657b142466648aa499f38fce9dfdc19e10812c454c09590dd51b4becc728cfc5ca246f14e49333fc148c8e" + }, + { + "msg": "beebab1d478d20241fb37679601eb98adb4b6bb7c64cc70540c0a688bd1b722a6c1d0bc3b407", + "secret": "7759065b59040d963cc3009848f265ba3387af5eabe71555c7e5a409135bf06d", + "signature": "674c310c4379764717911d7807656528bea2cd11dc19601d40889de2d6a2b2dbd2e08da1444304689d360be0b00e849f0c604841f17a2cbd7b9ddd06a1ca5f07" + }, + { + "msg": "cc9612c390c119acfde30242d10730cff003bfac5ba0dd9f37e36c45c30d40d271bad31c7a2305", + "secret": "248565272867757ac86d0b145e3d42c831ea621b4654b6c7bc01ce8fab2cdd85", + "signature": "9524ebfd73f87a3e9d584d91f353cc8577cdd52783b124092cc9da26bf69df814f814e7c6d2e9bf1d82837b15ab16757af5d8050df2bed002d17c8e9b8d9eb85" + }, + { + "msg": "2a7c473bef4c5b58ff770282e2d8d5202bcfde284638d218c8ac3cc512f95b3dd7ef5911aee345c0", + "secret": "544de634496c366b42968be09e0ef75b38b2506be711b31420dac2446c49004c", + "signature": "c469217d0eb59c142c84bf27e33477e8acaa8aafbf6a60bea1f022a83cc52abf1c028e0ac42279dd0d223bd009d445db2a46fe77a6e70f0e82b135e234600082" + }, + { + "msg": "65e264479ae8ea8f3f01329299fbac89114b0638c3e0cb380652ff83e5f5e3273e56a0b656ee296fb4", + "secret": "3a7b3a080130af729a6f00b6e07d55c2f993ef683675353df661d1acdbc284c0", + "signature": "c49155d2117e47760cefb8b9f62232beb21c71ec2c5a375293348046d920979dd2e38df3ae47fd5a1240fdcc2a05c5d273cafae623121d674ddf045bdcbd7684" + }, + { + "msg": "d97af1088a34e02c5c25a8f1d57221b253f02f1668ba51a0066eed62efcbeff5f762cac21c0eeeaeb323", + "secret": "4df0c5c2fc0acf93d6c80e8fcdd4ddef9dd45c39cdd47ec804a8996303f78b80", + "signature": "4e9d6ff7870eb9804a838de72007bcc9bb31d8251b2bf8e656d97935cfa367c5fb6c25486794456d1f93372c0214a3b6e7a8ac3cf89449854f31cddfdf368d8d" + }, + { + "msg": "3797df681e11377d02039393da0d2f3de3672b5643fb7cc9782df03c1382475a939f67064455ec4e1f0fe4", + "secret": "eaa9ee3e8d948f85b8725bfe1cbdb2bedf22709e66568b1a28664f1a1f0efec3", + "signature": "5c43feebc50e32110233604ae8f6ecaa3f57adcf72484ef941d649c41cd163e5c8a0e2d1fe8333e297124a2b73c5a54d933a32404ad2aade321d5fb6f6a29e08" + }, + { + "msg": "cf11cc198fd50d9861aa987157a8780bc7bfe6285f6416d38330ab224bef4cfa823548e34677317115215456", + "secret": "5649087dc80755c5c160a946f180959a6109a855f988140efde9be88a67b3664", + "signature": "883d5fdebe6a47b0e1192aee242cb722f9915c374783271fac6975dc394d218870adb66a79b99ad272953ba98f38626b0914649bdcec468d486c1b2a2a46a903" + }, + { + "msg": "cc41184ab14cba951f23c9541a865c7ddb1e28428ad48a5ff470e378c458afde63fa4b3bfae4da6ab0d6901878", + "secret": "bc226a0c02009214e1e835eff8dc7bd2494410b3b50c60712a8c8a2956c00023", + "signature": "36541407a63bc57b09d2b3556aa5a7104fbf98f44aca0c2209a5cfcf98e69c82b347b7a68e359e5a11c20c94eef10c1c1059a25cbb4fe0befb4987ca057eac0a" + }, + { + "msg": "6804dcfdd540b0453ad7da57f6792bb1f59b3a98780b41241ac73d1d7597fb884ee080ea20aff26032f2a5b7ffa4", + "secret": "11c45191beb0a0a25eb3c916898df26c189ad7a404b9426c59796937d16afcdc", + "signature": "2d2d20d93356a552d4bd5392679c12dd0f45da5a6660a99656b1cad06bd7e0cd6baf30226f81509d59a049a70ed310590725a0d54e70c2c777ecc85ef8881705" + }, + { + "msg": "62edc5bf05bdf320fec3469e604504dd065c9c7c2578156dbb41a63f5818d347ceab8045cd63cf282338576a5c0662", + "secret": "b84797f691a3a5d1c1cb1b90c06722ce2153fa19eb9e806e7913affb277dbb6e", + "signature": "6e105779b75e92866fd6a91038528ea7f940d188a58e06c7d07744a58dd7724a03b97941732da8c629c9e2cb4633a6de8b42ef5159ced7b7231a12bcab691d07" + }, + { + "msg": "ada410f2809bf3c063da562de951246ccc85c50a868985709acfb6d23ae1780716744524d5c5f4990ae97be8fa9e5ceb", + "secret": "6a02d3a3d345675771d02025beb27be22fefcee60b3524472d20469b0f797c21", + "signature": "56b3925ae143c218f74c6f8d90a65ecbd500a9dd63e35b5e7a4ec6710ab185f0b0810f177b609a36d9ff485c8ab9bb4c66a3088a6e5edd0fb00a127b89cbf002" + }, + { + "msg": "6bf0c915303e9d053899c80802a42399f98d9464f780824d901206d7680f56999c732f0f487486109e93d9666d5bfcf52d", + "secret": "ec32d99a440f3d0af18cb8b3dae83a2e9c8cb39dd8c00efaa99bc1e06eeb7c77", + "signature": "c550d93e776cf963fbc5e5a00c24ef21902f71cf70d0dec6e9f0b780bd37e901e00f0ddcb0290005ec63fec5e29c1a239e469d585c06e65fc16ca896a1688087" + }, + { + "msg": "", + "secret": "5954964ab744e524e7e70c43863d57d0c62e326d4ff1400d2cd0e2073e97759a", + "signature": "f66fe21afd43427b0e732baa3bed25345ddee1042afd33f188d0200dff34c90bd6d994a58f9654be780f9948c3e742b259f08ca6b18bb3475dee3fc619e85206" + }, + { + "msg": "84", + "secret": "e38a64044e641b7cb073f286daab1e4bea645eb50778533030de6da960775877", + "signature": "fcc9b4455ee34a6094cbde6f69d276ce152bc8f0da78894f06b02439bdbf3361e18ee6068119375349a107865d2c56bf07c851ebb89660034758ad507ae99282" + }, + { + "msg": "17b5", + "secret": "7e4325fa1b23c03398dfd1546f81dc2baba5486be2a96e00f5a4637c12945bbd", + "signature": "0b11160474e7938f26efda1642ecbf8370de377da9b94801cab8558ebaa98b48bff214f28b91b135009f12cf4a8a8a7863bfd9090f3a6d459998d6d2a74a6d07" + }, + { + "msg": "5b59ae", + "secret": "41add8b922cafaebc33a9bdac3abafc93e9f81aa7d5b8c01604460c3a56f00fb", + "signature": "9e4ba3c89cc9d7633bba3f1f190af9aebb455e86e0144924add7d0224f68b37ace4af4c14d4820495864f07aa2e3031529085adcb64563d2269ac0a020d1a289" + }, + { + "msg": "95134a6a", + "secret": "926ff83e06ebeb8de976151048f0af91f33b676530fa7ff3d0e6b06d143990c4", + "signature": "7099ade107f1340ed8655336526e21916ac9e179cd17a8082b8a87c5b00f350a616e7f574db785374aad9c665a122b33acf11a151e92fa8369ad1fb06086500b" + }, + { + "msg": "05bbac3f83", + "secret": "92618a5cf675a7983ca590f81fc44a9ca6db3bc92f7a36ff89b1eaf11f13f45c", + "signature": "d3502d568e2a331816cb164120de6fc95b8ac59c5173941281e88d18d6f2f6368233d0cc3e218c8d68791ab7da84fa849cac483529459ab0d3b11c0cef73818d" + }, + { + "msg": "cb5a0050dc17", + "secret": "8866243cd2ce307bf5a7a2fc80f322fcac36a5b03fc6ceecb84ba7adef621a41", + "signature": "178bfc2e2ea8f47f0401448bcbd85070050924794d547ec74d1e39b35c8ccf01345c321de1941a71f997f39dcf4e9c5b188d66101900c9518691b0080ee83f8a" + }, + { + "msg": "5237c24e545477", + "secret": "55470981e0e80ce4c5df351f444a9ef7544e0d59c59c21be79d4ff11f7627d00", + "signature": "38658bd6c30275b12fe2401757e0f062edbd3de32b15e2e5304c844638719b128b97e211bbbeabf80ed60aecc3e65779c4b23043be1dae728e28ed731571388c" + }, + { + "msg": "930cbace486e917e", + "secret": "ceafc7e3887a8e0faf63663d3dacba5b158507136f01e0b28da651e681c0ac41", + "signature": "3dae7f80287116740d29689eb0d752392e1324beb045e2eec8ee167bc3aeaeb7b290a179a73e42a34771bc0749dd35cc819955da2cb9f41bcdf3934873c2958f" + }, + { + "msg": "badd61b029a9640d21", + "secret": "9305469f9050d4063a9a97c5470b8504c1bc9f88036a9af60829c028b2ad578d", + "signature": "26fd8a84cda417752449acd00c0032df1d96117a0cdd486c140686390a93af6b351acc7be472dc22b164d356141f17dabf8d0db541e976acd51bf5f7e0567f88" + }, + { + "msg": "3ace90966c15263b90c5", + "secret": "b5ce6ec8691a365aecff7b9cc8b791ea3060d148b81bfe6aa1a3b6a04ab4e394", + "signature": "123d78c346427bae16e73670a9407575e6144a6918173a9701a64fd6e70357e2b793609ae131edca4c6ac0b1dfab9c593605d282521de6f3d8f36d446f5f888f" + }, + { + "msg": "d8b8ad363d6f7573f5af39", + "secret": "a9091500aea33a446b9aad8932d22a9172fa70cab14f69fe3da2c6cacade7367", + "signature": "855bbb9487758f0bc681669db3fb36b779a46b829b76838794da4241a233bc7e2940d679f16f7c08d6dfd430a542464bb1491de4032aaf3224d2ccd2df0f1100" + }, + { + "msg": "f564e2c21b31afe25bd890b7", + "secret": "fdad883e7ce792ba75a11000989041530912d4038825b0d783be7b0d31802f57", + "signature": "692f76c9c451af012b2781e8f2b9595d1d3593f4851f11b109c1b47ee67bbe70cdeb259aef123fb3ced81f6bd00294f574b2adfd905eaf98032f23fc93f7de0d" + }, + { + "msg": "8b07e7bb83e09a8f30f021e688", + "secret": "ff286edda0e412c119505576178663aad6e72a898134baacb43cef90a4f605d8", + "signature": "76a09632f983e579982a0988ab015909d7f16c1e7b79181f1b7c61f034b2502943afae3d233b158ad67e8ead8e74bb16b2f0e7bfcbcbef595befdf5ec1aaa88c" + }, + { + "msg": "f9ab048b3a052e4594d1efda5b69", + "secret": "fc5b461b8023215d50e3fcf0b40494551983683fae87391636e276f9b7f11550", + "signature": "da3ed31313ab8c590318a8af930e1c2284101800f2fa8a57bfbbc7ed3cc997c20dc1f18f2ebcf1f613246be4e0b219583f250551cc52ef610aea63867f4a298c" + }, + { + "msg": "2c204fe4a5e681aa2509f132ac2fe8", + "secret": "0b5732bb442dbf9eb8d708631fe2b5f00c21ccafa7f0dd2ff72d8724a9ed2f73", + "signature": "8bb3d7bdc82e1741e89490fe2698fd62be7f0d8dd9af3bb8f21b98141b497b1a8cb46d1c7764c85ab9b194f15875bfd184e0a7732eea6fdb873d4d8f981b4c04" + }, + { + "msg": "6ab989c06c024a67bdc7a4723a37ef80", + "secret": "ccc0420edba23a5d011a6039f06175d9e5f2a0beb46adb8d1b9b6911d4bc69ee", + "signature": "85c82417e47999774fe5a68b27dd8fc5036224925015bf48b663926461ddc359cbaaaf85f0d31124a79890d7360821f00f9995b94d03b3672a80a4425cb98802" + }, + { + "msg": "6dc339870150719e5b53718af044f8c8d8", + "secret": "c6fb79bc37b10c93450eb66dc445c7978a6d00c9ee54d50adf48d68e2f1d99fa", + "signature": "50b75ce9db72f4d3c7d66bf0062a562ae1a2a025314fb5a279c352dd4d8a46c28692e13d60c010b7d1860318c7a5312f6db593e6fe66b28518101a7c4ece020f" + }, + { + "msg": "50e912ff9fdee853f2bffad12f24c930569e", + "secret": "56bd451a4b6b55d916f5d36a9d600a98e010a6f56fa9db2bc0bbc76cf3bdedf0", + "signature": "b89844c003effc136a44d8529bd5af9e2066da3282b919889cf6b1b63546b48b87d8300a068c1d85dd6d50108bca9cfc5662bfbc4684460e0dc069d87542a305" + }, + { + "msg": "9ce74a69077b5db3f6e4b7c96e43921d456497", + "secret": "35703bbbcdbe80c23cdd53cbb8f0e4eb271f1951b126f0576a0c430c620ba08f", + "signature": "727457b7fcd2d16aca7107d8ec697ac617c7d7f1f36b00928fd367128d24d179fbb36089dcbc8f8c924e00ec134d2b562a9176a71bbc8c82af20d1d064cdf301" + }, + { + "msg": "57d039a1971cb501390d606379a369d8ba857291", + "secret": "1d368bafca4b53b008f65fea913e080b0296ee633e2811341c2a9f142d97989c", + "signature": "cb9a4a4f6442e5084ce0fc7d320484038557e79bb44b2ce8c59995fa21a134244348543121832fc6dd315505e636fe8946c8fe407da3a5fa35ebbfa5ae83d287" + }, + { + "msg": "1b5f1ef10c47a9852185dd3e3cdf946aea3f01b69d", + "secret": "112be88e7a7a2ebf385d3c612bdd32eb7a45e1d6ede2fa27bf591ee2ab443583", + "signature": "e694e8c6fe848197165c1b7150d07f2a6a7ddc5ce636c8f7483dd9f87ada2eec7b91fec92f1c72d2713363410b5532157d3e9354126db2b7f92c4f28ade4000c" + }, + { + "msg": "19f6d2338ae54b8f6a0a4ebe67e58e8c96ea7c6ab4dc", + "secret": "29c632b487429f1d1528ed428cb4ba3844c1b20fb784ba2990eab5230d8c1b4e", + "signature": "015704f124f9c7d3ad183d28d1a7f3278a9e1b2b160e334ca33fef7f0a5ec36fdb9f28634fd76021177333a8b307dd90bd3c45a3193ea7ab3cfa3905bd7b1101" + }, + { + "msg": "e467c3d431cf0d90ea9c87800dfbf8ae87b1e9a6366347", + "secret": "3ddf2f29788846793b5a0ec97149b598cff609fd927efcc9a3238bc7fedebed3", + "signature": "bcfd384449591c68be8386f8aa0d24d1c381781c27a2b5aae9cecd65aba0cdb6ea437223d3581b2c54da969368cd93eb2d9fcb8bd4e86d53d3983fa5a5b5338a" + }, + { + "msg": "d3c64c6610f2b3ea994a1bade14821779dbba54397176438", + "secret": "64a68d1fe3e22fbf6855379f9cc9172ba03e00d9a0f3501d102aedb4bc0a6e09", + "signature": "ed461114f3b314ac7234817143fff12eb63824ed68ff1773fea176488b9e0ae07f5b53280522c5f47521d28a6a1c9a892aef2fdbd8aac1f5e13d27b65ce4e587" + }, + { + "msg": "128dcac7f65bffd50c70e5494284bca13866eb72a30224789c", + "secret": "edc9137c3215497dbcea39f4cb0147a4c8016a67087cd26747c12eca17f79243", + "signature": "8fde21e64db6639105a87dfbb9d510814c533b9823aa7d4afe0129638655170fbc0b65a7bca778fc18aceb92d4a02224f2c54f2064331579e1cb77a0064f998a" + }, + { + "msg": "b44f8b0f690389d5e26018ab381b8eb1cd375d42efb2bd837abc", + "secret": "8e8e2f8e3ce960a0f41f48b7a823d8307ba8b0461230e58b499b91fb5e45f2a4", + "signature": "d802689b37773573f71a9c3c416e530011a3ee7974664d30227c2ea16d24cb56a186d57066f73b2592fd6c4e81529cbf399b3c052b217843664bef21a65d8181" + }, + { + "msg": "b0326a0aff7b636e39a4fe0c9a03ef7651af899499f4d951b2bb0a", + "secret": "10686373082ccfb2b84e0ae72e4023eb9c13227e96426dcc8493259f6f6c7f31", + "signature": "4b3d8829d082f6d3b551e3311432216f8a649b26d7429126489bc59c0ad5125a1f3bbb3fdb20864547bc7733669c98e7b2b559c77cb6785116d09a8aa3f6d68a" + }, + { + "msg": "83021c2d88d1a8901777a100a24433828924ce3b8643ce408349e1a9", + "secret": "475a0953ef7641a6115b963217900d7e7bc6f764d9d0a6543843b76daea45de3", + "signature": "6dd2b9cc733130f01630cfea33f7165750c839b68cd7cb7523e9a2920b5733a5ea5a475e51793280e4deab7c5bb27bc98c59511825202b78c8ed326ba8bcb783" + }, + { + "msg": "9e89d3531e909882289afabeee580d6ed95c9bb97c2a0cb3e2df34c93f", + "secret": "0e714095c59a33103bef37e30133aef81d3d82f75f0c7abe6a5d98ad723104d6", + "signature": "416a8378a5435b1b476106dc38ff5c26343dd29459fb676f93a1590c9ff3ef06e1d85d942972b1dd6e17f27c4ba95dbdf5bbc398c7c554f05d6e685dc23f9106" + }, + { + "msg": "dd09568afcb2ec0a88e7a952516911becf4422a9297ba5cfdc88717bcefe", + "secret": "f18e44c9c01042212bcab7db61da9a3275d7ca7b1c343cfc1e7c2b9229a3702c", + "signature": "e03dbdcd9933220f10e8ccd6ede6cff53637c61ed3373fc6a870ba67aa238dc29f46fa64dcc34a50a29791072a08b3f365f009e47bdb655d5ac9a35687098909" + }, + { + "msg": "6f3763fc14780acca724b12b029b47ae0a54e42a89688cf944b2fee0fb4797", + "secret": "2ffed2c14f50d89205fd15374e5669368c343d0753329b0e533cf4421c2342c7", + "signature": "1208903c2ef03a44fd1c79f7fb34262c1070d12260eb31323db4b9bb572d13b93670fbfc4737a63482fced399c6d8a82161ec564114507061a465a416de83185" + }, + { + "msg": "1ce6cadb4a3b52bb90239a81df908c0ae4a48da2a0bbe8e863b9f25b472c82dc", + "secret": "777500441cc9fff20e9f36bc1adcf0d6c83e649997f08ce25384e0fb0eba96c4", + "signature": "086d4215eca20c38c237464d890fee375f080d9cd2bc8076a19595ab6bee458466c4a2fb49fc9a3285ef788283ba9d2288037e7865000fc6f9bceeda2415ee82" + }, + { + "msg": "8bc5924293714c0133d590d6ce9f1bbdeedc2d0a791e91fe389a250f75f7d61166", + "secret": "016d72a0bde83cb185d9b4bd4dda94ffd8a64ead53d6e0362bdc660f4260bc50", + "signature": "0ba54ff3b8d2721cfdaf40c0f8d0ff3b61e95dd96a4f3765afdb0894143f21bd525f93f8dc048edd31594558082b08588d918120eab5c72d165d2f419a3fa302" + }, + { + "msg": "6f3cc5a91723ca14cb6b69c3190d55d8f61841dac3815c49974569dae5ad4a6545a0", + "secret": "989c8394b3eababfd1319d0fef27008b3fa2d22dd6a48b41154bf1fcf30e6949", + "signature": "966a60ec7ecf357244e1d30b57cddee8b05e1f5d9c48b50e49e1a854b95e58121c2f3a8607696d600a016fd717d229e5aac4e6e3a1c510f3bc43658af56d3787" + }, + { + "msg": "814622df86918939ad17c5ed2d1d3f0325431dba84dfbe478763313feaad3971c48eea", + "secret": "839f411389e825a098cf9e2e5c0c5a0c89140908397526f27e9a605e49dec9e4", + "signature": "1f8e7339620bddadcab8b1efc1a0af83c65c674df2605d5869fea00855108e34b824dc4bb886640fdeaa0e14774e5cf00a454c0c5eddf2ff8d3834cb92042106" + }, + { + "msg": "62fe25063ba5c01bb0eb6fee7654f231f9148c4b174b2164dd2c4e5c26a57e47289be2eb", + "secret": "98ce2dae6e0b610202c79e5673864b97cdff199280db8f0a5e7a5dd9b6985523", + "signature": "7d7cf3f6fd38a44ccf102f484cd7be66f867aa289e7a2afbb9e9311a90a615f9e3e3b55dae5b22e9dae661e4d9e8044e01844256598720788956e3e0a6a4aa8f" + }, + { + "msg": "b5a94b9265bcfa5e3409f4ce3cfecf9d2b0fbb8dc69fe06b340b1f62d22c72100d7091fab6", + "secret": "3c25ab3bb51d1264ff0f0ce097814b9730752f2f50d0db18bc63ba5ce34957ba", + "signature": "b839c1c1c60c3b6aeffbc4ed7d85f92296fcc1314a835647936eaab78a40f02653eb91ffed505249cf9a224fd8681b63f7d81257367a12544ad01368a2b4d90e" + }, + { + "msg": "d5d0ec861831a21bcce0fd21c6047046a41fe05cbc1f814dbd4a9619d7b0c3d234adccc49a01", + "secret": "b09c3eff741bc30e6926e9532c5e3a11e3f2cf74895c8ddad33dde741eb1a2c8", + "signature": "359f653df9d8d1b92f0c4d30c66e157273a8e8a40064085b5c3fa749cff88f4d9a68b5f902b4f668f924adf7579fa38a821220803a4095acffccfc2f55ad6a09" + }, + { + "msg": "0d61fbc9ef233786c4aaacb6c362fe3a87e8385c5c76e76e96ac5201dec0ae622e4fbaaf225744", + "secret": "e3a1b545b4012b76e895a55f74258a479fcf04e88df4d058cbe8b61dab1d1e54", + "signature": "7016a0eba6d8c138b3de51abefa75cde2a9761235137ddaa0faabd97f7e78b189fe2bd9b820b78152fe5b952359a417c926810b287b178f36648a1bbd5e81888" + }, + { + "msg": "c5477c6bfdba3feb7168853acf8823a1b5098a7f9dfcd403d1112612ddb9e8aaf7a99d390fdb16fd", + "secret": "746167c6147c97938efe87158d2e3824a25de8ab960a105c983c25a4012ded93", + "signature": "0fbf8c0f12f97e5a453194e2d3e280d188f5fffb191eb7da87249ae3103384b87873e50a5a6e4d47cfbeda3bdcf9a761a0bddb8eb543efe4bd31b28f5b89918f" + }, + { + "msg": "43d92eca24690e1c8d85b2b46dda5dd1e615bead8cbae4180b50304fd9a467ecf1c0e87cc7e42eca61", + "secret": "ec749814f3cde0e2e2f99889736861dfb4763624c210bf494212ff57b6daaa2c", + "signature": "9c1efe672b9dbe555b77cdeb688b232fa881e9ec8868b3e5a36eebe5871078158278375eb5e316cf50e08c0c25b16437b1a3aa32641432c388a7623d7b263508" + }, + { + "msg": "9a878845b6f681401eb92eba9de5063a39d4c2cb0c39e3c686880ce6cb2fd40eb73d53a31c4ceb6bb74b", + "secret": "784613c949005a766dac2513823f0f9ab72b15d2cb1c4071953788286765e9e2", + "signature": "a9ce22526480439100ee1761f35cb36b40f13cf5f87e38526a8d6564546b71451558225e768c6c002e72ac303e8ef2c37756e6c637a427913531f9c44fdb3a87" + }, + { + "msg": "845c5026a61159fa82728fb46ca029dfd53dbc972162f094d0f4eca25bc2c12d43ca86243bbb62d4714a55", + "secret": "6c9ccd55504dea411cd3a6c6302e2925c5cee9e42ff22a04a9f68860f9172044", + "signature": "761cd67ec494be4d488c9fc09417a5f731026c5f58333582bef35b03d4730b07898bf895ce63372a23cfffc6c0f42bc088e54fec7681ccdc9b6a79c30202ab02" + }, + { + "msg": "a28aa3e99881e8869dce7c70de94d477ae8b1e5a54f8eb9a31a6378f84aa7625cb54d9b6b30c2aaea242f6af", + "secret": "ef7c67749186c7cf0c80b2d4df8389c5067367c29b08f3b79b48e219861a3fce", + "signature": "da2b5a56b194fdfbc3f2adb215571c9d14c44118c91b237d44065aa32541facdd0ffc0eeedbc36bbf6d5182314895bd19adcbd0435c29287b22e0c68adf1e60a" + }, + { + "msg": "79834ca280aaf20ea16d87836cc82f3a98ac213598921f6a9fd4362c2ed81d853a6fc473387acebc143d2ee316", + "secret": "ebaf8d312b6121fdaaf82a58b25a11540235b85d8fcd1fc59af9bc01f656027c", + "signature": "280aa451ea8857feac53db24bad528b2ab05349e34b59cd8b56aaa2a2356f952fb0d175d51b58df96edfec5b6a9539f659fe9e104193bc727611d96c8b7fc48d" + }, + { + "msg": "b8cd45bda687ad49f9d102506b11adada0d6a989487ed9ac2fe12bf506c2a14b67e272cb75eb6850e2998eeb10c9", + "secret": "00f6f4a964eaf57a40639e250ebde475bb53329f2a90698660aca3cc0d88f963", + "signature": "b2c7334850ce5579ee6e56c86438f1f18e3a417e131378ba509c73f03c32e4f232a1fddf0fa229caba418e7a4993261f7e7823ac0c58bcbdb8c4082df22d2980" + }, + { + "msg": "d926a3298b3080d3f3b7e328b3e637937ba28c978597269c93e7501c672049b41ee31557c32a5ef80b41570db93b26", + "secret": "c6460d47b201411428355e89636b0fac96ea4d89830b70c79f65beef04c1b2f5", + "signature": "b4d72baa6d1f55bae90dd7dd376b16c664a7f5b08044e076ee5c63f34aef691b7251bd86bdbba6ec8083b2b0bae4569ebdcc30f6e00428cca8a7b5c4a1a9cb04" + }, + { + "msg": "730d3b4513ee24e886f7177b62a7c421deed0b39d17146985802e1906547b3f9a4a1020222d024551363f90b6e1119eb", + "secret": "b9dbd21bce5d28322ebe9e47566451f6d0c0eb40ee42fc148eb17b040beb05ae", + "signature": "6a3147bdf83f82745aeea9a793d0e6e31351f1129d5f4b7cb2536850770c4afb80d3a37d43ed5f60608965d9947a3900f1f2b824cc2990378dac773a8d8fbe06" + }, + { + "msg": "75ffa0b27cd5b739fdd977bbd4d996eeac3398e57461ef844d89a8d07e4e300e40386a873516ae0a7d70031a22463a325b", + "secret": "9f267d7bf9d94f20dc3c23e4dff5384293ad3f8da71be821c99f284403237c0a", + "signature": "88abf01016e2075ba91624ab2d88bd3ff537cea3d6b212b37ed51abdb70c84670a28a886b3c6a628c21b3ab4a104cf96638594f8ebb22bf2768f83eef47edb8e" + }, + { + "msg": "", + "secret": "93d472d99b2aceba55ca5fa26237c67ce5d7bcf1935755ec8e48d164f7d85dfc", + "signature": "d18b46368ae5dd75e4c4e0e2383dda980ce5510f7f7f4e66b0ddf33f00091992d39fc4a0b91f80357123b5b789bc284a2fce0b09b9c2c54f321c4c0267626a00" + }, + { + "msg": "78", + "secret": "88a038f82111b66e84f622aa39498e0287d86bdc42e7ae1e5ec2ef9aabe6d536", + "signature": "404be946f2512839a739e42c415817d243379788bdfebf00d0a6bff8b21ebb80e95ea4f049f379912af10e4bcc83e581c3d36fb269423e18a6d4582d6eb6bf0d" + }, + { + "msg": "fe49", + "secret": "f9f034faed81cd372ea36ed019d5361df6c3e1c2cbf120ef7320e67eb7959f1a", + "signature": "693e12e87ea244059fd5f92ff6627c007f50f42465a212bff4b7f8c73e7c44dc673e3c553e175d7289b1239694584e2af0b354d3a2c5768d952a91f32e847b02" + }, + { + "msg": "1a3c63", + "secret": "ab3e872591009794a5e4fc8ba2813aec2fdb3b8e737285022ca92fb4fc941339", + "signature": "8f2ff78512f827dd4a0412f9a2aeb5f59075696cada4ca2728e4eef71d47afa539135ffd04a35b4a979b43a02ff094976ddf71e03a064c40a02d7b8d70e84508" + }, + { + "msg": "8d1e3da5", + "secret": "9f90e4bcb6d8ed5eb6cb8a6d21244d7d175dfce2a3757be6d86ac627084b6ab3", + "signature": "2e6ea3eec80c8df51ef85cb82677496ea2a9036b7e6f65b74148f3e5fa3f2bd7ef0d5b8e5a8c092a7ee2a103bdfae1d2e90866d6c5eba4fb641cf90d6cd5e109" + }, + { + "msg": "42ce78ede0", + "secret": "6698f52d0d1f74f2c0d022defefdf46697471c1ccbfd9b7a5c9506d51e1c6323", + "signature": "68d9139586d5ceaa87c1f1b4aebc6c1b9a4b763b3348fb6a4205926d0bbb8f434348c7c502a3d0bc499a55b66cc73072400b86246ee6fec13c7148231bef4b0e" + }, + { + "msg": "df46e7aca8ea", + "secret": "803e0b485b471e147bc554196d9d8ad448f049bc92beb804611be25aa7e0ac9e", + "signature": "a314474d60781aedc84680367219ac1e4a2bed05565f25de4ba59029efb70ed50e474d44ec5edcd9feb177744a976a5f64a3830b98e91c9775815c2d5be41e0d" + }, + { + "msg": "2869aedc718582", + "secret": "82d010e3d67d5ea6741e1dd211b76b4d6682ada52195119d3d4dc65740244001", + "signature": "fcc41e61a5dae75de9d5e9840b6e244f61af012abba11707b66410bd0826d5f5faa1467f3ecdb6b589a9319d73ab4f2a15d69b740b6e76aa3260b8cdd1eb170c" + }, + { + "msg": "adb7514055ade786", + "secret": "13064c051e0f4c9dd939da9ef839a707e8b0d8605013c7fea9298140b200536a", + "signature": "370dab4d75514d32a08c5c274ca06d32d0d856fb49aacb7382f3ea51bf72780a669463a8c79683434ba1d13c236ebdac0454058c77b2d577383c6501456b3283" + }, + { + "msg": "6c9ca3edf6eecc2627", + "secret": "dad999c83196d22a719028d5608c9c1cf2800050ccd48303742692b18bfa70df", + "signature": "5dd8aa0b6c77d7f58cdbc58fea6a8b4235d813b280ed3337adf5c2e6ec9b094e98d6c025843a2bafa973bffdd36cd63f6b6282cafc82f62e1b0bcc0c49032801" + }, + { + "msg": "b6f6543206f2e093bae6", + "secret": "d53fee096c1324b79b5e105f3d97fcf596bc4cd7ee6187f168b34dddeae266b6", + "signature": "e425e51799cd5b51476589651c95efca931f6e1fddd6ecf72474e3e291a9643ea959dcc3d4ce2e4c1a14f031704c72c23febe2837a1f121c21b9d6f16c9b7680" + }, + { + "msg": "4b95bfc2838c42d54a9f85", + "secret": "450daa8981088052c00470191ee2ee71541aaa9ebef00ac6ed0f67b62ccec0ea", + "signature": "f7a21a44a10addbdfda17f7fc5d7438a1ed7518211f72155118071a8e6992be86b5778860e7d1c1932bacb4b09deebcd6b7ff40800002a78f73a2578a72d1c07" + }, + { + "msg": "f5a988f5f4d062d96d841cee", + "secret": "e7e6a7e9b432550734bb92119ae4d1c3a2bc91e972521918ba16a28be7b8bc2d", + "signature": "6bf703633ce3bf50f085c061d2d5e5c70649da96ec8db036b63c83d1f2fcf5a5ac0a9773339d98d38626a7c8aed1f4657a1def45fb5c23224977486bb9eac10a" + }, + { + "msg": "f8fddb5ae4ace4fa510f882742", + "secret": "0213da3a2c17517becea5228ab181b43b5d6667b1ac97c41d6269326000e37e5", + "signature": "555da045b36f052ace2e12b5b77c4579f7f28b53b8cea0bab6b0975b4ebf7ba28d0c48c43c6f840c3e704623c671d77eefb8cd6e40d39745bd6b88d1939a8784" + }, + { + "msg": "3da6296ba0db5ff7f0800008395f", + "secret": "587931cc89b2b996164f683c0b7f7a21cc5a32632f1b02c88b5ca29246a97b50", + "signature": "e08848223ee675332cf8c072f831b9cf53df2b36f158ede9cc8a4dd1a3649c34baad73469e088077412fa6083fc72b874c8cedad3ab88117e1d4d5dd73d65682" + }, + { + "msg": "11b45e5f224fc1c7cb77ff9a2f2487", + "secret": "b7fe750dd550cd29b59a9b08a3004368f1988c837d19ac3bfeb98882dd535d20", + "signature": "bdaa348163e3acb8b4ced44cc3ac93e9ce928efe8bf7bb76d624d084596e5777c5e2c02e481ba4e71f0141da5b200e97ef08674b183df46c6796000cf43cc689" + }, + { + "msg": "0e6cb07324cb4a5808c172dc147b805c", + "secret": "a924fdb5ed177c9ff0f36b88824a41ef080895c84b1d077db00f1a27acd2b55d", + "signature": "5e2f8b528bb1827fe6367ac8969e56a7764bcb806db90a7fe2afc793d779ff6fd35ca6b49da48cee12e4e6b82bd36ab9d7d960450da7f4f35fa62fbfbc046189" + }, + { + "msg": "6a88459dfbe4089b9a4437f129df94ad5e", + "secret": "6ca34d1b35766d2b98eebcbf1402fd58e2f0c34921ce187ef9140a1146a10be6", + "signature": "3d11ba3ff09322083d966e2fc5e2e4e129545937323d429b50c45945c3065aaa1a1054bb770a5e82afe05715a08363fe4c47c3a5ca9541a5246c2280593d3208" + }, + { + "msg": "b513688bd4fb3e9279ed44d42b210643a655", + "secret": "03acf149c034f4ac9dc79ca8b171cc894abf2fb0bf0cbc1f13278d3c3d89c68c", + "signature": "eec2ccc354befbd918bbad60cb59678b38ad65fb2a56f352e32e7202bec3534c91e3702a7e5449e17a00fa0c1f36c2a816528c323da5e7ef511f5a45b965698f" + }, + { + "msg": "573d8769d60e4edba5947ac8df58abe190a2df", + "secret": "5565855a66ceae7ed93603ed5b6857cff5bff04caa3e73d1ece0fb9ea9627572", + "signature": "fbf8fcd09775c798cd628264369786202de8a1f6c82bfdda9c09ba867889f00ba68daeeab4699c2ec9af2cfb2e2b0970c7130f675f58e97bf4db4e2cc7716881" + }, + { + "msg": "bd90233d25d8adee6a2e5a75e3028adc3782d770", + "secret": "86a1022129b57c42b2653b5616a3a78afe05bc2ae7bd0cf60362d188bf911575", + "signature": "6f8cc6f6f6250572c26a15992e4e72bf7b906ce631077f8c6e54974330cf52b09444cd5f6382e536f93a8751ddae4ddf9bc639cc47ebad657b8e7ef558ba1700" + }, + { + "msg": "cfbede7797f686e21c1f9ee4b453639ccb3db02c60", + "secret": "45b15be6d6bc92170716ccad732e509ceef9ed52ca72ac43b38c0c0d77657fed", + "signature": "53772d89f7b17fbdbd46cb3d610a09955e30605478fe967fdde3233a84fc0df39af9823045e539f117a06713fda742865e606324a0f219b288678bb33a2b1e0e" + }, + { + "msg": "d659051d35e1fa886da17188dacfe134c8fd4986bf12", + "secret": "e3a49f9b9b7587a36987fd8ff25d7ee184bcf68909ea2c9e8467cfb0d1d83433", + "signature": "d7b80c75738abe2c1eb31d8450ac80841031c2bed70be6e7bc276b3e707f71cc2bcb84e46f1094fba4a38310cafe2acd02e5e4c576ecf7ed518f2022eb9b8708" + }, + { + "msg": "77fdb902fad9736b2f1c0a9470e6270c1b7b80c47fa7db", + "secret": "cbf6c8494e02b93d33802901756558db7a33fd5575c9e95ed64ca027387ac161", + "signature": "266f051f9c48dbf07d5115c510ac2a13acd3222c9a9d148be28a3d1e4f82bae01f73ea68765e7db1421407c8794f99da672b16bb339a65b8c53dc444d271dc06" + }, + { + "msg": "798719340e6c9959be35ceb0f38ee8a81c971f214a41c4e3", + "secret": "2f033bb8b72c5e016e01c74623d6500f2d2e85ba7f4c07e684725e319fa82075", + "signature": "dc6458aefa87a454c94f250c360af1056cbccbd7ee29aa1c826cf7d9503eb1e9db4e886210c8c29b9962f791b9ca5f2f7559463c61a2e177a19d9d801c5c690c" + }, + { + "msg": "ba0181e83fb8573506e337a21bbed3d94858e8f8bcdcee6178", + "secret": "22ae097e5e8ef789f35191a8c9610f12f3ebfb140f988566e965d1bda1ecb5e8", + "signature": "ba0c0fea15e5583bf180f81410c82eaa03bb043b5543f6d57d66f3970bb5c7a2cf12db353bd696327b35121c788d9c0ba4dcab0278570ac37e15e88aa2d94204" + }, + { + "msg": "3ae814b6d430008d9d11aff370c8aba4e2965fc27a7b86db62fc", + "secret": "f454b23530a1fd27ee768f39b1c9c2a3126dfc15ad21395177e71905863efc85", + "signature": "bc0dbe63e5cb5425ae18a7332ee43193bc93db9c26868fa945e326c3598dcfc16556679237e653e39e335c1cf341b23466fd5637da230332e57f0162766a040b" + }, + { + "msg": "e7e84bd583f45c9dff328197d11b9d2dec5069e9fa1226815b72e5", + "secret": "28598dca5598e9c56eb9ca7a26e6c4034fe272057410785373d77e4bfe0b676c", + "signature": "1c19b4d6ae6d94b6fb3ec58c97bd15cea3d54c21958699107d5e8b208f5ecb16f9d9f0b692ddef7d31c2ce396cffe4b48845cd3263b77ecd42b9a162c5fc078e" + }, + { + "msg": "896d6f52e871962d5d27f579c5dd42df247fba2806a1de895098a9bb", + "secret": "1a803890243264e8da5613bf7ed9c17f7631a312ac2593585269c0c4e598977b", + "signature": "de9cc5224415f045d52376a6d04f43fbaf4dbe1fa60919ce7f950f98bc929ae93812db7a80673f29d4ce9220d81b55fb8cce5e57ed4b059b20ca28bd12a0e18d" + }, + { + "msg": "add25a3ec36f9d8db4d7f3ebd1e23622f2c3db922863b779dfa92c5c32", + "secret": "eff034aabd45f1e665aead8b4f4942dd7297735c328e684e67e4c79d3dc717b8", + "signature": "b769f8a8edc7bcfb2ef4677f940e00e73489df5094f7066569eeb9101288cfb315917dbdb2bd03a20380f92db18110e01e3c31129e1fc159444f3cf6d34b9b05" + }, + { + "msg": "8d1a6740e3c104dccf6d7424fa62ce481229786f3f3f795a43606f915236", + "secret": "13e2cc5a0fb17bc93272ae7c24d8e3a4ea79f9d2a28b4481bd868558133d6641", + "signature": "805e2cbf2bc32bc80e8f1c3cd6bb618fbe4539ed7094f7eb070161d0bff2e2eeb81393ec8e1e4581d1d214b5f5043d99745c66feb09f9b76c46512ef8908450f" + }, + { + "msg": "820521fa473f8ee6ce557acfb13cfcfeb9e243c08ddafb69ed1ffb58248353", + "secret": "81888cb69c310d23160ba5a974cef269913d6b98d0854384b50f9e8e35d6155f", + "signature": "251f5bded93817f412e0faecc57fc2074a48e44c96840e0f978440c2fb7795fafb3ce6520a74052bf5f825ddaa8e2d8b18125ac4c5b028e9572569758947810a" + }, + { + "msg": "1311f85113b83a220ee436383532405b991711f71796cffd34d247a2648862cd", + "secret": "2a1c74345b43fea7a72d115820d30afeb831ac742d4c02dfe1f12b8a73fd7579", + "signature": "ca4c6055b781676016283b4daa13576a1a13f784cafb1d4f1f0c97228ca2906e3ecdd7f0ca3a80ce71563c50474f9e997059006b934dc7c231018ec5f7b7c781" + }, + { + "msg": "f30946d8f8bdf81d72fe263e1b86d9e3b7d7be93d6e7f78d596ea3a0dd18609e34", + "secret": "cc61aa0f0803b89f18c63a43a7d70310916109a66fcd30702ff4fc4acb845571", + "signature": "02a5adc8e122c6896ae3969dc6d3f6387805ab8502989f7b68dd69f477767fdecc2c6ed493a52814d44d967fb78fbe58744e3217680fb4cb1f6e5dddb3c0900b" + }, + { + "msg": "38d5272ddeaf42b3b9862cc2651ee009cf1536996d96a3156455f6a9de3a0c841ea2", + "secret": "2d8eccd1c5d931f64bfe14dfd48afdb869f475a7c69cb43e420e5c7560836c12", + "signature": "e83dec5e15c8f6eccf7c3a906de5535d5869d4710882537be12936194028607c518181a846935574d2ed89bf536e76878e1bd7803dced59e7f590495d7f91703" + }, + { + "msg": "05829e42135396ec56f370fb561ea3bc87385751421a68577a66c6081a0c4f93a8f331", + "secret": "c9beaa57cd3576949610f82873515943c9e7861ea211bb41c9b6b9055d62c79e", + "signature": "618fb9c6622d337df86173cbc3dbead7deee58f7f78768936e5a74ecd43b627e2ac3c94dc02a0a652016364743bbdf95c078d83895e89e6f13ff0f6c44095289" + }, + { + "msg": "9a1b96b3b5eb5072f4ff5e7a424a50b31aa15103e4d068d90cf390c84236771b1ca5a989", + "secret": "69a74e3e7fc01807a1aead00c83ca88b2a8dc549411f015c6f1686dcda55d4cb", + "signature": "b4e6c80fe103ff33deacdfe9b04cb49c203b1b458e4aa344d9ab48b23e88601826a4010ebf52d679c662f95e9076ef87e24b8aff4c80f86dfc4559a1f292cf86" + }, + { + "msg": "33531cd40fc2eb6f5d9f5948e524e503cccdc013017b306d9bae525e2a10b1eb39e4c12a31", + "secret": "8c929ddcd9037fdbf9da1f0ec105b4a293116a899a78b25d1cde601465cbb9d8", + "signature": "a3701b65d56ae44a3c0a05f33d40518c3524439740da196671cadc5eccd15a85dc5f51373cf62ba7856b16def0dc0d550afc65a4803ed79da492a848f8d2a58e" + }, + { + "msg": "6e8f2e937a8f1b7b59cbfb962385c632e0e8870e5751a8842e55deb107a59cd89a33e9e35c18", + "secret": "3f60a93f820def17699629de09cb056f2c410c471f6e224c973c158611c7d50c", + "signature": "8dbe4130d2f6202e14e0289cadfef9f0ea5ed69da8b8a111c25e8b3824f00958fefdbdc5097fc9021e62cb1f958023c58bf5945502b5928b7f2627cd3b16a186" + }, + { + "msg": "2f38d1c507897026aaa08a640517f1259ea462388695a6a3875c1c4f0c1c11b7331ae7df235cc9", + "secret": "e0c0abf8d46726b67dff6e39dde8d2431227f1720a16ef9a86c73962f00122b8", + "signature": "d60ed0e03970d3d679daf7bed525518888243b611e25cfd15a8926250eb80cb8160f706da67aff6c5ea86cae446cfb7c5c9154cc4ea53176dc043419c940e98c" + }, + { + "msg": "113149bb8044b34e7864d73fa66c9693f95915514f4caaedf65c5c9578afba36e60937575fe2fc70", + "secret": "d8cd3640f6e81424038b3fde083fb68aba0c29671c33a7861233a3c2121084d8", + "signature": "c7fe83d54d915acc2db7f2824c9ff68bf8a708c926d5d6f6840639af7f658ef14ad21f7fc8a0f83571308e377043583bce6f57ec37bda65886ad21df7b908088" + }, + { + "msg": "752d42cc7a5e3a8f5e62aef8082962c97c6c092c982777ad2ae3c5cd3f4bfa873a263f81a7686cadd7", + "secret": "7581fde1314d40147ef808aebfa59b05c8534afdc3483a41328c2e5c90a0f0ab", + "signature": "833cfacf30a15b63a63b5ff5c4e80c68a106b2aab1389d02b3865e409ae151f29977d7bb02b0c05c7e6b16d0e795d015972d752b41f5482747bde1655f87d181" + }, + { + "msg": "4369e52ec32e945cceb12456e9bbdb4e862896dd458d7e600955a30748362e035cdab0ca8c3225d5955c", + "secret": "65ae26607c65e865d3486fdfa14251b0382d1296ac1d0101b9fc7171c93f7c88", + "signature": "e7171c9b6499ad9ae0257462948dcd71ac689b498cccad83be5a8e5f557df2a21a4008370accedfd4331c2746116bdba6e7f5fce889970b211f378e468a8f987" + }, + { + "msg": "1868b4dd2ecff80a8dd77ac7a6993298eda5041cb697cb010e787b865f83c55cf08ebbcc9b28da57778ebc", + "secret": "0884e3bc726ca620a9134f02cbeeb192aa46c2e87d1711be7a9f12c369be0bb3", + "signature": "f01309b50fc5d14413781ca140f0e6809db6aaced860aae18030bdb6d59fbf3a283ccaa498ceab3b8532543a670763a436da8740e2d4bbe2375f55a3a07a4783" + }, + { + "msg": "05364385e2571c470384048cff6dc3221c2e32070afda8ef0d8671dd7639ed7ffd13faae0b78520e1c486a41", + "secret": "28b5dcb3778a97e1932641c0db2840f320de3331b336843cc9b0a7d0847158ad", + "signature": "72b76239a28fcabeb1478defa972deebea2c95df0e6f6e5091b5bf768d7cf51742993d2ee188c50dc5a3e2dc3eeba2034fdd84c20b4d514d5444cdbc4b7ef08e" + }, + { + "msg": "304fa9879bd94a4c92f4e6e03f8406b2876c0dc73b8f48f9cad5c3d66689d5771fe02dfd1172d14e1067085452", + "secret": "015d006506e5f57e91aee8f9ee5f499c37857c5a240e29852ac55c51d02a4979", + "signature": "0725920864233ffe0abb072fc8a34f5a869137b0789e53d0832f773af57b3774b1b1e39a5d61fdf7fe74c64d300b557ca8b034ae083e7a64ef6cdd30b65f4388" + }, + { + "msg": "7ebbe4e55f8c0410709e3285a92ae21d272ca2189e9b1030b5e7f044dd547f40b0c3424c4c0567708d22d4ad471c", + "secret": "987d88133846c3ba8b5839f81c2364917cba01a156c27a75efbbb18d2b038410", + "signature": "675b0a86f65073c85e733c78a630207e9a5819b286e156a721e59db878ea270579359b53f3332ee0e7c86538c9f1313dfb597ca2cff89e0a6941e1056d58a408" + }, + { + "msg": "1f77aec89a588dbbbf432622f1c4144685b41350ad7f1c116133264ccd69d5108cb8d51a554cb97bb269c4ee7ec3d3", + "secret": "fd75b28318a1f3038964cedd65d8abad03422aea7c8035cd818ba2ced4804a55", + "signature": "4afa0ab8963108a504e2da46f3f93d3ec7e818762a73b889669b4a98c24b3a8769200e59f75b1ca5b080b5d62d5b78dbb1955a9e5292101f1ba41919ea134487" + }, + { + "msg": "fece86473bfa325ce4c51ee2abcad77d385adf304120a34ee9fa6ca862854ea1920668f90bdcc92b387722df228935de", + "secret": "ffa2490743f42aa3e78f54b1518054182a1ed7d40e0eb165f0cc6c9031fa5e8e", + "signature": "29c94c4541eee66abcc4f857f22f2125ae4a634abf7da2a4adabc14b6b4a1872101e995bc6213cfa975e53b25ba2ffa16a65ef082abb58bce3d06c502e9b3705" + }, + { + "msg": "ce83e2e14ae7be44d3f601ed95118e1781fc991cd62541af4d6e587eb5ba04a4f8e32b22b3fef512fc4edf94793a641e80", + "secret": "f41a890c27142928ec1f3ba0ef6010a2dbff4affefa3e3379ad477daefd6e075", + "signature": "10b1a604f7d954fa2516969857f22b32f17c713ec596f23d89beb2e1fa32af98cc7f7757ee443fb0477986cfaf3353492d410cc4879f1f21daba08a61747c909" + } +] diff --git a/rust/tw_keypair/tests/ed25519_waves_tests.rs b/rust/tw_keypair/tests/ed25519_waves_tests.rs new file mode 100644 index 00000000000..18a1df852e0 --- /dev/null +++ b/rust/tw_keypair/tests/ed25519_waves_tests.rs @@ -0,0 +1,62 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde::Deserialize; +use tw_encoding::hex; +use tw_hash::{H256, H512}; +use tw_keypair::ed25519::waves::KeyPair; +use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait, VerifyingKeyTrait}; +use tw_misc::traits::ToBytesZeroizing; + +/// The tests were generated in C++ using the `trezor-crypto` library. +const ED25519_WAVES_SIGN: &str = include_str!("ed25519_waves_sign.json"); +const ED25519_WAVES_PRIV_TO_PUB: &str = include_str!("ed25519_waves_priv_to_pub.json"); + +#[derive(Deserialize)] +struct Ed255191WavesSignTest { + secret: H256, + msg: String, + signature: H512, +} + +#[derive(Deserialize)] +struct Ed255191WavesPrivToPubTest { + secret: H256, + public: H256, +} + +#[test] +fn test_ed25519_waves_sign() { + let tests: Vec = serde_json::from_str(ED25519_WAVES_SIGN).unwrap(); + for test in tests.into_iter() { + let msg = hex::decode(&test.msg).unwrap(); + + let keypair = KeyPair::try_from(test.secret.as_slice()).unwrap(); + let actual = keypair.sign(msg.clone()).unwrap(); + assert_eq!(actual.to_bytes(), test.signature); + + assert!(keypair.verify(actual, msg)); + } +} + +#[test] +fn test_ed25519_waves_priv_to_pub() { + let tests: Vec = + serde_json::from_str(ED25519_WAVES_PRIV_TO_PUB).unwrap(); + + for test in tests.into_iter() { + let keypair = KeyPair::try_from(test.secret.as_slice()).unwrap(); + + let private = keypair.private(); + assert_eq!( + private.to_zeroizing_vec().as_slice(), + test.secret.as_slice() + ); + + let public = keypair.public(); + assert_eq!(public.to_bytes(), test.public); + } +} diff --git a/rust/tw_keypair/tests/nist256p1_priv_to_pub_compressed.json b/rust/tw_keypair/tests/nist256p1_priv_to_pub_compressed.json new file mode 100644 index 00000000000..ce6a20f605e --- /dev/null +++ b/rust/tw_keypair/tests/nist256p1_priv_to_pub_compressed.json @@ -0,0 +1,2002 @@ +[ + { + "public": "02dfe31ef16b7c65b47d44a8c0c422d9ed9ffc599fbd529f85e931a90117d4c84f", + "secret": "ed9ef57bb1a39863e79561d35e479297275eae7d400d9a296abe8699a8ba237e" + }, + { + "public": "03703d9bbad179672176410d68abb2705632d114d2377b6821858a6a802913d129", + "secret": "bca2a4e7db34577e1193d6b6312244a246832228598c91fd5123cba52c182979" + }, + { + "public": "0253b7183b79ee74cb498c0153d3cd942bd17d4993c388ad7eba57de94d4b896ed", + "secret": "03750b5633bc1886819aa9674dab0cb86fc62795af2661015e1fbe4a60b3995b" + }, + { + "public": "03b08e7f118a904b5818aa0b29abe6b447fa04733904d4d234a0734c867489afb4", + "secret": "00919591227121d8a75f49ca5a0884b0e98967314d8e56e35bc4c5ff1a20b543" + }, + { + "public": "030d761d0225b983e4a0de15253bacc9f7c02ba8b549fa96aede56d4e32c2b8987", + "secret": "d9051640e11607b11e2f4d059afec950153db7acea11f1200e698b00de49e1c9" + }, + { + "public": "039fb39dbd7de59971fa111273d5dd4911aedd3e3f122745144d1c39b50911207f", + "secret": "c58ff00d1749c72c6743f87be3714a1703a1217c8f702c2e460b8f0bda9552f4" + }, + { + "public": "03aa64cc234b97e17bb873bf9644d1af0ce4a36922a2cff1feb4195300f9d79be2", + "secret": "7ed09f82a1fe14dabadf0f6a623cbcf461c9f51cc689e634dd2c950c484f9bb7" + }, + { + "public": "02baca30ff26df6dda26cf9b6fef037824f27ee4f1c8bf2226bb50b18b981d3f0a", + "secret": "9043781fd8a9c17ea7b3c7ae8546c9efe9f089f1c7af0f0318c95f6bcf4cb575" + }, + { + "public": "02f155c59d49f0ae4db7efa61ee473c8a024377f7c44e6f78b71b86a277c487ab6", + "secret": "d39c2cbde0a4ae92d066854372969236963e08b27eec3ccb4f08700c04949506" + }, + { + "public": "03b68926809d4326ac459161f3d84ab5480c8dd3fba007d90c45629e5794eb4e27", + "secret": "a40185b5e0ec6da301cfbad30c418635269017e8d2c839d9a415d15bafd48ae2" + }, + { + "public": "02f4939235dfc9f8b162c310b74ac6367419441dfe3e567a9a9aa4c52417d259ab", + "secret": "c20e4020f41393638c707e6aa09c059fc40b74bdec9553dfd011f9bda4b3d000" + }, + { + "public": "0298595e656df719bec30828bf9f4a0ad516cf85ab6d8bfc8ae13ee17f5eed4d29", + "secret": "7436540a692de6ef33de2a6d7801176e4cad20c4da8149a28b32f4c375be60e7" + }, + { + "public": "036e3a2be65b0a5d05939fa79d582f456d0861c4a2f31a28780debc827f23ac991", + "secret": "e23f18768a48dac770da04915fa328245b798fa93a26c2d8f219974a6c9af83c" + }, + { + "public": "02a987cf9f5ef51d3cd68b43f0828938523f04fb27cd7131095db2cf95f5afceea", + "secret": "681c83f84f3da5f2e080ad0f5e551ee208a45d4e21189be6545e3d27931b52cc" + }, + { + "public": "03f87fd94afcd1c25db431758b0c5e5e07767ca5137db16e608723f2614bc1ba3d", + "secret": "3c275be6f363a2f38aceaa07ebe69b8fc16374981200e2c6f5fcd674fb69e584" + }, + { + "public": "036af4fd44051fc437d7c294d087a54430661c9ef22788fbcfa3d0cce9f635a4a5", + "secret": "3ad3b8e25774f0603982a6000b6f635e3a0927abd213c0999377e3727a469552" + }, + { + "public": "02ee2c687ec9786e41ee84f3439ca867ffecfc3a8449e114ce760f5b8b18d9b506", + "secret": "60c7d4d27fc471df89094a8b46e7681a902414fa54c808a96b58de313168d4d6" + }, + { + "public": "02daef6ab84ece15eee8fa39c7f089634411f1f5b79314271277a5866e237fa4ab", + "secret": "644ff7e768fd03063cb695c524449136aae637664dab255b094fcddf7a9a9d2c" + }, + { + "public": "022c423f0bfc4302d4cf964d22247b98bae63b014e9d505a1d3c6ecc37aec1371d", + "secret": "e857fc8dad9db8022e22415be4e811d35dcfdcf733ef448f0e076f3b9331a4a8" + }, + { + "public": "03779d2844b016f5e28e821a081671573888f2bf070d4eb3c4b254ad586fe5605a", + "secret": "95345497f847940a04d4a8a08a6d1bbd42cb87099b03b436e2fde1f0aa9b9d4a" + }, + { + "public": "0313b5b6c9e03cbe397c5d5d4cf3b4d7f03fcf06fae50fd801b33724575bd19f26", + "secret": "43e7c2a816884051a5fd9d39afa859d4579be1351303dbc2b82c8d0542b635b3" + }, + { + "public": "02547dd5270658bc9cccd19ee7c92f82da7e8b778e7c96a254983c8fbdce2f2335", + "secret": "c9c714d0a63b624476de4d6c6bb92e003f2abcbf2098827528e1bea27fb6113e" + }, + { + "public": "030597f69c4cdcaeb1bcd690f3b957a3f2e4ea3c195bfbe125eec823907bedd2d2", + "secret": "b9b4efb24c1a10ede058b48d417d64e9f893f11ac18acda86a1b604989eecc07" + }, + { + "public": "024c2f40d3765e64a77f17b46a8354bb993a669f3b91c5aea6b01775b1391e9a62", + "secret": "99d4f6f04baf38fff772e9dc9d1cd1457693173d9dd8a242585f8fa7f6c106cc" + }, + { + "public": "03842e61840c044411f3324e06df9956e7c677b07fbfbabe803a93bacd21656a8a", + "secret": "b71e4edd221472a4afe53b934059068547d6e3cb96e8071a554eea8e497fdcad" + }, + { + "public": "03e935514ef71e3226172656c4cdf1c2a99e2299b526b58dddfbc4048b6ce7eea1", + "secret": "46f1bb6607be17d89cf1a8774e4c09cee6cf494d97c960c61c5c3ba576f0f959" + }, + { + "public": "037b56bd753c393bcef053779a632fdb8e95245ce4788ffa9479a272ad0d2cb27e", + "secret": "0b56dfb1a4debb0cca33a0702e19caa26d88f2d5203d52daaa495a39938558be" + }, + { + "public": "02f6bbf29c2b1734dc270fb9ec43e34daee7530c56ef3caa291c842fdeeecee872", + "secret": "d27fd3993f6d7cff9ecc2323c38c80b93b29bdb08836dd34d6e4428b66f765b7" + }, + { + "public": "024d245efeb280b0f2ab3bb6f0878cb2226504e20bea37d2b875bf83b1b8a4742a", + "secret": "976d82f71b8675f86833f12ae83160dd37c65d70a4e5b3583a20259a55651a43" + }, + { + "public": "032096dd59a1b8365ab21936fda650f176cfa2281c6cf6d42b9930cbc33aab5e3d", + "secret": "c8a6312e153e33dc37df121a7c3043804cc23b16b1d1e540fc12553da1b25cbe" + }, + { + "public": "0221b5a1c77eb33f09501bcbadfe1b39a05e91dae038fe1c4e765c855774157f0d", + "secret": "35f2a1f97979f44e1fd4115afcdc5aa9e8c54ea23b65a73c663d1cc4e79d329b" + }, + { + "public": "0339d7a0d439d97443f7ac4fce88c78eb199531d1eeb2a3e5ce7553d3493d14c36", + "secret": "ab5a4ec44f2e97cbe76a2b54d50aff74a966d1c1b57fb174a8fe3867057a7240" + }, + { + "public": "02c96a729995a923b71f2032e2d6b09a6e5922f66eba382f0f77a84395c97ae178", + "secret": "3594b104b848ea43a1d95212b628d7f0eaa71f1e3ec18840925070c46b62c38f" + }, + { + "public": "02084a9eb9c0583f2770495ad8105498a1f22630c13f5449d3fc673ad0ba4df7fa", + "secret": "90901f1beb45fb08c3ce512171654538d39a9f4c56ae8fc684396a37885e896d" + }, + { + "public": "03164166be41b4f5fec85343ae44d45ae6c73468438a4dce920729737f19e1b405", + "secret": "b5aa79625a305aa340b9fedf61118bd22ac02ed5aae4c6261cabdb2abe1524a9" + }, + { + "public": "035817652447c4ed2e32ac28a53417bb5955a9cc3321d29621e69d7625c27b3c76", + "secret": "b0b9625228fb399e63e0f73cec09b0878a78d6493a3d50d43419e61beb1dd2c7" + }, + { + "public": "03c31f7f7d2bb8565ec9902b8183b0799c0731ae2e2c73f6ce583cecfee385ce14", + "secret": "ed910281283f7c03e92ec473fd1d98f675356770c536bf83a5dde4fdc8c7808c" + }, + { + "public": "029317038ee7a69ebafd89858b28fdfe84744be1231c42f3f4c41d82332849de8e", + "secret": "50b123abcae69a6fdc679bdddfb75bfa2a6ae45113e122b0a7cd5d7769fcd92a" + }, + { + "public": "03449d4ccc49174c5f041831472beb25241abd743e41e91a01b777d062aaf16f16", + "secret": "07a3e1d1915bc1a88edd115419cf31ecb0f6d2826615914b1c464dd1769e0a0d" + }, + { + "public": "03d2f0bc54a57c7a5e899b44fafc5c5098d351726dfcce089c64489d78e6b64ffe", + "secret": "697429f1456aa8c74332bb4467d7398c56a8ea92a0f409cf9936ef9ff35c36b0" + }, + { + "public": "03769d8d209ec35edb8a327794215c1498274ad1963f0bd9f0eaa46835d2b5df77", + "secret": "a44cb1a2908b50ce1603da32392fff8e4f6f94e24780ad8b6e90c37087b241bf" + }, + { + "public": "030b267cefb25d786122f2e9ca071da568ca1daddd6832f39d592b944820110d41", + "secret": "3a3734213d099f8aa4ba322c49a0db7c604463047a1889551042c5da291a49a3" + }, + { + "public": "021491a530631f7d24b7434b0e71e374c481f930933ec99415b9b5509d64046052", + "secret": "d89064d6ed42b9294b32560b30fa4b47f740462b38e4bc5da291cc417e19d4a8" + }, + { + "public": "02918de11ad3e4f51dfaf7220b24d3bd09cf7105db2deccade0bcbef96af8d7753", + "secret": "58e579bcd491f146ce9c1d6d15531ed43b2ecc908fab8aeebeba3fba00817966" + }, + { + "public": "02f8bf8d4181695429bb35849cea493e07f52a934d57935483ff40fa56f70a3b27", + "secret": "4b45e5aea80f230f7fb298914ffd285431eed8717074e02180aab7665eb4c9c2" + }, + { + "public": "03811a7e3f86aa62a10e4b0ea1225fae4885fe2b25b71339d6394a6356e83db9cd", + "secret": "ce13491d2a572c39f12b33bfcc904cc3d5ae8b2df7aa2addee560817e1e7217e" + }, + { + "public": "02d645773a478c354a13444cea71a16c9b2778e190862d76864bb297a44174d648", + "secret": "6d740b071d56a09618585fc43ebc7d331a7f10fb662a3980f6ce864c6f8c587f" + }, + { + "public": "02391f171c2f7018a9b80d3795330f42cfb66536ff17e116793b36450291851428", + "secret": "a1d409604d77dd84288f21233a523f8e07a31762b9a862d08f2ec1f993d9a281" + }, + { + "public": "02071ec493e87bad56da4d8cc5a009b1f8757dd2474abe6c0128c3089e436aeb15", + "secret": "3b90a2596117d7f805f640a8deb23b739ac04f20885f6a8b0112e0db6c3a13cb" + }, + { + "public": "038318bc7bf139a78ba30444044dc6607f41b1dc69186d88c5ee4cea292e1b7f43", + "secret": "b568066c67e691ff903e2341d032bed6039a1843fea33adedd9abf0a03e10a19" + }, + { + "public": "02ded2c5ba02a7878df6d058a40b221f2fe95aa758554292e8bf86037e34c724b3", + "secret": "41b15dee1526341ebbeaf87d2995d2a70e12736233bf0793734d8d671d791487" + }, + { + "public": "02414bf7b8d803a5cd75cdc6592b5f845e54546d5ed39f5ce3a2cc4655e292d300", + "secret": "cd9b3447ac7b22a821d3f7d3baa1a9b1c2f3505239efcdf2d74097f3151e22f1" + }, + { + "public": "0213522b7f3b5a2abdc0a58d1d2e703c09a9ce30fe627c9e98d17cde61d02237f2", + "secret": "58eb7757cf6e191cf9280efabad76706693a6b7909b4230c52fc8cebefedd51c" + }, + { + "public": "035cb615483aa47cc488274a92c9831872fac44c7d5a663b320da2d150b8ac1336", + "secret": "b1079fa040f54ace26357d587f9c807ba2da561b5524509589c410464cecd654" + }, + { + "public": "0328b4588099936102cc2468e6c76469839d5213df843ef75347458772fee85d50", + "secret": "7dadc0f5be38adedd9236384bede70821d0e976faa161f38c32a8d77d9a1e8b3" + }, + { + "public": "02bb8fbc81cc1dc1fedec5438fea066e9e0dbbf2ca36db0629017a4784590b4acb", + "secret": "f209980b7a9cc6c77ae4ca4dbc04235225cf1c88bd581bcc7964c31d2737d9ec" + }, + { + "public": "032bdf34526fdd3990b9941a0ddbdd19be3e1a72746f9271c80e20a7be9ed7c715", + "secret": "5a96c4548eac8e78dfaef855a31e3b2f19f6447e22e5184514175a3181fdf980" + }, + { + "public": "026014f6d3bcf788707718a4359e5ca82916fef261cb59e0fac41888e2d3716d7d", + "secret": "ac40b296c5d47882b763ea2b8e00619892228f259bf49cbb0dac7de5e19efe48" + }, + { + "public": "0278252c2f7d455598cf495904fc49dc5a45334b4437a818903848407aa8446012", + "secret": "7dc88455f4dd10d193981e145f3ef455c7dd94fe73be2004b36f3e6957b3cfdb" + }, + { + "public": "021fccdc2771b153f9c56af4240daafb328dab3c26c63184a33da69901465d252c", + "secret": "ffdc9d917aa7f510c9570ed338570ae759c659a821c46ecc84ecf2a2cf997481" + }, + { + "public": "03c8cd6ce5493288c12e92515cda6e7d95acf91247d985ea3be9a285f9ea0b309e", + "secret": "8a3f1339e5d3e5eb4638ed5d067f2708c796912cf2db4d2df9a874443b94b19e" + }, + { + "public": "03fd7902a17097e5666213abab7c468d4b4edded117665d7c485a705e74d3d6de8", + "secret": "db61083e9207353e7650d61b6eff556c6a1d186c479aebec80e78daf90181f99" + }, + { + "public": "02566613da0055ccd808084eda9930bee7b9bd87d915fc3cd042818e4a06f9c9fa", + "secret": "bbd52d8f3b98eb8d652f6f58cf4ea217ab7967c173264cb28083692e22fbe7bd" + }, + { + "public": "02b65210d52ca798d1297c4aa2dbb959507c815dc56d8883d7fee838f89b66ec28", + "secret": "39af8e6f5b809fb761ea372b54ef494f66d5cedc36fca08f7ceb9232c805422a" + }, + { + "public": "023e7cae7127d0181c39d3184451c81470bcca12ff2a1100ff5dd36fe6c4cb726f", + "secret": "bd888e6cf25a10f2ef2639fc42356ffa8fff28cbf486144c1211242613a5b1f4" + }, + { + "public": "03c2ea9be1ffd28f8dade240a710b56d85484b03d3db5660839606a24ee33c5d37", + "secret": "d23c9891eb59095189df84bc22750fbd27a7d9e9ac4f70873cf4979bb929162b" + }, + { + "public": "0226491f0f7bcd46a90b9f8a10989140ba316d3d37bd5fb2a95a48ef35fe94bfe7", + "secret": "b97d3dab39df578003ee19f2c91aa3e507d11fc92d13de898c0304aa812a9077" + }, + { + "public": "02044eb68e9106f19af2a78d64ca3de810894da129b6dddc7c702e978cbf9e5be9", + "secret": "344472fed56e6d7c3207ac397a43c36b090a8f9d12457bd63b12d44dea805922" + }, + { + "public": "03ad394c02ea9b5b0b6a08add9c1ab4fa94080ad7983ad48b0ed043a6aa32401dc", + "secret": "e7a395c3ec3385e83e2ec2a1fc5c311e2b74ef5ccf4ac465f5ab0e780cd3fb7f" + }, + { + "public": "036ebed918790c734e4d33ad6d1788ddbb24d7b646bc4d8234ed1dc090b7c56ea8", + "secret": "1725e3593f625434ad7467d755351f7d619726506ca6d7e935da2727815afb78" + }, + { + "public": "021a5fb8fd4ecbef29ea4df79885b267f4df99c7bb02a9238f1fe35260fde8e2f7", + "secret": "4071e3e6cb4940f8955660359012f2c326dfe139ca9c9b7d28e9ff7879d5c836" + }, + { + "public": "0216087af1622776e43e0977795f45ca41de5a71563b6fda7ddf65e83d0fcd1fe6", + "secret": "0df227db664cf0e38bc0ffdb46768c91db2efb26e850b1751e6e1b5675861add" + }, + { + "public": "03b710f2bdbd5993789fe317be14abd6f6b81e620691a5722bc6e3e4b031b0973e", + "secret": "00a01051569714edda40d8cb2923b8124f4a1635102c96e3d41f305ebcc6b892" + }, + { + "public": "02d526c195009bc260461123ec270c887e48ebe28fdf333938d80cecd85fd2d54e", + "secret": "4b97720dbfdad731ae54882f7c6f00c3ffeb6a74bd9c07ddf0affd91a7b7d3a6" + }, + { + "public": "02d2188294a3c216cd363c54f37535e4c354a0e9e59210f310f25669e1f66240c0", + "secret": "b8d266dcc91788971792dbdb6a5006f7389ccd4d4e6403c17fdc286d1c1d5551" + }, + { + "public": "03ae5d33a880f6559e39fc3962f3d2835ae5c6c873bed7f58cb13704c2c40977d4", + "secret": "80054a2700b28aab41c3d0159dcdbe4028363904f43aa1fbd72ce1d47f291dfa" + }, + { + "public": "02094b891e293ddd5419ea5ec66a1fde26d0a7a8fc72afb185ef7808849684ab6a", + "secret": "670bc810d6d87744fba089280457898c0b495b3f36e51251ec1eb7b1525c900e" + }, + { + "public": "0385090b0a36a4f4563eb7133e177268dcd1c20a70a4ed5e86239723e2e70a2edf", + "secret": "4fcc313e7c4b7292809d0945c68840d85d03d999a78f806724a295064d270321" + }, + { + "public": "0218211dd231d217cbb0bc06276fdcb3626d7fcfed715a4288a2d753fb17627355", + "secret": "a5acfa5c2ba1cf24f6d9e704f2814f7d764c188519b816d471eb4cfa569a4cf7" + }, + { + "public": "03aa36a3f9f0f35e9f752cded2739f8b69ccc108d726e8f28d43947a69d607c954", + "secret": "e95ace74c329bae92fa1f7fded5959609950bd82d1c3fbae29f3c04c647a0c7f" + }, + { + "public": "03d3a36b9088d68dea5f75798483d172edeab7902ebe6c33be51af6594c4ca3c8c", + "secret": "dee9d868830320e8dd17d329cd548bc6ceb752a36e4caaca00af84930aa7437d" + }, + { + "public": "02897bfa08bcc88211d1396a63b21a703e0e600fa2374fbd3fa2f852bb318e67b7", + "secret": "420cc2f574be5ba97d773324994e7c0d5abd0dbebdc977a85689b06106265698" + }, + { + "public": "02e8cc38b83dbe186d83748cea7e46e458bf7925b4357e9e41f2ba561181049e06", + "secret": "ab7248c0e02335d73f2f28602d3c98291d1886a4f04eea71124fcb3e9418ed6c" + }, + { + "public": "02b7d547a02135e0f6cdc905adf82827856104228e3830a83d9871a5d679462c6f", + "secret": "c31bb95375fef9b2ab999e4225538c8e74260c2698d8870372bbabf2fa051058" + }, + { + "public": "02145ab06c412c90df3aec730faba92b5cd39b49bacd879845ae7921071f921418", + "secret": "9fa829edf72f2dacca814c8e5efa7c963be057d613f45e996acb9d0abd414074" + }, + { + "public": "0283369c017411e8ac1efbe09540387de96375b0f768fa931e14499e9ecfea9d62", + "secret": "1a785bd0d956046ea2943e4440c63cbf780c0d65467ec5c1b64092de4c434f99" + }, + { + "public": "0364bb1fff865f837ed630053c0fe738eb56962042a434904b78b74eea6b6254cf", + "secret": "08daf00188789e1cf7c8ffdbcccb622384d4819c690e2658bc5c1425df710327" + }, + { + "public": "02c946cd73db0bf6898b01e4136d775dc95a8f2e4cbc5f96e7bbb93336ae39e278", + "secret": "5686e816095280cda36189bc663e4cbe69c0ab657251ac7be8c0831532640d24" + }, + { + "public": "03a70158365b90674cacb94a4ec325f94fc8f242fc819786b6f0ede99f23d13033", + "secret": "ead0571a92d9b93058a775e82427f3f66f0ff719c25144a6d14bc05877e2d14f" + }, + { + "public": "03fb7f59708f150ad38ddda5590ea8b2d1bda6cd9099c6ad28740ffbc9d768abe8", + "secret": "7c05f5517635ec51cc9f41ceaff58ee048002d8237f86543a0c95e906fbc5af1" + }, + { + "public": "0297beb3679b0ff939d73075eb8c9ede94c659d7759519894dc113e0569831c95d", + "secret": "a8711b83d7751241ac5f536e6aea027de4a8b1689c3dd2fb850b9637204dc4bd" + }, + { + "public": "02f458fe8e89b9d52bf583bb95bd9ddbe6480c8db36f1615949b42f3e9f690cb25", + "secret": "85cf92b99f7a0d1370cee3601ca3a7b18e2ea4657710f1b3cd003997e3e43d65" + }, + { + "public": "02d937c2d9e4999ba038164acf94ae925277133a4bc7aebd27d5a1edb3a4b83d3d", + "secret": "32da7a91ac927b0e044d214fed8a790d36e8054fa039452f63a031b6e369c795" + }, + { + "public": "02c2231ea46c01575bf5ec12290ee48a8f1178f7c4f425421ea53aab4379380f99", + "secret": "554df7f7a9e65d9d75c39dd6c65f250bc48a84c9256116c614ea4ab3f9a43771" + }, + { + "public": "02c123e712cc4074587686377115808a034590a5717ffd6079af476c999ce85b23", + "secret": "1356ce15581d7d9378461be04131b7ed20e887c4cc5bd27b2cb86ae24e9fd2d4" + }, + { + "public": "02038a8a078eef20925bca7e69af26fb5d3bb19d80c9e53999141cca01ec245a5f", + "secret": "1bad5743c432350fdfc7851c7502d3f41bb06544a36f666804df23532fc284b2" + }, + { + "public": "037e03376ce55d62e0cc8c7f685dbdf9e5be4847707ff3fdc30b2eee703c6e7fb8", + "secret": "a062df60d6ff65509e4e3c35ac615e96163f68862b98f843564218c5932b501f" + }, + { + "public": "03b32d2076effdfde44cef84e25bc16de6b42e5dc4f56d7c74af9d37aa32173c79", + "secret": "a487b31673820b5a192010339ac890fc666a7c821c8e2a2c0b58679806382dc5" + }, + { + "public": "02de4ae52758f34998aca357779877ddd6f9a21fedea9ea945beddfd0efbc94a08", + "secret": "468fc4605d329c6f24da88ce7732cba734cc414c743dabf8e9926fe02463ddbd" + }, + { + "public": "03770a8a94382c4d917508d8a12f5d05e6950ef3c6ce90dc9675c7ab62b97e2f6b", + "secret": "fc69a2100476f973c1e4571ba4db41e528bc6a8f69e45a4b5824ff39b8f22e53" + }, + { + "public": "0322de013ba3d1807e66d9a2de22b8c0b4fb2628b1eef3a7c544dfcbeace1ec7ad", + "secret": "b6e4adc2b0d4ac96e61525d402a2ad5f23e32c46bcd15a86b14f3b523689f06b" + }, + { + "public": "03ab9bec011f2f4c5370f2dc11b1dd06b4aaa86fa09baaa8e18d80a8c1cadd8213", + "secret": "cc8ddc499f07b63ba27fe62a9b209554ca6a6e7ee29cc92af5b5f7ebabd5b110" + }, + { + "public": "02fddd321d1c2d58f74c71f028532d7e9a612934723824db73c74ca9befb77dca9", + "secret": "5d06d3eaa9dea8b3a9e72f63e6e1b1951709c049d6e9dc01ef97c75fe1c90eab" + }, + { + "public": "039f299edb044d45216000d9212394356fefb917ac9c13bd37f5d0743880bfcba3", + "secret": "1675ecd9eedd75e3b6cf751876fcc688f605a88a25fc066dd208095709949005" + }, + { + "public": "03b2b620c94bcc465c270b7eaeb363fc2e8527fa95a81bac6c555e0eaff9d827a0", + "secret": "0375120e57ce3ba926b98401ca77d14fcdbd12dfdb3aac10bf0533fb20551686" + }, + { + "public": "035f65ae3be91059215c6073c08ea322476d607bee624ead58f0bd3adcddd05c4b", + "secret": "83c65362f1911e3ad95446cda0fa722d20cb375445cca3b92049682cb11f454d" + }, + { + "public": "021f4e14b1ad204ffe56997aac67b052f2abda2f819d9f17796b4e8b55177c343a", + "secret": "a73a5d461701f515e2caa6a8948f5ab5b6f03d495729a5efbd263b95cf13d5ee" + }, + { + "public": "023d5e4b79c2923f5678d16ad43c268c0c004a19921174d6323a24b8d1d629564e", + "secret": "2148da2f983591541719002a340c0c384255bedef2f003ba6c9b7fec22a31c14" + }, + { + "public": "02741fbcff4d695e9aedf84880dfe8a5e9e8156cc65b3facc4b9c1a0ab2366c868", + "secret": "f0745267ea45d502537ff0bac53f54c11d9f8f3bbfd7d5da16eafe99f8c31634" + }, + { + "public": "02efecbf50a5a9c32406854a37f967d47b3aa15e1223174af5789b064ebf532d6c", + "secret": "00149ac80dffa9a23697742f00cedb15e66b3e463ed1b06abf1f5e4a1f12810f" + }, + { + "public": "02cc71b25c3780b2579dc9babbc991db340f6f55a7c17da34f4936f30ccb925db3", + "secret": "5c6e1b21348c1a35d6ff7287d8f87ff72497f1ba73cacfbd1c587b416038a7a6" + }, + { + "public": "02fa94621d45f5bc1663c98ea177ac56adea13044d66a691d89e31b2ae5343e7de", + "secret": "229dcf37c5217de267d83971a330ea5a5a06b6c9ea49ef22a5c5981d98fe01ae" + }, + { + "public": "03c33429d0f943887d082c2d92c4d2d8beced0138351957d99848f981edf9b697c", + "secret": "d907cf868c55cc305e6149d54e36565aaca0eaee6fdd0bf13728424171f789f6" + }, + { + "public": "0342f1612dfaf6841a4a31f4d71b27aa57db60ecc3a54ffd341ba427740a0a513e", + "secret": "06ce1133faff03e8264a3ef90c660ddf84d8394ff58ff1daf8d2495b98459ded" + }, + { + "public": "0305823c91928cc268a8b5cb131545996e6296733543c3377e1d05ade44953837e", + "secret": "330251f55d6411b64ceb78982a5b97cec197e946fd9ab26d9257f98b4cda187e" + }, + { + "public": "03c1c1983acb49bab11de9fc62dae95d03cbfa5e51338f9890261de701b31094f0", + "secret": "b78fe70a5d18baf9a4633dd6b452977b692af9fe8f6d7e07e26c23044765971b" + }, + { + "public": "022a868adc302b56b97ab75e2e03daaeb416bcabb009b4842e40bd2d12d5cd00e7", + "secret": "d06177495ad10d26b7ac4030e5eb2d8503153eb4b13c46a39dfff2bacb350b9b" + }, + { + "public": "023f61bb5a927f28c3751904ff13feaefe33cc21fef2ad00a8b382b4ebe223d3d3", + "secret": "08474d986909714634060c4f0ae9014b7d275883d9061eaa2c74c710271bc217" + }, + { + "public": "025aebb9eca8a3d69c350037c6f5f80d6030e238581c4ff50ae5a78333662733a0", + "secret": "a47d62ffe2f36ecc5ec3de21013dcd7288e08b046e4219a47043a7619b937b79" + }, + { + "public": "0293b268a5b30ca8c15134cf98fe018ca35ef8190280b5a6e882da23cd6e78eca0", + "secret": "00dc43c1630a58b46894a34add8024145a1bbb4913f21dbbf16513819ac07eb1" + }, + { + "public": "03036ae0f34fa098a6c4718755d28dd6b7b6a65cd8b2c126272032b08b51222580", + "secret": "d45957eddebc0d4fe46027324e6bdb83399d87c73b88fef98522b08198806cd0" + }, + { + "public": "036b0edd9881e6fb65cea6416d94cb09d798071467b3ecd58dd75a410ab0d8fcca", + "secret": "041c78a47610730fd5400a0f4942d6550c54a4dd5cf8b155d77b510864e3c642" + }, + { + "public": "038659b98de188909e183360a1e84a1db088a6775c0c678194aa2c07323546683e", + "secret": "4d663eec997b2765e7746aee1f601dbe9dd1174d8223135d35cdd1d48e6af7ae" + }, + { + "public": "020f0b649f9cd41300f039a3aa4ae59c19d4ad0e72ec60c882c0f81399356b602e", + "secret": "6f5da6d383cde22bed6a3067c1194314316d0599e4b6e1d001272e5ccad3acc0" + }, + { + "public": "03761694c76b291e65fd8dbe867cfdd34e0994d2172b49ef0524a40b3eedd845da", + "secret": "4830112c3746ee4116c07f0978a6952f80138ec86c494a7f63ef8b8ff218f269" + }, + { + "public": "025d3f3bf8a486d43696ef6d178096d660a972ba1de0c8e0612f6d537f8a9528a3", + "secret": "91260a7ae742a34e092e8f03939abff246f90298dca5fe0ce194c6ab78b0e5db" + }, + { + "public": "03dfc51caeb5cb49f97da1b17414b8811b4e75607554aacdfb756e1bce54aa1130", + "secret": "370a01a430d1272c252cc893b666ac581a3ff2c182ed6c7e19bed1de04ebc0ff" + }, + { + "public": "0274c44bd1f4790504f474cecfaeebd7d8a313534b86fb2317ce512e5e12a43c67", + "secret": "4c0a8553debd8778b4b879b237c8069b7e827f5f02c4c3fe880e0ef4c8141ae0" + }, + { + "public": "021845adc066183f623b645ed7320babec3287b6c384dc8c3ed7fd41c75ef2a0bd", + "secret": "33360e3c416e66bb6236ba4a9e863bc33d5b34137bc7a7c849ddc65b296ff1e6" + }, + { + "public": "03dafcf6f3542fb51efc3a8a4f820550e8c957f94d8b9b407bc48d70751b58a5e1", + "secret": "e8920a8806c682ebc3738207add3349699237280998af88bbba4178a1668cf1a" + }, + { + "public": "03c2ff338000d43a86416e816d7b020622e44a7812e7f568389f32db4dcd6bbc77", + "secret": "848031465e383cae8242bf514ae19208266a597afc781e4330bc88d274547f85" + }, + { + "public": "0393b5864cacb3d5d58a436dd7e385b434df641bd83d432d6aec28024ec180b36b", + "secret": "c0f435a66fd80d607bc2a154276ec36c7debf4de8c570be5b2581a023ee3af2c" + }, + { + "public": "03daf6dc688f29c13f56d5689ef6d400f4fd5a4bd18a3deb7037a64e23c39c4e5f", + "secret": "ee308620fcc11565aff225845737bfdcd1a4e37bf374c6dcbc8952a9c0c3d928" + }, + { + "public": "029ff6b08fd406fecad7d3ac2a6b2f00b64585d297c789f82ecb994c2895203070", + "secret": "f520e34e47b3ad59435519d7ee8bd4054326f09627e328151e55c213bc9d2d84" + }, + { + "public": "03ebb67e432a172d6dfde2b2c0412d1fae4674453a8f8c30100a02141d40115f31", + "secret": "f99b4fd58efe114210638b8a86f126e390a4909307a8cb637d055b1fa2aacb26" + }, + { + "public": "02eefee7896cc495bdd7eca7d04ae8c4ca9687212ee2c0a7aa15c331107f767da1", + "secret": "1fd1c5a30a74dea3cb4bc70552e64b169dca1d2a33bb4a0952fa450f67e300f8" + }, + { + "public": "031faa7ade433a598b6d95ebd7b188188fa003cef4c9c701bdac8cf8b2e6451115", + "secret": "f3cd014ac7eb75f7c2704a0dc462fb87de91fb229b63ddf01b06ce2b31cc2bc6" + }, + { + "public": "036f96de9ff8f5af1bfd65a6e30b2c1ee4fc3b073789b33cbb67d43f6e6152ff12", + "secret": "80e2df85d296379865926fe3d26b3f66153171077b7d254e2e3ff9ca828e2f11" + }, + { + "public": "03df75dca87d58512f396de3b178c9f72b65e2a805a2a49bd230cf99402078c123", + "secret": "a01cd994a55e9cda001260908e66bb3e58774e4d161b5281724f81dc4dca1320" + }, + { + "public": "0240e81fb9ffaaa1142d72827f10cd4ae1daee86db174669092cd56918242544ca", + "secret": "972e33ff7e084a4714181e9a808cc3a78fb17e396a6f040718f7ecdb67260b62" + }, + { + "public": "0228e31084194aed2c00d475c25c46759689fc43c477783d90aca0ce37f02961e9", + "secret": "620a98f079a02e7396af1204635e3a1ee8854764b7d4a8a2c501a39c3ea36f94" + }, + { + "public": "0351a2556ad50fe5636dd52bfb3878df471513cda9c457872f4e5b8f19e2fa2815", + "secret": "ceb63ac1c6f86547fb9afd4e729644de9d7abe26b9b85e0a0bfca492973b24ce" + }, + { + "public": "025ef1b80644768b05c69aad5dcc868947b114830b2c62b8e7126061f7257ece44", + "secret": "0119988d3ff25e793b21ab4558b3879351ab64eacdda1e8eb069c6f7ed50879b" + }, + { + "public": "02c0724c4669360713f1fe5f8fc3e6b840a729e0fa69c2e0ea902b2426f583d8b1", + "secret": "11498edb61aefb830e42a72b3fe267687988f9d447e556aeff8e14039aeaca9f" + }, + { + "public": "039df8d27212ee5b102a1c090388cc57d50a69c97957056d34ed35cc7f72402430", + "secret": "7817e15b90f9058c93df8843d7941fc974eb8f3acd87e9043cbfe49b6305fba5" + }, + { + "public": "0285f2141fabafab325fb62f2217285bafaf98ab1ab30c06b051536a72799ab5f0", + "secret": "e2d8dd92399b7b13a3a0592947a2110e12529a597fb969c1557042d3c2c95135" + }, + { + "public": "03a15d99bf1e6a5e18d2dd1e88b5c1121107472a3d94cd6449c3c6fe0150e0719b", + "secret": "50b84bf0b049e99254ee246c26e476ca8d638b02d42354a3928c9cc012e27223" + }, + { + "public": "02e902c761454f8dc353eb454f31091d1bfa68d2012cfbcdad50c320f396805e6a", + "secret": "fc82ee21a751169573edf5cf0d2709c9dba0d80f7d3fbeec4a31b085f57a5786" + }, + { + "public": "031ceac8d906d407be82c1d0204302c72c845c838d12d796b7874f24c08b2749d9", + "secret": "e1d69a12ffb11aaa0461c33d57c7e6cfa81a433e27ce68993bafe720e19ee878" + }, + { + "public": "02db540a08ddb4d3fbee866941bd2be7c8a5780fe7375bb54abe293c647cc7c5aa", + "secret": "e9017aba32c1b6c1b239f44ad94e2b08e847572863c107aec1de965422913784" + }, + { + "public": "03b64cc268e07aa1ed32b04b67f8e6c1d19d91b64f0186a0799d2193476516164c", + "secret": "635c2dd80c56e3c6b8208c10e6d48960275806d720124716eeed90dd0bf8815c" + }, + { + "public": "022eddba9ba82412e818b0c63cafc3feb9c63a76774d0c5ab6db8aa09406f281b5", + "secret": "3c538857bb9965d292ecc68d2f824a12fb45ae4fb8be549ee4574bc8f6b16306" + }, + { + "public": "0215be69e47bbc8eacdb67a8cfe34200b7a4ac88c8481eec2bf196197689c712f6", + "secret": "48a8b67448b3760caecd57af1da89b5ee80d78925d06565a8f032eadeae4050a" + }, + { + "public": "03557b2a4c964db2f573a3c514f24cbc5070a9e339f5923b6240ad10204b7e43a2", + "secret": "fa230b561a6150e1d7085af43afb6f5bb8b81b44583f34b5d38a48eb17499a05" + }, + { + "public": "03e47ee96a44c6eaec31b0213ea6acdf4cf53e3d8d2aa8efb4bbf6c3878bc95285", + "secret": "1ec8594f1db3a68a90aa0fdfe7b7224d8c0f8e3f4d0a2fe765d2e40becaa5562" + }, + { + "public": "0218acb50a07a42b9688011cad405fa4b812bce24d9ad4bb28bf33d539ea262349", + "secret": "aad49a6962121ecf71f73f1675367559401258a74dcbdfe2296b12f50f39a19f" + }, + { + "public": "03b382b530e29cd71f6f15cde59ed915d1a9e7de0e8eebf7b2334bb21864e99f50", + "secret": "d20f1e4a64a17aa63779fdcc0f3302fc357678aae06b94dac9c20809f811257e" + }, + { + "public": "03a51c8aac57d2968f1f848304584e6ce009acdc1a62b9310fd9ffc0f0f8063d30", + "secret": "d6b1baa701b7e43c4a2fbed453c9d70850548cf0064ec448f076d5cac5da5b2d" + }, + { + "public": "026d9908597b085fb1c51ab602ae0722f52f68be7c797132d77029b37d604cf4b0", + "secret": "7e8ad42ae65ce9dc237796fbd4f5b2947c3168297e95aafb54858d13c194a7e7" + }, + { + "public": "0342936b575aaadc94c7edaf287076bb0dc1b082083ba97ceb70fece8cdc01ccfb", + "secret": "2974a7f4bdcf7bf096a1ae49b92914e4f50a3fa2bc59e8c0a3239c9cd9960314" + }, + { + "public": "03587031c4d6cfdd301f6fe2846bb03f25f4af28e79e85e98d4fd49ae0f9ce6ec9", + "secret": "580fa2916f9137023f793d0d34774640f4762f911d21ccfdaaf3b78ebc69d464" + }, + { + "public": "0228bdee236cf55ba43f0e8589e2de57a18c33cb905747bb4b23f478ced83e9a0f", + "secret": "6882f65e95f1b827c322b2e87c5bc4922cb3ad1e33393789a17acf091bb21f3a" + }, + { + "public": "0362fa306b217357f27c92d40ef8810e06d0f0fbb2348a482f8de15689088ac1b9", + "secret": "ac26e61ecbcf7a748a6dc3964c11a8c9913257a9921a06f328baa5979fc81e8e" + }, + { + "public": "02f72327e882e6388d5d6ce61b26f0f0d6b45baba8a6058d931f57f1ea1144a5f4", + "secret": "8576966f10de06e7a519499002c9cef7d72d5adbcc5f063e2e3829bf24fefd66" + }, + { + "public": "023ab0c611210be948e1b1469acf09a1d19dc66f0138154f4edc2c534919213034", + "secret": "0b4a37b757feb5a35bafd455f8ae4046cc3e845cd258cd06b3b31670457bf531" + }, + { + "public": "039012ab51b10c26690d94f24bcd6ccca2c669171ec8da69529f25c206233c7be4", + "secret": "23cf9d4bcb7068e6ea24f1119edc0d447c58fe3ccfaac60e8f27c2707bdc985d" + }, + { + "public": "0347a931d318b37f5df33c8c639d443985ce9a4682a16f5cb414000fcdae025a97", + "secret": "ee63cc52a67b6a0504fab1e241a31e37a374f359051939cd0af8583c6085bb16" + }, + { + "public": "03ab8b47003940cc06d86c1cbfa5851ffb16c7b2700b4581987a30463380def6a7", + "secret": "a43282c2229a4e72bbcaed06340addd43f4c0f93f3a54a803d3f93bb2e17d421" + }, + { + "public": "032dd2eb86ae535d3cecea49501398f74355d17014251beabddd4aaececd418ab5", + "secret": "9929a3035f9e5a947ead6bfa97c5f00103e6856a45198168c9e2573d588502e7" + }, + { + "public": "024565c5812d9b1d977144c9ce9ce78e3ba68c81dd881af3b70cf126f6c314a458", + "secret": "8d81fb207a42fdb515613d0bcfaa3d995509f9118c09459ddfa79b999f43cb63" + }, + { + "public": "023f9afffbef9d62adbbb65bfc14a8c780589c25485dfae33869f93371c35274a1", + "secret": "ea92387ab240914cb9fb450a027d4979d28843a00f767f39257757185ab36b60" + }, + { + "public": "034f526b796d8fb894550b6d2a03b8a421f92e68d29a5dd9e80fcb9382945fe833", + "secret": "1341ddbd5ef020e3ca054d504cfc368a90c74c71b72fe3b8da2f0ef3f7b438b2" + }, + { + "public": "03ba378a40188e49cd011a147ab37ede7f8b9ae848325ccfcb3611f1e7996eabf3", + "secret": "126b0fe88eb6b7bbe53ee4e7ebc76ff616ec75c8be4a68da13bf38bf70352518" + }, + { + "public": "02ed60170856c7d32e9e64a3e9438db07e525d55a9c9b03e5c99503794b686ca2b", + "secret": "97167ae109fbaa115d61c536be5c334754d5acc067261a97e9b05602484feb79" + }, + { + "public": "02c2d54c3fe10d3d73af2af75a0356a446ac77922be898da76dd6e440e932f053b", + "secret": "7a6856b3e2c5e65d3ac34647e3259ecebf17dca1f39f0aa3e14e1cffd4c2cbb0" + }, + { + "public": "0201150b97db44e2382e43a04f3c56738d0a2cf68cc8f31c9f1c6dfebc52fce88d", + "secret": "887fb1b70d369c8d1a03e7ad23bb619d295352d1dddab6bbc9d31491e1797dec" + }, + { + "public": "03b17ce198a55281f3085a9eb99a19e99f5a43d188dd89e443adc056d52633a863", + "secret": "5cbdb726f5e1be4b0d22ae76e0392ab34ee27acfac4c4dc2c129127a14787a6b" + }, + { + "public": "02e6e855eefc43664305cced1f78cc268a1d523aebee5e78c9bde7878ae494528a", + "secret": "8dfcd59aaadb8cd7d1e127aae2927326df3f299d03d3309ae2fcf691d2e5a63d" + }, + { + "public": "03c4ffbb7361c2260b9796d146199019af4df0044007bb516d2a7b04fe54ce1ca0", + "secret": "325b1756c2e4e21e461d0de7605c84f1f259bbdf3cd1e059f8bd9be427eca69c" + }, + { + "public": "03d8cd75b5099eaf8b6a0a78bfe88b81dd13d5677619eab3e4b3895c58702aee32", + "secret": "47c444b0d0fc235614f5e259c9ac434d43a3ab6940b1454127e24b04de43118f" + }, + { + "public": "03692d15ba47562f82456634bb45dc555c679be769b42ae67b8a44ab0776e84418", + "secret": "cc5eb2566c40121516c8c75f37f710c83ce1bd86970e1afd2ccfc7bfce23ea58" + }, + { + "public": "029006793b435ce871b20680bdd268dd8f61b73f3b6ddf01812472b4fed1b2c624", + "secret": "c2ba08ab437bb228824440516614fbfe7694cbe36d582597af16bd60a3a1612f" + }, + { + "public": "03278845d3c3fd86aa93d5f83b2374b13895b322760263766913b645d9a961213e", + "secret": "28b4e61d0717259ebb99a941d8a48ba28d1cef3d7dd217200e1fffa31947209d" + }, + { + "public": "022796d4677387b6791faa301312ed89fd3841f99c0fd3482c4e00cde4ff901cd4", + "secret": "9db0c4a6fdca097fbdd0b24ba543ae941e6ce0b9bda9429f56f880d25c6615f0" + }, + { + "public": "022721c4da4e38a33c52df73abbab56a76d798ad67a80c332fa5734e2c3b6bf8ae", + "secret": "4568d345a18f111ef9034b9fed63b7be042f7c41e248f884be2bb77e978324e2" + }, + { + "public": "02b9e83231f3cfd5e199c4c3958e83873751a2c8b040249142647414192b11ac81", + "secret": "876e91b95a66bc228fae79670a3eebd9375aa915936d34fb0ff90fa3f36b5807" + }, + { + "public": "0374613394d1189cd1f0b4d14bde78b6ba5c3f37411ae16d7abd5a5b4177db6320", + "secret": "6328f66ee46fb89df7bf1fe317f65d745b53a8c7a6d7062691a9c50ac8d89ebc" + }, + { + "public": "024216c5b152215591288c8cef9bc931156a89056aebf3885caebc4c2b751c2a3d", + "secret": "8712b964f91681c16d8d6e291ebec9b4b55d80b74e275d904f151c3bedfac109" + }, + { + "public": "03337e0039e8792860466151bdad944a0670087befa7df2be3b6a763afec49d818", + "secret": "fc194b717318ba2eff35ecbf0e7c62e1a39214aa91aff6a08fc38373fb02c419" + }, + { + "public": "02302b6265352fbd74ec49e9f83650b6993f7eb21d0c1ea7d2d82dd7b0d7b2ceb2", + "secret": "4133af8ac051768a8eb68b663aaf8955dc2a82e021081b5d46ee7b65b7c15437" + }, + { + "public": "038a6e248ed91f65aa96cc821e445544894c1057f517aca0993c1bd3734f592a1e", + "secret": "971d7752efb0c6f3effe401784b322382d7546b3de5f1b4a0372e1638e0802b8" + }, + { + "public": "0349dcd67323592c7649f91d5eab013d1f8e41dfebaddf3e9d0002a35d75ff7da0", + "secret": "61cdafafed5af201d39f7e10d20ad23f1e37ec7b12ad226958bf3d04b574aeb9" + }, + { + "public": "0381001d0ea8a9d8a7ef1bf24ca57f69e4226e7d59f0b770723987c36117d94191", + "secret": "1f35712210d3a1e4561d04c0c3cf3ee8f7469e2aa5aa700555efe3c63ea51892" + }, + { + "public": "02faf1087ce9f4c3481496359dd81973db97eb3b070d092a7d4e20648f90efca84", + "secret": "7fe2b65fb3ef9c3d8338987ba9bd7fd96fa946dc52fe5552d98872d904d37bbe" + }, + { + "public": "0377b396c7301bd04ec51361b7c1366660544e693ee8481b0615acd4e357d61ed8", + "secret": "3293f7589cad8311364acfa8cda6122ce777dd167957086ec38f2431389b1811" + }, + { + "public": "0288952936d813ec27a3199c18062b6be6348d4c5a478111e650a7130e37b7cb61", + "secret": "a5ecbefa4fb2d4c74bc57c1b51b22c87e03ee15652aac85644d4af91a3a3f5ad" + }, + { + "public": "0337529ef779ddd57c141f83cb70b1f94d7e2fb5fdcdfc3b5c2538ae3c19407867", + "secret": "f60b118d1ff97282d8b513b6301592d0c5cbde1bd9df3cd451d5d0b7bee25e1b" + }, + { + "public": "03514b2d4fa322e4220e03bfa4eff36996839c6e5f5a3db2d416ed7fbb8c848523", + "secret": "bc57429ee9b0eae3a05b92c91f11126795906a7f5d4bc852043015ca8daf5b8e" + }, + { + "public": "02e75e1a095aa7951435f6b4c693a4ddda27187cc5cdfaef7fa95429e80dfef3ed", + "secret": "add85c8a3e90d76d80457817a0c348d0b98f6cb502fd3c44d8395570ca652d32" + }, + { + "public": "0292a01e6805304eaecce3dd4a45c55f32a925eb221e272712743ffc0de7e535f9", + "secret": "e28fa4d284b12e87397d2ccf23210ba47b1f71fbc4d175aafd772dc94b4d6aa5" + }, + { + "public": "0202c4ddf6da70c550b9e720c081886d5d48f7d9b3bcfe63b4c3f188e7f9ed9138", + "secret": "6460f46b7d4e83315363d014d34cc9c0b2b058249f74b6b1273c6f0a4b3fbeee" + }, + { + "public": "03ad04afe8279d00c57cc57e0822749617e1135bb5e23d43349a68e63190f90746", + "secret": "8d936c905dfcf11f1ee52d82fb4b675d8ae8b3f9162874ae31e4e9b198795cb9" + }, + { + "public": "025a1f1f38321f7f13aea2515e6b30c3770de198ba36734a5ae804a133cd933b7a", + "secret": "05e4f2fe0b3e208dbf73b422bb06b8b05e9078f194ac78bff602ca3a68688cfa" + }, + { + "public": "03d8b005f1e177c7ad97a97ac3c2cc926d4514c69cde59dc3722b3fd98aec3b49f", + "secret": "4ac0bc9d7070550e257278ca53665ec6193a8475accd93b6a138ec3edb5aacac" + }, + { + "public": "025450b28dbeb2aab1dca5986e17d0719d08bb7ac9475b7fe55b1f57fe73cae255", + "secret": "6fa2ace42cbc8aafb0cf5b70bd1a617b9f1d6fc382250ca24b20fdea5c6fa3eb" + }, + { + "public": "027c65e2dbd5160047a92302a5b3c77c92059ecdf46296da6cdaeb6f2fd13b9502", + "secret": "52903e7360215ae9f8fd0f1a38128e327633021d63f25484ab7cd368343811f9" + }, + { + "public": "029ce84c24e03583cbdd57b4e7ecb6eee64fa5d013f7a7c5f3047cdf158f628cc0", + "secret": "291cb37faef915bc101cd0eab995ed2f0844616f2684d6836bb40687755419b0" + }, + { + "public": "0365fecd7094c29ed32e20a9eb8c88364fb8e461925c1c74c1a06acfd0fd6b7a49", + "secret": "03f1767d4d83fc61eada77445eb0df091903f0ad6d49f1cf46ace4f2e18cf7e4" + }, + { + "public": "03727ced4ded5df755150c0d68dd5a94b0b29a1614581959e466fa658652341852", + "secret": "86854aeba99eae5c113d128a486504a28f49c731f5769bfa9d847509a33cbc91" + }, + { + "public": "039eb04766c279dd330b728575481155e541b046e45fa1b1e24279c63e43f6a494", + "secret": "4f1cbcba8cef09407108ff7c7425f06e779e1b0245578a994bd2b934602ebcfd" + }, + { + "public": "02011e91cd7775a521be05761ded7af54a966ccb39f7ad1d3a731b398eaad92709", + "secret": "8c083d4f920497bad44201fd68c12a5ea7f5042cb44dc0ea4fc2fafb67297b34" + }, + { + "public": "02ce28e9a3e0b0e87b36aaaa6f10c5b2b8d7a193d427cc1b89e60173b2481ecc85", + "secret": "4ffb21045bd726f547b827571fb0d818f7825a5165a5762d406c2d923c409bc4" + }, + { + "public": "02149b8db4520f5aa488c91075bf066228989de165b27cc7ca80abdc445df80ffe", + "secret": "bafe8fa54b972de2cd565ab2e5644f3b9c6004cf9e786ebf38755930cd3552e0" + }, + { + "public": "03bd45993f9c8c8a6670fe9d66cc2a91ed814f826b44d50c6d0423ec0575e53f67", + "secret": "e86bc53ab49b9431ecebf0aa2afbdb0c15da169de5bde62e966193678b7eee22" + }, + { + "public": "02b04320a9937856d6c78da7455a069412b5f560225811d78cda97f3d28fc6cc90", + "secret": "be1f8dde234ab0231eec1bd81bd15dd37efa53301ca84417b9c80d252d6aee03" + }, + { + "public": "030371f19ea03c5b848954b186069ea3f0737c3ed863060184c3d3655ef399a333", + "secret": "1cfd1f7f8193d0564580ba07cbd48ea32e4664cfafbb18bda1e10436d7fe1337" + }, + { + "public": "03e59fee4bbe872687e6239ae9ced7a5520888afdb84c48ea4d663af9e07ef97cd", + "secret": "0401ea170aabcc125f5885c3ebdf1b4b77ce5008d9e248e2b073bc14d16c5408" + }, + { + "public": "02ed41d3fd4492664fe2d53856e2c91b0a025ffbc147c36188698aa5e83501a611", + "secret": "8b15a1d644b467c1395f04e61c581b9df1fd6e0c6bf26c93b7c97d21a60715a6" + }, + { + "public": "0257a58af09968497007e8466c93e8b223968ba0afa250124401ab64dfb4e0812a", + "secret": "6a557aa06d88268ed71e3808f804ab67bab2bfafe3c9d5f5e7ebfe5c21f0c6a9" + }, + { + "public": "02c9cca8a59da75c2cb6275736037e7f1ddad1f79894e60da7d4d447ce773ba3bc", + "secret": "015caec542647bf0c12c2a0864c9cc9faa443a646ba8f836a1bee656bf7ba3d7" + }, + { + "public": "0384c7f2951c01d47ea6fde0c6772fea69325a16e7892137ccd1981f1cbf244b44", + "secret": "22c6578ca8422532dbdd3a74c71d7a4516d19826bbf773d196e5dcf614d93577" + }, + { + "public": "02327a27188541ef28a60ce68475a2adc4e70ae60b197ae6bd861c6e7dc5a1b0e9", + "secret": "36e629d17a607a628a4856e87ddd230acb8060568bc3381518e08a99a0bc78d1" + }, + { + "public": "0329b624bf66611be968e4a6ed467b2a27cd0889c2e9e723fdf29c30da52ce12d5", + "secret": "295d7b8a638b5670cc12b2f6744cecc54c2f2f4ec5aee00581db4f24933f17e9" + }, + { + "public": "0223340154a63c65fca13b45bfea15f297257351a143b634597883737401241082", + "secret": "b180c7a10b48c16795edf29a822f90b203efdb8f4811c1b28e7f9effe25330cb" + }, + { + "public": "0290c87728b3e866494f7271e9f461c028931578e0c38b64d0046482b23b7e5e9a", + "secret": "65f6c209dc38c5df331cd00fdd542762c610679a1392948caf582992a7e9aac0" + }, + { + "public": "0377016dc7ca14b1dc52f189b8d9aea2a9a2cba260423866150d7941d39d9a4344", + "secret": "4a52733f0cd7204af26d3fce17d6429290d65e1d37c5e80b40d2e1de86d72891" + }, + { + "public": "03bd9728d9db8c4aee45fbe9226721d4035cde50e25b80ca115958a6847d8dfbda", + "secret": "1d61da07ab06140d1d7591c0084a3c0473f67a31820eb0437bbd62dd779a792d" + }, + { + "public": "02020bb0bbc4d67e4d858c59291e05f8c11e4c36373d66ba96a0cb529fc842bcb7", + "secret": "c750c44960434c2257e54e95c5dfa7c9c65477e0e90461f9dca34b784ad78d9a" + }, + { + "public": "0331873be2872abb977522065b6666fbde8e939d1e4217fd35ed4b58fe3a071388", + "secret": "b72980be9c2962df3c6c3cd7032b624ea0dc808d21f2a754d594b35de62609cc" + }, + { + "public": "024b006a61bfc501df3a364f44e078e9c99d939afe91b887fccf0cb07566436ef7", + "secret": "92244b5d210355ef97970320d8b0b418d21a99b2637412ddc56d07d5c4426a8e" + }, + { + "public": "034f1516518c3f68b01bd94e9c15184ce721dbeaf75ee33af08ab5077ef309f394", + "secret": "2ae7f7fea911674e1338e4ad704784d11860251e3765de6fe05ff4025acc56c2" + }, + { + "public": "034146ed7fe4f57261efd4ec29d5605b8dbe3a2bded226e4480933d31625b19ffb", + "secret": "6fa45479df9521372aebcf237e87d0101faecdaef2086353692608b89459b32c" + }, + { + "public": "03df7497457b3e1819641cdfabde62417b4dbcca47155065ffcbdde3f7eb83bb04", + "secret": "d646e8c6e1434c9dac1f90f14e840c69cee83ca4894bdc848da6cc3464e41de9" + }, + { + "public": "020f8a1f8817f3220270f0f0c8659e69f94c58019966097580a8dfaefd153ac9bf", + "secret": "204d72d6bf411301067bc1e117e89ca8cb74daa87b5a7ced37b010d69b08f627" + }, + { + "public": "03dc7ead56167dbe951ebfe2475722c64407c12530946718fb8790a56d6cabd4f0", + "secret": "1ba08a1dffadb0047e37ea85bb31842603a99cae49ab17de71c98c32ed28d320" + }, + { + "public": "03d49f266dc86be56100785451ec0910506fa74c64acc54e6092468789a595c2b3", + "secret": "1094d749d1ed0632a62e2d019d63f59c6dc76bd0a0fca0ab746d8eef2f6e47e8" + }, + { + "public": "035e382db2f83cdaf1bb8b464dedc4ee66712d9b8aa4304d8c5464d7cbf1048986", + "secret": "080f8e3d934bd5ca6b3ce67b43910f19f14e44a36e0742c56ac9cbc16284b4ea" + }, + { + "public": "02a2931fbb5335d03699c3b779b05a38b939aa49145106b97736cd3f7d71d6fbc5", + "secret": "22a9ff86b218b3aa99ca37ac29c9c5ac2c1755c25e81113280facc5ca9cdef02" + }, + { + "public": "02519b78172b03c879189b1ee47772e9259bdfc28c72333927186b61d5367ec3a0", + "secret": "a862f2f887b3b72736873d48457968c3a4711e697177a743347d6c886d8ab722" + }, + { + "public": "0363688ed3e56e5664f5992feff6d9e0d387db27e1ead02e78cdfd65e881e023e4", + "secret": "6a45049dd925d171ff7163af49dc74335ffb024fc29e56c48c77f1447a446edc" + }, + { + "public": "023059de5081ff8e3648d4788db5555d4a0c62978d9e6978783c0b35115e5459d1", + "secret": "824e7f47fadc615a5b64453543e05df9e254552623460f3d36c4bcf5b8e104bb" + }, + { + "public": "02658ca826da63394389f31613c12cd27c4c5a2a48349a08c3ed7712ded7799df3", + "secret": "b852b9262fae46786d8a30b0eac86f76a1857b4f0d06f8a3981c233374b449aa" + }, + { + "public": "02fbcd10984523e9a0d0811afe7d339b4d007c3bd9ea8581ae779786f6f430cad1", + "secret": "d642edd3d2a10e05bc36183d4906db850681c2f0d9f9e6ad9be294328915ad2d" + }, + { + "public": "02a4700229ddb2337a5afb96108e87e6d8f33837232040877f08e1f88909771d38", + "secret": "fe51943439fdc52509afd1a0a1c9eb0a7d944f1664b42d04e9c3bfb67f0e3ede" + }, + { + "public": "025f6e04f118e974f92aa2abe68f3192fd5ce4de80daa89081295b66c714904191", + "secret": "36d259fd87b443f778372382cd2f3fa215496152535245919fddd22873aabfc2" + }, + { + "public": "0232f55a65c7970c4e36912315c8dcaf5aba56f1db10c52fabcfaf1f1995d84009", + "secret": "43f223955174186175b061195855ab33ec463f793a2fc77432e1d66d7d002a7b" + }, + { + "public": "02c2254655317e752cd872854465909c1fd6aca35c34dda1c9f54909b35290eeb6", + "secret": "b14d632a9f54230a54e4066cc22d36dbbce06f14f370b04503aa8b0c97fa518c" + }, + { + "public": "029ec18218cecac443f6ec410faacf471291b780c5f82f55cf52772291fc13dbf9", + "secret": "afd4cb84847d27adfad8aebf0d15056ace09fe6abe8c4447838993e4dad71c9e" + }, + { + "public": "035497bfeb18f0a43a59558344b986442f3da4b9fb15f62d3398deb5531f2c7d84", + "secret": "a3b5fea0c5953d1b7e783f53d9e2afd9d5e7fbe33ee7ab2deadbd0a5b57b65e0" + }, + { + "public": "03a2c4f3e05c0e6052f99bcbc8f46a13a31c7999e4f85d8a8c1187902b0e5cbb17", + "secret": "5b8a66c09cc9e502e21c6677485f51468051d420de7471490bd19f17458da2b9" + }, + { + "public": "02f43d82c482e42436c98cdb4fde6c987ec574f75fb6a60ab080b7ca1b2d55d462", + "secret": "867aea8ba2ad5405262c7e3311c250f4e3e104632b1dab4a81b342cc4afb365b" + }, + { + "public": "02f99bdfda37597b62655033dc71523f35a064c2534c19ba3f7d0398ca6dd9920e", + "secret": "2ae64c9978c817eda6a60e72e5bb7385dfad37e3c740f01bddf787ba1b1527ef" + }, + { + "public": "0337bdb54d7b13ce45614c43e8c8ed9d814d9664bf41429d95c78d578dc9517203", + "secret": "8b81a930cbe86644cbfcfd505dd5c843b5cc17ccede14257c0e9b80e49d0d3e3" + }, + { + "public": "03a80f1c19a58fb9613826192518c00398d51c6a6b99d7aabd2cb6a36b633eb544", + "secret": "1ae36c5a3866034586cc41e8266e00bc7f620a297632fe3ee68956cf395ebd5b" + }, + { + "public": "026ec014d87bdb73cf0dccbad954125275f5d9a4e3a20a1e6b1e7511108d73d877", + "secret": "e8b02b599a24a7b8b18bd167b0b215ee6d279a19addb516691ab0cd75dec48eb" + }, + { + "public": "02b3e4cb3adf02328143203c99617117272de385610e4221c56b90c8b0ff36c140", + "secret": "eac6728a24479c99161a7ca99661fa3377ad748808e664b4d61744521bfd1f3f" + }, + { + "public": "03921e61fde8204a59a47a4d44f6e826aa695a49f93f253900fae620ee4ca6f687", + "secret": "30b2f9db502c396281dcb613434fed52db7ed183fe296f548fc0e89cf2e6500f" + }, + { + "public": "023b52dfe893e8653d78a4a9a28805a4f8c3e7d59c4b871c6a9ea2d88264536fd2", + "secret": "5102a9b52dcfc2c7dea66eb48509aab97a2d7f709c26d24744e5e9aec81983bc" + }, + { + "public": "026b17ffc9dee1b7fe8033c04578a12e799a7adb6812caecf1a22a1b3f43773e17", + "secret": "5079dbfaf09e635640aeb8f513209d23bb3c9537f79f7e8709125fd15edf721b" + }, + { + "public": "0230080a63a601a54561d0fa999c3c6e24b611e7097cb6eea01f863724340d667b", + "secret": "25692942d7e0b504befd456cce757b636e63bddd43f94783dc99d9719c6a258b" + }, + { + "public": "02738854cb5aed2d06592fbacd44234e7bc1c5ed7c37a4d4bc95f56c214d58231b", + "secret": "d842ae9b6bf641a26987c542579b310f9489eaa7fcd2d6218621de505fb73664" + }, + { + "public": "03af08361b121feca05c70a4be6179ec9dbb3599c6bff5722006bfadcbbde7d88b", + "secret": "da187a426dce79b25ac33521d1ed513cf96d4cfdbcaf28f05d90ec325830c096" + }, + { + "public": "02a8d7affa14c4e45a604771e779c09ef7cbeb1cd2951adfa8367e56fd49bb1936", + "secret": "c9b197f1e26e57c4908903f0d2f846ddb899d0aa846b04eafeeedef75abb0efd" + }, + { + "public": "0318798eea0227c9076c0af1e1bdca5d3ce0f995c26d55047d8cc776a39d633ea4", + "secret": "8b5e2babfe832bbfbea26eb928dd05fe530b4197cf6f6b5690b06196f2b23675" + }, + { + "public": "024cef44aac093f57190cc6fb98a73b3af8e03ae53820be66e39ec270480dbcc8d", + "secret": "760ebb7e496941a89e26d7f524d569188860a75c7fc0a6e8ce29c8478901ba3c" + }, + { + "public": "024d4bb52ec844a5d8557be11eb0d8e40465afb962d0352245339a1813d3d6a5ed", + "secret": "620fc9a9fa057fdb27c5aaec214ad3fa77d65278311ae6b9fffabf819aa25ce9" + }, + { + "public": "02f0c8f6bd2253487dfb8a0338569e086eccdf27f3d7c3d1c3d254ecd44887ad66", + "secret": "ed3b7603a5ecd9e360b52c0319fe4afcf9aff24a8d3e9a0cde7b81bb38e77e44" + }, + { + "public": "0288d6816c87eba46c1b6b5f0a21527ce97f5a30082d4d41dd2ebcb4f1494ceb97", + "secret": "1fc5f583890cf315e76c381ff4dcf974bef19cf6a3e8b196a55448973d7768f2" + }, + { + "public": "036dc5cd8e0e396b2bd3010af534fd8628275c47a1e69eaef3795d3409ec9a51c3", + "secret": "2b7c6ef1bf86f23452367fcbd443cc54ddc99a4ffd087024fa2529ecac7a2189" + }, + { + "public": "038af1a9ebe55182721a6f803fa1b0ddfd52f96d7b4319d7ed66ebe1f2037d89cf", + "secret": "27377a51c8dab4e3a75b99355b815d29d4a16078b4daee7e4bb23c8e595b3c5a" + }, + { + "public": "0378863404bacf5a997c67671bdfd0a447ad3b7b7a8c7c8e94bc0be4cac3f1b668", + "secret": "774501762991c36be721a84cdaff3a3b1c22c404be6daf26d81b2984805383b1" + }, + { + "public": "03ab47378ea0393ee3a24a5e8febed78f0f9dcf2059c9d1191a40356d73502bd62", + "secret": "c5c2a1e00cfbd1973d8129a7e6c7b9a9a5710880bea8d25fa0f7d925a80afa71" + }, + { + "public": "0284739a0b5b51028eed87181583a75a396a710560c94554795dabbe0d53d66f62", + "secret": "3e2d02f86f8ce020b24d71c9f912e3587bdb1978e83ef96071b0e1459f075022" + }, + { + "public": "02cbc6619377a3058ae96718c081ccea0e34103c01bf5653e166de12a14d2e9d8a", + "secret": "ca27d4d68a70d8bb6cfe671dd8e40a41eda66684b764034c33fd98433c868cd6" + }, + { + "public": "02f90585b3f83cf0273d0698c04e804769e627642338860df3921c7d4fc72738d7", + "secret": "ba47e26e932cf30af4acf914a69746c210f819c2ede70f24ebadf1f87b423359" + }, + { + "public": "03f33f1bdcadf7892222b5db52a92e2a39915c23e0b6fb94e57142e8b12ac9ca31", + "secret": "98ddb1de992c752a058b24dd4a41332276c24052464a186f83cedc0ac86a2bc0" + }, + { + "public": "02387db97ef3e4f6f1383dd4377e06c6b9d92c8296e8cafe1431bffb98dcbc6588", + "secret": "9532bdf5aae12cd46c8093c7a8136992dd3bd5e928bc3280b7a3a756cd02c843" + }, + { + "public": "0212dd23afdff7911d287fccf01906475469db6378ac00a47cb2c0ea7edd6beb10", + "secret": "995951ee9d3d56c215d8df2556b1869e6dd4536b50ffb34ab30642e9bb414428" + }, + { + "public": "0212f714c076c37d1a06e3794f00cd1f54dd726fddb18c5661a276de04db623bb6", + "secret": "0019d6e3cfcc8ae738e5b0fe6c0d26e07ea3bea9f8a6ae6d6f85d5b980b41304" + }, + { + "public": "03fad28f96d814f45ec1e09d1a9471cb413119ca603294508286e61bcee701c7c1", + "secret": "bd4881d599ee494a3c0d2ccba301727282aa4f5c68d8e8349a80e172b14ceb4e" + }, + { + "public": "03045d69ac42a1c35ee677a2d001b459cd44ba2da1c30644d7973cbdefdf917bc7", + "secret": "c6545f099d6effe96edcd584e5cb98845ee756d6d9ad2a87e010afbaf5c60670" + }, + { + "public": "023361338429360c13d56d633f99a74c7bbd83419a405bbd18d9118b31946cdf9f", + "secret": "61da2d2d2211b1ff9c3b8d771109da3da855ae27cc2c82fb7d3fe56af69a17d8" + }, + { + "public": "02f3942e42022902d5d1ea01d5d6d3ae57bd0d05e76580f3c18996f20620f335d3", + "secret": "f03d4c4ab3abd036f5c1a0112e72a9ddab0842a0d90f2a70c7b1ff67018d163b" + }, + { + "public": "02c5869f3c309df56096f408d21e1acf11b882b79a6f15bd4e0e0a944db0f95b1f", + "secret": "24182b54ec7fb735d6aad6f841486a2c19418715787854457cd3e38a755cc7cf" + }, + { + "public": "037013990356d77cddfe95553672ec3489f5ebe5b1b3d7299d05b13b3ed4e01aa0", + "secret": "9fc9bbf52ba96f20f69ecc3bd08deef45545f07b6dadc33ddfaf0726fbef4a76" + }, + { + "public": "029af59af633fa5c968595507fa84359c3b6f3f66fcc16f38247fe65daefe68cdc", + "secret": "e1669d5653aa217278fd688c745c22035739122f6ee693f14769aa42081ee41e" + }, + { + "public": "03fa9353ccf2b4ad38a7b59a9578d048bed7069aebe4651d9d730449ff4cb003ce", + "secret": "a21c73d4ed3c8e7597fd33cae453296c31caf4efe94d97059e82bf71acbe27ab" + }, + { + "public": "0367eab4da5dfecda9a2ba9067fe5fde358712aeff935db7a8a226d6ef75a0668b", + "secret": "7f9f4eef75892f0b21871f650eb94ca2d69865df459845592f7dfddfa42512ae" + }, + { + "public": "038cc3aa0102934236ea55b71d46ae35acb6dce82f0eee1031bc94986f91346732", + "secret": "b99caac70ea985ce85cd922f6e2557653ef4cbff404892eba6847fb8f8084857" + }, + { + "public": "031e91a8d59215d8cb367d53c3b807809008e11d3b5c378bc08911994f3ea9ca42", + "secret": "4bda9614a5031f0953af8c14151efa22f53a3fd97e34092b0503dcf7c87e17f7" + }, + { + "public": "035d846f1318796c623e6e2d8b41af95a37491fa0e1730d67163421ae952e98d18", + "secret": "c212f3c5cd51122c827076af43d7d6f44c76e4c4088a6b53a5f8166f19baa779" + }, + { + "public": "03a89536e27eb2c98fdcc77abdba971b323cb0b1552929b540950177067a90beb1", + "secret": "a286750f88a724d12d0c5810c559bd12d6058651e10b35d7264f98561335392a" + }, + { + "public": "03d155335fa57bcef8ea4e139a6b8dee10935298d93cb03054ec295bf4f8df2746", + "secret": "c19e423957fff1504735c924713e2ae40ecfe1f43f24a7b6c271cd3968079c35" + }, + { + "public": "025215514bcd97b94affe23aa4d00bfeeda56d446a6f9b162512faa3f7fafc75b4", + "secret": "9bc192a4888d51f2f3cb8420f62558ef09752251e657d4e72a83e4fccb6d82fc" + }, + { + "public": "02c8b55ffaab5ca56909450b1929939aa4d0587cbe1d92c1f629183011ade8171c", + "secret": "4a5f07b68ade18a968b4f8c96e066a3fc80725881a1380be951125952644240e" + }, + { + "public": "0366094633f8e241bf4cf2eadcae2276715489be91c08260a0eeb6595328b813fc", + "secret": "9d7b59c2ae0f969c89080ba7fbd7d5152ea9d5e8f02def48e5a48ce0c4d6b616" + }, + { + "public": "03d418a480d778c8efca66d558afbc7f0a321a682899da7d2436c2f47ce29027b7", + "secret": "c5a44d4a367607356d13adb4c071c1d821e6f741ea480a6ab300677091173ced" + }, + { + "public": "0322ca0229bc2e4cc6df1a49b19adbb935fa5effb7bb96a79b9a5f2e26d435d3cc", + "secret": "cc294a6f76a01f30377c779ed153da023511548d0917e98651a082145512e550" + }, + { + "public": "037eb505d95f221504185d800d6c051756337784e508777feae1d772d97ee7fe36", + "secret": "ce95ed6ffc07f1444f1db87865a64fb12d752eac7f4e5a83f4cf80a807dabff1" + }, + { + "public": "02cf8454d9d092166db8806c01c9b5c1889964343c522ce52f11477380eccb4f03", + "secret": "f3c70f5b7243eb6cb8f68376a6ef026ad3bbef04dc0f3262d25621cee4dac1b6" + }, + { + "public": "027e5cbc3b7824ed15e3452c1767e923f17c38cc07d98ae81279b3f442767408c3", + "secret": "f6fe97ce9fc87ad979b3353a694e2d63ee7ebeb0c14a9a9d56385921cf58db8d" + }, + { + "public": "03681dc8fef2c38df5b29cf90800eecf2c1de362e7373cee550b3dfdfec8dfb2fa", + "secret": "9c64b224a0abe8fc2d5632b8594018fac5b3c7b98088a97536d3dd969b2fbf3f" + }, + { + "public": "03178f4f5d96bc852b1ca98f7b1c78456901a9397abd358981f5d5855bc776fdf0", + "secret": "32fdfcc9e3f05fc41f6815bfa0bb3946eb3a8a216fbccdbf8cfbf322d620b0de" + }, + { + "public": "0288a6c657264c20fec3aa730410b7f935dbadfd5d056b901e02338626457e6cdc", + "secret": "acb1453ca6950f663d59391f2335c43d021b25702d92ef51901e6a4f7c9528c6" + }, + { + "public": "0314ec878fb5291ad96a5a63ed76a5631f1a87d2127160187526fa0f89debf8069", + "secret": "eabf297d76c5d242c99f037bef8ecef9fada46a29169f1daa9a52f1c6ec07b49" + }, + { + "public": "021d6ba3d1a2dff91c82e6e8126076081a8a046cfb5342e975271b48615e1ce3a2", + "secret": "ba9dd14c8db2352adc1cb617250d0ec8487db391cedb5bb13e57b3e5b053ff39" + }, + { + "public": "02a9f40d8a3a9245043e4e8887a623f2da301238801a596057595763d3d7c572d0", + "secret": "aaea654c95217137c7b2e3dbde2b1e50e2df7e218f8658ee0bce2a3ffb0bdf64" + }, + { + "public": "02f19c35bc1c9c75c8bc52fdd05064c7e92de14dbdb1bbe141943527d4a5034c86", + "secret": "40330a50b9981e22c9722b0ea660470689dc293b8773b701fb729ba6a9cabab6" + }, + { + "public": "034442c7ade83e99556b1acd8c54cb4ee06a0cabc30750b3118a28836be5ec5432", + "secret": "00e9370af7d8ac8e1fa8bb207f2961d48cb4ced48d399daacf2d5b5c9d10e3ed" + }, + { + "public": "039569a5ed2f2d0443106cc0d43d96afdaf04fc21192977cfae89c92a733344151", + "secret": "e51ec8b93ccbdbeb3de904c847d012daf4f4f5ed0d88d39b2f7c90a4490aea16" + }, + { + "public": "021aa508c5b259f6e59f96e1b95843a5f14dcb4592f5d39b826e1d8d21d0dbab6a", + "secret": "3f495caf7a240e5c374cf5094fc80114a3e648254d460d0217baeb8dfdfbbf3b" + }, + { + "public": "03413f39e0aabf71e2ff19e645e50711e389a7372079d340a2e6e6eca568d244d9", + "secret": "90c8b0f3f6a55f41fdc7b5a2edca07ce58369ee98d380b200390af21c53be84f" + }, + { + "public": "036403c8cd68fb89e22bb3ff587250edf1d8aeeb3f70815e02574a6520fc0b99ff", + "secret": "e7a68f6d002734a8488fca43bf7ef960712556d5c61c84a3c2a0b5178509fcad" + }, + { + "public": "0371ada899f5c153bac46c095b950ef01e682a5c027e9c2ab47aa68e7acb35c9f8", + "secret": "93bf75f49d95ad0d8c8c0d85c02bd719fdd80e46ee88373f47728fe0d6827c60" + }, + { + "public": "03319eaf44cb8798d0317c2686bcfbc24f379f4ffcf391e36b7b74085a572eb471", + "secret": "bd80e617dcb56d412f240665be17297827278dc0a7d5a93535209bea11c64338" + }, + { + "public": "03c162d4d901a979c8ef10a2cc929cfeeec17e2ce2a17b31bef11fc0b1b219d635", + "secret": "e4c0df129fae550a1432d894f83577abb56f21ee7e7827caca6aa953bcffe9a0" + }, + { + "public": "029a52c2cae38f91ba463dc1c890406bbbfd742d9d35331dc1a5c7d71b01a935ef", + "secret": "347e1ced09eb386584091859195d084bcc0419abca0a558f52eb58561a633341" + }, + { + "public": "02645995ac9bc769d977593083ef0d295c88d3ebbd01116b7d3ccc5fb3e97ad9ec", + "secret": "f66e5f6d0f5177d98b30a1e13da3c61324eb42a2cf5f1e055efd5b53e3b06836" + }, + { + "public": "03ffd6ae62195f42a34b6c13d563d9b15ca7038357145bae14c1b8769fbe181868", + "secret": "1683765be0926c3d7fffcea40fd17d7b74d55c2b296f2be38b04eeb8a5f2c1d5" + }, + { + "public": "03e9b0f0b8157a3716c78b9b55da2656fce8cf683e4930034920779632b4768c5e", + "secret": "27a0a68e1248e4e71612234e0f5ebd9a6eff7206f065f1491d05d12de037b2b3" + }, + { + "public": "039bc48404275297c5b3163a8a0b12594d2db067b874c7d44732089de357d89c6a", + "secret": "ce3603e7f567f65aafe5d2d18fcd75363639e088e58fbbfa4e706a7e75aa7ae9" + }, + { + "public": "0318e6704e0b335b691c37a07ae1137b35c27de78ed2b9c5b932ca8c1fe7962c0e", + "secret": "0849eef00cfd6e9bf4100628bf4753acee1f5001a9c3f70b8a7a3caffad4f22a" + }, + { + "public": "03ef1f44e9c25b9c0fad95c5e6daaa956e7495662ba5d10490066fbe96fa14cb27", + "secret": "b6c157b710c54a24883be891727321ec67330da5d2eed080b70e7a3695a8ec61" + }, + { + "public": "02662b846f34b43ed865644eea7648e537a924cbbc2841644459526fa90794ed7a", + "secret": "1a0a81c90bbf3e1f93d9f63424e672f0b578a9050a70725fbad07cdc1ae71392" + }, + { + "public": "02c399b5c9c19229136b7a3b4ec724b95cd63b9eab5962d8f1ac5258aa3335b112", + "secret": "d7f2f13eae3b4b37c15fb33b752c1b11bc1c8a2fdf72e6557563e350ecc0f0c0" + }, + { + "public": "039ae757a337f58c6d4f4ded2fb78d7f4ff054224786fee821a1793cd1fb17124b", + "secret": "f8fdd55f46335c391a179af0d0e5e6a4ab0db6801296fb6491b2ea69bbcb73fa" + }, + { + "public": "03b69fac4b3a303b0e5b6ebeb707a04c9490f546db8174020a0debbd6dc3e5fc3d", + "secret": "2cf4dcc374e5b8550098915bdcab4867ae82640f2e724a0ed612eaba8103a62b" + }, + { + "public": "02ba5f2a9dd19c8d23a918036bfe7794a073d4edefa26d79cdd42418a20e54d48e", + "secret": "cb43f8da85d4ab967be28d8987621338af82fbce72210fe123abe9152f237585" + }, + { + "public": "035a20ccca67b2a04fafdc4e627c5faa30683359a8214ae4551234d8610e51f035", + "secret": "13af535bea92b53d481cc4d1ac9fd2a6a032925c56bff5ff0a30cfed8e8c74c4" + }, + { + "public": "03b82d4372ae5b3bb7073d1cb9aeef279df0f9612bf8e0f8ea9642748c4539389e", + "secret": "57a8e301c388b32a19fdc9de863daf28fbf113280fa43a4329e8f055b84b1c9a" + }, + { + "public": "038a293cfc29c01e6c29f04e6d470976ce38b4c844a1ae6754cfc295b374f79b46", + "secret": "80bff2902ce1d84956c5138ded00478d1b8c4696ee33df10deb48efd8ad4769b" + }, + { + "public": "03dc283ff705a15c3507690bd52654c3c6fb7c025855906f3b4da52bcd423d9717", + "secret": "640419d7f064115041d3c9ca1de8d736355c7163104d8f107faa8e90fb71ec44" + }, + { + "public": "03d84c6397fa2b8704701cec2b6edf3d734e8e1c9bfa74ae5bd3ac26df0dab2d33", + "secret": "f35c10a69e25cc99ad33461f556f479ab63d9602d469a13dcb20aa37e39b5cc6" + }, + { + "public": "030c6ff1da272c92d99cdd73a9677c913c4ace1400e94c22d96ab348ba0975f45a", + "secret": "79062b54854f7ae03f50ded9dd4519b82dfab46609f082db898031ad96dad9f2" + }, + { + "public": "03b05feb3c56cdbff2830a3d200d56464143d76e36b53959566c43a84ce58b09d0", + "secret": "2cdb7bd144d54bbe4966cd2d285b81733d5a5d15747d7ccaac059716fda48ffa" + }, + { + "public": "02f1a6e5895c67f1f929eaca6bb6d63216f98178c75b5dd950a673c0c893ee3561", + "secret": "64541439a1d4889f33ecd2ffe93b7b9f9ef81faf4ffcf06d03998f189c8f8042" + }, + { + "public": "026fa46c6a361c5bf4946190b0d982ced53bf926ec2136d6fdbfb4750198ce0c2f", + "secret": "891cacde96211f0b70efa8c84fc253ba2dbf0280fa02619b8816b58bdadd3be3" + }, + { + "public": "035e86334cf8f455dc6760b1ab7dcc5cfb79c6187a7f0faf8b439a15013550c0af", + "secret": "7e64e1c40e413f8038a84b077843a8d3f092b05ffd573ff5eab8ba3dde57ac6c" + }, + { + "public": "037fc066b8e6134abc5aae9f46a20e9f2e48b7b3f1af8fb009907dc1a7796405b2", + "secret": "4bea2618485f906b3e0055b6ebedcff5c3908191a3ce6bce2e84710537c7e8cc" + }, + { + "public": "03bcea9c85ba75cb6ab32b957abdcbf80e02cb400cb496bb250a20a49bb578e2d4", + "secret": "c23bfb63ff711f0e0bad2f18776ccb8ea847519fc0b2bd7e1a640b593ad02d8c" + }, + { + "public": "036685764c25a4b248f63f09bf3fcd4e54551b10aa63a01bcca89f233aca812547", + "secret": "c7a79817d7ca0449326e76c82ed80f87b9d2826e80f5c806e1060b6ee8bacfed" + }, + { + "public": "024df9aab08d8c8aa7f62f3eeb9a874013744f2f7ab9646e63bfd5c67fe6dbd27a", + "secret": "de3e77755e133c72207cd76e8df4bf860a21777fa64181475eafcdf17ff1305e" + }, + { + "public": "031c90a518b738065ad07f0a4b15c28b439a6d97b8ce80d14700b19bdc411d7ed8", + "secret": "9fd45c0127e2b1cf3fc8ebfac57bd5389f0ff3e1ccce1b87dbc77d2773b120c6" + }, + { + "public": "022641517e31ef3e6fd8bc668a291d5606457edf9924d6c8a2d283063537db2def", + "secret": "e04855c24c9a7d8e1fd29a28ff98d43868cd8d325625e4e5ea301c15148b9bc4" + }, + { + "public": "034187a061b91f3c27f39e79e4ce7387bc4cde5805ae03b76697724884a184ddef", + "secret": "257df91cd76c11ee5fa713b4bcce19cddd4f1795b519ebf7235713c390745b81" + }, + { + "public": "0310d7016b936d2fdf05154b0de21baf174d36c8089c121ba41329bd9c8d7766ed", + "secret": "4abfff2bef18f0d02b8fdc267542006cc21d3e53c785ede4eb7ae45446447d1b" + }, + { + "public": "03023b3606d96a9c4069892bc4a7ec22e714ab481d989a194ee69cfe7f2a0e9fc2", + "secret": "8abcdd0c7bbc7c0304774f5ceeea8c0d62c54cf2567f8f12df39d997e66ced58" + }, + { + "public": "035dda27ad9896ab126b335c6bcc37d5f8b0ec33681678425459dd89ac40a68cb5", + "secret": "63e81539635eef5ec61e931cc5cfaccfc35033b926c8a381e9b9b5df980c975b" + }, + { + "public": "021e96330e247dd7590a1c6fdfbee30ba10936be27409df8ac40162d9f01dc9b1c", + "secret": "fb775db0e539b3194e801bd83a1c072a11da5b9a78ff1cd8e15e3157597839ce" + }, + { + "public": "0281a17a338318d32c96b4939de905fa07fdf16b41425e1c4668bc66ffcc6e4f8a", + "secret": "f424302468cf154b91d3acb74d1c134653dd015a0f9f1e9edffe71ef02a72f81" + }, + { + "public": "0316a8f143c8136bf47c331b05105a898183f6b8f718f8f16e0090afb9cc11de06", + "secret": "f2a8bd6ff9cf03ae02b4d4e3628ecd25c8b0ab4dc2ed84f49ba8e2cee15f7326" + }, + { + "public": "02d13eb98566c22a73664167203545b26273c48e08e7ffc97208f7b0f296683992", + "secret": "4e283c72c46098a23549112745a1d01964065d7972e17d12e250072d40e2d830" + }, + { + "public": "0324cc05ea94cf4130e9071487ad4b3c8696f5b871a252714d00cc8a3f9eab12ba", + "secret": "7ce9e71cee76d18b9d6f8bb4ac0637518b357114c4ae57c2b0efbe0830fa76f6" + }, + { + "public": "03e3d31458a1ff3bdc9122445301ee3c471949944771dca3f29c8f5e83610aafb6", + "secret": "b5044203d14903f9b1ee9f07047e155ff0049683b6c17752782b3d3b4ffb1672" + }, + { + "public": "03b8a1559f63e7c8267f860c7d5a437468c982bb9661345121d3eac401d881c893", + "secret": "023b7804e840e74e60a7f8928a17264348418ff8e7f39822f6365010212f3516" + }, + { + "public": "02018035261b11ac19d1ca0e02bfb1ff84c4e28f482d3e73bf84289777ab3a413e", + "secret": "2eecbddf300877fedeb37d9f9a45e80f7d197d570288db35bf556b95abbb90fa" + }, + { + "public": "0365b09a11b7b415ec2dab8414b6dba8dd38f39c199f0a60f94614d40233e81641", + "secret": "5a8bf0dc70514dabd1e9d05bbb8bc653d85dd6e6488da17601395af9703bd66d" + }, + { + "public": "02ce8794964a862827ca664c8dfa89561b0c170657affbb11314e9c7d5541e9010", + "secret": "089371f177096466ab45e3eb759c47fa1253a640f6c83108304582624b53fae5" + }, + { + "public": "037639454a4d562e2e9f2dfb257f378d1dfd3157b19c6bee0d13e7a9e9b434bb28", + "secret": "2153ea4e503da01392ab38f8645cf445e7c2fd67a8e1629e3724dc9046af5c6c" + }, + { + "public": "02c37a0c384514521174ccef6f49db2fdbf7779ecaf9afb9b5915af223901f4a19", + "secret": "485b00997884d4dac342b0fb357a324c9bf3ba4d8c1cf0faacdfdb7d53ea50d5" + }, + { + "public": "03df2e3a82c41252b8f3eaf36271faaa8046da4c2afca3997315105bf946f9ad4d", + "secret": "7f16469b3d590e36618d653caa9de9bc88907b9bad2e2f1bb895894b2160d55d" + }, + { + "public": "03d05cf435bbb9aa47051350577012464a4c56af1d53abe05cff393826058c9b59", + "secret": "762619ea7dd01115d22920076fa1514cb48180e59a09995b7bc3a1706d4c38b7" + }, + { + "public": "031898107a97b1b8955a05b809f7beb6dc83d89e4098e7e2baeee89e58067c3c37", + "secret": "4c963ed553bbeb42e9a81416a58d7ac462d95d11f640c61f78c5ef7149660453" + }, + { + "public": "02ad77d74fb04dae512ad515acfbc3de01615f51280778a3f99bfca82281842a77", + "secret": "ebb5c7ba498cd56105e81eb3aed91e31d1df68b04eff58361cb58ebe4d7bb575" + }, + { + "public": "02322c38c5ed799b00c7c53ac45e2f175aa3a054d7a9ce153cc9774db3fd061a62", + "secret": "cbf4918909deec40c7625446c46da6181c15979b7d10d5e3cc5aa9a68c94571d" + }, + { + "public": "02ae8af9f677c6e145b38bb5a2474211c8fc7beed08d936a80900c1f05656a0325", + "secret": "c47bb08853f4fc3f9b59c1787edec7f38e469c54a9fea9bc6c7de24a6d0527ae" + }, + { + "public": "03d4cb54f3cbca657bcd22a20592ebd3c05d509bb23d6781df330dcc19645ec7d8", + "secret": "89b8d1f151c997b61dd14db49222e65ce2d4c308ba3ffbef1d6c386fb87e012a" + }, + { + "public": "0221a39d27104cde35adb03b91b321213e2a168c0bab56e3de1b7db33c92980a5b", + "secret": "f36e4fc7ea52c00226ba5d18c7df70aa26b8bebae037cdbaa4d577ae2e27f67c" + }, + { + "public": "02ca49be7e55768918409f7ab13a733ed8cbf9ca1af07d7eec56182f1deba7479d", + "secret": "25e6856c4c136f3cb7598fe1dec3be0560350ebff4382d677c0cd64b4f599a31" + }, + { + "public": "029f5774ae34d1cde91f75d95f69f5e63a5d2c06eee8e410159c9740ddc7908613", + "secret": "594bdc0293e51d51a6cfdeac68d1a546d357965990c215aec5652416893f8e95" + }, + { + "public": "02f0544d242633678897541b58cd76808a62197a49a2e724ac952e1820a3568806", + "secret": "a673c0e2a32bae6e8db64a9c7f5d7f67ced1e098971bd565eea2b9a991143fdb" + }, + { + "public": "0390d6f70c7168122afe96466d182584b48325b432da5a164c2d9b6e8e183e1312", + "secret": "44a9b399339e8acca1b0e3097ed61fd8139b0e8f9f4e6763f4136d5e8f921d76" + }, + { + "public": "0238a50e53d7a800d4bdbbdd7258650129bd74ad824b5f1d62451de6610df930df", + "secret": "2f246af52c80bf9edfba5b2a00944861cd5ddde02aadbcf8f775a59ac0209cbb" + }, + { + "public": "032add8143960413f19ebb601748ea23cba5cd21187d6ab6d9d7109a95f6c34fb9", + "secret": "127e1a83e2d30bd30502f91e862e8ee996bb18f0f5059ba1893fbad2248a2a5f" + }, + { + "public": "02fbacead3f2f33b68d8846204f42a318e415c2146fe730757116f70e3935b8ca2", + "secret": "0093a7161219c4e0ca92c547c240ead06f42f40e1dd48aec8254a5f440563800" + }, + { + "public": "02cdbdff5009ecec7de0c9bec2059feff645d00be0b1c53a287ea9bf01a8626860", + "secret": "7d892deb6002a5e399bdd3265e1e5a6b25040ed46bbbb982f52b666a2aa40b86" + }, + { + "public": "02531e5b7ab16d9f4584c1539c0d3457c223562eaf65a673b35e50a1df25391855", + "secret": "60fb83a9c35a821693e851acf2d28c22e5c9326239b2f529d5dae6fb809c963c" + }, + { + "public": "02613e1052116f71714f15b8f06e35235e914b86e28b52193ed5e18f4c9f4b348a", + "secret": "7deb96d8013cbc4c61e8ea4a6622873de419bf3e14bc468df205ae6349a38d06" + }, + { + "public": "02b7a861c3766e251cb76824873f3ab059b61ce33ca2e7a947781fbe8ed8d05818", + "secret": "000ca2e445d65d6266696247865412670f86a9799e5dc3d32d85a7febd1fae1d" + }, + { + "public": "0294d942df66f3cc0548dc8f0e26d1f8a8e06d2d3bdf193be3fa27d627bdcf878a", + "secret": "d9c77fba6408c7c3d14725835c9516275a22f722d11a2ed977ad423b3ff8ef20" + }, + { + "public": "02ca1e42331a1404e0a3fd8deeb2b456ac91f48bb21f2d90317c7bcb9799feb79b", + "secret": "8f8976da7a8ce04696e26e1ebd9a2fd8b0aef2fd766bb6dddb3a15e9f190cb78" + }, + { + "public": "02acd07aca27bef56673837dfa5f4a3c7bf2a62f368381aba491cd01f449df0d08", + "secret": "db571922922024f3e32ae6b7addbe21952f949e084ea28d075a444e949ec3845" + }, + { + "public": "027a8f49d1eaeb1f1101d99ce90655c833fab34262a09b2c6a7e40550df9a273cd", + "secret": "76b34c1cfc2a38de5e2d8880e03bf6c06a2c36401a3168dbbcab40f7d285c6b5" + }, + { + "public": "026ada92df18c4fc501d396368ac3727a5cfb64485b2bd9cdcdb36f23fa2334f96", + "secret": "da026343486fa5e0d49b56aa4388bf7e4a36af7a9bbf0fccdb496e2758e6e5b4" + }, + { + "public": "0284e4e753f36b0c970bdb1b0d01afe8c65329f5d2db39561df5dd5a0718fd54d8", + "secret": "1c7aad6e1660c65d3ab44cf3ee57e66547aa00d8e5ac8226eab89982e618dcf4" + }, + { + "public": "03da1b2d423b003e51d2e30262668bd8f79bee79d9367e0d58620fbb9ba18e1fb7", + "secret": "f2ec7336a0f96a6339186f7cc55dab56e1e5f2b0175d5cb2f17e5b6778d2fdfe" + }, + { + "public": "023d961a5528be4d2bb021c6e77629cfd1d137182c7fcaa159ee69cc73a1571830", + "secret": "c42912ecf9f3f0ae660766d9e73e68152bdf27a0bbc7dd80edd31f11bb229b20" + }, + { + "public": "03d4ccd2f392e0692e8cfe2ff932ce55944a4e74411943d7657956ca15a84e50ef", + "secret": "516d1bd7a32cbd31bd19d3d3a9738554279b13874061456b60d6c9d826161211" + }, + { + "public": "0250f86265a0e43a829936167674d35f48799e72a5acafe0f8fb23275a77fb4e6e", + "secret": "2f4b03d384d2e42f28823f4829baad23bfc8c39d4652e54812c0f298f1df52c4" + }, + { + "public": "0333137ae3809bb90bcce4522d46d2cbd59d00ed356adff2f4cab5a998c2b5d063", + "secret": "31a34aa4f1183dab48c61caadeba3a7b615222c333a089cdc1c87461c3f9a0b6" + }, + { + "public": "02a1d5ca53fda4356302ea7485243a93e4e24eeaca70d3bf10ad2589b8d1edaa90", + "secret": "1a109c7078757b85270cb1bf8db42c0cd784832215fd2d4e4572e96c094dc712" + }, + { + "public": "0365d5a61964d9ce78ee27c3ddcf061ed748984e7715800ab246740a79448a82e9", + "secret": "19df40e18a9b48a019e3197179f94ff4c09b6ee53a70960bf16fb35c29bb3089" + }, + { + "public": "02c4e4b67f356fc8b34995c425c54c041a9360de2243ac3fb0115529b88db51891", + "secret": "e1f5a89933b8836d4300eb090b130db5d9c58d73f8368f3425f8bbe3195ce5dc" + }, + { + "public": "0212ef0c9f959a47f54dd4ff00a6465d0bb21d227d9914fcec2bd55a4c86da787e", + "secret": "e80ef8dbf68a194288820a18e07e5e241856c4307c39803234f9351d0bba8e2f" + }, + { + "public": "03481517ca5f96d27cbfc0d5b336d9ccb5d4662ec30e97ec1ee9d6ee8610bcd30f", + "secret": "0a9d44969f267fbe5bb24f1b8f1f2586e336f73a0f051e2e328ae1c055e42b48" + }, + { + "public": "03d81bc22a85aec3c759f91e0fb14000e053c1c79c04b645f804d6d4af3094bf5c", + "secret": "7153a7c8d753792cfb068c377f145539ea8b19aedb37e12db5230c8c925cc92d" + }, + { + "public": "020e0a135582ed752f3dec3f196e5af792f16d028b8edcd7b92167a09fe996a3d7", + "secret": "2bb6a4d7442b4a2c1fca6a06a7fb1880a1a193da022d7a68d78bf48dad80625d" + }, + { + "public": "03f6120bd65ac1a01e82e1c3019eccf7b575358b103ce9989313d6aeb98da197ee", + "secret": "041267a8d8718022d2422a0500620dbd96cecdef634d88ec2b4d8946cfe254d7" + }, + { + "public": "022d47a4ed9fa5e7198338ad8cff333ee2d4fe647f9d6e5bbbb79424d5b69eca70", + "secret": "b2cfc5e140d97320a3d38daa11549ef64fc588528d767d8dae5832fff45257e4" + }, + { + "public": "038bb22711e6c3ec43234ec1c43348d000c04e8cee2305143e2228a0134cf218b9", + "secret": "5f3d291eeaaaf2ccc7186c34d94cf95f74d9377460ecdb97d37bc256120fc880" + }, + { + "public": "02c89a0915eb202b330dbf8a5269112b3cf8df1b7067a377c9263d1718f8b07dab", + "secret": "cddabe7a94e9612791f99c43242284bc7e7aafa15b119718263e8d8d39924eeb" + }, + { + "public": "02c86849e4d589220b1afde3cf93aa8e44a6fe98e1667ea3128dd04371b7b03375", + "secret": "6b21ac71bd246245dabe127843c1107013e57eb075ce618909e6c8ae1a8979d1" + }, + { + "public": "03ac7ae8b943c8123f59313c00bede40ccbe4a34fe2cda42925100bd61492f90d9", + "secret": "39bbb4542c906aa797a96cf76cceeeb64a37d998fe87c2b604e671b71046c978" + }, + { + "public": "020781d8434d3c343a9abaaeb8ebebd4b3d38fca7004856189ef0c8ea1d2cfb318", + "secret": "ee2d1c29cf5c1b419938797dd5fe277c4a8c8faf267c1dbf5270776f8bd9884e" + }, + { + "public": "037321f25bf6e7dfd711c3d389d41c3e4b5f5ac280707ed54db8780402c8b0638a", + "secret": "addbc334294266c2ba2637f0e80080599744761b7aa3cc59fb2e3bbfd8ae17d1" + }, + { + "public": "030746addc962c772fa1e271ace7cd875bdcc2cdf80fff3cb1df1de4aa1647ab92", + "secret": "9490dde430ece02ccac2cc07b24a850ea29d009d9af3a72d30725e8818a6b461" + }, + { + "public": "02208bf3c632bbd1696f4a2e3954ef65919900d615a5f95750d6c72496147e0dbc", + "secret": "230d5c5bc9217b166c32f65691567b694ae952ba521eb9b3e9a936e74d392c71" + }, + { + "public": "02ce7e22448a0870afdca6e3727fe4acf3fafbfe6dee7bd6112745be1a0e3eb214", + "secret": "9829307c02964d68689ff5701a60dff4776fc2697f1dd16689a80f1c771d293d" + }, + { + "public": "039dab9980e189a7fa4e7a644b02a24c30875bd697c2e4d20d9cd13ecdfaac011c", + "secret": "fadc27fc5c0c4d1f6adafcfa919f3816d9c0cac5fd1f3d1422ece65a3c7d37c5" + }, + { + "public": "02e5ab7edadc71d30c3cc1c89953ce57ef5b85315e04376bbace196cfdc59eb237", + "secret": "cd2256b706dea2c1a9af344e7e11b9916162eb0700ed08ff01a89d5956c4acb5" + }, + { + "public": "0335d2a137f80e64e31fcfa3e6632a5cba28df02fe4c8e0e833783d5eb8e87d821", + "secret": "9ec021b6359abde85c20837174c34dec0d887e98b470719eb3c8188580394907" + }, + { + "public": "0371379ad86024642981706c29981b0cea3b33ef53a4ce335cbceb7865962ea5e3", + "secret": "75404cc6cd4357c50c00a665e0c57a1bfac930a568328951d9a24a820516041e" + }, + { + "public": "02814f5e4a826a713cffaafe579fff6f6d6706817ce02ace266b69acb614f28a95", + "secret": "6d1f7a494ded6b61e3db7917b3799312164bc110bb877db36d2662865e86cd92" + }, + { + "public": "02e4671fd72397cba3dda14b5e332421cfff2a613592a78b30604e617011114257", + "secret": "56277c6b50f0b3cf7cc3e27f2b8e49be99719f0f92859407dc58c8df9f02ec4e" + }, + { + "public": "036f37506b331f1131e4a123a18e23f76c13a2cebb58dfdbbe14a62ac027fb3640", + "secret": "2690382500a731dc2f8a75953aef580f29d49d154c28ee2f12654bb298f4dd9b" + }, + { + "public": "029a72834f0be3d6004483e8914e2c831252b219affe3bcf67498cd326abccbb92", + "secret": "ebe754c28d2bd009da817e87d5d1d2aabe3c0b31d83d6406efae31068d6e4ba4" + }, + { + "public": "036dbb5d21b6d127f06c01aa81b17c78020f327c7e4c656b801c10fc03ed771a22", + "secret": "0951716ebd6987a3ffed61765e923e2261ba5028f9f57d07ac185a1b4ece95ea" + }, + { + "public": "026c20ed6702e87a0e148e50b489fb7c24be79c6a86e6061f54932c4d8d0d805cd", + "secret": "44590f68415717a5a722c046e74079c94453338ff219925383cd69bb38dec152" + }, + { + "public": "0368f158c78db6fee324bde3f0d0ceb7214ce8f8716795f36b239952b2fc0459d2", + "secret": "9dd8c46d7d9296ece9c65b553a1c1b0dd8917f77238a4030afbfb06ae9b5d530" + }, + { + "public": "0254cd2e8bdb11c84e887323fe696c0fdcbcacbfd42705b41d6dcb98baa66eac42", + "secret": "41cc63d46c9903c4a98d780825afd522c64990eda08149e8c4ced9ea93294b73" + }, + { + "public": "0217cb549059f2df69953a4f4e96da2129c3406ed70f4d87d4b66ac58576521ee8", + "secret": "2b7d8cd7d369f5c77ac432faaa4a1a6e58581abaf9df7ee47537916761621d69" + }, + { + "public": "03e91cadb0277cbca2f5492148ec06ce158c945381bf21deb79456912399b8f80f", + "secret": "5e47064a951ac96990fbf0d9b6cdf7f8435ddaa126d8ae91c4e47079448173fc" + }, + { + "public": "02b6dfc3336617876e514ca28f5bc037b7a5b7d1c7ee51391eadc2b87b8e3054ed", + "secret": "fd110f9adf35f43e253b1f17e47243b33ea589f51afb6516570648d22ca1d706" + }, + { + "public": "026a9d3c5cfa3244dab529bcd88ef639bbee20e90579ba350f18fc118bf89c2321", + "secret": "1777326024e19eee0ca7bb977c98558f550b39714df0e1624dd0b061bd992964" + }, + { + "public": "0251d8fdcc544f502bc00dcd0b65701fc032b3e6b87b63de538ea53ae7a383656d", + "secret": "592e88935b01b96405d94d04ccf6267f72357f4dda71503f88895aa77d437209" + }, + { + "public": "039cd7f451bdf706b2407a28128f6edac102d3312ab3b5c3ee55a9be85898176a8", + "secret": "d25567e20c236d3912b03343915e443dded960df4eb70bdba1e392dafb26c672" + }, + { + "public": "0384203ec4bac2107842209e170e1535335a94f574e7282ae6505aa3554005b104", + "secret": "965dfa8f09e76afd4867d27fe5ba7c9d79b44e477c4144ff8b9150d9f57632a6" + }, + { + "public": "036a7ecc0c247df28ab0a7accae48373f5f9aeabdd0f154b618c22c86fa65fc4db", + "secret": "5bc79eb9e82d280c1ded48ed38554253fb7ea04d6339a049b6c1650ea6f40f1b" + }, + { + "public": "038b58f310b82e88456d7dfea7c0c392b38715f91e6fae89eec817df4e3437f778", + "secret": "17fe86898db79b929d3ca5b7a055984a214223a8daf0fe8c3d8232fc800d9a09" + }, + { + "public": "03f9052d75a63c8532d14994ce81dad480136faf94813b47ace54e38e08fc97b54", + "secret": "4cdf3c2aa3422c0ce8f81f781e414b24aff8a246910ac826cf652253367b1f31" + }, + { + "public": "039e9b541bda0fd9dffd3edd59ddcf269155e04f6954f0f6bb21db16703f08f130", + "secret": "a87c4ee23fab65752c55c19a2a3e43a420a4db66663d6e8df8b5aa34f7cb12e4" + }, + { + "public": "02134a3b0ba8899b4d09d314d472e9b6c7ba1d5722ed031e35536fdfb84234a5fd", + "secret": "1c6438db84b952320d3634a2375ebf5bfa77ea1c43f8f9d12d69d06798e5eabf" + }, + { + "public": "030a0df6c44ff76c7a8165c9b287fb532fe736d6bc61c27f5ce2ef6c2f9bccd6b9", + "secret": "dfcc8fc735d49a97984c9117246044a60e845a92271bbf30dbaa391b58f549f2" + }, + { + "public": "020eca1f6b62ae5aac6171ff29a6070748e485b2bb2e3c46b17bec9b0cbc642ae6", + "secret": "349020832833d2c90fad971a4f12559f193b326b5c9f3397adb73fc6dbbb9d46" + }, + { + "public": "02068db4b2e7060f27edbe221a0974a54a9b29e198edfbb780ed2a410a2fbb2de8", + "secret": "9c0cb4595a9f89003e24e69369cb0491050926b43a01aa4735324dcf58b577bf" + }, + { + "public": "036c7c2d40d1ff2e4d68f345ec00992e9808c58be98cf91445c257e7fb9dc96f64", + "secret": "d72afeb789d1cec33c7f24e15158ee3c514769313d90937473fba81cf5793dc0" + }, + { + "public": "03558ad1cac71506255ed9cc7b0a3dc0909004ef23c944bb56ef40a4bddda025ce", + "secret": "c4dab8a22e7bb63f4f8e68c2b0236df52f222f3b819e9a6fdef985b774b8446f" + }, + { + "public": "03e7d2614e71ab78d808c6244f4f25415d9fb64964a6599e24364c84a03cee94ff", + "secret": "ea31108e32a55a91ee62788e20da3956a2b6010cf4431bf65d57955df0341690" + }, + { + "public": "03b2749206ab7a35f43ddb940d7f34695c8da485345cf9fafb68ef43ab998a96f3", + "secret": "7623c47d93f83485a16e6f8d7a216eeda19f69be9a4fa877f6980a0395e65af2" + }, + { + "public": "021c8d57c032abd27234310dff86464215d06cfb864f8250235447a9e9fd669b93", + "secret": "4b5542c56fea30f19e8abb3ca81963a7c75f29d30cb96e14d362d0c047c4c6f1" + }, + { + "public": "0339e0c2e84582b02cb7d07d054e71086306068f2850af6db7c856f6752e251540", + "secret": "a2c0048e96fb7f6e55dc7246b6b702c7fa029d8b78f7006faa5ddd8833b820f0" + }, + { + "public": "037ce4fd2bfcdd5c8c8d3fae34aca3db3223a208da275f1912f04d1628b00cc964", + "secret": "223ea29215c65b0dabdefb5639476b19133db4a8b831c3dbc73727bc33038f7b" + }, + { + "public": "039abfd07c4120518d322b839f144788512ce9a7ab84aa316573ba23d3eb39c3d7", + "secret": "077c4fc2b2cb0c95eaa7dec5a028d1bc39160f0369caf493499e43ba6936a545" + }, + { + "public": "03627019487c9e847f71ab3e969939881698836d5675e71df113ace548df698eb1", + "secret": "f502e26db7a3d1067c8053edf562ed255255218777b36896295307c5433b445a" + }, + { + "public": "02e3a703e64f579097eacbe0a829bc9fd3631a07ddd2494b47cee8bd18403dfc49", + "secret": "7f2ce44f8f766da1f8396902ffac1e556100870fa8266bfa61e2e1f3ee8651b5" + }, + { + "public": "035bd62828eb1dc765306b76b6cdff08b00ebe5a63da843ac98173a50f57bc567a", + "secret": "c442fe421eb9e09f1e6e82ab1e5b75d9c901d3e18d0229c5f5b7161f6043e447" + }, + { + "public": "0270a03fc149b19b7fec0f05a9e82071fd48cfd1a85cbf560fe1e40aab7bba40be", + "secret": "91c6486fa147a141622d3c99b4e01a22171a8a3aa3ba4777269b2f7514dbba23" + }, + { + "public": "02d119b76952bcecf956b282d88d168d10ffe914708dfc46b46391ab1335b7c0ab", + "secret": "d462da8f1dc6aa09ed03d638a8f85a3b39112e4464b84171ff3d9e4fcfd46bf1" + }, + { + "public": "03b15106387b5538dfdc572a2bf46ad603c0a3154fa755cf6f0dfcea14a1c5ef2a", + "secret": "ed2c28993c895c7b0503b469b5e6a90cb9dcfc27126f61016661a67e0e719c9a" + }, + { + "public": "02464d8e233a25fd3eb8b4109d7e6792a8006a1947cc247bdb1ab0a53cc6eab16a", + "secret": "f74d87eaaba1349c31c4a3aea3c0b8862d2daa128291bedb95b252e005362ea8" + }, + { + "public": "0306f427104a61bcbe5f68053c2b6f0195ea98aed335e47b62c81ece46555e7805", + "secret": "3047a50207d25d0729430f8a94559606c13bce462cff07f18ce584c6660620bb" + }, + { + "public": "03c3ef468449c951a330134a1eb3b8da29f96bdb81aea15e1b44d3f45896c161aa", + "secret": "ac0f2295e74fa9730c7076d4e69bd5723afedcb55d431844547382cceb3ecf3a" + }, + { + "public": "02439933cd7586cadb7ad4775fb5a435d5d2cedf620df0a5e15f56c29d1692e519", + "secret": "8a72326d6926c38655aa08ed76ec887a2a443cda129da10e3dfdc40128d0a55b" + }, + { + "public": "035ee9665cdd8180856f7ad236528728f07c75e5de9546f72422981f3a071ab839", + "secret": "a0d2b3a2282fe673f966fbc2df363aef024d1466c7a758aff6e4e9644275867e" + }, + { + "public": "0304888aa7ccf2c8e7b24b9dad11a4a2b6c6f83bd904a82a1dea7a3c5a518dffc8", + "secret": "cf04150ffbfdcd5a33b3c801ce9ba22a733d76f359106006e2f3df788035514c" + }, + { + "public": "03223e9204a6cd4ddd3c4c1e8c204d64e78c8d922e7b83807d58029f99b9e480c3", + "secret": "97892fdba572720c6a6a26d758f5dad9627b1917b85da9f1103691314c26f6e7" + }, + { + "public": "02c2b249e8556f27ff24bd346db9c2adf0ab0b89d671aaac9ead56990abc974cba", + "secret": "cd190b27794641e035d24aa12a0c52742e45b1f55ee5905fd5d8620f1866d569" + }, + { + "public": "02fae519ae95e19cb8783ccbee45ad0b3d4b3b53ebaa524ad5dec657dfa638519d", + "secret": "4164e38437a786f5b8d48738b01771ec6feab7961061aa3370cb19532578d371" + }, + { + "public": "02f1aba1a25f65e58397f6322c4d719988bd49abf5d8305b519f989b7a03d19126", + "secret": "62245e844fdff31ffcc6fa24f044ed7937e43ecac250d89b443ed7813d3b770f" + }, + { + "public": "023b6d32391a493061f1db3f5f1fa3fb674b31ad5774774a56fbffed357d006139", + "secret": "e96f64cdd4e41444de7e91fc162c79fb230051c0c974ce197a6b89f42f81d57a" + }, + { + "public": "03c9a6fca35bd6595471aa6e46b1e500dbd66e7be270b871e2dc2ab401cde3ef27", + "secret": "865f5862059addaba9292fa41687b2fe9fc88d7cd69e71e14e89f2cfb2aaef57" + }, + { + "public": "02c73d3b8df2fb52c345b24004a35ddf6f57a3d7cdabe9f4b87f7a3a8d1520a5ea", + "secret": "ca23513f36e33ea8e033f48189bd11f823709f2ab17acd7d2b8580e98c501673" + }, + { + "public": "035496862b9f30c9b4da31ccef7071bf26186f685dc5e9c9f72fe52bca8001d8ae", + "secret": "5b388c74773e76c7d14670aa80ed6bf98dfda6578c4fa63aa79b2140ec91e3ef" + }, + { + "public": "021ec4d3e4496fe56bd6cc00122b97ec722787a8c001c0c40a2c0aebb74c6dabc3", + "secret": "f06ff358d629c6ba77695e765eb0bf880bb2274d013fa19c80e2409ac28eef99" + }, + { + "public": "03ce5e8de866eadb173197936062acd7991047cb46b65a4521e1b093f191054986", + "secret": "01cf2942d384c0d74528676a0a858ef5093b682a5121af2985c70cea056a8834" + }, + { + "public": "020442d9207a6a98c90915e8c1f639dbe75e8d7cb93219a95b05c49aaac3ce14c0", + "secret": "df3f20553cd4e1a298725b8a698738e82db524836685dc5499d4db026f0e523b" + }, + { + "public": "034b24fc6f0798641094442b680c299e9afa01c05783dd1ccd16567ab3bc993af3", + "secret": "ea2bf30dc0fdc389821513b23289d50717e74b8558766964de53aa45e5f0ff9d" + }, + { + "public": "030956fafe5044d2fc912c927ac7ea17af502379d7d2d723f22debee4df7bc4a63", + "secret": "c13eb8432d2bf6cd9938b6b73b38ee5c47b8b5ea57a0bf43cc8ba642de79ef1d" + }, + { + "public": "028e4cebaac86966af581fe2c3aa69410e5c732d03e3b4a2ee1cd53131e02e48f2", + "secret": "baf8a4fd4755581864f7e9095fd41e529851821ed64b494b32cdc100d53e0b98" + }, + { + "public": "02981c68c30392224a813cb00550fbd42dbbada526ab8be5e2b60d998b428e8da0", + "secret": "af9efcc2fdfa1f3356d502ec0901d9f6ad4cb87d890cebcd1a124f4232f2b15d" + }, + { + "public": "035e645486a47848b8e728525451b89d7c5acbde5fb6e82f86371a6d89bc338173", + "secret": "33f867bd83a2e48f6f46dee0a4313f14fb381416a8ae32a20f8b9cba4835f5ee" + }, + { + "public": "02dbae41a3e89f4d2d5bd3b05ba3c7501c7e8e43b8ea8a7d05931bd9f55b23207c", + "secret": "8e7ca41b47355678baa76933cf1c86a7a7f98a317df7aad5b310797505ff4fd5" + }, + { + "public": "02f9b8f308250242e9e1e0a51b4b4170305e7d88f6f39d24d807b2e079454c61e5", + "secret": "c4937778129960c2c1c5aebfe11c1bcac21982484dd45e082d9e1b97796e20c4" + }, + { + "public": "03924f9e8bc7df2072013596e49095ad090324b69972a1f453b99e02cfaddd10ce", + "secret": "67e9121ef801ae0bbae99ec4d9330e0f3c9026e33cfdce2c51f1f77a9133e2d2" + }, + { + "public": "02d576a04d32658db50ad6907d8d60d5054ae1e329e03458e92051c4ddf077179a", + "secret": "77e6cfe349d366c611e226f1c30f6d20c399eae38bcb3b40b63d67c2ea8bac8b" + }, + { + "public": "0395640d680d135c921bb27e39b7af05703097ddc7f0e79cf3373883ff140f0bed", + "secret": "c65e02236df8379ded21c124813330afdb88610e703fafa0898cb81c7f606299" + }, + { + "public": "02672034f63d05f2dba97b4609b2bbb301b011ba1b63836ca749b51569eb505933", + "secret": "f2bc40f6c555b5bfbfbee6c155171ec175dbebbb7d1ef06071495abfc4047c24" + }, + { + "public": "03602f1e425f80a4ba38e616d33619b0b2ab4961995af492dec63388cf932c1166", + "secret": "6526ed354b850dd135b235d34ad91834b6911f92cd0af2b37a58c6b2ff093ee2" + }, + { + "public": "026747260b8ee8d0bf20cd5609da54339653b59a795a4cbb9e820becc1f4b36c04", + "secret": "897117a54b8ebb294cf7d299ba43632ed4d4d3ec925e791122694628e26411cb" + }, + { + "public": "02b20d7e037f8cc574881225e767a2fd9bf5a21ecf438168273622f70b426ce22a", + "secret": "d3deafd2093258452b2ab3a329810943d3b1adf805d8a7b140c2cb37f2fffe18" + }, + { + "public": "03e5402a7c6b1915527a2d94e746fa6fd6642c524d13c3e040377e9948786e9ad2", + "secret": "5488639cb8a83b8aa0551c0e17af9ac6bad1771dfc66798d4c2df3c7570d220f" + }, + { + "public": "02cf6e2f3db6af81ff6408f9f67945d78045339caa4b0911140c3f40da90e4b9b3", + "secret": "6cc514697a9f25e6a120013fa734c3c708018aa0ed356110069d612cd34b3588" + }, + { + "public": "03d4107ee5c9b3bf01e23f7723baadbba51cfbd96ecc1cd9e504c48b02081dfdb1", + "secret": "a0070aa7ba4632bd7c9993e7abf2aaf3ffe6d4a3cc92b51f30618211a0c7a294" + }, + { + "public": "038a914c47a114eb2da81a7f3294f311f2ffef80b2de1f474805cf0f845837f95c", + "secret": "c71e794ecc69d8b659133821ab133bb9c632e9bcaa18508d938972a3f4e6ea2f" + }, + { + "public": "03ae78ffce1837ddcea2800ccd6be892a907a267216978c7b21ec749f0d44b65ae", + "secret": "47c0e4aaaf5f1f0de52388107e5c4b882fede181933f0216d6c64640a1ec1714" + }, + { + "public": "0204e303a235a67a8482b26365bdcb8b1f79c811772798b4131ca233f0b9209b4b", + "secret": "c55f3aa960479db0c174ce18c85934a6babb7e06b8b86aa6de69acf54a311705" + }, + { + "public": "029d11f19c205b0cd07d760f53b02a4d8b0a2b7d3310ea92e1b5395d87f7bbb2b5", + "secret": "10451defd859bc1cf3070b418a7a110c4cf5eb77350e87b79cfe7da20c60def3" + }, + { + "public": "02e7490b1c94ccc00287d5049a814fcde208cb5c71c1056ed7895f1f81ef62c614", + "secret": "a0304e73dc2f8933735cd565f5b84d513699ac1afa54645643f0e6d474150905" + }, + { + "public": "031e938fac7f6ac24e229f7c4d7467361a606a5207c568ad873995a9f80c1ca54d", + "secret": "c763ec3b797e4db6ed8b85593c44a9dd8d518e52a5aa8420f0910795937a56bd" + }, + { + "public": "02a3714ef6ecf36e9e9cf1afb676c4a7aefb3cac8475a55a08a63ecc8277e4c059", + "secret": "64d9fbf5b0b4d2f4f86c31bd34d48d00b9571177a0f7e111ed30a402fd0a956d" + }, + { + "public": "03c6e6a4c1e05df3795e3e0483247604ca808e1c08c2fc8e95b34e0c0bc5ccef32", + "secret": "b4fc0e7501ac366de72e59ea95a505c7726e7773c19690d2ce0cde281e7331e4" + }, + { + "public": "026062b4a89b4920c73fe4f25700a889ec4a79aeae1bd1b9a62353d62b103a0790", + "secret": "25bf5cd8849f3d08b82be180c664189f8ccb637bd0d29543f4c5c6c071890ab4" + }, + { + "public": "03f2d532ebc65b25f632598e9b222e7ece13c140ca23f36ae1eedef2fc45d5d717", + "secret": "102f188330fc3e30804ef9eae2c90ac1a585e69e67e85026d992426acac374cd" + }, + { + "public": "0232893bdeb942ca86f7381c973511285c7a38f6caa9f7d035cbc7b3089b63bbfe", + "secret": "171dfc15a5a0a677b304e40e398b1271799013b27113f8a0288d96e7860bbe47" + }, + { + "public": "031995695433bec62dcfff0255010821f55ab12e28e6b03a58074ee70ef40383b3", + "secret": "66834c2e6a75670e0dd329a8c0f2bd36132334eaf863d8e58c5d3eb27fa9fa91" + }, + { + "public": "03ed0f62e54c9435d6c4a0d3e1b1d34ee1067905f3b45a9302eab93d57ec978c10", + "secret": "f1aa5870c1daefb9ac47f65424a304b7a7a8a0b5bb178f448a3d2307a558b583" + }, + { + "public": "0372610c580fc7e4716e326dc76396397326084b7fb832945f35d999f847814b29", + "secret": "0a39f8eab6fb7b7a8d8d67db847eb7f9dfe2afc31b7cc1746ad9ece60bf09957" + }, + { + "public": "03d450e91388112e09fffae550d0b259b52363ec77f7391e3a6ef202e3ed661c4d", + "secret": "5637e46c8081c6fafda6aadf025feec97a8d3fd0827bf4a4cea2d9e7c245dc3d" + }, + { + "public": "03cab2ab0397ef9a8b285b0be70eccbf6ee63a42357a30cfbfd438436f0f9bd293", + "secret": "82574afd35c9788c1a853a77a923df3204d75b005a323d5d00ed4a890e3e24ca" + }, + { + "public": "0391e17b1abfdb4e61d964ad2df7d7d66fee3111181f8498cad0e7e08a16bd9580", + "secret": "a78c3ea501b4209923d27a55e6ce8ea8629376e13d814d2b27dac2a5052ecdcb" + } +] \ No newline at end of file diff --git a/rust/tw_keypair/tests/nist256p1_tests.rs b/rust/tw_keypair/tests/nist256p1_tests.rs new file mode 100644 index 00000000000..552f40838ca --- /dev/null +++ b/rust/tw_keypair/tests/nist256p1_tests.rs @@ -0,0 +1,82 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde::Deserialize; +use tw_hash::{H256, H264, H520}; +use tw_keypair::ecdsa::nist256p1::{PrivateKey, PublicKey, VerifySignature}; +use tw_keypair::traits::VerifyingKeyTrait; + +/// The tests were generated in C++ using the `trezor-crypto` library. +const NIST256P1_VERIFY: &str = include_str!("nist256p1_verify.json"); +const NIST256P1_PRIV_TO_PUB_COMPRESSED: &str = + include_str!("nist256p1_priv_to_pub_compressed.json"); + +#[derive(Deserialize)] +struct Nist256p1VerifyTest { + public: H264, + msg: H256, + signature: H520, +} + +#[derive(Deserialize)] +struct Nist256p1PrivToPubCompressedTest { + secret: H256, + public: H264, +} + +#[test] +fn test_nist256p1_verify() { + let tests: Vec = serde_json::from_str(NIST256P1_VERIFY).unwrap(); + for test in tests { + let public = PublicKey::try_from(test.public.as_slice()).unwrap(); + + let verify_sign = VerifySignature::try_from(test.signature.as_slice()).unwrap(); + assert!(public.verify(verify_sign, test.msg)); + } +} + +#[test] +fn test_nist256p1_priv_to_pub() { + let tests: Vec = + serde_json::from_str(NIST256P1_PRIV_TO_PUB_COMPRESSED).unwrap(); + for test in tests { + let private = PrivateKey::try_from(test.secret.as_slice()).unwrap(); + let actual_public = private.public().compressed(); + + assert_eq!(actual_public, test.public); + } +} + +#[test] +#[cfg(not(target_arch = "wasm32"))] +fn test_nist256p1_sign_verify_ring() { + use ring::rand::{generate, SystemRandom}; + use ring::signature::{UnparsedPublicKey, ECDSA_P256_SHA256_FIXED}; + use tw_keypair::ecdsa::nist256p1::KeyPair; + use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; + + let rng = SystemRandom::new(); + + for _ in 0..1000 { + let secret: [u8; 32] = generate(&rng).unwrap().expose(); + let msg_to_sign: [u8; 64] = generate(&rng).unwrap().expose(); + + let hash_to_sign = tw_hash::sha2::sha256(&msg_to_sign); + let hash_to_sign = H256::try_from(hash_to_sign.as_slice()).unwrap(); + + let key_pair = KeyPair::try_from(secret.as_slice()).unwrap(); + let actual_sign = key_pair.sign(hash_to_sign).unwrap(); + + let public_bytes = key_pair.public().uncompressed(); + + let ring_public_key = + UnparsedPublicKey::new(&ECDSA_P256_SHA256_FIXED, public_bytes.as_slice()); + + ring_public_key + .verify(&msg_to_sign, &actual_sign.to_bytes()[0..64]) + .unwrap(); + } +} diff --git a/rust/tw_keypair/tests/nist256p1_verify.json b/rust/tw_keypair/tests/nist256p1_verify.json new file mode 100644 index 00000000000..be3e6b48bae --- /dev/null +++ b/rust/tw_keypair/tests/nist256p1_verify.json @@ -0,0 +1,1002 @@ +[ + { + "msg": "3fc3eff7f53bdacc4812a1197a1469c98c5c9d3f7f4885f1dde397ac0b34d745", + "public": "02bd5c3600089676a981d2cdfa0147a5e4abdd3b6f34319fe2b8ea6cd9569880b1", + "signature": "6395017ab93fa645130c21381f66229f0ad31ad50efa613e546b6b1e687362ff591661477e794337b971b466efbc6115d5f35b463cffb6f785dd438f3d9331f601" + }, + { + "msg": "b3cabcaaec7ec718b59c9bb505eef38537c9d094d6f3ff71efc4836bec67c265", + "public": "03e1769d3203a68b05de51ebc9320752c76fdae6b2661ca80754a5f974dd97fc5a", + "signature": "6746a775588d74df8894f888cba28f1ab60b96b7750df10a8db724210d25787069984130ad944827082b13e53a96b64d1c5e776d8fafbf5985b714cb8b5cd5f301" + }, + { + "msg": "b7d5145165f9a93480432896a9a778a524fc4f79f433c0effb5a75b051c7c568", + "public": "03e15210081adc36af63c47b690aa474bcd3704e3c532034c63722304b28d10142", + "signature": "6cc8a52ea475c5ab090bb91f62e1e3e9831450b6941d30ed0600a08acec014db65e7ecee3a3e1f95a53054a03f16f0e44f1d0aa3a44b87a4664819a0f5c0f38800" + }, + { + "msg": "61e03b0d61ddb1c171e8d164bd2d0c1b5fc4e797b64668a5beab255d3e0fc71a", + "public": "03bcb0565985be22b287f1e5b38350e1b9c6c631fd6e63585041b24e148ec910eb", + "signature": "d3771f7602b46c4d12bd784d60aabe9eaeb40862750ddd5d49bbdf6697c34cf9377a7803f627f44f461378636322879efb8919654a2c69a280ab7efb5f2466a600" + }, + { + "msg": "3bcd1aabe2a97aa8867b36aaac5a4b22c1296012aeffde0f8ad791b19f12b008", + "public": "02a9946591f8eb3d5c7160ce92f0b88b1d28fbfb1ce6bb39cc7746a93d2ce6a7ba", + "signature": "40be3646c364a142e7f07cce413db8270eba8c52a2b023ada2f89cc3cd5972c0547799bd6b5d8d447d7ffd850329504717da9305dae85645c54393a5035e9ee700" + }, + { + "msg": "e2487f2d414d441de4b805f7ddc33974449fc4f02c8badf15377c72c92375c7d", + "public": "0352006d72237e24a93cc77444daea4bdd20944d5774a283c1636ac24acf6f377f", + "signature": "b2ebfa1cc565825c15012e7f0ee5404092aaa66a811c1c73e188133da9068e0a43295b7137f53bb47ea605a103f709b367d9d682f546dd4f43030023d93a615c01" + }, + { + "msg": "1937c84233473b755ab2520c8803eeca90c7bd503557a9ce5443f1bf300e3d17", + "public": "03d7b1aec7fdacbbfe8c54a75fea07ab5cd2e0882e17edcfeb2f4f6782782b9b9e", + "signature": "f65c1e6f47c0a1c7224da6fc8aa0eea9449cae1abb32decd121f21e1241bf894374c2c6368585275b595ab18f4b754b6532920522fc821446771d5b721ca0edd00" + }, + { + "msg": "0edee26db4d0d974c63311d3e9f5732bba946d7af2f32c70fc5965d7fcf1dcec", + "public": "02b80fbd6b8044b1a0dbb551be88426848280dbd8a2c5815a03bae9fa5b187c66b", + "signature": "dc2c74750ef6cbf3c66592815ccb3e3c64a6c28f3633da9a5c951c28094d5b9016e577dedc6a1d1e6d13438c7b8abfe2553b7d44597620755b87e68aecf51f2b01" + }, + { + "msg": "91f9815b4b0a1f2257233d4d0485888a1f5d63498fc3a63c1fae47ed56e10c74", + "public": "032b48232a0edd6a126ff656866d7cf2313d9a15ab2165b975f04a1c68f184e822", + "signature": "c7dd5b50a2a5aa775b23a90b0dba80edc6a12d9f0bf87081cea8cb61e3de62e21afff84bb8c656bbf08e68a25f6a8e7ddb8f186f19e91008cafd08013a04432d01" + }, + { + "msg": "d153b2e6928e837ca82d95c8220bc242d78961c07beb7f027406abf4555ab913", + "public": "038732fcdb97ce1516afb530ee6d65552ad3edbc5be837a7e679c52f8e23c06569", + "signature": "a7a4f7bd4990e28110d082da52c7eb68d16084ca36b27d57a235c463bfc2a2701cf453b26dce6cf37dadeea611969f440f4093a9e14be89417a355086cf5ed2e00" + }, + { + "msg": "4abd48f6439581235e5e28d69100daf4b82068e8265bfb2cfa157b68b7e4b035", + "public": "036c55666ec34bb4007f81205dd0e7c729ff5b04ac84d9229fb232527d62c2f1dc", + "signature": "7b41a6c06d61460d71e889db7edbb3d8bb01b3567949075559d6a97019c897e5539d107b38cc72b73beaeb1a69f9898c9e64f643b22fdc06569d5a4a222e11da00" + }, + { + "msg": "9f6864416087761a262d230c18f38f378f4740f74e65b6da2615fbaee0cd71e8", + "public": "032bfdb75624c4ae1f2a3955288dd8909dce93adf287225a5e320db948f0b22435", + "signature": "1c9195c7cc520448504a2a852fd5486734b73ff0f2afab8db1ecb12199a4b03c79a7f2c9ab686fec9bf24e6aeb2875de2e5faadcd27afd6f93e0291e3f1da60801" + }, + { + "msg": "b1ba41975b2f4dc77dfcee85a9b72a3b8ba4dd96e35dcfd0bdbf53055ed398e1", + "public": "032ec852c9deeb23e560e13510c2c628d67b193df9e0452155aa71f5b578790793", + "signature": "e60260f176fb6acd22358eb962dcba83793ae0501cf87d5e57081a763bdc82cb6fabf7e84e8abd4e8957df72fbc69943d6b6bbc77e2a1969a3f79803be6694f201" + }, + { + "msg": "0110cf18650bf4be0adafe1fea013bd8a09fc03f741e61d92741437cfee4f4b3", + "public": "02cfc31fc32508e5ed1b86cd43cb11de3b7bc2772c4eb8f079442b1e368c04eabd", + "signature": "6fd40f0b74d0e0801bf23cebdceb14d2da9a12d18a7790a48482804d5d39173b482630a66e052adeaebb96361c07568742398dd4589854d6b2ecdb32440ea08e01" + }, + { + "msg": "32ed28b7fc122057c755a175ec9fbb90ec3853a44916716fd5dcde5e90b7dca0", + "public": "036dba0f232c63aad24085a0f7595169382bbb5842e1a451279b29cec85825df5f", + "signature": "a8b87d61a848e00c3d990313d0166e89c877d2dd9fe73f2821c2424d875df93d3a67678c89d2115f3b937ac3d31f6e0bdc908d44c77bb7eeebc0a68c2055fceb00" + }, + { + "msg": "15a6ecbd1fd7c29a1767ec4e1d1d0d2af4c7bb88a020c9ba965c8e20d3593b78", + "public": "03fda85841b3444f7e2088a3974945e85be3c8271976bec13cdc0e69de7d79b3eb", + "signature": "63721b96d11c1c87e9ba9fa0a17fae8512ecb4bde9707c2713865942c8c68f2e77400ac8f4f395bc98567d6f1bbdedd3c5796103724e735712675f10a06f98c901" + }, + { + "msg": "7a1dc6e41b5f92cd792a053eae2b0132533743e06577511df56a0c0b944a4539", + "public": "036aeb177cf9866d44cb178d90ea5167426ab4172afe652ebb94447bcafab8110f", + "signature": "2b65b981bc7394a1f3a30fe80a515b28d9a776ae575cade7e389f51f7a14548b5449b3ba65e794e8e78614273d0decded5ea7f08fa8ea8f5935aad0ac08ca0d600" + }, + { + "msg": "a8b1447535b6ab857e224265c0e5383c2f7f73c545d034d1798df9888284ee6b", + "public": "02bdeb1540cecb3e32732becfc864eb4d8bc32ff9056f5d3b6876efbedf31e41f2", + "signature": "52642ce792cb100c6e4d90391d852556de043e2c766fb67b9edf677586b395783cedebe889e600435cf6899ba77888b286f23498d2f9ba97eaa605673f6b567b00" + }, + { + "msg": "6d515e85dde7759b4b695f82aab2fc39b493d027615d6acee6f56fb98d3b2145", + "public": "028280bf538923014130e3af18ed49557010b9a36b2b1162758e77b187682b6797", + "signature": "2d80b2f9c7e29ce0387752d9f5ed018066fa75ab02f3dbcb4b670648c1afde877e2a5dc4118f776788adf16765ad7344728dfabb1ad148146f22a41d2009faec00" + }, + { + "msg": "00269fa879e3db9d46467a407a969fea1e505b88fdb6b6e7218bb2849becef20", + "public": "035df9fb6043633059197e3f62c49f845b1c5d2cb7ed9cb07113acf245daa90ec3", + "signature": "1cf25dd1177165146e6dfae00eee4771f466e2c82b9f0b84e6e437cd5426eaa546da7e6db98a39df39f27ebd93b1cc3ef87a74f62d6d8ce4981f03980dcfa28201" + }, + { + "msg": "9f0ea3a876fd07905f0966f89b8b84179babbda1dc1cabb0b2554f9d387800fb", + "public": "0299d233d1b0ec629bcf53a3ed3258b9ed2d3076c018103526399b617abe14702e", + "signature": "2399ba08f57b961640b59c04ea0d9eee4ba432271ac22b6706e8a4aa772bdfa04d3683a31fe24bf52c3a95bcd7f4f2b40cac2123c84d3be60354261e1300b1ea00" + }, + { + "msg": "3ecbf5ae34bb1d6b6f077493e388dd1fd0401da4055d9c89adf5fa1f0f93ffaf", + "public": "021a4db4d76d6407831b9753c16be63759c7bb6e928691ea8200104ed55d555c50", + "signature": "2fbf7b63be3596dee0fdb1090e2e56019995a47a8038e61451e291d89b84cee658eb740ae1b463c3335f291ebfaf6c68cb89d05889240730d32b8e769f6957e700" + }, + { + "msg": "974bd8323f0c7efdc0539ec16a4879742067f1f9052c97bc2547d21783923c85", + "public": "0306981738b4150e93dd2166fcba2c58c071136d6e882d8acb61ceeff5e3f54fff", + "signature": "7edeb9613910719ba0c0d52ba574158864842d511f1edc900ea1420e91a315c37c846b167e01ebb7af8b368cc0407d8ffd6a20633fcf68cdace05b7361dab1db00" + }, + { + "msg": "aefe6faedb93a13872a4a4421587069c724d290af053c0af8cbb9da6d52f408f", + "public": "022f78d10160a00769fbe07658b7716aaa7cbde788729c9c2f5fd9630f3b9bec61", + "signature": "32efdcc36ddb3286bdd8f79464096d4e70db457dd4ef144555d55d6f4dee1d0e1a0aa3211be35c1666a4c8a90b2f9c4582b71012d2ff3ab00081abaaef9c1bd900" + }, + { + "msg": "046c497c6003c9280bbe2665c5468b4500b89406c568ab8cd6517b641a93fc19", + "public": "0323dc4cdada0f44b441ffcbdacce62541c99a72fddd8d7a490af042ce2f16734f", + "signature": "f171ed4486177b9540eee1c4407212bbb187306dd8efe116901a1d8aa141113f05bed5d27e0da272631b3d485ca174fdf8a66e9a0faa2fc1f5256afd606617b401" + }, + { + "msg": "65b8f1b63cdc4018902faa868c54d44f5a11ff1f761b9beb05416cd85d4ad3ca", + "public": "03d8cfd0f1293d35a883094f6a7edf6e7842371cb03952f6621b518ab3ff4edf38", + "signature": "cc7d33c9cced8e8851af21bbd67a6df71a63050de3116ae3c50a89433254c78f20e5ec4e930e4e3b1d7c059ea4f7b568e1e8042e4291216c7ee220317008838200" + }, + { + "msg": "25467e135350ea79786eca6c59d7bae35618cfeebef9e5153ed2e073f2ee9b62", + "public": "0229939fe834e69da9c62012dd435eccc33505737452d8e3151850feebac2111a6", + "signature": "1b47d5cc524cb49b86d0e74603a75941a57712696f1146ef7f65769418c15b9c306ca8a3676534734bf731113622133cb5b913366a162e526ca0811181c3679600" + }, + { + "msg": "d52391ecb02377e9eff37461be0ab53a78ae1a9471f9a6be2e89be9765a9dbff", + "public": "03075f93a73a5b190532efd4a09fe08e830ecbb5c1305a55382c4e4f905bd6a05b", + "signature": "e2fc7a1ebd5464ab071160a9b8b88d99f4c8aaf144eb184d4577fb03aa305ebb53005d92a2042dfac3c8219e1480e32abf1c69f03aca8021f4b9e5d1490c8bcd01" + }, + { + "msg": "d8114ca952df7809f2382d65adabbab7beda7a25053513a71f6a8064983cbd5d", + "public": "039ed005dbab34510197e26da0e4bb107f8cb64b1b9e862c43ef0a86f05dc5d76b", + "signature": "5e3e3a7a1fba3b46bda2983ed5523c1ea69bfc8752bb88e54a31cc64bff2918111e2e30f8552cc8635c0802ded373875056325193ed9b8ad97b27e9e1e2e44f001" + }, + { + "msg": "f26734f0d4f5105395115fa6839381c70d9816fa818fb9e605634a59a5ea9200", + "public": "03cb130e405ef4bc7441546a833e36a78284ef826b4fa0ecda66c330523dbf96f2", + "signature": "a14d1f583507e6ea09dfcb42f5762b08ce37337c95a07aa1eadcb02c1dc7e8cf241be4178f71dd14ed307665f0e4cb673d907ebf874882c0221f6ec6b128a6ca01" + }, + { + "msg": "1ba610ee3629bf8bbcf1ba30c1ed9f4c4dea2ced0531af6a26ef7f2ee18aea16", + "public": "029832e3af9059871ed0905d44280c828e20cd25bd65c66bfbc3e7150c963ec099", + "signature": "25dec6ad1d408e978c265fb50c91ea880c8e4a1088ef460015d0c48434db61b7017cf33fbea390f584b69c92ff3c47c1e095d5a764f8892710aa20822bdf546000" + }, + { + "msg": "596a17a1a27e7617ffa8e777a0006c21d903145dbc2644e290a33c6cefb51277", + "public": "022c2903fff6eae8b633366f301476b9106a8a0f70f541c19cdb9dbf2cca2b210f", + "signature": "d92d628ea518d56da346c97f444af5d9685274a118467fb0ab3c4070d7968ffd590f12fb0599e4042e37661b32a4534cda84792b394f1d432e1f02ee0e4d43aa01" + }, + { + "msg": "ac2492c69d56aa433e07001673ceb19bf465ad2e18b55d07a1824e5593f51b05", + "public": "034ec5a7377fe20f714ca3eac79ed4629c53563daf59e4d89a6d0acda501b4f33b", + "signature": "71f89cadaf3d92a163267ede71a796923ef3c48a5e5f21d705a222a11164bcaf5efaab21bc657cc0f31d130e6256bcd36d7cdfa26fed351aa6a9fe209a976f9300" + }, + { + "msg": "2b535e4a945059fadefa75b3b1f6b22f8b4100356a63c37f769645dc8c87c189", + "public": "02271cf2a6263bc94702c725ba4732ae041c300196cfa6dedbe5cb37f4c717fdfd", + "signature": "3b630d2965b2682c406ec57adbdc9c4866345517822f8ac53510e17632b27ca54302d88b6586a653b4a97a5f5981eba669a73de2a140e081c5fe52603695665c00" + }, + { + "msg": "6cc3e765e85f30c1020e671083f7224bd1084649c568fabcf1ca1123bb8736f2", + "public": "03dac2cf067abf1b2d521730b85a65a79071e9c5ac9f2950ac5bd44e2e850d8278", + "signature": "138725f707643147214361cd5488782342e0a8a7110fdd329b920138bae2e06e4fe7c588a17f83094d7cd62f29562a7eb6c30d507232882e9e842312cf85ada600" + }, + { + "msg": "0356c52fea3a008d8041d3f1d170c5bdc8a6d08c694b44e7e0d0a8fc6f230d73", + "public": "02a9228fb8f3e82801442e9b04482c5f9db4fc4f280da1c6870d5cd3d15196f13c", + "signature": "9a275425a2a2bc2c60de1eae59975bec0783b1a933718e6a1a48f919200858c71b12f70610a0d43b955c87be21b83e34ce02829dbb6335c34af674195ce00bcd00" + }, + { + "msg": "6325bf73910d1c5f96010cec3c77115f59c7bc822ee4c8989549c26f1fab9127", + "public": "02d1b6d93b5bafb24ccbbab836cb86c2c69845b4c82219ad2950ef02113cbdefce", + "signature": "bfe8ccd383777e7bc4b92ba2bae37268a261072b62cb9a44935cdcd3eb1b26bb17eae6ef3f1563943b9e86e158c78a9915b1539c7da59b4a321d32622cd56c7501" + }, + { + "msg": "246b49aa5e4df5ba1f72de7c1586a6f1bca3a333b167a8e784f8d970f93ad7a2", + "public": "038bdf68b59d6a9a971471363308fed7eeab3edf161a7a0ab5ca75dc45895519a9", + "signature": "d0b1740e9031fb6cc2ef5c6442753eec6fafe4720611f783eb998cd3ec41b0276e167a54f13fdf45ff4b95dfe7df151c682c4a1d605ea96479dab5cd288f9a3400" + }, + { + "msg": "12fdc2e910238d41fd3310581ac3597c4700d62956199dad122b82296331c372", + "public": "038ea7c99a61b80f6c919548b636569e06a4994944a3278eeec476a22a9daadd92", + "signature": "61fab12d9952f5409ea7d39ca504a5fa3ed800669b9547dfd162257856e80f763afb00758dcea39f89388ea6dc5b62bd5f2aa9708cf6444287df7d3a6bd1d0ef01" + }, + { + "msg": "1e8866b818eb815b08d97cbffe5f43d34c57362d703c9d84a7cbd919291c05d1", + "public": "02ed631d277945c7b67f1cb4c80d30a42e2e173384cf682526cb4a748dc6713944", + "signature": "eac977f9cb5ac1e48e8b654ba37139746a95e31fd8a0286d531867a7a68b2b0362d57a28d434428ff4692dbc78a58e602eed24cbdca4ed55e888c4254bfb7e0001" + }, + { + "msg": "26fe270945afddef41c3b950e0881d87aa77982daed5694af80b84d51eff7b07", + "public": "031942a43382ced8c22119b6f1247f27881bf72e99386923f62836773cb6b62368", + "signature": "678bcb8f52a48f17c4e4a2cc77efd555b9e7ee3980f099cebdb61b676f9884c80310bbddc46aa8822d2ef8b0ad4d0cf535f06f841c0a117176c4bcd84800350c01" + }, + { + "msg": "1e43fd1be84d7cd94b67565268eee393ddf6b05ad6e5b51c4f6ee230114e126f", + "public": "03979edad0c1d333d6635f3870ba4c138aef2fb973b7ba05f894824fba268ebb81", + "signature": "b168c3dbe98662e458f03c7a6eee6e026380185b201caa458d599267babe486622caf00f0e38468ca2f3ee4e710b33c14a50555d0abb2d17c56177eaf580b69101" + }, + { + "msg": "12f297ca2b124fef4088eaea5b45c10e4ccd0fd25f3a92acd7c67a7f40036053", + "public": "03e9d39f299da31babea8a4e6d90e06248221c1c91a79049a856356b8e481a7060", + "signature": "4521499cda4daeeeed67909a6f6f527cc879be608cc5dd80ab21500c8c6997ff3e1eb47a861d5c64198d897833649ec8a7229f077000989256bddfa95ec50a6d00" + }, + { + "msg": "04eb1bb739c3b0624234c7f1fa4503ef27ff7685f68864d83dddbef77fc9e9f8", + "public": "020e3b20515c80fddb5791d74559956dda0a2e64b34b22261b74065eac86564c72", + "signature": "1104b25f2ec935b2d9bce6c5ef6c3cb8081ec209b5a40b2b6c7907f4bc7b1ca3164cfc1dec4bb8d038c58438ba5f5592da90ffb34dfd639baae99d7419318e9c01" + }, + { + "msg": "0324578f1b0a4ab5836c7a3bdf763790cc53727eadeb26cc46b72e7ac8e4d805", + "public": "030f65d4187fd1531b9a3c40303a9eb500d15c67645107c475c763b4e844632fbb", + "signature": "bff3bfc1a67326ff2e0f4eca75b792df1e0400f81b3dc916679676cbe02da5556c83c4dd7481b18eecd55870a0eb5db0c60a779a98c9f3fba55ba422cbb2c76901" + }, + { + "msg": "958a4bdbf9068d1d5464fda8b4c6230e767a2ad1ab3c558d5c264df657995990", + "public": "02100ebb4188fb7217879958f725f8ecdcda2b4080e07641f17893cee6fe7beb38", + "signature": "e06abdcefbf288f7a7f22495fb6b97dbcc013d84d958e8dc429d3e53b2a9195f65eee8ff4513ac0524d6285c183c2fb8a66e30119e7e267f58b515f201d3cf2900" + }, + { + "msg": "445fd9e57a60c2633a4b8b9a8c110acb191b357ace066f221fe8e7449b840210", + "public": "027ca4521ce6505deb2a602a7eb895db03bfbbbfccdc3e5a5be51df3f4b667c43a", + "signature": "7f184573a04b75e5f7c2910e7b27a0ba3902975a46d3f4d62d4555f682acfc6478f8676e9ea4a2d511efd9e0fbe3fad5f2a0c6da6452c78cc82dcde17dff80fb00" + }, + { + "msg": "cb5126e7bcf90ac8fd8e16425be182b1bff823d4c9bf78bc15239ceff3652006", + "public": "02421807dc097b43cb1393e58381c2e034296b45d023ba6bd0e75a21dc3e85d0f8", + "signature": "96d68316fa1ff9d1cc22816b269476ad79df1540184c83008bf578914fdcb53d231031fdadfde32bfff363764ee02849b7c8f527b0f7f995b5364b5971a19d5000" + }, + { + "msg": "b6d91681e95d5f0197ce9c6f665e4a7fe76a3735d9fc3563872b08dc107558f2", + "public": "03b33e7976f663d11f7a704c6e3ccfc70d3b5933dfc25ddb76b5188e878a284ee0", + "signature": "03b78ecb1335039becb02b4a45e4f21b7e3a3e1066022eea4338dad59b676d2d36d59af875db0759c3293c973c28811a04c6b64ef30df8e8c9c18c01a4c8d5ae00" + }, + { + "msg": "9a31efa8f36c52c6c7da92fb2d1f55556ee2dd1bd3853cdaceeda95167f67336", + "public": "02596617a3f8cc872b91959fd8d4867f6ca1d2cd9a5e3ba9766a80a1f2c77982fc", + "signature": "233baaa28ea121de21cd8b4c6183291e3b539dc2f4a2c85d83aed406a499a00369314651312fb4a76cc7a50542d810bf4009ff2458589abf371d297cdc26c82e00" + }, + { + "msg": "52ab0863b4f9462ee4736fb56d15c650f89993d62bcf714a162abaf39ef9efcc", + "public": "023186db1431fdfa794bea08caafe88de583b293b1be86fb17d31a0f0a5ed48f63", + "signature": "c1a1aa7b6b24928ddcb8f4819172347e58465c5876c743fdb90075674a234984549c8432977e3a4e6029311ccd9137ecec0dc4423cd490476a46a7e6249e212e01" + }, + { + "msg": "c7ba381632e0b824d32ff392359c77cc3e6470903754c795b2e7cccaf94c5830", + "public": "0374c18faf24c8d348c04a7b38b06e4668d286526b53bd220d9d9f61c376cc946a", + "signature": "f9cd79cb37a012b28383a25e6a4a57c4df81c02fe4e2c63f0cfcacc7fbaf3362061a61e10b07a06c9269bd0d211d2115a6fb413f7086a48ed835c9463c2f955401" + }, + { + "msg": "83ba7eb93c29a695c064c9ca3402c053076cff9fe5a69970c0eea8d5629a99fc", + "public": "034545e47fc15bcbbfa0946efe5d9f0ba8d36bf985e0d83652c2889246ffeebffc", + "signature": "b10e9a888646dbcc44600f76ea3d848b939e93bccf9ff385928604e478518a1e39f38441005f149d0b17cc3fcb8401b831e3a673cc5df83a15d17c47f1c9f9b600" + }, + { + "msg": "7a7d76893d52f5651ce15095f8a2c9cd4164dd0b87352f4a9089d7603664fb3d", + "public": "028f307ae6d8228aa1fffe5ab5431d411581df74499c439b0b6dfdd66f00d9c7d1", + "signature": "a47a6d7145e88cb453a5b69023f338be9e46903f40d2c804c19aeb79a4b6c68f3f88a1079f42a7a3809c65666d9fbbe62ddd2baaa0fc8e5cd617c8ed85e4b2bf00" + }, + { + "msg": "8322ab3cb08859f3d22432fae44686db628eb3dfdf96a5cf74db4f81295d3f6c", + "public": "02623a9c2b896fd8fb803992518862310ff2c5406c9ced7129e3db123cabdcb8fb", + "signature": "f539666bf63d1bff626af3f46d7075f223e72b43210fbcda6e4246d713b13108756dfeb7e4085bbf201baba3c2103c96a209445f7aea187c4994dea3a9d2e17f00" + }, + { + "msg": "4fa29976bdeca39cdfcf4db96bf45b6c6b5e5a15b3c188ea7176d3f418f39d51", + "public": "034ff218bf704d7429c5193daaaaddadb6f1750837ca2c6c91a4c3d55fb21470e7", + "signature": "e41e02a928dcf470ef82467d4f82ad1054b3aeb478c703bc320097ad4cc6c1de4ae58dc297a3cce8b2ee7a292591e2469a1f3e138d7bb6c41fbfdf6a41bd8f8001" + }, + { + "msg": "4ced4df2ef23a8b1826b28264d13de5808e48c60b0530642c6d4048801a75134", + "public": "03cbbda178c8df2840316e913cb2f8ef088e97c613343bd6cc4f8a5e6fb6c291cd", + "signature": "ed2c4e662b397259d1f4b8cacfefd663d69cceed1b42e5da589e9d130595a43e7343325e6b2bbd976c4f34d74e0624239167ffbb9ae3ced83c4c4ec7c203a32401" + }, + { + "msg": "9c3437dd44a0df0ac7465740723eb8f35ca6203e560debfb8f195aba563e7e1e", + "public": "03253928f9484105b0652364165416fa9a5486f07cd1ba95233aa60cc087b3bdcd", + "signature": "ab78d6efa9cf6cac1dd99dcec9c76b6b48b51bbd1c8b9fa7549b60b550d9ee0e1de99f3437f1ba440b9861399fdb862a463cb762403053f5c9dd28dc966e4adf01" + }, + { + "msg": "745525a42229775504361c878f44251d54e72fb65ff47d2d7f1eee3968f9f4a9", + "public": "0297171e016a5ede6b3393748a6f68f10b38fcaf4a85d547b4a089925528a0200b", + "signature": "69d2880009c1e98f9274c5b6e820291cb9d7d1e23d0b60cf846ffe298759e37702bfa506863316c332270403d2b7e46282379bc9093890004f8637ee54bff58300" + }, + { + "msg": "a4ab6cf2aeb5e6541e915f24432b3d50c56be51f19ee56044b4a6892e1c19f7f", + "public": "02b9fd033daba6f286f66b9acb80b8c147de8b1fdbd25a8cb382fd5a85ab15182c", + "signature": "1f99bd3048e56900b19b28a9968bc7038fb69555ccd585c41d37c41b7cd90b240f6e743d5cc6d5f3ef3f8d37a3d409c699c9f4f760bea574bcb2af7b0af2e0bb01" + }, + { + "msg": "738469509823c190072194bc6f9cd2f33805896d6917e6579b79eabc1c21f7d4", + "public": "02e91e4371a7489a21ae304184d0d054051baf11515be7d2c17af9efce261b235a", + "signature": "4c8234057e3d510179cea17dfcf120e1111192008b3e54304c97406552cf086a55b73a896ceef4ebdbd62b77c6b7b04affcdba537c98badcedddc186648999e301" + }, + { + "msg": "c300aa5fb20aeb5596d3566ea834953cf183864968e3abf3bdbab814a7b2c8a9", + "public": "0223963e1f63eea649f1f353f83a76d1c04283a54a437718e006147b06c800dc82", + "signature": "727dec3fecdfa335e4960b2d4e34c09e8d44250c89bf37d558842273d7e5304d2c04af0b18f197dbff298c4c140beb5bd34a6ac0849c63ec7b9bc7f8cf3b767301" + }, + { + "msg": "16e53ba6d754baa5aa5062211f299bb7a75c111361b96a429c0a07d75b68bfcc", + "public": "03446506ac0f5fc1cf53575935f12a5206edaeb37ba1e7d8cfd03a7e82c73e8012", + "signature": "f746494dab76acee6f8129921c1f0a4e84a95abef258462a236a69addbdd1dd765019b69a83bb3f9ddebeb93335ffedcb15dd9d7ad21c5becc087bd90ee259bf00" + }, + { + "msg": "bc18bea21e2891b93ec98a0ed318e2b14360cf4b46294ff4cec272b411645b0b", + "public": "03bdbafee84f686c03f0035bc188804a53615487a0bf1c3319b82d9be05639a5e5", + "signature": "b6f1e22b1e1b0e243cb79f248d28db9d33dfa3a03c2e979b1f47780bba769d7246b253cf66025a3b1f8ae1deafcc99e60ddf9d3a99fed46434d775cd80ca3ca001" + }, + { + "msg": "c339fef3e89a271229482bee60c34678f4cd39447159716a2172668bab0b6243", + "public": "0398bb95c5f55d5515fd401d2652b1afe872adcaa015613bcbc8188c80dbb3f2a6", + "signature": "d2cd7d2e692be077ee6a74a6308c34e94b2070451047644377aa6003f89c138e6cdf177bc30273e74b69de5e0f585b1d8dea3d88df44287249236685ed3980ab00" + }, + { + "msg": "a47efec21a497c7901db94abb40e05bed3e62c6b051b4c1f5fc67fe56baa5810", + "public": "03c502add2d2d771b74156a4d168d1b56178b5325eba22e7e227913161eee98e6b", + "signature": "15372a39c3d8e9181ed583a9ad0535d11e7f8b6a965e09b03be91a8ba46662f106c5f84c7a7eb84b466698f00a7f8100e80f96345763b202bc401bae85a83c0d00" + }, + { + "msg": "94833030239a0e64ab35a391ad05a42d7b28b22b7572594dbd8e3bc4cdbe930c", + "public": "03842965449136edcf38bd46a11a409fa45748479f6059de428ecd7ee449d59a1b", + "signature": "fa71c897519debe5f5f4084cb1288566b99e527188b440e5c5bb51db49280fc26e9c76d5b8d5be2cc766391e08ccb8dfa0e4e916bddc904a453799ef38f7a23b00" + }, + { + "msg": "a05c4bbfa8845783a15c2690dfbcca9e03f0e3aa044126351ca78e6385cf46fe", + "public": "03441b1e30551ec2a5b5508d09b117b50f7b937eaf7f95edc2bee6144260a15a50", + "signature": "eeb5036392639c038ec38f4751386a6f4271805badf2170b02fac188a53957183f5ac281584068dd535e7ad2b5b2eba4e03c57d38950f6830e0d9296b1cb87cd00" + }, + { + "msg": "f08c11d380837cad3d72b22a464559267e1261b9d3ab9488d87ba5347d3d2b9c", + "public": "03ae84d572ae6f36c6a3f1cdc629f5f91ce0ef478c9e19c0f96267c32ab88d6141", + "signature": "7a1b4c7a3fe09b127086bce0f41e5995a83534198624836b395c7dbef1c5b2ff1ad4c0e999b3243455a3637d25a1222504945889f126be37ce5fe37e917a508a00" + }, + { + "msg": "7d41ad3cd3f270483ff066769cc3fd687e29ce4802cff89dc3c4fb2167375d6b", + "public": "0261074a43044b4cf5009ea56c4d2d55b2a4c1e58883755546f03ec356d23042de", + "signature": "5cf77ca9a3c071e41e66ec01630d5ff6fe21851a33c916d5a038dad02f5f456206493da5edd0d2e68e181bd6032ed13c6166b36581f1bbf4c6faa03c6281859501" + }, + { + "msg": "164c895907451a070d7b96463be7d5d656b7d4e7a3dcbab60c61c4fd44f3680f", + "public": "02efe37cbc78550d5da1746c567dfc1f25698d05e3b5973ca8aa5c80a5aa04dd86", + "signature": "bb768a43b3c725211b48271c2c057c068be7ab0ab91e6811e555a9cc7609cfef4b353c22c3de5784757e735e6d6af5f8a5aa152404f76af089c831d98890b8b700" + }, + { + "msg": "e18462a1eac0891c359bc8a5eb8b17cda65fba96a8b2bd9c67a95eab8e703586", + "public": "039f439e6e37a6ec6ffcffa694a0cc5ad5225ca5aaa0598081646782f44e6b0cc0", + "signature": "bab5bf26af49ef94b8071ad2092510fa5b480ca7396520da2e5b21fb622656234ad6d15472dbb7b5df4a3e8884b3c9177f4546aaf4a52add02885cc9bb22c68b00" + }, + { + "msg": "7f00101af3cb669314371cdbf64e4fc7df938d8a15420997900a8f277c40a075", + "public": "033884a47014b9592152d2855c0026ee85f96f5e672eda3093598bcdb103815b5e", + "signature": "d18c022a0d2aa444529ec85058ff177a30c74be3e0784345d97d702142e2decb1bfa9b328a5917cf38a958ca7e6e97b195babcd15d45a22281f50ebd9ff738a900" + }, + { + "msg": "a1d380fe9121aa80316f774e4d1d009f8918127c65a62e2723d8d41c9e9f4952", + "public": "02e9004374af450c60fd6c2be52f230b9451e38476ab82752a1d8fd6089af0e5f8", + "signature": "c2d551cda73f2f85306ce6ab5f62887fcf09a65c2793d5eb0f8843750ab6212911a55504844520f8183cffc29ad0ca8027d5f2793a5921edf5b0c261ae06735601" + }, + { + "msg": "fce9c71cdf019ff3576191ac1dccf95a7b45907cd850413df60aa5875242680c", + "public": "023109d150cf515759983a54f59da9d20d8bc5c2bfaab5ccb2658449f9bf24d1a6", + "signature": "78568b8230691ab4e6b43a28b3aa95cbd4a368d82f9fc11fd17050092ad9f3db5e8d9b6b0057f76d1f91679936a0bba94edaaec9ae2749ab4910bf1ec533f2e100" + }, + { + "msg": "52edf7e0aaf718bb1d4791cfcf1a6e3867ffe35016b78816be8e1cce3652376f", + "public": "02adbc5454569f0af4c5fa0c1bcc6b411c600d06265498702dd250a4b7ea51a0c1", + "signature": "295182a6931a3961d11ddddc7061046288768ea6669994fada6a8add08ea83de6dc610cad3ef29573b5a25d71b44707bc58b290a40c23df4d58b525a0324aef300" + }, + { + "msg": "679af3abb13c4dea52e18202d7d1bef32922197a64a0d57e5ebaa71b33e85b15", + "public": "028fbf6b72bfa9723e019f0fbe99eec6baa73f9e3f83928ef72d094a126bad40e1", + "signature": "83ee54bd037ce225b8e5ebe7fb40c0289d3ee6dfc6c898d6102e7496cdf4dbae7e4d839a11e8b77e8d0859d6c57b8fa312957948be990dd69110744e4d5d84f901" + }, + { + "msg": "cf47123f40d553e05cc4583fb3aea115d001b64bcdc51b6d414e23e4f0f1e174", + "public": "0228130059b895889dec3a6ae4a3e28a2bfa3fa907b32436cfcbfc7cacd62db729", + "signature": "1eb5e9b43bd48edc9cb878fe980c68615eff3ede4704907e76268b403c770ef60b1f0efbfd5eb2797d294b7348d678d353fa332a859be8c9db8f7b8acea9a18c00" + }, + { + "msg": "1960f75f57e258682ebd79f105046e2bd46ff9fcacb8e22e9912034fef7c827d", + "public": "03e0bc5a7ab830686ad70f1163ba8fe78f303c47b7bbea383c2ca425b2862b653c", + "signature": "bfe67188d0947885bb5adf38be9d2350dc80433f82aad285b0cfe89ef8b4a3d26d7a7d758a73d7602da22722e1c0d2ee4cff40e4231adb6420ca473ddf8df29b00" + }, + { + "msg": "926e804c36ada479d8ebaff00f966fcd4d1cc043b0ae0f107737f86a92d5f81e", + "public": "0390de70c32f27ef20ec94f07c580fac5d47b0d422a07c346a5e635b0473e588b2", + "signature": "b6adf78048d20e1eadb9709d069d106576d4b3c34b16e67d9638182303f73a7a0afa5e95633ac83d29c528f705944b6f3180fd96654aeb42e263281e57feb68900" + }, + { + "msg": "3e450081494f59d063fb04e5e8f49a0f0f6edada3efae4dbe97c966167b75e10", + "public": "036b34461a70809d475eadbec9be7a015bdbdc18d2a4d0a3b805fe036b7e195521", + "signature": "1c7632ffc765c162560e9424b172f83fe3e8b0f63158bbe6b3fc767634f409200616bdd26bf3b0f9152875ba52ff0246be2d160acdbd01cd3bdff1bc2618080101" + }, + { + "msg": "d93fce08e59fa8f1dbfb09c79226cdfee8c1f1b461a168074452477ff8e03e5e", + "public": "0289a06d50ee94e23086a20c1a06d30d0b75f92850fdbad79dbe9b87ad4d1a9548", + "signature": "6e8bdb887b54cc16186a3c3a82ec20e42e88505b561270c6b9b5acf672b73b1466319db312712c8eacfeac568e97aca8c5dc6a776df4cb1423c7f695bb8e29f300" + }, + { + "msg": "ddd1f5c6be1e2116bdeeb1440ddf654cd4dab727c3ddce7a91733be7d1c3a6c4", + "public": "025f53e9d681ad31f5372e21e1084daa464e9fbed1a75d1717008ec5c3fe5c6ce9", + "signature": "aba9aad274bebae8d376a1fe3c65e80ddd5ef36e1539c69c04ed5c3d91757a471be76b11e1a066a8a146039d15491d1698483e4e6a055659503deae716e00e2401" + }, + { + "msg": "19f0ce1f203d1f9eb946efa1e2f1cd64b2baa25a2350d5fba56e6304ccd678dc", + "public": "03218a7e8f36fad9d767a76fa210f4b96fb01e242659e9018c5375bbd11ecee3cf", + "signature": "a15108353a10377f5ef6da5cc6f9b0153b0fd39b55971e58c51d130dbf19979204180bbc1aef2172131e353cd14f2d036c7ee178d31d42252065062cbd2e10d700" + }, + { + "msg": "22436e4853d30fd3aefefef4b98d67740fe2253325c29cc685efea5df0b9baee", + "public": "037af1932918cff245304b903b1b90865546e28c9b2c5e8029ea2df02cfe43deb8", + "signature": "7bfccd2afadb224dfff4fb70fa7804d68dab72abe6a3b9f1eb732830cf8702d035eababb6f8a146b37bd19c6948c991d1dfd703964bbe77dee3a6713741ef4c901" + }, + { + "msg": "4fd33dd117315b01607a833e7c01a3c27f535f5a8e2c7934879255581cd5cbbc", + "public": "036b87f3e1b60398153016574802d98c6f184988135dc089e5b9fb0bc6dcb2cc82", + "signature": "2afb7af9ba44c9b1a654f998e3e29232f8b094ad9afc753daea8bb131c5af3e6146b0acc91f5872fa5e8d2e0ea0e1b8a39d76cac9eab0a4a0d41fc833dc83eaa01" + }, + { + "msg": "7d485e9430416ca249f244327f62dda7abc163a6e6c0d20e6d9a74572399faf6", + "public": "03fa7e3904278ce777e81dd254b76ab2a108992c656561b8a43a2eece48c928046", + "signature": "874c889a41afaea6ef3784d02dbedf6d0f0c4130e003c6bd2981d4572ac84f0c5819dcac16daae8dd01232f0c68bf5a06700bd99f15f7a48d678a7dec0c8b1f800" + }, + { + "msg": "bfac57e436c155db299144a8fcd216b5d694db3a1160fa3a6ae574d9389609f4", + "public": "0378b2c943f20677467941624cd63355103b11147a2d70e486832191126b348e6c", + "signature": "ef4ae9a5bd14b8d236b88163f42e11ea49d828008fb79212deb7d1568b1ad11d6075fa382e5b61318966781e4865449b81652be1e6240a587b96e96b5291880200" + }, + { + "msg": "69c9bb3ae6974d1cafc56dbe04b1879dbdd17934afa59f5936d7af30073e70bd", + "public": "038a762606924c83031303f5f0ccce531583213ae04dc0f44d6bba06541aa1092b", + "signature": "771e724bc76a3f8d99758a5f9f24a3677ee8730b3c3dfda5deed03e9e37b7e2f706cbce8d279ab4266ecc432a2aab0219675d4eee5d3ca929158a1ac26d18b9601" + }, + { + "msg": "c52225b22b8d542d8e6616f325b5c96041919bdde2ac447f9fe957570317ea3a", + "public": "03a20c2bccac5ac49668312b2da0145ed1eb92b1de2090c667e0c0d8cbbfbb6fa9", + "signature": "434febbc358c48cef3a4efbf1212242af07cd0939a5e810d9e65c464bf363d8d5ab67b179e067ccbd554253e9ffab425a84071f555ca76ca60cb8d65a098b1ca01" + }, + { + "msg": "96b9ce54d0e85e7cc355c1bd727d45561d99ecaa1cb697ef2a1421ec764484cb", + "public": "03525d84aaf798c74909a17992003193166c07fb15585fd0d3b1b536b1b4a69d78", + "signature": "2f29105f779cbb92685dbdae22fca4728476c7d1c2061f3e64ee2357863a3634236b435de0e64684d5b291197ba02558818a0182cc3bb65311c49b538e736c6a01" + }, + { + "msg": "6b5688fe036c8eb9428c42710454a3d1cdb32932fd2855ff8fccf1df33d510b7", + "public": "02833f9a790408bad18b5b02e1a2abc299d4e43412dba2d508e6d4dabdcd4af4b3", + "signature": "e6a14a348e88b5b88a5bfef99ff875bcb4e3c319f3a3e6d88fd62673e662d3f729b2be101723a507332f40827a296ee3c4b2efdf82c3d0fed1bd7f71fffca92301" + }, + { + "msg": "5ad229445332b1b32883fa32362150241981378ed172f20de15ffbb4f61ce282", + "public": "02157cd2f9c5e10005694b8275321c96faf4fca2ccbb0d4899d1b9e0a2004948f8", + "signature": "852147d6bfc915734ce5045968b2b1f6f65ed68de9314be249fc3e9844dd2ace635e13fe5f7f850331b3394700315893cf241b2fb78734aa4a13257e4779fa4b00" + }, + { + "msg": "d49f7799263d64165e501fa14556f130e45e9326ff7d5653ff97f78a341f3d7a", + "public": "036982b1c5a6976385fec62cd00277bacbe0c77640b0e950248d5eea71d3b155be", + "signature": "c85c84c700cf2e45deffb02710fffac594a3b33dd415e5e7de9725389b5e3d5f73dc40bc110154c813c195f83c55fa33096687ceff7e8554f4a2c188c659510500" + }, + { + "msg": "3c5004b47789981a1da3db2140495fe19feaebbf3c826c9e9de3a2fe28ba0bc9", + "public": "03fc56103d4a480e78d0005888fa2ef7157d77498e3b9020792c87cc5b3def9de3", + "signature": "44f788b5b664d7d99f4cc2d0e6e6332514d1c91f20cedc1cf994973a1d8301555c551376d14eed7c171142f70a22b0b566bf64fc4458e0ebdde7dedf321c171c00" + }, + { + "msg": "41bfe75c9172ff886b548d382994fe8aeae45aebd91f5e592baa2f421aa2096a", + "public": "03ad8c5fcf627e52b8bae873d1e3a0b519fcd339ce8e865843341b7482d3c87821", + "signature": "3fe4b20488103d94a1bbc8a58ced5142663dab8a1dd6db893a8a06353b3393834cf60178a7baf6f7204b827ab7d91858303f5646c5c9cce6de102e32ecb155ba00" + }, + { + "msg": "89f50c90468973e4682535cdeda35c04de67a71df0309159774a1c4dd84b19b7", + "public": "027820e600dc174ac71e319fe0828183154a34c27c5ffc79fcf20acff1260af002", + "signature": "50b7543501bdcc9c93ba897c24f7af1fb815e46edba7e6be7be8a5170faf421f18ce5bcb9d707adef7522250b80c529b0166da10fa2872a153766889772a35fd01" + }, + { + "msg": "4b09ada99a7a75b2aab3a3a42f67662d804dbb8b66aa3fd4700a8f7979c8b1c2", + "public": "026b481fa0bb6e8ba10e9111a5197038166f0a6f8fa8a61bd0e46b1eced4be799f", + "signature": "08a0324c0b7d6d311339e2a46e923f4902644d801edf99ada14bed497d595da46b2027680cb3e6dfdf790ae82d0d6ca9a4d7fe760476ce092fb5a7ea9e1c5c7d00" + }, + { + "msg": "dc773edbd2177cb77b64a1a942ded630a2c1712bbb48b8a10349a5bed8d22807", + "public": "03f4d032cd68079b44e775d56703565224e5a94e277ca4055557a8f09ee14ec428", + "signature": "51894016718e4b8a279cb00a451f10853a1e74a10448922be297d6eb5050fee4680d6df8ebed164f5797a6ab2547e61683bd4f0c104ae2dbcce8028400ebc3f900" + }, + { + "msg": "c6293b9694947257744622fd5425294933fd3512fc0574b75ad4da96b0b3a525", + "public": "0347fa429023909fbac3c32b67ee2b705ea7106ed9ae0bbfe2e35781146f6ab351", + "signature": "eaace7d9a0536f410fc8ed0ee59b620c99bddec0aee5b55fceebad3a839f76300bde708b0bba9a0fa663ac49043786b7c6d328536b0f8cd9bc732ed0819fe46801" + }, + { + "msg": "5e98afdf4a518d715bad40dfd751f09c36ef762445be9cbd4c26799033837778", + "public": "030b18940240b7a9d06fd73c2ce2611de315126c2b2f5c8b93be2d5bdc02fe0e3c", + "signature": "374f1731b2f86452a6a88e2f7842bd18b6f8e2ba32ca168a85429e845f583c3e4ca70eac76b87974c10c61f22980b7806aa5e3321cc030c5b4a7e54dbc8e908501" + }, + { + "msg": "94d239a1f39d9f62d248bdbf7de0ac05884a1ce2c6b119435127301f15c70136", + "public": "03d879c5a58bc2619b31143c5502a07ade0a73680761956d15bd4b456c80574957", + "signature": "88246dd3ebea2cfe956c7750fc9897520915a69d9c0a7bfc2eb991fbf8cc84b814de2e2118ff4909e344f9592d78663fbfbb9b7527a4a40669e9d6f099b7eb5200" + }, + { + "msg": "c770ef59fffea0e9cc4acdc4d943e08c73d0700aac3e4087b825da7b0eb6f1de", + "public": "02b9f6fa9dc7c39b378a4d31f5d01f7b93c7397d45bc67b56e4c2616b264c7d144", + "signature": "f6f49ac0bc5b75f4f3f11ba325fd4841e6cc90cc2781054e56ce1467eff8082a47879e2275404720bba1bb337fcf07c14b14c41f9092800e87e373f91268215701" + }, + { + "msg": "76dc6d34d093dfe1b1c1bbe794dc405d761f27127cdeaa3ef0e6aa829a81dec1", + "public": "03932e4c20eca4320a26d60eb3682b733067bb9842913c2e8dff11c7992e939199", + "signature": "0805d0eb47424994b76487edd5e01800176e573fc9906b27b0c671deb60edd092060aa7be791cc422b03c7e88d9e9425e68e1b183d224ce1fb944aff82c017e800" + }, + { + "msg": "9dc93362931f5fb6195662d3277b4c92b1a2842eaab1d24c22fb138feadb74a3", + "public": "02f58a86c599be85e6a7b4103f4d2ef6f01b44bfda7493677d3201e92795e7641e", + "signature": "32a4e6c9683b3d45e05f18485bcf0c2047b26ea756c0b6a290021cfaf13f50a953d9427b89d19f21c6083fcf17a1962a12ed4fb4298561d820400786f5b727f901" + }, + { + "msg": "c7db05cdf5af763077f13fe2e714a409896a7e4351120abb42dc0250a6e99d1d", + "public": "021419d903b1da308722df3553550c796ff0b79112820a422933490bac424ff432", + "signature": "7915b2de75d1bba17a81ac468ca4c41ff2668374a83ee25da216aff6db2f280c6c2e3c68e1943dbbcae24d5db15248685d2f16a27b2a9ec3255fedda6e11655701" + }, + { + "msg": "93d39132bb5263f071ad4521eabb5839bb017e1bcd7b9ea55fbae7c79147f8e4", + "public": "0323a990aad046a4df424fc18b77e79a8ec75edc212a6b78624a9ae5aed8c79f29", + "signature": "1b36a13885734933ec6a34670b2a7ed413819ce8f2f16a718ba5794474f3dee56b8cca0962e3fdd71bfa0c6cf98bb47c55d5943d4935d5c7c4f429e98f2afe5e00" + }, + { + "msg": "9ec2939da635d26604c9a677319456dc5dbd15558353255764279e5247ecb61b", + "public": "02ced242803f47a86555de266465899ff7ba279ad567109deef0675513f8367b9a", + "signature": "b2d9f9470bfcb03636d149790fbb9072135aea1d43478085e0aa78eafefe391d13650a4043a9fc98958553203e4b0ac8630fea75419a1573b1001bccf01b558401" + }, + { + "msg": "025884d79596bb826a6f96346ff461aae6d48fb391ae0b59444a5708bfdb2518", + "public": "03eaab98b4e5791560dcb8b4de021fd7c89a92f5191395524d1737d46e3a6a7072", + "signature": "76f5af50171eb4c533d08aa027b17e1a6f0c9190f1595d5ed88263ad278b5c48103963250caafaa627eb0035fb6249357c6061ba63c9747bfb626c460a5460f200" + }, + { + "msg": "c3e64dd0487f7d4ce65cd4b0bc5aeafdf8f9008fd29441a4aea50bb7a7f417fb", + "public": "0397a4c2176af8f993e3e46707db836704775f528a26c337721e7f56f43781ecc0", + "signature": "080ed37b79d046d487e905e9dbad2a7b5d471c8be0493a90459a9d54c3c1f3d77d7de97b55fde42374d2bd7237a8d69671b144335d71f6b318eaf464f1f4e11100" + }, + { + "msg": "0ccf0788cdcb12810d0b1454f7c4c2bb0184eabe9a2cfe2531c6000053d49f06", + "public": "02a0cf8aa724756fa8522ff369b3fe96644d42f10a02e9f7c75aac6a416fbf689c", + "signature": "857ebc4c2a2e7be151e0d3f6dfa49b1c3f86cb0e6b74e3ac3420059f2f3c26313942c04819b6e5731ea7037ce9010933f3c73bebbcc13385ca6a7cca0e12b5c001" + }, + { + "msg": "4c860154022eddb3883324f2d51d93b7ab741c4dd25b9c3bef799db0ae5344db", + "public": "03e6cb13eea9391f87e962399deb6445429aa5b8082edadb46041498f5b50afff0", + "signature": "88744628f0a48cd12de3d2d75085ec753c6790ad61eb1a1dcac1daeaa7f50efe568b8aeede479a12081a6e38e1b208ecda08ce81b0b5685f8f5254b6dcbd8f7a00" + }, + { + "msg": "0113e04b61c67c5671e12c50a62b495685bfadb86c40b2c693cf4538de12e896", + "public": "023ae3efd821937e856110201ba21bc625499e37ac27041be02952d09190625ba1", + "signature": "5bbc2eb1e91a41f4b6dc6561667757169aecd36199ea349da6d0b5acc4d1ecaa77e5bcfaf2fd59f2338021528f9ec4efc76ee36fc6cb9ab3ae62d7f645f7a85000" + }, + { + "msg": "b2a9f64d2d5fb589605efde5ddf5d92df6ebf5d734aaae43c160979f08ee9a54", + "public": "026146398a533cb1e4e05b74190202e6850f7a5dda9367d613755f34329a7d17c0", + "signature": "95218bc7c360753c59dae7ffb27ceb3e2639193412a8e2aa20d3f395c93473165ef48df73b0ab42921c8a7b350ee14af68da829faac9947cf9466e829eb9675401" + }, + { + "msg": "5c2a3b4b726e51c3cef5e21419ab5dc056ca662ba0806cf9581491044eb13a4c", + "public": "02aa5ddba756ff435714c80d1a2a3f36b734a922c5d21978f6ac4c87955981a539", + "signature": "9eac9d57909bd07636f0a6d64361c2d277f588cee9741a4e221790535682044c1d2380844a98b37165eb1dd0fcdd691059a540a9be5b7de5beeb5a841f347cb600" + }, + { + "msg": "0befb28775b8b678e1bed23cb3d6df6a696ef4b019915429ed9a84914beb45e5", + "public": "0267daf13e1a8f9a9be56351e5a370edf441bdf44ceeb07ee2fd89c5186f35730c", + "signature": "e2b46c68cf4f17f60976e6bc21906c6a8431503a45dbbf2819f0764363c69d3923ac379a3d1508aa8993507ec2d26de125c671aaebc787f64cf5c43352c5e4a801" + }, + { + "msg": "ae5f4184a9d51956bfde709c0d5bfe0285dcddf75dfed7dbdabe37cb2c7c5dc4", + "public": "0275b2b37d6a8e02d2a383c01b3fc48300fc79fd6c4bfe5b1ceba73206a9c9f559", + "signature": "0c2e9e5126b7af7acbdbd88e34f446dd6a0e616aeecc6d1d79a0093a53f1c0c10fc309b368e745bb6dc589c508e28f3d0f3115a954d9c85ff422ba8d60b225bd00" + }, + { + "msg": "d6dc458027dc5b0ac41464eb6cc3afddd04d60db7130829451c0b2980b08d5e8", + "public": "020a7c721a860ed02fae18746acbeee4b9509b23cffe0bb63ff655e78b6e74946c", + "signature": "f952c51906c56367d7f1fb7525300333ec056af52a5e84f52cba27025a6a23420707a32b622a323299128e26d15e5c50e55996c2fd9c5b22e106d9483cfcd1de00" + }, + { + "msg": "04f12387e22c2dc381f32acf0b8313b327229af93ec59b4900eebf7739cc25f7", + "public": "022790ab589470a57dd2ad49d1aff25835d19cccbc5b7e636a51225cbb46a3353e", + "signature": "6b8d3980eae61a814d22adf2bca71b33c83f8532f506b0073e16cf7cfe8afd7e4c00d84f114388129605d4871de5370d60eb83f6c9050e9ef8334749999e504001" + }, + { + "msg": "dcfc5cb615b41c0d66579dc6a4197fe66edeba028755948c354a826f7fc6620d", + "public": "03536a1d56ebf0dad8e7ec223adcdeac47acaa84dc660ad10f9325a65e34411a58", + "signature": "f185d2320b7b9ccaff26d971dc695115e956ec1629f207dbd7bd90fe5875fffd61adaec5700c7d725da91da671e995dced8d3dbd67d78a8981b8a515f862a8e101" + }, + { + "msg": "a5db20dbaa5184cdd2e3017314df7f44481ca3cc991cb4fded693e7c8234c182", + "public": "03ac82fdcb9fc2b0a3d9428b4e789ac9da7f55d2ddcc50e7899f3d3ce115b42d3e", + "signature": "ec502b6cc06351eb1b66387b94c6996afe75a4d1fe24a6f1c3f1d89fac928df83fad94e5fbbe643b28fb444d582bde42880df6bc6ddd40db276f7388bb6b18f301" + }, + { + "msg": "933848e70c2cbcacaab64661417a67f8f46f6ad4effbe38da473e2721ddcf8be", + "public": "02b86afe1bcb784e96016bb462873bf64c6459a25f3bed71e837c2d9fe7c7b2da9", + "signature": "356b5824c70847dc44e139582109cce7ebc0348e9e255f2e3edc27375b7a613f41f616fed2a7acbd3b123e64986857fb51dd3958acbd57f484f3333d247db18b00" + }, + { + "msg": "c45524b8c421184b77c74f563f14a0a3238b9033271193084c47c1a5ae675cbf", + "public": "022ca9d5c56918eb4055e67e2fa8e9cc69bde096c5aa1bb0c8107805d86a866c03", + "signature": "61fcfc1a6459640df7e5219c652d17b2ba8bbe28eae8cc53b94724b0aadfeee3752e501ffd98c0618a2daf53a682b27f1f8d1ec827bf9a39655323a1e0f68c3501" + }, + { + "msg": "ee709aaadec038c6ea52c564c160e2e2b55818ebbf478b63f5ca5a95b8745527", + "public": "03638856e9889eec9cc92557c1281cf7ad47afdfaf9c47b5d6816332fe061e5000", + "signature": "1730d3af72ab31f1e1becfa2811194c6ff1c7345583fb64d8357b9bc4ff9745f6aa310cc5ecdf951a6f58000ab9c65a32fdf6c0cf4a2c45d53831f80371b435101" + }, + { + "msg": "286b2f3b2ac538a66d16a860d8a2ab3ee25c75da8f3b0ca68ab75285946ffd6b", + "public": "0285c87814cb6458a2859baea328d3a32e83b1800fb5bed8e47f8a5be9afb7f177", + "signature": "889dba688dc1bc8ca810093efaf2a87d997029bdce3b4a4b0cefb21464ad83282ab1d9a08af713bb65273e5daa6000829ed4ad8f8845a8af689161c49476d94f00" + }, + { + "msg": "3386de44ae7abe91bf4ec476051d0f3d55b97c1c8d4d8b7a92d53935874e96de", + "public": "02b8fd730a823da73efebe11a7054ec0afe90e1ec00739e55648e486e95a894c39", + "signature": "14cad8e1eec609f64f43058a21a4b4a80017dc155d379fe8845344279481b0a46f9c9a1f7c122bdfdf321cf77d0b2df9a516f832e00891cd10c5cc263457012301" + }, + { + "msg": "07e587cfc7fd5cd3342cdfa4d6774753372773582e990d2ecb077304f5adcec1", + "public": "03f556c5f8f8365b3f6f84c95e353695dc9e55d2a121d09c9669119f314aef170f", + "signature": "adb0d6e32ad324c19b2087a8ba5db60504d2b9162f1287cbc2109cbc6720fa6513de578eacf8441a7d6254b144ea47d077c4fad4077395ec31ce5a950e0509fe00" + }, + { + "msg": "853a1f6d070021b7c85ce4460500b5d44ee38610249a2e0e1901acb390fe4ccb", + "public": "0389a36a6bfcd54af9e519fe4d3e7831835bd819af17546fdcf63a011c5c98b902", + "signature": "d190ac27ba07414f970f6307dc70bfd62d35a3c275048ed61e87dc0042631d294fe6d7d6e5d767a20b7862270c1fd1bbb4e92c844135517771efe72d7b5ee71100" + }, + { + "msg": "1407379ad990df947fa6f68937d64edaf3a54ece6f3b38cbe01ca7234bc4304a", + "public": "03c5c8c88a198092929e78f11d486f48468c4b00c7cd968a6027db91473f3390ae", + "signature": "fb8b664702bdd29be4d6523505bea7da45d2e486a45e8cf54333c2b90682e59331090cd4639fcb2dafcc15b3da4d0bc3daee36b1635ab942f3fe3646753cf30000" + }, + { + "msg": "c9683e514178398b2b2d2e42eeb007bdab290e978ee4d2dc436f5c45767fb272", + "public": "03d136db028b07a40d4d94d1b9679d22b60276016104f0be6fb439e3ce27d7ee45", + "signature": "6f6e4b846fc946b0089e24f27979889b47b6f83443f26464a16f5d1e9d816c0b329b4c29b9ec7c2e192a52c3188ff9c66885bb9bf3c54d8160699972e9842e3b01" + }, + { + "msg": "1d849c1c2530a799af707957ed2b271939d4765d412880fa96473fdeb7cd2ad9", + "public": "0212b62cb9288d72aed1076b84e30a5b4625c2f01cbc2529fbfc6771859adc2910", + "signature": "11e22cb59dca2b574415094b0f042db9cbe4b6a2c54c9f693bd2b0b601fc9bc31f66b30ff63fe8496026cd4f039dd90b07db6aeb182f77d749ca39271ef1555900" + }, + { + "msg": "9ab54651db79bb295e682764c4ecb923c2e237db451ed176ef0af7bbb21de1ba", + "public": "026c8e922f52cbe0b6ed9c92b5b5ddb6b65d194d035eb6d8a9778ea37fa4603f98", + "signature": "c5532f626356c235b94f7a1e0d2075ccb0b38300e24a9a56c7b6548b7cbbf9103e00846c0f3c3b8da0773a0ee6261e1432cfc72f30e9696ad5260bfc975e857901" + }, + { + "msg": "bc02dc328a6a279122370599e8cfc2a8e01a9e453ddffb84b9408cbdf82c5f6f", + "public": "03a01795f2a337723d699ea1959affafb3c00169f5f76993a52fd2f3ddfdfb859d", + "signature": "065a0a24b7fad521ed6cbb8c6540904a6683c21e73e6216fa44b031dbc6b29d4069f6c02562ca547a1bcf3b75320b6eb944041113952d46979cb7d2e5a958da901" + }, + { + "msg": "032a86fdd8a9a73d979f0433722f831ece0235d538bc08aef1e8a48705332402", + "public": "03a48a22cc332106f20207320693114fc1bd50cfeb6687c4ed5ae277bf3fab3351", + "signature": "a6c9dfae8d57c1d451fb48844e6e86775f6cc207b197a59369e988368d8e7c2151677e22bef5cbca142717b524a2d2f088cb366825597ad60992d7fc13953ac200" + }, + { + "msg": "c4aba052904d82d06baa56ccac0c8d4560d91cc0fe390967d09f21ab19a825ff", + "public": "02686b0eb9c3a8c05220bd3833d1098e4403c40d9c44cda29f38338bcb0a6732ad", + "signature": "a7725043d2988330501e5bed3d278f325ab8e78333e697116f0c0ee626b6cb5264d8e58002b97ad0e7a50d64ff451db028dd7aade0b913fdcd415f33952f470900" + }, + { + "msg": "de1f1838f64215becdcf6abe9848c065e43f36adbcb71fd942b7f7b7d4a83637", + "public": "03c2f8a402a81df49a9032e098424e256e6ac7d0aa2c30624edfeef779ffcb2ad9", + "signature": "cd8ec780d703262b2f08726082adce5670e3064b8918c9b7bfae2cd1de47689b641d4bff92924b2b01f3785a4ba2d130d199ece4e05268ba30788b6278708e0d00" + }, + { + "msg": "b041cd62f589989af4a9ba2710898544f3b891d8a2831efee4710f1cd2e12f4d", + "public": "03724e0e6593a43c5778f71f02481eabcca8c2bd7259966df427d57e76fe9611df", + "signature": "de7d1314b8cf60e0e4f6953fdf06a1d0cf8b788c263694baf7fe3893341f3b86094ebfc4e26fe68b21fc884676e0d9886a5ba2e260911f78f0b9fa156e49a57200" + }, + { + "msg": "1b7db2d3218ff8ab06e747aff00fb6b064ca2470fb72ee8048361038a75a5a22", + "public": "0337274e38041176819a3d3d9974b45b37e95131f0cf2e89db6693e3ffbf6f2239", + "signature": "511b00146f3f71c41cd777afe582c9a3f959123b36b11fc69f056e11e73355c1520de8723a532d887d0b02ef6846ca93084f82c74688a30285d64a43bba473a401" + }, + { + "msg": "93f51b4f331b338c9248e43edc6695331a011d7f22213096c77d092ddd8aa379", + "public": "03aaa31287bf17fe5c55b8c3528a8f4debf154ee8972af92f1e357d28bb93a8a67", + "signature": "70ce706cc6047df9d2dc1758380b1670a879eea8a72379a4147a5dc2edb425aa3b34684a14f54115e36e9270ecce6ad44a2594a8c5aec09c2d68da83e815c28b00" + }, + { + "msg": "31e42d26d74bbc7915678bceb4c8afac84b199d1db8894f660db59fdb1b84295", + "public": "037ca7676a70308c461509d0e02f81afc1708a2e0e6d8b6bd7d23f418c772dd167", + "signature": "54527fd21a1970764027b2a063a4a6f6067a073d0ff4b185a550e6c59f4eb29c2acca45978cabab73cb63516a4615e2508bba9cc3c55daeb5e17b4c2b2b7e7ee00" + }, + { + "msg": "15dbd0da6716b1eec5b4b64d453967e6badaf94da86204e6283a832524ccf185", + "public": "03a055f06f9a0ab65f96a95128cdc2d835029583e1a107309e3572a7b1dcd57a7e", + "signature": "d388f75f368c787e293d23a9e02467e3610dfbf39e7bbeea3aa21ed269c6622b69b173ed0e9b09aca5179e6f215a3ed138b46a85ff4b7b7c1ba42d58eec87f6f00" + }, + { + "msg": "ad7fc731d2f6f4ec93a44ba84be8219e5ebb89b8f125b201d7476b67e20156a0", + "public": "022b3ba1e116bfa297f880dfd1460f1942a2adc51f428a24d47fc8f13e6389a3b0", + "signature": "655c90c009643d0152878d21c0fe616196962cd80a89c92cda83ec02047e4ed4310aefb52eb9e99526d1523a3f106e19a5a731c24b003eead8ce9b8b91c5ed7600" + }, + { + "msg": "9d9f11bafb363386b0f5a5931d9b2602f0132bfe12867030009d142a75f1780c", + "public": "03fdace9a987f2b7b03c82eab9d70585fc4df126e81274b0523b87021508cafa98", + "signature": "f234fd76b9648bee6831b3fc770aed902b199460cc80688605a51a9546d19058570935a6ca24ee0b80bba9cb10590fbf3e3033e387bd910629f21cacb579c1ac01" + }, + { + "msg": "638509b6e3bc6c23f7e5dda3c0402d1f15e544ef71c233d7062ebfbd064e02e7", + "public": "038f9d6a94a697bce2786366a735858a9144b7415e11a7980592e0c79f0533fb8a", + "signature": "b6175f8032bf4e0aedba3d459f4779aee92c6cdb5808b70db1b7367ad5a6b6c137bffc4c977cc8a5e744e11ea68bc4a310b97d5f41b44fea4bf808285987d03100" + }, + { + "msg": "a6a0069c8e12bf403d51c38a1118aac395bd9979a1a33b4daf84c5ba7482b970", + "public": "02fa879a673f860f28ef8431031639fa43772c3374c714be1b5f0dddb7dc0e93d9", + "signature": "391c80b0949b3df3ffc350225bbaf34bf92239dcc998efa8a810d17af6c3e18e5ae4b85160be6cd9bd0554d0d2349309747bbd55b901a5cb786c609b24a9be1a01" + }, + { + "msg": "d3ded209637fa67fe6490ac2028488d3aa88d82fb6f538809a3544386e9339e9", + "public": "0226d232a4730d0ddd40cd12b0a5079999371b913a9f73f4d08ff92db600237ae2", + "signature": "ba59f24c5c696a1e88461befaef5b10f66c3aeae09bb24ec14b4508097668dff274b14d445c37dfe84fe52d19a05977b1c76c0341ff99933db2086cf9904b0a400" + }, + { + "msg": "c1a69018668ec96c191e8cd3a0be8fe312938935edf9f07908c33d656c2b5496", + "public": "025e978f74992d9434d2b5d89b1f3729a4f974f00c869f82b2902afce67705d37c", + "signature": "fcc376c1ac54bfdf3523a1fe4be0cc2b3bbffaab1268881da4594c4c0aaa86370d903c0914259738efec172b19fd15da509a0028bc292c0f2c89af78bef11f1600" + }, + { + "msg": "71a733853f250f18e12719ce54896768979dc3b8c5fc8797c9a8e0946f051943", + "public": "02afc1ad2a570ba37ef881f8661f2da86a3e60d9315aaf0b5a7d968deebdd209bd", + "signature": "42c4da5534204fd7989f1212e129aca62db4430fb55bcaf1d7ab0e3fc913fa9600ea8193dffdacd100141e3ca962d2364362581482448222801e1a4998ced1fb00" + }, + { + "msg": "44dabd4f167123937d08a182a35dad7e74de17d8e1716bf15777b46b017eb6c4", + "public": "03a89b1525844d2f6f6a092591ebfeeba51c0f8127e676f3f8f2aff1d90efeeddb", + "signature": "a026bdbe7a209135e4fbb5d353304aef4ff7fe45413deb493186af31ca2fb0893098952cde4edd960bfd26d8b8ed7ba94ea5c5bc65088804c2b9b84f2be06d4200" + }, + { + "msg": "640cb6fb7defd56c7bf8d4dc44de2d6e7837aa93f79ed1de7088301a4447ba19", + "public": "03b611f1a304682fccfd3ab2248fe10ae97c897b701b78ac9c066468fb2bd4493f", + "signature": "7c90909e29537ce8fc6d099a00093c7365ec1400739887224c503787de5350fe430ea3b7d80c82254f26cc40aaa03f713aab337ffd1aeba0b78e9454f92ed69e00" + }, + { + "msg": "9554d3a89a1aa24cbefa13cbbe7bcb57e2b6bb668f99a2734a0539f778a6399c", + "public": "03fcb16f66edfc668e2a47447a35fed46d25ee3e54f2a67a774d974101abd6a03c", + "signature": "838f0d8621afb2ead38ed49306b43ec6219f32d5292dbc61b338d56663df02fe228e396e35ba9935729f38c16940894a1e34cc6dd21f639594ed8ae7ea9c1cbd00" + }, + { + "msg": "7830c6494e4d78e40f3b921f65a09d2d473f4f3925dfdc59386c1f53f80a70ce", + "public": "032b86992498c182b23848cb78d963fa164261890a2609e27764251d7989d2be20", + "signature": "daae45a67ab6a49b2bea152045bc2f00d7af5f4dd39520a4ec7b3d1d20da353666f4fdb79dfd0db62472a5057192719cf3eb6f3883e33d349b6c85b33c8683b101" + }, + { + "msg": "ead59e94a77a16366f9c2f1a6b6d13e128933df61dd358c494d66ff7ff15b2bc", + "public": "036667429c2e3474ae8bd90b703931e033dc74e71e4e7063ce487cf5ec150bbc4c", + "signature": "c92e4fa64b93c4b9c8dca5512c85ad4f86fd129fc961c58580e23c2e81b50b512211a729775ed4d4eb2c2d563436435c01ce49002175b84c93edfef356b84fdd01" + }, + { + "msg": "4887e01f47e15782c1cd1854e6f9d35a4b7058208e8ba28d1eb3a1a7f0a48534", + "public": "02736c00acf2a700aeb019f50686ba33872b5da1c6bf2c0971e7271861d7a26a76", + "signature": "afc1dcb43fc30e365f2086aed106485b8e8b0a8f8b950229799416dcb56004856d10b7a2b81d7e117408ea18a287e95dccedac6222447f831f198d51a78f1e2d01" + }, + { + "msg": "4139cdeb3da2e569059e32083f4dc334e9d8f57123bae99a42f9ed72469db2ac", + "public": "0253e98979b0aaa096bfd2c356513b1a319582379c75c6b959a1dcd1bcb5dbc35b", + "signature": "57abe21cd5fb48a1958ad6d343a2da588b01c896a7d8b8e57218180eab1b504c30e28ed102bfeb90f2323d1322c694f1ab0553ba02cb5eb34624805a02436d1e01" + }, + { + "msg": "cfcbc6fc3e89c4b872f5d974965c468d04e8aee52ee5da9e4a43439e143efc4d", + "public": "02ff53bdf008de96d46f770d8d3b55d2ae3b4f407118181234ac6f464dd0a0c11f", + "signature": "8096e006b8c7ec91a5a8b1c395cb825a5aa7ba22c3bddbe4ccb7a822a239bb7b48d41db6b5eac534a7b955d64fca936d359eb6ecd3de75326d6c8390a9d9907200" + }, + { + "msg": "70eaa7e052ab72438cbd1d214512d6f3f7f61670ff01db555fb7abe2811aaa3b", + "public": "0221f5c9e5da0357b8f97cbd56fb5f3ef973d76f145fc8d9eff78233df88200dd0", + "signature": "3fd1029a3c2c2fe299c4f6546870d03f76745a9d4af56e8bc6ba0ffe405702795a95dd0cde69801deefc721f4069ed106b1b5967768590d825b22c97f510550b01" + }, + { + "msg": "29f97816f151119fdd90f9120eefecd34d2388ec8ba781861072720c52a0d17e", + "public": "03f552f0c96387cdbd390123aba84ab06ed01a327b983b8d2eff5a193bbc95da71", + "signature": "65674a756f23ae12282e5274cbfa57214c9157ef9552095e7794f09dbe377ccb0854efa0c0ab4e9f8a6196d349927f06119dfde257099c5082568b9fb76c1bf401" + }, + { + "msg": "dd1a7bbe7b0be2891e1fe87d05d275cbc95ccd6b078f11ab167ca578221112c3", + "public": "036e369722ac3a6530024e955f165963ba755672f4c252768b3d930ad99dca28a5", + "signature": "d5b2536dd7dfb43d03e056d67a52989078d6835bbcf465527bf202769f1a58071c03643803b72c95b40f1541b60bcf6a91c22b2bb53482389b23691c0019b8ea00" + }, + { + "msg": "edaaff3ac83e27c336e2e6149ef23c78c79582688c97c3665fc695af9fd81615", + "public": "02a5599c94a1458275e351bd8abc305e02d71e49fa5b076157ac3444dc9b27f40a", + "signature": "e2f3726f2799d8adb519c6917e46ddef5df9ba2314d6ac01d7d193fa099325877663f27184c7148a3e551304694b0cca8ac17427c48e8546e0b9a3a6b109943e01" + }, + { + "msg": "5d074e3a702bfdf6633415fb6b64029cc4b6869b36134dafce4e3a7beb2c03ca", + "public": "02695a27576a29680d55fb975fc85109ebf1c6ea90cca7baa92cc5fbffa41ae8ed", + "signature": "932fcfbbbd8f3393780199a8bdffc11c0aa6623ae12ba934e698a2e5a9543a926dc7d7c1a16dade3ce303ed64e5f771b8d2c3e05f61620b8971173c0ab1d09ae00" + }, + { + "msg": "c9a49945fed5087166d2dd110b1212230112be21a68442717209f6300c97e6a9", + "public": "02262ce2f806941fc69d31854954cc3e07d483cd57e67696e7a8ef1c3a547d77e7", + "signature": "6c586e57098b2b0b2ba9a83cb84771ea8a8ef2656a4df84e2363d019c0417da62c4273d89944a5b438f5f916663bf2cda854c6dfe94ae1d1cb5b868c4e847f0600" + }, + { + "msg": "cc2a09e8b0944d5cf91725d8d5a3b87c968dfa3d0c5d3f22b0f77dd110bf9890", + "public": "036b05a4b49d3cbd8ba9301c3a4e0351fc19dbcedf37de190e6e54ffac9af1c9c0", + "signature": "371c064f1801b48e4d739ef3348ef086a4524e15c1605a99478e942f747058647f15e1d49aead9d3f345424f0a07ccd4d36628127782e2fa27b3333ce97e06da01" + }, + { + "msg": "a1cfea85fda469414130156b7fb2f6efa8686bcb836d002c529ab8249842f41a", + "public": "03044976c1829ca459f5d400ddd6d6c756a12cbb9d1a00c9a2dc59a587d9c62b31", + "signature": "bc17fe41a937e9b2fcd1595f3962f4077c7a72a343f4f86fe335b8cc6a6db3ba473c32e9328ff81f08fc8c79e342f04196ef5e57fb6959de5e61137d649dea2201" + }, + { + "msg": "d2991d9cde6be3718a2401ca13b17348c992e02672ea18a5203b948b4730e902", + "public": "0285c681ecfb3a07e50ffd807eaf1e84b3c780a279705ec57bcefab7562b953e92", + "signature": "a71d46c77a339ca3a1f3292edc292473f92a7fed07dc9e58413f3cfe63cc8d7f1c73b826c2be69076b3bfdebcb6bbde07f462d7d30f710afe38a8b62a781e69501" + }, + { + "msg": "b07cc9c52fc2aee15cdd38fc5ae1cc3ae62c1fc8f104bdb06e88898e819a9db4", + "public": "0297a73d8b2c36895ecdb1989fb3d1b24eafecda6bf20e40d7a05ef6f97a11c1f8", + "signature": "4a34e31972f3a6fb8afbf32d893f9c3fb08c97e0aae2c1577bc9aa905ee7714722d3eede66eb55eb104a54650220953d94102e35480daf92d372816e31285b6d00" + }, + { + "msg": "c926456121e620bafe4ca8de0837a50f2c0f1e365b71233bcbda870f24f78e91", + "public": "034c71da194d2abf16f1422bbbeb3b4103839e352aaf6aecb591eb57e8860b2b24", + "signature": "5d13b729f521b259c73535cce9ba7d013c6ad16b36d8dceddcca46751f02e6465d805a0762d5c4dd9d9cf56c89dbb88b82bf588fd6795114bcaa5b0208b6c5db00" + }, + { + "msg": "cd6e8424d5a18e1974f09a584596e565399897edb345f8d56888225bd1f4f90b", + "public": "039f1dbfbfc119896a45a2864b451633785875d445c6a8d34fd94c7c743669b557", + "signature": "5895ed11c6f3688c8f5e7ad341af46e6b6dbe7de54618e02fc6d41c507eaedb02874b578c594427f1f826e50187ad0fb896f8348a2c2c3c7def1e8fe855d9c8c01" + }, + { + "msg": "1b099a065ed5448c599a09966d804b7451a08a6a7f66ac0608f66fc958465604", + "public": "024f6464a3caad876d300c3a733441e0c9ec104d1503afa15d8c6b3d1b96c3b411", + "signature": "73b662a2c80ca75f54ca4dab84aebcdd8dd77131bd02be947a3e936b54bd263d1e4a53baedafd3e25c0a7902b0434aeb534625801dadfefb9b84cfa32d7b705601" + }, + { + "msg": "5df9c4996c1f4db5972d75d963c08106cb1620cd51b6756734655764dfc49722", + "public": "03b6514ae0c0d7eaf2bd968573b375d751fc5aa5937cccefb9a81e47831549a7bb", + "signature": "4919015c4fef75648b24e870c9a05fdc2ea4835ffb846cca397694279b56a4e85b338a63b66cfada1cd8a9f4a43a7302c7ace1bb53c211b83e5e7f5849949d2100" + }, + { + "msg": "c2a252397d9fad25d7eaa173fdd3b185620c3601263e19b2974f75d32d77c566", + "public": "03c84f9de9480fdeaa0ec28070b825e96a31a81206e4ea3e8db4b9881b03f1f477", + "signature": "1a85ce67a516c89a35e85e32e23d2229a026fab3f2a470a2b14420d9b969302f34485feae37a5d11a4ca28946fd469515a2f742ac5c0e55ba6890689c250c78200" + }, + { + "msg": "aaba181ebf3d4e020710378d3a0525e9e90a858a6680a59777017dc4360a61d3", + "public": "0354f4be049e42e0a244ad3f0be1d406f0a53d6300f59f9a96272ab7ee27a950b9", + "signature": "1e56f54882d54cbd975c4ed963dbc717d6ae5fd0a5adc690fe85243ca69c6d6506a53a3b547497e335a20f835f54089977ba34d5360eca2eada2a818d5e7744800" + }, + { + "msg": "74e7cd4dc90a51f3e7a24036f455eac8fac3a0ac122e5ec89e724d3d2a24c6e4", + "public": "022d2d32b9e2440b8c1775d0d61208942002a3839282f5c50fafd34ab47512406d", + "signature": "5f39d0a4edd6a4153fef5d6a718494862a14283ad7c5700260ccc86273f5af7537bd3a920b86356b0db81bf11daeb4592b1e878b30cd93576396657adca8adb101" + }, + { + "msg": "e4203fed0e7eeccca4c7c0db5ab8c054c6b6562ae431dd027bb42f0bde51a8a8", + "public": "02fd3f9da44e91850f37264552966d28112b7f4ee58242d4a1dc7a694249aa36c2", + "signature": "bad8d91a1bb9fcf46c08a0441ec7de319a175a117d737a983bbd40ff159335c551ed0fdf5ca2db8727bec73b09113e319da4590105e1be1158a19bacc93e3b9400" + }, + { + "msg": "7ebb53b846dee758813ddfaa80d1b352e8a5a404b4b99f3cc71611f18d1c9d17", + "public": "024363bba43a2ba7e524a58393f00e5dc59dc1aebdc1d03b4dc3e79b13b5478ae3", + "signature": "fdb3564389ae66755530055dca9c7c00ebcec3c20ed7562e09d81692cd244ea04e76fe615d9f677e1da81178e360136e67be49802589c3d8fbf2d3572e0e663401" + }, + { + "msg": "0c5900e8335f0aa1ef3c50fad6f5d15fdf154188fe05428b1b36b0a564c3160b", + "public": "03742c266730be91b922933c2fb016292223c540999559f8ae744eb668d8cef93f", + "signature": "a577717ce057a1643e7cbd5d4262df5325475c541d44793690820a59dae775b750a610ab0a2a45e0cec3e17010ffb2e612ee1c055d15c8d2bb904ee284276a2401" + }, + { + "msg": "cfa5c2bed201258ea8bec3f63caa83349fa9707b26223584d87b383e1a6b011b", + "public": "039fd9b76dddd5fce9ab07aaabc1a44d75f488e3df3328125fefb387230fcdca5c", + "signature": "948d6e20f634774e77d1fe5ccdcecfd46fc3290084f9528d1ed3c9155d9f9155274d2d93a17f4b3d6324eb0d97d36c9cd461182a3ac61147f3381203c72ee04700" + }, + { + "msg": "9ad75cfe7e400ade47f8c608bb8f87b1fd3adf0fcb2bf93b9bc71d3e2f4c2ebc", + "public": "02a437757a6cd3f86aedf0eebde30247ada9f49c2006a101af919b37c871c2ca79", + "signature": "32ba9cf2b53cc809cd9d07a5b34660afb95550d9c1bcb8b08e0023fbe26fb573787458b92f2022e4e403a5c0d1ea0061cf595efa0666ffe4c1c2e446d09c592d00" + }, + { + "msg": "79b20edf913b547f23f91ea6365bb1cba64757e6586a44efeac139f863a4e882", + "public": "021386b7c193235bab9c6d3abddc7a303d32f18a77e8c21ff54056b2ec035d8ae3", + "signature": "db22ca7125252deaf6505747bfe8f029699e978b83c0c4576f59a119ae681318407f3abb83b4955db429d8cce8fe7785c42507da0338fa98f07e2cc64c43713001" + }, + { + "msg": "7fe4e9277997144b95cf3170c263fb2f995dde699f0f8267339e007cd38dd35b", + "public": "02dcc22860f9faf3a05ef478f9f79aab337cd2bdeb9ca726b5554960b7a587f781", + "signature": "c88a4da89205c15e56fc1929ffa4bd415756d013e6d20f22fdebf61fa8a3e6283b89311964bcf1777b2ec6f64caf3afe34b718ed0a375e7bdf3b58496505aee401" + }, + { + "msg": "1b6da9500d3387cb95b0bfa612414e7ea7cea45dc4664f4965b94636a1e5133f", + "public": "030b06ad28c89798cb8910f4b5938ad4a14b0b1576e8c7381c8bbc09f45d5dfea5", + "signature": "13f72cf3428cbe4971b84718079948f3862042a04229809a3f1bad0ffbf542652db40fe879f354d3412c76631a7fce4d8c8c0f73b5f5fad4e0e7c6af33a03cd500" + }, + { + "msg": "a427043ccb244f6a5d618a81fbc797cf380918ea2b50211a20a8b13804cc57b6", + "public": "0212961fd2d031323f6f100bd45777b393666676b6b8763f99b52d6aef4563a5f5", + "signature": "b9e9a08c2ca913f091c0257edacb524c17fe99fb8620d56aad2526db5436615a0c25b6e6f151f96e68d4a3a896415caef08a686b6ae6b9983d60627a872f1de500" + }, + { + "msg": "aa3fd54da89092eb3661e293b62d0d21533d5494b4ba190132033130f8de6927", + "public": "0271b5826c54df70df8accea20767c67a1e2cbf3b387e1b3f9d76071bbe1bf8824", + "signature": "cf4265f4dd8c2a59c8a0d98daece29b90757e23272e297c81c633e9f22465b186cdb062352411ba4d4736533605a7125b55f1258f8d8fa14516c0d4a7dff8fa601" + }, + { + "msg": "f6f902a83438757b667cd2caff21f87ca916e5f7068dd7ecef781992f31d2980", + "public": "03831409e8b3c894bf4a8a3cf5461642751a77fda4aa34836acf72a01505debd87", + "signature": "b32b0e2fa5c1312ce6257bfe30e5a7d1ae7b2bfe28da65116abf51aed4d66d7d323ff1dce58d438f6f3dbe164178ab2f44f08e2c967e54f186d9d5662465e57101" + }, + { + "msg": "d3fd3c44ee37d86a45d95a3ab85f85334432f2dc207d819692a15b6ca0103899", + "public": "03ec2cc840c200b0282e23c1bcb3e34c319d56b29593792e0d595ef370fe266240", + "signature": "5c9efc97ba2fac911b3e36a336a847ed53bbeda85eb5f44b84609aef16a1bca72593465905e377e7a0183ecb35a6258aac79514716e888c93fcd3868ba1fe64b01" + }, + { + "msg": "afeb30e2f4ebc983903610df2e874ce013e033ba77d02b6c93e2d3ca4236b572", + "public": "023355ee3cf4d8cc677d7aa47afa2c4a16b10f7f9925b8af4c847014769115abd6", + "signature": "0148a3eb23c4374f4bbaf5ac747c34f8c8d7e2050e4d8d8175ea3ec4160912a25e64303645c8c7739512ab2b7ddc63c833d1bcb8ea0e958c2e5de633948d1bb600" + }, + { + "msg": "08ef80a7e1877311aa0d6165f4a0d6add54307e92b2bf8116c17ee36747e70b0", + "public": "03eb0e806bdaf027c57e2f0ed9537daef0a2320e38ba9c6f401cbdc69ce477e8cc", + "signature": "a7f69995eef81cfe65277458246a9bd4f35215f5096f67b5fb0e4f47742c98bd053a4d3d9b54c6beb11941b77a04de740a9f66d1b3f6c1c0545a47ee0b0e83d500" + }, + { + "msg": "c31de17e163cb83444a631525f5e96b4bcac4e8ed62884b8c0c5e916bf5149cc", + "public": "02736f27236545a60767f86440436106ff8f4f8ca13c0483623679bdae813f08f4", + "signature": "28955e49d5259d388e817335aa49dd86b91d4ce948b44bbea9e407dfa984ebd0023f4306835d7b08f7fa0473b264c83b8c82192a05ace5758c1fd56bb744b5f800" + }, + { + "msg": "e413c3499458047a321e25198e88a9e92938232f4c21abe8b810b54ea2ccfeac", + "public": "02ea60309fa72ba7040a56ebb02cc20ff88a7b586daca1b1af5d1d9f0cb5278c58", + "signature": "bfedcdf4639f26244730a6022c464c7d57ffe08b1b42539c3d06aeea6358cbde5ab2a9750390732c441194d2a81e56d5e1bfd0e7ea077d749f576019186c2d2f00" + }, + { + "msg": "d970fd9c7142128ffb1bab8db0d053a1a382c29b11c9554d8e9863b54a82f310", + "public": "030f7b15dd1b9e0c1c698821f9672fdb369485f94257b4ec4ebe982f7fd49b0441", + "signature": "54ef9d78d1fd3a1df95bee9fb5b8606c08be12db26d8d119388bf29a659d20362b968c5e4622871036d6446e06929ddede65d26181a3994e455e4242fec7f90000" + }, + { + "msg": "8c608f2b20474c24ab9506ad40d193d7596b79ed37fe71a94a15e2313f808a45", + "public": "03332c8a79698042e961a8d896a092186eb1da519d7816fa707d0ec215b7d628f5", + "signature": "af46e873dbaafcd798dd945d002e44b325d1f62fcb1e43709c86bb06415492154ba275cad180422552d718e6efb0b7cdc70294332f2168cf6a3a4439e17983ee00" + }, + { + "msg": "6541cb0acf1d394a2c4de1d7651070003f56680d42f22c3ac9372862b4858637", + "public": "02773269d3c3160bc36c120508969d815d59648a92b11c76522cf4b391d70cb10e", + "signature": "70f57b1f35b8f9c3059f4892c774badfeeef7de98b622f1a90541fe85b273f363d2e9149aeb45ef4c6c561b27dc4a582284c9adbd497ed67dcab7731522c9b4d00" + }, + { + "msg": "5157b4b9d2ea016981c24657332b0c6ab58bdd86bf4715a4ee4015a4c84be4a7", + "public": "02dcdd7b9c3ab33a2f38f0b072edf38a4d9d8c329b590f04bc6fee0e69317a0bdf", + "signature": "090fc815decb1703feceb0e4770b7dae4b966eb712c20409cd9ce0dfe78b9cb0015ece2b85aef0a5f7170016533ce29c525e6a9b62a4374f47e2e81d2000f41d01" + }, + { + "msg": "3e38d8ef65935ff97c6f4bb8e1e26a2c83dfe54819f6a36013b6b2b390a28a37", + "public": "037af3e2f3315d6c2e19e2c3a78ecbce27d0d5bf651367421ee3e168ae728de75e", + "signature": "b14b95ed8833f3ef04d71c9e5bbef0e03d1b464015322ed9828528f014fa385931f23a017f6ecd201fe54b8f5b8dc2e907e226047296bca9207b25ff71a1225e01" + }, + { + "msg": "262b95b3a45338fbf224aa641b28735bd714deda1637d2bad3cebf4b8f76afb1", + "public": "039f0236f9bcdbf1f6ff4d2f683efb0370c00a216e86fb1b27bebae2bf52449ef6", + "signature": "bb6a6c92c8aecd3ed8ec2936b78558f452ee2366f7152ba601f1e39fd8d4b12178c2b52e4aa7f7d98a8f7674e8564a8ca81a23a3320bd7f1b9f72be6f50b7d1300" + }, + { + "msg": "d4e015addf1574b6afc6a42a37632fcab008e993506ef3313aba6bb7a976e056", + "public": "0204a85da62fecc3f902d84f920f6a1a71736df3c48d56e17ec43c71877a59cff8", + "signature": "4bf07f5c7e7228ca3a4a179e06590cf4eae89980d76a2d1b4f8859a870ca533b341df4b24fb991d993b6f26d5d62fe9071453690f6e25be6482ae1c8dc4c5c5200" + }, + { + "msg": "c5e55894dcfae05f0a0caf0392a0307833cae1303936522fc60e80ff5a3bbbb9", + "public": "02185a34bba433a3691f27daf47b20a721c1c5c39fbf648417cbf4c4e8f0b1e38f", + "signature": "705d5d3570765b181434539cdc6d200c933b2f3fa786d879962f45ea71a8c1864e1d085e5b8c67e83253f064b2876928484e398922d2015ae78098d76af0698e00" + }, + { + "msg": "b63d5ab659ea147e1bacde7a4595a8416822f9a32afda8305d7a0de28c80ae5a", + "public": "03bd1f84e3e9ef3db1447fd5f3d5dbf5a13049790bc94750e4c844e2cf618953b7", + "signature": "de0d6663be255ecd5ed52b2e21071833ba8455abe3667c3720aba72a0548d1c32a4bc7422758fd53492ccca1eb5acc0091f13af3f77ab9e29232350f4aa71a3700" + }, + { + "msg": "dba2999a5578177f5b93468f404be90447b1633bd7626caaa1975e5cdb741e26", + "public": "0295934c21feb5c14635b3d0231b3def9a055eb459f4157103d0aca731ae79deca", + "signature": "5172095511225a04c7f27b29777f3908d3d9703481d56b703448776dc7bbae4f636ec4a62a84f3e8ee0b4e9021e71013b2827598fb8055003ac10f1bbb8a09c001" + }, + { + "msg": "b767d4e5e8cb0b4a8db8d0730e89a8daafcb370316cea5b2ee201d11fde1bc92", + "public": "02812f641e155989a0479747194c849e6568b7ee22ff2cb3b5ce2ce6fb59832d92", + "signature": "8ab2c4c03eb2b56ec47519022e72128b12546c32794050aa57137d4507ea0d701cdab1b778a520304e51a2012a84d71a51adecf078c1f24f6bc13f36f31277fa01" + } +] diff --git a/rust/tw_keypair/tests/private_key_ffi_tests.rs b/rust/tw_keypair/tests/private_key_ffi_tests.rs new file mode 100644 index 00000000000..7092f7b7407 --- /dev/null +++ b/rust/tw_keypair/tests/private_key_ffi_tests.rs @@ -0,0 +1,210 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_encoding::hex; +use tw_hash::sha2::sha256; +use tw_hash::sha3::keccak256; +use tw_hash::H256; +use tw_keypair::ffi::privkey::{ + tw_private_key_create_with_data, tw_private_key_get_public_key_by_type, + tw_private_key_is_valid, tw_private_key_sign, +}; +use tw_keypair::ffi::pubkey::{tw_public_key_data, tw_public_key_delete}; +use tw_keypair::test_utils::tw_private_key_helper::TWPrivateKeyHelper; +use tw_keypair::tw::{Curve, PublicKeyType}; +use tw_memory::ffi::c_byte_array::CByteArray; + +fn test_sign(curve: Curve, secret: &str, msg: &str, expected_sign: &str) { + let tw_privkey = TWPrivateKeyHelper::with_hex(secret); + let msg = hex::decode(msg).unwrap(); + let msg_raw = CByteArray::from(msg); + let actual = unsafe { + tw_private_key_sign( + tw_privkey.ptr(), + msg_raw.data(), + msg_raw.size(), + curve as u32, + ) + .into_vec() + }; + let expected = hex::decode(expected_sign).unwrap(); + assert_eq!(actual, expected); +} + +#[test] +fn test_tw_private_key_create() { + let tw_privkey = TWPrivateKeyHelper::with_hex( + "ef2cf705af8714b35c0855030f358f2bee356ff3579cea2607b2025d80133c3a", + ); + assert!(!tw_privkey.is_null()); + + // Invalid hex. + let tw_privkey = TWPrivateKeyHelper::with_bytes(*b"123"); + assert!(tw_privkey.is_null()); + + // Zero private key. + let tw_privkey = TWPrivateKeyHelper::with_hex( + "0000000000000000000000000000000000000000000000000000000000000000", + ); + assert!(tw_privkey.is_null()); +} + +#[test] +fn test_tw_private_key_delete_null() { + unsafe { tw_private_key_create_with_data(std::ptr::null_mut(), 0) }; +} + +#[test] +fn test_tw_private_key_sign_secp256k1() { + let secret = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"; + let msg = "1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"; + let sign = "8720a46b5b3963790d94bcc61ad57ca02fd153584315bfa161ed3455e336ba624d68df010ed934b8792c5b6a57ba86c3da31d039f9612b44d1bf054132254de901"; + test_sign(Curve::Secp256k1, secret, msg, sign); +} + +#[test] +fn test_tw_private_key_sign_ed25519() { + let secret = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"; + let msg = hex::encode(sha256(b"Hello"), false); + let sign = "42848abf2641a731e18b8a1fb80eff341a5acebdc56faeccdcbadb960aef775192842fccec344679446daa4d02d264259c8f9aa364164ebe0ebea218581e2e03"; + test_sign(Curve::Ed25519, secret, &msg, sign); +} + +#[test] +fn test_tw_private_key_sign_ed25519_blake2b() { + let secret = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"; + let msg = hex::encode(sha256(b"Hello"), false); + let sign = "5c1473944cd0234ebc5a91b2966b9e707a33b936dadd149417a2e53b6b3fc97bef17b767b1690708c74d7b4c8fe48703fd44a6ef59d4cc5b9f88ba992db0a003"; + test_sign(Curve::Ed25519Blake2bNano, secret, &msg, sign); +} + +#[test] +fn test_tw_private_key_sign_nist256p1() { + let secret = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"; + let msg = hex::encode(keccak256(b"hello"), false); + let sign = "8859e63a0c0cc2fc7f788d7e78406157b288faa6f76f76d37c4cd1534e8d83c468f9fd6ca7dde378df594625dcde98559389569e039282275e3d87c26e36447401"; + test_sign(Curve::Nist256p1, secret, &msg, sign); +} + +#[test] +fn test_tw_private_key_sign_curve25519_waves() { + let secret = "c45d1ba60a5d929d228d1b69a8f91bd256262498e81d32b6411d6dac9a60ed3b"; + let msg = "e6167d"; + let sign = "05db2483b8107187448e7f3d5581e48380a83338d53fe70c59e88b281b995216085518e5d331a2b698f2d0d387529dd94437157df88cb14be8d1925afbb6e80d"; + test_sign(Curve::Curve25519Waves, secret, msg, sign); +} + +#[test] +fn test_tw_private_key_sign_ed25519_extended_cardano() { + let secret = "e8c8c5b2df13f3abed4e6b1609c808e08ff959d7e6fc3d849e3f2880550b574437aa559095324d78459b9bb2da069da32337e1cc5da78f48e1bd084670107f3110f3245ddf9132ecef98c670272ef39c03a232107733d4a1d28cb53318df26fa\ + e0d152bb611cb9ff34e945e4ff627e6fba81da687a601a879759cd76530b5744424db69a75edd4780a5fbc05d1a3c84ac4166ff8e424808481dd8e77627ce5f5bf2eea84515a4e16c4ff06c92381822d910b5cbf9e9c144e1fb76a6291af7276"; + let msg = hex::encode(sha256(b"Hello"), false); + let sign = "0a8c6e36f9afa324e9065d185cf5df2815c6b997b2e1627e8612ddba8097dcfc325ad6b2317cda2159407463cdd2706af97f299873e940f43f986951f8809108"; + test_sign(Curve::Ed25519ExtendedCardano, secret, &msg, sign); +} + +#[test] +fn test_tw_private_key_sign_starkex() { + let secret = "0139fe4d6f02e666e86a6f58e65060f115cd3c185bd9e98bd829636931458f79"; + let msg = "06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76"; + let sign = "061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9a"; + test_sign(Curve::Starkex, secret, msg, sign); +} + +#[test] +fn test_tw_private_key_sign_invalid_hash() { + let tw_privkey = TWPrivateKeyHelper::with_hex( + "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", + ); + let hash = hex::decode("0xf86a808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000808080").unwrap(); + let hash_raw = CByteArray::from(hash); + let actual = unsafe { + tw_private_key_sign( + tw_privkey.ptr(), + hash_raw.data(), + hash_raw.size(), + Curve::Secp256k1 as u32, + ) + .into_vec() + }; + assert!(actual.is_empty()); +} + +#[test] +fn test_tw_private_key_sign_null_hash() { + let tw_privkey = TWPrivateKeyHelper::with_hex( + "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", + ); + let actual = unsafe { + tw_private_key_sign( + tw_privkey.ptr(), + std::ptr::null(), + 0, + Curve::Secp256k1 as u32, + ) + .into_vec() + }; + assert!(actual.is_empty()); +} + +#[test] +fn test_tw_private_key_get_public_key_by_type() { + #[track_caller] + fn test_get_public_key_data_hex(tw_privkey: &TWPrivateKeyHelper, ty: PublicKeyType) -> String { + let tw_pubkey = + unsafe { tw_private_key_get_public_key_by_type(tw_privkey.ptr(), ty as u32) }; + assert!(!tw_pubkey.is_null()); + + let actual = unsafe { tw_public_key_data(tw_pubkey).into_vec() }; + unsafe { tw_public_key_delete(tw_pubkey) }; + hex::encode(actual, false) + } + + let tw_privkey = TWPrivateKeyHelper::with_hex( + "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", + ); + assert!(!tw_privkey.is_null()); + + // secp256k1 compressed + let actual = test_get_public_key_data_hex(&tw_privkey, PublicKeyType::Secp256k1); + assert_eq!( + actual, + "0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1" + ); + + // secp256k1 uncompressed + let actual = test_get_public_key_data_hex(&tw_privkey, PublicKeyType::Secp256k1Extended); + assert_eq!(actual, "0499c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c166b489a4b7c491e7688e6ebea3a71fc3a1a48d60f98d5ce84c93b65e423fde91"); +} + +#[test] +fn test_tw_private_key_is_valid() { + fn is_valid(privkey_bytes: Vec) -> bool { + let privkey_raw = CByteArray::from(privkey_bytes); + unsafe { + tw_private_key_is_valid( + privkey_raw.data(), + privkey_raw.size(), + Curve::Secp256k1 as u32, + ) + } + } + + // Non-zero private key. + let privkey_bytes = + H256::from("0000000000000000000000000000000000000000000000000000000000000001"); + assert!(is_valid(privkey_bytes.into_vec())); + + // Cardano private key. + let privkey_bytes = + hex::decode("089b68e458861be0c44bf9f7967f05cc91e51ede86dc679448a3566990b7785bd48c330875b1e0d03caaed0e67cecc42075dce1c7a13b1c49240508848ac82f603391c68824881ae3fc23a56a1a75ada3b96382db502e37564e84a5413cfaf1290dbd508e5ec71afaea98da2df1533c22ef02a26bb87b31907d0b2738fb7785b38d53aa68fc01230784c9209b2b2a2faf28491b3b1f1d221e63e704bbd0403c4154425dfbb01a2c5c042da411703603f89af89e57faae2946e2a5c18b1c5ca0e").unwrap(); + assert!(is_valid(privkey_bytes)); + + // Zero private key. + let privkey_bytes = + H256::from("0000000000000000000000000000000000000000000000000000000000000000"); + assert!(!is_valid(privkey_bytes.into_vec())); +} diff --git a/rust/tw_keypair/tests/public_key_ffi_tests.rs b/rust/tw_keypair/tests/public_key_ffi_tests.rs new file mode 100644 index 00000000000..9922b0fd8a3 --- /dev/null +++ b/rust/tw_keypair/tests/public_key_ffi_tests.rs @@ -0,0 +1,124 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_encoding::hex; +use tw_hash::sha2::sha256; +use tw_hash::sha3::keccak256; +use tw_keypair::ffi::pubkey::{tw_public_key_delete, tw_public_key_verify}; +use tw_keypair::test_utils::tw_public_key_helper::TWPublicKeyHelper; +use tw_keypair::tw::PublicKeyType; +use tw_memory::ffi::c_byte_array::CByteArray; + +fn test_verify(ty: PublicKeyType, public: &str, msg: &str, sign: &str) { + let tw_public = TWPublicKeyHelper::with_hex(public, ty); + assert!(!tw_public.is_null()); + + let signature_bytes = hex::decode(sign).unwrap(); + let signature_raw = CByteArray::from(signature_bytes); + let msg = hex::decode(msg).unwrap(); + let msg_raw = CByteArray::from(msg); + + let valid = unsafe { + tw_public_key_verify( + tw_public.ptr(), + signature_raw.data(), + signature_raw.size(), + msg_raw.data(), + msg_raw.size(), + ) + }; + assert!(valid); +} + +#[test] +fn test_tw_public_key_create_by_type() { + let tw_public = TWPublicKeyHelper::with_hex( + "02a18a98316b5f52596e75bfa5ca9fa9912edd0c989b86b73d41bb64c9c6adb992", + PublicKeyType::Secp256k1, + ); + assert!(!tw_public.is_null()); + + // Compressed pubkey with '03' prefix. + let tw_public = TWPublicKeyHelper::with_hex( + "0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1", + PublicKeyType::Secp256k1, + ); + assert!(!tw_public.is_null()); + + let tw_public = TWPublicKeyHelper::with_hex( + "0499c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c166b489a4b7c491e7688e6ebea3a71fc3a1a48d60f98d5ce84c93b65e423fde91", + PublicKeyType::Secp256k1Extended, + ); + assert!(!tw_public.is_null()); + + // Pass an extended pubkey, but Secp256k1 type. + let tw_public = TWPublicKeyHelper::with_hex( + "0499c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c166b489a4b7c491e7688e6ebea3a71fc3a1a48d60f98d5ce84c93b65e423fde91", + PublicKeyType::Secp256k1, + ); + assert!(tw_public.is_null()); + + // Pass a compressed pubkey, but Secp256k1Extended type. + let tw_public = TWPublicKeyHelper::with_hex( + "02a18a98316b5f52596e75bfa5ca9fa9912edd0c989b86b73d41bb64c9c6adb992", + PublicKeyType::Secp256k1Extended, + ); + assert!(tw_public.is_null()); +} + +#[test] +fn test_tw_public_key_delete_null() { + unsafe { tw_public_key_delete(std::ptr::null_mut()) }; +} + +#[test] +fn test_tw_public_key_verify_secp256k1() { + let public = "0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1"; + let msg = hex::encode(keccak256(b"hello").as_slice(), false); + let sign = "8720a46b5b3963790d94bcc61ad57ca02fd153584315bfa161ed3455e336ba624d68df010ed934b8792c5b6a57ba86c3da31d039f9612b44d1bf054132254de901"; + test_verify(PublicKeyType::Secp256k1, public, &msg, sign); +} + +#[test] +fn test_tw_public_key_verify_nist256p1() { + let public = "026d786ab8fda678cf50f71d13641049a393b325063b8c0d4e5070de48a2caf9ab"; + let msg = hex::encode(keccak256(b"hello").as_slice(), false); + let sign = "8859e63a0c0cc2fc7f788d7e78406157b288faa6f76f76d37c4cd1534e8d83c468f9fd6ca7dde378df594625dcde98559389569e039282275e3d87c26e36447401"; + test_verify(PublicKeyType::Nist256p1, public, &msg, sign); +} + +#[test] +fn test_tw_public_key_verify_ed25519() { + let public = "4870d56d074c50e891506d78faa4fb69ca039cc5f131eb491e166b975880e867"; + let msg = hex::encode(sha256(b"Hello").as_slice(), false); + let sign = "42848abf2641a731e18b8a1fb80eff341a5acebdc56faeccdcbadb960aef775192842fccec344679446daa4d02d264259c8f9aa364164ebe0ebea218581e2e03"; + test_verify(PublicKeyType::Ed25519, public, &msg, sign); +} + +#[test] +fn test_tw_public_key_verify_ed25519_blake2b() { + let public = "b689ab808542e13f3d2ec56fe1efe43a1660dcadc73ce489fde7df98dd8ce5d9"; + let msg = hex::encode(sha256(b"Hello").as_slice(), false); + let sign = "5c1473944cd0234ebc5a91b2966b9e707a33b936dadd149417a2e53b6b3fc97bef17b767b1690708c74d7b4c8fe48703fd44a6ef59d4cc5b9f88ba992db0a003"; + test_verify(PublicKeyType::Ed25519Blake2b, public, &msg, sign); +} + +#[test] +fn test_tw_public_key_verify_curve25519_waves() { + let public = "559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d"; + let msg = "0402559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d00000000016372e852120000000005f5e1000000000005f5e10001570acc4110b78a6d38b34d879b5bba38806202ecf1732f8542000766616c6166656c"; + let sign = "af7989256f496e103ce95096b3f52196dd9132e044905fe486da3b829b5e403bcba95ab7e650a4a33948c2d05cfca2dce4d4df747e26402974490fb4c49fbe8f"; + test_verify(PublicKeyType::Curve25519Waves, public, msg, sign); +} + +#[test] +fn test_tw_public_key_verify_ed25519_extended_cardano() { + let public = "57fd54be7b38bb8952782c2f59aa276928a4dcbb66c8c62ce44f9d623ecd5a03bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4\ + 06465638ee0e1ca1a9f34d940a0c8c48bbaaa0db124107e4c1b12b872a67511fed7f28be986cbe06819165f2ee41b403678a098961013cf4a2f3e9ea61fb6c1a"; + let msg = hex::encode(keccak256(b"hello").as_slice(), false); + let sign = "375df53b6a4931dcf41e062b1c64288ed4ff3307f862d5c1b1c71964ce3b14c99422d0fdfeb2807e9900a26d491d5e8a874c24f98eec141ed694d7a433a90f08"; + test_verify(PublicKeyType::Ed25519ExtendedCardano, public, &msg, sign); +} diff --git a/rust/tw_keypair/tests/secp256k1_sign.json b/rust/tw_keypair/tests/secp256k1_sign.json new file mode 100644 index 00000000000..42c29431309 --- /dev/null +++ b/rust/tw_keypair/tests/secp256k1_sign.json @@ -0,0 +1,502 @@ +[ + { + "hash": "e47a0e9d0444a58e4aa423b92734280d9c754dbf05e488f69c496105236577ea", + "secret": "cc446e8442f76121d3fc366e0d0ab30920ad4d1f02010f2d715622ab261962a4", + "signature": "bc70bd3d2e8ffdb2dc6ca44e19696e505b3c4890d51963f5eb387b1d21b70d0605f22f354e81e9821c1ce5f6e26b88b3207b030ad455cd27079bea14b3fc7d8201" + }, + { + "hash": "8b2f77ece11bab3cb3cd62c24a655af6399d87d3fd084e50c4d8aca31fe5e6c1", + "secret": "7057b3fa5dd6d46ffa291530630a14c79973efa3f3ae9b7393bfb40caf90887a", + "signature": "5305bc280335e1ac1de5814e7079810ad5f1ab6e26e9ca4aa1ff91454889ac495176bec4dc64967e90215a7e5b13adc2fee0c6914f27093ab3c27250d861101f01" + }, + { + "hash": "1cbed9daf28b8baa54c91578cb81a7fc0149287ad78a392538a6ea34d382908f", + "secret": "36a45e08fae0b1bb715e2a187715343727359bee1a475f53f5f107d9b911f554", + "signature": "672a72b22ccb35129defdb671c2f3c11070e0c4fa912edfe2e30130640c3febe5ae9cacd0b44533a4492b3d56c276399f727fb4bd9c0f48cc91bb7cf464558f900" + }, + { + "hash": "0a9f9273696da5bbbc45c169defaa5b67e1b907b0554400f28ff1134f2399f8b", + "secret": "b304f3a5467454e71141afb281565b1cbd45b56b719e77605b8cd9c28217eaf6", + "signature": "e62704eb552e0c188e15e1d67e2f4e707d3562ca72edd31a11967109923ed07c06f30297cf701e7a05e0873654274ce79aeb7de6355bf6577ca8d42f280c65d401" + }, + { + "hash": "1e02ef222b0dad8126c3e67fb9e331953b2b7a838396eba93d7cb300dce250bd", + "secret": "d287cbca0ba0e5e595137c138e74cbe623173aab9b5d396307bdc396adab1ef8", + "signature": "34c7da02b40ad41c887581c4e0978d50fb9eabf9a7bb22d7cf3809de33f3ec737561f9965a315566fe0216b6b9a629af373c1ab897ab16bff0320550cddd3e1300" + }, + { + "hash": "7016b6f5e3527de8353c8b7406683dd27e8afcfd7a132269d1bb1d3b6c2abece", + "secret": "20be2b599db8e62388a88e04eac527bf5b66924b1fc5ddee41bc76a2870115a9", + "signature": "2b2260a994488642b9c5c40630563b0f0adcbaccfeffe98e8bf4aeeac5c9722d15878d4df619bac21ce9bbecabcc185522bc4adbee5cdaa31e53a4e4c0a591ee00" + }, + { + "hash": "ea2d778a76d19bfa4fef744485582291587e7c117c092055e0ec250b02a1dd82", + "secret": "2a8f9d590eee3785a12460029d16c7d324a1102e30af5183d77c70b3883354d9", + "signature": "9fdcd89858e8c376ec68c9099587370a6418beea052ea6562fe0ee7ee0532873620d0e3c6ab90ea0c0f675f11311e2ba509cd01d45ff4c775fc7197a85f35b2501" + }, + { + "hash": "bf8a405154b95076dd08e0eae0e1cbde8addc8c03ee9685f8471b1d59b97c10e", + "secret": "50ef8cff6983498f713823b7c39b51ef75066badfca1ed6af1951ea5f3b0021a", + "signature": "11d811bac7a9a37dd84b1a5e5780775797c35f9b6f217d2d36bf2f28879441f60e304299405918a8e6ef9e7775557a2af30548e6af32b2561392d472bcba8a0e01" + }, + { + "hash": "14a880275ab266a5ccad44c0a7330092beb2b6e355bb0800841abc7a22fec160", + "secret": "acfd831f61dac04c52edca91e62400764b58eea62876bd7491faa01be9955d18", + "signature": "dc9e5af5d74b7c09e0c2f8752296f5abe0103d7d77e48e769a3b75721dc8c41e109edb98d19ee4d635f1c56123f87ef4652d15fc9e1785c3955f286a5f599f0301" + }, + { + "hash": "a2c9b0b7cdcf47e43bb1a0d55f29cc7bded2c20e3c278bb609315e771265b8d3", + "secret": "900a33439cc91e06f10ed016a9b77fb4a2bcb9c0b5f5d64e27f23b224326cb65", + "signature": "5acf06a23a4d7aa0efffb6d17f81aa3c95c2c4977cf44d72a962c98342e1bbb808f43a2a654dcbf64608537744c61ed2e910b07375df33867ea72d6e9f136e4800" + }, + { + "hash": "6e00f7ac0f2ce58900b37a562313b526fcf35f0f2fe91b8b813876271a394231", + "secret": "67fb6806a97821affb20e960ba5dd57e9fe565cb419f92df85f03fcb6e353206", + "signature": "29ed122ff7f02cfdede11d3a5f817027f003bb94cde9b6a19361f3e4c49a150174614ed76ff575b909c835e7053776a35d593bd064373d8e2d682d1d1888cb0b00" + }, + { + "hash": "63bcc7f35ec60b4660a4af4ac568ac7023c72b7b94cb69ea561bd0a51b533208", + "secret": "d5567b440186aadc79fb41bb9a5595a6d3e9ffd1b31de5f319a056c55c1a5180", + "signature": "0f8bf2b4f1c32829ea7b8ecdbc4b1d920c5ead58589f895445aa9db05a726096636b873e4f33327e19db035ef1037f2d7a46b325d72a961c34fe5bf49ef70d8401" + }, + { + "hash": "b2bd1ed10c343ab83ac0183f3d03f87c4513e22337b07f31a69acd57614454b1", + "secret": "c6703284f87452cdd53e5224b4ff6afb8547f89f42e105c28d7aac35ddc0cfe3", + "signature": "5fb35b052aa058c673249c90161e9ff8690886ce3e7fbe158260fe3a0e0bbfed7171eaddc33251a4243fef892985bcfe05f94ebde202ae899284f7d72741001d01" + }, + { + "hash": "76c0016f9efcfb0f3114e30f7e69497b6be26c9b17068f195ec085a981a021a7", + "secret": "be44fd4ec29b541947cd7b8e697dba1ac42fe1bfe879a726e8a1bce894f81251", + "signature": "af56b85d029a78b04aa720a4d7113d457f67d0de422288b817f4ef2e389933f227a29153c399a9a417d9506750c51fcc20b31b802e54acba141fe33fce2d165f00" + }, + { + "hash": "a05f60c79781ee69ab3d656e2408c88fdfc96659ad16bca61091c435b3d7c33c", + "secret": "d7f53418f26ace9f24aa5fadb327f65fdd622f4b3bfd4638b713b50c362cc742", + "signature": "7dbbdb81fcf9cc8b963aa5e3d9cbd3863f99fbd048e81341ea006bbb6a7c4e5221bbd317baa74557129c35b2cd1443fec4dde78a2b5064fbc57e53c9d5b7d9d400" + }, + { + "hash": "3fe350ed89da6946d9291dfdcde0a55febbdab3eebd9010e2804a3fd1ba9d647", + "secret": "a88c8001d825d7c0019ba16a0ce7c89941d4bffdbff4cba2a348fedf4f992f02", + "signature": "abd4cc167b48215c1e82a7dc25c8cfda5962ef8e4c3d71f16877fa1d0fedfe240039664a8442f3cb636a87564e700c92ddc06f33579e3277ec5edf8b2118b11000" + }, + { + "hash": "ed9d574fd453b5fac1b0a93a80573d7df064b37e166b0c3e4a10c016ac6ecee0", + "secret": "9f75127c2d635a5fd27ef3b613ab5011db8b3f5e37537629cba43e37aa4d4489", + "signature": "7aaec22a07280b4425410d154f43223731d2c606a11282fffe6aeee84d8184ae7c0bf88fe4ca6c628c9488f67e45c3a4ad0c0edb4d50bfec3c0656a65b44938e01" + }, + { + "hash": "2624913cb516d997d6d042e5d9d5b30a4131ba3c31f82a34d35018350e9ca68f", + "secret": "9d802bc343231cbb7004deedfc056372e6fa4b02b48ac56285941ed33761bcd4", + "signature": "6b02fdf137298bc5b6dcf9494cd6dfa7c369dbf7b3068a826abea8fad8cc65254bece3c0ce763f5fbf8b157cd9cbc295a63ec2c0d2bc24f5d9d0e024b01fe7a601" + }, + { + "hash": "44b4a8ae135675846b8fa732eb2622f63f95fce5b2b10730d20283ee7a6aed63", + "secret": "bf9e6687a24727d2350a5da6211a43fb3d44a27ba81e1ad9c5d1b91526752963", + "signature": "4fa117306368df1170d7fcca01e80353f77f52db807619ad824e5c4898a0663d72af1dbead5f3930f377b92cb9b6a502fba73eae09baceff500a0a58b2325d4301" + }, + { + "hash": "6f7878f3ad75aa55ec6c7599ad1400101b656fc73535c9e685ca602f8cd20a15", + "secret": "1532a5657dda80bda879f601d8a021607d381714256503ab616d2326b9941eb3", + "signature": "c46940f33634f37110d801358db10471b89be637fc082a0ce7da2fb6c8012a9622df557358ef2a766ad81381fa02182fdcd259e263452ffae50690bfc473920401" + }, + { + "hash": "3c014d188be81a9c617625ae2d9bf67108b260858fc4a01e9b835630b1f0496d", + "secret": "2f5d0ecc962c457e148e2858a56faaf873ff86484f50ba73721edede01da6c37", + "signature": "1adffa5c92d00be99358ac30eaf77608f6c335594f7d2cb698c84675b7e5ab5b686a35dda6163fe469465a6e97e949d584906e6cf19f7786cc45269e38301bbf00" + }, + { + "hash": "c294789679f2e887db22b0c7cb240b911468e3a2ab028878bec2d8357ae61900", + "secret": "10bc10b8a45e77c332dfab667e413f18ea3dca01af9a3d0f687d4158f2616e54", + "signature": "73cc844db97306c77cde84719815feb9a0f6758332956ee1fee809b69c4928845920e6c820d38445ad770a462444f23bda87f8b1c7a596617ed66f9c08975a4101" + }, + { + "hash": "afdac9c303e05371802bd0054f0b01a90d5fbf0df8acd41659812eb0f0fbbf63", + "secret": "3ed0724a7874a3642c5a8956d5ee63bb1ecb683383af7cbcfbde3e6593d98e92", + "signature": "7a3bfe06618e30c936a42d9977955ec8b2ca9d5bb3ac64e05c963bef5f65a3e938f48fb000cdfe86a3c80161a272c8636d24e92fcb753735e0f209b8031489c501" + }, + { + "hash": "f9d60b3db6cbf724a573180d7cbbacbac3f64da7b4490b73c1bb7e6b45ed13b8", + "secret": "f53c65f3da3171b9ad12edac26e56dcef4db8859a933c6b2a25d34c4c89ba8ab", + "signature": "0ffed06a951920df34e47d81f9cb2e22266a4d676dc62ab52eceb809688a5bfb0c8af3d4097ee1b73379fe0dd98b84d37f8e5a3e7cec51aab9ab207a2ef80c5101" + }, + { + "hash": "c5ca565076b7e29aafe596bbb7fc62e35705dc15d6b9f4ca81a57e4049fd79fd", + "secret": "9f3953048042a019fa38eda94fd3a4551f27f7c9561b2172b026dee63a0c4733", + "signature": "c5b92f677aed396d95ccab20dc3dcdf138795faf895840f7fd487048aca2400c4616204d45c3974d917250cdb255979b7f81cb1366d6468d22ed5e4ab022180a00" + }, + { + "hash": "dba0ffe77b1e38325881bb24fe9eb2cf997e1f6a14924e2b876f5238376175b8", + "secret": "4a8d2fb9a95a5d2371da8b0eda619acfc32f6f282992033b3ff445b51669ab70", + "signature": "443be5107a6a48f383a36330eb07ec8db9193314120b5bfaf31eaa79836a19381daeced7e560aeb4c2a568ad8d2389292ff51b8a8d3d368b399b755717c5ef1e00" + }, + { + "hash": "4b18609c56d06e1797248e227f3c6bcabf117275d14afaf23980a7c12ca1bd68", + "secret": "178c69840aa469159e2c36d456a5074363e7092765177d1e363e9bafa66411ac", + "signature": "43414a346bac4c686fbc1c9dfab55ae91a4cc0c323e742d5bfaea3393f0e301c18d6be934769ec7f492cb0718e4d7fbe66a3afd5b0f0572cf49a0b72eee9e95601" + }, + { + "hash": "04b09c6a84f7a6f2d307fc568a706e69544f5391a61656a0cdbc5531dc50ab0e", + "secret": "59f1f81572e13525f4bd23ffe8cc0d8a3f1e61b8eadcb8b7f4007ccc5014773a", + "signature": "77c3de54e7f3792b1f518ec687df2cbe578e732f47266cc6e5345792e07675bc696e2441cda375f5868e8e584db548fff218a8b66f6d56daad84128be0b6a3d300" + }, + { + "hash": "8f9f8950a3c69ac371c1111a45b09f9c97b47eb298b78134b1c8f2061262f58a", + "secret": "d75c60c24f7ec9779f8e4784708e250b18c37feed155bdda66c39f43342b5edc", + "signature": "5e11734434c570813a6eec42226e4bcddf94c60829333fea8bdc89b802e912520ab53114e73c70f541079e833a483aca59a9c547f355c870d08312470ec759b500" + }, + { + "hash": "fbe8b8c87551c171dea9aa9f1398d160652ea825339adbd6f85dfa0b58a86562", + "secret": "87818f8318b7b249a23f0719fee909d8431ff9e590c86497b1bd271efc3f79c6", + "signature": "64b9be5b2aa9a5325014e11366bcb13a06710ecc0d763044802c84939b8fa4ab6e895868df160b7ce836252600f3293309b7b4cb75dd7bd0194edb4d657937c100" + }, + { + "hash": "3b3b081e02323e29f343d08848b43eb988cc76e0985d25e38e8929c7005b8158", + "secret": "7ec81368d0eb5a50a4d155558d0fc4f59e0b1469ce92cc8b607db929a6b08903", + "signature": "ed6583c58c9d3303340b2ce0b254d8cb105933c88a62ee222c60ffa14a44aa335d8a7db8cf17412c4c5383d65b5f7ca2e094baa0124da78ffa134f47acd6d68c00" + }, + { + "hash": "72cc90e1a27aef0e63afae2e6b2d6adaee0d6cb749683f6a540082b718a611f1", + "secret": "3f7cf867ba530c11c2c913e61951bc81b0326eaa088561afd70d5c48ad34162f", + "signature": "51ecc9b8ceb5c699efbb1563e28879bb21800ab95224ad65a8b8680576c247b16da9ae06f2f36dbb714beaaf473db13c2363f5c26d449b2902ccf8d98c16e2d201" + }, + { + "hash": "f9c6c5d2ae4e6546be1ce7de502ca88f2c4a187240963f967fa4a23a85991115", + "secret": "476b349bfdefb01e41779a96e8b1ebb2222be3ab19ce9fff5d128065f1b837d0", + "signature": "91b9f72f12b1616849badb127384ff8f4527490418706da39f15bca4d7bfc4b8564d82702ebcc2f70c07457d7b59240aaeed0919a95780fde6975dcb599b925b00" + }, + { + "hash": "72d98ac91ff1d66f2e1009e61ba2b44c53b38d67b137e0a6128f68b6079054d7", + "secret": "99b5b03cf2dc0c3ad8517cba4186cbb286c77ce26bb71e23ff1b531021976202", + "signature": "1895371c41d18068a95d12503e45a58a00973892acc4289424728284a5f926e06da7cbc921529deaf85d5a148f730b842468f0d51c525b979f10dac638537f2e00" + }, + { + "hash": "a5ec417f9eac2510c5443f21b50513bdc86ce6c4a66bc14359890dd5cc8a7b40", + "secret": "ce58ce540c3001a2fd28502e5ca892e7b7eb00f55c287062ec843ac7752fa6e2", + "signature": "4fe00b7b0ef1c0a9a8e057a512721bc3ca8052a06272c94010c266b22343678b31dc281f2edddebb9b64b0d174a97ed5611a7f7c12e6f4f06a151965f2f30e3e00" + }, + { + "hash": "ca74b92c4e9cf5a3cf4de9268ac60bc9866e3238966232bc6f4e4d2f69a62885", + "secret": "b96f178ca927f5f8e8eaa6c45441db5060dcd1a09f272aeafda0bcf0c700cff5", + "signature": "7f12290968b60841be92af8e5d9e99ea4a7565eeb58130c2ec6b0ae4acd5466276d96e5806beeac99a25a67be8164e31eca830a5389d101a8c24e60aa06909e200" + }, + { + "hash": "73c75c6d182eae3e423af7049e5b18f8240b635d9d1b694d12342da08f6777e5", + "secret": "826a2f08be69e49d8b09b29db3b4aa5779a6744332725fa5e1814110fa4cdb96", + "signature": "a2137c4d430546cb04cca3c0299d6938deffb9e78df6c5a36296cde6584cb02a42f2d6105700f1772752a1dfcaeb113582c6999a9f44da60d50f0ea8affc321901" + }, + { + "hash": "77a033035f47a54614ab135b507e5f2989b2ffc62793ba59c662f744f3f7d1ee", + "secret": "e01a232ba20a238fa1725ae95a08d5d0ec15379f362ce1b0ef59ad209bcd06e8", + "signature": "e7945c17883124cc85134043842fb236510993134d9975c8e32eaf23e9877823255c257b86334488151dfbd65360bf534418f76d53050f9879ad04881f71394501" + }, + { + "hash": "45a2c86962e9e408a81f9f687795fdb71b3a000b62731f438d12ee40d42cb374", + "secret": "9b8c1f300a7ff483e0f6d1d72cf87b88d10fe27461399df74ed9dd319848afd3", + "signature": "1a4452b0ccf856aa29ac2b56d7db74ac5bfbb9cde6fa71599717824c9d1305a23cfab403e7f59865f33dde626e220a3d59597c050723e0af44a57d360c2a19dc01" + }, + { + "hash": "49c76133ced2492f275f0b6fb26067020b1919691c0956c9ddb0e4375bfdf4ff", + "secret": "6e6ed3ec4255c4194f479d05b3c7c2e1bed82ff1b7994d0692094dd2637c12a7", + "signature": "71d128694790c1dd91632b2866e6a6840c79b24420105e3b74913a37f799fc616a0b0d5dd4babea6bb01d11f823ea66a740ca249db6e19086565ee7279eb2db101" + }, + { + "hash": "387eb0cf09c1a00c17a6ac8ece3119b34d116733e3960fb44e50056c4d0a0aaa", + "secret": "58e1ceb39789cb4fa05d3af6f9172cf6af53fb8679bf32979b28f4ebd173d72b", + "signature": "3228a0322861193e10664780df8c557267f5208904afacb9035524e2c16c7df166ed796dfefe493defc5c1348cbf9ce727f603c65a3c5e500da8a99f88abcd7201" + }, + { + "hash": "bc8e5887ce6847ed67be2a781633b06dcdfb816038ca0739a105d0bc80a56d75", + "secret": "30b94c4c87c770bd21620791704408291b86b171e939c2e2a05ae618a6fe94b9", + "signature": "58e1821e5cc088e26980559a87da62b6fb9d5b0515f3a245fde860472dccdeb22e8d460802ce34174abe3f138f78ea7516fde8109f24a7c2efd5318e2240891001" + }, + { + "hash": "c428d4f949aa6fdab3fa5a784451745bac85fd81c2a0d9c5ac6f521786cd56df", + "secret": "af86222312d73b593e911ba7a929f3e97d567043bd0078d443206b7d0f3f931f", + "signature": "0fdafcb6f9037865092a50ce95ab9891f9b478242aa206ef4ded9f8b61b86f2f0c22d2e545c8dbe7f24a1a5137e38e325fa926c7f17c15ccf962aae8d962db0d00" + }, + { + "hash": "219f55f3012c0c7d16e0172c6ff1b21080bdf3d6e867d32196e3b79a5b4a6b7c", + "secret": "fbcbefbf81c033df501309e25f39ef3e15b22ec2061c97c3f8a5c784a7149b87", + "signature": "3d0a81e647abddc6d38fca85acadf483835471e5d8820ad51891ef19a65346ea6646b6f3d9d8556831c48e289a23b277b5169d9ccea6ad76c846b74e5ef38a6a01" + }, + { + "hash": "1b8840a329ad8bcf2614a06c3d6bfb9385cbb22a58952fe6eea67310518f983e", + "secret": "a1632ce45d4025bff643ce1a0b36a3ca7a7298804602679eb49be6bfc6553c92", + "signature": "059d90136fd57f372dd12fc1e10f2d3a3700031c4f1e4f36459923e4fd3c5d6b6d83e3914a3eef24b951c38025092303eb91e786a2ef9a23750b54faadbb00c700" + }, + { + "hash": "d17decd2a4b042738261d6e63ef012cc6867e729a3e6f8ba3e1bc79532bb463c", + "secret": "d2247583b85dd27f39ca9dc8893966364e8a12699753f6ef9928f2d9011f9cdc", + "signature": "f12be874105b61217869cf6b49239385cf1c3753b5551cfb1acd358dc297780765f0c591dcb24f142a5045ab0fca4e90bb9cbb4c08195a34483b8ad3bce9cc2d00" + }, + { + "hash": "bafe579a79d46dc918dd15b6643f312e952ca60deb5bbfe2bb361c1eea9c4a4c", + "secret": "9fab92109e06a36d1d21002b020c1f9171003fd463105fa8896970496f56c13e", + "signature": "bda204f249f0c0d2a60ea4b4e87bee941d356224160f2a769881635d5363700963cae24a2bfeb616c3f72d4be2128bea3972ad14ffded72964a66546e8383cb200" + }, + { + "hash": "eff6a8531bae03050f6a62f7652b6213e1755631191c363826ddcb185e2a43b9", + "secret": "fe756b78c5425405092aa198c7104069343e0c890998a1e699bc9f908c7cb264", + "signature": "853d656c1d293e04f7d425c13412b350582c23700d4975e302fdd761562bc9e00c0bf359d360a736089690e1e3f517fdb4db230f7e76489fc9e1eb58500f8f3e00" + }, + { + "hash": "4917e9f4dc97968151de0bf71b5b2bc6ee2544b49b368b305d503dc781ca8cdb", + "secret": "6b3a552cd49db3aff943b090dfac7e41a820d8b6319fcb66e5b75c2e89dc4a8a", + "signature": "d571d17126ccab7734c7d3b348117fb105f45e6d1b6b6898a489cf8ef99e1ab95f0fdce993b33123cfddbbeb7c6788655829b6d9ddada341540a2629342f061000" + }, + { + "hash": "738841a6a624805d5cc0670c9508c72f2bd3bd41e3a652efe536ae67da39525b", + "secret": "a5c50ca0b63ed744d10b3579fd187f2555da46350fdca74be052ef7f19ac2ac8", + "signature": "3d050cdb5a335a85907be4a660f153cce2a18f5db3e3db6671e2a76daf3d02935bbcc06971462f25409defa49250d30bdb55710cf69a5cd1a486926a7fe5ce3d01" + }, + { + "hash": "44751c72517dfe4fcf5c09b84926f096d4ef69d6c131aab4d3d2af88798fd0ea", + "secret": "67f42cbc42437eb15d699848783c4295514c9fa8b57c5f4dc9284f1c421e3a0f", + "signature": "18bc8c522efd14dff80a0c5c68db20f0e3947d2040fc7235fdf60038769a6e8c099700104432dfa3292985b5f48939b36fe83d048835fd71b36f37b034d280fb01" + }, + { + "hash": "ab3eb5d20f2b4a616e9b2a94b6bcd586bdaf485bb78e444505ea94f379e53892", + "secret": "73ed40023e40bbccc49e26497004c6e5347af9272382ac1d5a9a3f2f78f9bf23", + "signature": "b7c4af4bb53af7ec1abcdec919d344ba06f5d4623594eb380def2215d188b9c63c62b6cd1cc4eaf7967a3ff4e50364d2591e8966856c15bb4efd2fc8d75fa25c01" + }, + { + "hash": "8d49d2d75ca11e719941259415524e2a2d8858a5e1c98431b0ca4036b0af6f27", + "secret": "d8a771b23524fb94766078c3f458939ad178a05caadfcc6cf5a9410dc2ca7cd0", + "signature": "3fe0f65840cc2795197740e3776ccbabbaa1c68111fb2250979889e7cd663a9141f997a117a30ef68164a8a57d3ba82f58d7684cd69658b47f267a93ac09807701" + }, + { + "hash": "100de6c9ccec037ab289812462506507e615dd1e7ad4fb33c724af9f1eab1070", + "secret": "ed7fa7580d848a15ca06018acf288dfe7547e0e44e175d009f52a47e40e5cc17", + "signature": "c52016c525ed8d7f7b7345cb55c98ae9bce55afffb517a19c06a31b68bb7bfc77b580affb42618f30bdc7526d42e6439621311407bcaf12d3ea716bb4cb20bd101" + }, + { + "hash": "0b629bac6290cb90bf375893c324f7e8b3e1dcb76c6defbe9be1b0953a5eae38", + "secret": "ef44bf690abde5c3226dda3aa133b77a81f0082542d9bb9b439645d63fa0003b", + "signature": "3e5a814d8e201cca69cdc570697fa8dccbe48c04ebfaaadaf8343749b5e7fe200a384ebfad0f54c8cef939a549a2a91d4039889eb9034f259648c0ea203fc4e701" + }, + { + "hash": "98fa86a923ef8f905672122adf722a3c56ee0b8349fefa5b703b7d0381ccd421", + "secret": "8c9154788d4a19ad4e081cc243be741eafd48d166ac6fe110dfb3dfeeb10feae", + "signature": "803095e5097c048b12026e8d25b513ec7417bef26c3b1bf910c9773a2d37895e0dc1e184eea9ffbe7f8640e7c98d7b893512336f8125dc37f9e4b460f6f58cd000" + }, + { + "hash": "976c107451a19191481fb3f413a42ff20066cc7e0d8a3809ee614cdb1728ecb9", + "secret": "64c008571f89e53d19491e7e63e6968ae08187a9dfa6bd99c6ff5c5f79f88004", + "signature": "7ca7c754521993153d08d0f22f65bfbfe69ede0900ce759e4ba7fbd5fffdde631b864769cd882c37e559af447b0d47121f32b8697d13d3fd3c504d687b4cbc2c00" + }, + { + "hash": "d8887c6743c97980b503c9a69d0a43602db596094553661fa1df2a4a6ac18a53", + "secret": "c364018454d6ad29523a425e83ffed1d0b5d17e52fd16524e746d860a5634b92", + "signature": "a7c22af6a315bd946cb8f933a29dbdd6a80192c4147b4d208ef3a818e208edbf6058c36a51a31456b16e853eaa830d0d3cc075371b65188f07ba6220fc9c906801" + }, + { + "hash": "e3590881f4b0b72083a8d7ec269d3c11ed8daf2a9a187348b04205f1bb036548", + "secret": "e1aa8dd19ac8b8bfb872d4340241ba096a16f44417f7425bc1dd1d20d5acd35b", + "signature": "731551bcd244e4a05f7cca7aca9a93f5029bfcf8f6cfae30e8ae610c569985f634a6c871a775033480f00263fb138b5260acdd8b93df9647db6e6b3cc20a4f6301" + }, + { + "hash": "a43034b5cf06b3d905b376e178ea231208cb830aa4cc7679ec49e7720ef873a5", + "secret": "0d58f19d5eaa6c3e8659507395125ae60099cef7f1255a13c217cd61f2e6ae4d", + "signature": "f5bba2ef38972af254b97edc3b24982dd24a505ba1f339c18fe714f8fdbdb20b21f9efeb0a019ff2060d14b655687ef70b2c09e6e8596cadef183de4d6c5283200" + }, + { + "hash": "1287ac880b34700a5689fe03ee7fcf6b13d988680e69e9272455d3fb38a5e246", + "secret": "0f91d900b120b9c6960878f8985772d8cf29ffae77748aff0889183ed2bcd618", + "signature": "c5bc42118301c8ff4631e652b4d6bef8c13b521bd404d5e7af710c9ab63c248048cfedfa380ca3d966a718a36fcf3c6a1f456c8f8b47b2fd83931fcb4f02d08e01" + }, + { + "hash": "5c67aca884da5f738b1624c501bf4063540a2153d602aaff56ebac89e11dcd1c", + "secret": "81f54e72796d6a447bcff6b1022bf6cbf48b2b39160b9e2a4fa07116783c8bf9", + "signature": "0e322a34ce048d9497562c53cff8d3266e889513f489decccdbbcfce99ffd478796852b4b6fbb07e6f58651347d00b99c5a2c5633f03baeab5314aa6cb58ecba01" + }, + { + "hash": "01133a236bba209b10a710a8f70a4fce5eb043306efa3ba74ae6bfe016e0b7de", + "secret": "ce469cba4dd74fff54257d98deb1ec0f4b4d92f13c22ccf88327729fb20a91a9", + "signature": "4e893c2de72f2b9bef8adc86ac6da301a0e774feb3ec10c6995262b3a554f209732bd0ae41bfe5fc1b863cb08d74ef15fa250264649030b30c8fe44e894c171601" + }, + { + "hash": "c02ae83c3be29fb6e9ee687c4f01cea32ce8bedba3f5441a6cca28b38ae92c54", + "secret": "209eb71c8e9e5de35e67440e331d2f2f31aff08410baa2e0ca7eb29cf20fe003", + "signature": "882d92979f3fd4df2b19eecea6b4ca3104898774e83506d934736a80697a19366f4fde413d1fcf73adc00dd3938be427cea62a2aa166eab209703a3f63bd77fc00" + }, + { + "hash": "f536d9eb429ba0bc5459fa7960c7693775004ea9e57d56e3e94c3b196c49bbb4", + "secret": "359484ee9ea253e1f791d1a17f790f79f6607bbe26146aa8ac542d8612858e1a", + "signature": "7c78ceba9be9ddf18f2b5bc4414d795d882564a768a78f28702570bb04d0a65e17184e9ef667a2ad8e689ae8c03c8eb73117436035653481f297cd84a1f67a9301" + }, + { + "hash": "0a67a0dfdb30f0a6e5863a62b6b7659d0276c7b12c8a8ea87478fd921c3101c9", + "secret": "29aeae7ac718964988b35969113c14b39c81e2064794d92af09b4a1d27804267", + "signature": "63982045a58db529208243ddeab9b5835d7ce4d45cc557f9b7ecbfd1e9ceebe5567a65ad3f50a8e365146a7fc1490dc5bfd53bcb22e6256ec4c18047caaa4a1b00" + }, + { + "hash": "a8a12969ea054930d0898b91184929ac78ad0fd313ac7e809deafc7e01b9a368", + "secret": "75b84e21a454b7e454a17e054d0657d1c9ccb801e8579f5b9cebe9249e020fe6", + "signature": "383cd3371af70ee93ddd440643196ef945a2c9918be5381f6c41052f6e0279730372f224b2d6be3fcc0f9fb1e2d974923e2ff6129a1dcce73d2ce2d3442d30d001" + }, + { + "hash": "5ac6af499cb44bd57dfc9ceda8316c43e02627f14722e2ed6d0051e9539a2406", + "secret": "92292d68d69cc60544f58f51a947bf3b1b5c7f1015bc04a8a3b7ba93d74191c6", + "signature": "100380c86d1e6d552d5d940fb9670814b45ef2eb5818a69a6ede13e64aad9d7f36245f21b758aac79b46d3df520bdd96a62f052762fb371e56b5aa288c0b849e01" + }, + { + "hash": "07497143b21ed20745359b553d106c25d77304995206d3df2d0248a8ddf23089", + "secret": "650ad13e7686f91d2c92f7b8fd1e8d61fc9143774d3bc1b7826b1642d39bd6b9", + "signature": "a77d5774655680c3920641003a86a5be888c45a9da3f8f40d28f08e2925409b119cdf82f7ad7f7b954123cc73c96256373461d949e6befb51da41290f79608ff01" + }, + { + "hash": "06fd74e0d7c1d5337c6a370a8c6b7373449521af7cba1b7ef7959b23e711a054", + "secret": "d528eccaf41259f212192f4c02e2ac07f5caf759b927c7b012ea15639476fc3f", + "signature": "0bb24124b2eac1ce7f9acd56c40c8e1ebe46534ea6d2bc0f4383e11084ad2783772209e0343be1ad24e5dc2c5f57d301f1c73fd34cded83986321c4360a502cb01" + }, + { + "hash": "883771ff758909a7599473c3fbf94c24244c8cda06c1bac871a18d293fce0bbf", + "secret": "e151f9e575c9e0540b68dc277a2223539a8344c3061d6727599c28363d3b233d", + "signature": "73fd11a7b59ddbaec26cd5bea43a54dca96dd5af9592ea953217e46354d4e3cf27bda4fdae1faed15039179f7c7bcd4cbbc513aaa6a9486f69219dd7f70219e101" + }, + { + "hash": "0df5e39277381f18a0d775d5d286a764584c69dcbe32a9a5c7e507efaeb27e1f", + "secret": "00c4a04d39bb8c57322973905e3c8c4b18c87246cc848cf6a8cde7a0db70ca63", + "signature": "970b0bfeb72b54932915c8b435755af319bc2f06bc855eb2ea1b9d95651edf6b181e7972820bbc0baa82537a6ae223558e18476fbd0a01bdcd495f3955dc0c8300" + }, + { + "hash": "6df9a6205bd51fd47b464010bed49292ce0c11ffa4b71dbf1d138ad085eb0259", + "secret": "2dc5614cd6016205763887255255caefe7d45fad6533af075923ebfd72826bef", + "signature": "3d662fff2fd66147b8d26126c0c733113f2e9d1d1538513c6f1fe1e24dc6acb73668517cdf18edabf93a8673837e15613f0f5e6908e374093bc3ab067bf976cd01" + }, + { + "hash": "f3a83eba1d296ae5f2ee8187a3bafb895d1f2a15aa0aefb5f8e5369d2a37dbf4", + "secret": "41065f52afeceefe3f0c3cad6b17203c1588d467179c51be86b15a2aeba12abb", + "signature": "950ae774809cc3b55a36f1545340ac5e7a389b7f3a36f1a4f0a91b81518d748c47d9aeef9634f11296abea27336dbf36c5aeb515b497c6ceb8439a14238162b701" + }, + { + "hash": "a7f45622b70ff8d2fd31dd7440cebbf75294ce9429da31ae167473a90f6d9fd0", + "secret": "6d5ec055def0492d9fa613b0fcfaf80f7ff01316bfd8a2e0a4b8caef80df2f5a", + "signature": "448a6aeeb7163b08fdc0b9a2a2f412ccf215feee8d98c76c84b157c3a204c52a041ba06ffe90dd39732ef07342fdc4c7f03a6d895036a9208455732cff6effe200" + }, + { + "hash": "31ad8350e908b50fc1962b8d92fecf3d1ffee7d04a75545b1f4c5facd18cc6ed", + "secret": "408fd6b154cd826297934641614fd02327ec55ad86fb0440c170bfa82ab55025", + "signature": "7537f706fa6c98bb0e53d45d0531cea5ac2e3d92327fb867211a316ea15a2ea611e89049859edbbf78c0a9bd04de39bae265d6b23b4ae5df19c3529bed05be5801" + }, + { + "hash": "1466a7f0f1836ff904e250cb68ea1780bb36f1f91b26fa0dc46cbe20607ba3af", + "secret": "b393312821e73f19eb762a8b4148977236eec4e62cd49b27d2d9de7d1290a2e5", + "signature": "8d94d905a984fc65e9ff50370c30f1eb8cce5dc373c4744d6e77ce8caad957cc06e2c895f32b5f14a63c71b78e25ac3454507bc5b709aa84bf80713bdac6ba1f01" + }, + { + "hash": "b7b9eb14091e5b7c58275b94a7b18deab7e15511873e5e0446d0a7e33be29a03", + "secret": "21ad5b12aba0f73925faffa0dfdfd6a392e5fdc25e622c90be0a9e3996e875ca", + "signature": "8e56b3cbbaf6aa67590e1fbcf08998a0b5d224e90462b7fca645d4b16d056a7e20f8f4dacaa635c51b6ee3cfd71fa54e71ff25f167b35eafa1c6b91686274d3200" + }, + { + "hash": "dcc962043b56a88c57829ff8711fe930aaf4d5b536a2a77d4ad5872d56951d9c", + "secret": "59d21db2cfa096c2f12fe6470331ed422f8b1523810a8602217b1e8b30f4c5e4", + "signature": "5a5ac663960d84b3e0432c6497d5b49c6e1852c839e19169ab48fd0aeeeda28f10b6a106119ca4a73328d264c301657d443b77d01ae19737f7cde37ce128a81501" + }, + { + "hash": "ed80800e8c54311caa90f6500919e73c74e8fe63b274f77d1e0510c28ebbf794", + "secret": "0df0a1d3c6fc9c2998eca608d63a9227b345c3ca5d07b033ed270148b980c1ce", + "signature": "c59abb615cfe8e010afafbb5705b1a82017ef8804c423d17be59996476bfb14a108f77fcb6e1e591100055b36e5ac1932413f4c8eb696065b6a0f00c88ce1df101" + }, + { + "hash": "625e6831a31ab003c8fc67f5966ccb4cfca04ea958cffa62da790cb9c709097c", + "secret": "112c9ca4426ae3c15bcd2bb09c29e9ed1fb84d3e01ef9fc9cf91d8eb02a29781", + "signature": "bfa82e403eb4b5d93f3dde39170975b3ba8df615b0fd81fd7963f32c1ae6fe976a43be2e74b17d4880da0f4a437ddc6a3aed938c988f096bfc321316caebd99900" + }, + { + "hash": "297d4f7b3342a3379b6ceab7a00299cb62a57b1c1a242f26bb1029cd3330e834", + "secret": "47520e3ba5aba9384d865176422be4179bc800b95c620fb086b67adad7fbbfa3", + "signature": "0af9b607ca5c239449e6a61231e625f2b910d6e91f06b2c67fa53c3c005abb4528a982f4078b18b0b97ba9a63daef121059c8489116d9e815af65c18953d118a01" + }, + { + "hash": "77e3814bd70a03368799cc5832e56c013c56b9c0b0bd3e2b567fcb71c32ed28e", + "secret": "20e5e49627e340c1b69bdbd341df97626968bfdc338e4e2e8761a4b95029c2b0", + "signature": "8aff739ee3e77f81c2fc8420c361d26dcbfd7721879a58b902866f8d3d40b2bf33793318ffeea1fde04ae92c62d115e9c9820d26b438ee3b1c4007794bb4432800" + }, + { + "hash": "998a4f780984056368e0ed5594ae1baec3cea5a265f425d08147e6fa0574399e", + "secret": "00c1f966de7e9a697eee6285692c89405936050e5ca515e3dd215d45a9cb30db", + "signature": "31b0d5bb010ea160e52b3c4dc65330200af2ceebe84a36185caaf1d0fe3e5df22a9a71525a4e7ece31a280cb6c70256cbd397bb3b01350ab3cab0e1b9512b6c800" + }, + { + "hash": "712acfad93cc8a23b6d2025f8e9fa7de633c757d62414b7cc1c9cdaf4145f014", + "secret": "3b3ed15fb5fce6ee908c674d3473c3b53d20486b449a6ced8c63b35134c558ab", + "signature": "95cbd1cff963ff5fb12ac9b88c5542d9e80b6598a78a6a483b8821cf461108b4208f482624a168bf446df32695d0460a6a699a12dc8a7b66f6b1d56b668d106b00" + }, + { + "hash": "bf03271a3d3cac46a2f4af85ab4f28dc94b60845e8e97c5513cc26d33029c583", + "secret": "08b9e31e9593601e7cfca3418efe530b4780b525a08af0d7b0027ec8af3af148", + "signature": "6582629fbd61152183129c373519d0008384284928ebeccc367aa99aa1405cf821db10ada6fec1480de397fdedf924b7d845f318a3e01e1f40a2eaeb68f8815d01" + }, + { + "hash": "0054212f799cbc36e84771c779dc23018b98ea415311428bd6b4788eaafbb24c", + "secret": "b66c1c37869c3033dc1d5f47a50bac9b0addc121b401bcf0b5a9113080711dbe", + "signature": "73c3aa168c21b93a8f126b008bb0abbeb5130ab50cd30195d0d2bdcfaa87031940dcad39d4e7b2fc93768eb218683ed9ca34ccc7556b3fad6aee45f1d79fd6bf01" + }, + { + "hash": "f5e6eb9c9f0b22b2d86444c1ed8fea751ccb8b5de0ca90610380960198808301", + "secret": "a2af24e33b9995b4f2317c22094d7d564516d5a3b9d6613d988403d704ae2dae", + "signature": "5537b5fd15e0183565126b49799e87c5f322fdc40a4eb8bb96641961c86fe6557662a9478ec75b25617beb3f317b394d896eea6552c96a10b43ae08c2aaa56a001" + }, + { + "hash": "df2655b91f9979e7ef9934f6d42c70506e9fdc4c9a631c219d5c2cbcc5b2fbf2", + "secret": "a582bfc617c3d71b8f1d36a78d5da56a263f2cd4402a8ac3d1407fa7570d49dd", + "signature": "1d0c768064da2f755ca3027070315cee087ecee60abe9552a0c88d8485fb440c65e837a2390a68389996bb24f7aa8ec1414c8d05721531b76cefac67a0b8bffb01" + }, + { + "hash": "92a55da0a5a40ce354fcea2e1e19e423c4bc1918b27cb1da9a82bbd19739c656", + "secret": "54e3763bffdc23be470911b4a00cf1823402d60e6b3f05cb412eb743c710e6f0", + "signature": "ea15a3a4f3b394ae794f9011232b6475af40f70f4f0e56beaad1866080ddd064710d38451c93e451aa5e45e7bbba53a60d101a9ccecc8f81e1a450e8fca63e5c00" + }, + { + "hash": "dc789ac9ee186b72ed75874ddfec75404a325ce715f4acf0e7f15716873d27a5", + "secret": "eed38ac3a51d0b3b30ca0ba931a2eccc581978287af8dfe83c8a13f18063e4b1", + "signature": "b723f6c7ca1ed1cb2ac085cf084596c507471de79300071f54e00d857e9b3e502ecb9449f8a9e2d5d9983197b28dbf80249357ee5335b5c2c973de474ac2966501" + }, + { + "hash": "256bf871b80790aeeb3129684e6577094e892de32277d56335b6efa548c7760c", + "secret": "1d92a4d3e7702fc569e34b8c03a6002454d2a7b7ce1fb9e9e68fc1c22079b2a2", + "signature": "0cefc1979b394dc0ee569bdeb61e93abeeca083cddd1679a0216faaa5c2ee75136121fc441fa56eae40283ff8d9961deb8f12c2053f7e0d2a5e0e2b07398581800" + }, + { + "hash": "135dac79c2c15df5df784efc77d01f37fae8391f744e0754b3c2ced4b1aa4f35", + "secret": "cc4a32d268af6bd954854cc7a566fbbad87014735355bf37ff9815a32ad711bb", + "signature": "76be7b98542d4201fb6841fb729e6d0c864826632b6f88ff3865a26c6a134e743508e6d2cf5fc314600e0af433d9feef330bc470d9facd032fed13c47ab84e7701" + }, + { + "hash": "32dc1a4cacd866796365578fc9f34933d045181850fc595f308fce0b098179be", + "secret": "5d6353c5e80c2c8484a7fba5c8747acea369b3867e38202847d7412b9fffa3ec", + "signature": "3d4e87f439c79a4a41d2e3ae85012bb2be536e576f0d9b443e05dde31f6b764150d3890ba16d0831a161e0b46d953aa428da51ee32a0b4fdcee4a3474dbbdb5300" + }, + { + "hash": "edb61c559d972631edfc038ed12ec70c4f7364edb160622e30af1e983b26f44c", + "secret": "eae7552961000ec6c486803e006c5e9c8a426df8d712844616dfbf27845297c3", + "signature": "354b81c1993efae290a52bfbe4bf0fe8b1bbb311e75315a450ae5459fc2559a304af37c0c47e96238f487444ec022c23245814d0af97691d1578caa043fc575800" + }, + { + "hash": "3dc69be3da8b0c46ec7abb0b50d1232136ce3d2e5906d8714165ffc679145af0", + "secret": "1e10080c45b5b583072e1685f6afe98c357ed0d7dafb6c3aa63f06fbe47adcb2", + "signature": "9539625576b54478ac7bf94d107d1106a3ac9a0664080fb29fbaf3d0a8ce3882204faccb5b8b798b611e73d089326ff40e4fb497d50e563e649d5712c752c19600" + }, + { + "hash": "d60d71d332ef3ea77188f8845182b78bbae330790cdef1f08689d5466405c675", + "secret": "a6971000bf7ce5b2ab4529f60836de2a818ec2f643294d438d3973443a014051", + "signature": "4dcbedef2023d1812b3fcbc87b692be63363382a470073e4ab4116920b8e93da6f8b5671f51b16d7437df976e080a4eea99c5d07452b74f64ff0f5d7c5de648701" + }, + { + "hash": "2cc264741c237b61b59376c554424ad49ab4c92fa91fd0ed44dc27755525779e", + "secret": "b1aae3bf6f3a423291dc61dfc1a7d463a8e0d3823c243e99dd53228acf4d37f6", + "signature": "7f58d5e3289bb3e057789ed49841dc45ff5b94b31e1572d144c9544fe1bc30e21cb31301f2754e4ad6f471fd5610287245bdda11766038706b0e70bb1f46c38f00" + }, + { + "hash": "bf50b828447a139678010329af94d718f026c8275646cb6aa3b88d0536edb278", + "secret": "9d92aa2dd3d49f846e399d820642eafde6e47341ed863643225ffb68d982f8cd", + "signature": "7d1f5708d69f77b0df5996604c2eca23c9c24e4c29929d3a2d012aec01e42f8a00fd2f5f9b4dee732c93bba18a6680c771487a42055498e217ad72f2b71c824a00" + }, + { + "hash": "60eb03e7c48e7ce8cd7b9801957e97f15a4685441ab62e152294faed5f70447a", + "secret": "22cf83b25deb53cfe31c555fd0255b3272d7ab61f2aca1f19f36cc4bd82cea49", + "signature": "0aabd16e289762f9c363329e883b505f1c85c8fb0df7f383db2c7f651794ae2d6095285a7588187c78527d3b45438085bb69610c959dae58a08b1b43334f1d7b00" + } +] \ No newline at end of file diff --git a/rust/tw_keypair/tests/secp256k1_tests.rs b/rust/tw_keypair/tests/secp256k1_tests.rs new file mode 100644 index 00000000000..9774a47a2c1 --- /dev/null +++ b/rust/tw_keypair/tests/secp256k1_tests.rs @@ -0,0 +1,33 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde::Deserialize; +use tw_hash::{H256, H520}; +use tw_keypair::ecdsa::secp256k1::{KeyPair, VerifySignature}; +use tw_keypair::traits::{SigningKeyTrait, VerifyingKeyTrait}; + +/// The tests were generated in C++ using the `trezor-crypto` library. +const SECP256K1_SIGN: &str = include_str!("secp256k1_sign.json"); + +#[derive(Deserialize)] +struct Secp256k1SignTest { + secret: H256, + hash: H256, + signature: H520, +} + +#[test] +fn test_secp256k1_sign_verify() { + let tests: Vec = serde_json::from_str(SECP256K1_SIGN).unwrap(); + for test in tests { + let keypair = KeyPair::try_from(test.secret.as_slice()).unwrap(); + let actual = keypair.sign(test.hash).unwrap(); + assert_eq!(actual.to_bytes(), test.signature); + + let verify_sign = VerifySignature::from(actual); + assert!(keypair.verify(verify_sign, test.hash)); + } +} diff --git a/rust/tw_keypair/tests/tw_keypair_starkex_tests.rs b/rust/tw_keypair/tests/tw_keypair_starkex_tests.rs new file mode 100644 index 00000000000..40d28c2417d --- /dev/null +++ b/rust/tw_keypair/tests/tw_keypair_starkex_tests.rs @@ -0,0 +1,38 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_encoding::hex; +use tw_hash::H512; +use tw_keypair::tw::{Curve, PrivateKey, PublicKeyType}; + +#[test] +fn test_starkex_tw_private_key() { + let privkey_bytes = + hex::decode("058ab7989d625b1a690400dcbe6e070627adedceff7bd196e58d4791026a8afe").unwrap(); + let pubkey_bytes = + hex::decode("02a4c7332c55d6c1c510d24272d1db82878f2302f05b53bcc38695ed5f78fffd").unwrap(); + + let privkey = PrivateKey::new(privkey_bytes.clone()).unwrap(); + assert_eq!(privkey.key().into_vec(), privkey_bytes); + + let public = privkey + .get_public_key_by_type(PublicKeyType::Starkex) + .unwrap(); + assert_eq!(public.to_bytes(), pubkey_bytes); +} + +#[test] +fn test_starkex_tw_private_key_sign() { + let privkey_bytes = + hex::decode("0139fe4d6f02e666e86a6f58e65060f115cd3c185bd9e98bd829636931458f79").unwrap(); + let hash_to_sign = + hex::decode("06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76").unwrap(); + + let privkey = PrivateKey::new(privkey_bytes).unwrap(); + let actual = privkey.sign(&hash_to_sign, Curve::Starkex).unwrap(); + let expected = H512::from("061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9a"); + assert_eq!(actual, expected.into_vec()); +} diff --git a/rust/tw_memory/Cargo.toml b/rust/tw_memory/Cargo.toml index faa09cfbde6..2c7b443f83f 100644 --- a/rust/tw_memory/Cargo.toml +++ b/rust/tw_memory/Cargo.toml @@ -4,3 +4,6 @@ version = "0.1.0" edition = "2021" [dependencies] + +[features] +test-utils = [] diff --git a/rust/tw_memory/src/ffi/c_byte_array.rs b/rust/tw_memory/src/ffi/c_byte_array.rs index 65f04e2f13d..337cff99677 100644 --- a/rust/tw_memory/src/ffi/c_byte_array.rs +++ b/rust/tw_memory/src/ffi/c_byte_array.rs @@ -28,8 +28,22 @@ impl Drop for CByteArray { } impl From> for CByteArray { - fn from(data: Vec) -> Self { - CByteArray::new(data) + fn from(mut mut_vec: Vec) -> Self { + let data = mut_vec.as_mut_ptr(); + let size = mut_vec.len(); + let capacity = mut_vec.capacity(); + std::mem::forget(mut_vec); + CByteArray { + data, + size, + capacity, + } + } +} + +impl Default for CByteArray { + fn default() -> Self { + CByteArray::new() } } @@ -43,17 +57,9 @@ impl CByteArray { } } - /// Returns a `CByteArray` instance from the given `mut_vec` bytes. - pub fn new(mut mut_vec: Vec) -> CByteArray { - let data = mut_vec.as_mut_ptr(); - let size = mut_vec.len(); - let capacity = mut_vec.capacity(); - std::mem::forget(mut_vec); - CByteArray { - data, - size, - capacity, - } + /// Returns an empty `CByteArray` instance. + pub fn new() -> CByteArray { + CByteArray::from(Vec::new()) } /// Converts `CByteArray` into `Vec` without additional allocation. @@ -95,6 +101,11 @@ impl CByteArray { pub fn size(&self) -> usize { self.size } + + /// Returns the data slice. + pub unsafe fn as_slice(&self) -> &[u8] { + std::slice::from_raw_parts(self.data, self.size) + } } /// Releases the memory previously allocated for the pointer to `CByteArray`. diff --git a/rust/tw_memory/src/ffi/mod.rs b/rust/tw_memory/src/ffi/mod.rs index 058f841646a..9afbd7509f1 100644 --- a/rust/tw_memory/src/ffi/mod.rs +++ b/rust/tw_memory/src/ffi/mod.rs @@ -11,6 +11,9 @@ use std::ffi::{c_char, CString}; pub mod c_byte_array; pub mod c_byte_array_ref; pub mod c_result; +pub mod tw_data; +pub mod tw_data_vector; +pub mod tw_string; /// Releases the memory previously allocated for the `ptr` string. /// \param ptr *non-null* C-compatible, nul-terminated string. @@ -32,13 +35,20 @@ pub trait RawPtrTrait: Sized { Some(*Box::from_raw(raw)) } - unsafe fn from_ptr_as_ref(raw: *mut Self) -> Option<&'static Self> { + unsafe fn from_ptr_as_ref(raw: *const Self) -> Option<&'static Self> { if raw.is_null() { return None; } Some(&*raw) } + unsafe fn from_ptr_as_mut(raw: *mut Self) -> Option<&'static mut Self> { + if raw.is_null() { + return None; + } + Some(&mut *raw) + } + unsafe fn from_ptr_as_box(raw: *mut Self) -> Option> { if raw.is_null() { return None; diff --git a/rust/tw_memory/src/ffi/tw_data.rs b/rust/tw_memory/src/ffi/tw_data.rs new file mode 100644 index 00000000000..ad4c6ad4400 --- /dev/null +++ b/rust/tw_memory/src/ffi/tw_data.rs @@ -0,0 +1,104 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ffi::c_byte_array_ref::CByteArrayRef; +use crate::ffi::RawPtrTrait; +use crate::Data; + +/// Defines a resizable block of data. +/// +/// The implementation of these methods should be language-specific to minimize translation overhead. +/// For instance it should be a `jbyteArray` for Java and an `NSData` for Swift. +#[derive(Clone, Debug, Default)] +pub struct TWData(Data); + +impl TWData { + /// Returns an empty `TWData` instance. + pub fn new() -> TWData { + TWData(Vec::new()) + } + + /// Creates a `TWData` from a raw byte array. + pub unsafe fn from_raw_data(bytes: *const u8, size: usize) -> Option { + CByteArrayRef::new(bytes, size).to_vec().map(TWData) + } + + /// Converts `TWData` into `Data` without additional allocation. + pub fn into_vec(self) -> Data { + self.0 + } + + /// Copies underlying data. + pub fn to_vec(&self) -> Data { + self.0.clone() + } + + /// Returns the data slice. + pub fn as_slice(&self) -> &[u8] { + self.0.as_slice() + } + + /// Returns a pointer to the data. + pub fn data(&self) -> *const u8 { + self.0.as_ptr() + } + + /// Returns a length of the data. + pub fn size(&self) -> usize { + self.0.len() + } +} + +impl From for TWData { + fn from(data: Data) -> Self { + TWData(data) + } +} + +impl RawPtrTrait for TWData {} + +/// Creates a block of data from a byte array. +/// +/// \param bytes Non-null raw bytes buffer +/// \param size size of the buffer +/// \return Non-null filled block of data. +#[no_mangle] +pub unsafe extern "C" fn tw_data_create_with_bytes(bytes: *const u8, size: usize) -> *mut TWData { + TWData::from_raw_data(bytes, size) + .map(|data| data.into_ptr()) + .unwrap_or_else(std::ptr::null_mut) +} + +/// Deletes a block of data created with a `TWDataCreate*` method. +/// +/// \param data A non-null valid block of data +#[no_mangle] +pub unsafe extern "C" fn tw_data_delete(data: *mut TWData) { + // Take the ownership back to rust and drop the owner. + let _ = TWData::from_ptr(data); +} + +/// Returns the raw pointer to the contents of data. +/// +/// \param data A non-null valid block of data +/// \return the raw pointer to the contents of data +#[no_mangle] +pub unsafe extern "C" fn tw_data_bytes(data: *const TWData) -> *const u8 { + TWData::from_ptr_as_ref(data) + .map(TWData::data) + .unwrap_or_else(std::ptr::null) +} + +/// Returns the size in bytes. +/// +/// \param data A non-null valid block of data +/// \return the size of the given block of data +#[no_mangle] +pub unsafe extern "C" fn tw_data_size(data: *const TWData) -> usize { + TWData::from_ptr_as_ref(data) + .map(|data| data.size()) + .unwrap_or_default() +} diff --git a/rust/tw_memory/src/ffi/tw_data_vector.rs b/rust/tw_memory/src/ffi/tw_data_vector.rs new file mode 100644 index 00000000000..5c596ede7fa --- /dev/null +++ b/rust/tw_memory/src/ffi/tw_data_vector.rs @@ -0,0 +1,74 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ffi::tw_data::TWData; +use crate::ffi::RawPtrTrait; + +type Data = Vec; + +/// A vector of `TWData` byte arrays. +#[derive(Default)] +pub struct TWDataVector(Vec); + +impl TWDataVector { + /// Returns an empty `TWDataVector` instance. + pub fn new() -> TWDataVector { + TWDataVector(Vec::new()) + } + + /// Adds an element to a vector of `TWData`. + pub fn push(&mut self, data: TWData) { + self.0.push(data); + } + + /// Retrieves the number of elements. + pub fn size(&self) -> usize { + self.0.len() + } + + /// Converts `Vec` to `Vec` by cloning each element. + pub fn to_data_vec(&self) -> Vec { + self.0.iter().map(TWData::to_vec).collect() + } +} + +impl RawPtrTrait for TWDataVector {} + +/// Creates a Vector of Data. +/// +/// \note Must be deleted with \TWDataVectorDelete +/// \return a non-null Vector of Data. +#[no_mangle] +pub unsafe extern "C" fn tw_data_vector_create() -> *mut TWDataVector { + TWDataVector::new().into_ptr() +} + +/// Delete/Deallocate a Vector of Data +/// +/// \param data_vector A non-null Vector of data +#[no_mangle] +pub unsafe extern "C" fn tw_data_vector_delete(data_vector: *mut TWDataVector) { + // Take the ownership back to rust and drop the owner. + let _ = TWDataVector::from_ptr(data_vector); +} + +/// Add an element to a Vector of Data. Element is cloned +/// +/// \param data_vector A non-null Vector of data +/// \param data A non-null valid block of data +/// \note data input parameter must be deleted on its own +#[no_mangle] +pub unsafe extern "C" fn tw_data_vector_add(data_vector: *mut TWDataVector, data: *const TWData) { + let Some(data_vector) = TWDataVector::from_ptr_as_mut(data_vector) else { + return; + }; + + let Some(data_ref) = TWData::from_ptr_as_ref(data) else { + return; + }; + + data_vector.push(data_ref.clone()); +} diff --git a/rust/tw_memory/src/ffi/tw_string.rs b/rust/tw_memory/src/ffi/tw_string.rs new file mode 100644 index 00000000000..41d122bd64e --- /dev/null +++ b/rust/tw_memory/src/ffi/tw_string.rs @@ -0,0 +1,80 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ffi::RawPtrTrait; +use std::ffi::{c_char, CStr, CString}; + +/// Defines a resizable string. +/// +/// The implementation of these methods should be language-specific to minimize translation overhead. +/// For instance it should be a `jstring` for Java and an `NSString` for Swift. +/// Create allocates memory, the delete call should be called at the end to release memory. +#[derive(Debug, Default)] +pub struct TWString(CString); + +impl TWString { + /// Returns an empty `TWString` instance. + pub fn new() -> TWString { + TWString(CString::default()) + } + + /// Creates a `TWString` from a null-terminated UTF8 byte array. + pub unsafe fn from_c_str(ptr: *const c_char) -> Option { + if ptr.is_null() { + return None; + } + let str = CStr::from_ptr(ptr); + Some(TWString(CString::from(str))) + } + + /// Converts `TWString` into `String` without additional allocation. + pub fn into_string(self) -> Option { + self.0.into_string().ok() + } + + /// Returns a string slice. + pub fn as_str(&self) -> Option<&str> { + self.0.to_str().ok() + } + + /// Returns the const pointer to the string. + pub fn as_c_char(&self) -> *const c_char { + self.0.as_ptr() + } +} + +impl From for TWString { + fn from(s: String) -> Self { + TWString(CString::new(s).expect("CString::new(String) should never fail")) + } +} + +impl RawPtrTrait for TWString {} + +/// Creates a `TWString` from a null-terminated UTF8 byte array. It must be deleted at the end. +/// \param bytes a null-terminated UTF8 byte array. +#[no_mangle] +pub unsafe extern "C" fn tw_string_create_with_utf8_bytes(bytes: *const c_char) -> *mut TWString { + TWString::from_c_str(bytes) + .map(TWString::into_ptr) + .unwrap_or_else(std::ptr::null_mut) +} + +/// Returns the raw pointer to the string's UTF8 bytes (null-terminated). +/// \param str a TWString pointer. +#[no_mangle] +pub unsafe extern "C" fn tw_string_utf8_bytes(str: *const TWString) -> *const c_char { + TWString::from_ptr_as_ref(str) + .map(|str| str.as_c_char()) + .unwrap_or_else(std::ptr::null) +} + +/// Deletes a string created with a `TWStringCreate*` method and frees the memory. +/// \param str a `TWString` pointer. +#[no_mangle] +pub unsafe extern "C" fn tw_string_delete(str: *mut TWString) { + let _ = TWString::from_ptr(str); +} diff --git a/rust/tw_memory/src/lib.rs b/rust/tw_memory/src/lib.rs index 2a164e90020..ff757aa6b04 100644 --- a/rust/tw_memory/src/lib.rs +++ b/rust/tw_memory/src/lib.rs @@ -6,8 +6,13 @@ use std::ffi::{c_char, CString}; +pub type Data = Vec; + pub mod ffi; +#[cfg(feature = "test-utils")] +pub mod test_utils; + pub fn c_string_standalone>(input: S) -> *const c_char { let res = CString::new(input.into()).unwrap(); let p = res.as_ptr(); diff --git a/rust/tw_memory/src/test_utils/mod.rs b/rust/tw_memory/src/test_utils/mod.rs new file mode 100644 index 00000000000..d2520378719 --- /dev/null +++ b/rust/tw_memory/src/test_utils/mod.rs @@ -0,0 +1,10 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod tw_data_helper; +pub mod tw_data_vector_helper; +pub mod tw_string_helper; +pub mod tw_wrapper; diff --git a/rust/tw_memory/src/test_utils/tw_data_helper.rs b/rust/tw_memory/src/test_utils/tw_data_helper.rs new file mode 100644 index 00000000000..4a317968e29 --- /dev/null +++ b/rust/tw_memory/src/test_utils/tw_data_helper.rs @@ -0,0 +1,54 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ffi::tw_data::{ + tw_data_bytes, tw_data_create_with_bytes, tw_data_delete, tw_data_size, TWData, +}; +use crate::Data; + +pub struct TWDataHelper { + ptr: *mut TWData, +} + +impl TWDataHelper { + pub fn create(bytes: Data) -> Self { + let ptr = unsafe { tw_data_create_with_bytes(bytes.as_ptr(), bytes.len()) }; + assert!(!ptr.is_null()); + TWDataHelper { ptr } + } + + pub fn wrap(ptr: *mut TWData) -> Self { + TWDataHelper { ptr } + } + + pub fn ptr(&self) -> *mut TWData { + self.ptr + } + + pub fn is_null(&self) -> bool { + self.ptr.is_null() + } + + pub fn to_vec(&self) -> Option { + if self.ptr.is_null() { + return None; + } + let bytes_ptr = unsafe { tw_data_bytes(self.ptr) }; + let len = unsafe { tw_data_size(self.ptr) }; + + let bytes = unsafe { std::slice::from_raw_parts(bytes_ptr, len) }.to_vec(); + Some(bytes) + } +} + +impl Drop for TWDataHelper { + fn drop(&mut self) { + if self.ptr.is_null() { + return; + } + unsafe { tw_data_delete(self.ptr) } + } +} diff --git a/rust/tw_memory/src/test_utils/tw_data_vector_helper.rs b/rust/tw_memory/src/test_utils/tw_data_vector_helper.rs new file mode 100644 index 00000000000..340461e5ec8 --- /dev/null +++ b/rust/tw_memory/src/test_utils/tw_data_vector_helper.rs @@ -0,0 +1,44 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ffi::tw_data_vector::{ + tw_data_vector_add, tw_data_vector_create, tw_data_vector_delete, TWDataVector, +}; +use crate::test_utils::tw_data_helper::TWDataHelper; + +type Data = Vec; + +pub struct TWDataVectorHelper { + ptr: *mut TWDataVector, +} + +impl TWDataVectorHelper { + pub fn create(vec: I) -> TWDataVectorHelper + where + I: IntoIterator, + { + let ptr = unsafe { tw_data_vector_create() }; + for data in vec { + let data = TWDataHelper::create(data); + unsafe { tw_data_vector_add(ptr, data.ptr()) }; + } + + TWDataVectorHelper { ptr } + } + + pub fn ptr(&self) -> *mut TWDataVector { + self.ptr + } +} + +impl Drop for TWDataVectorHelper { + fn drop(&mut self) { + if self.ptr.is_null() { + return; + } + unsafe { tw_data_vector_delete(self.ptr) } + } +} diff --git a/rust/tw_memory/src/test_utils/tw_string_helper.rs b/rust/tw_memory/src/test_utils/tw_string_helper.rs new file mode 100644 index 00000000000..dd1fb22194a --- /dev/null +++ b/rust/tw_memory/src/test_utils/tw_string_helper.rs @@ -0,0 +1,52 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ffi::tw_string::{ + tw_string_create_with_utf8_bytes, tw_string_delete, tw_string_utf8_bytes, TWString, +}; +use std::ffi::{CStr, CString}; + +pub struct TWStringHelper { + ptr: *mut TWString, +} + +impl TWStringHelper { + pub fn create(s: &str) -> TWStringHelper { + let cstring = CString::new(s).unwrap(); + let ptr = unsafe { tw_string_create_with_utf8_bytes(cstring.as_ptr()) }; + assert!(!ptr.is_null()); + TWStringHelper { ptr } + } + + pub fn wrap(ptr: *mut TWString) -> Self { + TWStringHelper { ptr } + } + + pub fn ptr(&self) -> *mut TWString { + self.ptr + } + + pub fn to_string(&self) -> Option { + if self.ptr.is_null() { + return None; + } + let c_str = unsafe { tw_string_utf8_bytes(self.ptr) }; + let str = unsafe { CStr::from_ptr(c_str) } + .to_str() + .expect("Received an invalid c_str") + .to_string(); + Some(str) + } +} + +impl Drop for TWStringHelper { + fn drop(&mut self) { + if self.ptr.is_null() { + return; + } + unsafe { tw_string_delete(self.ptr) } + } +} diff --git a/rust/tw_memory/src/test_utils/tw_wrapper.rs b/rust/tw_memory/src/test_utils/tw_wrapper.rs new file mode 100644 index 00000000000..8cb5d26ada5 --- /dev/null +++ b/rust/tw_memory/src/test_utils/tw_wrapper.rs @@ -0,0 +1,32 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub trait WithDestructor: Sized { + fn destructor() -> unsafe extern "C" fn(*mut Self); +} + +pub struct TWWrapper { + ptr: *mut T, +} + +impl TWWrapper { + pub fn wrap(ptr: *mut T) -> Self { + TWWrapper { ptr } + } + + pub fn ptr(&self) -> *mut T { + self.ptr + } +} + +impl Drop for TWWrapper { + fn drop(&mut self) { + if self.ptr.is_null() { + return; + } + unsafe { (T::destructor())(self.ptr) } + } +} diff --git a/rust/tw_memory/tests/c_byte_array_ffi_tests.rs b/rust/tw_memory/tests/c_byte_array_ffi_tests.rs index 5cbd8078f11..e3fe8e41ba3 100644 --- a/rust/tw_memory/tests/c_byte_array_ffi_tests.rs +++ b/rust/tw_memory/tests/c_byte_array_ffi_tests.rs @@ -11,7 +11,7 @@ fn test_free_c_byte_array() { unsafe { free_c_byte_array(std::ptr::null_mut()); - let mut raw_array = CByteArray::new(vec![1, 2, 3]); + let mut raw_array = CByteArray::from(vec![1, 2, 3]); free_c_byte_array(&mut raw_array as *mut CByteArray); // The following leads to an undefined behaviour. @@ -22,12 +22,12 @@ fn test_free_c_byte_array() { #[test] fn test_drop_c_byte_array() { // The memory must be released on `Drop::drop`. - let _ = CByteArray::new(vec![1, 2, 3]); + let _ = CByteArray::from(vec![1, 2, 3]); } #[test] fn test_c_byte_array_into_vec() { // The memory must be valid after `CByteArray::into_vec` and `CByteArray::drop`. - let data = unsafe { CByteArray::new(vec![1, 2, 3]).into_vec() }; + let data = unsafe { CByteArray::from(vec![1, 2, 3]).into_vec() }; assert_eq!(data, [1, 2, 3]); } diff --git a/rust/tw_memory/tests/c_result_ffi_tests.rs b/rust/tw_memory/tests/c_result_ffi_tests.rs index 58e11216516..dca5cee3f9e 100644 --- a/rust/tw_memory/tests/c_result_ffi_tests.rs +++ b/rust/tw_memory/tests/c_result_ffi_tests.rs @@ -9,7 +9,7 @@ use tw_memory::ffi::c_result::{OK_CODE, UNKNOWN_ERROR}; #[test] fn test_c_result_unwrap() { - let c_res = CByteArrayResult::ok(CByteArray::new(vec![1, 2, 3])); + let c_res = CByteArrayResult::ok(CByteArray::from(vec![1, 2, 3])); assert!(c_res.is_ok()); assert!(!c_res.is_err()); @@ -35,7 +35,7 @@ fn test_c_result_error_with_ok_code() { #[test] fn test_c_result_into_result() { - let c_res = CByteArrayResult::ok(CByteArray::new(vec![1, 2, 3])); + let c_res = CByteArrayResult::ok(CByteArray::from(vec![1, 2, 3])); c_res.into_result().unwrap(); let c_res = CByteArrayResult::error(10); diff --git a/rust/tw_number/Cargo.toml b/rust/tw_number/Cargo.toml new file mode 100644 index 00000000000..3a180315669 --- /dev/null +++ b/rust/tw_number/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "tw_number" +version = "0.1.0" +edition = "2021" + +[features] +arbitrary = ["dep:arbitrary", "primitive-types/arbitrary"] +default = ["helpers", "serde"] +helpers = [] + +[dependencies] +arbitrary = { version = "1", features = ["derive"], optional = true } +lazy_static = "1.4.0" +primitive-types = "0.12.1" +serde = { version = "1.0.159", features = ["derive"], optional = true } +tw_hash = { path = "../tw_hash" } +tw_memory = { path = "../tw_memory" } + +[dev-dependencies] +tw_encoding = { path = "../tw_encoding" } diff --git a/rust/tw_number/src/i256.rs b/rust/tw_number/src/i256.rs new file mode 100644 index 00000000000..aaae24a8257 --- /dev/null +++ b/rust/tw_number/src/i256.rs @@ -0,0 +1,458 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::{NumberError, NumberResult, Sign, U256}; +use lazy_static::lazy_static; +use primitive_types::U256 as BaseU256; +use std::borrow::Cow; +use std::fmt; +use std::str::FromStr; +use tw_hash::H256; +use tw_memory::Data; + +lazy_static! { + static ref NEGATIVE_BIT_MASK: BaseU256 = BaseU256::from(1u8) << 255u8; + static ref MAX_POSITIVE_ABS: BaseU256 = BaseU256::MAX / 2u64; + static ref MAX_NEGATIVE_ABS: BaseU256 = *MAX_POSITIVE_ABS + 1u64; +} + +#[derive(Clone, PartialEq)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct I256(BaseU256); + +// cbindgen:ignore +impl I256 { + pub const BITS: usize = 256; + + pub fn max() -> I256 { + I256(*MAX_POSITIVE_ABS) + } + + pub fn min() -> I256 { + I256::try_from_sign_and_abs(Sign::Negative, *MAX_NEGATIVE_ABS) + .expect("Expected a valid negative abs value") + } + + /// Converts the signed 256-bit number to an unsigned 256-bit representation. + /// Thus, + /// - `1` is [`U256::from(1)`] + /// - `0` is [`U256::zero()`] + /// - `-1` is [`U256::MAX`] + /// - `-2` is [`U256::MAX - 1`] + /// ... + #[inline] + pub fn to_u256_repr(&self) -> U256 { + U256::from(self.0) + } + + /// Constructs a signed 256-bit number from an unsigned 256-bit representation. + /// Thus, + /// - [`U256::from(1)`] is `1` + /// - [`U256::zero()`] is `0` + /// - [`U256::MAX`] is `-1` + /// - [`U256::MAX - 1`] is `-2` + /// ... + #[inline] + pub fn from_u256_repr(unsigned: U256) -> I256 { + I256(unsigned.0) + } + + /// Constructs a signed 256-bit number from a 256-bit big-endian representation. + #[inline] + pub fn from_big_endian(data: H256) -> I256 { + let inner = primitive_types::U256::from_big_endian(data.as_slice()); + I256(inner) + } + + #[inline] + pub fn from_big_endian_slice(data: &[u8]) -> NumberResult { + let u = U256::from_big_endian_slice(data)?; + Ok(I256::from_u256_repr(u)) + } + + #[inline] + pub fn to_big_endian(&self) -> H256 { + self.to_u256_repr().to_big_endian() + } + + #[inline] + pub fn to_big_endian_compact(&self) -> Data { + self.to_u256_repr().to_big_endian_compact() + } + + /// Returns the sign of the integer. + pub fn sign(&self) -> Sign { + let most_significant_bit = self.0 & *NEGATIVE_BIT_MASK; + if most_significant_bit.is_zero() { + Sign::Positive + } else { + Sign::Negative + } + } + + fn try_from_sign_and_abs(sign: Sign, abs_value: BaseU256) -> NumberResult { + if abs_value.is_zero() { + return Ok(I256(abs_value)); + } + + match sign { + Sign::Positive if abs_value > *MAX_POSITIVE_ABS => Err(NumberError::Overflow), + Sign::Positive => Ok(I256(abs_value)), + Sign::Negative if abs_value > *MAX_NEGATIVE_ABS => Err(NumberError::Overflow), + Sign::Negative => { + let int = twos_complement(abs_value); + Ok(I256(int)) + }, + } + } + + fn to_sign_and_abs(&self) -> (Sign, BaseU256) { + let sign = self.sign(); + let abs = match sign { + Sign::Positive => self.0, + Sign::Negative => twos_complement(self.0), + }; + (sign, abs) + } +} + +#[cfg(feature = "helpers")] +impl I256 { + #[inline] + pub fn encode_be_compact(num: i64) -> Cow<'static, [u8]> { + I256::from(num).to_big_endian_compact().into() + } +} + +impl TryFrom for U256 { + type Error = NumberError; + + fn try_from(i: I256) -> Result { + let (sign, abs) = i.to_sign_and_abs(); + if sign.is_negative() { + return Err(NumberError::Overflow); + } + Ok(U256(abs)) + } +} + +impl TryFrom for I256 { + type Error = NumberError; + + fn try_from(u: U256) -> Result { + I256::try_from_sign_and_abs(Sign::Positive, u.0) + } +} + +impl FromStr for I256 { + type Err = NumberError; + + fn from_str(s: &str) -> Result { + let (sign, value_str) = match s.strip_prefix('-') { + Some(value_str) => (Sign::Negative, value_str), + None => (Sign::Positive, s), + }; + + let abs_value = BaseU256::from_dec_str(value_str) + .map_err(|_| NumberError::InvalidStringRepresentation)?; + I256::try_from_sign_and_abs(sign, abs_value) + } +} + +impl fmt::Debug for I256 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{self}") + } +} + +impl fmt::Display for I256 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let (sign, abs) = self.to_sign_and_abs(); + write!(f, "{sign}{abs}") + } +} + +#[cfg(feature = "serde")] +mod impl_serde { + use super::I256; + use serde::de::Error as DeError; + use serde::{Deserialize, Deserializer, Serializer}; + use std::str::FromStr; + + impl I256 { + pub fn as_decimal_str(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } + + pub fn from_decimal_str<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s: &str = Deserialize::deserialize(deserializer)?; + I256::from_str(s).map_err(|e| DeError::custom(format!("{e:?}"))) + } + + pub fn from_i64_or_decimal_str<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + crate::serde_common::from_num_or_decimal_str::<'de, I256, i64, D>(deserializer) + } + } +} + +macro_rules! impl_map_from_signed { + ($int:ty) => { + impl From<$int> for I256 { + fn from(int: $int) -> Self { + let sign = if int >= 0 { + Sign::Positive + } else { + Sign::Negative + }; + let abs_value = match int.overflowing_abs() { + (_, true) => BaseU256::from(<$int>::MAX) + 1u64, + (abs_i, false) => BaseU256::from(abs_i), + }; + I256::try_from_sign_and_abs(sign, abs_value).expect("Unexpected overflow") + } + } + }; +} + +macro_rules! impl_map_from_unsigned { + ($uint:ty) => { + impl From<$uint> for I256 { + fn from(uint: $uint) -> Self { + I256(BaseU256::from(uint)) + } + } + }; +} + +impl_map_from_signed!(i8); +impl_map_from_signed!(i16); +impl_map_from_signed!(i32); +impl_map_from_signed!(i64); + +impl_map_from_unsigned!(u8); +impl_map_from_unsigned!(u16); +impl_map_from_unsigned!(u32); +impl_map_from_unsigned!(u64); + +/// Compute the [two's complement](https://en.wikipedia.org/wiki/Two%27s_complement) of this number. +fn twos_complement(abs_value: BaseU256) -> BaseU256 { + let (value, _overflowed) = (!abs_value).overflowing_add(BaseU256::from(1u64)); + value +} + +#[cfg(test)] +mod tests { + use super::*; + + fn test_i256_from_str_impl(dec_str: &str, expected: BaseU256) { + let i256 = I256::from_str(dec_str).unwrap(); + assert_eq!(i256.0, expected, "{}", dec_str); + } + + fn test_i256_display_impl(base_u: BaseU256, expected: &str) { + assert_eq!(I256(base_u).to_string(), expected); + } + + fn test_i256_from_impl(int: I, expected_base: BaseU256) + where + I256: From, + { + assert_eq!(I256::from(int), I256(expected_base)); + } + + fn test_i256_to_u256(int: I, expected: U256) + where + I256: From, + { + assert_eq!(U256::try_from(I256::from(int)).unwrap(), expected); + } + + #[track_caller] + fn test_i256_to_u256_error(int: I) + where + I256: From, + { + let int = I256::from(int); + U256::try_from(int).unwrap_err(); + } + + #[track_caller] + fn test_i256_from_u256(uint: BaseU256) { + let int = I256::try_from(U256::from(uint)).unwrap(); + assert_eq!(int.0, uint); + } + + #[track_caller] + fn test_i256_from_u256_error(uint: BaseU256) { + I256::try_from(U256::from(uint)).unwrap_err(); + } + + #[test] + fn test_i256_from_str() { + test_i256_from_str_impl( + "57896044618658097711785492504343953926634992332820282019728792003956564819967", + *MAX_POSITIVE_ABS, + ); + test_i256_from_str_impl("2", BaseU256::from(2u8)); + test_i256_from_str_impl("1", BaseU256::from(1u8)); + test_i256_from_str_impl("0", BaseU256::zero()); + test_i256_from_str_impl("-0", BaseU256::zero()); + test_i256_from_str_impl("-1", BaseU256::MAX); + test_i256_from_str_impl("-2", BaseU256::MAX - 1); + test_i256_from_str_impl("-3", BaseU256::MAX - 2); + test_i256_from_str_impl( + "-57896044618658097711785492504343953926634992332820282019728792003956564819967", + *MAX_NEGATIVE_ABS + 1, + ); + test_i256_from_str_impl( + "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + *MAX_NEGATIVE_ABS, + ); + } + + #[test] + fn test_i256_from_str_negative() { + test_i256_from_str_impl("-0", BaseU256::zero()); + test_i256_from_str_impl("-1", BaseU256::MAX); + test_i256_from_str_impl("-2", BaseU256::MAX - 1); + test_i256_from_str_impl("-3", BaseU256::MAX - 2); + test_i256_from_str_impl( + "-57896044618658097711785492504343953926634992332820282019728792003956564819967", + *MAX_NEGATIVE_ABS + 1, + ); + test_i256_from_str_impl( + "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + *MAX_NEGATIVE_ABS, + ); + } + + #[test] + fn test_i256_from_str_overflow() { + let invalid = [ + "57896044618658097711785492504343953926634992332820282019728792003956564819968", + "57896044618658097711785492504343953926634992332820282019728792003956564819969", + "115792089237316195423570985008687907853269984665640564039457584007913129639935", + ]; + for num_str in invalid { + I256::from_str(num_str).expect_err(num_str); + } + } + + #[test] + fn test_i256_from_str_negative_overflow() { + let invalid = [ + "-57896044618658097711785492504343953926634992332820282019728792003956564819969", + "-57896044618658097711785492504343953926634992332820282019728792003956564819970", + "-115792089237316195423570985008687907853269984665640564039457584007913129639935", + ]; + for num_str in invalid { + I256::from_str(num_str).expect_err(num_str); + } + } + + #[test] + fn test_i256_display() { + test_i256_display_impl(BaseU256::zero(), "0"); + test_i256_display_impl(BaseU256::from(1u64), "1"); + test_i256_display_impl(BaseU256::from(2u64), "2"); + test_i256_display_impl(BaseU256::from(10u64), "10"); + test_i256_display_impl( + *MAX_POSITIVE_ABS, + "57896044618658097711785492504343953926634992332820282019728792003956564819967", + ); + } + + #[test] + fn test_i256_display_negative() { + test_i256_display_impl(BaseU256::MAX, "-1"); + test_i256_display_impl(BaseU256::MAX - 1, "-2"); + test_i256_display_impl(BaseU256::MAX - 9, "-10"); + test_i256_display_impl( + *MAX_NEGATIVE_ABS + 1, + "-57896044618658097711785492504343953926634992332820282019728792003956564819967", + ); + test_i256_display_impl( + *MAX_NEGATIVE_ABS, + "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + ); + } + + #[test] + fn test_i256_from_unsigned() { + test_i256_from_impl(0u16, BaseU256::zero()); + test_i256_from_impl(1u16, BaseU256::from(1u64)); + test_i256_from_impl(10u16, BaseU256::from(10u64)); + } + + #[test] + fn test_i256_from_positive() { + test_i256_from_impl(0i16, BaseU256::zero()); + test_i256_from_impl(1i16, BaseU256::from(1u64)); + test_i256_from_impl(10i16, BaseU256::from(10u64)); + test_i256_from_impl(i16::MAX, BaseU256::from(i16::MAX as u64)); + } + + #[test] + fn test_i256_from_negative() { + test_i256_from_impl(-1i8, BaseU256::MAX); + test_i256_from_impl(-2i8, BaseU256::MAX - 1u64); + test_i256_from_impl(-10i8, BaseU256::MAX - 9u64); + test_i256_from_impl(i8::MIN + 1, BaseU256::MAX - i8::MAX + 1); + test_i256_from_impl(i8::MIN, BaseU256::MAX - i8::MAX); + test_i256_from_impl(i64::MIN + 1, BaseU256::MAX - i64::MAX + 1); + test_i256_from_impl(i64::MIN, BaseU256::MAX - i64::MAX); + } + + #[test] + fn test_i256_try_to_u256() { + test_i256_to_u256(0i64, U256::from(0u64)); + test_i256_to_u256(1i64, U256::from(1u64)); + test_i256_to_u256(I256::max(), U256::from(BaseU256::MAX / 2)); + } + + #[test] + fn test_i256_try_to_u256_error() { + test_i256_to_u256_error(-1i64); + test_i256_to_u256_error(-10i64); + test_i256_to_u256_error(i64::MIN); + } + + #[test] + fn test_i256_try_from_u256() { + test_i256_from_u256(BaseU256::zero()); + test_i256_from_u256(BaseU256::from(1)); + test_i256_from_u256(BaseU256::from(10)); + test_i256_from_u256(BaseU256::MAX / 2); + } + + #[test] + fn test_i256_try_from_u256_error() { + test_i256_from_u256_error(BaseU256::MAX - 1); + test_i256_from_u256_error(BaseU256::MAX / 2 + 1); + test_i256_from_u256_error(BaseU256::MAX); + } + + #[test] + fn test_i256_max_min() { + assert_eq!( + I256::max().to_string(), + "57896044618658097711785492504343953926634992332820282019728792003956564819967" + ); + assert_eq!( + I256::min().to_string(), + "-57896044618658097711785492504343953926634992332820282019728792003956564819968" + ); + } +} diff --git a/rust/tw_number/src/lib.rs b/rust/tw_number/src/lib.rs new file mode 100644 index 00000000000..13e6bb25ee6 --- /dev/null +++ b/rust/tw_number/src/lib.rs @@ -0,0 +1,51 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +mod i256; +mod sign; +mod u256; + +pub use i256::I256; +pub use sign::Sign; +pub use u256::U256; + +pub type NumberResult = Result; + +#[derive(Debug, PartialEq)] +pub enum NumberError { + IntegerOverflow, + InvalidBinaryRepresentation, + InvalidStringRepresentation, + Overflow, +} + +#[cfg(feature = "serde")] +pub(crate) mod serde_common { + use crate::NumberError; + use serde::de::Error as DeError; + use serde::{Deserialize, Deserializer}; + use std::str::FromStr; + + #[derive(Deserialize)] + #[serde(untagged)] + enum NumOrStr { + Num(Num), + Str(String), + } + + pub(crate) fn from_num_or_decimal_str<'de, T, Num, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + T: From + FromStr, + Num: Deserialize<'de>, + { + let num_or_str: NumOrStr = NumOrStr::deserialize(deserializer)?; + match num_or_str { + NumOrStr::Num(num) => Ok(T::from(num)), + NumOrStr::Str(s) => T::from_str(&s).map_err(|e| DeError::custom(format!("{e:?}"))), + } + } +} diff --git a/rust/tw_number/src/sign.rs b/rust/tw_number/src/sign.rs new file mode 100644 index 00000000000..438742ee06c --- /dev/null +++ b/rust/tw_number/src/sign.rs @@ -0,0 +1,45 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::fmt; + +/// Represents a sign of an integer. +pub enum Sign { + Positive, + Negative, +} + +impl Sign { + /// Returns whether the sign is positive. + #[inline] + pub fn is_positive(&self) -> bool { + matches!(self, Sign::Positive) + } + + /// Returns whether the sign is negative. + #[inline] + pub fn is_negative(&self) -> bool { + !self.is_positive() + } + + /// Returns the sign character. + #[inline] + pub fn as_char(&self) -> char { + match self { + Sign::Positive => '+', + Sign::Negative => '-', + } + } +} + +impl fmt::Display for Sign { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match (self, f.sign_plus()) { + (Self::Positive, false) => Ok(()), + _ => write!(f, "{}", self.as_char()), + } + } +} diff --git a/rust/tw_number/src/u256.rs b/rust/tw_number/src/u256.rs new file mode 100644 index 00000000000..049fbb0b95b --- /dev/null +++ b/rust/tw_number/src/u256.rs @@ -0,0 +1,264 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::{NumberError, NumberResult}; +use std::borrow::Cow; +use std::fmt; +use std::fmt::Formatter; +use std::ops::Add; +use std::str::FromStr; +use tw_hash::H256; +use tw_memory::Data; + +#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct U256(pub(crate) primitive_types::U256); + +impl From for U256 { + #[inline] + fn from(num: primitive_types::U256) -> Self { + U256(num) + } +} + +impl From for primitive_types::U256 { + #[inline] + fn from(num: U256) -> Self { + num.0 + } +} + +// cbindgen:ignore +impl U256 { + pub const WORDS_COUNT: usize = 4; + pub const BYTES: usize = U256::WORDS_COUNT * 8; + pub const BITS: usize = 256; + pub const MAX: U256 = U256(primitive_types::U256::MAX); + + #[inline] + pub fn zero() -> U256 { + U256::default() + } + + #[inline] + pub fn from_little_endian(data: H256) -> U256 { + let inner = primitive_types::U256::from_little_endian(data.as_slice()); + U256::from(inner) + } + + #[inline] + pub fn from_big_endian(data: H256) -> U256 { + let inner = primitive_types::U256::from_big_endian(data.as_slice()); + U256::from(inner) + } + + #[inline] + pub fn from_big_endian_slice(data: &[u8]) -> NumberResult { + if data.len() > Self::BYTES { + return Err(NumberError::InvalidBinaryRepresentation); + } + let inner = primitive_types::U256::from_big_endian(data); + Ok(U256::from(inner)) + } + + #[inline] + pub fn from_little_endian_slice(data: &[u8]) -> NumberResult { + if data.len() > Self::BYTES { + return Err(NumberError::InvalidBinaryRepresentation); + } + let inner = primitive_types::U256::from_little_endian(data); + Ok(U256::from(inner)) + } + + #[inline] + pub fn to_little_endian(&self) -> H256 { + let mut res = H256::default(); + self.0.to_little_endian(res.as_mut_slice()); + res + } + + pub fn to_little_endian_compact(&self) -> Data { + let leading_zero_bytes = self.leading_zero_bytes(); + let zero_bytes_start_at = U256::BYTES - leading_zero_bytes; + let bytes = self.to_little_endian(); + bytes[..zero_bytes_start_at].to_vec() + } + + #[inline] + pub fn to_big_endian(&self) -> H256 { + let mut res = H256::default(); + self.0.to_big_endian(res.as_mut_slice()); + res + } + + pub fn to_big_endian_compact(&self) -> Data { + let leading_zero_bytes = self.leading_zero_bytes(); + let bytes = self.to_big_endian(); + bytes[leading_zero_bytes..].to_vec() + } + + pub fn to_big_endian_compact_min_len(&self, min_len: usize) -> Data { + let bytes = self.to_big_endian_compact(); + + if min_len > bytes.len() { + let mut output = vec![0; min_len]; + let starts_at = min_len - bytes.len(); + output[starts_at..min_len].copy_from_slice(bytes.as_slice()); + return output; + } + + bytes + } + + #[inline] + pub fn is_zero(&self) -> bool { + self.0.is_zero() + } + + #[inline] + pub fn bits(&self) -> usize { + self.0.bits() + } + + #[inline] + pub fn low_u8(&self) -> u8 { + let lowest_byte_idx = 0; + self.0.byte(lowest_byte_idx) + } + + /// Checked addition. Returns `NumberError::IntegerOverflow` if overflow occurred. + #[inline] + pub fn checked_add(&self, rhs: T) -> NumberResult + where + T: Into, + { + let rhs = rhs.into(); + self.0 + .checked_add(rhs) + .map(U256) + .ok_or(NumberError::IntegerOverflow) + } + + #[inline] + fn leading_zero_bytes(&self) -> usize { + U256::BYTES - (self.0.bits() + 7) / 8 + } +} + +#[cfg(feature = "helpers")] +impl U256 { + #[inline] + pub fn encode_be_compact(num: u64) -> Cow<'static, [u8]> { + U256::from(num).to_big_endian_compact().into() + } +} + +impl FromStr for U256 { + type Err = NumberError; + + #[inline] + fn from_str(s: &str) -> Result { + let inner = if s.starts_with("0x") { + primitive_types::U256::from_str(s) + .map_err(|_| NumberError::InvalidStringRepresentation)? + } else { + primitive_types::U256::from_dec_str(s) + .map_err(|_| NumberError::InvalidStringRepresentation)? + }; + Ok(U256(inner)) + } +} + +impl fmt::Display for U256 { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +/// Implements `Add`, `Add` etc for [U256]. +impl Add for U256 +where + T: Into, +{ + type Output = U256; + + #[inline] + fn add(self, rhs: T) -> Self::Output { + U256(self.0 + rhs.into()) + } +} + +#[cfg(feature = "serde")] +mod impl_serde { + use super::U256; + use serde::de::Error as DeError; + use serde::{Deserialize, Deserializer, Serializer}; + use std::str::FromStr; + + impl U256 { + pub fn as_decimal_str(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } + + pub fn from_decimal_str<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s: &str = Deserialize::deserialize(deserializer)?; + U256::from_str(s).map_err(|e| DeError::custom(format!("{e:?}"))) + } + + pub fn from_u64_or_decimal_str<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + crate::serde_common::from_num_or_decimal_str::<'de, U256, u64, D>(deserializer) + } + } +} + +macro_rules! impl_map_from { + ($u:ty, $int:ty) => { + impl From<$int> for $u { + fn from(int: $int) -> $u { + <$u>::from(primitive_types::U256::from(int)) + } + } + + impl TryFrom<$u> for $int { + type Error = NumberError; + + fn try_from(u: $u) -> Result { + <$int>::try_from(u.0).map_err(|_| NumberError::IntegerOverflow) + } + } + }; +} + +impl_map_from!(U256, u8); +impl_map_from!(U256, u16); +impl_map_from!(U256, u32); +impl_map_from!(U256, u64); +impl_map_from!(U256, usize); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_u256_from_str() { + assert_eq!(U256::from_str("0x0"), Ok(U256::zero())); + assert_eq!(U256::from_str("0x00"), Ok(U256::zero())); + assert_eq!(U256::from_str("0x01"), Ok(U256::from(1_u64))); + assert_eq!(U256::from_str("0x2"), Ok(U256::from(2_u64))); + assert_eq!(U256::from_str("0x0000a"), Ok(U256::from(10_u64))); + assert_eq!(U256::from_str("4"), Ok(U256::from(4_u64))); + } +} diff --git a/rust/tw_number/tests/u256.rs b/rust/tw_number/tests/u256.rs new file mode 100644 index 00000000000..dd8c2dbaafc --- /dev/null +++ b/rust/tw_number/tests/u256.rs @@ -0,0 +1,208 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::str::FromStr; +use tw_encoding::hex; +use tw_encoding::hex::ToHex; +use tw_hash::H256; +use tw_number::U256; + +struct TestCase { + num_str: &'static str, + little_endian_compact: &'static str, + little_endian: &'static str, + big_endian_compact: &'static str, + big_endian: &'static str, +} + +const TEST_CASES: &[TestCase] = &[ + TestCase { + num_str: "0", + little_endian_compact: "", + little_endian: "0000000000000000000000000000000000000000000000000000000000000000", + big_endian_compact: "", + big_endian: "0000000000000000000000000000000000000000000000000000000000000000", + }, + TestCase { + num_str: "1", + little_endian_compact: "01", + little_endian: "0100000000000000000000000000000000000000000000000000000000000000", + big_endian_compact: "01", + big_endian: "0000000000000000000000000000000000000000000000000000000000000001", + }, + TestCase { + num_str: "7", + little_endian_compact: "07", + little_endian: "0700000000000000000000000000000000000000000000000000000000000000", + big_endian_compact: "07", + big_endian: "0000000000000000000000000000000000000000000000000000000000000007", + }, + TestCase { + num_str: "100", + little_endian_compact: "64", + little_endian: "6400000000000000000000000000000000000000000000000000000000000000", + big_endian_compact: "64", + big_endian: "0000000000000000000000000000000000000000000000000000000000000064", + }, + TestCase { + num_str: "255", + little_endian_compact: "ff", + little_endian: "ff00000000000000000000000000000000000000000000000000000000000000", + big_endian_compact: "ff", + big_endian: "00000000000000000000000000000000000000000000000000000000000000ff", + }, + TestCase { + num_str: "256", + little_endian_compact: "0001", + little_endian: "0001000000000000000000000000000000000000000000000000000000000000", + big_endian_compact: "0100", + big_endian: "0000000000000000000000000000000000000000000000000000000000000100", + }, + TestCase { + num_str: "1000000", + little_endian_compact: "40420f", + little_endian: "40420f0000000000000000000000000000000000000000000000000000000000", + big_endian_compact: "0f4240", + big_endian: "00000000000000000000000000000000000000000000000000000000000f4240", + }, + TestCase { + num_str: "20000000000", + little_endian_compact: "00c817a804", + little_endian: "00c817a804000000000000000000000000000000000000000000000000000000", + big_endian_compact: "04a817c800", + big_endian: "00000000000000000000000000000000000000000000000000000004a817c800", + }, + TestCase { + num_str: "1311768467463790320", + little_endian_compact: "f0debc9a78563412", + little_endian: "f0debc9a78563412000000000000000000000000000000000000000000000000", + big_endian_compact: "123456789abcdef0", + big_endian: "000000000000000000000000000000000000000000000000123456789abcdef0", + }, + TestCase { + num_str: "94522879700260683142460330790866415", + little_endian_compact: "efcdab89674523f1debc9a78563412", + little_endian: "efcdab89674523f1debc9a785634120000000000000000000000000000000000", + big_endian_compact: "123456789abcdef123456789abcdef", + big_endian: "0000000000000000000000000000000000123456789abcdef123456789abcdef", + }, + TestCase { + num_str: "91343852333181432387730302044767688728495783936", + little_endian_compact: "0000000000000000000000000000000000000010", + little_endian: "0000000000000000000000000000000000000010000000000000000000000000", + big_endian_compact: "1000000000000000000000000000000000000000", + big_endian: "0000000000000000000000001000000000000000000000000000000000000000", + }, + TestCase { + num_str: "18515461264373351373200002665853028612451056578545711640558177340181847433846", + little_endian_compact: "766263aa200659e163ff713c5da1e1036086677553fe9521bc39d90b3461ef28", + little_endian: "766263aa200659e163ff713c5da1e1036086677553fe9521bc39d90b3461ef28", + big_endian_compact: "28ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276", + big_endian: "28ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276", + }, +]; + +#[test] +fn test_u256_big_endian_compact() { + for test in TEST_CASES { + let bytes = hex::decode(test.big_endian_compact).unwrap(); + let num = U256::from_big_endian_slice(bytes.as_slice()).unwrap(); + + assert_eq!(num.to_string(), test.num_str); + + let actual_hex_encoded = hex::encode(num.to_big_endian_compact(), false); + assert_eq!(actual_hex_encoded, test.big_endian_compact); + } +} + +#[test] +fn test_u256_big_endian() { + for test in TEST_CASES { + let bytes = H256::from_str(test.big_endian).unwrap(); + let num = U256::from_big_endian(bytes); + + assert_eq!(num.to_string(), test.num_str); + + let actual_bytes = num.to_big_endian(); + assert_eq!(actual_bytes, bytes); + } +} + +#[test] +fn test_u256_little_endian_compact() { + for test in TEST_CASES { + let bytes = hex::decode(test.little_endian_compact).unwrap(); + let num = U256::from_little_endian_slice(bytes.as_slice()).unwrap(); + + assert_eq!(num.to_string(), test.num_str); + + let actual_hex_encoded = hex::encode(num.to_little_endian_compact(), false); + assert_eq!(actual_hex_encoded, test.little_endian_compact); + } +} + +#[test] +fn test_u256_little_endian() { + for test in TEST_CASES { + let bytes = H256::from_str(test.little_endian).unwrap(); + let num = U256::from_little_endian(bytes); + + assert_eq!(num.to_string(), test.num_str); + + let actual_bytes = num.to_little_endian(); + assert_eq!(actual_bytes, bytes); + } +} + +#[test] +fn test_u256_from_str() { + for test in TEST_CASES { + let bytes = H256::from_str(test.little_endian).unwrap(); + let num = U256::from_little_endian(bytes); + + let actual = U256::from_str(test.num_str).unwrap(); + assert_eq!(num, actual); + } +} + +#[test] +fn test_u256_big_endian_min_len() { + // 0x0100 + let num = U256::from(256_u64); + + let num_0 = num.to_big_endian_compact_min_len(0); + assert_eq!(hex::encode(num_0, false), "0100"); + + let num_1 = num.to_big_endian_compact_min_len(1); + assert_eq!(hex::encode(num_1, false), "0100"); + + let num_2 = num.to_big_endian_compact_min_len(2); + assert_eq!(hex::encode(num_2, false), "0100"); + + let num_3 = num.to_big_endian_compact_min_len(3); + assert_eq!(num_3.to_hex(), "000100"); + + let num_20 = num.to_big_endian_compact_min_len(20); + assert_eq!(num_20.to_hex(), "0000000000000000000000000000000000000100"); + + let num_32 = num.to_big_endian_compact_min_len(32); + assert_eq!( + num_32.to_hex(), + "0000000000000000000000000000000000000000000000000000000000000100" + ); + + let num_33 = num.to_big_endian_compact_min_len(33); + assert_eq!( + num_33.to_hex(), + "000000000000000000000000000000000000000000000000000000000000000100" + ); + + let num_64 = num.to_big_endian_compact_min_len(64); + assert_eq!( + num_64.to_hex(), + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100" + ); +} diff --git a/rust/tw_proto/Cargo.toml b/rust/tw_proto/Cargo.toml index 22179b761bc..73be2eecd76 100644 --- a/rust/tw_proto/Cargo.toml +++ b/rust/tw_proto/Cargo.toml @@ -3,7 +3,12 @@ name = "tw_proto" version = "0.1.0" edition = "2021" +[features] +fuzz = ["arbitrary"] + [dependencies] +# Enable in fuzz tests only! +arbitrary = { version = "1", features = ["derive"], optional = true } quick-protobuf = "0.8.1" tw_encoding = { path = "../tw_encoding" } tw_memory = { path = "../tw_memory" } diff --git a/rust/tw_proto/build.rs b/rust/tw_proto/build.rs index 1ba71bd5a11..b3e60df9a68 100644 --- a/rust/tw_proto/build.rs +++ b/rust/tw_proto/build.rs @@ -6,6 +6,8 @@ use pb_rs::types::FileDescriptor; use pb_rs::ConfigBuilder; +#[cfg(feature = "fuzz")] +use std::io::{self, Read, Write}; use std::path::{Path, PathBuf}; use std::{env, fs}; @@ -50,4 +52,54 @@ fn main() { .expect("Error configuring pb-rs builder") .build(); FileDescriptor::run(&out_protos).expect("Error generating proto files"); + + #[cfg(feature = "fuzz")] + add_custom_derives(&out_dir, &["arbitrary::Arbitrary"]) + .expect("Error on adding 'arbitrary::Arbitrary' derive"); +} + +/// Unfortunately, `pb-rs` does not provide a proper support of custom derives. +/// [`ConfigBuilder::custom_struct_derive`] adds the derive macroses for structs only, +/// however they should be added for enums too. +/// Issues: https://github.com/tafia/quick-protobuf/issues/195, https://github.com/tafia/quick-protobuf/issues/212 +#[cfg(feature = "fuzz")] +fn add_custom_derives(out_dir: &Path, custom_derives: &[&str]) -> io::Result<()> { + // Debug is derived for all generated types. + let pattern = "#[derive(Debug"; + let replace_with = format!("#[derive(Debug, {}", custom_derives.join(", ")); + + let tw_dir = out_dir.join("TW"); + for blockchain_dir in tw_dir.read_dir()? { + let blockchain_dir = blockchain_dir?.path(); + + // There can be `mod.rs` files. Skip them. + if !blockchain_dir.is_dir() { + continue; + } + + let blockchain_proto = blockchain_dir.join("Proto.rs"); + replace_proto_content(&blockchain_proto, pattern, &replace_with)?; + } + + Ok(()) +} + +#[cfg(feature = "fuzz")] +fn replace_proto_content(path_to_file: &Path, pattern: &str, replace_with: &str) -> io::Result<()> { + let proto_content = { + let mut proto_file = fs::File::open(path_to_file)?; + + let mut proto_content = String::new(); + proto_file.read_to_string(&mut proto_content)?; + + proto_content + }; + + let upgraded_proto_content = proto_content.replace(pattern, &replace_with); + + let mut file = fs::OpenOptions::new() + .write(true) + .truncate(true) + .open(&path_to_file)?; + file.write_all(upgraded_proto_content.as_bytes()) } diff --git a/rust/tw_proto/src/lib.rs b/rust/tw_proto/src/lib.rs index 1b52d670b61..cf592f95037 100644 --- a/rust/tw_proto/src/lib.rs +++ b/rust/tw_proto/src/lib.rs @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer}; +use quick_protobuf::{BytesReader, Writer}; #[allow(non_snake_case)] #[rustfmt::skip] @@ -15,7 +15,7 @@ mod generated { pub use generated::TW::*; pub use quick_protobuf::{ deserialize_from_slice as deserialize_prefixed, serialize_into_vec as serialize_prefixed, - Error as ProtoError, Result as ProtoResult, + Error as ProtoError, MessageRead, MessageWrite, Result as ProtoResult, }; pub mod ffi; @@ -37,3 +37,19 @@ pub fn deserialize<'a, T: MessageRead<'a>>(data: &'a [u8]) -> ProtoResult { let mut reader = BytesReader::from_bytes(data); T::from_reader(&mut reader, data) } + +/// There is no way to create an instance of the `NoMessage` enum as it doesn't has variants. +pub enum NoMessage {} + +impl MessageWrite for NoMessage {} + +/// `DummyMessage` has no effect on `MessageWrite` and `MessageRead`. +pub struct DummyMessage; + +impl MessageWrite for DummyMessage {} + +impl<'a> MessageRead<'a> for DummyMessage { + fn from_reader(_r: &mut BytesReader, _bytes: &'a [u8]) -> ProtoResult { + Ok(DummyMessage) + } +} diff --git a/rust/tw_proto/tests/proto_ffi_tests.rs b/rust/tw_proto/tests/proto_ffi_tests.rs index 654141268da..ce3df7c6a03 100644 --- a/rust/tw_proto/tests/proto_ffi_tests.rs +++ b/rust/tw_proto/tests/proto_ffi_tests.rs @@ -11,7 +11,7 @@ use tw_memory::ffi::c_byte_array::CByteArray; fn test_pass_eth_signing_msg_through() { let serialized = tw_encoding::hex::decode("0a0101120100220509c76524002a030130b9422a3078366231373534373465383930393463343464613938623935346565646561633439353237316430664a20608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151523812360a2a30783533323262333463383865643036393139373162663532613730343734343866306634656663383412081bc16d674ec80000").unwrap(); let actual = unsafe { - let array = CByteArray::new(serialized.clone()); + let array = CByteArray::from(serialized.clone()); tw_proto::ffi::pass_eth_signing_msg_through(array.data(), array.size()) .unwrap() .into_vec() diff --git a/rust/tw_ronin/Cargo.toml b/rust/tw_ronin/Cargo.toml new file mode 100644 index 00000000000..98a13847890 --- /dev/null +++ b/rust/tw_ronin/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "tw_ronin" +version = "0.1.0" +edition = "2021" + +[dependencies] +tw_coin_entry = { path = "../tw_coin_entry" } +tw_evm = { path = "../tw_evm" } +tw_keypair = { path = "../tw_keypair" } +tw_memory = { path = "../tw_memory" } +tw_proto = { path = "../tw_proto" } + +[dev-dependencies] +tw_coin_entry = { path = "../tw_coin_entry", features = ["test-utils"] } +tw_encoding = { path = "../tw_encoding" } +tw_number = { path = "../tw_number", features = ["helpers"] } diff --git a/rust/tw_ronin/src/address.rs b/rust/tw_ronin/src/address.rs new file mode 100644 index 00000000000..e14be4225e0 --- /dev/null +++ b/rust/tw_ronin/src/address.rs @@ -0,0 +1,67 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::fmt; +use std::str::FromStr; +use tw_coin_entry::coin_entry::CoinAddress; +use tw_coin_entry::error::AddressError; +use tw_evm::address::{Address as EthAddress, EvmAddress}; +use tw_keypair::ecdsa::secp256k1; +use tw_memory::Data; + +/// cbindgen:ignore +const RONIN_PREFIX: &str = "ronin:"; + +#[derive(Debug)] +pub struct Address(EthAddress); + +impl Address { + /// Initializes an address with a `secp256k1` public key. + #[inline] + pub fn with_secp256k1_pubkey(pubkey: &secp256k1::PublicKey) -> Address { + Address(EthAddress::with_secp256k1_pubkey(pubkey)) + } +} + +impl EvmAddress for Address {} + +impl From
for EthAddress { + #[inline] + fn from(addr: Address) -> Self { + addr.0 + } +} + +impl FromStr for Address { + type Err = AddressError; + + fn from_str(s: &str) -> Result { + // Accept both Ronin and standard addresses. + let standard = match s.strip_prefix(RONIN_PREFIX) { + Some(ronin_no_prefix) => format!("0x{ronin_no_prefix}"), + None => s.to_string(), + }; + EthAddress::from_str(&standard).map(Address) + } +} + +impl fmt::Display for Address { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let standard_prefixed = self.0.to_string(); + // Strip the `0x` prefix. + let standard_no_prefix = standard_prefixed + .strip_prefix("0x") + .unwrap_or(&standard_prefixed); + write!(f, "{RONIN_PREFIX}{standard_no_prefix}") + } +} + +impl CoinAddress for Address { + #[inline] + fn data(&self) -> Data { + self.0.data() + } +} diff --git a/rust/tw_ronin/src/entry.rs b/rust/tw_ronin/src/entry.rs new file mode 100644 index 00000000000..976a08e0eca --- /dev/null +++ b/rust/tw_ronin/src/entry.rs @@ -0,0 +1,100 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::Address; +use crate::ronin_context::RoninContext; +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::derivation::Derivation; +use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::prefix::NoPrefix; +use tw_evm::evm_entry::EvmEntry; +use tw_evm::modules::compiler::Compiler; +use tw_evm::modules::json_signer::EthJsonSigner; +use tw_evm::modules::message_signer::EthMessageSigner; +use tw_evm::modules::signer::Signer; +use tw_keypair::tw::PublicKey; +use tw_proto::Ethereum::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct RoninEntry; + +impl CoinEntry for RoninEntry { + type AddressPrefix = NoPrefix; + type Address = Address; + type SigningInput<'a> = Proto::SigningInput<'a>; + type SigningOutput = Proto::SigningOutput<'static>; + type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + + // Optional modules: + type JsonSigner = EthJsonSigner; + type PlanBuilder = NoPlanBuilder; + type MessageSigner = EthMessageSigner; + + #[inline] + fn parse_address( + &self, + _coin: &dyn CoinContext, + address: &str, + _prefix: Option, + ) -> AddressResult { + Address::from_str(address) + } + + fn derive_address( + &self, + _coin: &dyn CoinContext, + public_key: PublicKey, + _derivation: Derivation, + _prefix: Option, + ) -> AddressResult { + let public_key = public_key + .to_secp256k1() + .ok_or(AddressError::PublicKeyTypeMismatch)?; + Ok(Address::with_secp256k1_pubkey(public_key)) + } + + #[inline] + fn sign(&self, _coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput { + Signer::::sign_proto(input) + } + + #[inline] + fn preimage_hashes( + &self, + _coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + ) -> Self::PreSigningOutput { + Compiler::::preimage_hashes(input) + } + + #[inline] + fn compile( + &self, + _coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Self::SigningOutput { + Compiler::::compile(input, signatures, public_keys) + } + + #[inline] + fn json_signer(&self) -> Option { + Some(EthJsonSigner::default()) + } + + #[inline] + fn message_signer(&self) -> Option { + Some(EthMessageSigner) + } +} + +impl EvmEntry for RoninEntry { + type Context = RoninContext; +} diff --git a/rust/tw_ronin/src/lib.rs b/rust/tw_ronin/src/lib.rs new file mode 100644 index 00000000000..132d9b9b730 --- /dev/null +++ b/rust/tw_ronin/src/lib.rs @@ -0,0 +1,9 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod address; +pub mod entry; +pub mod ronin_context; diff --git a/rust/tw_ronin/src/ronin_context.rs b/rust/tw_ronin/src/ronin_context.rs new file mode 100644 index 00000000000..0799f42e3f5 --- /dev/null +++ b/rust/tw_ronin/src/ronin_context.rs @@ -0,0 +1,15 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::Address; +use tw_evm::evm_context::EvmContext; + +#[derive(Default)] +pub struct RoninContext; + +impl EvmContext for RoninContext { + type Address = Address; +} diff --git a/rust/tw_ronin/tests/address.rs b/rust/tw_ronin/tests/address.rs new file mode 100644 index 00000000000..0e62310ddb0 --- /dev/null +++ b/rust/tw_ronin/tests/address.rs @@ -0,0 +1,43 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::str::FromStr; +use tw_ronin::address::Address; + +#[test] +fn test_ronin_address_valid() { + let normalized = "ronin:EC49280228b0D05Aa8e8b756503254e1eE7835ab"; + let valid = [ + normalized, + "ronin:ec49280228b0d05aa8e8b756503254e1ee7835ab", + "0xEC49280228b0D05Aa8e8b756503254e1eE7835ab", + "ronin:0xEC49280228b0D05Aa8e8b756503254e1eE7835ab", + ]; + + for test in valid { + let addr = Address::from_str(test).unwrap(); + assert_eq!(addr.to_string(), normalized); + } +} + +#[test] +fn test_ronin_address_invalid() { + let invalid = [ + "EC49280228b0D05Aa8e8b756503254e1eE7835ab", // no prefix + "ec49280228b0d05aa8e8b756503254e1ee7835ab", // no prefix + "roni:EC49280228b0D05Aa8e8b756503254e1eE7835ab", // wrong prefix + "ronin=EC49280228b0D05Aa8e8b756503254e1eE7835ab", // wrong prefix + "0xronin:EC49280228b0D05Aa8e8b756503254e1eE7835ab", // wrong prefix + "EC49280228b0D05Aa8e8b756503254e1eE7835", // too short + "ronin:EC49280228b0D05Aa8e8b756503254e1eE7835", // too short + "ronin:ec49280228b0d05aa8e8b756503254e1ee7835", // too short + "", // empty + ]; + + for test in invalid { + Address::from_str(test).unwrap_err(); + } +} diff --git a/rust/tw_ronin/tests/compiler.rs b/rust/tw_ronin/tests/compiler.rs new file mode 100644 index 00000000000..53972303b5c --- /dev/null +++ b/rust/tw_ronin/tests/compiler.rs @@ -0,0 +1,77 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::borrow::Cow; +use tw_coin_entry::coin_entry_ext::CoinEntryExt; +use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_keypair::ecdsa::secp256k1; +use tw_keypair::tw; +use tw_number::U256; +use tw_proto::Ethereum::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; +use tw_proto::{deserialize, serialize}; +use tw_ronin::entry::RoninEntry; + +#[test] +fn test_ronin_preimage_hashes_and_compile() { + let transfer = Proto::mod_Transaction::Transfer { + amount: U256::encode_be_compact(1_000_000_000_000_000_000), + data: Cow::default(), + }; + let input = Proto::SigningInput { + nonce: U256::encode_be_compact(11), + chain_id: U256::encode_be_compact(1), + gas_price: U256::encode_be_compact(20_000_000_000), + gas_limit: U256::encode_be_compact(21_000), + to_address: "ronin:3535353535353535353535353535353535353535".into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::transfer(transfer), + }), + ..Proto::SigningInput::default() + }; + + let input_data = serialize(&input).unwrap(); + + let res = RoninEntry + .preimage_hashes(&EmptyCoinContext, &input_data) + .expect("!preimage_hashes"); + let preimage: CompilerProto::PreSigningOutput = + deserialize(res.as_slice()).expect("Coin entry returned an invalid output"); + + assert_eq!(preimage.error, SigningErrorType::OK); + assert!(preimage.error_message.is_empty()); + assert_eq!( + preimage.data_hash.to_hex(), + "15e180a6274b2f6a572b9b51823fce25ef39576d10188ecdcd7de44526c47217" + ); + + // Simulate signature, normally obtained from signature server + let public_key = secp256k1::PublicKey::try_from("044bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382ce28cab79ad7119ee1ad3ebcdb98a16805211530ecc6cfefa1b88e6dff99232a").unwrap(); + let public_key = tw::PublicKey::Secp256k1Extended(public_key); + let signature = "360a84fb41ad07f07c845fedc34cde728421803ebbaae392fc39c116b29fc07b53bd9d1376e15a191d844db458893b928f3efbfee90c9febf51ab84c9796677900".decode_hex().unwrap(); + + // Verify signature (pubkey & hash & signature) + assert!(public_key.verify(&signature, &preimage.data_hash)); + + // Step 3: Compile transaction info + let output_data = RoninEntry + .compile( + &EmptyCoinContext, + &input_data, + vec![signature], + vec![public_key.to_bytes()], + ) + .expect("!compile"); + let output: Proto::SigningOutput = + deserialize(&output_data).expect("Coin entry returned an invalid output"); + + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + let expected_encoded = "f86c0b8504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a0360a84fb41ad07f07c845fedc34cde728421803ebbaae392fc39c116b29fc07ba053bd9d1376e15a191d844db458893b928f3efbfee90c9febf51ab84c97966779"; + assert_eq!(output.encoded.to_hex(), expected_encoded); +} diff --git a/rust/tw_ronin/tests/rlp.rs b/rust/tw_ronin/tests/rlp.rs new file mode 100644 index 00000000000..d4d88b6dbb0 --- /dev/null +++ b/rust/tw_ronin/tests/rlp.rs @@ -0,0 +1,34 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::borrow::Cow; +use tw_coin_entry::error::SigningErrorType; +use tw_encoding::hex::ToHex; +use tw_evm::evm_entry::EvmEntryExt; +use tw_proto::EthereumRlp::Proto as RlpProto; +use tw_proto::{deserialize, serialize}; +use tw_ronin::entry::RoninEntry; +use RlpProto::mod_RlpItem::OneOfitem as Item; + +#[test] +fn test_rlp_encode_ronin_address() { + let ronin_addr = "ronin:6b175474e89094c44da98b954eedeac495271d0f"; + let input = RlpProto::EncodingInput { + item: Some(RlpProto::RlpItem { + item: Item::address(Cow::from(ronin_addr)), + }), + }; + let input_data = serialize(&input).unwrap(); + let output_data = RoninEntry.encode_rlp(&input_data).unwrap(); + let output: RlpProto::EncodingOutput = + deserialize(&output_data).expect("Coin entry returned an invalid output"); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + assert_eq!( + output.encoded.to_hex(), + "946b175474e89094c44da98b954eedeac495271d0f" + ); +} diff --git a/rust/tw_ronin/tests/signer.rs b/rust/tw_ronin/tests/signer.rs new file mode 100644 index 00000000000..32dd2cb5338 --- /dev/null +++ b/rust/tw_ronin/tests/signer.rs @@ -0,0 +1,68 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::borrow::Cow; +use tw_coin_entry::coin_entry_ext::CoinEntryExt; +use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_number::U256; +use tw_proto::Ethereum::Proto; +use tw_proto::{deserialize, serialize}; +use tw_ronin::entry::RoninEntry; + +/// https://explorer.roninchain.com/tx/0xf13a2c4421700f8782ca73eaf16bb8baf82bcf093e23570a1ff062cdd8dbf6c3 +#[test] +fn test_ronin_signing() { + let private = "0x4646464646464646464646464646464646464646464646464646464646464646" + .decode_hex() + .unwrap(); + + let transfer = Proto::mod_Transaction::Transfer { + amount: U256::encode_be_compact(276_447), + data: Cow::default(), + }; + + let input = Proto::SigningInput { + chain_id: U256::encode_be_compact(2020), + nonce: U256::encode_be_compact(0), + gas_price: U256::encode_be_compact(1_000_000_000), + gas_limit: U256::encode_be_compact(21_000), + to_address: "ronin:c36edf48e21cf395b206352a1819de658fd7f988".into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::transfer(transfer), + }), + private_key: private.into(), + ..Proto::SigningInput::default() + }; + let input_data = serialize(&input).unwrap(); + + let output_data = RoninEntry + .sign(&EmptyCoinContext, &input_data) + .expect("!sign"); + let output: Proto::SigningOutput = + deserialize(&output_data).expect("Coin entry returned an invalid output"); + + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected = "f86880843b9aca0082520894c36edf48e21cf395b206352a1819de658fd7f988830437df80820feca0442aa06b0d0465bfecf84b28e2ce614a32a1ccc12735dc03a5799517d6659d7aa004e1bf2efa30743f1b6d49dbec2671e9fb5ead1e7da15e352ca1df6fb86a8ba7"; + assert_eq!(output.encoded.to_hex(), expected); +} + +#[test] +fn test_sign_json() { + let input_json = r#"{"chainId":"B+Q=","nonce":"AA==","gasPrice":"O5rKAA==","gasLimit":"Ugg=","toAddress":"ronin:c36edf48e21cf395b206352a1819de658fd7f988","privateKey":"RkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkY=","transaction":{"transfer":{"amount":"BDff"}}}"#; + let private_key = "0x4646464646464646464646464646464646464646464646464646464646464646" + .decode_hex() + .unwrap(); + + RoninEntry + .sign_json(&EmptyCoinContext, input_json, private_key) + .expect_err("'EthEntry::sign_json' is not supported yet"); + + // Expected result - "f86880843b9aca0082520894c36edf48e21cf395b206352a1819de658fd7f988830437df80820feca0442aa06b0d0465bfecf84b28e2ce614a32a1ccc12735dc03a5799517d6659d7aa004e1bf2efa30743f1b6d49dbec2671e9fb5ead1e7da15e352ca1df6fb86a8ba7" +} diff --git a/rust/tw_starknet/Cargo.toml b/rust/tw_starknet/Cargo.toml deleted file mode 100644 index 8aa39b7880c..00000000000 --- a/rust/tw_starknet/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "tw_starknet" -version = "0.1.0" -edition = "2021" - -[dependencies] -hex = "0.4.3" -starknet-crypto = "0.5.1" -starknet-ff = "0.3.3" -tw_encoding = { path = "../tw_encoding" } -tw_memory = { path = "../tw_memory" } diff --git a/rust/tw_starknet/src/ffi.rs b/rust/tw_starknet/src/ffi.rs deleted file mode 100644 index 79f243b52fe..00000000000 --- a/rust/tw_starknet/src/ffi.rs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#![allow(clippy::missing_safety_doc)] - -use crate::key_pair; -use std::ffi::{c_char, CStr}; -use tw_memory::ffi::c_result::{CBoolResult, CStrResult, ErrorCode}; - -#[repr(C)] -pub enum CStarknetCode { - Ok = 0, - InvalidInput = 1, - PrivKeyError = 2, -} - -impl From for ErrorCode { - fn from(code: CStarknetCode) -> Self { - code as ErrorCode - } -} - -impl From for CStarknetCode { - fn from(_: key_pair::StarknetKeyPairError) -> Self { - CStarknetCode::PrivKeyError - } -} - -/// Returns a StarkNet pubkey corresponding to the given `priv_key`. -/// \param priv_key - *non-null* C-compatible, nul-terminated string. -/// \return *non-null* C-compatible, nul-terminated string. -#[no_mangle] -pub unsafe extern "C" fn starknet_pubkey_from_private(priv_key: *const c_char) -> CStrResult { - let priv_key = match CStr::from_ptr(priv_key).to_str() { - Ok(priv_key) => priv_key, - Err(_) => return CStrResult::error(CStarknetCode::InvalidInput), - }; - key_pair::starknet_pubkey_from_private(priv_key) - .map(tw_memory::c_string_standalone) - .map_err(CStarknetCode::from) - .into() -} - -/// Signs the input `hash` with the given `priv_key` and returns a signature compatible with StarkNet. -/// \param priv_key *non-null* C-compatible, nul-terminated string. -/// \param hash *non-null* C-compatible, nul-terminated string. -/// \return *non-null* C-compatible, nul-terminated string. -#[no_mangle] -pub unsafe extern "C" fn starknet_sign(priv_key: *const c_char, hash: *const c_char) -> CStrResult { - let priv_key = match CStr::from_ptr(priv_key).to_str() { - Ok(priv_key) => priv_key, - Err(_) => return CStrResult::error(CStarknetCode::InvalidInput), - }; - let hash = match CStr::from_ptr(hash).to_str() { - Ok(hash) => hash, - Err(_) => return CStrResult::error(CStarknetCode::InvalidInput), - }; - key_pair::starknet_sign(priv_key, hash) - .map(tw_memory::c_string_standalone) - .map_err(CStarknetCode::from) - .into() -} - -/// Verifies if the given signature (`r` and `s`) is valid over a message `hash` given a StarkNet `pub_key`. -/// \param pub_key *non-null* C-compatible, nul-terminated string. -/// \param hash *non-null* C-compatible, nul-terminated string. -/// \param r *non-null* C-compatible, nul-terminated string. -/// \param s *non-null* C-compatible, nul-terminated string. -/// \return true if the signature is valid. -#[no_mangle] -pub unsafe extern "C" fn starknet_verify( - pub_key: *const c_char, - hash: *const c_char, - r: *const c_char, - s: *const c_char, -) -> CBoolResult { - macro_rules! parse_c_str { - ($s:expr) => { - match CStr::from_ptr($s).to_str() { - Ok(s) => s, - Err(_) => return CBoolResult::error(CStarknetCode::InvalidInput), - } - }; - } - - let pub_key = parse_c_str!(pub_key); - let hash = parse_c_str!(hash); - let r = parse_c_str!(r); - let s = parse_c_str!(s); - - key_pair::starknet_verify(pub_key, hash, r, s) - .map_err(CStarknetCode::from) - .into() -} diff --git a/rust/tw_starknet/src/key_pair.rs b/rust/tw_starknet/src/key_pair.rs deleted file mode 100644 index 1d63b6f6f56..00000000000 --- a/rust/tw_starknet/src/key_pair.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -use starknet_crypto::{get_public_key, rfc6979_generate_k, sign, verify, SignError, Signature}; -use starknet_ff::{FieldElement, FromByteArrayError}; -use tw_encoding::hex::{self as tw_hex, FromHexError}; - -/// The maximum number of attempts to sign a message. -/// As the number is coming from `rfc6979_generate_k` so the probability is lower. -const SIGN_RETRIES: usize = 5; - -pub type Result = std::result::Result; - -#[derive(Debug)] -pub enum StarknetKeyPairError { - HexError(FromHexError), - ByteArrayError(FromByteArrayError), - InvalidLength, - ErrorSigning, -} - -impl From for StarknetKeyPairError { - fn from(err: FromHexError) -> Self { - StarknetKeyPairError::HexError(err) - } -} - -impl From for StarknetKeyPairError { - fn from(err: FromByteArrayError) -> Self { - StarknetKeyPairError::ByteArrayError(err) - } -} - -pub fn starknet_pubkey_from_private(priv_key: &str) -> Result { - let private_key = field_element_from_be_hex(priv_key)?; - Ok(format!("{:#02x}", get_public_key(&private_key))) -} - -pub fn starknet_sign(priv_key: &str, hash: &str) -> Result { - let private_key = field_element_from_be_hex(priv_key)?; - let hash_field = field_element_from_be_hex(hash)?; - let signature = ecdsa_sign(&private_key, &hash_field)?; - Ok(signature.to_string()) -} - -pub fn starknet_verify(pub_key: &str, hash: &str, r: &str, s: &str) -> Result { - let pub_key = field_element_from_be_hex(pub_key)?; - let hash = field_element_from_be_hex(hash)?; - let r = field_element_from_be_hex(r)?; - let s = field_element_from_be_hex(s)?; - - Ok(verify(&pub_key, &hash, &r, &s).unwrap_or_default()) -} - -fn field_element_from_be_hex(hex: &str) -> Result { - let decoded = tw_hex::decode(hex)?; - if decoded.len() > 32 { - return Err(StarknetKeyPairError::InvalidLength); - } - - let mut buffer = [0u8; 32]; - buffer[(32 - decoded.len())..].copy_from_slice(&decoded[..]); - - FieldElement::from_bytes_be(&buffer).map_err(StarknetKeyPairError::from) -} - -/// `starknet-core` depends on an out-dated `starknet-crypto` crate. -/// We need to reimplement the same but using the latest `starknet-crypto` version. -/// https://github.com/xJonathanLEI/starknet-rs/blob/0c78b365c2a7a7d4138553cba42fa69d695aa73d/starknet-core/src/crypto.rs#L34-L59 -pub fn ecdsa_sign(private_key: &FieldElement, message_hash: &FieldElement) -> Result { - // Seed-retry logic ported from `cairo-lang` - let mut seed = None; - for _ in 0..SIGN_RETRIES { - let k = rfc6979_generate_k(message_hash, private_key, seed.as_ref()); - - match sign(private_key, message_hash, &k) { - Ok(sig) => { - return Ok(Signature { r: sig.r, s: sig.s }); - }, - Err(SignError::InvalidMessageHash) => return Err(StarknetKeyPairError::ErrorSigning), - Err(SignError::InvalidK) => { - // Bump seed and retry - seed = match seed { - Some(prev_seed) => Some(prev_seed + FieldElement::ONE), - None => Some(FieldElement::ONE), - }; - }, - }; - } - Err(StarknetKeyPairError::ErrorSigning) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_starknet_sign_invalid_k() { - let private = "0000000000000000000000000000000000000000000000000000000000000000"; - let hash = "0000000000000000000000000000000000000000000000000000000000000000"; - let err = starknet_sign(private, hash).expect_err("Retry limit expected"); - assert!(matches!(err, StarknetKeyPairError::ErrorSigning)); - } -} diff --git a/rust/tw_starknet/tests/starknet_ffi_tests.rs b/rust/tw_starknet/tests/starknet_ffi_tests.rs deleted file mode 100644 index 958a08847fe..00000000000 --- a/rust/tw_starknet/tests/starknet_ffi_tests.rs +++ /dev/null @@ -1,128 +0,0 @@ -use std::ffi::{c_char, CString}; -use tw_memory::c_string_standalone; -use tw_memory::ffi::c_result::OK_CODE; -use tw_memory::ffi::free_string; -use tw_starknet::ffi::{ - starknet_pubkey_from_private, starknet_sign, starknet_verify, CStarknetCode, -}; - -#[test] -fn test_starknet_pubkey_from_private() { - let priv_key_hex = - c_string_standalone("058ab7989d625b1a690400dcbe6e070627adedceff7bd196e58d4791026a8afe"); - let pub_key_raw = unsafe { starknet_pubkey_from_private(priv_key_hex) }; - assert_eq!(pub_key_raw.code, OK_CODE); - let actual = unsafe { - CString::from_raw(pub_key_raw.result as *mut c_char) - .into_string() - .unwrap() - }; - assert_eq!( - actual, - "0x2a4c7332c55d6c1c510d24272d1db82878f2302f05b53bcc38695ed5f78fffd" - ); - - unsafe { free_string(priv_key_hex) }; -} - -#[test] -fn test_starknet_pubkey_from_private_invalid() { - let priv_key_raw = - c_string_standalone("058ab7989d625b1a690400dcbe6e070627adedceff7bd196e58d4791026a8afx"); - let pub_key_raw = unsafe { starknet_pubkey_from_private(priv_key_raw) }; - assert_eq!(pub_key_raw.code, CStarknetCode::PrivKeyError as i32); - assert!(pub_key_raw.result.is_null()); - - // Non-null terminated string. - let invalid_data = vec![159, 146, 150]; - let invalid_priv_key_ptr = invalid_data.as_ptr() as *const c_char; - - let pub_key_raw = unsafe { starknet_pubkey_from_private(invalid_priv_key_ptr) }; - assert_eq!(pub_key_raw.code, CStarknetCode::InvalidInput as i32); - assert!(pub_key_raw.result.is_null()); - - unsafe { free_string(priv_key_raw) }; -} - -#[test] -fn test_starknet_sign() { - let priv_key_raw = - c_string_standalone("0139fe4d6f02e666e86a6f58e65060f115cd3c185bd9e98bd829636931458f79"); - let digest_raw = - c_string_standalone("06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76"); - - let signature = unsafe { starknet_sign(priv_key_raw, digest_raw) }; - assert_eq!(signature.code, OK_CODE); - let actual = unsafe { CString::from_raw(signature.result.cast_mut()) } - .into_string() - .unwrap(); - let expected = "061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9a"; - assert_eq!(actual, expected); - - unsafe { free_string(priv_key_raw) }; - unsafe { free_string(digest_raw) }; -} - -#[test] -fn test_starknet_verify() { - let pubkey_raw = - c_string_standalone("02c5dbad71c92a45cc4b40573ae661f8147869a91d57b8d9b8f48c8af7f83159"); - let hash_raw = - c_string_standalone("06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76"); - let signature_r_raw = - c_string_standalone("061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f"); - let signature_s_raw = - c_string_standalone("04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9a"); - - let res = unsafe { starknet_verify(pubkey_raw, hash_raw, signature_r_raw, signature_s_raw) }; - assert_eq!(res.code, OK_CODE); - assert!(res.result); - - unsafe { free_string(pubkey_raw) }; - unsafe { free_string(hash_raw) }; - unsafe { free_string(signature_r_raw) }; - unsafe { free_string(signature_s_raw) }; -} - -#[test] -fn test_starknet_verify_fail() { - let pubkey_raw = - c_string_standalone("02c5dbad71c92a45cc4b40573ae661f8147869a91d57b8d9b8f48c8af7f83159"); - let hash_raw = - c_string_standalone("06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76"); - let signature_r_raw = - c_string_standalone("061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f"); - let signature_s_raw = - c_string_standalone("04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9b"); - - let res = unsafe { starknet_verify(pubkey_raw, hash_raw, signature_r_raw, signature_s_raw) }; - assert_eq!(res.code, OK_CODE); - assert!(!res.result); - - unsafe { free_string(pubkey_raw) }; - unsafe { free_string(hash_raw) }; - unsafe { free_string(signature_r_raw) }; - unsafe { free_string(signature_s_raw) }; -} - -#[test] -fn test_starknet_private_key_invalid() { - let private_raw = - c_string_standalone("02c5dbad71c92a45cc4b40573ae661f8147869a91d57b8d9b8f48c8af7f831590123"); - - let res = unsafe { starknet_pubkey_from_private(private_raw) }; - assert_eq!(res.code, CStarknetCode::PrivKeyError as i32); - - unsafe { free_string(private_raw) }; -} - -#[test] -fn test_starknet_private_key_zero() { - let private_raw = - c_string_standalone("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - - let res = unsafe { starknet_pubkey_from_private(private_raw) }; - assert_eq!(res.code, CStarknetCode::PrivKeyError as i32); - - unsafe { free_string(private_raw) }; -} diff --git a/rust/tw_utxo/Cargo.toml b/rust/tw_utxo/Cargo.toml new file mode 100644 index 00000000000..5631960638f --- /dev/null +++ b/rust/tw_utxo/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "tw_utxo" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tw_coin_entry = { path = "../tw_coin_entry" } +tw_keypair = { path = "../tw_keypair" } +tw_proto = { path = "../tw_proto" } +tw_memory = { path = "../tw_memory" } +tw_encoding = { path = "../tw_encoding" } +bitcoin = "0.30.1" +secp256k1 = { version = "0.27.0", features = [ "rand-std" ] } + +[dev-dependencies] +tw_encoding = { path = "../tw_encoding" } diff --git a/rust/tw_utxo/src/compiler.rs b/rust/tw_utxo/src/compiler.rs new file mode 100644 index 00000000000..776d5f24a32 --- /dev/null +++ b/rust/tw_utxo/src/compiler.rs @@ -0,0 +1,453 @@ +use crate::{Error, Result}; +use bitcoin::blockdata::locktime::absolute::{Height, LockTime, Time}; +use bitcoin::consensus::Encodable; +use bitcoin::hashes::Hash; +use bitcoin::sighash::{EcdsaSighashType, Prevouts, SighashCache, TapSighashType}; +use bitcoin::taproot::TapLeafHash; +use bitcoin::{OutPoint, Script, ScriptBuf, Sequence, Transaction, TxIn, TxOut, Txid, Witness}; +use std::marker::PhantomData; +use tw_proto::Utxo::Proto::{self, SighashType}; + +type ProtoLockTimeVariant = Proto::mod_LockTime::OneOfvariant; +type ProtoSigningMethod = Proto::SigningMethod; + +pub trait UtxoContext { + type SigningInput<'a>; + type SigningOutput; + type PreSigningOutput; +} + +pub struct StandardBitcoinContext; + +impl UtxoContext for StandardBitcoinContext { + type SigningInput<'a> = Proto::SigningInput<'a>; + type SigningOutput = Proto::SigningInput<'static>; + type PreSigningOutput = Proto::SigningInput<'static>; +} + +pub struct Compiler { + _phantom: PhantomData, +} + +impl Compiler { + #[inline] + pub fn preimage_hashes(proto: Proto::SigningInput<'_>) -> Proto::PreSigningOutput<'static> { + Self::preimage_hashes_impl(proto) + .or_else(|err| { + std::result::Result::<_, ()>::Ok(Proto::PreSigningOutput { + error: err.into(), + ..Default::default() + }) + }) + .expect("did not convert error value") + } + + #[inline] + pub fn compile(proto: Proto::PreSerialization<'_>) -> Proto::SerializedTransaction<'static> { + Self::compile_impl(proto) + .or_else(|err| { + std::result::Result::<_, ()>::Ok(Proto::SerializedTransaction { + error: err.into(), + ..Default::default() + }) + }) + .expect("did not convert error value") + } + + fn preimage_hashes_impl( + mut proto: Proto::SigningInput<'_>, + ) -> Result> { + // TODO: Check for duplicate Txid (user error). + + // Calculate total outputs amount, based on it we can determine how many inputs to select. + let total_input: u64 = proto.inputs.iter().map(|input| input.value).sum(); + let total_output: u64 = proto.outputs.iter().map(|output| output.value).sum(); + + // Do some easy checks first. + + // Insufficient input amount. + if total_output > total_input { + return Err(Error::from(Proto::Error::Error_insufficient_inputs)); + } + + // Change scriptPubkey must be set if change output is enabled. + if !proto.disable_change_output && proto.change_script_pubkey.is_empty() { + return Err(Error::from( + Proto::Error::Error_missing_change_script_pubkey, + )); + } + + // If the input selector is InputSelector::SelectAscending, we sort the + // input first. + if let Proto::InputSelector::SelectAscending = proto.input_selector { + proto.inputs.sort_by(|a, b| a.value.cmp(&b.value)); + } + + // Unless InputSelector::UseAll is provided, we only use the necessariy + // amount of inputs to cover `total_output`. Any other input gets + // dropped. + let selected = if let Proto::InputSelector::SelectInOrder + | Proto::InputSelector::SelectAscending = proto.input_selector + { + let mut total_input = total_input; + let mut remaining = total_output; + + let selected: Vec = proto + .inputs + .into_iter() + .take_while(|input| { + if remaining == 0 { + return false; + } + + total_input += input.value; + remaining = remaining.saturating_sub(input.value); + + true + }) + .map(|input| Proto::TxIn { + txid: input.txid.to_vec().into(), + script_pubkey: input.script_pubkey.to_vec().into(), + leaf_hash: input.leaf_hash.to_vec().into(), + ..input + }) + .collect(); + + selected + } else { + // TODO: Write a function for this + proto + .inputs + .into_iter() + .map(|input| Proto::TxIn { + txid: input.txid.to_vec().into(), + script_pubkey: input.script_pubkey.to_vec().into(), + leaf_hash: input.leaf_hash.to_vec().into(), + ..input + }) + .collect() + }; + + // Update protobuf structure with selected inputs. + proto.inputs = selected.clone(); + + // Update the `total_input` amount based on the selected inputs. + let total_input: u64 = proto.inputs.iter().map(|input| input.value).sum(); + + // Calculate the total input weight projection. + let input_weight: u64 = proto.inputs.iter().map(|input| input.weight_estimate).sum(); + + // Convert Protobuf structure to `bitcoin` crate native transaction, + // used for weight/fee calculation. + let tx = convert_proto_to_tx(&proto)?; + + // Estimate of the change output weight. + let output_weight = if proto.disable_change_output { + 0 + } else { + // VarInt + script_pubkey size, rough estimate. + 1 + proto.change_script_pubkey.len() as u64 + }; + + // Calculate the full weight projection (base weight + input & output weight). + let weight_estimate = tx.weight().to_wu() + input_weight + output_weight; + let fee_estimate = (weight_estimate + 3) / 4 * proto.weight_base; + + // Check if the fee projection would make the change amount negative + // (implying insufficient input amount). + let change_amount_before_fee = total_input - total_output; + if change_amount_before_fee < fee_estimate { + return Err(Error::from(Proto::Error::Error_insufficient_inputs)); + } + + if !proto.disable_change_output { + // The amount to be returned (if enabled). + let change_amount = change_amount_before_fee - fee_estimate; + + // Update the passed on protobuf structure by adding a change output + // (return to sender) + if change_amount != 0 { + proto.outputs.push(Proto::TxOut { + value: change_amount, + script_pubkey: proto.change_script_pubkey.clone(), + }); + } + } + + // Convert *updated* Protobuf structure to `bitcoin` crate native + // transaction. + let tx = convert_proto_to_tx(&proto)?; + + let mut cache = SighashCache::new(&tx); + + let mut sighashes: Vec<(Vec, ProtoSigningMethod, Proto::SighashType)> = vec![]; + + for (index, input) in proto.inputs.iter().enumerate() { + match input.signing_method { + // Use the legacy hashing mechanism (e.g. P2SH, P2PK, P2PKH). + ProtoSigningMethod::Legacy => { + let script_pubkey = Script::from_bytes(input.script_pubkey.as_ref()); + let sighash_type = if let SighashType::UseDefault = input.sighash_type { + EcdsaSighashType::All + } else { + EcdsaSighashType::from_consensus(input.sighash_type as u32) + }; + let sighash = + cache.legacy_signature_hash(index, script_pubkey, sighash_type.to_u32())?; + + sighashes.push(( + sighash.as_byte_array().to_vec(), + ProtoSigningMethod::Legacy, + input.sighash_type, + )); + }, + // Use the Segwit hashing mechanism (e.g. P2WSH, P2WPKH). + ProtoSigningMethod::Segwit => { + let script_pubkey = ScriptBuf::from_bytes(input.script_pubkey.to_vec()); + let sighash_type = if let SighashType::UseDefault = input.sighash_type { + EcdsaSighashType::All + } else { + EcdsaSighashType::from_consensus(input.sighash_type as u32) + }; + + let sighash = cache.segwit_signature_hash( + index, + script_pubkey.as_script(), + input.value, + sighash_type, + )?; + + sighashes.push(( + sighash.as_byte_array().to_vec(), + ProtoSigningMethod::Segwit, + input.sighash_type, + )); + }, + // Use the Taproot hashing mechanism (e.g. P2TR key-path/script-path) + ProtoSigningMethod::TaprootAll => { + let leaf_hash = if input.leaf_hash.is_empty() { + None + } else { + Some(( + TapLeafHash::from_slice(input.leaf_hash.as_ref()) + .map_err(|_| Error::from(Proto::Error::Error_invalid_leaf_hash))?, + // TODO: We might want to make this configurable?. + 0xFFFFFFFF, + )) + }; + + // Note that `input.sighash_type = 0` is handled by the underlying library. + let sighash_type = TapSighashType::from_consensus_u8(input.sighash_type as u8) + .map_err(|_| Error::from(Proto::Error::Error_invalid_sighash_type))?; + + let prevouts = proto + .inputs + .iter() + .map(|i| TxOut { + value: i.value, + script_pubkey: ScriptBuf::from_bytes(i.script_pubkey.to_vec()), + }) + .collect::>(); + + let sighash = cache.taproot_signature_hash( + index, + &Prevouts::All(&prevouts), + None, + leaf_hash, + sighash_type, + )?; + + sighashes.push(( + sighash.as_byte_array().to_vec(), + ProtoSigningMethod::TaprootAll, + input.sighash_type, + )); + }, + ProtoSigningMethod::TaprootOnePrevout => { + let leaf_hash = if input.leaf_hash.is_empty() { + None + } else { + Some(( + TapLeafHash::from_slice(input.leaf_hash.as_ref()) + .map_err(|_| Error::from(Proto::Error::Error_invalid_leaf_hash))?, + // TODO: We might want to make this configurable?. + 0xFFFFFFFF, + )) + }; + + // Note that `input.sighash_type = 0` is handled by the underlying library. + let sighash_type = TapSighashType::from_consensus_u8(input.sighash_type as u8) + .map_err(|_| Error::from(Proto::Error::Error_invalid_sighash_type))?; + + let prevouts = Prevouts::One( + index, + TxOut { + value: input.value, + script_pubkey: ScriptBuf::from_bytes(input.script_pubkey.to_vec()), + }, + ); + + let sighash = cache.taproot_signature_hash( + index, + &prevouts, + None, + leaf_hash, + sighash_type, + )?; + + sighashes.push(( + sighash.as_byte_array().to_vec(), + ProtoSigningMethod::TaprootOnePrevout, + input.sighash_type, + )); + }, + } + } + + let tx = cache.into_transaction(); + // The transaction identifier, which we represent in + // non-reversed/non-network order. + let txid: Vec = tx.txid().as_byte_array().iter().copied().rev().collect(); + + Ok(Proto::PreSigningOutput { + error: Proto::Error::OK, + txid: txid.into(), + sighashes: sighashes + .into_iter() + .map(|(sighash, method, sighash_type)| Proto::Sighash { + sighash: sighash.into(), + signing_method: method, + sighash_type, + }) + .collect(), + inputs: selected, + outputs: proto + .outputs + .into_iter() + .map(|output| Proto::TxOut { + value: output.value, + script_pubkey: output.script_pubkey.to_vec().into(), + }) + .collect(), + weight_estimate, + fee_estimate, + }) + } + + fn compile_impl( + proto: Proto::PreSerialization<'_>, + ) -> Result> { + let mut tx = Transaction { + version: proto.version, + lock_time: lock_time_from_proto(&proto.lock_time)?, + input: vec![], + output: vec![], + }; + + for txin in &proto.inputs { + let txid = Txid::from_slice(txin.txid.as_ref()) + .map_err(|_| Error::from(Proto::Error::Error_invalid_txid))?; + let vout = txin.vout; + let sequence = Sequence::from_consensus(txin.sequence); + let script_sig = ScriptBuf::from_bytes(txin.script_sig.to_vec()); + let witness = Witness::from_slice( + &txin + .witness_items + .iter() + .map(|s| s.as_ref()) + .collect::>(), + ); + + tx.input.push(TxIn { + previous_output: OutPoint { txid, vout }, + script_sig, + sequence, + witness, + }); + } + + for txout in &proto.outputs { + tx.output.push(TxOut { + value: txout.value, + script_pubkey: ScriptBuf::from_bytes(txout.script_pubkey.to_vec()), + }); + } + + // Encode the transaction. + let mut buffer = vec![]; + tx.consensus_encode(&mut buffer) + .map_err(|_| Error::from(Proto::Error::Error_failed_encoding))?; + + // The transaction identifier, which we represent in + // non-reversed/non-network order. + let txid: Vec = tx.txid().as_byte_array().iter().copied().rev().collect(); + + Ok(Proto::SerializedTransaction { + error: Proto::Error::OK, + encoded: buffer.into(), + txid: txid.into(), + weight: tx.weight().to_wu(), + fee: tx.weight().to_vbytes_ceil() * proto.weight_base, + }) + } +} + +fn convert_proto_to_tx<'a>(proto: &'a Proto::SigningInput<'a>) -> Result { + let mut tx = Transaction { + version: proto.version, + lock_time: lock_time_from_proto(&proto.lock_time)?, + input: vec![], + output: vec![], + }; + + for txin in &proto.inputs { + let txid = Txid::from_slice(txin.txid.as_ref()) + .map_err(|_| Error::from(Proto::Error::Error_invalid_txid))?; + + let vout = txin.vout; + + tx.input.push(TxIn { + previous_output: OutPoint { txid, vout }, + script_sig: ScriptBuf::new(), + sequence: Sequence(txin.sequence), + witness: Witness::new(), + }); + } + + for txout in &proto.outputs { + tx.output.push(TxOut { + value: txout.value, + script_pubkey: ScriptBuf::from_bytes(txout.script_pubkey.to_vec()), + }); + } + + Ok(tx) +} + +// Convenience function to retreive the lock time. If none is provided, the +// default lock time is used (immediately spendable). +fn lock_time_from_proto(proto: &Option) -> Result { + let lock_time = if let Some(lock_time) = proto { + match lock_time.variant { + ProtoLockTimeVariant::blocks(block) => LockTime::Blocks( + Height::from_consensus(block) + .map_err(|_| Error::from(Proto::Error::Error_invalid_lock_time))?, + ), + ProtoLockTimeVariant::seconds(secs) => LockTime::Seconds( + Time::from_consensus(secs) + .map_err(|_| Error::from(Proto::Error::Error_invalid_lock_time))?, + ), + ProtoLockTimeVariant::None => LockTime::Blocks( + Height::from_consensus(0) + .map_err(|_| Error::from(Proto::Error::Error_invalid_lock_time))?, + ), + } + } else { + LockTime::Blocks( + Height::from_consensus(0) + .map_err(|_| Error::from(Proto::Error::Error_invalid_lock_time))?, + ) + }; + + Ok(lock_time) +} diff --git a/rust/tw_utxo/src/lib.rs b/rust/tw_utxo/src/lib.rs new file mode 100644 index 00000000000..3724b9ef75c --- /dev/null +++ b/rust/tw_utxo/src/lib.rs @@ -0,0 +1,26 @@ +use tw_proto::Utxo::Proto; + +pub mod compiler; + +pub type Result = std::result::Result; + +#[derive(Debug)] +pub struct Error(Proto::Error); + +impl From for Error { + fn from(value: Proto::Error) -> Self { + Error(value) + } +} + +impl From for Error { + fn from(_value: bitcoin::sighash::Error) -> Self { + Error(Proto::Error::Error_sighash_failed) + } +} + +impl From for Proto::Error { + fn from(value: Error) -> Self { + value.0 + } +} diff --git a/rust/tw_utxo/tests/common.rs b/rust/tw_utxo/tests/common.rs new file mode 100644 index 00000000000..c8b65535cab --- /dev/null +++ b/rust/tw_utxo/tests/common.rs @@ -0,0 +1,23 @@ +#![allow(dead_code)] + +use bitcoin::key::UntweakedPublicKey; +use bitcoin::{PubkeyHash, PublicKey, WPubkeyHash}; +use secp256k1::{hashes::Hash, XOnlyPublicKey}; +use tw_encoding::hex; + +pub fn pubkey_hash_from_hex(hex: &str) -> PubkeyHash { + PubkeyHash::from_byte_array(hex::decode(hex).unwrap().try_into().unwrap()) +} + +pub fn witness_pubkey_hash(hex: &str) -> WPubkeyHash { + WPubkeyHash::from_byte_array(hex::decode(hex).unwrap().try_into().unwrap()) +} + +pub fn untweaked_pubkey(hex: &str) -> UntweakedPublicKey { + let pubkey = PublicKey::from_slice(&hex::decode(hex).unwrap()).unwrap(); + XOnlyPublicKey::from(pubkey.inner) +} + +pub fn txid_rev(hex: &str) -> Vec { + hex::decode(hex).unwrap().into_iter().rev().collect() +} diff --git a/rust/tw_utxo/tests/input_selection.rs b/rust/tw_utxo/tests/input_selection.rs new file mode 100644 index 00000000000..a8b00d87285 --- /dev/null +++ b/rust/tw_utxo/tests/input_selection.rs @@ -0,0 +1,597 @@ +mod common; +use common::{pubkey_hash_from_hex, txid_rev}; + +use bitcoin::ScriptBuf; +use tw_proto::Utxo::Proto; +use tw_utxo::compiler::{Compiler, StandardBitcoinContext}; + +const WEIGHT_BASE: u64 = 2; + +// Convenience function, creates the change output script. +fn change_output() -> ScriptBuf { + let pubkey_hash = pubkey_hash_from_hex("aabbccddeeff00112233445566778899aabbccdd"); + ScriptBuf::new_p2pkh(&pubkey_hash) +} + +#[test] +fn input_selector_all() { + // Reusing the txid is fine here, although in production this would mark the transaction invalid. + let txid = txid_rev("1e1cdc48aa990d7e154a161d5b5f1cad737742e97d2712ab188027bb42e6e47b"); + + let tx1 = Proto::TxIn { + txid: txid.as_slice().into(), + value: 1_000, + sequence: u32::MAX, + ..Default::default() + }; + let tx2 = Proto::TxIn { + txid: txid.as_slice().into(), + value: 2_000, + sequence: u32::MAX, + ..Default::default() + }; + let tx3 = Proto::TxIn { + txid: txid.as_slice().into(), + value: 3_000, + sequence: u32::MAX, + ..Default::default() + }; + + let out1 = Proto::TxOut { + value: 500, + script_pubkey: Default::default(), + }; + + // Generate prehashes without change output. + let signing = Proto::SigningInput { + version: 2, + lock_time: Default::default(), + inputs: vec![tx1.clone(), tx2.clone(), tx3.clone()], + outputs: vec![out1.clone()], + // Explicitly select all inputs. + input_selector: Proto::InputSelector::UseAll, + weight_base: WEIGHT_BASE, + change_script_pubkey: Default::default(), + // DISABLE change output. + disable_change_output: true, + }; + + let output = Compiler::::preimage_hashes(signing); + assert_eq!(output.error, Proto::Error::OK); + assert_eq!(output.sighashes.len(), 3); + + // All inputs are used as mandated by the `input_selector`. Technically only + // one input is needed. + assert_eq!(output.inputs.len(), 3); + assert_eq!(output.inputs[0], tx1); + assert_eq!(output.inputs[1], tx2); + assert_eq!(output.inputs[2], tx3); + + assert_eq!(output.outputs.len(), 1); + assert_eq!(output.outputs[0], out1); + + // Generate prehashes WITH change output. + let change_script = change_output(); + let signing = Proto::SigningInput { + version: 2, + lock_time: Default::default(), + inputs: vec![tx1.clone(), tx2.clone(), tx3.clone()], + outputs: vec![out1.clone()], + // Explicitly select all inputs. + input_selector: Proto::InputSelector::UseAll, + weight_base: WEIGHT_BASE, + change_script_pubkey: change_script.as_bytes().into(), + // ENABLE change output. + disable_change_output: false, + }; + + let output = Compiler::::preimage_hashes(signing); + assert_eq!(output.error, Proto::Error::OK); + assert_eq!(output.sighashes.len(), 3); + assert_eq!(output.weight_estimate, 594); + assert_eq!(output.fee_estimate, (594 + 3) / 4 * WEIGHT_BASE); + + assert_eq!(output.inputs.len(), 3); + assert_eq!(output.inputs[0], tx1); + assert_eq!(output.inputs[1], tx2); + assert_eq!(output.inputs[2], tx3); + + // All inputs: 6_000, all outputs: 500 + let change_out = Proto::TxOut { + value: 6_000 - 500 - output.fee_estimate, + script_pubkey: change_script.as_bytes().into(), + }; + + assert_eq!(output.outputs.len(), 2); + assert_eq!(output.outputs[0], out1); + assert_eq!(output.outputs[1], change_out); +} + +#[test] +fn input_selector_all_insufficient_inputs() { + // Reusing the txid is fine here, although in production this would mark the transaction invalid. + let txid = txid_rev("1e1cdc48aa990d7e154a161d5b5f1cad737742e97d2712ab188027bb42e6e47b"); + + let tx1 = Proto::TxIn { + txid: txid.as_slice().into(), + value: 1_000, + sequence: u32::MAX, + ..Default::default() + }; + let tx2 = Proto::TxIn { + txid: txid.as_slice().into(), + value: 2_000, + sequence: u32::MAX, + ..Default::default() + }; + let tx3 = Proto::TxIn { + txid: txid.as_slice().into(), + value: 3_000, + sequence: u32::MAX, + ..Default::default() + }; + + let out1 = Proto::TxOut { + value: 6_000, + script_pubkey: Default::default(), + }; + + // Generate prehashes without change output. + let signing = Proto::SigningInput { + version: 2, + lock_time: Default::default(), + inputs: vec![tx1.clone(), tx2.clone(), tx3.clone()], + outputs: vec![out1.clone()], + // Explicitly select all inputs. + input_selector: Proto::InputSelector::UseAll, + weight_base: WEIGHT_BASE, + change_script_pubkey: Default::default(), + // DISABLE change output. + disable_change_output: true, + }; + + let output = Compiler::::preimage_hashes(signing); + // While the input covers all outputs, it does not + // cover the projected fee. + assert_eq!(output.error, Proto::Error::Error_insufficient_inputs); + assert_eq!(output.sighashes.len(), 0); + assert_eq!(output.inputs.len(), 0); + assert_eq!(output.outputs.len(), 0); + + // Generate prehashes WITH change output (same outcome). + let change_script = change_output(); + let signing = Proto::SigningInput { + version: 2, + lock_time: Default::default(), + inputs: vec![tx1.clone(), tx2.clone(), tx3.clone()], + outputs: vec![out1.clone()], + // Explicitly select all inputs. + input_selector: Proto::InputSelector::UseAll, + weight_base: WEIGHT_BASE, + change_script_pubkey: change_script.as_bytes().into(), + // ENABLE change output. + disable_change_output: false, + }; + + let output = Compiler::::preimage_hashes(signing); + assert_eq!(output.error, Proto::Error::Error_insufficient_inputs); + assert_eq!(output.sighashes.len(), 0); + assert_eq!(output.inputs.len(), 0); + assert_eq!(output.outputs.len(), 0); +} + +#[test] +fn input_selector_one_input_required() { + // Reusing the txid is fine here, although in production this would mark the transaction invalid. + let txid = txid_rev("1e1cdc48aa990d7e154a161d5b5f1cad737742e97d2712ab188027bb42e6e47b"); + + let tx1 = Proto::TxIn { + txid: txid.as_slice().into(), + value: 4_000, + sequence: u32::MAX, + ..Default::default() + }; + let tx2 = Proto::TxIn { + txid: txid.as_slice().into(), + value: 4_000, + sequence: u32::MAX, + ..Default::default() + }; + let tx3 = Proto::TxIn { + txid: txid.as_slice().into(), + value: 4_000, + sequence: u32::MAX, + ..Default::default() + }; + + let out1 = Proto::TxOut { + value: 500, + script_pubkey: Default::default(), + }; + let out2 = Proto::TxOut { + value: 500, + script_pubkey: Default::default(), + }; + + // Generate sighashes without change output. + let signing = Proto::SigningInput { + version: 2, + lock_time: Default::default(), + inputs: vec![tx1.clone(), tx2.clone(), tx3.clone()], + outputs: vec![out1.clone(), out2.clone()], + input_selector: Proto::InputSelector::SelectInOrder, + weight_base: WEIGHT_BASE, + change_script_pubkey: Default::default(), + // DISABLE change output. + disable_change_output: true, + }; + + let output = Compiler::::preimage_hashes(signing); + assert_eq!(output.error, Proto::Error::OK); + assert_eq!(output.sighashes.len(), 1); + + // One inputs covers the full output. + assert_eq!(output.inputs.len(), 1); + assert_eq!(output.inputs[0], tx1); + + assert_eq!(output.outputs.len(), 2); + assert_eq!(output.outputs[0], out1); + assert_eq!(output.outputs[1], out2); + + // Generate sighashes WITH change output. + let change_script = change_output(); + let signing = Proto::SigningInput { + version: 2, + lock_time: Default::default(), + inputs: vec![tx1.clone(), tx2.clone(), tx3.clone()], + outputs: vec![out1.clone(), out2.clone()], + input_selector: Proto::InputSelector::SelectInOrder, + weight_base: WEIGHT_BASE, + change_script_pubkey: change_script.as_bytes().into(), + // ENABLE change output. + disable_change_output: false, + }; + + let output = Compiler::::preimage_hashes(signing); + assert_eq!(output.error, Proto::Error::OK); + assert_eq!(output.sighashes.len(), 1); + assert_eq!(output.weight_estimate, 302); + assert_eq!(output.fee_estimate, (302 + 3) / 4 * WEIGHT_BASE); + + // One inputs covers the full output. + assert_eq!(output.inputs.len(), 1); + assert_eq!(output.inputs[0], tx1); + + // All inputs: 4_000, all outputs: 2_000 + let change_out = Proto::TxOut { + value: 4_000 - 1_000 - output.fee_estimate, + script_pubkey: change_script.as_bytes().into(), + }; + + assert_eq!(output.outputs.len(), 3); + assert_eq!(output.outputs[0], out1); + assert_eq!(output.outputs[1], out2); + assert_eq!(output.outputs[2], change_out); +} + +#[test] +fn input_selector_two_inputs_required() { + // Reusing the txid is fine here, although in production this would mark the transaction invalid. + let txid = txid_rev("1e1cdc48aa990d7e154a161d5b5f1cad737742e97d2712ab188027bb42e6e47b"); + + let tx1 = Proto::TxIn { + txid: txid.as_slice().into(), + value: 1_000, + sequence: u32::MAX, + ..Default::default() + }; + let tx2 = Proto::TxIn { + txid: txid.as_slice().into(), + value: 3_000, + sequence: u32::MAX, + ..Default::default() + }; + let tx3 = Proto::TxIn { + txid: txid.as_slice().into(), + value: 4_000, + sequence: u32::MAX, + ..Default::default() + }; + + let out1 = Proto::TxOut { + value: 1_000, + script_pubkey: Default::default(), + }; + let out2 = Proto::TxOut { + value: 1_000, + script_pubkey: Default::default(), + }; + + // Generate sighashes without change output. + let signing = Proto::SigningInput { + version: 2, + lock_time: Default::default(), + inputs: vec![tx1.clone(), tx2.clone(), tx3.clone()], + outputs: vec![out1.clone(), out2.clone()], + // We only select the necessary value of inputs to cover the output + // value. + input_selector: Proto::InputSelector::SelectInOrder, + weight_base: WEIGHT_BASE, + change_script_pubkey: Default::default(), + // DISABLE change output. + disable_change_output: true, + }; + + let output = Compiler::::preimage_hashes(signing); + assert_eq!(output.error, Proto::Error::OK); + //assert_eq!(output.sighashes.len(), 2); + + // Only two inputs are needed to cover outputs. + assert_eq!(output.inputs.len(), 2); + assert_eq!(output.inputs[0], tx1); + assert_eq!(output.inputs[1], tx2); + + assert_eq!(output.outputs.len(), 2); + assert_eq!(output.outputs[0], out1); + assert_eq!(output.outputs[1], out2); + + // Generate sighashes WITH change output. + let change_script = change_output(); + let signing = Proto::SigningInput { + version: 2, + lock_time: Default::default(), + inputs: vec![tx1.clone(), tx2.clone(), tx3.clone()], + outputs: vec![out1.clone(), out2.clone()], + // We only select the necessary value of inputs to cover the output + // value. + input_selector: Proto::InputSelector::SelectInOrder, + weight_base: WEIGHT_BASE, + change_script_pubkey: change_script.as_bytes().into(), + // ENABLE change output. + disable_change_output: false, + }; + + let output = Compiler::::preimage_hashes(signing); + assert_eq!(output.error, Proto::Error::OK); + assert_eq!(output.sighashes.len(), 2); + assert_eq!(output.weight_estimate, 466); + assert_eq!(output.fee_estimate, (466 + 3) / 4 * WEIGHT_BASE); + + // Only two inputs are needed to cover outputs. + assert_eq!(output.inputs.len(), 2); + assert_eq!(output.inputs[0], tx1); + assert_eq!(output.inputs[1], tx2); + + // All inputs: 4_000, all outputs: 2_000 + let change_out = Proto::TxOut { + value: 4_000 - 2_000 - output.fee_estimate, + script_pubkey: change_script.as_bytes().into(), + }; + + assert_eq!(output.outputs.len(), 3); + assert_eq!(output.outputs[0], out1); + assert_eq!(output.outputs[1], out2); + assert_eq!(output.outputs[2], change_out); +} + +#[test] +fn input_selector_one_input_cannot_cover_fees() { + // Reusing the txid is fine here, although in production this would mark the transaction invalid. + let txid = txid_rev("1e1cdc48aa990d7e154a161d5b5f1cad737742e97d2712ab188027bb42e6e47b"); + + let tx1 = Proto::TxIn { + txid: txid.as_slice().into(), + value: 2_000, + sequence: u32::MAX, + ..Default::default() + }; + + let out1 = Proto::TxOut { + value: 1_000, + script_pubkey: Default::default(), + }; + let out2 = Proto::TxOut { + value: 1_000, + script_pubkey: Default::default(), + }; + + // Generate sighashes without change output. + let signing = Proto::SigningInput { + version: 2, + lock_time: Default::default(), + inputs: vec![tx1.clone()], + outputs: vec![out1.clone(), out2.clone()], + input_selector: Proto::InputSelector::SelectInOrder, + weight_base: WEIGHT_BASE, + change_script_pubkey: Default::default(), + // DISABLE change output. + disable_change_output: true, + }; + + let output = Compiler::::preimage_hashes(signing); + // While the input covers all outputs, it does not + // cover the projected fee. + assert_eq!(output.error, Proto::Error::Error_insufficient_inputs); + assert_eq!(output.weight_estimate, 0); + assert_eq!(output.fee_estimate, 0); + assert_eq!(output.sighashes.len(), 0); + assert_eq!(output.inputs.len(), 0); + assert_eq!(output.outputs.len(), 0); + + // Generate sighashes WITH change output (same outcome). + let change_script = change_output(); + let signing = Proto::SigningInput { + version: 2, + lock_time: Default::default(), + inputs: vec![tx1.clone()], + outputs: vec![out1.clone(), out2.clone()], + input_selector: Proto::InputSelector::SelectInOrder, + weight_base: WEIGHT_BASE, + change_script_pubkey: change_script.as_bytes().into(), + // ENABLE change output. + disable_change_output: false, + }; + + let output = Compiler::::preimage_hashes(signing); + assert_eq!(output.error, Proto::Error::Error_insufficient_inputs); + assert_eq!(output.sighashes.len(), 0); + assert_eq!(output.weight_estimate, 0); + assert_eq!(output.fee_estimate, 0); + assert_eq!(output.inputs.len(), 0); + assert_eq!(output.outputs.len(), 0); +} + +#[test] +fn input_selector_exact_balance_no_change() { + // Reusing the txid is fine here, although in production this would mark the transaction invalid. + let txid = txid_rev("1e1cdc48aa990d7e154a161d5b5f1cad737742e97d2712ab188027bb42e6e47b"); + + let tx1 = Proto::TxIn { + txid: txid.as_slice().into(), + // Covers the exact output value + projected fee. + value: 2_000 + (302 + 3) / 4 * WEIGHT_BASE, + sequence: u32::MAX, + ..Default::default() + }; + + let out1 = Proto::TxOut { + value: 1_000, + script_pubkey: Default::default(), + }; + let out2 = Proto::TxOut { + value: 1_000, + script_pubkey: Default::default(), + }; + + let change_script = change_output(); + let signing = Proto::SigningInput { + version: 2, + lock_time: Default::default(), + inputs: vec![tx1.clone()], + outputs: vec![out1.clone(), out2.clone()], + input_selector: Proto::InputSelector::SelectInOrder, + weight_base: WEIGHT_BASE, + change_script_pubkey: change_script.as_bytes().into(), + // ENABLE change output. + disable_change_output: false, + }; + + let output = Compiler::::preimage_hashes(signing); + assert_eq!(output.error, Proto::Error::OK); + assert_eq!(output.sighashes.len(), 1); + assert_eq!(output.weight_estimate, 302); + assert_eq!(output.fee_estimate, (302 + 3) / 4 * WEIGHT_BASE); + + // One inputs covers the full output. + assert_eq!(output.inputs.len(), 1); + assert_eq!(output.inputs[0], tx1); + + // NO change output + assert_eq!(output.outputs.len(), 2); + assert_eq!(output.outputs[0], out1); + assert_eq!(output.outputs[1], out2); +} + +#[test] +fn input_selector_empty_script_bufs() { + // Reusing the txid is fine here, although in production this would mark the transaction invalid. + let txid = txid_rev("1e1cdc48aa990d7e154a161d5b5f1cad737742e97d2712ab188027bb42e6e47b"); + + let tx1 = Proto::TxIn { + txid: txid.as_slice().into(), + value: 4_000, + sequence: u32::MAX, + ..Default::default() + }; + + let out1 = Proto::TxOut { + value: 1_000, + script_pubkey: Default::default(), + }; + let out2 = Proto::TxOut { + value: 1_000, + script_pubkey: Default::default(), + }; + + let signing = Proto::SigningInput { + version: 2, + lock_time: Default::default(), + inputs: vec![tx1.clone()], + outputs: vec![out1.clone(), out2.clone()], + input_selector: Proto::InputSelector::SelectInOrder, + weight_base: WEIGHT_BASE, + // NO change script_pubkey specified, results in an error. + change_script_pubkey: Default::default(), + // ENABLE change output. + disable_change_output: false, + }; + + let output = Compiler::::preimage_hashes(signing); + assert_eq!( + output.error, + Proto::Error::Error_missing_change_script_pubkey + ); + assert_eq!(output.sighashes.len(), 0); + assert_eq!(output.weight_estimate, 0); + assert_eq!(output.fee_estimate, 0); + assert_eq!(output.inputs.len(), 0); + assert_eq!(output.outputs.len(), 0); +} + +#[test] +fn input_selector_select_ascending() { + // Reusing the txid is fine here, although in production this would mark the transaction invalid. + let txid = txid_rev("1e1cdc48aa990d7e154a161d5b5f1cad737742e97d2712ab188027bb42e6e47b"); + + let tx1 = Proto::TxIn { + txid: txid.as_slice().into(), + value: 8_000, + sequence: u32::MAX, + ..Default::default() + }; + + let tx2 = Proto::TxIn { + txid: txid.as_slice().into(), + value: 4_000, + sequence: u32::MAX, + ..Default::default() + }; + + let tx3 = Proto::TxIn { + txid: txid.as_slice().into(), + value: 2_000, + sequence: u32::MAX, + ..Default::default() + }; + + let out1 = Proto::TxOut { + value: 3_000, + script_pubkey: Default::default(), + }; + + let change_script = change_output(); + let signing = Proto::SigningInput { + version: 2, + lock_time: Default::default(), + inputs: vec![tx1.clone(), tx2.clone(), tx3.clone()], + outputs: vec![out1.clone()], + // Select in ASCENDING order: + input_selector: Proto::InputSelector::SelectAscending, + weight_base: WEIGHT_BASE, + change_script_pubkey: change_script.as_bytes().into(), + // ENABLE change output. + disable_change_output: false, + }; + + let output = Compiler::::preimage_hashes(signing); + assert_eq!(output.error, Proto::Error::OK); + + // Two inputs where selected (in ASCENDING order). + assert_eq!(output.inputs.len(), 2); + assert_eq!(output.inputs[0], tx3); + assert_eq!(output.inputs[1], tx2); + + // Two outputs; target and change. + assert_eq!(output.outputs.len(), 2); + assert_eq!(output.outputs[0], out1); +} diff --git a/rust/tw_utxo/tests/p2pkh.rs b/rust/tw_utxo/tests/p2pkh.rs new file mode 100644 index 00000000000..99fe20e1cc0 --- /dev/null +++ b/rust/tw_utxo/tests/p2pkh.rs @@ -0,0 +1,53 @@ +mod common; +use common::{pubkey_hash_from_hex, txid_rev}; + +use bitcoin::ScriptBuf; +use tw_encoding::hex; +use tw_proto::Utxo::Proto; +use tw_utxo::compiler::{Compiler, StandardBitcoinContext}; + +#[test] +fn sighash_input_p2pkh_output_p2pkh() { + let pubkey_hash = pubkey_hash_from_hex("e4c1ea86373d554b8f4efff2cfb0001ea19124d2"); + let input_script_pubkey = ScriptBuf::new_p2pkh(&pubkey_hash); + + let pubkey_hash = pubkey_hash_from_hex("5eaaa4f458f9158f86afcba08dd7448d27045e3d"); + let output_script_pubkey = ScriptBuf::new_p2pkh(&pubkey_hash); + + let txid = txid_rev("1e1cdc48aa990d7e154a161d5b5f1cad737742e97d2712ab188027bb42e6e47b"); + + let signing = Proto::SigningInput { + version: 2, + lock_time: Default::default(), + inputs: vec![Proto::TxIn { + txid: txid.into(), + vout: 0, + // Amount is not part of sighash for `Legacy`. + value: u64::MAX, + sequence: u32::MAX, + script_pubkey: input_script_pubkey.as_bytes().into(), + sighash_type: Proto::SighashType::All, + signing_method: Proto::SigningMethod::Legacy, + weight_estimate: 1, + leaf_hash: Default::default(), + }], + outputs: vec![Proto::TxOut { + value: 50 * 100_000_000 - 1_000_000, + script_pubkey: output_script_pubkey.as_bytes().into(), + }], + input_selector: Proto::InputSelector::UseAll, + weight_base: 1, + change_script_pubkey: Default::default(), + disable_change_output: true, + }; + + let output = Compiler::::preimage_hashes(signing); + assert_eq!(output.error, Proto::Error::OK); + + let hashes = output.sighashes; + assert_eq!(hashes.len(), 1); + assert_eq!( + hex::encode(hashes[0].sighash.as_ref(), false), + "6a0e072da66b141fdb448323d54765cafcaf084a06d2fa13c8aed0c694e50d18" + ); +} diff --git a/rust/tw_utxo/tests/p2tr.rs b/rust/tw_utxo/tests/p2tr.rs new file mode 100644 index 00000000000..f8521872f33 --- /dev/null +++ b/rust/tw_utxo/tests/p2tr.rs @@ -0,0 +1,56 @@ +mod common; +use common::{pubkey_hash_from_hex, txid_rev, untweaked_pubkey}; + +use bitcoin::ScriptBuf; +use tw_encoding::hex; +use tw_proto::Utxo::Proto; +use tw_utxo::compiler::{Compiler, StandardBitcoinContext}; + +#[test] +fn sighash_input_p2pkh_output_p2tr_key_spend() { + let pubkey_hash = pubkey_hash_from_hex("a0cd6d6e2f9804351ba4b722b708bc2fd3229a5a"); + let input_script_pubkey = ScriptBuf::new_p2pkh(&pubkey_hash); + + let untweaked_pubkey = + untweaked_pubkey("02c0938cf377023dfde55e9c96b3cff4ca8894fb6b5d2009006bd43c0bff69cac9"); + let output_script_pubkey = + // Merkle root of `None` is interpreted as P2TR key-spend. + ScriptBuf::new_v1_p2tr(&secp256k1::Secp256k1::new(), untweaked_pubkey, None); + + let txid = txid_rev("c50563913e5a838f937c94232f5a8fc74e58b629fae41dfdffcc9a70f833b53a"); + + let signing = Proto::SigningInput { + version: 2, + lock_time: Default::default(), + inputs: vec![Proto::TxIn { + txid: txid.into(), + vout: 0, + // Amount is not part of sighash for `Legacy`. + value: u64::MAX, + sequence: u32::MAX, + script_pubkey: input_script_pubkey.as_bytes().into(), + sighash_type: Proto::SighashType::All, + signing_method: Proto::SigningMethod::Legacy, + weight_estimate: 1, + leaf_hash: Default::default(), + }], + outputs: vec![Proto::TxOut { + value: 50 * 100_000_000 - 1_000_000, + script_pubkey: output_script_pubkey.as_bytes().into(), + }], + input_selector: Proto::InputSelector::UseAll, + weight_base: 1, + change_script_pubkey: Default::default(), + disable_change_output: true, + }; + + let output = Compiler::::preimage_hashes(signing); + assert_eq!(output.error, Proto::Error::OK); + + let hashes = output.sighashes; + assert_eq!(hashes.len(), 1); + assert_eq!( + hex::encode(hashes[0].sighash.as_ref(), false), + "c914fd08efdcc7f8007c75c39ab47e1ee736a6ce1e6363250fe88cda8fca04d1" + ); +} diff --git a/rust/tw_utxo/tests/p2wpkh.rs b/rust/tw_utxo/tests/p2wpkh.rs new file mode 100644 index 00000000000..3ad47370695 --- /dev/null +++ b/rust/tw_utxo/tests/p2wpkh.rs @@ -0,0 +1,100 @@ +mod common; +use common::{pubkey_hash_from_hex, txid_rev, witness_pubkey_hash}; + +use bitcoin::ScriptBuf; +use tw_encoding::hex; +use tw_proto::Utxo::Proto; +use tw_utxo::compiler::{Compiler, StandardBitcoinContext}; + +#[test] +fn sighash_input_p2pkh_output_p2wpkh() { + let pubkey_hash = pubkey_hash_from_hex("60cda7b50f14c152d7401c28ae773c698db92373"); + let input_script_pubkey = ScriptBuf::new_p2pkh(&pubkey_hash); + + let wpubkey_hash = witness_pubkey_hash("0d0e1cec6c2babe8badde5e9b3dea667da90036d"); + let output_script_pubkey = ScriptBuf::new_v0_p2wpkh(&wpubkey_hash); + + let txid = txid_rev("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911"); + + let signing = Proto::SigningInput { + version: 2, + lock_time: Default::default(), + inputs: vec![Proto::TxIn { + txid: txid.into(), + vout: 0, + // Amount is not part of sighash for `Legacy`. + value: u64::MAX, + sequence: u32::MAX, + script_pubkey: input_script_pubkey.as_bytes().into(), + sighash_type: Proto::SighashType::All, + signing_method: Proto::SigningMethod::Legacy, + weight_estimate: 1, + leaf_hash: Default::default(), + }], + outputs: vec![Proto::TxOut { + value: 50 * 100_000_000 - 1_000_000, + script_pubkey: output_script_pubkey.as_bytes().into(), + }], + input_selector: Proto::InputSelector::UseAll, + weight_base: 1, + change_script_pubkey: Default::default(), + disable_change_output: true, + }; + + let output = Compiler::::preimage_hashes(signing); + assert_eq!(output.error, Proto::Error::OK); + + let hashes = output.sighashes; + assert_eq!(hashes.len(), 1); + assert_eq!( + hex::encode(hashes[0].sighash.as_ref(), false), + "c4963ecd6c08be4c9dd66416349084a5b54318b3802370451d580210bc883463" + ); +} + +#[test] +fn sighash_input_p2wpkh_output_p2wpkh() { + let wpubkey_hash = witness_pubkey_hash("0d0e1cec6c2babe8badde5e9b3dea667da90036d"); + let input_script_pubkey = ScriptBuf::new_v0_p2wpkh(&wpubkey_hash) + .p2wpkh_script_code() + .unwrap(); + + let wpubkey_hash = witness_pubkey_hash("60cda7b50f14c152d7401c28ae773c698db92373"); + let output_script_pubkey = ScriptBuf::new_v0_p2wpkh(&wpubkey_hash); + + let txid = txid_rev("858e450a1da44397bde05ca2f8a78510d74c623cc2f69736a8b3fbfadc161f6e"); + + let signing = Proto::SigningInput { + version: 2, + lock_time: Default::default(), + inputs: vec![Proto::TxIn { + txid: txid.into(), + vout: 0, + value: 50 * 100_000_000 - 1_000_000, + sequence: u32::MAX, + script_pubkey: input_script_pubkey.as_bytes().into(), + sighash_type: Proto::SighashType::All, + signing_method: Proto::SigningMethod::Segwit, + weight_estimate: 1, + leaf_hash: Default::default(), + }], + outputs: vec![Proto::TxOut { + value: 50 * 100_000_000 - 1_000_000 * 2, + script_pubkey: output_script_pubkey.as_bytes().into(), + }], + input_selector: Proto::InputSelector::UseAll, + weight_base: 1, + change_script_pubkey: Default::default(), + disable_change_output: true, + }; + + let output = Compiler::::preimage_hashes(signing); + assert_eq!(output.error, Proto::Error::OK); + + let hashes = output.sighashes; + assert_eq!(hashes.len(), 1); + assert_eq!( + hex::encode(hashes[0].sighash.as_ref(), false), + "6900ebbef74c938ec2310df10cd520b5e7c82c0fe1bb68c62c8fae7bf54e2092" + ); +} diff --git a/rust/wallet_core_rs/Cargo.toml b/rust/wallet_core_rs/Cargo.toml index 87386edb3d6..90586221f9a 100644 --- a/rust/wallet_core_rs/Cargo.toml +++ b/rust/wallet_core_rs/Cargo.toml @@ -5,14 +5,30 @@ edition = "2021" [lib] name = "wallet_core_rs" -crate-type = ["staticlib"] # Creates static lib +crate-type = ["staticlib", "rlib"] # Creates static lib + +[features] +default = ["bitcoin-legacy", "ethereum-abi", "ethereum-rlp"] +bitcoin-legacy = [] +ethereum-abi = [] +ethereum-rlp = [] [dependencies] +tw_any_coin = { path = "../tw_any_coin" } +tw_bitcoin = { path = "../tw_bitcoin" } +tw_coin_entry = { path = "../tw_coin_entry", features = ["test-utils"] } +tw_coin_registry = { path = "../tw_coin_registry" } tw_encoding = { path = "../tw_encoding" } +tw_ethereum = { path = "../tw_ethereum" } tw_hash = { path = "../tw_hash" } tw_keypair = { path = "../tw_keypair" } tw_memory = { path = "../tw_memory" } tw_move_parser = { path = "../tw_move_parser" } +tw_misc = { path = "../tw_misc" } tw_proto = { path = "../tw_proto" } -tw_bitcoin = { path = "../tw_bitcoin" } -tw_starknet = { path = "../tw_starknet" } + +[dev-dependencies] +serde_json = "1.0.96" +tw_any_coin = { path = "../tw_any_coin", features = ["test-utils"] } +tw_memory = { path = "../tw_memory", features = ["test-utils"] } +tw_number = { path = "../tw_number", features = ["helpers"] } diff --git a/rust/wallet_core_rs/cbindgen.toml b/rust/wallet_core_rs/cbindgen.toml index f2a1fc06b70..fcab0505411 100644 --- a/rust/wallet_core_rs/cbindgen.toml +++ b/rust/wallet_core_rs/cbindgen.toml @@ -6,5 +6,27 @@ namespaces = ["TW", "Rust"] [parse] parse_deps = true -extra_bindings = ["tw_bitcoin", "tw_memory", "tw_encoding", "tw_hash", "tw_keypair", "tw_move_parser", "tw_proto", "tw_starknet"] -include = ["tw_bitcoin", "tw_memory", "tw_encoding", "tw_hash", "tw_keypair", "tw_move_parser", "tw_proto", "tw_starknet"] +extra_bindings = [ + "tw_any_coin", + "tw_bitcoin", + "tw_coin_registry", + "tw_encoding", + "tw_ethereum", + "tw_hash", + "tw_keypair", + "tw_memory", + "tw_move_parser", + "tw_proto" +] +include = [ + "tw_any_coin", + "tw_bitcoin", + "tw_coin_registry", + "tw_encoding", + "tw_ethereum", + "tw_hash", + "tw_keypair", + "tw_memory", + "tw_move_parser", + "tw_proto" +] diff --git a/rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs b/rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs new file mode 100644 index 00000000000..5a172e2c648 --- /dev/null +++ b/rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs @@ -0,0 +1,308 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#![allow(clippy::missing_safety_doc)] + +use std::ffi::{c_char, CStr}; +use tw_bitcoin::aliases::*; +use tw_bitcoin::native::consensus::Decodable; +use tw_bitcoin::native::{PublicKey, Transaction}; +use tw_memory::ffi::c_byte_array::CByteArray; +use tw_memory::ffi::c_byte_array_ref::CByteArrayRef; +use tw_memory::ffi::c_result::CUInt64Result; +use tw_misc::try_or_else; +use tw_proto::Bitcoin::Proto as LegacyProto; +use tw_proto::BitcoinV2::Proto; +use tw_proto::Common::Proto as CommonProto; + +// NOTE: The tests for those APIs can be found in `tw_bitcoin`. + +#[no_mangle] +#[deprecated] +// Builds the P2PKH scriptPubkey. +pub unsafe extern "C" fn tw_bitcoin_legacy_build_p2pkh_script( + _satoshis: i64, + pubkey: *const u8, + pubkey_len: usize, +) -> CByteArray { + // Convert Recipient + let slice = try_or_else!( + CByteArrayRef::new(pubkey, pubkey_len).as_slice(), + CByteArray::null + ); + let recipient = try_or_else!(PublicKey::from_slice(slice), CByteArray::null); + + let output = Proto::Output { + value: _satoshis as u64, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2pkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(recipient.to_bytes().into()), + }), + }), + }; + + let res = try_or_else!( + tw_bitcoin::modules::transactions::OutputBuilder::utxo_from_proto(&output), + CByteArray::null + ); + + // Prepare and serialize protobuf structure. + let proto = LegacyProto::TransactionOutput { + value: res.value as i64, + script: res.script_pubkey, + spendingScript: Default::default(), + }; + + let serialized = tw_proto::serialize(&proto).expect("failed to serialized transaction output"); + CByteArray::from(serialized) +} + +#[no_mangle] +#[deprecated] +// Builds the P2WPKH scriptPubkey. +pub unsafe extern "C" fn tw_bitcoin_legacy_build_p2wpkh_script( + _satoshis: i64, + pubkey: *const u8, + pubkey_len: usize, +) -> CByteArray { + // Convert Recipient + let slice = try_or_else!( + CByteArrayRef::new(pubkey, pubkey_len).as_slice(), + CByteArray::null + ); + + let recipient = try_or_else!(PublicKey::from_slice(slice), CByteArray::null); + + let output = Proto::Output { + value: _satoshis as u64, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(recipient.to_bytes().into()), + }), + }), + }; + + let res = try_or_else!( + tw_bitcoin::modules::transactions::OutputBuilder::utxo_from_proto(&output), + CByteArray::null + ); + + // Prepare and serialize protobuf structure. + let proto = LegacyProto::TransactionOutput { + value: res.value as i64, + script: res.script_pubkey, + spendingScript: Default::default(), + }; + + let serialized = tw_proto::serialize(&proto).expect("failed to serialized transaction output"); + CByteArray::from(serialized) +} + +#[no_mangle] +#[deprecated] +// Builds the P2TR key-path scriptPubkey. +pub unsafe extern "C" fn tw_bitcoin_legacy_build_p2tr_key_path_script( + _satoshis: i64, + pubkey: *const u8, + pubkey_len: usize, +) -> CByteArray { + // Convert Recipient + let slice = try_or_else!( + CByteArrayRef::new(pubkey, pubkey_len).as_slice(), + CByteArray::null + ); + let recipient = try_or_else!(PublicKey::from_slice(slice), CByteArray::null); + + let output = Proto::Output { + value: _satoshis as u64, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2tr_key_path(recipient.to_bytes().into()), + }), + }; + + let res = try_or_else!( + tw_bitcoin::modules::transactions::OutputBuilder::utxo_from_proto(&output), + CByteArray::null + ); + + // Prepare and serialize protobuf structure. + let proto = LegacyProto::TransactionOutput { + value: res.value as i64, + script: res.script_pubkey, + spendingScript: Default::default(), + }; + + let serialized = tw_proto::serialize(&proto).expect("failed to serialized transaction output"); + CByteArray::from(serialized) +} + +#[no_mangle] +#[deprecated] +// Builds the Ordinals inscripton for BRC20 transfer. +pub unsafe extern "C" fn tw_bitcoin_legacy_build_brc20_transfer_inscription( + // The 4-byte ticker. + ticker: *const c_char, + value: u64, + _satoshis: i64, + pubkey: *const u8, + pubkey_len: usize, +) -> CByteArray { + // Convert Recipient + let slice = try_or_else!( + CByteArrayRef::new(pubkey, pubkey_len).as_slice(), + CByteArray::null + ); + + let recipient = try_or_else!(PublicKey::from_slice(slice), CByteArray::null); + + // Convert ticket. + let ticker = match CStr::from_ptr(ticker).to_str() { + Ok(input) => input, + Err(_) => return CByteArray::null(), + }; + + let output = Proto::Output { + value: _satoshis as u64, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::brc20_inscribe( + Proto::mod_Output::OutputBrc20Inscription { + inscribe_to: recipient.to_bytes().into(), + ticker: ticker.into(), + transfer_amount: value, + }, + ), + }), + }; + + let res = try_or_else!( + tw_bitcoin::modules::transactions::OutputBuilder::utxo_from_proto(&output), + CByteArray::null + ); + + // Prepare and serialize protobuf structure. + let proto = LegacyProto::TransactionOutput { + value: res.value as i64, + script: res.script_pubkey, + spendingScript: res.taproot_payload, + }; + + let serialized = tw_proto::serialize(&proto).expect("failed to serialized transaction output"); + CByteArray::from(serialized) +} + +#[no_mangle] +#[deprecated] +// Builds the Ordinals inscripton for BRC20 transfer. +pub unsafe extern "C" fn tw_bitcoin_legacy_build_nft_inscription( + mime_type: *const c_char, + payload: *const u8, + payload_len: usize, + _satoshis: i64, + pubkey: *const u8, + pubkey_len: usize, +) -> CByteArray { + // Convert mimeType. + let mime_type = match CStr::from_ptr(mime_type).to_str() { + Ok(input) => input, + Err(_) => return CByteArray::null(), + }; + + // Convert data to inscribe. + let payload = try_or_else!( + CByteArrayRef::new(payload, payload_len).as_slice(), + CByteArray::null + ); + + // Convert Recipient. + let slice = try_or_else!( + CByteArrayRef::new(pubkey, pubkey_len).as_slice(), + CByteArray::null + ); + + let recipient = try_or_else!(PublicKey::from_slice(slice), CByteArray::null); + + // Inscribe NFT data. + let output = Proto::Output { + value: _satoshis as u64, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::ordinal_inscribe( + Proto::mod_Output::OutputOrdinalInscription { + inscribe_to: recipient.to_bytes().into(), + mime_type: mime_type.into(), + payload: payload.into(), + }, + ), + }), + }; + + let res = try_or_else!( + tw_bitcoin::modules::transactions::OutputBuilder::utxo_from_proto(&output), + CByteArray::null + ); + + // Prepare and serialize protobuf structure. + let proto = LegacyProto::TransactionOutput { + value: res.value as i64, + script: res.script_pubkey, + spendingScript: res.taproot_payload, + }; + + let serialized = tw_proto::serialize(&proto).expect("failed to serialized transaction output"); + CByteArray::from(serialized) +} + +#[deprecated] +#[no_mangle] +pub unsafe extern "C" fn tw_bitcoin_legacy_calculate_transaction_fee( + input: *const u8, + input_len: usize, + sat_vb: u64, +) -> CUInt64Result { + let Some(mut encoded) = CByteArrayRef::new(input, input_len).as_slice() else { + return CUInt64Result::error(1); + }; + + // Decode transaction. + let Ok(tx) = Transaction::consensus_decode(&mut encoded) else { + return CUInt64Result::error(1); + }; + + // Calculate fee. + let weight = tx.weight(); + let fee = weight.to_vbytes_ceil() * sat_vb; + + CUInt64Result::ok(fee) +} + +#[no_mangle] +#[deprecated] +pub unsafe extern "C" fn tw_bitcoin_legacy_taproot_build_and_sign_transaction( + input: *const u8, + input_len: usize, +) -> CByteArray { + let data = CByteArrayRef::new(input, input_len) + .to_vec() + .unwrap_or_default(); + + let proto: LegacyProto::SigningInput = + try_or_else!(tw_proto::deserialize(&data), CByteArray::null); + + let Ok(signing) = tw_bitcoin::modules::legacy::taproot_build_and_sign_transaction(proto) else { + // Convert the `BitcoinV2.proto` error type inot the `Common.proto` + // errot type and return. + let error = LegacyProto::SigningOutput { + error: CommonProto::SigningError::Error_general, + ..Default::default() + }; + + let serialized = tw_proto::serialize(&error).expect("failed to serialize error message"); + return CByteArray::from(serialized) + }; + + // Serialize SigningOutput and return. + let serialized = tw_proto::serialize(&signing).expect("failed to serialize signed transaction"); + CByteArray::from(serialized) +} diff --git a/rust/wallet_core_rs/src/ffi/bitcoin/mod.rs b/rust/wallet_core_rs/src/ffi/bitcoin/mod.rs new file mode 100644 index 00000000000..5b0dcab0885 --- /dev/null +++ b/rust/wallet_core_rs/src/ffi/bitcoin/mod.rs @@ -0,0 +1,8 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#[cfg(feature = "bitcoin-legacy")] +pub mod legacy; diff --git a/rust/wallet_core_rs/src/ffi/ethereum/abi.rs b/rust/wallet_core_rs/src/ffi/ethereum/abi.rs new file mode 100644 index 00000000000..76b9906d0bc --- /dev/null +++ b/rust/wallet_core_rs/src/ffi/ethereum/abi.rs @@ -0,0 +1,110 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#![allow(clippy::missing_safety_doc)] + +use tw_coin_registry::coin_type::CoinType; +use tw_coin_registry::dispatcher::evm_dispatcher; +use tw_memory::ffi::tw_data::TWData; +use tw_memory::ffi::tw_string::TWString; +use tw_memory::ffi::RawPtrTrait; +use tw_misc::try_or_else; + +/// Decode function call data to human readable json format, according to input abi json. +/// +/// \param coin EVM-compatible coin type. +/// \param input The serialized data of `TW.EthereumAbi.Proto.ContractCallDecodingInput`. +/// \return serialized `EthereumAbi::Proto::ContractCallDecodingOutput`. +#[no_mangle] +pub unsafe extern "C" fn tw_ethereum_abi_decode_contract_call( + coin: CoinType, + input: *const TWData, +) -> *mut TWData { + let input_data = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); + let evm_dispatcher = try_or_else!(evm_dispatcher(coin), std::ptr::null_mut); + + evm_dispatcher + .decode_abi_contract_call(input_data.as_slice()) + .map(|data| TWData::from(data).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} + +/// Decode a function input or output data according to a given ABI. +/// +/// \param coin EVM-compatible coin type. +/// \param input The serialized data of `TW.EthereumAbi.Proto.ParamsDecodingInput`. +/// \return The serialized data of a `TW.EthereumAbi.Proto.ParamsDecodingOutput` proto object. +#[no_mangle] +pub unsafe extern "C" fn tw_ethereum_abi_decode_params( + coin: CoinType, + input: *const TWData, +) -> *mut TWData { + let input_data = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); + let evm_dispatcher = try_or_else!(evm_dispatcher(coin), std::ptr::null_mut); + + evm_dispatcher + .decode_abi_params(input_data.as_slice()) + .map(|data| TWData::from(data).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} + +/// Returns the function type signature, of the form "baz(int32,uint256)". +/// +/// \param coin EVM-compatible coin type. +/// \param input The serialized data of `TW.EthereumAbi.Proto.FunctionGetTypeInput`. +/// \return function type signature as a Non-null string. +#[no_mangle] +pub unsafe extern "C" fn tw_ethereum_abi_function_get_signature( + coin: CoinType, + input: *const TWData, +) -> *mut TWString { + let input_data = try_or_else!(TWData::from_ptr_as_ref(input), || TWString::new() + .into_ptr()); + let evm_dispatcher = try_or_else!(evm_dispatcher(coin), || TWString::new().into_ptr()); + + evm_dispatcher + .get_abi_function_signature(input_data.as_slice()) + .map(|str| TWString::from(str).into_ptr()) + .unwrap_or_else(|_| TWString::new().into_ptr()) +} + +/// Encode function inputs to Eth ABI binary. +/// +/// \param coin EVM-compatible coin type. +/// \param input The serialized data of `TW.EthereumAbi.Proto.FunctionEncodingInput`. +/// \return The serialized data of a `TW.EthereumAbi.Proto.FunctionEncodingOutput` proto object. +#[no_mangle] +pub unsafe extern "C" fn tw_ethereum_abi_encode_function( + coin: CoinType, + input: *const TWData, +) -> *mut TWData { + let input_data = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); + let evm_dispatcher = try_or_else!(evm_dispatcher(coin), std::ptr::null_mut); + + evm_dispatcher + .encode_abi_function(input_data.as_slice()) + .map(|data| TWData::from(data).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} + +/// /// Decodes an Eth ABI value according to a given type. +/// +/// \param coin EVM-compatible coin type. +/// \param input The serialized data of `TW.EthereumAbi.Proto.ValueDecodingInput`. +/// \return The serialized data of a `TW.EthereumAbi.Proto.ValueDecodingOutput` proto object. +#[no_mangle] +pub unsafe extern "C" fn tw_ethereum_abi_decode_value( + coin: CoinType, + input: *const TWData, +) -> *mut TWData { + let input_data = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); + let evm_dispatcher = try_or_else!(evm_dispatcher(coin), std::ptr::null_mut); + + evm_dispatcher + .decode_abi_value(input_data.as_slice()) + .map(|data| TWData::from(data).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} diff --git a/rust/wallet_core_rs/src/ffi/ethereum/mod.rs b/rust/wallet_core_rs/src/ffi/ethereum/mod.rs new file mode 100644 index 00000000000..e3d6538646b --- /dev/null +++ b/rust/wallet_core_rs/src/ffi/ethereum/mod.rs @@ -0,0 +1,10 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#[cfg(feature = "ethereum-abi")] +pub mod abi; +#[cfg(feature = "ethereum-rlp")] +pub mod rlp; diff --git a/rust/wallet_core_rs/src/ffi/ethereum/rlp.rs b/rust/wallet_core_rs/src/ffi/ethereum/rlp.rs new file mode 100644 index 00000000000..308228bd09b --- /dev/null +++ b/rust/wallet_core_rs/src/ffi/ethereum/rlp.rs @@ -0,0 +1,31 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#![allow(clippy::missing_safety_doc)] + +use tw_coin_registry::coin_type::CoinType; +use tw_coin_registry::dispatcher::evm_dispatcher; +use tw_memory::ffi::tw_data::TWData; +use tw_memory::ffi::RawPtrTrait; +use tw_misc::try_or_else; + +/// Encodes an item or a list of items as Eth RLP binary format. +/// +/// \param coin EVM-compatible coin type. +/// \param input Non-null serialized `EthereumRlp::Proto::EncodingInput`. +/// \return serialized `EthereumRlp::Proto::EncodingOutput`. +#[no_mangle] +pub unsafe extern "C" fn tw_ethereum_rlp_encode( + coin: CoinType, + input: *const TWData, +) -> *mut TWData { + let input_data = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); + let evm_dispatcher = try_or_else!(evm_dispatcher(coin), std::ptr::null_mut); + evm_dispatcher + .encode_rlp(input_data.as_slice()) + .map(|data| TWData::from(data).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} diff --git a/rust/wallet_core_rs/src/ffi/mod.rs b/rust/wallet_core_rs/src/ffi/mod.rs new file mode 100644 index 00000000000..54176826c15 --- /dev/null +++ b/rust/wallet_core_rs/src/ffi/mod.rs @@ -0,0 +1,8 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod bitcoin; +pub mod ethereum; diff --git a/rust/wallet_core_rs/src/lib.rs b/rust/wallet_core_rs/src/lib.rs index b3eb03421d4..6335fa4a0f4 100644 --- a/rust/wallet_core_rs/src/lib.rs +++ b/rust/wallet_core_rs/src/lib.rs @@ -4,11 +4,15 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +pub extern crate tw_any_coin; pub extern crate tw_bitcoin; +pub extern crate tw_coin_registry; pub extern crate tw_encoding; +pub extern crate tw_ethereum; pub extern crate tw_hash; pub extern crate tw_keypair; pub extern crate tw_memory; pub extern crate tw_move_parser; pub extern crate tw_proto; -pub extern crate tw_starknet; + +pub mod ffi; diff --git a/rust/wallet_core_rs/tests/data/custom.json b/rust/wallet_core_rs/tests/data/custom.json new file mode 100644 index 00000000000..711a8c86217 --- /dev/null +++ b/rust/wallet_core_rs/tests/data/custom.json @@ -0,0 +1,20 @@ +{ + "ec37a4a0": { + "constant": false, + "inputs": [{ + "name": "name", + "type": "string" + }, { + "name": "age", + "type": "uint" + }, { + "name": "height", + "type": "int32" + }], + "name": "setName", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +} \ No newline at end of file diff --git a/rust/wallet_core_rs/tests/data/custom_decoded.json b/rust/wallet_core_rs/tests/data/custom_decoded.json new file mode 100644 index 00000000000..c5f0e4b4cb8 --- /dev/null +++ b/rust/wallet_core_rs/tests/data/custom_decoded.json @@ -0,0 +1,20 @@ +{ + "function": "setName(string,uint256,int32)", + "inputs": [ + { + "name": "name", + "type": "string", + "value": "trusty" + }, + { + "name": "age", + "type": "uint256", + "value": "3" + }, + { + "name": "height", + "type": "int32", + "value": "100" + } + ] +} \ No newline at end of file diff --git a/rust/wallet_core_rs/tests/ethereum_abi.rs b/rust/wallet_core_rs/tests/ethereum_abi.rs new file mode 100644 index 00000000000..85b98a043dc --- /dev/null +++ b/rust/wallet_core_rs/tests/ethereum_abi.rs @@ -0,0 +1,196 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde_json::{json, Value as Json}; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_memory::test_utils::tw_string_helper::TWStringHelper; +use tw_number::U256; +use tw_proto::EthereumAbi::{Proto as AbiProto, Proto}; +use tw_proto::{deserialize, serialize}; +use wallet_core_rs::ffi::ethereum::abi::{ + tw_ethereum_abi_decode_contract_call, tw_ethereum_abi_decode_params, + tw_ethereum_abi_decode_value, tw_ethereum_abi_encode_function, + tw_ethereum_abi_function_get_signature, +}; + +use Proto::mod_ParamType::OneOfparam as ParamTypeEnum; +use Proto::mod_Token::OneOftoken as TokenEnum; +use Proto::AbiError as AbiErrorKind; + +const ETHEREUM_COIN_TYPE: u32 = 60; + +fn param(name: &str, kind: ParamTypeEnum<'static>) -> Proto::Param<'static> { + Proto::Param { + name: name.to_string().into(), + param: Some(Proto::ParamType { param: kind }), + } +} + +fn named_token(name: &str, token: TokenEnum<'static>) -> Proto::Token<'static> { + Proto::Token { + name: name.to_string().into(), + token, + } +} + +fn number_n(value: u64) -> Proto::NumberNParam<'static> { + Proto::NumberNParam { + bits: BITS, + value: U256::encode_be_compact(value), + } +} + +#[test] +fn test_ethereum_abi_decode_contract_call() { + const CUSTOM_ABI_JSON: &str = include_str!("data/custom.json"); + const CUSTOM_DECODED_JSON: &str = include_str!("data/custom_decoded.json"); + + let encoded = "ec37a4a000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000067472757374790000000000000000000000000000000000000000000000000000".decode_hex().unwrap(); + + let input = AbiProto::ContractCallDecodingInput { + encoded: encoded.into(), + smart_contract_abi_json: CUSTOM_ABI_JSON.into(), + }; + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output_data = TWDataHelper::wrap(unsafe { + tw_ethereum_abi_decode_contract_call(ETHEREUM_COIN_TYPE, input_data.ptr()) + }) + .to_vec() + .expect("!tw_ethereum_abi_decode_contract_call returned nullptr"); + let output: AbiProto::ContractCallDecodingOutput = deserialize(&output_data) + .expect("!tw_ethereum_abi_decode_contract_call returned an invalid output"); + + assert_eq!(output.error, AbiErrorKind::OK); + assert!(output.error_message.is_empty()); + + let actual: Json = serde_json::from_str(&output.decoded_json).unwrap(); + let expected: Json = serde_json::from_str(CUSTOM_DECODED_JSON).unwrap(); + assert_eq!(actual, expected); +} + +#[test] +fn test_ethereum_abi_decode_params() { + let abi_json = json!([ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ]); + let abi_json = serde_json::to_string(&abi_json).unwrap(); + // https://etherscan.io/tx/0xc2744000a107aee4761cf8a638657f91c3003a54e2f1818c37d781be7e48187a + let encoded = "00000000000000000000000088341d1a8f672d2780c8dc725902aae72f143b0c0000000000000000000000000000000000000000000000000000000000000001".decode_hex().unwrap(); + + let input = AbiProto::ParamsDecodingInput { + encoded: encoded.into(), + abi: AbiProto::mod_ParamsDecodingInput::OneOfabi::abi_json(abi_json.into()), + }; + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output_data = TWDataHelper::wrap(unsafe { + tw_ethereum_abi_decode_params(ETHEREUM_COIN_TYPE, input_data.ptr()) + }) + .to_vec() + .expect("!tw_ethereum_abi_decode_params returned nullptr"); + + let output: AbiProto::ParamsDecodingOutput = deserialize(&output_data) + .expect("!tw_ethereum_abi_decode_params returned an invalid output"); + + assert_eq!(output.error, AbiErrorKind::OK); + assert!(output.error_message.is_empty()); + + let expected_tokens = vec![ + named_token( + "to", + TokenEnum::address("0x88341d1a8F672D2780C8dC725902AAe72F143B0c".into()), + ), + named_token("approved", TokenEnum::boolean(true)), + ]; + assert_eq!(output.tokens, expected_tokens); +} + +#[test] +fn test_ethereum_abi_function_get_signature() { + let input = AbiProto::FunctionGetTypeInput { + function_name: "baz".into(), + inputs: vec![ + param( + "foo", + ParamTypeEnum::number_uint(Proto::NumberNType { bits: 64 }), + ), + param("bar", ParamTypeEnum::address(Proto::AddressType {})), + ], + }; + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let actual = TWStringHelper::wrap(unsafe { + tw_ethereum_abi_function_get_signature(ETHEREUM_COIN_TYPE, input_data.ptr()) + }) + .to_string() + .expect("!tw_ethereum_abi_function_get_signature returned nullptr"); + + assert_eq!(actual, "baz(uint64,address)"); +} + +#[test] +fn test_ethereum_abi_encode_function() { + let input = AbiProto::FunctionEncodingInput { + function_name: "baz".into(), + tokens: vec![ + named_token("", TokenEnum::number_uint(number_n::<256>(69))), + named_token("", TokenEnum::boolean(true)), + ], + }; + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output_data = TWDataHelper::wrap(unsafe { + tw_ethereum_abi_encode_function(ETHEREUM_COIN_TYPE, input_data.ptr()) + }) + .to_vec() + .expect("!tw_ethereum_abi_encode_function returned nullptr"); + + let output: AbiProto::FunctionEncodingOutput = deserialize(&output_data) + .expect("!tw_ethereum_abi_encode_function returned an invalid output"); + + assert_eq!(output.error, AbiErrorKind::OK); + assert!(output.error_message.is_empty()); + assert_eq!(output.encoded.to_hex(), "72ed38b600000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001"); +} + +#[test] +fn test_ethereum_abi_decode_value() { + let input = AbiProto::ValueDecodingInput { + encoded: "000000000000000000000000000000000000000000000000000000000000002a" + .decode_hex() + .unwrap() + .into(), + param_type: "int8".into(), + }; + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output_data = TWDataHelper::wrap(unsafe { + tw_ethereum_abi_decode_value(ETHEREUM_COIN_TYPE, input_data.ptr()) + }) + .to_vec() + .expect("!tw_ethereum_abi_decode_value returned nullptr"); + + let output: AbiProto::ValueDecodingOutput = deserialize(&output_data) + .expect("!tw_ethereum_abi_decode_value returned an invalid output"); + + assert_eq!(output.error, AbiErrorKind::OK); + assert!(output.error_message.is_empty()); + assert_eq!(output.param_str, "42"); +} diff --git a/rust/wallet_core_rs/tests/ethereum_rlp.rs b/rust/wallet_core_rs/tests/ethereum_rlp.rs new file mode 100644 index 00000000000..29811ac0def --- /dev/null +++ b/rust/wallet_core_rs/tests/ethereum_rlp.rs @@ -0,0 +1,37 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::error::SigningErrorType; +use tw_encoding::hex::ToHex; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_proto::EthereumRlp::Proto as RlpProto; +use tw_proto::{deserialize, serialize}; +use wallet_core_rs::ffi::ethereum::rlp::tw_ethereum_rlp_encode; +use RlpProto::mod_RlpItem::OneOfitem as Item; + +const ETHEREUM_COIN_TYPE: u32 = 60; + +#[test] +fn test_ethereum_rlp() { + let item = RlpProto::RlpItem { + item: Item::number_u64(128), + }; + let input = RlpProto::EncodingInput { item: Some(item) }; + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output_data = + TWDataHelper::wrap(unsafe { tw_ethereum_rlp_encode(ETHEREUM_COIN_TYPE, input_data.ptr()) }) + .to_vec() + .expect("!tw_ethereum_rlp_encode returned nullptr"); + let output: RlpProto::EncodingOutput = + deserialize(&output_data).expect("!tw_ethereum_rlp_encode returned an invalid output"); + + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected_encoded = "8180"; + assert_eq!(output.encoded.to_hex(), expected_encoded); +} diff --git a/samples/go/sample/external_signing.go b/samples/go/sample/external_signing.go index 87b5b96d6b3..161de4380db 100644 --- a/samples/go/sample/external_signing.go +++ b/samples/go/sample/external_signing.go @@ -68,29 +68,25 @@ func SignExternalEthereumDemo() { coin := core.CoinTypeEthereum fmt.Println("\n==> Step 1: Prepare transaction input (protobuf)") - txInputData := core.BuildInput( - coin, - "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F", // from - "0x3535353535353535353535353535353535353535", // to - "1000000000000000000", // amount - "ETH", // asset - "", // memo - "", // chainId - ) - fmt.Println("txInputData len: ", len(txInputData)) - - // Set a few other values var input ethereum.SigningInput - proto.Unmarshal(txInputData, &input) + input.Transaction = ðereum.Transaction { + TransactionOneof: ðereum.Transaction_Transfer_ { + Transfer: ðereum.Transaction_Transfer{ + Amount: big.NewInt(1000000000000000000).Bytes(), + }, + }, + } + input.ChainId = big.NewInt(1).Bytes() + input.ToAddress = "0x3535353535353535353535353535353535353535" input.Nonce = big.NewInt(11).Bytes() input.GasPrice = big.NewInt(20000000000).Bytes() input.GasLimit = big.NewInt(21000).Bytes() input.TxMode = ethereum.TransactionMode_Legacy - txInputData2, _ := proto.Marshal(&input) - fmt.Println("txInputData len: ", len(txInputData2)) + txInputData, _ := proto.Marshal(&input) + fmt.Println("txInputData len: ", len(txInputData)) fmt.Println("\n==> Step 2: Obtain preimage hash") - hashes := core.PreImageHashes(coin, txInputData2) + hashes := core.PreImageHashes(coin, txInputData) fmt.Println("hash(es): ", len(hashes), hex.EncodeToString(hashes)) var preSigningOutput transactioncompiler.PreSigningOutput @@ -100,7 +96,7 @@ func SignExternalEthereumDemo() { // Simulate signature, normally obtained from signature server signature, _ := hex.DecodeString("360a84fb41ad07f07c845fedc34cde728421803ebbaae392fc39c116b29fc07b53bd9d1376e15a191d844db458893b928f3efbfee90c9febf51ab84c9796677900") publicKey, _ := hex.DecodeString("044bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382ce28cab79ad7119ee1ad3ebcdb98a16805211530ecc6cfefa1b88e6dff99232a") - txOutput := core.CompileWithSignatures(coin, txInputData2, [][]byte{signature}, [][]byte{publicKey}) + txOutput := core.CompileWithSignatures(coin, txInputData, [][]byte{signature}, [][]byte{publicKey}) fmt.Println("final txOutput proto: ", len(txOutput)) var output ethereum.SigningOutput diff --git a/samples/kmp/shared/build.gradle.kts b/samples/kmp/shared/build.gradle.kts index 6200cda53af..fdc3fb1dbc4 100644 --- a/samples/kmp/shared/build.gradle.kts +++ b/samples/kmp/shared/build.gradle.kts @@ -35,7 +35,7 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation("com.trustwallet:wallet-core-kotlin:3.2.21") + implementation("com.trustwallet:wallet-core-kotlin:3.3.2") } } val commonTest by getting { diff --git a/src/Aeternity/Signer.cpp b/src/Aeternity/Signer.cpp index ea05ed59875..20d31a6dd61 100644 --- a/src/Aeternity/Signer.cpp +++ b/src/Aeternity/Signer.cpp @@ -48,20 +48,23 @@ Proto::SigningOutput Signer::sign(const TW::PrivateKey& privateKey, Transaction& return createProtoOutput(signature, signedEncodedTx); } -Data Signer::buildRlpTxRaw(Data& txRaw, Data& sigRaw) { - auto rlpTxRaw = Data(); - auto signaturesList = Data(); - append(signaturesList, Ethereum::RLP::encode(sigRaw)); +Data Signer::buildRlpTxRaw(const Data& txRaw, const Data& sigRaw) { + EthereumRlp::Proto::EncodingInput input; + auto* rlpList = input.mutable_item()->mutable_list(); - append(rlpTxRaw, Ethereum::RLP::encode(Identifiers::objectTagSignedTransaction)); - append(rlpTxRaw, Ethereum::RLP::encode(Identifiers::rlpMessageVersion)); - append(rlpTxRaw, Ethereum::RLP::encodeList(signaturesList)); - append(rlpTxRaw, Ethereum::RLP::encode(txRaw)); + rlpList->add_items()->set_number_u64(Identifiers::objectTagSignedTransaction); + rlpList->add_items()->set_number_u64(Identifiers::rlpMessageVersion); - return Ethereum::RLP::encodeList(rlpTxRaw); + // Append a list of signatures. + auto* signaturesList = rlpList->add_items()->mutable_list(); + signaturesList->add_items()->set_data(sigRaw.data(), sigRaw.size()); + + rlpList->add_items()->set_data(txRaw.data(), txRaw.size()); + + return Ethereum::RLP::encode(input); } -Data Signer::buildMessageToSign(Data& txRaw) { +Data Signer::buildMessageToSign(const Data& txRaw) { auto data = Data(); Data bytes(Identifiers::networkId.begin(), Identifiers::networkId.end()); append(data, bytes); diff --git a/src/Aeternity/Signer.h b/src/Aeternity/Signer.h index fd085b2fdb3..db49586d535 100644 --- a/src/Aeternity/Signer.h +++ b/src/Aeternity/Signer.h @@ -23,9 +23,9 @@ class Signer { private: static const uint8_t checkSumSize = 4; - static Data buildRlpTxRaw(Data& txRaw, Data& sigRaw); + static Data buildRlpTxRaw(const Data& txRaw, const Data& sigRaw); - static Data buildMessageToSign(Data& txRaw); + static Data buildMessageToSign(const Data& txRaw); static Proto::SigningOutput createProtoOutput(std::string& signature, const std::string& signedTx); diff --git a/src/Aeternity/Transaction.cpp b/src/Aeternity/Transaction.cpp index cb104daede2..90dd197b74b 100644 --- a/src/Aeternity/Transaction.cpp +++ b/src/Aeternity/Transaction.cpp @@ -12,21 +12,44 @@ namespace TW::Aeternity { +/// Aeternity network does not accept zero int values as rlp param, +/// instead empty byte array should be encoded +/// see https://forum.aeternity.com/t/invalid-tx-error-on-mainnet-goggle-says-it-looks-good/4118/5?u=defuera +EthereumRlp::Proto::RlpItem prepareSafeZero(const uint256_t& value) { + EthereumRlp::Proto::RlpItem item; + + if (value == 0) { + Data zeroValue{0}; + item.set_data(zeroValue.data(), zeroValue.size()); + } else { + auto valueData = store(value); + item.set_number_u256(valueData.data(), valueData.size()); + } + + return item; +} + /// RLP returns a byte serialized representation Data Transaction::encode() { - auto encoded = Data(); - append(encoded, Ethereum::RLP::encode(Identifiers::objectTagSpendTransaction)); - append(encoded, Ethereum::RLP::encode(Identifiers::rlpMessageVersion)); - append(encoded, Ethereum::RLP::encode(buildTag(sender_id))); - append(encoded, Ethereum::RLP::encode(buildTag(recipient_id))); - append(encoded, encodeSafeZero(amount)); - append(encoded, encodeSafeZero(fee)); - append(encoded, encodeSafeZero(ttl)); - append(encoded, encodeSafeZero(nonce)); - append(encoded, Ethereum::RLP::encode(payload)); - - const Data& raw = Ethereum::RLP::encodeList(encoded); - return raw; + EthereumRlp::Proto::EncodingInput input; + auto* rlpList = input.mutable_item()->mutable_list(); + + auto senderIdTag = buildTag(sender_id); + auto recipientIdTag = buildTag(recipient_id); + + rlpList->add_items()->set_number_u64(Identifiers::objectTagSpendTransaction); + rlpList->add_items()->set_number_u64(Identifiers::rlpMessageVersion); + rlpList->add_items()->set_data(senderIdTag.data(), senderIdTag.size()); + rlpList->add_items()->set_data(recipientIdTag.data(), recipientIdTag.size()); + + *rlpList->add_items() = prepareSafeZero(amount); + *rlpList->add_items() = prepareSafeZero(fee); + *rlpList->add_items() = prepareSafeZero(ttl); + *rlpList->add_items() = prepareSafeZero(nonce); + + rlpList->add_items()->set_data(payload.data(), payload.size()); + + return Ethereum::RLP::encode(input); } TW::Data Transaction::buildTag(const std::string& address) { @@ -39,11 +62,4 @@ TW::Data Transaction::buildTag(const std::string& address) { return data; } -TW::Data Transaction::encodeSafeZero(uint256_t value) { - if (value == 0) { - return Ethereum::RLP::encode(Data{0}); - } - return Ethereum::RLP::encode(value); -} - } // namespace TW::Aeternity diff --git a/src/Aeternity/Transaction.h b/src/Aeternity/Transaction.h index 20efc9d5d79..f019395ecd6 100644 --- a/src/Aeternity/Transaction.h +++ b/src/Aeternity/Transaction.h @@ -52,13 +52,6 @@ class Transaction { //// buildIDTag assemble an id() object //// see https://github.com/aeternity/protocol/blob/epoch-v0.22.0/serializations.md#the-id-type static Data buildTag(const std::string& address); - - /// Aeternity network does not accept zero int values as rlp param, - /// instead empty byte array should be encoded - /// see https://forum.aeternity.com/t/invalid-tx-error-on-mainnet-goggle-says-it-looks-good/4118/5?u=defuera - static Data encodeSafeZero(uint256_t value); - - }; } // namespace TW::Aeternity diff --git a/src/Aion/RLP.h b/src/Aion/RLP.h index 5f0c8a1991c..a1995e11a60 100644 --- a/src/Aion/RLP.h +++ b/src/Aion/RLP.h @@ -17,20 +17,28 @@ namespace TW::Aion { +using boost::multiprecision::uint128_t; + /// Aion's RLP encoding for long numbers /// https://github.com/aionnetwork/aion/issues/680 struct RLP { - static Data encodeLong(boost::multiprecision::uint128_t l) noexcept { + static EthereumRlp::Proto::RlpItem prepareLong(uint128_t l) { + EthereumRlp::Proto::RlpItem item; + if ((l & 0x00000000FFFFFFFFL) == l) { - return Ethereum::RLP::encode(static_cast(l)); + auto u256 = store(l); + item.set_number_u256(u256.data(), u256.size()); + } else { + Data result(9); + result[0] = 0x80 + 8; + for (int i = 8; i > 0; i--) { + result[i] = (byte)(l & 0xFF); + l >>= 8; + } + item.set_raw_encoded(result.data(), result.size()); } - Data result(9); - result[0] = 0x80 + 8; - for (int i = 8; i > 0; i--) { - result[i] = (byte)(l & 0xFF); - l >>= 8; - } - return result; + + return item; } }; diff --git a/src/Aion/Transaction.cpp b/src/Aion/Transaction.cpp index 3cfad0b3d91..62f81e6b163 100644 --- a/src/Aion/Transaction.cpp +++ b/src/Aion/Transaction.cpp @@ -5,27 +5,42 @@ // file LICENSE at the root of the source code distribution tree. #include "Transaction.h" + +#include "Ethereum/RLP.h" #include "RLP.h" +#include "proto/EthereumRlp.pb.h" +#include "uint256.h" using namespace TW; using boost::multiprecision::uint128_t; namespace TW::Aion { +static const uint128_t gTransactionType = 1; + Data Transaction::encode() const noexcept { - auto encoded = Data(); - append(encoded, Ethereum::RLP::encode(nonce)); - append(encoded, Ethereum::RLP::encode(to.bytes)); - append(encoded, Ethereum::RLP::encode(amount)); - append(encoded, Ethereum::RLP::encode(payload)); - append(encoded, Ethereum::RLP::encode(timestamp)); - append(encoded, RLP::encodeLong(gasLimit)); - append(encoded, RLP::encodeLong(gasPrice)); - append(encoded, RLP::encodeLong(uint128_t(1))); // Aion transaction type + auto nonceData = store(nonce); + auto amountData = store(amount); + auto timestampData = store(timestamp); + + EthereumRlp::Proto::EncodingInput input; + auto* rlpList = input.mutable_item()->mutable_list(); + + rlpList->add_items()->set_number_u256(nonceData.data(), nonceData.size()); + rlpList->add_items()->set_data(to.bytes.data(), to.bytes.size()); + rlpList->add_items()->set_number_u256(amountData.data(), amountData.size()); + rlpList->add_items()->set_data(payload.data(), payload.size()); + rlpList->add_items()->set_number_u256(timestampData.data(), timestampData.size()); + + *rlpList->add_items() = RLP::prepareLong(gasLimit); + *rlpList->add_items() = RLP::prepareLong(gasPrice); + *rlpList->add_items() = RLP::prepareLong(gTransactionType); + if (!signature.empty()) { - append(encoded, Ethereum::RLP::encode(signature)); + rlpList->add_items()->set_data(signature.data(), signature.size()); } - return Ethereum::RLP::encodeList(encoded); + + return Ethereum::RLP::encode(input); } } // namespace TW::Aion diff --git a/src/Bitcoin/Script.cpp b/src/Bitcoin/Script.cpp index 0122f6d5eb6..86bde0a6669 100644 --- a/src/Bitcoin/Script.cpp +++ b/src/Bitcoin/Script.cpp @@ -530,7 +530,7 @@ Script Script::lockScriptForAddress(const std::string& string, enum TWCoinType c Proto::TransactionOutput Script::buildBRC20InscribeTransfer(const std::string& ticker, uint64_t amount, const Data& publicKey) { TW::Bitcoin::Proto::TransactionOutput out; - Rust::CByteArrayWrapper res = TW::Rust::tw_build_brc20_transfer_inscription(ticker.data(), amount, 0, publicKey.data(), publicKey.size()); + Rust::CByteArrayWrapper res = TW::Rust::tw_bitcoin_legacy_build_brc20_transfer_inscription(ticker.data(), amount, 0, publicKey.data(), publicKey.size()); auto result = res.data; out.ParseFromArray(result.data(), static_cast(result.size())); return out; @@ -538,7 +538,7 @@ Proto::TransactionOutput Script::buildBRC20InscribeTransfer(const std::string& t Proto::TransactionOutput Script::buildOrdinalNftInscription(const std::string& mimeType, const Data& payload, const Data& publicKey) { TW::Bitcoin::Proto::TransactionOutput out; - Rust::CByteArrayWrapper res = TW::Rust::tw_bitcoin_build_nft_inscription( + Rust::CByteArrayWrapper res = TW::Rust::tw_bitcoin_legacy_build_nft_inscription( mimeType.data(), payload.data(), payload.size(), diff --git a/src/Bitcoin/Signer.cpp b/src/Bitcoin/Signer.cpp index ff33e295c98..0781237f862 100644 --- a/src/Bitcoin/Signer.cpp +++ b/src/Bitcoin/Signer.cpp @@ -25,7 +25,7 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input, std::optiona Proto::SigningOutput output; if (input.is_it_brc_operation()) { auto serializedInput = data(input.SerializeAsString()); - Rust::CByteArrayWrapper res = Rust::tw_taproot_build_and_sign_transaction(serializedInput.data(), serializedInput.size()); + Rust::CByteArrayWrapper res = Rust::tw_bitcoin_legacy_taproot_build_and_sign_transaction(serializedInput.data(), serializedInput.size()); output.ParseFromArray(res.data.data(), static_cast(res.data.size())); return output; } diff --git a/src/Bitcoin/Transaction.cpp b/src/Bitcoin/Transaction.cpp index 46dd2f4ba93..3a0cc8a4ac6 100644 --- a/src/Bitcoin/Transaction.cpp +++ b/src/Bitcoin/Transaction.cpp @@ -239,7 +239,7 @@ void Transaction::serializeInput(size_t subindex, const Script& scriptCode, size } std::optional Transaction::calculateFee(const Data& encoded, uint64_t satVb) { - Rust::CUInt64ResultWrapper res = Rust::tw_bitcoin_calculate_transaction_fee(encoded.data(), encoded.size(), satVb); + Rust::CUInt64ResultWrapper res = Rust::tw_bitcoin_legacy_calculate_transaction_fee(encoded.data(), encoded.size(), satVb); if (res.isErr()) { return std::nullopt; } diff --git a/src/CoinEntry.cpp b/src/CoinEntry.cpp index cc08f2787a7..56ba53c3f95 100644 --- a/src/CoinEntry.cpp +++ b/src/CoinEntry.cpp @@ -6,7 +6,10 @@ #include "CoinEntry.h" #include "Coin.h" +#include "HexCoding.h" +#include "rust/Wrapper.h" #include +#include namespace TW { @@ -29,4 +32,101 @@ byte getFromPrefixPkhOrDefault(const PrefixVariant &prefix, TWCoinType coin) { return TW::p2pkhPrefix(coin); } +bool validateAddressRust(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) { + if (!std::holds_alternative(addressPrefix)) { + throw std::invalid_argument("`Rust::tw_any_address_is_valid_bech32`, `Rust::tw_any_address_is_valid_ss58` are not supported yet"); + } + + Rust::TWStringWrapper addressStr = address; + return Rust::tw_any_address_is_valid(addressStr.get(), static_cast(coin)); +} + +std::string normalizeAddressRust(TWCoinType coin, const std::string& address) { + Rust::TWStringWrapper addressStr = address; + + auto anyAddress = Rust::wrapTWAnyAddress( + Rust::tw_any_address_create_with_string(addressStr.get(), static_cast(coin))); + if (!anyAddress) { + return {}; + } + + Rust::TWStringWrapper normalized = Rust::tw_any_address_description(anyAddress.get()); + return normalized.toStringOrDefault(); +} + +std::string deriveAddressRust(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) { + if (!std::holds_alternative(addressPrefix)) { + throw std::invalid_argument("`Rust::tw_any_address_create_bech32_with_public_key`, " + "`Rust::tw_any_address_create_ss58_with_public_key`, " + "`Rust::tw_any_address_create_with_public_key_filecoin_address_type` are not supported yet"); + } + + auto *twPublicKeyRaw = Rust::tw_public_key_create_with_data(publicKey.bytes.data(), + publicKey.bytes.size(), + static_cast(publicKey.type)); + auto twPublicKey = Rust::wrapTWPublicKey(twPublicKeyRaw); + if (!twPublicKey) { + return {}; + } + + auto *anyAddressRaw = Rust::tw_any_address_create_with_public_key_derivation(twPublicKey.get(), + static_cast(coin), + static_cast(derivation)); + auto anyAddress = Rust::wrapTWAnyAddress(anyAddressRaw); + if (!anyAddress) { + return {}; + } + + Rust::TWStringWrapper derivedAddress = Rust::tw_any_address_description(anyAddress.get()); + return derivedAddress.toStringOrDefault(); +} + +Data addressToDataRust(TWCoinType coin, const std::string& address) { + Rust::TWStringWrapper addressStr = address; + + auto anyAddress = Rust::wrapTWAnyAddress( + Rust::tw_any_address_create_with_string(addressStr.get(), static_cast(coin))); + if (!anyAddress) { + return {}; + } + + Rust::TWDataWrapper data = Rust::tw_any_address_data(anyAddress.get()); + return data.toDataOrDefault(); +} + +void signRust(const Data& dataIn, TWCoinType coin, Data& dataOut) { + Rust::TWDataWrapper input = Rust::tw_data_create_with_bytes(dataIn.data(), dataIn.size()); + Rust::TWDataWrapper output = Rust::tw_any_signer_sign(input.get(), static_cast(coin)); + + dataOut = output.toDataOrDefault(); +} + +Data preImageHashesRust(TWCoinType coin, const Data& dataIn) { + Rust::TWDataWrapper input = dataIn; + Rust::TWDataWrapper output = Rust::tw_transaction_compiler_pre_image_hashes(static_cast(coin), input.get()); + + return output.toDataOrDefault(); +} + +void compileRust( + TWCoinType coin, + const Data& dataIn, + const std::vector& signatures, + const std::vector& publicKeys, + Data& dataOut +) { + Rust::TWDataWrapper input = dataIn; + + std::vector publicKeysData; + for (const auto& publicKey : publicKeys) { + publicKeysData.push_back(publicKey.bytes); + } + + Rust::TWDataVectorWrapper signaturesVec = signatures; + Rust::TWDataVectorWrapper publicKeysVec = publicKeysData; + + Rust::TWDataWrapper output = Rust::tw_transaction_compiler_compile(static_cast(coin), input.get(), signaturesVec.get(), publicKeysVec.get()); + dataOut = output.toDataOrDefault(); +} + } // namespace TW diff --git a/src/CoinEntry.h b/src/CoinEntry.h index c8b0b81bbaf..fec4bd8f265 100644 --- a/src/CoinEntry.h +++ b/src/CoinEntry.h @@ -83,6 +83,33 @@ void signTemplate(const Data& dataIn, Data& dataOut) { dataOut.insert(dataOut.end(), serializedOut.begin(), serializedOut.end()); } +// In each coin's Entry.cpp that is implemented in Rust, this function calls `tw_any_address_is_valid*`. +bool validateAddressRust(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix); + +// In each coin's Entry.cpp that is implemented in Rust, this function calls `tw_any_address_create_with_string*`. +std::string normalizeAddressRust(TWCoinType coin, const std::string& address); + +// In each coin's Entry.cpp that is implemented in Rust, this function calls `tw_any_address_create_with_public_key*`. +std::string deriveAddressRust(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix); + +// In each coin's Entry.cpp that is implemented in Rust, this function calls `tw_any_address_create_with_string*`. +Data addressToDataRust(TWCoinType coin, const std::string& address); + +// In each coin's Entry.cpp that is implemented in Rust, this function calls `tw_any_signer_sign`. +// Note: use output parameter to avoid unneeded copies +void signRust(const Data& dataIn, TWCoinType coin, Data& dataOut); + +// In each coin's Entry.cpp that is implemented in Rust, this function calls `tw_transaction_compiler_pre_image_hashes`. +Data preImageHashesRust(TWCoinType coin, const Data& dataIn); + +// In each coin's Entry.cpp that is implemented in Rust, this function calls `tw_transaction_compiler_compile`. +// Note: use output parameter to avoid unneeded copies +void compileRust(TWCoinType coin, + const Data& dataIn, + const std::vector& signatures, + const std::vector& publicKeys, + Data& dataOut); + // Note: use output parameter to avoid unneeded copies template void planTemplate(const Data& dataIn, Data& dataOut) { diff --git a/src/Ethereum/ABI.h b/src/Ethereum/ABI.h deleted file mode 100644 index 6764769a2da..00000000000 --- a/src/Ethereum/ABI.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "ABI/Array.h" -#include "ABI/Bytes.h" -#include "ABI/Function.h" -#include "ABI/ParamAddress.h" -#include "ABI/ParamBase.h" -#include "ABI/Parameters.h" -#include "ABI/ParamFactory.h" -#include "ABI/ParamNumber.h" -#include "ABI/ParamStruct.h" -#include "ABI/Tuple.h" diff --git a/src/Ethereum/ABI/Array.cpp b/src/Ethereum/ABI/Array.cpp deleted file mode 100644 index b47fc8b034c..00000000000 --- a/src/Ethereum/ABI/Array.cpp +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Array.h" -#include "ParamFactory.h" -#include "ValueEncoder.h" -#include - -#include - -#include - -namespace TW::Ethereum::ABI { - -using namespace TW; -using json = nlohmann::json; - -int ParamArray::addParam(const std::shared_ptr& param) { - assert(param != nullptr); - if (param == nullptr) { - return -1; - } - if (_params.getCount() >= 1 && param->getType() != getProtoType()) { - return -2; - } // do not add different types - return _params.addParam(param); -} - -void ParamArray::addParams(const std::vector>& params) { - for (auto p : params) { - addParam(p); - } -} - -std::shared_ptr ParamArray::getProtoElem() const { - if (_params.getCount() >= 1) { - return _params.getParamUnsafe(0); - } - return _proto; -} - -std::string ParamArray::getProtoType() const { - const auto proto = getProtoElem(); - return (proto != nullptr) ? proto->getType() : "__empty__"; -} - -size_t ParamArray::getSize() const { - return 32 + _params.getSize(); -} - -void ParamArray::encode(Data& data) const { - size_t n = _params.getCount(); - ValueEncoder::encodeUInt256(uint256_t(n), data); - _params.encode(data); -} - -bool ParamArray::decode(const Data& encoded, size_t& offset_inout) { - size_t origOffset = offset_inout; - // read length - uint256_t len256; - if (!ABI::decode(encoded, len256, offset_inout)) { - return false; - } - // check if length is in the size_t range - auto len = static_cast(len256); - if (len256 != uint256_t(len)) { - return false; - } - // check number of values - auto n = _params.getCount(); - if (n == 0 || n > len) { - // Encoded length is less than params count, unsafe to continue decoding - return false; - } - if (n < len) { - // pad with first type - auto first = _params.getParamUnsafe(0); - for (size_t i = 0; i < len - n; i++) { - _params.addParam(first->clone()); - } - } - - // read values - auto res = _params.decode(encoded, offset_inout); - - // padding - offset_inout = origOffset + ValueEncoder::paddedTo32(offset_inout - origOffset); - return res; -} - -bool ParamArray::setValueJson(const std::string& value) { - if (_params.getCount() < 1) { - // no single element - return false; - } - auto valuesJson = json::parse(value, nullptr, false); - if (valuesJson.is_discarded()) { - return false; - } - if (!valuesJson.is_array()) { - return false; - } - // make sure enough elements are in the array - while (_params.getCount() < valuesJson.size()) { - addParam(getProtoElem()->clone()); - } - int cnt = 0; - for (const auto& e : valuesJson) { - std::string eString = e.is_string() ? e.get() : e.dump(); - _params.getParamUnsafe(cnt)->setValueJson(eString); - ++cnt; - } - return true; -} - -Data ParamArray::hashStruct() const { - if (_params.getCount() == 0) { - return Hash::keccak256(Data()); - } - Data hash(32); - Data hashes = _params.encodeHashes(); - if (hashes.size() > 0) { - hash = Hash::keccak256(hashes); - } - return hash; -} - -void ParamArray::fillExtraTypesMap(ExtraTypesMap& extraTypes) const { - if (const auto& proto = getProtoElem(); proto != nullptr) { - proto->fillExtraTypesMap(extraTypes); - } -} - -std::shared_ptr ParamArray::clone() const { - auto newArray = std::make_shared(); - newArray->_params = _params.clone(); - newArray->_proto = _proto->clone(); - return newArray; -} - -void ParamArrayFix::encode(Data& data) const { - this->_params.encode(data); -} - -bool ParamArrayFix::decode(const Data& encoded, size_t& offset_inout) { - return this->_params.decode(encoded, offset_inout); -} - -bool ParamArrayFix::setValueJson(const std::string& value) { - auto valuesJson = json::parse(value, nullptr, false); - if (valuesJson.is_discarded() || !valuesJson.is_array() || _params.getCount() != valuesJson.size()) { - return false; - } - - std::size_t idx{0}; - for (auto&& e : valuesJson) { - std::string eString = e.is_string() ? e.get() : e.dump(); - _params.getParamUnsafe(idx)->setValueJson(eString); - ++idx; - } - return true; -} - -std::shared_ptr ParamArrayFix::clone() const { - auto newArray = std::make_shared(); - newArray->_params = _params.clone(); - return newArray; -} - -void ParamArrayFix::addParams(const Params& params) { - auto addParamFunctor = [this](auto&& param) { - if (param == nullptr) { - throw std::runtime_error("param can't be nullptr"); - } - if (_params.getCount() >= 1 && param->getType() != _params.getParamUnsafe(0)->getType()) { - throw std::runtime_error("params need to be the same type"); - } // do not add different types - _params.addParam(param); - }; - std::for_each(begin(params), end(params), addParamFunctor); -} - -} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/Array.h b/src/Ethereum/ABI/Array.h deleted file mode 100644 index 4638c38f171..00000000000 --- a/src/Ethereum/ABI/Array.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "ParamBase.h" -#include "ParamNumber.h" -#include "Parameters.h" - -namespace TW::Ethereum::ABI { - -/// Dynamic array of the same types, "[]" -/// Normally has at least one element. Empty array can have prototype set so its type is known. -/// Empty with no prototype is possible, but should be avoided. -class ParamArray final : public ParamCollection { -private: - ParamSet _params; - std::shared_ptr _proto; // an optional prototype element, determines the array type, useful in empty array case - -private: - std::shared_ptr getProtoElem() const; // the first element if exists, otherwise the proto element - std::string getProtoType() const; - -public: - ParamArray() = default; - ParamArray(const std::shared_ptr& param1) - : ParamCollection() { addParam(param1); } - ParamArray(const std::vector>& params) - : ParamCollection() { setVal(params); } - void setVal(const std::vector>& params) { addParams(params); } - std::vector> const& getVal() const { return _params.getParams(); } - int addParam(const std::shared_ptr& param); - void addParams(const std::vector>& params); - void setProto(const std::shared_ptr& proto) { _proto = proto; } - std::shared_ptr getParam(int paramIndex) { return _params.getParamUnsafe(paramIndex); } - - std::string getType() const override { return getProtoType() + "[]"; } - size_t getSize() const override; - bool isDynamic() const override { return true; } - size_t getCount() const override { return _params.getCount(); } - void encode(Data& data) const override; - bool decode(const Data& encoded, size_t& offset_inout) override; - bool setValueJson(const std::string& value) override; - Data hashStruct() const override; - void fillExtraTypesMap(ExtraTypesMap& extraTypes) const override; - std::shared_ptr clone() const override; -}; - -/// Fixed-size array of the same type e.g, "type[4]" -class ParamArrayFix final : public ParamCollection { -public: - //! Public Definitions - using Params = std::vector>; - - //! Public constructors - ParamArrayFix() = default; - - explicit ParamArrayFix(const Params& params) noexcept(false) - : ParamCollection() { - this->addParams(params); - } - - //! Public member methods - [[nodiscard]] std::size_t getCount() const override { return _params.getCount(); } - [[nodiscard]] size_t getSize() const override { return _params.getSize(); } - [[nodiscard]] bool isDynamic() const override { return false; } - [[nodiscard]] std::string getType() const override { return _params.getParamUnsafe(0)->getType() + "[" + std::to_string(_params.getCount()) + "]"; } - void encode(Data& data) const override; - bool decode(const Data& encoded, size_t& offset_inout) override; - bool setValueJson(const std::string& value) override; - std::shared_ptr clone() const override; - -private: - //! Private member functions - void addParams(const Params& params) noexcept(false); - - //! Private member fields - ParamSet _params; -}; - -} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/Bytes.cpp b/src/Ethereum/ABI/Bytes.cpp deleted file mode 100644 index 5cbee7fc970..00000000000 --- a/src/Ethereum/ABI/Bytes.cpp +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Bytes.h" -#include "ParamNumber.h" -#include "ValueEncoder.h" -#include -#include - -#include - -namespace TW::Ethereum::ABI { - -void ParamByteArray::encodeBytes(const Data& bytes, Data& data) { - ValueEncoder::encodeUInt256(uint256_t(bytes.size()), data); - - const auto count = bytes.size(); - const auto padding = ValueEncoder::padNeeded32(count); - data.insert(data.end(), bytes.begin(), bytes.begin() + count); - append(data, Data(padding)); -} - -bool ParamByteArray::decodeBytes(const Data& encoded, Data& decoded, size_t& offset_inout) { - size_t origOffset = offset_inout; - // read len - uint256_t len256; - if (!ABI::decode(encoded, len256, offset_inout)) { - return false; - } - // check if length is in the size_t range - auto len = static_cast(len256); - if (len256 != uint256_t(len)) { - return false; - } - // check if there is enough data - if (encoded.size() < offset_inout + len) { - return false; - } - // read data - decoded = Data(encoded.begin() + offset_inout, encoded.begin() + offset_inout + len); - offset_inout += len; - // padding - offset_inout = origOffset + ValueEncoder::paddedTo32(offset_inout - origOffset); - return true; -} - -bool ParamByteArray::setValueJson(const std::string& value) { - setVal(parse_hex(value)); - return true; -} - -Data ParamByteArray::hashStruct() const { - return Hash::keccak256(_bytes); -} - -std::shared_ptr ParamByteArray::clone() const { - return std::make_shared(_bytes); -} - -void ParamByteArrayFix::setVal(const Data& val) { - if (val.size() > _n) { // crop right - _bytes = subData(val, 0, _n); - } else { - _bytes = val; - if (_bytes.size() < _n) { // pad on right - append(_bytes, Data(_n - _bytes.size())); - } - } - assert(_bytes.size() == _n); -} - -void ParamByteArrayFix::encode(Data& data) const { - const auto count = _bytes.size(); - const auto padding = ValueEncoder::padNeeded32(count); - data.insert(data.end(), _bytes.begin(), _bytes.begin() + count); - append(data, Data(padding)); -} - -bool ParamByteArrayFix::decodeBytesFix(const Data& encoded, size_t n, Data& decoded, - size_t& offset_inout) { - size_t origOffset = offset_inout; - if (encoded.size() < offset_inout + n) { - // not enough data - return false; - } - if (decoded.size() < n) { - append(decoded, Data(n - decoded.size())); - } - std::copy(encoded.begin() + offset_inout, encoded.begin() + (offset_inout + n), - decoded.begin()); - offset_inout += n; - // padding - offset_inout = origOffset + ValueEncoder::paddedTo32(offset_inout - origOffset); - return true; -} - -bool ParamByteArrayFix::setValueJson(const std::string& value) { - setVal(parse_hex(value)); - return true; -} - -Data ParamByteArrayFix::hashStruct() const { - if (_bytes.size() > 32) { - return Hash::keccak256(_bytes); - } - return ParamBase::hashStruct(); -} - -std::shared_ptr ParamByteArrayFix::clone() const { - return std::make_shared(_n, _bytes); -} - -void ParamString::encodeString(const std::string& decoded, Data& data) { - auto bytes = Data(decoded.begin(), decoded.end()); - ParamByteArray::encodeBytes(bytes, data); -} - -bool ParamString::decodeString(const Data& encoded, std::string& decoded, size_t& offset_inout) { - Data decodedData; - if (!ParamByteArray::decodeBytes(encoded, decodedData, offset_inout)) { - return false; - } - decoded = std::string(decodedData.begin(), decodedData.end()); - return true; -} - -Data ParamString::hashStruct() const { - Data hash(32); - Data encoded = data(_str); - hash = Hash::keccak256(encoded); - return hash; -} - -std::shared_ptr ParamString::clone() const { - return std::make_shared(_str); -} - -} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/Bytes.h b/src/Ethereum/ABI/Bytes.h deleted file mode 100644 index 3fc92f4c78e..00000000000 --- a/src/Ethereum/ABI/Bytes.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "ParamBase.h" -#include "ValueEncoder.h" -#include - -namespace TW::Ethereum::ABI { - -/// Dynamic array of bytes "bytes" -class ParamByteArray final: public ParamCollection -{ -private: - Data _bytes; -public: - ParamByteArray() = default; - ParamByteArray(const Data& val) : ParamCollection() { setVal(val); } - void setVal(const Data& val) { _bytes = val; } - const Data& getVal() const { return _bytes; } - std::string getType() const override { return "bytes"; }; - size_t getSize() const override { return 32 + ValueEncoder::paddedTo32(_bytes.size()); } - bool isDynamic() const override { return true; } - size_t getCount() const override { return _bytes.size(); } - static void encodeBytes(const Data& bytes, Data& data); - void encode(Data& data) const override { encodeBytes(_bytes, data); } - static bool decodeBytes(const Data& encoded, Data& decoded, size_t& offset_inout); - bool decode(const Data& encoded, size_t& offset_inout) override { - return decodeBytes(encoded, _bytes, offset_inout); - } - bool setValueJson(const std::string& value) override; - Data hashStruct() const override; - std::shared_ptr clone() const override; -}; - -/// Fixed-size array of bytes, "bytes" -class ParamByteArrayFix final: public ParamCollection -{ -private: - size_t _n; - Data _bytes; -public: - ParamByteArrayFix(size_t n): ParamCollection(), _n(n), _bytes(Data(_n)) {} - ParamByteArrayFix(size_t n, const Data& val): ParamCollection(), _n(n), _bytes(Data(_n)) { setVal(val); } - void setVal(const Data& val); - const std::vector& getVal() const { return _bytes; } - std::string getType() const override { return "bytes" + std::to_string(_n); }; - size_t getSize() const override { return ValueEncoder::paddedTo32(_bytes.size()); } - bool isDynamic() const override { return false; } - size_t getCount() const override { return _bytes.size(); } - void encode(Data& data) const override; - static bool decodeBytesFix(const Data& encoded, size_t n, Data& decoded, size_t& offset_inout); - bool decode(const Data& encoded, size_t& offset_inout) override { - return decodeBytesFix(encoded, _n, _bytes, offset_inout); - } - bool setValueJson(const std::string& value) override; - Data hashStruct() const override; - std::shared_ptr clone() const override; -}; - -/// Var-length string parameter -class ParamString final: public ParamCollection -{ -private: - std::string _str; -public: - ParamString() = default; - ParamString(const std::string& val): ParamCollection() { setVal(val); } - void setVal(const std::string& val) { _str = val; } - const std::string& getVal() const { return _str; } - std::string getType() const override { return "string"; }; - size_t getSize() const override { return 32 + ValueEncoder::paddedTo32(_str.size()); } - bool isDynamic() const override { return true; } - size_t getCount() const override { return _str.size(); } - static void encodeString(const std::string& decoded, Data& data); - void encode(Data& data) const override { ParamString::encodeString(_str, data); } - static bool decodeString(const Data& encoded, std::string& decoded, size_t& offset_inout); - bool decode(const Data& encoded, size_t& offset_inout) override { - return decodeString(encoded, _str, offset_inout); - } - bool setValueJson(const std::string& value) override { _str = value; return true; } - Data hashStruct() const override; - std::shared_ptr clone() const override; -}; - -} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/Function.cpp b/src/Ethereum/ABI/Function.cpp index d249c9e2752..ffe84e940f4 100644 --- a/src/Ethereum/ABI/Function.cpp +++ b/src/Ethereum/ABI/Function.cpp @@ -6,51 +6,207 @@ #include "Function.h" -#include +#include "rust/Wrapper.h" +#include "TrustWalletCore/TWCoinType.h" namespace TW::Ethereum::ABI { -Data Function::getSignature() const { - auto typ = getType(); - auto hash = Hash::keccak256(Data(typ.begin(), typ.end())); - auto signature = Data(hash.begin(), hash.begin() + 4); - return signature; +static constexpr std::size_t FUNCTION_SIGNATURE_LEN = 4; + +int Function::addParam(AbiProto::Param paramType, AbiProto::Token paramValue, bool isOutput) { + if (isOutput) { + auto idx = outputValues.size(); + *outputs.add_params() = std::move(paramType); + outputValues.emplace_back(std::move(paramValue)); + return static_cast(idx); + } + + auto idx = inputValues.size(); + *inputs.add_params() = std::move(paramType); + inputValues.emplace_back(std::move(paramValue)); + return static_cast(idx); } -void Function::encode(Data& data) const { - Data signature = getSignature(); - append(data, signature); - _inParams.encode(data); +int Function::addUintParam(uint32_t bits, const Data& encodedValue, bool isOutput) { + AbiProto::Param paramType; + paramType.mutable_param()->mutable_number_uint()->set_bits(bits); + + AbiProto::Token paramValue; + auto* numValue = paramValue.mutable_number_uint(); + numValue->set_bits(bits); + numValue->set_value(encodedValue.data(), encodedValue.size()); + + return addParam(std::move(paramType), std::move(paramValue), isOutput); } -bool Function::decodeOutput(const Data& encoded, size_t& offset_inout) { - // read parameter values - if (!_outParams.decode(encoded, offset_inout)) { - return false; +int Function::addIntParam(uint32_t bits, const Data& encodedValue, bool isOutput) { + AbiProto::Param paramType; + paramType.mutable_param()->mutable_number_int()->set_bits(bits); + + AbiProto::Token paramValue; + auto* numValue = paramValue.mutable_number_int(); + numValue->set_bits(bits); + numValue->set_value(encodedValue.data(), encodedValue.size()); + + return addParam(std::move(paramType), std::move(paramValue), isOutput); +} + +int Function::addInArrayParam(int idx, AbiProto::ParamType paramType, AbiProto::Token paramValue) { + if (idx < 0) { + return -1; } - return true; + + auto idxSize = static_cast(idx); + if (idxSize >= inputValues.size()) { + return -1; + } + + auto& arrayToken = inputValues[idxSize]; + auto& arrayType = *inputs.mutable_params(idx)->mutable_param(); + + if (!arrayToken.has_array() || !arrayType.has_array()) { + return -1; + } + + auto arrayInElementIdx = arrayToken.array().elements_size(); + + *arrayToken.mutable_array()->add_elements() = std::move(paramValue); + *arrayToken.mutable_array()->mutable_element_type() = paramType; + // Override the element type. + *arrayType.mutable_array()->mutable_element_type() = std::move(paramType); + + return arrayInElementIdx; } -bool Function::decodeInput(const Data& encoded, size_t& offset_inout) { - // read 4-byte hash - auto p = ParamByteArrayFix(4); - if (!p.decode(encoded, offset_inout)) { - return false; +int Function::addInArrayUintParam(int idx, uint32_t bits, const Data& encodedValue) { + AbiProto::ParamType paramType; + paramType.mutable_number_uint()->set_bits(bits); + + AbiProto::Token paramValue; + auto* numValue = paramValue.mutable_number_uint(); + numValue->set_bits(bits); + numValue->set_value(encodedValue.data(), encodedValue.size()); + + return addInArrayParam(idx, std::move(paramType), std::move(paramValue)); +} + +int Function::addInArrayIntParam(int idx, uint32_t bits, const Data& encodedValue) { + AbiProto::ParamType paramType; + paramType.mutable_number_int()->set_bits(bits); + + AbiProto::Token paramValue; + auto* numValue = paramValue.mutable_number_int(); + numValue->set_bits(bits); + numValue->set_value(encodedValue.data(), encodedValue.size()); + + return addInArrayParam(idx, std::move(paramType), std::move(paramValue)); +} + +MaybeToken Function::getParam(int idx, bool isOutput) const { + const auto& values = isOutput ? outputValues : inputValues; + + if (idx < 0) { + return {}; } - std::vector hash = p.getVal(); - // adjust offset; hash is NOT padded to 32 bytes - offset_inout = offset_inout - 32 + 4; - // verify hash - Data hashExpect = getSignature(); - if (hash != hashExpect) { - // invalid hash + + auto idxSize = static_cast(idx); + if (idxSize >= values.size()) { + return {}; + } + + return values[idxSize]; +} + +bool Function::decode(const Data& encoded, bool isOutput) { + AbiProto::ParamsDecodingInput input; + + input.set_encoded(encoded.data(), encoded.size()); + if (isOutput) { + *input.mutable_abi_params() = outputs; + } else { + *input.mutable_abi_params() = inputs; + } + + Rust::TWDataWrapper inputData(data(input.SerializeAsString())); + Rust::TWDataWrapper outputPtr = Rust::tw_ethereum_abi_decode_params(TWCoinTypeEthereum, inputData.get()); + + auto outputData = outputPtr.toDataOrDefault(); + if (outputData.empty()) { return false; } - // read parameters - if (!_inParams.decode(encoded, offset_inout)) { + + AbiProto::ParamsDecodingOutput output; + output.ParseFromArray(outputData.data(), static_cast(outputData.size())); + + if (output.error() != AbiProto::AbiError::OK) { return false; } + + std::vector decoded; + for (const auto ¶m : output.tokens()) { + decoded.emplace_back(param); + } + + if (isOutput) { + outputValues = decoded; + } else { + inputValues = decoded; + } return true; } +std::string Function::getType() const { + AbiProto::FunctionGetTypeInput input; + input.set_function_name(name); + *input.mutable_inputs() = inputs.params(); + + Rust::TWDataWrapper inputData(data(input.SerializeAsString())); + Rust::TWStringWrapper outputPtr = Rust::tw_ethereum_abi_function_get_signature(TWCoinTypeEthereum, inputData.get()); + + return outputPtr.toStringOrDefault(); +} + +MaybeData Function::encodeFunctionCall(const std::string& functionName, const Tokens& tokens) { + AbiProto::FunctionEncodingInput input; + input.set_function_name(functionName); + for (const auto& token : tokens) { + *input.add_tokens() = token; + } + + Rust::TWDataWrapper inputData(data(input.SerializeAsString())); + Rust::TWDataWrapper outputPtr = Rust::tw_ethereum_abi_encode_function(TWCoinTypeEthereum, inputData.get()); + + auto outputData = outputPtr.toDataOrDefault(); + if (outputData.empty()) { + return {}; + } + + AbiProto::FunctionEncodingOutput output; + output.ParseFromArray(outputData.data(), static_cast(outputData.size())); + + if (output.error() != AbiProto::AbiError::OK) { + return {}; + } + + return data(output.encoded()); +} + +MaybeData Function::encodeFunctionCall(const std::string& functionName, const BaseParams& params) { + Tokens namedParams; + for (const auto& param : params) { + namedParams.push_back(param->toToken()); + } + return encodeFunctionCall(functionName, namedParams); +} + +MaybeData Function::encodeParams(const BaseParams& params) { + auto encoded = encodeFunctionCall("", params); + if (!encoded.has_value() || encoded.value().size() < FUNCTION_SIGNATURE_LEN) { + return {}; + } + + // The encoded data includes the function call signature (4 bytes). Erase it. + return subData(encoded.value(), FUNCTION_SIGNATURE_LEN); +} + } // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/Function.h b/src/Ethereum/ABI/Function.h index d331ea030f8..fbed1fcbb4d 100644 --- a/src/Ethereum/ABI/Function.h +++ b/src/Ethereum/ABI/Function.h @@ -6,72 +6,97 @@ #pragma once -#include "ParamBase.h" -#include "Parameters.h" -#include "Bytes.h" +#include "ProtoParam.h" +#include "proto/EthereumAbi.pb.h" +#include "../../HexCoding.h" #include "../../uint256.h" -#include "../../Hash.h" +#include +#include #include namespace TW::Ethereum::ABI { -/// Non-generic version of Function, templated version is impossible to pass around to and back over C interface -/// (void* looses the template parameters). +namespace AbiProto = EthereumAbi::Proto; + +using MaybeToken = std::optional; +using MaybeData = std::optional; +using Tokens = std::vector; + class Function { public: - std::string name; - ParamSet _inParams; - ParamSet _outParams; - - Function(std::string name) : name(std::move(name)) {} - Function(std::string name, const std::vector>& inParams) - : name(std::move(name)), _inParams(ParamSet(inParams)) {} - virtual ~Function() {} - /// Add an input parameter. Returns the index of the parameter. - int addInParam(std::shared_ptr param) { - return _inParams.addParam(param); - } - /// Add an output parameter. Returns the index of the parameter. - int addOutParam(std::shared_ptr param) { - return _outParams.addParam(param); - } - /// Add an input or output parameter. Returns the index of the parameter. - int addParam(std::shared_ptr param, bool isOutput = false) { - return isOutput ? _outParams.addParam(param) : _inParams.addParam(param); - } - /// Get an input parameter. - bool getInParam(int paramIndex, std::shared_ptr& param_out) { - return _inParams.getParam(paramIndex, param_out); - } - /// Get an output parameter. - bool getOutParam(int paramIndex, std::shared_ptr& param_out) { - return _outParams.getParam(paramIndex, param_out); + explicit Function(std::string name): name(std::move(name)) {} + + /// Adds an input or output parameter. Returns the index of the parameter. + int addParam(AbiProto::Param paramType, AbiProto::Token paramValue, bool isOutput = false); + + /// Adds an input or output uint parameter. Returns the index of the parameter. + int addUintParam(uint32_t bits, const Data& encodedValue, bool isOutput = false); + + /// Adds an input or output int parameter. Returns the index of the parameter. + int addIntParam(uint32_t bits, const Data& encodedValue, bool isOutput = false); + + /// Adds a parameter to the input array. + /// Please note the array should be present at `inputValues[idx]`. + int addInArrayParam(int idx, AbiProto::ParamType paramType, AbiProto::Token paramValue); + + /// Adds a uint parameter to the input array. + /// Please note the array should be present at `inputValues[idx]`. + int addInArrayUintParam(int idx, uint32_t bits, const Data& encodedValue); + + /// Adds an int parameter to the input array. + /// Please note the array should be present at `inputValues[idx]`. + int addInArrayIntParam(int idx, uint32_t bits, const Data& encodedValue); + + /// Returns an input or output parameter. + MaybeToken getParam(int idx, bool isOutput = false) const; + + /// Returns the data of an input or output uint parameter. + Data getUintParamData(int idx, uint32_t bits, bool isOutput = false) const { + auto param = getParam(idx, isOutput); + if (!param.has_value() || !param->has_number_uint() || param->number_uint().bits() != bits) { + return store(0); + } + return data(param->number_uint().value()); } - /// Get an input or output parameter. - bool getParam(int paramIndex, std::shared_ptr& param_out, bool isOutput = false) { - return isOutput ? _outParams.getParam(paramIndex, param_out) : _inParams.getParam(paramIndex, param_out); + + /// Returns an input or output uint parameter. + template + T getUintParam(int idx, uint32_t bits, bool isOutput = false) const { + auto valueData = getUintParamData(idx, bits, isOutput); + auto val256 = load(valueData); + return static_cast(val256); } - /// Return the function type signature, of the form "baz(int32,uint256)" - std::string getType() const { - return name + _inParams.getType(); + + /// Encodes a function call to Eth ABI binary. + MaybeData encodeInput() const { + return encodeFunctionCall(name, inputValues); } - /// Return the 4-byte function signature - Data getSignature() const; + /// Decode binary, fill input or output parameters. + bool decode(const Data& encoded, bool isOutput = false); - virtual void encode(Data& data) const; + /// Returns the function type signature, of the form "baz(int32,uint256)". + std::string getType() const; - /// Decode binary, fill output parameters - bool decodeOutput(const Data& encoded, size_t& offset_inout); - /// Decode binary, fill input parameters - bool decodeInput(const Data& encoded, size_t& offset_inout); -}; + /// Encodes a function call to Eth ABI binary. + static MaybeData encodeFunctionCall(const std::string& functionName, const Tokens& params); -inline void encode(const Function& func, Data& data) { - func.encode(data); -} + /// Encodes a function call to Eth ABI binary. + static MaybeData encodeFunctionCall(const std::string& functionName, const BaseParams& params); + + /// Encodes params to Eth ABI binary. + static MaybeData encodeParams(const BaseParams& params); + +private: + std::string name; + AbiProto::AbiParams inputs; + AbiProto::AbiParams outputs; + + Tokens inputValues; + Tokens outputValues; +}; } // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/ParamAddress.cpp b/src/Ethereum/ABI/ParamAddress.cpp deleted file mode 100644 index 1707f9f2084..00000000000 --- a/src/Ethereum/ABI/ParamAddress.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "ParamAddress.h" -#include -#include - -namespace TW::Ethereum::ABI { - -Data ParamAddress::getData() const { - Data data = store(_val.getVal(), bytes); - return data; -} - -bool ParamAddress::setValueJson(const std::string& value) { - _val.setVal(load(parse_hex(value))); - return true; -} - -std::shared_ptr ParamAddress::clone() const { - return std::make_shared(getData()); -} - -} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/ParamAddress.h b/src/Ethereum/ABI/ParamAddress.h deleted file mode 100644 index c9ae0fa9d16..00000000000 --- a/src/Ethereum/ABI/ParamAddress.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "ParamNumber.h" -#include - -namespace TW::Ethereum::ABI { - -/// 160-bit Address parameter, "address". Padded to the right, treated like ParamUInt160 -class ParamAddress final: public ParamBase -{ -private: - ParamUIntN _val; -public: - static const size_t bytes = 20; - ParamAddress(): _val(bytes * 8) {} - explicit ParamAddress(const Data& val): _val(bytes * 8, load(val)) {} - std::string getType() const override { return "address"; }; - size_t getSize() const override { return _val.getSize(); } - bool isDynamic() const override { return _val.isDynamic(); } - void encode(Data& data) const override { _val.encode(data); } - bool decode(const Data& encoded, size_t& offset_inout) override { return _val.decode(encoded, offset_inout); } - // get the value as (20-byte) byte array (as opposed to uint256_t) - Data getData() const; - bool setValueJson(const std::string& value) override; - std::shared_ptr clone() const override; -}; - -} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/ParamBase.h b/src/Ethereum/ABI/ParamBase.h deleted file mode 100644 index 882a9571e0f..00000000000 --- a/src/Ethereum/ABI/ParamBase.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "Data.h" - -#include -#include -#include - -namespace TW::Ethereum::ABI { - -// A map of `StructName -> StructType` key-values, where `StructType` is a full type including the structure name. -// Referenced struct type should be sorted by name see: https://eips.ethereum.org/EIPS/eip-712#definition-of-encodetype -using ExtraTypesMap = std::map>; - -/// Abstract base class for parameters. -class ParamBase -{ -public: - virtual ~ParamBase() = default; - virtual std::string getType() const = 0; - virtual size_t getSize() const = 0; - virtual bool isDynamic() const = 0; - virtual void encode(Data& data) const = 0; - virtual bool decode(const Data& encoded, size_t& offset_inout) = 0; - virtual bool setValueJson(const std::string& value) = 0; - // EIP712-style hash of the value (used for signing); default implementation - virtual Data hashStruct() const; - // Helper for EIP712 encoding; fill the given `extraTypes` (recursively). - virtual void fillExtraTypesMap([[maybe_unused]] ExtraTypesMap& extraTypes) const {} - // Creates a copy of this element. - // This method **must** be implemented in a `final` class only. - virtual std::shared_ptr clone() const = 0; -}; - -/// Collection of parameters base class -class ParamCollection: public ParamBase -{ -public: - virtual size_t getCount() const = 0; -}; - -} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/ParamFactory.cpp b/src/Ethereum/ABI/ParamFactory.cpp deleted file mode 100644 index cef102441a2..00000000000 --- a/src/Ethereum/ABI/ParamFactory.cpp +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "ParamFactory.h" -#include "ParamAddress.h" -#include "HexCoding.h" - -#include -#include - -using namespace std; -using namespace boost::algorithm; -using json = nlohmann::json; - -namespace TW::Ethereum::ABI { - -static int parseBitSize(const std::string& type) { - int size = stoi(type); - if (size < 8 || size > 256 || size % 8 != 0 || - size == 8 || size == 16 || size == 32 || size == 64 || size == 256) { - throw invalid_argument("invalid bit size"); - } - return size; -} - -static std::shared_ptr makeUInt(const std::string& type) { - auto bits = parseBitSize(type); - return make_shared(bits); -} - -static std::shared_ptr makeInt(const std::string& type) { - auto bits = parseBitSize(type); - return make_shared(bits); -} - -static bool isArrayType(const std::string& type) { - return ends_with(type, "[]") && type.length() >= 3; -} - -static std::string getArrayElemType(const std::string& arrayType) { - if (isArrayType(arrayType)) { - return arrayType.substr(0, arrayType.length() - 2); - } - return ""; -} - -std::shared_ptr ParamFactory::make(const std::string& type) { - shared_ptr param; - if (isArrayType(type)) { - auto elemType = getArrayElemType(type); - auto elemParam = make(elemType); - if (!elemParam) { - return param; - } - param = make_shared(elemParam); - } else if (type == "address") { - param = make_shared(); - } else if (type == "uint8") { - param = make_shared(); - } else if (type == "uint16") { - param = make_shared(); - } else if (type == "uint32") { - param = make_shared(); - } else if (type == "uint64") { - param = make_shared(); - } else if (type == "uint256" || type == "uint") { - param = make_shared(); - } else if (type == "int8") { - param = make_shared(); - } else if (type == "int16") { - param = make_shared(); - } else if (type == "int32") { - param = make_shared(); - } else if (type == "int64") { - param = make_shared(); - } else if (type == "int256" || type == "int") { - param = make_shared(); - } else if (starts_with(type, "uint")) { - param = makeUInt(type.substr(4, type.size() - 1)); - } else if (starts_with(type, "int")) { - param = makeInt(type.substr(3, type.size() - 1)); - } else if (type == "bool") { - param = make_shared(); - } else if (type == "bytes") { - param = make_shared(); - } else if (starts_with(type, "bytes")) { - auto bits = stoi(type.substr(5, type.size() - 1)); - param = make_shared(bits); - } else if (type == "string") { - param = make_shared(); - } - return param; -} - -std::string joinArrayElems(const std::vector& strings) { - auto array = json::array(); - for (const auto& string : strings) { - // parse to prevent quotes on simple values - auto value = json::parse(string, nullptr, false); - if (value.is_discarded()) { - // fallback - value = json(string); - } - array.push_back(value); - } - return array.dump(); -} - -std::shared_ptr ParamFactory::makeNamed(const std::string& name, const std::string& type) { - auto param = make(type); - if (!param) { - return nullptr; - } - return std::make_shared(name, param); -} - -bool ParamFactory::isPrimitive(const std::string& type) { - if (starts_with(type, "address")) { - return true; - } else if (starts_with(type, "uint")) { - return true; - } else if (starts_with(type, "int")) { - return true; - } else if (starts_with(type, "bool")) { - return true; - } else if (starts_with(type, "bytes")) { - return true; - } else if (starts_with(type, "string")) { - return true; - } - return false; -} - -std::string ParamFactory::getValue(const std::shared_ptr& param, const std::string& type) { - std::string result = ""; - if (isArrayType(type)) { - auto values = getArrayValue(param, type); - result = joinArrayElems(values); - } else if (type == "address") { - auto value = dynamic_pointer_cast(param); - result = hexEncoded(value->getData()); - } else if (type == "uint8") { - result = boost::lexical_cast((uint)dynamic_pointer_cast(param)->getVal()); - } else if (type == "uint16") { - result = boost::lexical_cast(dynamic_pointer_cast(param)->getVal()); - } else if (type == "uint32") { - result = boost::lexical_cast(dynamic_pointer_cast(param)->getVal()); - } else if (type == "uint64") { - result = boost::lexical_cast(dynamic_pointer_cast(param)->getVal()); - } else if (type == "uint256" || type == "uint") { - result = boost::lexical_cast(dynamic_pointer_cast(param)->getVal()); - } else if (type == "int8") { - result = boost::lexical_cast((int)dynamic_pointer_cast(param)->getVal()); - } else if (type == "int16") { - result = boost::lexical_cast(dynamic_pointer_cast(param)->getVal()); - } else if (type == "int32") { - result = boost::lexical_cast(dynamic_pointer_cast(param)->getVal()); - } else if (type == "int64") { - result = boost::lexical_cast(dynamic_pointer_cast(param)->getVal()); - } else if (type == "int256" || type == "int") { - result = boost::lexical_cast(dynamic_pointer_cast(param)->getVal()); - } else if (starts_with(type, "uint")) { - auto value = dynamic_pointer_cast(param); - result = boost::lexical_cast(value->getVal()); - } else if (starts_with(type, "int")) { - auto value = dynamic_pointer_cast(param); - result = boost::lexical_cast(value->getVal()); - } else if (type == "bool") { - auto value = dynamic_pointer_cast(param); - result = value->getVal() ? "true" : "false"; - } else if (type == "bytes") { - auto value = dynamic_pointer_cast(param); - result = hexEncoded(value->getVal()); - } else if (starts_with(type, "bytes")) { - auto value = dynamic_pointer_cast(param); - result = hexEncoded(value->getVal()); - } else if (type == "string") { - auto value = dynamic_pointer_cast(param); - result = value->getVal(); - } - return result; -} - -std::vector ParamFactory::getArrayValue(const std::shared_ptr& param, const std::string& type) { - if (!isArrayType(type)) { - return std::vector(); - } - auto array = dynamic_pointer_cast(param); - if (!array) { - return std::vector(); - } - auto elemType = getArrayElemType(type); - auto elems = array->getVal(); - std::vector values(elems.size()); - for (auto i = 0ul; i < elems.size(); ++i) { - values[i] = getValue(elems[i], elemType); - } - return values; -} - -} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/ParamFactory.h b/src/Ethereum/ABI/ParamFactory.h deleted file mode 100644 index abeb490ce05..00000000000 --- a/src/Ethereum/ABI/ParamFactory.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "Array.h" -#include "Bytes.h" -#include "ParamBase.h" -#include "ParamStruct.h" - -#include -#include - -#include "Wasm.h" - -namespace TW::Ethereum::ABI { - -/// Factory creates concrete ParamBase class from string type. -class ParamFactory { -public: - /// Create a param of given type - static std::shared_ptr make(const std::string& type); - /// Create a named param, with given name and type - static std::shared_ptr makeNamed(const std::string& name, const std::string& type); - /// Check if given type is a primitive type - static bool isPrimitive(const std::string& type); - - static std::string getValue(const std::shared_ptr& param, const std::string& type); - static std::vector getArrayValue(const std::shared_ptr& param, - const std::string& type); -}; - -} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/ParamNumber.cpp b/src/Ethereum/ABI/ParamNumber.cpp deleted file mode 100644 index 9def217a589..00000000000 --- a/src/Ethereum/ABI/ParamNumber.cpp +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "ParamNumber.h" - -#include -#include -#include - -#include - -#include -#include - -namespace TW::Ethereum::ABI { - -bool ParamUInt256::setUInt256FromValueJson(uint256_t& dest, const std::string& value) { - // try hex string or number - if (value.length() >= 3 && value.substr(0, 2) == "0x") { - dest = load(parse_hex(value)); - return true; - } - return boost::conversion::detail::try_lexical_convert(value, dest); -} - -bool ParamInt256::setInt256FromValueJson(int256_t& dest, const std::string& value) { - // try hex string or number - if (value.length() >= 3 && value.substr(0, 2) == "0x") { - dest = ValueEncoder::int256FromUint256(load(parse_hex(value))); - return true; - } - return boost::conversion::detail::try_lexical_convert(value, dest); -} - -bool ParamBool::setValueJson(const std::string& value) { - if (value == "true" || value == "1") { - setVal(true); - return true; - } - if (value == "false" || value == "0") { - setVal(false); - return true; - } - return false; -} - -bool ParamUInt8::setValueJson(const std::string& value) { - uint16_t val; - if (!boost::conversion::detail::try_lexical_convert(value, val)) { - return false; - } - setVal(static_cast(val)); - return true; -} - -bool ParamInt8::setValueJson(const std::string& value) { - int16_t val; - if (!boost::conversion::detail::try_lexical_convert(value, val)) { - return false; - } - setVal(static_cast(val)); - return true; -} - -void ParamUIntN::setVal(uint256_t val) { - // mask it to the given bits - _val = val & _mask; -} - -bool ParamUIntN::decode(const Data& encoded, size_t& offset_inout) { - uint256_t temp; - auto res = decodeNumber(encoded, temp, offset_inout); - setVal(temp); - return res; -} - -void ParamUIntN::init() { - _mask = maskForBits(bits); -} - -uint256_t ParamUIntN::maskForBits(size_t bits) { - assert(bits >= 8 && bits <= 256 && (bits % 8) == 0); - // exclude predefined sizes - assert(bits != 8 && bits != 16 && bits != 32 && bits != 64 && bits != 256); - return (uint256_t(1) << bits) - 1; -} - -void ParamIntN::setVal(int256_t val) { - // mask it to the given bits - if (val < 0) { - _val = ValueEncoder::int256FromUint256(~((~((uint256_t)val)) & _mask)); - } else { - _val = ValueEncoder::int256FromUint256(((uint256_t)val) & _mask); - } -} - -bool ParamIntN::decodeNumber(const Data& encoded, int256_t& decoded, size_t& offset_inout) { - uint256_t valU; - auto res = ABI::decode(encoded, valU, offset_inout); - decoded = ValueEncoder::int256FromUint256(valU); - return res; -} - -bool ParamIntN::decode(const Data& encoded, size_t& offset_inout) { - int256_t temp; - auto res = decodeNumber(encoded, temp, offset_inout); - setVal(temp); - return res; -} - -void ParamIntN::init() { - _mask = ParamUIntN::maskForBits(bits); -} - -} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/ParamNumber.h b/src/Ethereum/ABI/ParamNumber.h deleted file mode 100644 index a9b9ea6f631..00000000000 --- a/src/Ethereum/ABI/ParamNumber.h +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "ParamBase.h" -#include "ValueEncoder.h" - -#include -#include - -#include - -#include - -namespace TW::Ethereum::ABI { - - -inline bool decode(const Data& encoded, uint256_t& decoded, size_t& offset_inout) -{ - decoded = 0u; - if (encoded.empty() || (encoded.size() < (ValueEncoder::encodedIntSize + offset_inout))) { - return false; - } - decoded = loadWithOffset(encoded, offset_inout); - offset_inout += ValueEncoder::encodedIntSize; - return true; -} - -/// Generic parameter class for numeric types, like bool, uint32, int64, etc. All are stored on 256 bits. -template -class ParamNumberType : public ParamBase -{ -public: - ParamNumberType() = default; - explicit ParamNumberType(const T& val): _val(val) { } - void setVal(T val) { _val = val; } - T getVal() const { return _val; } - virtual std::string getType() const = 0; - virtual size_t getSize() const { return ValueEncoder::encodedIntSize; } - virtual bool isDynamic() const { return false; } - virtual void encode(Data& data) const { - // cast up - ValueEncoder::encodeUInt256(static_cast(_val), data); - } - static bool decodeNumber(const Data& encoded, T& decoded, size_t& offset_inout) { - uint256_t val256; - if (!ABI::decode(encoded, val256, offset_inout)) { return false; } - // cast down - decoded = static_cast(val256); - return true; - } - virtual bool decode(const Data& encoded, size_t& offset_inout) { - return decodeNumber(encoded, _val, offset_inout); - } - virtual bool setValueJson(const std::string& value) { - return boost::conversion::detail::try_lexical_convert(value, _val); - } -protected: - T _val; -}; - -class ParamUInt256 final : public ParamNumberType -{ -public: - ParamUInt256() : ParamNumberType(uint256_t(0)) {} - explicit ParamUInt256(const uint256_t& val) : ParamNumberType(val) {} - std::string getType() const override { return "uint256"; } - uint256_t getVal() const { return ParamNumberType::getVal(); } - bool setValueJson(const std::string& value) override { return setUInt256FromValueJson(_val, value); } - static bool setUInt256FromValueJson(uint256_t& dest, const std::string& value); - std::shared_ptr clone() const override { return std::make_shared(_val); } -}; - -class ParamInt256 final : public ParamNumberType -{ -public: - ParamInt256() : ParamNumberType(int256_t(0)) {} - explicit ParamInt256(const int256_t& val) : ParamNumberType(val) {} - std::string getType() const override { return "int256"; } - int256_t getVal() const { return ParamNumberType::getVal(); } - bool setValueJson(const std::string& value) override { return setInt256FromValueJson(_val, value); } - static bool setInt256FromValueJson(int256_t& dest, const std::string& value); - std::shared_ptr clone() const override { return std::make_shared(_val); } -}; - -class ParamBool final : public ParamNumberType -{ -public: - ParamBool() : ParamNumberType(false) {} - explicit ParamBool(bool val) : ParamNumberType(val) {} - std::string getType() const override { return "bool"; } - bool getVal() const { return ParamNumberType::getVal(); } - bool setValueJson(const std::string& value) override; - std::shared_ptr clone() const override { return std::make_shared(_val); } -}; - -class ParamUInt8 final : public ParamNumberType -{ -public: - ParamUInt8() : ParamNumberType(0) {} - explicit ParamUInt8(uint8_t val) : ParamNumberType(val) {} - std::string getType() const override { return "uint8"; } - bool setValueJson(const std::string& value) override; - std::shared_ptr clone() const override { return std::make_shared(_val); } -}; - -class ParamInt8 final : public ParamNumberType -{ -public: - ParamInt8() : ParamNumberType(0) {} - explicit ParamInt8(int8_t val) : ParamNumberType(val) {} - std::string getType() const override { return "int8"; } - bool setValueJson(const std::string& value) override; - std::shared_ptr clone() const override { return std::make_shared(_val); } -}; - -class ParamUInt16 final : public ParamNumberType -{ -public: - ParamUInt16() : ParamNumberType(0) {} - explicit ParamUInt16(uint16_t val) : ParamNumberType(val) {} - std::string getType() const override { return "uint16"; } - std::shared_ptr clone() const override { return std::make_shared(_val); } -}; - -class ParamInt16 final : public ParamNumberType -{ -public: - ParamInt16() : ParamNumberType(0) {} - explicit ParamInt16(int16_t val) : ParamNumberType(val) {} - std::string getType() const override { return "int16"; } - std::shared_ptr clone() const override { return std::make_shared(_val); } -}; - -class ParamUInt32 final : public ParamNumberType -{ -public: - ParamUInt32() : ParamNumberType(0) {} - explicit ParamUInt32(uint32_t val) : ParamNumberType(val) {} - std::string getType() const override { return "uint32"; } - std::shared_ptr clone() const override { return std::make_shared(_val); } -}; - -class ParamInt32 final : public ParamNumberType -{ -public: - ParamInt32() : ParamNumberType(0) {} - explicit ParamInt32(int32_t val) : ParamNumberType(val) {} - std::string getType() const override { return "int32"; } - std::shared_ptr clone() const override { return std::make_shared(_val); } -}; - -class ParamUInt64 final : public ParamNumberType -{ -public: - ParamUInt64() : ParamNumberType(0) {} - explicit ParamUInt64(uint64_t val) : ParamNumberType(val) {} - std::string getType() const override { return "uint64"; } - std::shared_ptr clone() const override { return std::make_shared(_val); } -}; - -class ParamInt64 final : public ParamNumberType -{ -public: - ParamInt64() : ParamNumberType(0) {} - explicit ParamInt64(int64_t val) : ParamNumberType(val) {} - std::string getType() const override { return "int64"; } - std::shared_ptr clone() const override { return std::make_shared(_val); } -}; - -/// Generic parameter class for all other bit sizes, like UInt24, 40, 48, ... 248. -/// For predefined sizes (8, 16, 32, 64, 256) use the sepcial types like UInt32. -/// Stored on 256 bits. -class ParamUIntN final : public ParamBase -{ -public: - const size_t bits; - ParamUIntN(size_t bits_in) : bits(bits_in) { init(); } - ParamUIntN(size_t bits_in, uint256_t val) : bits(bits_in) { init(); setVal(val); } - void setVal(uint256_t val); - uint256_t getVal() const { return _val; } - std::string getType() const override { return "uint" + std::to_string(bits); } - size_t getSize() const override { return ValueEncoder::encodedIntSize; } - bool isDynamic() const override { return false; } - void encode(Data& data) const override { ValueEncoder::encodeUInt256(_val, data); } - static bool decodeNumber(const Data& encoded, uint256_t& decoded, size_t& offset_inout) { - return ABI::decode(encoded, decoded, offset_inout); - } - bool decode(const Data& encoded, size_t& offset_inout) override; - static uint256_t maskForBits(size_t bits); - bool setValueJson(const std::string& value) override { return ParamUInt256::setUInt256FromValueJson(_val, value); } - std::shared_ptr clone() const override { return std::make_shared(bits, _val); } - -private: - void init(); - uint256_t _val; - uint256_t _mask; -}; - -/// Generic parameter class for all other bit sizes, like Int24, 40, 48, ... 248. -/// For predefined sizes (8, 16, 32, 64, 256) use the sepcial types like Int32. -/// Stored on 256 bits. -class ParamIntN final : public ParamBase -{ -public: - const size_t bits; - ParamIntN(size_t bits_in) : bits(bits_in) { init(); } - ParamIntN(size_t bits_in, int256_t val) : bits(bits_in) { init(); setVal(val); } - void setVal(int256_t val); - int256_t getVal() const { return _val; } - std::string getType() const override { return "int" + std::to_string(bits); } - size_t getSize() const override { return ValueEncoder::encodedIntSize; } - bool isDynamic() const override { return false; } - void encode(Data& data) const override { ValueEncoder::encodeUInt256((uint256_t)_val, data); } - static bool decodeNumber(const Data& encoded, int256_t& decoded, size_t& offset_inout); - bool decode(const Data& encoded, size_t& offset_inout) override; - bool setValueJson(const std::string& value) override { return ParamInt256::setInt256FromValueJson(_val, value); } - std::shared_ptr clone() const override { return std::make_shared(bits, _val); } - -private: - void init(); - int256_t _val; - uint256_t _mask; -}; - -} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/ParamStruct.cpp b/src/Ethereum/ABI/ParamStruct.cpp deleted file mode 100644 index 9d1c2690ad8..00000000000 --- a/src/Ethereum/ABI/ParamStruct.cpp +++ /dev/null @@ -1,390 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "ParamStruct.h" -#include "ValueEncoder.h" -#include "ParamFactory.h" -#include -#include - -#include -#include - -#include -#include - -namespace TW::Ethereum::ABI { - -using json = nlohmann::json; - -static const Data EipStructPrefix = parse_hex("1901"); -static const auto Eip712Domain = "EIP712Domain"; - -std::string ParamNamed::getType() const { - return _param->getType() + " " + _name; -} - -std::shared_ptr ParamNamed::cloneNamed() const { - return std::make_shared(_name, _param->clone()); -} - -ParamSetNamed::~ParamSetNamed() { - _params.clear(); -} - -/// Returns the index of the parameter -int ParamSetNamed::addParam(const std::shared_ptr& param) { - if (param.get() == nullptr) { - return -1; - } - assert(param.get() != nullptr); - _params.push_back(param); - return static_cast(_params.size() - 1); -} - -void ParamSetNamed::addParams(const std::vector>& params) { - for (auto p : params) { - addParam(p); - } -} - -std::string ParamSetNamed::getType() const { - std::string t = "("; - int cnt = 0; - for (auto p : _params) { - if (cnt++ > 0) { - t += ","; - } - t += p->getType(); - } - t += ")"; - return t; -} - -Data ParamSetNamed::encodeHashes() const { - Data hashes; - for (auto p : _params) { - append(hashes, p->hashStruct()); - } - return hashes; -} - -void ParamSetNamed::fillExtraTypesMap(ExtraTypesMap& extraTypes) const { - for (auto& p : _params) { - p->fillExtraTypesMap(extraTypes); - } -} - -std::shared_ptr ParamSetNamed::findParamByName(const std::string& name) const { - for (auto& p : _params) { - if (p->_name == name) { - return p; - } - } - return nullptr; -} - -ParamSetNamed ParamSetNamed::clone() const { - ParamSetNamed newSet; - for (const auto& p : _params) { - newSet.addParam(p->cloneNamed()); - } - return newSet; -} - -Data ParamStruct::hashType() const { - return Hash::keccak256(TW::data(encodeType())); -} - -Data ParamStruct::encodeHashes() const { - Data hashes; - Data paramsHashes = _params.encodeHashes(); - if (paramsHashes.size() > 0) { - auto fullType = encodeType(); - hashes = Hash::keccak256(TW::data(fullType)); - append(hashes, paramsHashes); - } - return hashes; -} - -Data ParamStruct::hashStruct() const { - Data hash(32); - Data hashes = encodeHashes(); - if (hashes.size() > 0) { - hash = Hash::keccak256(hashes); - } - return hash; -} - -std::string ParamStruct::encodeType() const { - ExtraTypesMap extraTypes; - fillExtraTypesMap(extraTypes); - auto structFullType = extraTypes[_name]; - extraTypes.erase(_name); - - for (const auto& [paramName, paramType] : extraTypes) { - structFullType += paramType; - } - return structFullType; -} - -void ParamStruct::fillExtraTypesMap(ExtraTypesMap& extraTypes) const { - if (extraTypes.contains(_name)) { - return; - } - extraTypes[_name] = _name + _params.getType(); - _params.fillExtraTypesMap(extraTypes); -} - -std::shared_ptr ParamStruct::clone() const { - return std::make_shared(_name, _params.clone()); -} - -Data ParamStruct::hashStructJson(const std::string& messageJson) { - auto message = json::parse(messageJson, nullptr, false); - if (message.is_discarded()) { - throw std::invalid_argument("Could not parse Json"); - } - if (!message.is_object()) { - throw std::invalid_argument("Expecting Json object"); - } - if (!message.contains("primaryType") || !message["primaryType"].is_string()) { - throw std::invalid_argument("Top-level string field 'primaryType' missing"); - } - if (!message.contains("domain") || !message["domain"].is_object()) { - throw std::invalid_argument("Top-level object field 'domain' missing"); - } - if (!message.contains("message") || !message["message"].is_object()) { - throw std::invalid_argument("Top-level object field 'message' missing"); - } - if (!message.contains("types") || !message["types"].is_object()) { - throw std::invalid_argument("Top-level object field 'types' missing"); - } - - // concatenate hashes - Data hashes = EipStructPrefix; - - auto domainStruct = makeStruct( - Eip712Domain, - message["domain"].dump(), - message["types"].dump()); - if (domainStruct) { - TW::append(hashes, domainStruct->hashStruct()); - - auto messageStruct = makeStruct( - message["primaryType"].get(), - message["message"].dump(), - message["types"].dump()); - if (messageStruct) { - const auto messageHash = messageStruct->hashStruct(); - TW::append(hashes, messageHash); - return Hash::keccak256(hashes); - } - } - return {}; // fallback -} - -std::shared_ptr findType(const std::string& typeName, const std::vector>& types) { - for (auto& t : types) { - if (t->getType() == typeName) { - return t; - } - } - return nullptr; -} - -std::shared_ptr ParamStruct::makeStruct(const std::string& structType, const std::string& valueJson, const std::string& typesJson) { - try { - // parse types - auto types = makeTypes(typesJson); - // find type info - auto typeInfo = findType(structType, types); - if (!typeInfo) { - throw std::invalid_argument("Type not found, " + structType); - } - auto values = json::parse(valueJson, nullptr, false); - if (values.is_discarded()) { - throw std::invalid_argument("Could not parse value Json"); - } - if (!values.is_object()) { - throw std::invalid_argument("Expecting object"); - } - std::vector> params; - const auto& typeParams = typeInfo->getParams(); - // iterate through the type; order is important and field order in the value json is not defined - for (auto i = 0ul; i < typeParams.getCount(); ++i) { - auto name = typeParams.getParam(static_cast(i))->getName(); - auto type = typeParams.getParam(static_cast(i))->getParam()->getType(); - // look for it in value (may throw) - auto value = values[name]; - // first try simple params - auto paramVal = ParamFactory::make(type); - if (paramVal) { - if (!values.is_null()) { - std::string valueString = value.is_string() ? value.get() : value.dump(); - if (!paramVal->setValueJson(valueString)) { - throw std::invalid_argument("Could not set type for param " + name); - } - } - params.push_back(std::make_shared(name, paramVal)); - } else if (type.length() >= 2 && type.substr(type.length() - 2, 2) == "[]") { - // array of struct - auto arrayType = type.substr(0, type.length() - 2); - auto subTypeInfo = findType(arrayType, types); - if (!subTypeInfo) { - throw std::invalid_argument("Could not find type for array sub-struct " + arrayType); - } - if (!value.is_array()) { - throw std::invalid_argument("Value must be array for type " + type); - } - std::vector> paramsArray; - if (value.size() == 0) { - // empty array - auto subStruct = makeStruct(arrayType, "{}", typesJson); - if (!subStruct) { - throw std::invalid_argument("Could not process array sub-struct " + arrayType + " " + "{}"); - } - assert(subStruct); - auto tmp = std::make_shared(paramsArray); - tmp->setProto(subStruct); - params.push_back(std::make_shared(name, tmp)); - } else { - for (const auto& e : value) { - auto subStruct = makeStruct(arrayType, e.dump(), typesJson); - if (!subStruct) { - throw std::invalid_argument("Could not process array sub-struct " + arrayType + " " + e.dump()); - } - assert(subStruct); - paramsArray.push_back(subStruct); - } - params.push_back(std::make_shared(name, std::make_shared(paramsArray))); - } - } else { - // try if sub struct - auto subTypeInfo = findType(type, types); - if (!subTypeInfo) { - throw std::invalid_argument("Could not find type for sub-struct " + type); - } - if (value.is_null()) { - params.push_back(std::make_shared(name, std::make_shared(type, std::vector>{}))); - } else { - auto subStruct = makeStruct(type, value.dump(), typesJson); - if (!subStruct) { - throw std::invalid_argument("Could not process sub-struct " + type); - } - assert(subStruct); - params.push_back(std::make_shared(name, subStruct)); - } - } - } - return std::make_shared(structType, params); - } catch (const std::invalid_argument& ex) { - throw; - } catch (const std::exception& ex) { - throw std::invalid_argument(std::string("Could not process Json: ") + ex.what()); - } catch (...) { - throw std::invalid_argument("Could not process Json"); - } -} - -std::shared_ptr ParamStruct::makeType(const std::string& structName, const std::string& structJson, const std::vector>& extraTypes, bool ignoreMissingType) { - try { - if (structName.empty()) { - throw std::invalid_argument("Missing type name"); - } - auto jsonValue = json::parse(structJson, nullptr, false); - if (jsonValue.is_discarded()) { - throw std::invalid_argument("Could not parse type Json"); - } - if (!jsonValue.is_array()) { - throw std::invalid_argument("Expecting array"); - } - std::vector> params; - for (auto& p2 : jsonValue) { - auto name = p2["name"].get(); - auto type = p2["type"].get(); - if (name.empty() || type.empty()) { - throw std::invalid_argument("Expecting 'name' and 'type', in " + structName); - } - auto named = ParamFactory::makeNamed(name, type); - if (named) { - // simple type (incl. array of simple type) - params.push_back(named); - } else if (type == structName) { - // recursive to self - params.push_back(std::make_shared(name, std::make_shared(type, std::vector>{}))); - } else if (type.length() >= 2 && type.substr(type.length() - 2, 2) == "[]") { - // array of struct - auto arrayType = type.substr(0, type.length() - 2); - if (ignoreMissingType) { - params.push_back(std::make_shared(name, std::make_shared(std::make_shared(arrayType, std::vector>{})))); - } else { - // try array struct from extra types - auto p2struct = findType(arrayType, extraTypes); - if (!p2struct) { - throw std::invalid_argument("Unknown struct array type " + arrayType); - } - params.push_back(std::make_shared(name, std::make_shared(p2struct))); - } - } else { - if (ignoreMissingType) { - params.push_back(std::make_shared(name, std::make_shared(type, std::vector>{}))); - } else { - // try struct from extra types - auto p2struct = findType(type, extraTypes); - if (!p2struct) { - throw std::invalid_argument("Unknown type " + type); - } - params.push_back(std::make_shared(name, p2struct)); - } - } - } - if (params.size() == 0) { - throw std::invalid_argument("No valid params found"); - } - return std::make_shared(structName, params); - } catch (const std::invalid_argument& ex) { - throw; - } catch (const std::exception& ex) { - throw std::invalid_argument(std::string("Could not process Json: ") + ex.what()); - } catch (...) { - throw std::invalid_argument("Could not process Json"); - } -} - -std::vector> ParamStruct::makeTypes(const std::string& structTypes) { - try { - auto jsonValue = json::parse(structTypes, nullptr, false); - if (jsonValue.is_discarded()) { - throw std::invalid_argument("Could not parse types Json"); - } - if (!jsonValue.is_object()) { - throw std::invalid_argument("Expecting object"); - } - // do it in 2 passes, as type order may be undefined - std::vector> types1; - for (json::iterator it = jsonValue.begin(); it != jsonValue.end(); it++) { - // may throw - auto struct1 = makeType(it.key(), it.value().dump(), {}, true); - types1.push_back(struct1); - } - std::vector> types2; - for (json::iterator it = jsonValue.begin(); it != jsonValue.end(); it++) { - // may throw - auto struct1 = makeType(it.key(), it.value().dump(), types1, false); - types2.push_back(struct1); - } - return types2; - } catch (const std::invalid_argument& ex) { - throw; - } catch (const std::exception& ex) { - throw std::invalid_argument(std::string("Could not process Json: ") + ex.what()); - } catch (...) { - throw std::invalid_argument("Could not process Json"); - } -} - -} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/ParamStruct.h b/src/Ethereum/ABI/ParamStruct.h deleted file mode 100644 index 0368029c776..00000000000 --- a/src/Ethereum/ABI/ParamStruct.h +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "ParamBase.h" - -#include -#include -#include - -namespace TW::Ethereum::ABI { - -/// A named parameter. -class ParamNamed final : public ParamBase -{ -public: - std::string _name; - std::shared_ptr _param; - -public: - explicit ParamNamed(const std::string& name, std::shared_ptr param): _name(name), _param(param) {} - - std::string getName() const { return _name; } - std::shared_ptr getParam() const { return _param; } - std::string getType() const override; - size_t getSize() const override { return _param->getSize(); } - bool isDynamic() const override { return _param->isDynamic(); } - void encode(Data& data) const override { return _param->encode(data); } - bool decode(const Data& encoded, size_t& offset_inout) override { return _param->decode(encoded, offset_inout); } - bool setValueJson(const std::string& value) override { return _param->setValueJson(value); } - Data hashStruct() const override { return _param->hashStruct(); } - void fillExtraTypesMap(ExtraTypesMap& extraTypes) const override { _param->fillExtraTypesMap(extraTypes); } - std::shared_ptr clone() const override { return cloneNamed(); } - std::shared_ptr cloneNamed() const; -}; - -/// A collection of named parameters. See also: ParamStruct -class ParamSetNamed { -private: - std::vector> _params; - -public: - ParamSetNamed() = default; - ParamSetNamed(const std::shared_ptr& param1) { addParam(param1); } - ParamSetNamed(const std::vector>& params) { addParams(params); } - virtual ~ParamSetNamed(); - - /// Returns the index of the parameter - int addParam(const std::shared_ptr& param); - void addParams(const std::vector>& params); - size_t getCount() const { return _params.size(); } - std::shared_ptr getParam(int idx) const { return _params[idx]; } - std::string getType() const; - Data encodeHashes() const; - void fillExtraTypesMap(ExtraTypesMap& extraTypes) const; - std::shared_ptr findParamByName(const std::string& name) const; - ParamSetNamed clone() const; -}; - -/// A named structure (set of parameters plus a type name). -class ParamStruct final : public ParamCollection -{ -private: - std::string _name; - ParamSetNamed _params; - -public: - ParamStruct() = default; - ParamStruct(const std::string& name, const std::vector>& params) : ParamCollection(), _name(name), _params(ParamSetNamed(params)) {} - ParamStruct(const std::string& name, ParamSetNamed&& params) : ParamCollection(), _name(name), _params(std::move(params)) {} - - std::string getType() const override { return _name; } - const ParamSetNamed& getParams() const { return _params; } - - /// Compute the hash of a struct, used for signing, according to EIP712 - Data hashStruct() const override; - /// Get full type, extended by used sub-types, of the form 'Mail(Person from,Person to,string contents)Person(string name,address wallet)' - std::string encodeType() const; - /// Get the hash of the full type. - Data hashType() const; - - size_t getSize() const override { return _params.getCount(); } - bool isDynamic() const override { return true; } - size_t getCount() const override { return _params.getCount(); } - void encode([[maybe_unused]] Data& data) const override {} - bool decode([[maybe_unused]] const Data& encoded, [[maybe_unused]] size_t& offset_inout) override { return true; } - bool setValueJson([[maybe_unused]] const std::string& value) override { return false; } // see makeStruct - Data encodeHashes() const; - void fillExtraTypesMap(ExtraTypesMap& extraTypes) const override; - std::shared_ptr clone() const override; - std::shared_ptr findParamByName(const std::string& name) const { return _params.findParamByName(name); } - - /// Compute the hash of a struct, used for signing, according to EIP712 ("v4"). - /// Input is a Json object (as string), with following fields: - /// - types: map of used struct types (see makeTypes()) - /// - primaryType: the type of the message (string) - /// - domain: EIP712 domain specifier values - /// - message: the message (object). - /// Throws on error. - /// Example input: - /// R"({ - /// "types": { - /// "EIP712Domain": [ - /// {"name": "name", "type": "string"}, - /// {"name": "version", "type": "string"}, - /// {"name": "chainId", "type": "uint256"}, - /// {"name": "verifyingContract", "type": "address"} - /// ], - /// "Person": [ - /// {"name": "name", "type": "string"}, - /// {"name": "wallet", "type": "address"} - /// ] - /// }, - /// "primaryType": "Person", - /// "domain": { - /// "name": "Ether Person", - /// "version": "1", - /// "chainId": 1, - /// "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - /// }, - /// "message": { - /// "name": "Cow", - /// "wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - /// } - /// })"); - static Data hashStructJson(const std::string& messageJson); - - /// Make a named struct, described by a json string (with values), and its type info (may contain type info of sub-types also). - /// Throws on error. - static std::shared_ptr makeStruct(const std::string& structType, const std::string& valueJson, const std::string& typesJson); - - /// Parse a json with a list of types, and build a vector of named structs. Structs params have the given name and type, and empty value. - /// Ex. input: R"({"Person": [{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}], "Mail": [{"name": "from", "type": "Person"}, {"name": "to", "type": "Person"}, {"name": "contents", "type": "string"}]})" - /// Order does not matter. Note the quote delimiters. - /// Throws on error. - static std::vector> makeTypes(const std::string& structTypes); - - /// Make a named struct, with the given types, with empty values. - /// Similar to makeTypes, but works with only one type. - /// Ex. input: "Person", R"([{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}])" - /// Throws on error. - static std::shared_ptr makeType(const std::string& structName, const std::string& structJson, const std::vector>& extraTypes = {}, bool ignoreMissingType = false); -}; - -} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/Parameters.cpp b/src/Ethereum/ABI/Parameters.cpp deleted file mode 100644 index 973443fa540..00000000000 --- a/src/Ethereum/ABI/Parameters.cpp +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Parameters.h" -#include "ValueEncoder.h" -#include - -#include -#include - -namespace TW::Ethereum::ABI { - -ParamSet::~ParamSet() { - _params.clear(); -} - -/// Returns the index of the parameter -int ParamSet::addParam(const std::shared_ptr& param) { - if (param.get() == nullptr) { - return -1; - } - assert(param.get() != nullptr); - _params.push_back(param); - return static_cast(_params.size() - 1); -} - -void ParamSet::addParams(const std::vector>& params) { - for (auto p : params) { - addParam(p); - } -} - -bool ParamSet::getParam(size_t paramIndex, std::shared_ptr& param_out) const { - if (paramIndex >= _params.size() || paramIndex < 0) { - return false; - } - param_out = _params[paramIndex]; - return true; -} - -std::shared_ptr ParamSet::getParamUnsafe(size_t paramIndex) const { - if (_params.size() == 0) { - // zero parameter, nothing to return. This may cause trouble (segfault) - return nullptr; - } - if (paramIndex >= _params.size() || paramIndex < 0) { - // invalid index, return the first instead of nullptr - return _params[0]; - } - return _params[paramIndex]; -} - -/// Return the function type signature, of the form "baz(int32,uint256)" -std::string ParamSet::getType() const { - std::string t = "("; - int cnt = 0; - for (auto p : _params) { - if (cnt++ > 0) { - t += ","; - } - t += p->getType(); - } - t += ")"; - return t; -} - -bool ParamSet::isDynamic() const { - for (const auto& p : _params) { - if (p->isDynamic()) { - return true; - } - } - return false; -} - -size_t ParamSet::getSize() const { - // 2-pass encoding - size_t s = 0; - for (auto p : _params) { - if (p->isDynamic() || p->getSize() > ValueEncoder::encodedIntSize) { - // offset used - s += 32; - } - s += p->getSize(); - } - return ValueEncoder::paddedTo32(s); -} - -size_t ParamSet::getHeadSize() const { - size_t s = 0; - for (auto p : _params) { - if (p->isDynamic()) { - s += 32; - } else { - s += p->getSize(); - } - } - return s; -} - -void ParamSet::encode(Data& data) const { - // 2-pass encoding - size_t headSize = getHeadSize(); - size_t dynamicOffset = 0; - - // pass 1: small values or indices - for (auto p : _params) { - if (p->isDynamic() || p->getSize() > ValueEncoder::encodedIntSize) { - // include only offset - ValueEncoder::encodeUInt256(uint256_t(headSize + dynamicOffset), data); - dynamicOffset += p->getSize(); - } else { - // encode small data - p->encode(data); - } - } - - // pass 2: dynamic values - for (auto p : _params) { - if (p->isDynamic() || p->getSize() > ValueEncoder::encodedIntSize) { - // encode large data - p->encode(data); - } - } -} - -bool ParamSet::decode(const Data& encoded, size_t& offset_inout) { - size_t arrayOffset = offset_inout; - - for (const auto& p : _params) { - // Decode a dynamic element. - if (p->isDynamic()) { - uint256_t index; - if (!ABI::decode(encoded, index, offset_inout)) { - return false; - } - - // Check if length is in the size_t range. - auto indexSize = static_cast(index); - if (indexSize != index) { - return false; - } - - // Calculate an offset relative to the beginning of this array. - size_t newOffset = arrayOffset + indexSize; - if (!p->decode(encoded, newOffset)) { - return false; - } - - continue; - } - - // Otherwise decode a static element, e.g `p->isDynamic() == false`. - if (!p->decode(encoded, offset_inout)) { - return false; - } - } - - return true; -} - -Data ParamSet::encodeHashes() const { - Data hashes; - for (auto p : _params) { - append(hashes, p->hashStruct()); - } - return hashes; -} - -ParamSet ParamSet::clone() const { - ParamSet newSet; - for (const auto& p : _params) { - newSet.addParam(p->clone()); - } - - return newSet; -} - -Data Parameters::hashStruct() const { - Data hash(32); - Data hashes = _params.encodeHashes(); - if (hashes.size() > 0) { - hash = Hash::keccak256(hashes); - } - return hash; -} - -std::shared_ptr Parameters::clone() const { - auto newParams = std::make_shared(); - newParams->_params = _params.clone(); - return newParams; -} - -} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/Parameters.h b/src/Ethereum/ABI/Parameters.h deleted file mode 100644 index cc6d0844cd7..00000000000 --- a/src/Ethereum/ABI/Parameters.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "ParamBase.h" -#include "ParamNumber.h" - -#include -#include -#include - -namespace TW::Ethereum::ABI { - -/// A set of parameters -class ParamSet { -private: - std::vector> _params; - -public: - ParamSet() = default; - ParamSet(const std::shared_ptr& param1) { addParam(param1); } - ParamSet(const std::vector>& params) { addParams(params); } - virtual ~ParamSet(); - - /// Returns the index of the parameter - int addParam(const std::shared_ptr& param); - void addParams(const std::vector>& params); - bool getParam(size_t paramIndex, std::shared_ptr& param_out) const; - std::shared_ptr getParamUnsafe(size_t paramIndex) const; - size_t getCount() const { return _params.size(); } - std::vector> const& getParams() const { return _params; } - /// Return the function type signature, of the form "baz(int32,uint256)" - std::string getType() const; - bool isDynamic() const; - size_t getSize() const; - virtual void encode(Data& data) const; - virtual bool decode(const Data& encoded, size_t& offset_inout); - Data encodeHashes() const; - /// Creates a copy of this set with exactly the same params. - ParamSet clone() const; - -private: - size_t getHeadSize() const; -}; - -/// Collection of different parameters, dynamic length, "(,,...)". -class Parameters final : public ParamCollection -{ -private: - ParamSet _params; - -public: - Parameters() = default; - Parameters(const std::vector>& params) : ParamCollection(), _params(ParamSet(params)) {} - void addParam(const std::shared_ptr& param) { _params.addParam(param); } - void addParams(const std::vector>& params) { _params.addParams(params); } - std::shared_ptr getParam(size_t paramIndex) const { return _params.getParamUnsafe(paramIndex); } - std::string getType() const override { return _params.getType(); } - size_t getSize() const override { return _params.getSize(); } - bool isDynamic() const override { return true; } - size_t getCount() const override { return _params.getCount(); } - void encode(Data& data) const override { _params.encode(data); } - bool decode(const Data& encoded, size_t& offset_inout) override { return _params.decode(encoded, offset_inout); } - bool setValueJson([[maybe_unused]] const std::string& value) override { return false; } - Data hashStruct() const override; - std::shared_ptr clone() const override; -}; - -} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/ProtoParam.h b/src/Ethereum/ABI/ProtoParam.h new file mode 100644 index 00000000000..30acb10abc4 --- /dev/null +++ b/src/Ethereum/ABI/ProtoParam.h @@ -0,0 +1,115 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "proto/EthereumAbi.pb.h" +#include "uint256.h" + +namespace TW::Ethereum::ABI { + +namespace AbiProto = EthereumAbi::Proto; + +struct BaseProtoParam { + virtual ~BaseProtoParam() noexcept = default; + + virtual AbiProto::Token toToken() const = 0; +}; + +using BaseParams = std::vector>; + +class ProtoBool final: public BaseProtoParam { +public: + explicit ProtoBool(bool val): m_value(val) { + } + + ~ProtoBool() override = default; + + AbiProto::Token toToken() const override { + AbiProto::Token proto; + proto.set_boolean(m_value); + return proto; + } + +private: + bool m_value = false; +}; + +class ProtoUInt256 final: public BaseProtoParam { +public: + explicit ProtoUInt256(const uint256_t &num): m_number(store(num)) { + } + + explicit ProtoUInt256(Data numData): m_number(std::move(numData)) { + } + + ~ProtoUInt256() override = default; + + AbiProto::Token toToken() const override { + AbiProto::Token proto; + proto.mutable_number_uint()->set_bits(256); + proto.mutable_number_uint()->set_value(m_number.data(), m_number.size()); + return proto; + } + +private: + Data m_number; +}; + +class ProtoByteArray final: public BaseProtoParam { +public: + explicit ProtoByteArray(Data data): m_data(std::move(data)) { + } + + ~ProtoByteArray() override = default; + + AbiProto::Token toToken() const override { + AbiProto::Token proto; + proto.set_byte_array(m_data.data(), m_data.size()); + return proto; + } + +private: + Data m_data; +}; + +class ProtoString final: public BaseProtoParam { +public: + explicit ProtoString(std::string str): m_string(std::move(str)) { + } + + ~ProtoString() override = default; + + AbiProto::Token toToken() const override { + AbiProto::Token proto; + proto.set_string_value(m_string.data(), m_string.size()); + return proto; + } + +private: + std::string m_string; +}; + +class ProtoAddress final: public BaseProtoParam { +public: + ProtoAddress() = default; + + explicit ProtoAddress(std::string addr): m_address(std::move(addr)) { + } + + ~ProtoAddress() override = default; + + AbiProto::Token toToken() const override { + AbiProto::Token proto; + proto.set_address(m_address); + return proto; + } + +private: + std::string m_address {"0x0000000000000000000000000000000000000000"}; +}; + +} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/Tuple.cpp b/src/Ethereum/ABI/Tuple.cpp deleted file mode 100644 index 1f661b4aeca..00000000000 --- a/src/Ethereum/ABI/Tuple.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Tuple.h" - -#include - -namespace TW::Ethereum::ABI { - -int ParamTuple::addParam(std::shared_ptr param) { - return _params.addParam(param); -} - -std::shared_ptr ParamTuple::clone() const { - auto newTuple = std::make_shared(); - newTuple->_params = _params.clone(); - return newTuple; -} - -} // namespace TW::Ethereum::ABI \ No newline at end of file diff --git a/src/Ethereum/ABI/Tuple.h b/src/Ethereum/ABI/Tuple.h deleted file mode 100644 index 2911703987e..00000000000 --- a/src/Ethereum/ABI/Tuple.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "ParamBase.h" -#include "Parameters.h" -#include "Data.h" - -namespace TW::Ethereum::ABI { - -/// A Tuple is a collection of parameters -class ParamTuple final : public ParamCollection -{ -public: - ParamSet _params; - - ParamTuple() = default; - explicit ParamTuple(const std::vector>& params) : _params(ParamSet(params)) {} - - /// Add a parameter. Returns the index of the parameter. - int addParam(std::shared_ptr param); - /// Get a parameter. - bool getParam(int paramIndex, std::shared_ptr& param_out) { - return _params.getParam(paramIndex, param_out); - } - /// Return the type signature, of the form "(int32,uint256)" - std::string getType() const override { return _params.getType(); } - - size_t getSize() const override { return _params.getSize(); } - bool isDynamic() const override { return _params.isDynamic(); } - void encode(Data& data) const override { return _params.encode(data); } - bool decode(const Data& encoded, size_t& offset_inout) override { return _params.decode(encoded, offset_inout); } - bool setValueJson([[maybe_unused]] const std::string& value) override { return false; } - size_t getCount() const override { return _params.getCount(); } - std::shared_ptr clone() const override; -}; - -} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/ValueDecoder.cpp b/src/Ethereum/ABI/ValueDecoder.cpp index 86544101070..ac46303ce05 100644 --- a/src/Ethereum/ABI/ValueDecoder.cpp +++ b/src/Ethereum/ABI/ValueDecoder.cpp @@ -5,8 +5,9 @@ // file LICENSE at the root of the source code distribution tree. #include "ValueDecoder.h" -#include "Array.h" -#include "ParamFactory.h" +#include "proto/EthereumAbi.pb.h" +#include "rust/Wrapper.h" +#include "TrustWalletCore/TWCoinType.h" namespace TW::Ethereum::ABI { @@ -17,29 +18,27 @@ uint256_t ValueDecoder::decodeUInt256(const Data& data) { return load(data); } -std::string ValueDecoder::decodeValue(const Data& data, const std::string& type) { - auto param = ParamFactory::make(type); - if (!param) { +std::string ValueDecoder::decodeValue(const Data& encoded, const std::string& type) { + EthereumAbi::Proto::ValueDecodingInput input; + input.set_encoded(encoded.data(), encoded.size()); + input.set_param_type(type); + + Rust::TWDataWrapper inputData(data(input.SerializeAsString())); + Rust::TWDataWrapper outputPtr = Rust::tw_ethereum_abi_decode_value(TWCoinTypeEthereum, inputData.get()); + + auto outputData = outputPtr.toDataOrDefault(); + if (outputData.empty()) { return ""; } - size_t offset = 0; - if (!param->decode(data, offset)) { + + EthereumAbi::Proto::ValueDecodingOutput output; + output.ParseFromArray(outputData.data(), static_cast(outputData.size())); + + if (output.error() != EthereumAbi::Proto::AbiError::OK) { return ""; } - return ParamFactory::getValue(param, param->getType()); -} -std::vector ValueDecoder::decodeArray(const Data& data, const std::string& type) { - auto param = ParamFactory::make(type); - if (!param) { - return std::vector{}; - } - size_t offset = 0; - if (!param->decode(data, offset)) { - return std::vector{}; - } - auto values = ParamFactory::getArrayValue(param, type); - return values; + return output.param_str(); } } // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/ValueDecoder.h b/src/Ethereum/ABI/ValueDecoder.h index aee43f8655a..049bda9231a 100644 --- a/src/Ethereum/ABI/ValueDecoder.h +++ b/src/Ethereum/ABI/ValueDecoder.h @@ -19,7 +19,5 @@ class ValueDecoder { static uint256_t decodeUInt256(const Data& data); // Decode an arbitrary type, return value as string static std::string decodeValue(const Data& data, const std::string& type); - // Decode an array of given simple types; return each element as a string in a vector - static std::vector decodeArray(const Data& data, const std::string& elementType); }; } // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/Barz.cpp b/src/Ethereum/Barz.cpp index e6cc3e92c56..d3fb86ba45a 100644 --- a/src/Ethereum/Barz.cpp +++ b/src/Ethereum/Barz.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "ABI.h" +#include "ABI/Function.h" #include "AddressChecksum.h" #include "EIP1014.h" #include "Hash.h" @@ -15,23 +15,21 @@ namespace TW::Barz { -using ParamBasePtr = std::shared_ptr; -using ParamCollection = std::vector; - std::string getCounterfactualAddress(const Proto::ContractAddressInput input) { - auto params = Ethereum::ABI::ParamTuple(); - params.addParam(std::make_shared(parse_hex(input.account_facet()))); - params.addParam(std::make_shared(parse_hex(input.verification_facet()))); - params.addParam(std::make_shared(parse_hex(input.entry_point()))); - params.addParam(std::make_shared(parse_hex(input.facet_registry()))); - params.addParam(std::make_shared(parse_hex(input.default_fallback()))); - params.addParam(std::make_shared(parse_hex(input.public_key()))); - - Data encoded; - params.encode(encoded); + auto encodedData = Ethereum::ABI::Function::encodeParams(Ethereum::ABI::BaseParams { + std::make_shared(input.account_facet()), + std::make_shared(input.verification_facet()), + std::make_shared(input.entry_point()), + std::make_shared(input.facet_registry()), + std::make_shared(input.default_fallback()), + std::make_shared(parse_hex(input.public_key())), + }); + if (!encodedData.has_value()) { + return {}; + } Data initCode = parse_hex(input.bytecode()); - append(initCode, encoded); + append(initCode, encodedData.value()); const Data initCodeHash = Hash::keccak256(initCode); Data salt = store(input.salt(), 32); @@ -39,16 +37,18 @@ std::string getCounterfactualAddress(const Proto::ContractAddressInput input) { } Data getInitCode(const std::string& factoryAddress, const PublicKey& publicKey, const std::string& verificationFacet, const uint32_t salt) { - auto createAccountFunc = Ethereum::ABI::Function("createAccount", ParamCollection{ - std::make_shared(parse_hex(verificationFacet)), - std::make_shared(publicKey.bytes), - std::make_shared(salt)}); - Data createAccountFuncEncoded; - createAccountFunc.encode(createAccountFuncEncoded); + auto createAccountFuncEncoded = Ethereum::ABI::Function::encodeFunctionCall("createAccount", Ethereum::ABI::BaseParams { + std::make_shared(verificationFacet), + std::make_shared(publicKey.bytes), + std::make_shared(salt), + }); + if (!createAccountFuncEncoded.has_value()) { + return {}; + } Data envelope; append(envelope, parse_hex(factoryAddress)); - append(envelope, createAccountFuncEncoded); + append(envelope, createAccountFuncEncoded.value()); return envelope; } @@ -67,22 +67,24 @@ Data getFormattedSignature(const Data& signature, const Data challenge, const Da const auto parsedSignatureOptional = ASN::AsnParser::ecdsa_signature_from_der(signature); if (!parsedSignatureOptional.has_value()) { - return Data(); + return {}; } const Data parsedSignature = parsedSignatureOptional.value(); const Data rValue = subData(parsedSignature, 0, 32); const Data sValue = subData(parsedSignature, 32, 64); - auto params = Ethereum::ABI::ParamTuple(); - params.addParam(std::make_shared(uint256_t(hexEncoded(rValue)))); - params.addParam(std::make_shared(uint256_t(hexEncoded(sValue)))); - params.addParam(std::make_shared(authenticatorData)); - params.addParam(std::make_shared(clientDataJSONPre)); - params.addParam(std::make_shared(clientDataJSONPost)); + auto encoded = Ethereum::ABI::Function::encodeParams(Ethereum::ABI::BaseParams { + std::make_shared(rValue), + std::make_shared(sValue), + std::make_shared(authenticatorData), + std::make_shared(clientDataJSONPre), + std::make_shared(clientDataJSONPost), + }); - Data encoded; - params.encode(encoded); - return encoded; + if (encoded.has_value()) { + return encoded.value(); + } + return {}; } } // namespace TW::Barz diff --git a/src/Ethereum/ContractCall.cpp b/src/Ethereum/ContractCall.cpp index f2850c21dc4..d8d996c2deb 100644 --- a/src/Ethereum/ContractCall.cpp +++ b/src/Ethereum/ContractCall.cpp @@ -5,160 +5,36 @@ // file LICENSE at the root of the source code distribution tree. #include "ContractCall.h" -#include "ABI.h" #include "HexCoding.h" -#include "uint256.h" -#include +#include "proto/EthereumAbi.pb.h" +#include "TrustWalletCore/TWCoinType.h" using namespace std; using json = nlohmann::json; namespace TW::Ethereum::ABI { -static void fillArray(ParamSet& paramSet, const string& type) { - auto baseType = string(type.begin(), type.end() - 2); - auto value = ParamFactory::make(baseType); - auto param = make_shared(value); - paramSet.addParam(param); -} - -static void fill(ParamSet& paramSet, const string& type) { - if (boost::algorithm::ends_with(type, "[]")) { - fillArray(paramSet, type); - } else { - auto param = ParamFactory::make(type); - paramSet.addParam(param); - } -} - -static vector getArrayValue(const ParamSet& paramSet, const string& type, int idx) { - shared_ptr param; - paramSet.getParam(idx, param); - return ParamFactory::getArrayValue(param, type); -} - -static json buildInputs(const ParamSet& paramSet, const json& registry); // forward - -static json getTupleValue(const ParamSet& paramSet, [[maybe_unused]] const string& type, int idx, const json& typeInfo) { - shared_ptr param; - paramSet.getParam(idx, param); - auto paramTuple = dynamic_pointer_cast(param); - if (!paramTuple.get()) { - return {}; - } - return buildInputs(paramTuple->_params, typeInfo["components"]); -} - -static vector getArrayValueTuple(const ParamSet& paramSet, int idx, const json& typeInfo) { - shared_ptr param; - paramSet.getParam(idx, param); - - auto array = dynamic_pointer_cast(param); - if (!array) { - return {}; - } - - std::vector values; - for (const auto& tuple : array->getVal()) { - if (auto paramTuple = dynamic_pointer_cast(tuple); paramTuple) { - values.emplace_back(buildInputs(paramTuple->_params, typeInfo["components"])); - } - } - - return values; -} - -static string getValue(const ParamSet& paramSet, const string& type, int idx) { - shared_ptr param; - paramSet.getParam(idx, param); - return ParamFactory::getValue(param, type); -} - -static json buildInputs(const ParamSet& paramSet, const json& registry) { - auto inputs = json::array(); - for (auto i = 0ul; i < registry.size(); i++) { - auto info = registry[i]; - auto type = info["type"]; - auto input = json{ - {"name", info["name"]}, - {"type", type} - }; - if (type == "tuple[]") { - input["components"] = getArrayValueTuple(paramSet, static_cast(i), info); - } else if (boost::algorithm::ends_with(type.get(), "[]")) { - input["value"] = json(getArrayValue(paramSet, type, static_cast(i))); - } else if (type == "tuple") { - input["components"] = getTupleValue(paramSet, type, static_cast(i), info); - } else if (type == "bool") { - input["value"] = getValue(paramSet, type, static_cast(i)) == "true" ? json(true) : json(false); - } else { - input["value"] = getValue(paramSet, type, static_cast(i)); - } - inputs.push_back(input); - } - return inputs; -} - -void fillTuple(ParamSet& paramSet, const json& jsonSet); // forward - -void fillTupleArray(ParamSet& paramSet, const json& jsonSet); // forward - -void decodeParamSet(ParamSet& paramSet, const json& jsonSet) { - for (auto& comp : jsonSet) { - if (comp["type"] == "tuple") { - fillTuple(paramSet, comp["components"]); - } else if (comp["type"] == "tuple[]") { - fillTupleArray(paramSet, comp["components"]); - } else { - fill(paramSet, comp["type"]); - } - } -} - -void fillTuple(ParamSet& paramSet, const json& jsonSet) { - std::shared_ptr param = make_shared(); - decodeParamSet(param->_params, jsonSet); - paramSet.addParam(param); -} - -void fillTupleArray(ParamSet& paramSet, const json& jsonSet) { - std::shared_ptr tuple = make_shared(); - decodeParamSet(tuple->_params, jsonSet); - - auto tupleArray = make_shared(tuple); - paramSet.addParam(tupleArray); -} - -optional decodeCall(const Data& call, const json& abi) { - // check bytes length - if (call.size() <= 4) { - return {}; - } +optional decodeCall(const Data& call, const std::string& abi) { + EthereumAbi::Proto::ContractCallDecodingInput input; + input.set_encoded(call.data(), call.size()); + input.set_smart_contract_abi_json(abi); - auto methodId = hex(Data(call.begin(), call.begin() + 4)); + Rust::TWDataWrapper inputData(data(input.SerializeAsString())); + Rust::TWDataWrapper outputPtr = Rust::tw_ethereum_abi_decode_contract_call(TWCoinTypeEthereum, inputData.get()); - if (abi.find(methodId) == abi.end()) { + auto outputData = outputPtr.toDataOrDefault(); + if (outputData.empty()) { return {}; } - // build Function with types - const auto registry = abi[methodId]; - auto func = Function(registry["name"]); - decodeParamSet(func._inParams, registry["inputs"]); + EthereumAbi::Proto::ContractCallDecodingOutput output; + output.ParseFromArray(outputData.data(), static_cast(outputData.size())); - // decode inputs - size_t offset = 0; - auto success = func.decodeInput(call, offset); - if (!success) { + if (output.error() != EthereumAbi::Proto::AbiError::OK) { return {}; } - // build output json - auto decoded = json{ - {"function", func.getType()}, - {"inputs", buildInputs(func._inParams, registry["inputs"])}, - }; - return decoded.dump(); + return output.decoded_json(); } } // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ContractCall.h b/src/Ethereum/ContractCall.h index d07f11712aa..0060abfddba 100644 --- a/src/Ethereum/ContractCall.h +++ b/src/Ethereum/ContractCall.h @@ -12,5 +12,5 @@ #include namespace TW::Ethereum::ABI { - std::optional decodeCall(const Data& call, const nlohmann::json& abi); + std::optional decodeCall(const Data& call, const std::string& abi); } // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/EIP1967.cpp b/src/Ethereum/EIP1967.cpp index 55a6907d8f1..58106977ebe 100644 --- a/src/Ethereum/EIP1967.cpp +++ b/src/Ethereum/EIP1967.cpp @@ -5,11 +5,9 @@ // file LICENSE at the root of the source code distribution tree. #include "EIP1014.h" -#include "AddressChecksum.h" +#include "ABI/Function.h" #include "Hash.h" #include "HexCoding.h" -#include -#include "ABI.h" namespace TW::Ethereum { @@ -18,13 +16,16 @@ static const std::string eip1967ProxyBytecodeHex = R"(0x608060405260405162000c51 Data getEIP1967ProxyInitCode(const std::string& logicAddress, const Data& data) { Data initCode = parse_hex(eip1967ProxyBytecodeHex); - auto proxyConstructorParams = ABI::ParamTuple(); - proxyConstructorParams.addParam(std::make_shared(parse_hex(logicAddress))); - proxyConstructorParams.addParam(std::make_shared(data)); - Data proxyConstructorParamsEncoded; - proxyConstructorParams.encode(proxyConstructorParamsEncoded); + auto proxyConstructorParamsEncoded = Ethereum::ABI::Function::encodeParams(Ethereum::ABI::BaseParams { + std::make_shared(logicAddress), + std::make_shared(data), + }); - append(initCode, proxyConstructorParamsEncoded); + if (!proxyConstructorParamsEncoded.has_value()) { + return {}; + } + + append(initCode, proxyConstructorParamsEncoded.value()); return initCode; } diff --git a/src/Ethereum/ERC4337.cpp b/src/Ethereum/ERC4337.cpp deleted file mode 100644 index 6adad8f6739..00000000000 --- a/src/Ethereum/ERC4337.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "ABI.h" -#include "AddressChecksum.h" -#include "EIP1014.h" -#include "EIP1967.h" -#include "Hash.h" -#include "HexCoding.h" -#include - -namespace TW::Ethereum { - -using ParamBasePtr = std::shared_ptr; -using ParamCollection = std::vector; - -// https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/samples/SimpleAccount.sol#L57 -Data getERC4337ExecuteBytecode(const Data& toAddress, const uint256_t& value, const Data& data) { - auto executeFunc = ABI::Function("execute", ParamCollection{ - std::make_shared(toAddress), - std::make_shared(value), - std::make_shared(data)}); - Data executeFuncEncoded; - executeFunc.encode(executeFuncEncoded); - return executeFuncEncoded; -} - -// https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/samples/SimpleAccount.sol#L65 -Data getERC4337ExecuteBatchBytecode(const std::vector& toAddresses, const std::vector& amounts, const std::vector& payloads) { - auto addressesParam = ABI::ParamArray(); - for (const auto& toAddress: toAddresses) { - addressesParam.addParam(std::make_shared(toAddress)); - } - auto valuesParam = ABI::ParamArray(); - for (const auto& amount: amounts) { - valuesParam.addParam(std::make_shared(amount)); - } - auto payloadsParam = ABI::ParamArray(); - for (const auto& payload: payloads) { - payloadsParam.addParam(std::make_shared(payload)); - } - auto executeFunc = ABI::Function("executeBatch", ParamCollection{ - std::make_shared(addressesParam), - std::make_shared(valuesParam), - std::make_shared(payloadsParam)}); - Data executeFuncEncoded; - executeFunc.encode(executeFuncEncoded); - return executeFuncEncoded; -} - - -} // namespace TW::Ethereum diff --git a/src/Ethereum/ERC4337.h b/src/Ethereum/ERC4337.h deleted file mode 100644 index cabf114c675..00000000000 --- a/src/Ethereum/ERC4337.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "Data.h" -#include "uint256.h" - -namespace TW::Ethereum { - -Data getERC4337ExecuteBytecode(const Data& toAddress, const uint256_t& value, const Data& data); -Data getERC4337ExecuteBatchBytecode(const std::vector& toAddresses, const std::vector& values, const std::vector& payloads); - -} diff --git a/src/Ethereum/Entry.cpp b/src/Ethereum/Entry.cpp index 901ec3fa8a5..85c679900c4 100644 --- a/src/Ethereum/Entry.cpp +++ b/src/Ethereum/Entry.cpp @@ -7,90 +7,61 @@ #include "Entry.h" #include "Address.h" -#include "proto/Common.pb.h" -#include "proto/TransactionCompiler.pb.h" -#include "Signer.h" +#include "HexCoding.h" +#include "proto/Ethereum.pb.h" -#include "proto/TransactionCompiler.pb.h" +#include namespace TW::Ethereum { using namespace std; -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - return Address::isValid(address); +bool Entry::validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const { + return validateAddressRust(coin, address, addressPrefix); } -string Entry::normalizeAddress([[maybe_unused]] TWCoinType coin, const string& address) const { - // normalized with EIP55 checksum - return Address(address).string(); +string Entry::normalizeAddress(TWCoinType coin, const string& address) const { + return normalizeAddressRust(coin, address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - return Address(publicKey).string(); +std::string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const { + return deriveAddressRust(coin, publicKey, derivation, addressPrefix); } -Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { - const auto addr = Address(address); - return {addr.bytes.begin(), addr.bytes.end()}; +Data Entry::addressToData(TWCoinType coin, const std::string& address) const { + return addressToDataRust(coin, address); } void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { - signTemplate(dataIn, dataOut); + signRust(dataIn, coin, dataOut); } +// TODO call `signRustJSON` when it's done. string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { - return Signer::signJSON(json, key); -} - -Data Entry::preImageHashes([[maybe_unused]] TWCoinType coin, const Data& txInputData) const { - return txCompilerTemplate( - txInputData, [](const auto& input, auto& output) { - const auto transaction = Signer::build(input); - const auto chainId = load(data(input.chain_id())); // retrieve chainId from input - auto preHash = transaction->preHash(chainId); - auto preImage = transaction->serialize(chainId); - output.set_data_hash(preHash.data(), preHash.size()); - output.set_data(preImage.data(), preImage.size()); - }); -} - -void Entry::compile([[maybe_unused]] TWCoinType coin, const Data& txInputData, const std::vector& signatures, [[maybe_unused]] const std::vector& publicKeys, Data& dataOut) const { - dataOut = txCompilerTemplate( - txInputData, [&](const auto& input, auto& output) { - if (signatures.size() != 1) { - output.set_error(Common::Proto::Error_signatures_count); - output.set_error_message(Common::Proto::SigningError_Name(Common::Proto::Error_signatures_count)); - return; - } - output = Signer::compile(input, signatures[0]); - }); -} + auto input = Proto::SigningInput(); + google::protobuf::util::JsonStringToMessage(json, &input); + input.set_private_key(key.data(), key.size()); -Data Entry::buildTransactionInput([[maybe_unused]] TWCoinType coinType, [[maybe_unused]] const std::string& from, const std::string& to, const uint256_t& amount, [[maybe_unused]] const std::string& asset, [[maybe_unused]] const std::string& memo, const std::string& chainId) const { - Proto::SigningInput input; + auto inputData = data(input.SerializeAsString()); + Data dataOut; + sign(coin, inputData, dataOut); - auto chainIdData = store(uint256_t(1)); - if (chainId.length() > 0) { - // parse amount - uint256_t chainIdUint256{chainId}; - chainIdData = store(chainIdUint256); + if(dataOut.empty()) { + return {}; } - input.set_chain_id(chainIdData.data(), chainIdData.size()); - if (!Address::isValid(to)) { - throw std::invalid_argument("Invalid to address"); - } - input.set_to_address(to); + Proto::SigningOutput output; + output.ParseFromArray(dataOut.data(), static_cast(dataOut.size())); - auto& transfer = *input.mutable_transaction()->mutable_transfer(); - const auto amountData = store(amount); - transfer.set_amount(amountData.data(), amountData.size()); + return hex(output.encoded()); +} - // not set: nonce, gasPrice, gasLimit, tx_mode (need to be set afterwards) +Data Entry::preImageHashes(TWCoinType coin, const Data& txInputData) const { + return preImageHashesRust(coin, txInputData); +} - const auto txInputData = data(input.SerializeAsString()); - return txInputData; +void Entry::compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const { + compileRust(coin, txInputData, signatures, publicKeys, dataOut); } } // namespace TW::Ethereum diff --git a/src/Ethereum/Entry.h b/src/Ethereum/Entry.h index e5d3d41c30d..4d3c909df02 100644 --- a/src/Ethereum/Entry.h +++ b/src/Ethereum/Entry.h @@ -12,19 +12,18 @@ namespace TW::Ethereum { /// Entry point for Ethereum and Ethereum-fork coins. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry : public CoinEntry { +class Entry final : public CoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const final; - std::string normalizeAddress(TWCoinType coin, const std::string& address) const final; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const final; - Data addressToData(TWCoinType coin, const std::string& address) const final; - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const override; - bool supportsJSONSigning() const final { return true; } - std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const final; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const override; + std::string normalizeAddress(TWCoinType coin, const std::string& address) const override; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const override; + Data addressToData(TWCoinType coin, const std::string& address) const override; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const override; + bool supportsJSONSigning() const override { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const override; - Data preImageHashes(TWCoinType coin, const Data& txInputData) const override; - void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const override; - Data buildTransactionInput(TWCoinType coinType, const std::string& from, const std::string& to, const uint256_t& amount, const std::string& asset, const std::string& memo, const std::string& chainId) const final; + Data preImageHashes(TWCoinType coin, const Data& txInputData) const override; + void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const override; }; } // namespace TW::Ethereum diff --git a/src/Ethereum/MessageSigner.cpp b/src/Ethereum/MessageSigner.cpp index 4f31657921e..a24ee7a81c5 100644 --- a/src/Ethereum/MessageSigner.cpp +++ b/src/Ethereum/MessageSigner.cpp @@ -4,65 +4,117 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "HexCoding.h" #include "MessageSigner.h" -#include -#include -#include +#include +#include +#include "rust/Wrapper.h" +#include "TrustWalletCore/TWCoinType.h" namespace TW::Ethereum { -Data MessageSigner::generateMessage(const std::string& message) { - std::string prefix(1, MessageSigner::EthereumPrefix); - std::stringstream ss; - ss << prefix << MessageSigner::MessagePrefix << std::to_string(message.size()) << message; - Data signableMessage = Hash::keccak256(data(ss.str())); - return signableMessage; +std::string signMessageRust(const PrivateKey& privateKey, const std::string& message, Proto::MessageType msgType, MessageSigner::MaybeChainId chainId) { + Proto::MessageSigningInput input; + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_message(message); + input.set_message_type(msgType); + + if (chainId.has_value()) { + input.mutable_chain_id()->set_chain_id(static_cast(chainId.value())); + } + + Rust::TWDataWrapper inputData(data(input.SerializeAsString())); + Rust::TWDataWrapper outputPtr = Rust::tw_message_signer_sign(inputData.get(), TWCoinTypeEthereum); + + auto outputData = outputPtr.toDataOrDefault(); + if (outputData.empty()) { + return {}; + } + + Proto::MessageSigningOutput output; + output.ParseFromArray(outputData.data(), static_cast(outputData.size())); + + if (output.error() != Common::Proto::SigningError::OK) { + return {}; + } + return output.signature(); +} + +Data messagePreImageHashRust(const std::string& message, Proto::MessageType msgType) { + Proto::MessageSigningInput input; + input.set_message(message); + input.set_message_type(msgType); + + Rust::TWDataWrapper inputData(data(input.SerializeAsString())); + Rust::TWDataWrapper outputPtr = Rust::tw_message_signer_pre_image_hashes(inputData.get(), TWCoinTypeEthereum); + + auto outputData = outputPtr.toDataOrDefault(); + if (outputData.empty()) { + return {}; + } + + TxCompiler::Proto::PreSigningOutput output; + output.ParseFromArray(outputData.data(), static_cast(outputData.size())); + + if (output.error() != Common::Proto::SigningError::OK) { + return {}; + } + return data(output.data_hash()); +} + +bool verifyMessageRust(const PublicKey& publicKey, const std::string& message, const std::string& signature) { + Proto::MessageVerifyingInput input; + input.set_public_key(publicKey.bytes.data(), publicKey.bytes.size()); + input.set_message(message); + input.set_signature(signature); + + Rust::TWDataWrapper inputData(data(input.SerializeAsString())); + return Rust::tw_message_signer_verify(inputData.get(), TWCoinTypeEthereum); } std::string MessageSigner::signMessage(const PrivateKey& privateKey, const std::string& message, MessageType msgType, MaybeChainId chainId) { - auto signableMessage = generateMessage(message); - return signHash(privateKey, signableMessage, msgType, chainId); + auto protoMsgType = Proto::MessageType::MessageType_legacy; + switch (msgType) { + case MessageType::Eip155: { + protoMsgType = Proto::MessageType::MessageType_eip155; + break; + } + case MessageType::ImmutableX: { + protoMsgType = Proto::MessageType::MessageType_immutable_x; + break; + } + default: { + break; + } + } + + return signMessageRust(privateKey, message, protoMsgType, chainId); } std::string MessageSigner::signTypedData(const PrivateKey& privateKey, const std::string& data, MessageType msgType, MessageSigner::MaybeChainId chainId) { - if (msgType == MessageType::Eip155 && nlohmann::json::accept(data)) { - auto json = nlohmann::json::parse(data); - if (json.contains("types") && json.at("types").contains("EIP712Domain")) { - const auto& chainIdValue = json.at("domain").at("chainId"); - std::size_t actualChainId{0}; - if (chainIdValue.is_string()) { - actualChainId = std::stoull(chainIdValue.get()); - } else { - actualChainId = chainIdValue.get(); - } - if (actualChainId != *chainId) { - return "EIP712 chainId is different than the current chainID."; - } + auto protoMsgType = Proto::MessageType::MessageType_typed; + switch (msgType) { + case MessageType::Eip155: { + protoMsgType = Proto::MessageType::MessageType_typed_eip155; + break; + } + default: { + break; } } - auto signableMessage = ABI::ParamStruct::hashStructJson(data); - return signHash(privateKey, signableMessage, msgType, chainId); + + return signMessageRust(privateKey, data, protoMsgType, chainId); } bool MessageSigner::verifyMessage(const PublicKey& publicKey, const std::string& message, const std::string& signature) noexcept { - Data msg = generateMessage(message); - //! If it's json && EIP712Domain then we hash the struct - if (nlohmann::json::accept(message)) { - auto json = nlohmann::json::parse(message); - if (json.contains("types") && json.at("types").contains("EIP712Domain")) { - msg = ABI::ParamStruct::hashStructJson(message); - } - } - auto rawSignature = parse_hex(signature); - auto recovered = publicKey.recover(rawSignature, msg); - return recovered == publicKey && publicKey.verify(rawSignature, msg); + return verifyMessageRust(publicKey, message, signature); +} + +Data MessageSigner::messagePreImageHash(const std::string& message) noexcept { + return messagePreImageHashRust(message, Proto::MessageType::MessageType_legacy); } -std::string MessageSigner::signHash(const PrivateKey& privateKey, const Data& signableMessage, MessageType msgType, TW::Ethereum::MessageSigner::MaybeChainId chainId) { - auto data = privateKey.sign(signableMessage, TWCurveSECP256k1); - prepareSignature(data, msgType, chainId); - return hex(data); +Data MessageSigner::typedDataPreImageHash(const std::string& data) noexcept { + return messagePreImageHashRust(data, Proto::MessageType::MessageType_typed); } void MessageSigner::prepareSignature(Data& signature, MessageType msgType, TW::Ethereum::MessageSigner::MaybeChainId chainId) noexcept { diff --git a/src/Ethereum/MessageSigner.h b/src/Ethereum/MessageSigner.h index b6042d23bdd..5aeac0e05e4 100644 --- a/src/Ethereum/MessageSigner.h +++ b/src/Ethereum/MessageSigner.h @@ -43,10 +43,17 @@ class MessageSigner { /// \param signature signature to verify the message against /// \return true if the message match the signature, false otherwise static bool verifyMessage(const PublicKey& publicKey, const std::string& message, const std::string& signature) noexcept; - static constexpr auto MessagePrefix = "Ethereum Signed Message:\n"; - static constexpr std::uint8_t EthereumPrefix{0x19}; - static Data generateMessage(const std::string& message); - static std::string signHash(const PrivateKey& privateKey, const Data& signableMessage, MessageType msgType, TW::Ethereum::MessageSigner::MaybeChainId chainId); + + /// Computes a hash of the message following EIP-191. + /// \param message message to hash + /// \return hash of the tuped data. + static Data messagePreImageHash(const std::string& message) noexcept; + + /// Computes a hash of the typed data according to EIP-712 V4. + /// \param data json data + /// \return hash of the tuped data. + static Data typedDataPreImageHash(const std::string& data) noexcept; + static void prepareSignature(Data& signature, MessageType msgType, MaybeChainId chainId = std::nullopt) noexcept; }; diff --git a/src/Ethereum/RLP.cpp b/src/Ethereum/RLP.cpp index f8179cbaadf..486162a58de 100644 --- a/src/Ethereum/RLP.cpp +++ b/src/Ethereum/RLP.cpp @@ -6,196 +6,41 @@ #include "RLP.h" -#include "../BinaryCoding.h" -#include "../Numeric.h" +#include "BinaryCoding.h" +#include "TrustWalletCore/TWCoinType.h" +#include "rust/Wrapper.h" #include namespace TW::Ethereum { -Data RLP::encode(const uint256_t& value) noexcept { - using boost::multiprecision::cpp_int; +Data RLP::encode(const EthereumRlp::Proto::EncodingInput& input) { + Rust::TWDataWrapper inputData(data(input.SerializeAsString())); + Rust::TWDataWrapper outputPtr = Rust::tw_ethereum_rlp_encode(TWCoinTypeEthereum, inputData.get()); - Data bytes; - export_bits(value, std::back_inserter(bytes), 8); - - if (bytes.empty() || (bytes.size() == 1 && bytes[0] == 0)) { - return {0x80}; + auto outputData = outputPtr.toDataOrDefault(); + if (outputData.empty()) { + return {}; } - return encode(bytes); -} + EthereumRlp::Proto::EncodingOutput output; + output.ParseFromArray(outputData.data(), static_cast(outputData.size())); -Data RLP::encodeList(const Data& encoded) noexcept { - auto result = encodeHeader(encoded.size(), 0xc0, 0xf7); - result.reserve(result.size() + encoded.size()); - result.insert(result.end(), encoded.begin(), encoded.end()); - return result; + return data(output.encoded()); } -Data RLP::encode(const Data& data) noexcept { - if (data.size() == 1 && data[0] <= 0x7f) { - // Fits in single byte, no header - return data; - } - - auto encoded = encodeHeader(data.size(), 0x80, 0xb7); - encoded.insert(encoded.end(), data.begin(), data.end()); - return encoded; +Data RLP::encodeString(const std::string& s) { + EthereumRlp::Proto::EncodingInput input; + input.mutable_item()->set_string_item(s); + return encode(input); } -Data RLP::encodeHeader(uint64_t size, uint8_t smallTag, uint8_t largeTag) noexcept { - if (size < 56) { - return {static_cast(smallTag + size)}; - } +Data RLP::encodeU256(const uint256_t & num) { + auto numData = store(num); - const auto sizeData = putVarInt(size); - - auto header = Data(); - header.reserve(1 + sizeData.size()); - header.push_back(largeTag + static_cast(sizeData.size())); - header.insert(header.end(), sizeData.begin(), sizeData.end()); - return header; -} - -Data RLP::putVarInt(uint64_t i) noexcept { - Data bytes; // accumulate bytes here, in reverse order - do { - // take LSB byte, append - bytes.push_back(i & 0xff); - i = i >> 8; - } while (i); - assert(bytes.size() >= 1 && bytes.size() <= 8); - std::reverse(bytes.begin(), bytes.end()); - return bytes; -} - -uint64_t RLP::parseVarInt(size_t size, const Data& data, size_t index) { - if (size < 1 || size > 8) { - throw std::invalid_argument("invalid length length"); - } - if (data.size() - index < size) { - throw std::invalid_argument("Not enough data for varInt"); - } - if (size >= 2 && data[index] == 0) { - throw std::invalid_argument("multi-byte length must have no leading zero"); - } - uint64_t val = 0; - for (auto i = 0U; i < size; ++i) { - val = val << 8; - val += data[index + i]; - } - return static_cast(val); -} - -RLP::DecodedItem RLP::decodeList(const Data& input) { - RLP::DecodedItem item; - auto remainder = input; - while (true) { - auto listItem = RLP::decode(remainder); - item.decoded.push_back(listItem.decoded[0]); - if (listItem.remainder.size() == 0) { - break; - } else { - remainder = listItem.remainder; - } - } - return item; -} - -RLP::DecodedItem RLP::decode(const Data& input) { - if (input.size() == 0) { - throw std::invalid_argument("can't decode empty rlp data"); - } - RLP::DecodedItem item; - auto inputLen = input.size(); - auto prefix = input[0]; - if (prefix <= 0x7f) { - // 00--7f: a single byte whose value is in the [0x00, 0x7f] range, that byte is its own RLP encoding. - item.decoded.emplace_back(Data{input[0]}); - item.remainder = subData(input, 1); - return item; - } - if (prefix <= 0xb7) { - // 80--b7: short string - // string is 0-55 bytes long. A single byte with value 0x80 plus the length of the string followed by the string - // The range of the first byte is [0x80, 0xb7] - - // empty string - if (prefix == 0x80) { - item.decoded.emplace_back(); - item.remainder = subData(input, 1); - return item; - } - - auto strLen = prefix - 0x80; - if (strLen == 1 && input[1] <= 0x7f) { - throw std::invalid_argument("single byte below 128 must be encoded as itself"); - } - - if (inputLen < (1U + strLen)) { - throw std::invalid_argument(std::string("invalid short string, length ") + std::to_string(strLen)); - } - item.decoded.push_back(subData(input, 1, strLen)); - item.remainder = subData(input, 1 + strLen); - - return item; - } - if (prefix <= 0xbf) { - // b8--bf: long string - auto lenOfStrLen = size_t(prefix - 0xb7); - auto strLen = static_cast(parseVarInt(lenOfStrLen, input, 1)); - bool isStrLenInvalid = inputLen < lenOfStrLen - || checkAddUnsignedOverflow(1U + lenOfStrLen, strLen) - || inputLen < (1U + lenOfStrLen + strLen); - if (isStrLenInvalid) { - throw std::invalid_argument(std::string("Invalid rlp encoding length, length ") + std::to_string(strLen)); - } - auto data = subData(input, 1 + lenOfStrLen, strLen); - item.decoded.push_back(data); - item.remainder = subData(input, 1 + lenOfStrLen + strLen); - return item; - } - if (prefix <= 0xf7) { - // c0--f7: a list between 0-55 bytes long - auto listLen = size_t(prefix - 0xc0); - if (inputLen < (1 + listLen)) { - throw std::invalid_argument(std::string("Invalid rlp string length, length ") + std::to_string(listLen)); - } - // empty list - if (listLen == 0) { - item.remainder = subData(input, 1); - return item; - } - - // decode list - auto listItem = decodeList(subData(input, 1, listLen)); - for (auto& data : listItem.decoded) { - item.decoded.push_back(data); - } - item.remainder = subData(input, 1 + listLen); - return item; - } - // f8--ff - auto lenOfListLen = size_t(prefix - 0xf7); - auto listLen = static_cast(parseVarInt(lenOfListLen, input, 1)); - if (listLen < 56) { - throw std::invalid_argument("length below 56 must be encoded in one byte"); - } - auto isListLenInvalid = inputLen < lenOfListLen - || checkAddUnsignedOverflow(1U + lenOfListLen, listLen) - || inputLen < (1U + lenOfListLen + listLen); - if (isListLenInvalid) { - throw std::invalid_argument(std::string("Invalid rlp list length, length ") + std::to_string(listLen)); - } - - // decode list - auto listItem = decodeList(subData(input, 1 + lenOfListLen, listLen)); - for (auto& data : listItem.decoded) { - item.decoded.push_back(data); - } - item.remainder = subData(input, 1 + lenOfListLen + listLen); - return item; + EthereumRlp::Proto::EncodingInput input; + input.mutable_item()->set_number_u256(numData.data(), numData.size()); + return encode(input); } } // namespace TW::Ethereum diff --git a/src/Ethereum/RLP.h b/src/Ethereum/RLP.h index c0366c023b1..57c0ef18113 100644 --- a/src/Ethereum/RLP.h +++ b/src/Ethereum/RLP.h @@ -6,9 +6,9 @@ #pragma once -#include "Transaction.h" #include "Data.h" -#include "../uint256.h" +#include "proto/EthereumRlp.pb.h" +#include "uint256.h" #include #include @@ -20,87 +20,11 @@ namespace TW::Ethereum { /// /// - SeeAlso: https://github.com/ethereum/wiki/wiki/RLP struct RLP { - /// Encodes a string; - static Data encode(const std::string& string) noexcept { - return encode(Data(string.begin(), string.end())); - } + static Data encode(const EthereumRlp::Proto::EncodingInput& input); - static Data encode(uint8_t number) noexcept { return encode(uint256_t(number)); } + static Data encodeString(const std::string& s); - static Data encode(uint16_t number) noexcept { return encode(uint256_t(number)); } - - static Data encode(int32_t number) noexcept { - if (number < 0) { - return {}; // RLP cannot encode negative numbers - } - return encode(static_cast(number)); - } - - static Data encode(uint32_t number) noexcept { return encode(uint256_t(number)); } - - static Data encode(int64_t number) noexcept { - if (number < 0) { - return {}; // RLP cannot encode negative numbers - } - return encode(static_cast(number)); - } - - static Data encode(uint64_t number) noexcept { return encode(uint256_t(number)); } - - static Data encode(const uint256_t& number) noexcept; - - /// Wraps encoded data as a list. - static Data encodeList(const Data& encoded) noexcept; - - /// Encodes a block of data. - static Data encode(const Data& data) noexcept; - - /// Encodes a static array. - template - static Data encode(const std::array& data) noexcept { - if (N == 1 && data[0] <= 0x7f) { - // Fits in single byte, no header - return Data(data.begin(), data.end()); - } - - auto encoded = encodeHeader(data.size(), 0x80, 0xb7); - encoded.insert(encoded.end(), data.begin(), data.end()); - return encoded; - } - - /// Encodes a list of elements. - template - static Data encodeList(T elements) noexcept { - auto encodedData = Data(); - for (const auto& el : elements) { - auto encoded = encode(el); - if (encoded.empty()) { - return {}; - } - encodedData.insert(encodedData.end(), encoded.begin(), encoded.end()); - } - - auto encoded = encodeHeader(encodedData.size(), 0xc0, 0xf7); - encoded.insert(encoded.end(), encodedData.begin(), encodedData.end()); - return encoded; - } - - /// Encodes a list header. - static Data encodeHeader(uint64_t size, uint8_t smallTag, uint8_t largeTag) noexcept; - - struct DecodedItem { - std::vector decoded; - Data remainder; - }; - - static DecodedItem decodeList(const Data& input); - /// Decodes data, remainder from RLP encoded data - static DecodedItem decode(const Data& data); - - /// Returns the representation of an integer using the least number of bytes needed, between 1 and 8 bytes, big endian - static Data putVarInt(uint64_t i) noexcept; - /// Parses an integer of given size, between 1 and 8 bytes, big endian - static uint64_t parseVarInt(size_t size, const Data& data, size_t index); + static Data encodeU256(const uint256_t& num); }; } // namespace TW::Ethereum diff --git a/src/Ethereum/Signer.cpp b/src/Ethereum/Signer.cpp deleted file mode 100644 index 8684e149e85..00000000000 --- a/src/Ethereum/Signer.cpp +++ /dev/null @@ -1,429 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Signer.h" -#include "HexCoding.h" -#include "Coin.h" -#include -#include - -namespace TW::Ethereum { - -Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { - try { - uint256_t chainID = load(input.chain_id()); - auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); - auto transaction = Signer::build(input); - - auto signature = sign(key, chainID, transaction); - - auto output = Proto::SigningOutput(); - - auto pre_hash = transaction->preHash(chainID); - output.set_pre_hash(pre_hash.data(), pre_hash.size()); - - auto encoded = transaction->encoded(signature, chainID); - output.set_encoded(encoded.data(), encoded.size()); - - auto v = store(signature.v, 1); - output.set_v(v.data(), v.size()); - auto r = store(signature.r, 32); - output.set_r(r.data(), r.size()); - auto s = store(signature.s, 32); - output.set_s(s.data(), s.size()); - - output.set_data(transaction->payload.data(), transaction->payload.size()); - - return output; - } catch (std::exception&) { - return Proto::SigningOutput(); - } -} - -std::string Signer::signJSON(const std::string& json, const Data& key) { - auto input = Proto::SigningInput(); - google::protobuf::util::JsonStringToMessage(json, &input); - input.set_private_key(key.data(), key.size()); - auto output = Signer::sign(input); - return hex(output.encoded()); -} - -Proto::SigningOutput Signer::compile(const Proto::SigningInput& input, const Data& signature) noexcept { - try { - uint256_t chainID = load(input.chain_id()); - auto transaction = Signer::build(input); - - // prepare Signature - const Signature sigStruct = signatureDataToStruct(signature, transaction->usesReplayProtection(), chainID); - - auto output = Proto::SigningOutput(); - - auto pre_hash = transaction->preHash(chainID); - output.set_pre_hash(pre_hash.data(), pre_hash.size()); - - auto encoded = transaction->encoded(sigStruct, chainID); - output.set_encoded(encoded.data(), encoded.size()); - - auto v = store(sigStruct.v, 1); - output.set_v(v.data(), v.size()); - auto r = store(sigStruct.r, 32); - output.set_r(r.data(), r.size()); - auto s = store(sigStruct.s, 32); - output.set_s(s.data(), s.size()); - - output.set_data(transaction->payload.data(), transaction->payload.size()); - return output; - } catch (std::exception&) { - return Proto::SigningOutput(); - } -} - -Signature Signer::signatureDataToStruct(const Data& signature, bool includeEip155, const uint256_t& chainID) noexcept { - if (!includeEip155) { - return signatureDataToStructSimple(signature); - } - return signatureDataToStructWithEip155(chainID, signature); -} - -Signature Signer::signatureDataToStructSimple(const Data& signature) noexcept { - boost::multiprecision::uint256_t r, s, v; - import_bits(r, signature.begin(), signature.begin() + 32); - import_bits(s, signature.begin() + 32, signature.begin() + 64); - import_bits(v, signature.begin() + 64, signature.begin() + 65); - return Signature{r, s, v}; -} - -Data Signer::simpleStructToSignatureData(const Signature& signature) noexcept { - Data fullSignature; - - auto r = store(signature.r, 32); - append(fullSignature, r); - auto s = store(signature.s, 32); - append(fullSignature, s); - auto v = store(signature.v, 1); - append(fullSignature, v); - - return fullSignature; -} - -Signature Signer::signatureDataToStructWithEip155(const uint256_t& chainID, const Data& signature) noexcept { - Signature rsv = signatureDataToStructSimple(signature); - // Embed chainID in V param, for replay protection, legacy (EIP155) - if (chainID != 0) { - rsv.v += 35 + chainID + chainID; - } else { - rsv.v += 27; - } - return rsv; -} - -Signature Signer::sign(const PrivateKey& privateKey, const Data& hash, bool includeEip155, const uint256_t& chainID) noexcept { - auto signature = privateKey.sign(hash, TWCurveSECP256k1); - return signatureDataToStruct(signature, includeEip155, chainID); -} - -// May throw -Data addressStringToData(const std::string& asString) { - if (asString.empty()) { - return {}; - } - // only ronin address prefix is not 0x - if (asString.compare(0, 2, "0x") != 0) { - return TW::addressToData(TWCoinTypeRonin, asString); - } - return TW::addressToData(TWCoinTypeEthereum, asString); -} - -std::shared_ptr Signer::build(const Proto::SigningInput& input) { - Data toAddress = addressStringToData(input.to_address()); - uint256_t nonce = load(input.nonce()); - uint256_t gasPrice = load(input.gas_price()); - uint256_t gasLimit = load(input.gas_limit()); - uint256_t maxInclusionFeePerGas = load(input.max_inclusion_fee_per_gas()); - uint256_t maxFeePerGas = load(input.max_fee_per_gas()); - - // EIP4337 - Data entryPointAddress = addressStringToData(input.user_operation().entry_point()); - Data senderAddress = addressStringToData(input.user_operation().sender()); - Data initCode = Data(input.user_operation().init_code().begin(), input.user_operation().init_code().end()); - uint256_t preVerificationGas = load(input.user_operation().pre_verification_gas()); - uint256_t verificationGasLimit = load(input.user_operation().verification_gas_limit()); - Data paymasterAndData = Data(input.user_operation().paymaster_and_data().begin(), input.user_operation().paymaster_and_data().end()); - - switch (input.transaction().transaction_oneof_case()) { - case Proto::Transaction::kTransfer: { - switch (input.tx_mode()) { - case Proto::TransactionMode::Legacy: - default: - return TransactionNonTyped::buildNativeTransfer( - nonce, gasPrice, gasLimit, - /* to: */ toAddress, - /* amount: */ load(input.transaction().transfer().amount()), - /* optional data: */ Data(input.transaction().transfer().data().begin(), input.transaction().transfer().data().end())); - - case Proto::TransactionMode::Enveloped: // Eip1559 - return TransactionEip1559::buildNativeTransfer( - nonce, maxInclusionFeePerGas, maxFeePerGas, gasLimit, - /* to: */ toAddress, - /* amount: */ load(input.transaction().transfer().amount()), - /* optional data: */ Data(input.transaction().transfer().data().begin(), input.transaction().transfer().data().end())); - - case Proto::TransactionMode::UserOp: - return UserOperation::buildNativeTransfer( - entryPointAddress, - senderAddress, - toAddress, - load(input.transaction().transfer().amount()), - nonce, - gasLimit, - verificationGasLimit, - maxFeePerGas, - maxInclusionFeePerGas, - preVerificationGas, - paymasterAndData, - initCode, - Data(input.transaction().transfer().data().begin(), input.transaction().transfer().data().end()) - ); - } - } - - case Proto::Transaction::kErc20Transfer: { - Data tokenToAddress = addressStringToData(input.transaction().erc20_transfer().to()); - switch (input.tx_mode()) { - case Proto::TransactionMode::Legacy: - default: - return TransactionNonTyped::buildERC20Transfer( - nonce, gasPrice, gasLimit, - /* tokenContract: */ toAddress, - /* toAddress */ tokenToAddress, - /* amount: */ load(input.transaction().erc20_transfer().amount())); - - case Proto::TransactionMode::Enveloped: // Eip1559 - return TransactionEip1559::buildERC20Transfer( - nonce, maxInclusionFeePerGas, maxFeePerGas, gasLimit, - /* tokenContract: */ toAddress, - /* toAddress */ tokenToAddress, - /* amount: */ load(input.transaction().erc20_transfer().amount())); - - case Proto::TransactionMode::UserOp: - return UserOperation::buildERC20Transfer( - entryPointAddress, - senderAddress, - toAddress, - tokenToAddress, - load(input.transaction().erc20_transfer().amount()), - nonce, - gasLimit, - verificationGasLimit, - maxFeePerGas, - maxInclusionFeePerGas, - preVerificationGas, - paymasterAndData, - initCode); - } - } - - case Proto::Transaction::kErc20Approve: { - Data spenderAddress = addressStringToData(input.transaction().erc20_approve().spender()); - switch (input.tx_mode()) { - case Proto::TransactionMode::Legacy: - default: - return TransactionNonTyped::buildERC20Approve( - nonce, gasPrice, gasLimit, - /* tokenContract: */ toAddress, - /* toAddress */ spenderAddress, - /* amount: */ load(input.transaction().erc20_approve().amount())); - - case Proto::TransactionMode::Enveloped: // Eip1559 - return TransactionEip1559::buildERC20Approve( - nonce, maxInclusionFeePerGas, maxFeePerGas, gasLimit, - /* tokenContract: */ toAddress, - /* toAddress */ spenderAddress, - /* amount: */ load(input.transaction().erc20_approve().amount())); - - case Proto::TransactionMode::UserOp: - return UserOperation::buildERC20Approve( - entryPointAddress, - senderAddress, - toAddress, - spenderAddress, - load(input.transaction().erc20_approve().amount()), - nonce, - gasLimit, - verificationGasLimit, - maxFeePerGas, - maxInclusionFeePerGas, - preVerificationGas, - paymasterAndData, - initCode); - } - } - - case Proto::Transaction::kErc721Transfer: { - Data tokenToAddress = addressStringToData(input.transaction().erc721_transfer().to()); - Data tokenFromAddress = addressStringToData(input.transaction().erc721_transfer().from()); - switch (input.tx_mode()) { - case Proto::TransactionMode::Legacy: - default: - return TransactionNonTyped::buildERC721Transfer( - nonce, gasPrice, gasLimit, - /* tokenContract: */ toAddress, - /* fromAddress: */ tokenFromAddress, - /* toAddress */ tokenToAddress, - /* tokenId: */ load(input.transaction().erc721_transfer().token_id())); - - case Proto::TransactionMode::Enveloped: // Eip1559 - return TransactionEip1559::buildERC721Transfer( - nonce, maxInclusionFeePerGas, maxFeePerGas, gasLimit, - /* tokenContract: */ toAddress, - /* fromAddress: */ tokenFromAddress, - /* toAddress */ tokenToAddress, - /* tokenId: */ load(input.transaction().erc721_transfer().token_id())); - - case Proto::TransactionMode::UserOp: - return UserOperation::buildERC721Transfer( - entryPointAddress, - senderAddress, - toAddress, - tokenFromAddress, - tokenToAddress, - load(input.transaction().erc721_transfer().token_id()), - nonce, - gasLimit, - verificationGasLimit, - maxFeePerGas, - maxInclusionFeePerGas, - preVerificationGas, - paymasterAndData, - initCode); - } - } - - case Proto::Transaction::kErc1155Transfer: { - Data tokenToAddress = addressStringToData(input.transaction().erc1155_transfer().to()); - Data tokenFromAddress = addressStringToData(input.transaction().erc1155_transfer().from()); - switch (input.tx_mode()) { - case Proto::TransactionMode::Legacy: - default: - return TransactionNonTyped::buildERC1155Transfer( - nonce, gasPrice, gasLimit, - /* tokenContract: */ toAddress, - /* fromAddress: */ tokenFromAddress, - /* toAddress */ tokenToAddress, - /* tokenId: */ load(input.transaction().erc1155_transfer().token_id()), - /* value */ load(input.transaction().erc1155_transfer().value()), - /* data */ Data(input.transaction().erc1155_transfer().data().begin(), input.transaction().erc1155_transfer().data().end())); - - case Proto::TransactionMode::Enveloped: // Eip1559 - return TransactionEip1559::buildERC1155Transfer( - nonce, maxInclusionFeePerGas, maxFeePerGas, gasLimit, - /* tokenContract: */ toAddress, - /* fromAddress: */ tokenFromAddress, - /* toAddress */ tokenToAddress, - /* tokenId: */ load(input.transaction().erc1155_transfer().token_id()), - /* value */ load(input.transaction().erc1155_transfer().value()), - /* data */ Data(input.transaction().erc1155_transfer().data().begin(), input.transaction().erc1155_transfer().data().end())); - - case Proto::TransactionMode::UserOp: - return UserOperation::buildERC1155Transfer( - entryPointAddress, - senderAddress, - toAddress, - tokenFromAddress, - tokenToAddress, - load(input.transaction().erc1155_transfer().token_id()), - load(input.transaction().erc1155_transfer().value()), - Data(input.transaction().erc1155_transfer().data().begin(), input.transaction().erc1155_transfer().data().end()), - nonce, - gasLimit, - verificationGasLimit, - maxFeePerGas, - maxInclusionFeePerGas, - preVerificationGas, - paymasterAndData, - initCode); - } - } - - case Proto::Transaction::kBatch: { - switch (input.tx_mode()) { - case Proto::TransactionMode::Legacy: - default: - return nullptr; - case Proto::TransactionMode::Enveloped: // Eip1559 - return nullptr; - case Proto::TransactionMode::UserOp: - std::vector addresses; - std::vector amounts; - std::vector payloads; - for (int i=0; i < input.transaction().batch().calls().size(); i++) { - addresses.push_back(addressStringToData(input.transaction().batch().calls()[i].address())); - amounts.push_back(load(input.transaction().batch().calls()[i].amount())); - payloads.push_back(Data(input.transaction().batch().calls()[i].payload().begin(), input.transaction().batch().calls()[i].payload().end())); - } - - return UserOperation::buildBatch( - entryPointAddress, - senderAddress, - addresses, - amounts, - nonce, - gasLimit, - verificationGasLimit, - maxFeePerGas, - maxInclusionFeePerGas, - preVerificationGas, - paymasterAndData, - initCode, - payloads - ); - } - } - - case Proto::Transaction::kContractGeneric: - default: { - switch (input.tx_mode()) { - case Proto::TransactionMode::Legacy: - default: - return TransactionNonTyped::buildNativeTransfer( - nonce, gasPrice, gasLimit, - /* to: */ toAddress, - /* amount: */ load(input.transaction().contract_generic().amount()), - /* transaction: */ Data(input.transaction().contract_generic().data().begin(), input.transaction().contract_generic().data().end())); - - case Proto::TransactionMode::Enveloped: // Eip1559 - return TransactionEip1559::buildNativeTransfer( - nonce, maxInclusionFeePerGas, maxFeePerGas, gasLimit, - /* to: */ toAddress, - /* amount: */ load(input.transaction().contract_generic().amount()), - /* transaction: */ Data(input.transaction().contract_generic().data().begin(), input.transaction().contract_generic().data().end())); - - case Proto::TransactionMode::UserOp: - return UserOperation::buildNativeTransfer( - entryPointAddress, - senderAddress, - toAddress, - load(input.transaction().contract_generic().amount()), - nonce, - gasLimit, - verificationGasLimit, - maxFeePerGas, - maxInclusionFeePerGas, - preVerificationGas, - paymasterAndData, - initCode, - Data(input.transaction().contract_generic().data().begin(), input.transaction().contract_generic().data().end())); - } - } - } -} - -Signature Signer::sign(const PrivateKey& privateKey, const uint256_t& chainID, std::shared_ptr transaction) noexcept { - auto preHash = transaction->preHash(chainID); - return Signer::sign(privateKey, preHash, transaction->usesReplayProtection(), chainID); -} - -} // namespace TW::Ethereum diff --git a/src/Ethereum/Signer.h b/src/Ethereum/Signer.h deleted file mode 100644 index 3a28793b2f9..00000000000 --- a/src/Ethereum/Signer.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "RLP.h" -#include "Transaction.h" -#include "Data.h" -#include "../Hash.h" -#include "../PrivateKey.h" -#include "../proto/Ethereum.pb.h" -#include "../uint256.h" - -#include -#include -#include -#include - -namespace TW::Ethereum { - -/// Helper class that performs Ethereum transaction signing. -class Signer { - public: - /// Signs a Proto::SigningInput transaction - static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept; - /// Signs a json Proto::SigningInput with private key - static std::string signJSON(const std::string& json, const Data& key); - - public: - Signer() = delete; - - /// Signs the given transaction. - static Signature sign(const PrivateKey& privateKey, const uint256_t& chainID, std::shared_ptr transaction) noexcept; - /// Compiles a Proto::SigningInput transaction, with external signature - static Proto::SigningOutput compile(const Proto::SigningInput& input, const Data& signature) noexcept; - - /// build Transaction from signing input - static std::shared_ptr build(const Proto::SigningInput& input); - - /// Signs a hash with the given private key for the given chain identifier. - /// - /// \returns the r, s, and v values of the transaction signature - static Signature sign(const PrivateKey& privateKey, const Data& hash, bool includeEip155, const uint256_t& chainID) noexcept; - - /// Break up the signature into the R, S, and V values. - /// \returns the r, s, and v values of the transaction signature - static Signature signatureDataToStruct(const Data& signature, bool includeEip155, const uint256_t& chainID) noexcept; - - /// Break up the signature into the R, S, and V values, with no replay protection. - /// \returns the r, s, and v values of the transaction signature - static Signature signatureDataToStructSimple(const Data& signature) noexcept; - - /// Converts R, S, and V values into the full signature, with no replay protection. - /// \returns the full signature bytes - static Data simpleStructToSignatureData(const Signature& signature) noexcept; - - /// Break up the signature into the R, S, and V values, and include chainID in V for replay protection (Eip155) - /// \returns the r, s, and v values of the transaction signature - static Signature signatureDataToStructWithEip155(const uint256_t& chainID, const Data& signature) noexcept; -}; - -} // namespace TW::Ethereum diff --git a/src/Ethereum/Transaction.cpp b/src/Ethereum/Transaction.cpp deleted file mode 100644 index ec5a3badf1e..00000000000 --- a/src/Ethereum/Transaction.cpp +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Transaction.h" -#include "Ethereum/ABI.h" -#include "HexCoding.h" -#include "RLP.h" -#include "Signer.h" -#include -#include -#include - -namespace TW::Ethereum { - -using json = nlohmann::json; -using ParamBasePtr = std::shared_ptr; -using ParamCollection = std::vector; -using UserOperationPtr = std::shared_ptr; - -static const Data EmptyListEncoded = parse_hex("c0"); - -/// TransactionNonTyped -std::shared_ptr -TransactionNonTyped::buildNativeTransfer(const uint256_t& nonce, - const uint256_t& gasPrice, const uint256_t& gasLimit, - const Data& toAddress, const uint256_t& amount, const Data& data) { - return std::make_shared(nonce, gasPrice, gasLimit, toAddress, amount, data); -} - -std::shared_ptr -TransactionNonTyped::buildERC20Transfer(const uint256_t& nonce, - const uint256_t& gasPrice, const uint256_t& gasLimit, - const Data& tokenContract, const Data& toAddress, const uint256_t& amount) { - return std::make_shared(nonce, gasPrice, gasLimit, tokenContract, 0, buildERC20TransferCall(toAddress, amount)); -} - -std::shared_ptr -TransactionNonTyped::buildERC20Approve(const uint256_t& nonce, - const uint256_t& gasPrice, const uint256_t& gasLimit, - const Data& tokenContract, const Data& spenderAddress, const uint256_t& amount) { - return std::make_shared(nonce, gasPrice, gasLimit, tokenContract, 0, buildERC20ApproveCall(spenderAddress, amount)); -} - -std::shared_ptr -TransactionNonTyped::buildERC721Transfer(const uint256_t& nonce, - const uint256_t& gasPrice, const uint256_t& gasLimit, - const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId) { - return std::make_shared(nonce, gasPrice, gasLimit, tokenContract, 0, buildERC721TransferFromCall(from, to, tokenId)); -} - -std::shared_ptr -TransactionNonTyped::buildERC1155Transfer(const uint256_t& nonce, - const uint256_t& gasPrice, const uint256_t& gasLimit, - const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data) { - return std::make_shared(nonce, gasPrice, gasLimit, tokenContract, 0, buildERC1155TransferFromCall(from, to, tokenId, value, data)); -} - -Data TransactionNonTyped::preHash(const uint256_t chainID) const { - return Hash::keccak256(serialize(chainID)); -} - -Data TransactionNonTyped::serialize(const uint256_t chainID) const { - Data encoded; - append(encoded, RLP::encode(nonce)); - append(encoded, RLP::encode(gasPrice)); - append(encoded, RLP::encode(gasLimit)); - append(encoded, RLP::encode(to)); - append(encoded, RLP::encode(amount)); - append(encoded, RLP::encode(payload)); - append(encoded, RLP::encode(chainID)); - append(encoded, RLP::encode(0)); - append(encoded, RLP::encode(0)); - return RLP::encodeList(encoded); -} - -Data TransactionNonTyped::encoded(const Signature& signature, [[maybe_unused]] const uint256_t chainID) const { - Data encoded; - append(encoded, RLP::encode(nonce)); - append(encoded, RLP::encode(gasPrice)); - append(encoded, RLP::encode(gasLimit)); - append(encoded, RLP::encode(to)); - append(encoded, RLP::encode(amount)); - append(encoded, RLP::encode(payload)); - append(encoded, RLP::encode(signature.v)); - append(encoded, RLP::encode(signature.r)); - append(encoded, RLP::encode(signature.s)); - return RLP::encodeList(encoded); -} - -Data TransactionNonTyped::buildERC20TransferCall(const Data& to, const uint256_t& amount) { - // clang-format off - auto func = ABI::Function("transfer", ParamCollection{ - std::make_shared(to), - std::make_shared(amount) - }); - // clang-format on - Data payload; - func.encode(payload); - return payload; -} - -Data TransactionNonTyped::buildERC20ApproveCall(const Data& spender, const uint256_t& amount) { - // clang-format off - auto func = ABI::Function("approve", ParamCollection{ - std::make_shared(spender), - std::make_shared(amount) - }); - // clang-format on - Data payload; - func.encode(payload); - return payload; -} - -Data TransactionNonTyped::buildERC721TransferFromCall(const Data& from, const Data& to, const uint256_t& tokenId) { - // clang-format off - auto func = ABI::Function("transferFrom", ParamCollection{ - std::make_shared(from), - std::make_shared(to), - std::make_shared(tokenId) - }); - // clang-format on - Data payload; - func.encode(payload); - return payload; -} - -Data TransactionNonTyped::buildERC1155TransferFromCall(const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data) { - // clang-format off - auto func = ABI::Function("safeTransferFrom", ParamCollection{ - std::make_shared(from), - std::make_shared(to), - std::make_shared(tokenId), - std::make_shared(value), - std::make_shared(data) - }); - // clang-format on - Data payload; - func.encode(payload); - return payload; -} - -/// TransactionEip1559 -Data TransactionEip1559::preHash(const uint256_t chainID) const { - return Hash::keccak256(serialize(chainID)); -} - -Data TransactionEip1559::serialize(const uint256_t chainID) const { - Data encoded; - append(encoded, RLP::encode(chainID)); - append(encoded, RLP::encode(nonce)); - append(encoded, RLP::encode(maxInclusionFeePerGas)); - append(encoded, RLP::encode(maxFeePerGas)); - append(encoded, RLP::encode(gasLimit)); - append(encoded, RLP::encode(to)); - append(encoded, RLP::encode(amount)); - append(encoded, RLP::encode(payload)); - append(encoded, EmptyListEncoded); // empty accessList - - Data envelope; - append(envelope, static_cast(type)); - append(envelope, RLP::encodeList(encoded)); - return envelope; -} - -Data TransactionEip1559::encoded(const Signature& signature, const uint256_t chainID) const { - Data encoded; - append(encoded, RLP::encode(chainID)); - append(encoded, RLP::encode(nonce)); - append(encoded, RLP::encode(maxInclusionFeePerGas)); - append(encoded, RLP::encode(maxFeePerGas)); - append(encoded, RLP::encode(gasLimit)); - append(encoded, RLP::encode(to)); - append(encoded, RLP::encode(amount)); - append(encoded, RLP::encode(payload)); - append(encoded, EmptyListEncoded); // empty accessList - append(encoded, RLP::encode(signature.v)); - append(encoded, RLP::encode(signature.r)); - append(encoded, RLP::encode(signature.s)); - - Data envelope; - append(envelope, static_cast(type)); - append(envelope, RLP::encodeList(encoded)); - return envelope; -} - -std::shared_ptr -TransactionEip1559::buildNativeTransfer(const uint256_t& nonce, - const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, - const Data& toAddress, const uint256_t& amount, const Data& data) { - return std::make_shared(nonce, maxInclusionFeePerGas, maxFeePerGas, gasPrice, toAddress, amount, data); -} - -std::shared_ptr -TransactionEip1559::buildERC20Transfer(const uint256_t& nonce, - const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, - const Data& tokenContract, const Data& toAddress, const uint256_t& amount) { - return std::make_shared(nonce, maxInclusionFeePerGas, maxFeePerGas, gasPrice, tokenContract, 0, TransactionNonTyped::buildERC20TransferCall(toAddress, amount)); -} - -std::shared_ptr -TransactionEip1559::buildERC20Approve(const uint256_t& nonce, - const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, - const Data& tokenContract, const Data& spenderAddress, const uint256_t& amount) { - return std::make_shared(nonce, maxInclusionFeePerGas, maxFeePerGas, gasPrice, tokenContract, 0, TransactionNonTyped::buildERC20ApproveCall(spenderAddress, amount)); -} - -std::shared_ptr -TransactionEip1559::buildERC721Transfer(const uint256_t& nonce, - const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, - const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId) { - return std::make_shared(nonce, maxInclusionFeePerGas, maxFeePerGas, gasPrice, tokenContract, 0, TransactionNonTyped::buildERC721TransferFromCall(from, to, tokenId)); -} - -std::shared_ptr -TransactionEip1559::buildERC1155Transfer(const uint256_t& nonce, - const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, - const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data) { - return std::make_shared(nonce, maxInclusionFeePerGas, maxFeePerGas, gasPrice, tokenContract, 0, TransactionNonTyped::buildERC1155TransferFromCall(from, to, tokenId, value, data)); -} - -/// UserOperation -Data UserOperation::preHash(const uint256_t chainID) const { - auto params = ABI::ParamTuple(ParamCollection{ - std::make_shared(32, Hash::keccak256(serialize(chainID))), - std::make_shared(entryPoint), - std::make_shared(chainID)}); - Data encoded; - params.encode(encoded); - return Hash::keccak256(encoded); -} - -Data UserOperation::serialize([[maybe_unused]] const uint256_t chainID) const { - auto params = ABI::ParamTuple(ParamCollection{ - std::make_shared(sender), - std::make_shared(nonce), - std::make_shared(32, Hash::keccak256(initCode)), - std::make_shared(32, Hash::keccak256(payload)), - std::make_shared(gasLimit), - std::make_shared(verificationGasLimit), - std::make_shared(preVerificationGas), - std::make_shared(maxFeePerGas), - std::make_shared(maxInclusionFeePerGas), - std::make_shared(32, Hash::keccak256(paymasterAndData))}); - Data serialized; - params.encode(serialized); - - return serialized; -} - -Data UserOperation::encoded(const Signature& signature, [[maybe_unused]] const uint256_t chainID) const { - Data rawSignature = Signer::simpleStructToSignatureData(signature); - rawSignature[64] += 27; - - const json tx = { - {"sender", hexEncoded(sender)}, - {"nonce", nonce.str()}, - {"initCode", hexEncoded(initCode)}, - {"callData", hexEncoded(payload)}, - {"callGasLimit", gasLimit.str()}, - {"verificationGasLimit", verificationGasLimit.str()}, - {"maxFeePerGas", maxFeePerGas.str()}, - {"maxPriorityFeePerGas", maxInclusionFeePerGas.str()}, - {"paymasterAndData", hexEncoded(paymasterAndData)}, - {"preVerificationGas", preVerificationGas.str()}, - {"signature", hexEncoded(rawSignature)}}; - const auto txString = tx.dump(); - return Data(txString.begin(), txString.end()); -} - -UserOperationPtr -UserOperation::buildNativeTransfer(const Data& entryPointAddress, const Data& senderAddress, - const Data& toAddress, const uint256_t& amount, const uint256_t& nonce, - const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, - const Data& paymasterAndData, const Data& initCode, const Data& payload) { - return std::make_shared( - entryPointAddress, - senderAddress, - nonce, - initCode, - gasLimit, - verificationGasLimit, - maxFeePerGas, - maxInclusionFeePerGas, - preVerificationGas, - Ethereum::getERC4337ExecuteBytecode(toAddress, amount, payload), - paymasterAndData); -} - -UserOperationPtr -UserOperation::buildBatch(const Data& entryPointAddress, const Data& senderAddress, - const std::vector& toAddresses, const std::vector& amounts, const uint256_t& nonce, - const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, - const Data paymasterAndData, const Data& initCode, const std::vector& payloads) { - return std::make_shared( - entryPointAddress, - senderAddress, - nonce, - initCode, - gasLimit, - verificationGasLimit, - maxFeePerGas, - maxInclusionFeePerGas, - preVerificationGas, - Ethereum::getERC4337ExecuteBatchBytecode(toAddresses, amounts, payloads), - paymasterAndData); -} - -UserOperationPtr -UserOperation::buildERC20Transfer(const Data& entryPointAddress, const Data& senderAddress, - const Data& tokenContract, const Data& toAddress, const uint256_t& amount, const uint256_t& nonce, - const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, - const Data& paymasterAndData, const Data& initCode) { - return std::make_shared( - entryPointAddress, - senderAddress, - nonce, - initCode, - gasLimit, - verificationGasLimit, - maxFeePerGas, - maxInclusionFeePerGas, - preVerificationGas, - Ethereum::getERC4337ExecuteBytecode(tokenContract, 0, TransactionNonTyped::buildERC20TransferCall(toAddress, amount)), - paymasterAndData); -} - -UserOperationPtr -UserOperation::buildERC20Approve(const Data& entryPointAddress, const Data& senderAddress, - const Data& tokenContract, const Data& spenderAddress, const uint256_t& amount, const uint256_t& nonce, - const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, - const Data& paymasterAndData, const Data& initCode) { - return std::make_shared( - entryPointAddress, - senderAddress, - nonce, - initCode, - gasLimit, - verificationGasLimit, - maxFeePerGas, - maxInclusionFeePerGas, - preVerificationGas, - Ethereum::getERC4337ExecuteBytecode(tokenContract, 0, TransactionNonTyped::buildERC20ApproveCall(spenderAddress, amount)), - paymasterAndData); -} - -UserOperationPtr -UserOperation::buildERC721Transfer(const Data& entryPointAddress, const Data& senderAddress, - const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& nonce, - const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, - const Data& paymasterAndData, const Data& initCode) { - return std::make_shared( - entryPointAddress, - senderAddress, - nonce, - initCode, - gasLimit, - verificationGasLimit, - maxFeePerGas, - maxInclusionFeePerGas, - preVerificationGas, - Ethereum::getERC4337ExecuteBytecode(tokenContract, 0, TransactionNonTyped::buildERC721TransferFromCall(from, to, tokenId)), - paymasterAndData); -} - -UserOperationPtr -UserOperation::buildERC1155Transfer(const Data& entryPointAddress, const Data& senderAddress, - const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data, const uint256_t& nonce, - const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, - const Data& paymasterAndData, const Data& initCode) { - return std::make_shared( - entryPointAddress, - senderAddress, - nonce, - initCode, - gasLimit, - verificationGasLimit, - maxFeePerGas, - maxInclusionFeePerGas, - preVerificationGas, - Ethereum::getERC4337ExecuteBytecode(tokenContract, 0, TransactionNonTyped::buildERC1155TransferFromCall(from, to, tokenId, value, data)), - paymasterAndData); -} - -} // namespace TW::Ethereum \ No newline at end of file diff --git a/src/Ethereum/Transaction.h b/src/Ethereum/Transaction.h deleted file mode 100644 index b14e2efd44b..00000000000 --- a/src/Ethereum/Transaction.h +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "Address.h" -#include "../uint256.h" - -#include - -namespace TW::Ethereum { - -// Transactions can be: -// - Non-typed (legacy, pre-EIP2718) transactions: -// -- simple ETH transfer -// -- others with payload, function call, e.g. ERC20 transfer -// - Typed transactions (enveloped, EIP2718), with specific type and transaction payload - -/// R-S-V Signature values -struct Signature { -public: - uint256_t r, s, v; -}; - -/// Base class for all transactions. -/// Non-typed and various typed transactions derive from this. -class TransactionBase { -public: - uint256_t nonce; - Data payload; - -public: - TransactionBase(const uint256_t& nonce, const Data& payload): nonce(nonce), payload(payload) {} - virtual ~TransactionBase() {} - // pre-sign hash of the tx, for signing - virtual Data preHash(const uint256_t chainID) const = 0; - // pre-sign image of tx - virtual Data serialize(const uint256_t chainID) const = 0; - // encoded tx (signed) - virtual Data encoded(const Signature& signature, const uint256_t chainID) const = 0; - // Signals wether this tx type uses Eip155-style replay protection in the signature - virtual bool usesReplayProtection() const = 0; - -protected: - TransactionBase() {} -}; - -/// Original transaction format, with no explicit type, legacy as pre-EIP2718 -class TransactionNonTyped: public TransactionBase { -public: - uint256_t gasPrice; - uint256_t gasLimit; - // Public key hash (Address.bytes) - Data to; - uint256_t amount; - - // Factory methods - // Create a native transfer transaction - static std::shared_ptr buildNativeTransfer(const uint256_t& nonce, - const uint256_t& gasPrice, const uint256_t& gasLimit, - const Data& toAddress, const uint256_t& amount, const Data& data = {}); - - // Create an ERC20 token transfer transaction - static std::shared_ptr buildERC20Transfer(const uint256_t& nonce, - const uint256_t& gasPrice, const uint256_t& gasLimit, - const Data& tokenContract, const Data& toAddress, const uint256_t& amount); - - // Create an ERC20 approve transaction - static std::shared_ptr buildERC20Approve(const uint256_t& nonce, - const uint256_t& gasPrice, const uint256_t& gasLimit, - const Data& tokenContract, const Data& spenderAddress, const uint256_t& amount); - - // Create an ERC721 NFT transfer transaction - static std::shared_ptr buildERC721Transfer(const uint256_t& nonce, - const uint256_t& gasPrice, const uint256_t& gasLimit, - const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId); - - // Create an ERC1155 NFT transfer transaction - static std::shared_ptr buildERC1155Transfer(const uint256_t& nonce, - const uint256_t& gasPrice, const uint256_t& gasLimit, - const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data); - - // Helpers for building contract calls - static Data buildERC20TransferCall(const Data& to, const uint256_t& amount); - static Data buildERC20ApproveCall(const Data& spender, const uint256_t& amount); - static Data buildERC721TransferFromCall(const Data& from, const Data& to, const uint256_t& tokenId); - static Data buildERC1155TransferFromCall(const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data); - - virtual Data preHash(const uint256_t chainID) const; - virtual Data serialize(const uint256_t chainID) const; - virtual Data encoded(const Signature& signature, const uint256_t chainID) const; - virtual bool usesReplayProtection() const { return true; } - -public: - TransactionNonTyped(const uint256_t& nonce, - const uint256_t& gasPrice, const uint256_t& gasLimit, - const Data& to, const uint256_t& amount, const Data& payload = {}) - : TransactionBase(nonce, payload) - , gasPrice(std::move(gasPrice)) - , gasLimit(std::move(gasLimit)) - , to(std::move(to)) - , amount(std::move(amount)) {} -}; - -enum TransactionType: uint8_t { - TxType_OptionalAccessList = 0x01, - TxType_Eip1559 = 0x02, - TxType_Eip4337 = 0x03, -}; - -/// Base class for various typed transactions. -class TransactionTyped: public TransactionBase { -public: - // transaction type - TransactionType type; - - TransactionTyped(TransactionType type, const uint256_t& nonce, const Data& payload) - : TransactionBase(nonce, payload), type(type) {} - virtual bool usesReplayProtection() const { return false; } -}; - -/// EIP1559 transaction -class TransactionEip1559: public TransactionTyped { -public: - uint256_t maxInclusionFeePerGas; - uint256_t maxFeePerGas; - uint256_t gasLimit; - // Public key hash (Address.bytes) - Data to; - uint256_t amount; - - // Factory methods - // Create a native transfer transaction - static std::shared_ptr buildNativeTransfer(const uint256_t& nonce, - const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, - const Data& toAddress, const uint256_t& amount, const Data& data = {}); - // Create an ERC20 token transfer transaction - static std::shared_ptr buildERC20Transfer(const uint256_t& nonce, - const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, - const Data& tokenContract, const Data& toAddress, const uint256_t& amount); - // Create an ERC20 approve transaction - static std::shared_ptr buildERC20Approve(const uint256_t& nonce, - const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, - const Data& tokenContract, const Data& spenderAddress, const uint256_t& amount); - // Create an ERC721 NFT transfer transaction - static std::shared_ptr buildERC721Transfer(const uint256_t& nonce, - const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, - const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId); - // Create an ERC1155 NFT transfer transaction - static std::shared_ptr buildERC1155Transfer(const uint256_t& nonce, - const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, - const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data); - - virtual Data preHash(const uint256_t chainID) const; - virtual Data serialize(const uint256_t chainID) const; - virtual Data encoded(const Signature& signature, const uint256_t chainID) const; - -public: - TransactionEip1559(const uint256_t& nonce, - const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasLimit, - const Data& to, const uint256_t& amount, const Data& payload = {}) - : TransactionTyped(TxType_Eip1559, nonce, payload) - , maxInclusionFeePerGas(std::move(maxInclusionFeePerGas)) - , maxFeePerGas(std::move(maxFeePerGas)) - , gasLimit(std::move(gasLimit)) - , to(std::move(to)) - , amount(std::move(amount)) {} -}; - -/// EIP4337 UserOperation -// https://github.com/ethereum/EIPs/blob/3fd65b1a782912bfc18cb975c62c55f733c7c96e/EIPS/eip-4337.md#specification -class UserOperation: public TransactionTyped { - using UserOperationPtr = std::shared_ptr; -public: - Data entryPoint; - Data sender; - Data initCode; - uint256_t gasLimit; - uint256_t verificationGasLimit; - uint256_t maxFeePerGas; - uint256_t maxInclusionFeePerGas; - uint256_t preVerificationGas; - Data paymasterAndData; - - // Factory methods - // Create a native transfer transaction - static UserOperationPtr buildNativeTransfer(const Data& entryPointAddress, const Data& senderAddress, - const Data& toAddress, const uint256_t& amount, const uint256_t& nonce, - const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, - const Data& paymasterAndData = {}, const Data& initCode = {}, const Data& payload = {}); - - // Create a batched transaction for ERC-4337 wallets - static UserOperationPtr buildBatch(const Data& entryPointAddress, const Data& senderAddress, - const std::vector& toAddresses, const std::vector& amounts, const uint256_t& nonce, - const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, - const Data paymasterAndData = {}, const Data& initCode = {}, const std::vector& payloads = {}); - - // Create an ERC20 token transfer transaction - static UserOperationPtr buildERC20Transfer(const Data& entryPointAddress, const Data& senderAddress, - const Data& tokenContract, const Data& toAddress, const uint256_t& amount, const uint256_t& nonce, - const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, - const Data& paymasterAndData = {}, const Data& initCode = {}); - // Create an ERC20 approve transaction - static UserOperationPtr buildERC20Approve(const Data& entryPointAddress, const Data& senderAddress, - const Data& tokenContract, const Data& spenderAddress, const uint256_t& amount, const uint256_t& nonce, - const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, - const Data& paymasterAndData = {}, const Data& initCode = {}); - // Create an ERC721 NFT transfer transaction - static UserOperationPtr buildERC721Transfer(const Data& entryPointAddress, const Data& senderAddress, - const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& nonce, - const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, - const Data& paymasterAndData = {}, const Data& initCode = {}); - // Create an ERC1155 NFT transfer transaction - static UserOperationPtr buildERC1155Transfer(const Data& entryPointAddress, const Data& senderAddress, - const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data, const uint256_t& nonce, - const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, - const Data& paymasterAndData = {}, const Data& initCode = {}); - - virtual Data preHash(const uint256_t chainID) const; - virtual Data serialize(const uint256_t chainID) const; - virtual Data encoded(const Signature& signature, const uint256_t chainID) const; - -public: - UserOperation(const Data& entryPoint, const Data& sender, const uint256_t& nonce, const Data& initCode, - const uint256_t& gasLimit, const uint256_t& verificationGasLimit, const uint256_t& maxFeePerGas, const uint256_t& maxInclusionFeePerGas, const uint256_t& preVerificationGas, - const Data& payload = {}, const Data& paymasterAndData = {}) - : TransactionTyped(TxType_Eip4337, nonce, payload) - , entryPoint(std::move(entryPoint)) - , sender(std::move(sender)) - , initCode(std::move(initCode)) - , gasLimit(std::move(gasLimit)) - , verificationGasLimit(std::move(verificationGasLimit)) - , maxFeePerGas(std::move(maxFeePerGas)) - , maxInclusionFeePerGas(std::move(maxInclusionFeePerGas)) - , preVerificationGas(std::move(preVerificationGas)) - , paymasterAndData(std::move(paymasterAndData)) {} -}; - -} // namespace TW::Ethereum diff --git a/src/Filecoin/Signer.cpp b/src/Filecoin/Signer.cpp index f2f19608ade..c2f2ceafa3e 100644 --- a/src/Filecoin/Signer.cpp +++ b/src/Filecoin/Signer.cpp @@ -7,11 +7,21 @@ #include #include "AddressConverter.h" -#include "Ethereum/Transaction.h" +#include "Ethereum/Entry.h" +#include "Result.h" #include "Signer.h" +#include "proto/Ethereum.pb.h" +#include "proto/TransactionCompiler.pb.h" namespace TW::Filecoin { +Proto::SigningOutput signingOutputError(Common::Proto::SigningError error) { + Proto::SigningOutput outputErr; + outputErr.set_error(error); + outputErr.set_error_message(Common::Proto::SigningError_Name(error)); + return outputErr; +} + // ChainId defines the chain ID used in the Ethereum JSON-RPC endpoint. // As per https://github.com/ethereum-lists/chains static constexpr uint256_t FILECOIN_EIP155_CHAIN_ID = 314; @@ -132,17 +142,38 @@ Proto::SigningOutput Signer::signDelegated(const Proto::SigningInput& input) { } Data toBytes(toEth->bytes.begin(), toEth->bytes.end()); + Ethereum::Proto::SigningInput ethInput; + + auto chainId = store(FILECOIN_EIP155_CHAIN_ID); + auto nonce = store(uint256_t(input.nonce())); + auto gasLimit = store(uint256_t(input.gas_limit())); + + ethInput.set_chain_id(chainId.data(), chainId.size()); + ethInput.set_nonce(nonce.data(), nonce.size()); + ethInput.set_tx_mode(Ethereum::Proto::Enveloped); + ethInput.set_gas_limit(gasLimit.data(), gasLimit.size()); + ethInput.set_max_inclusion_fee_per_gas(input.gas_premium()); + ethInput.set_max_fee_per_gas(input.gas_fee_cap()); + ethInput.set_to_address(toEth->string()); + + auto* transfer = ethInput.mutable_transaction()->mutable_transfer(); + transfer->set_amount(input.value()); + transfer->set_data(params.data(), params.size()); + + // Get an Ethereum EIP1559 native transfer preHash to sign. + auto ethOutputData = Ethereum::Entry().preImageHashes(TWCoinTypeEthereum, data(ethInput.SerializeAsString())); + if (ethOutputData.empty()) { + return signingOutputError(Common::Proto::SigningError::Error_internal); + } + + TxCompiler::Proto::PreSigningOutput ethOutput; + ethOutput.ParseFromArray(ethOutputData.data(), static_cast(ethOutputData.size())); + if (ethOutput.error() != Common::Proto::SigningError::OK) { + return signingOutputError(ethOutput.error()); + } + + auto preHash = data(ethOutput.data_hash()); // Sign transaction as an Ethereum EIP1559 native transfer. - auto ethTransaction = Ethereum::TransactionEip1559::buildNativeTransfer( - /* nonce */ input.nonce(), - /* maxInclusionFeePerGas */ load(input.gas_premium()), - /* maxFeePerGas */ load(input.gas_fee_cap()), - /* gasLimit */ input.gas_limit(), - /* toAddress */ toBytes, - /* amount */ load(input.value()), - /* data */ params - ); - Data preHash = ethTransaction->preHash(FILECOIN_EIP155_CHAIN_ID); Data signature = privateKey.sign(preHash, TWCurveSECP256k1); // Generate a Filecoin signed message. diff --git a/src/Greenfield/SignerEip712.cpp b/src/Greenfield/SignerEip712.cpp index e4a5e91c2c2..ce803cda6f6 100644 --- a/src/Greenfield/SignerEip712.cpp +++ b/src/Greenfield/SignerEip712.cpp @@ -8,7 +8,6 @@ #include "Constants.h" #include "Ethereum/MessageSigner.h" -#include "Ethereum/ABI/ParamStruct.h" #include "HexCoding.h" #include @@ -243,7 +242,7 @@ SigningResult SignerEip712::preImageHash(const Proto::SigningInp } const auto txTypedData = txTypedDataResult.payload(); - const auto txTypedDataHash = Ethereum::ABI::ParamStruct::hashStructJson(txTypedData.dump()); + const auto txTypedDataHash = Ethereum::MessageSigner::typedDataPreImageHash(txTypedData.dump()); return SigningResult::success({.typedData = txTypedData, .typedDataHash = txTypedDataHash}); } diff --git a/src/Harmony/Signer.cpp b/src/Harmony/Signer.cpp index 6fc570d235e..9b262426187 100644 --- a/src/Harmony/Signer.cpp +++ b/src/Harmony/Signer.cpp @@ -12,6 +12,7 @@ namespace TW::Harmony { using INVALID_ENUM = std::integral_constant; +using RLP = TW::Ethereum::RLP; std::tuple Signer::values(const uint256_t& chainID, const Data& signature) noexcept { @@ -307,159 +308,224 @@ void Signer::sign(const PrivateKey& privateKey, const Data& hash, T& transaction } Data Signer::rlpNoHash(const Transaction& transaction, const bool include_vrs) const noexcept { - auto encoded = Data(); - using RLP = TW::Ethereum::RLP; - append(encoded, RLP::encode(transaction.nonce)); - append(encoded, RLP::encode(transaction.gasPrice)); - append(encoded, RLP::encode(transaction.gasLimit)); - append(encoded, RLP::encode(transaction.fromShardID)); - append(encoded, RLP::encode(transaction.toShardID)); - append(encoded, RLP::encode(transaction.to.getKeyHash())); - append(encoded, RLP::encode(transaction.amount)); - append(encoded, RLP::encode(transaction.payload)); + auto nonce = store(transaction.nonce); + auto gasPrice = store(transaction.gasPrice); + auto gasLimit = store(transaction.gasLimit); + auto fromShardID = store(transaction.fromShardID); + auto toShardID = store(transaction.toShardID); + auto toKeyHash = transaction.to.getKeyHash(); + auto amount = store(transaction.amount); + + Data v; + Data r; + Data s; if (include_vrs) { - append(encoded, RLP::encode(transaction.v)); - append(encoded, RLP::encode(transaction.r)); - append(encoded, RLP::encode(transaction.s)); + v = store(transaction.v); + r = store(transaction.r); + s = store(transaction.s); } else { - append(encoded, RLP::encode(chainID)); - append(encoded, RLP::encode(0)); - append(encoded, RLP::encode(0)); + v = store(chainID); + r = store(0); + s = store(0); } - return RLP::encodeList(encoded); -} -template -Data Signer::rlpNoHash(const Staking& transaction, const bool include_vrs) const - noexcept { - auto encoded = Data(); - using RLP = TW::Ethereum::RLP; + EthereumRlp::Proto::EncodingInput input; + auto* rlpList = input.mutable_item()->mutable_list(); + + rlpList->add_items()->set_number_u256(nonce.data(), nonce.size()); + rlpList->add_items()->set_number_u256(gasPrice.data(), gasPrice.size()); + rlpList->add_items()->set_number_u256(gasLimit.data(), gasLimit.size()); + rlpList->add_items()->set_number_u256(fromShardID.data(), fromShardID.size()); + rlpList->add_items()->set_number_u256(toShardID.data(), toShardID.size()); + rlpList->add_items()->set_data(toKeyHash.data(), toKeyHash.size()); + rlpList->add_items()->set_number_u256(amount.data(), amount.size()); + rlpList->add_items()->set_data(transaction.payload.data(), transaction.payload.size()); + + rlpList->add_items()->set_number_u256(v.data(), v.size()); + rlpList->add_items()->set_number_u256(r.data(), r.size()); + rlpList->add_items()->set_number_u256(s.data(), s.size()); - append(encoded, RLP::encode(transaction.directive)); - append(encoded, rlpNoHashDirective(transaction)); + return RLP::encode(input); +} - append(encoded, RLP::encode(transaction.nonce)); - append(encoded, RLP::encode(transaction.gasPrice)); - append(encoded, RLP::encode(transaction.gasLimit)); +template +Data Signer::rlpNoHash(const Staking& transaction, const bool include_vrs) const noexcept { + Data v; + Data r; + Data s; if (include_vrs) { - append(encoded, RLP::encode(transaction.v)); - append(encoded, RLP::encode(transaction.r)); - append(encoded, RLP::encode(transaction.s)); + v = store(transaction.v); + r = store(transaction.r); + s = store(transaction.s); } else { - append(encoded, RLP::encode(chainID)); - append(encoded, RLP::encode(0)); - append(encoded, RLP::encode(0)); + v = store(chainID); + r = store(0); + s = store(0); } - return RLP::encodeList(encoded); + + auto nonce = store(transaction.nonce); + auto gasPrice = store(transaction.gasPrice); + auto gasLimit = store(transaction.gasLimit); + + EthereumRlp::Proto::EncodingInput input; + auto* rlpList = input.mutable_item()->mutable_list(); + + rlpList->add_items()->set_number_u64(transaction.directive); + *rlpList->add_items() = rlpNoHashDirective(transaction); + + rlpList->add_items()->set_number_u256(nonce.data(), nonce.size()); + rlpList->add_items()->set_number_u256(gasPrice.data(), gasPrice.size()); + rlpList->add_items()->set_number_u256(gasLimit.data(), gasLimit.size()); + + rlpList->add_items()->set_number_u256(v.data(), v.size()); + rlpList->add_items()->set_number_u256(r.data(), r.size()); + rlpList->add_items()->set_number_u256(s.data(), s.size()); + + return RLP::encode(input); } -Data Signer::rlpNoHashDirective(const Staking& transaction) const noexcept { - auto encoded = Data(); - using RLP = TW::Ethereum::RLP; +template +EthereumRlp::Proto::RlpItem Signer::rlpPrepareDescription(const Staking& transaction) const noexcept { + EthereumRlp::Proto::RlpItem item; + auto* rlpList = item.mutable_list(); - append(encoded, RLP::encode(transaction.stakeMsg.validatorAddress.getKeyHash())); + rlpList->add_items()->set_string_item(transaction.stakeMsg.description.name); + rlpList->add_items()->set_string_item(transaction.stakeMsg.description.identity); + rlpList->add_items()->set_string_item(transaction.stakeMsg.description.website); + rlpList->add_items()->set_string_item(transaction.stakeMsg.description.securityContact); + rlpList->add_items()->set_string_item(transaction.stakeMsg.description.details); - auto descriptionEncoded = Data(); - append(descriptionEncoded, RLP::encode(transaction.stakeMsg.description.name)); - append(descriptionEncoded, RLP::encode(transaction.stakeMsg.description.identity)); - append(descriptionEncoded, RLP::encode(transaction.stakeMsg.description.website)); - append(descriptionEncoded, RLP::encode(transaction.stakeMsg.description.securityContact)); - append(descriptionEncoded, RLP::encode(transaction.stakeMsg.description.details)); - append(encoded, RLP::encodeList(descriptionEncoded)); + return item; +} - auto commissionEncoded = Data(); +EthereumRlp::Proto::RlpItem Signer::rlpPrepareCommissionRates(const Staking &transaction) noexcept { + auto rateValue = store(transaction.stakeMsg.commissionRates.rate.value); + auto maxRateValue = store(transaction.stakeMsg.commissionRates.maxRate.value); + auto maxChangeRateValue = store(transaction.stakeMsg.commissionRates.maxChangeRate.value); - auto rateEncoded = Data(); - append(rateEncoded, RLP::encode(transaction.stakeMsg.commissionRates.rate.value)); - append(commissionEncoded, RLP::encodeList(rateEncoded)); + EthereumRlp::Proto::RlpItem item; + auto* commissionList = item.mutable_list(); - auto maxRateEncoded = Data(); - append(maxRateEncoded, RLP::encode(transaction.stakeMsg.commissionRates.maxRate.value)); - append(commissionEncoded, RLP::encodeList(maxRateEncoded)); + // Append `commission::rate` properties list with a single item. + auto* rateList = commissionList->add_items()->mutable_list(); + rateList->add_items()->set_number_u256(rateValue.data(), rateValue.size()); - auto maxChangeRateEncoded = Data(); - append(maxChangeRateEncoded, - RLP::encode(transaction.stakeMsg.commissionRates.maxChangeRate.value)); - append(commissionEncoded, RLP::encodeList(maxChangeRateEncoded)); + // Append `commission::maxRate` properties list. + auto* maxRateList = commissionList->add_items()->mutable_list(); + maxRateList->add_items()->set_number_u256(maxRateValue.data(), maxRateValue.size()); - append(encoded, RLP::encodeList(commissionEncoded)); + // Append `commission::maxChangeRate` properties list. + auto* maxChangeRateList = commissionList->add_items()->mutable_list(); + maxChangeRateList->add_items()->set_number_u256(maxChangeRateValue.data(), maxChangeRateValue.size()); - append(encoded, RLP::encode(transaction.stakeMsg.minSelfDelegation)); - append(encoded, RLP::encode(transaction.stakeMsg.maxTotalDelegation)); + return item; +} - auto slotPubKeysEncoded = Data(); - for (auto pk : transaction.stakeMsg.slotPubKeys) { - append(slotPubKeysEncoded, RLP::encode(pk)); +EthereumRlp::Proto::RlpItem Signer::rlpNoHashDirective(const Staking& transaction) const noexcept { + auto validatorKeyHash = transaction.stakeMsg.validatorAddress.getKeyHash(); + auto minSelfDelegation = store(transaction.stakeMsg.minSelfDelegation); + auto maxTotalDelegation = store(transaction.stakeMsg.maxTotalDelegation); + auto amount = store(transaction.stakeMsg.amount); + + EthereumRlp::Proto::RlpItem item; + auto* rlpList = item.mutable_list(); + + rlpList->add_items()->set_data(validatorKeyHash.data(), validatorKeyHash.size()); + *rlpList->add_items() = rlpPrepareDescription(transaction); + + // Append `commission` properties list of `rate`, `maxRate`, `maxChangeRate` sublists. + *rlpList->add_items() = rlpPrepareCommissionRates(transaction); + + rlpList->add_items()->set_number_u256(minSelfDelegation.data(), minSelfDelegation.size()); + rlpList->add_items()->set_number_u256(maxTotalDelegation.data(), maxTotalDelegation.size()); + + // Append a list of slot public keys. + auto* slotPubkeysList = rlpList->add_items()->mutable_list(); + for (const auto& pk : transaction.stakeMsg.slotPubKeys) { + slotPubkeysList->add_items()->set_data(pk.data(), pk.size()); } - append(encoded, RLP::encodeList(slotPubKeysEncoded)); - auto slotBlsSigsEncoded = Data(); - for (auto sig : transaction.stakeMsg.slotKeySigs) { - append(slotBlsSigsEncoded, RLP::encode(sig)); + // Append a list of slot key signatures. + auto* slotKeySigsList = rlpList->add_items()->mutable_list(); + for (const auto& sign : transaction.stakeMsg.slotKeySigs) { + slotKeySigsList->add_items()->set_data(sign.data(), sign.size()); } - append(encoded, RLP::encodeList(slotBlsSigsEncoded)); - append(encoded, RLP::encode(transaction.stakeMsg.amount)); + rlpList->add_items()->set_number_u256(amount.data(), amount.size()); - return RLP::encodeList(encoded); + return item; } -Data Signer::rlpNoHashDirective(const Staking& transaction) const noexcept { - auto encoded = Data(); - using RLP = TW::Ethereum::RLP; +EthereumRlp::Proto::RlpItem Signer::rlpNoHashDirective(const Staking& transaction) const noexcept { + auto validatorKeyHash = transaction.stakeMsg.validatorAddress.getKeyHash(); + auto minSelfDelegation = store(transaction.stakeMsg.minSelfDelegation); + auto maxTotalDelegation = store(transaction.stakeMsg.maxTotalDelegation); + auto active = store(transaction.stakeMsg.active); - append(encoded, RLP::encode(transaction.stakeMsg.validatorAddress.getKeyHash())); + EthereumRlp::Proto::RlpItem item; + auto* rlpList = item.mutable_list(); - auto descriptionEncoded = Data(); - append(descriptionEncoded, RLP::encode(transaction.stakeMsg.description.name)); - append(descriptionEncoded, RLP::encode(transaction.stakeMsg.description.identity)); - append(descriptionEncoded, RLP::encode(transaction.stakeMsg.description.website)); - append(descriptionEncoded, RLP::encode(transaction.stakeMsg.description.securityContact)); - append(descriptionEncoded, RLP::encode(transaction.stakeMsg.description.details)); - append(encoded, RLP::encodeList(descriptionEncoded)); + rlpList->add_items()->set_data(validatorKeyHash.data(), validatorKeyHash.size()); + *rlpList->add_items() = rlpPrepareDescription(transaction); - auto decEncoded = Data(); + auto* commissionRateList = rlpList->add_items()->mutable_list(); if (transaction.stakeMsg.commissionRate.has_value()) { // Note: std::optional.value() is not available in XCode with target < iOS 12; using '*' - append(decEncoded, RLP::encode((*transaction.stakeMsg.commissionRate).value)); + auto commissionRateValue = store((*transaction.stakeMsg.commissionRate).value); + commissionRateList->add_items()->set_number_u256(commissionRateValue.data(), commissionRateValue.size()); } - append(encoded, RLP::encodeList(decEncoded)); - append(encoded, RLP::encode(transaction.stakeMsg.minSelfDelegation)); - append(encoded, RLP::encode(transaction.stakeMsg.maxTotalDelegation)); + rlpList->add_items()->set_number_u256(minSelfDelegation.data(), minSelfDelegation.size()); + rlpList->add_items()->set_number_u256(maxTotalDelegation.data(), maxTotalDelegation.size()); - append(encoded, RLP::encode(transaction.stakeMsg.slotKeyToRemove)); - append(encoded, RLP::encode(transaction.stakeMsg.slotKeyToAdd)); - append(encoded, RLP::encode(transaction.stakeMsg.slotKeyToAddSig)); + rlpList->add_items()->set_data(transaction.stakeMsg.slotKeyToRemove.data(), transaction.stakeMsg.slotKeyToRemove.size()); + rlpList->add_items()->set_data(transaction.stakeMsg.slotKeyToAdd.data(), transaction.stakeMsg.slotKeyToAdd.size()); + rlpList->add_items()->set_data(transaction.stakeMsg.slotKeyToAddSig.data(), transaction.stakeMsg.slotKeyToAddSig.size()); - append(encoded, RLP::encode(transaction.stakeMsg.active)); + rlpList->add_items()->set_number_u256(active.data(), active.size()); - return RLP::encodeList(encoded); + return item; } -Data Signer::rlpNoHashDirective(const Staking& transaction) const noexcept { - auto encoded = Data(); - using RLP = TW::Ethereum::RLP; - append(encoded, RLP::encode(transaction.stakeMsg.delegatorAddress.getKeyHash())); - append(encoded, RLP::encode(transaction.stakeMsg.validatorAddress.getKeyHash())); - append(encoded, RLP::encode(transaction.stakeMsg.amount)); - return RLP::encodeList(encoded); +EthereumRlp::Proto::RlpItem Signer::rlpNoHashDirective(const Staking& transaction) const noexcept { + auto delegatorKeyHash = transaction.stakeMsg.delegatorAddress.getKeyHash(); + auto validatorKeyHash = transaction.stakeMsg.validatorAddress.getKeyHash(); + auto amount = store(transaction.stakeMsg.amount); + + EthereumRlp::Proto::RlpItem item; + auto* rlpList = item.mutable_list(); + + rlpList->add_items()->set_data(delegatorKeyHash.data(), delegatorKeyHash.size()); + rlpList->add_items()->set_data(validatorKeyHash.data(), validatorKeyHash.size()); + rlpList->add_items()->set_number_u256(amount.data(), amount.size()); + + return item; } -Data Signer::rlpNoHashDirective(const Staking& transaction) const noexcept { - auto encoded = Data(); - using RLP = TW::Ethereum::RLP; - append(encoded, RLP::encode(transaction.stakeMsg.delegatorAddress.getKeyHash())); - append(encoded, RLP::encode(transaction.stakeMsg.validatorAddress.getKeyHash())); - append(encoded, RLP::encode(transaction.stakeMsg.amount)); - return RLP::encodeList(encoded); +EthereumRlp::Proto::RlpItem Signer::rlpNoHashDirective(const Staking& transaction) const noexcept { + auto delegatorKeyHash = transaction.stakeMsg.delegatorAddress.getKeyHash(); + auto validatorKeyHash = transaction.stakeMsg.validatorAddress.getKeyHash(); + auto amount = store(transaction.stakeMsg.amount); + + EthereumRlp::Proto::RlpItem item; + auto* rlpList = item.mutable_list(); + + rlpList->add_items()->set_data(delegatorKeyHash.data(), delegatorKeyHash.size()); + rlpList->add_items()->set_data(validatorKeyHash.data(), validatorKeyHash.size()); + rlpList->add_items()->set_number_u256(amount.data(), amount.size()); + + return item; } -Data Signer::rlpNoHashDirective(const Staking& transaction) const noexcept { - auto encoded = Data(); - using RLP = TW::Ethereum::RLP; - append(encoded, RLP::encode(transaction.stakeMsg.delegatorAddress.getKeyHash())); - return RLP::encodeList(encoded); +EthereumRlp::Proto::RlpItem Signer::rlpNoHashDirective(const Staking& transaction) const noexcept { + auto delegatorKeyHash = transaction.stakeMsg.delegatorAddress.getKeyHash(); + + EthereumRlp::Proto::RlpItem item; + auto* rlpList = item.mutable_list(); + + rlpList->add_items()->set_data(delegatorKeyHash.data(), delegatorKeyHash.size()); + + return item; } std::string Signer::txnAsRLPHex(Transaction& transaction) const noexcept { diff --git a/src/Harmony/Signer.h b/src/Harmony/Signer.h index 71a45b162b1..9a24142279e 100644 --- a/src/Harmony/Signer.h +++ b/src/Harmony/Signer.h @@ -12,6 +12,7 @@ #include "../Hash.h" #include "../PrivateKey.h" #include "../proto/Harmony.pb.h" +#include "../proto/EthereumRlp.pb.h" #include #include @@ -96,11 +97,16 @@ class Signer { template Data rlpNoHash(const Staking &transaction, const bool) const noexcept; - Data rlpNoHashDirective(const Staking &transaction) const noexcept; - Data rlpNoHashDirective(const Staking &transaction) const noexcept; - Data rlpNoHashDirective(const Staking &transaction) const noexcept; - Data rlpNoHashDirective(const Staking &transaction) const noexcept; - Data rlpNoHashDirective(const Staking &transaction) const noexcept; + EthereumRlp::Proto::RlpItem rlpNoHashDirective(const Staking &transaction) const noexcept; + EthereumRlp::Proto::RlpItem rlpNoHashDirective(const Staking &transaction) const noexcept; + EthereumRlp::Proto::RlpItem rlpNoHashDirective(const Staking &transaction) const noexcept; + EthereumRlp::Proto::RlpItem rlpNoHashDirective(const Staking &transaction) const noexcept; + EthereumRlp::Proto::RlpItem rlpNoHashDirective(const Staking &transaction) const noexcept; + + template + EthereumRlp::Proto::RlpItem rlpPrepareDescription(const Staking& transaction) const noexcept; + + static EthereumRlp::Proto::RlpItem rlpPrepareCommissionRates(const Staking &transaction) noexcept; }; } // namespace TW::Harmony diff --git a/src/Hash.cpp b/src/Hash.cpp index 9556cca78f2..389854338ec 100644 --- a/src/Hash.cpp +++ b/src/Hash.cpp @@ -94,16 +94,27 @@ Data Hash::blake256(const byte* data, size_t size) { } Data Hash::blake2b(const byte* data, size_t dataSize) { - return Rust::CByteArrayWrapper(Rust::blake2_b(data, dataSize, 32)).data; + Rust::CByteArrayResultWrapper res = Rust::blake2_b(data, dataSize, 32); + if (res.isErr()) { + throw std::runtime_error("Error 'blake2_b' hashing"); + } + return res.unwrap().data; } Data Hash::blake2b(const byte* data, size_t dataSize, size_t hashSize) { - return Rust::CByteArrayWrapper(Rust::blake2_b(data, dataSize, hashSize)).data; + Rust::CByteArrayResultWrapper res = Rust::blake2_b(data, dataSize, hashSize); + if (res.isErr()) { + throw std::runtime_error("Error 'blake2_b' hashing"); + } + return res.unwrap().data; } Data Hash::blake2b(const byte* data, size_t dataSize, size_t hashSize, const Data& personal) { - Rust::CByteArrayWrapper res = Rust::blake2_b_personal(data, dataSize, hashSize, personal.data(), personal.size()); - return res.data; + Rust::CByteArrayResultWrapper res = Rust::blake2_b_personal(data, dataSize, hashSize, personal.data(), personal.size()); + if (res.isErr()) { + throw std::runtime_error("Error 'blake2_b_personal' hashing"); + } + return res.unwrap().data; } Data Hash::groestl512(const byte* data, size_t size) { diff --git a/src/ImmutableX/StarkKey.cpp b/src/ImmutableX/StarkKey.cpp index 2f2604ccadd..f550b7ab14d 100644 --- a/src/ImmutableX/StarkKey.cpp +++ b/src/ImmutableX/StarkKey.cpp @@ -4,8 +4,6 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include -#include #include #include #include @@ -47,44 +45,9 @@ PrivateKey getPrivateKeyFromEthPrivKey(const PrivateKey& ethPrivKey) { PrivateKey getPrivateKeyFromRawSignature(const Data& signature, const DerivationPath& derivationPath) { using namespace internal; - //auto data = parse_hex(signature); - auto ethSignature = Ethereum::Signer::signatureDataToStructSimple(signature); - auto seed = store(ethSignature.s); + // The signature is `rsv`, where `s` starts at 32 and is 32 long. + auto seed = subData(signature, 32, 32); return getPrivateKeyFromSeed(seed, derivationPath); } -Data getPublicKeyFromPrivateKey(const Data& privateKey) { - auto pubKey = Rust::starknet_pubkey_from_private(hex(privateKey).c_str()); - if (pubKey.code != Rust::OK_CODE) { - return {}; - } - const auto toReturn = parse_hex(pubKey.result, true); - Rust::free_string(pubKey.result); - return toReturn; -} - -Data sign(const Data& privateKey, const Data& digest) { - auto privKeyStr = hex(privateKey); - auto hexDigest = hex(digest); - auto resultSignature = Rust::starknet_sign(privKeyStr.c_str(), hexDigest.c_str()); - if (resultSignature.code != Rust::OK_CODE) { - return {}; - } - auto toReturn = parse_hex(resultSignature.result); - Rust::free_string(resultSignature.result); - return toReturn; -} - -bool verify(const Data& pubKey, const Data& signature, const Data& digest) { - if (signature.size() != 64) { - return false; - } - auto r = hex(subData(signature, 0, 32)); - auto s = hex(subData(signature, 32)); - auto pubKeyStr = hex(pubKey); - auto digestStr = hex(digest); - const auto res = Rust::starknet_verify(pubKeyStr.c_str(), digestStr.c_str(), r.c_str(), s.c_str()); - return res.code == Rust::OK_CODE && res.result; -} - } // namespace TW::ImmutableX diff --git a/src/ImmutableX/StarkKey.h b/src/ImmutableX/StarkKey.h index 82e3c4eae49..5c2643a56da 100644 --- a/src/ImmutableX/StarkKey.h +++ b/src/ImmutableX/StarkKey.h @@ -23,10 +23,4 @@ PrivateKey getPrivateKeyFromEthPrivKey(const PrivateKey& ethPrivKey); PrivateKey getPrivateKeyFromRawSignature(const Data& signature, const DerivationPath& derivationPath); -Data getPublicKeyFromPrivateKey(const Data& privateKey); - -Data sign(const Data &privateKey, const Data& digest); - -bool verify(const Data &pubKey, const Data& signature, const Data& digest); - } // namespace TW::ImmutableX diff --git a/src/LiquidStaking/LiquidStaking.cpp b/src/LiquidStaking/LiquidStaking.cpp index a50c26d725e..b635e5c268c 100644 --- a/src/LiquidStaking/LiquidStaking.cpp +++ b/src/LiquidStaking/LiquidStaking.cpp @@ -15,8 +15,6 @@ // ETH #include "Ethereum/ABI/Function.h" -#include "Ethereum/ABI/ParamAddress.h" -#include "Ethereum/ABI/ParamBase.h" #include "Ethereum/Address.h" #include "proto/Ethereum.pb.h" #include "uint256.h" @@ -35,7 +33,6 @@ struct PairHash { using EVMLiquidStakingFunctionRegistry = std::unordered_map; using EVMLiquidStakingParamsRegistry = std::unordered_map; using EVMLiquidStakingRegistry = std::unordered_map; -using Params = std::vector>; static const EVMLiquidStakingFunctionRegistry gStraderFunctionRegistry = {{std::make_pair(Proto::POLYGON, Action::Stake), "swapMaticForMaticXViaInstantPool"}, @@ -62,29 +59,35 @@ namespace internal { } void handleStake(const Proto::Stake& stake, const Proto::Blockchain& blockchain, Data& payload, uint256_t& amount, const Proto::Protocol protocol) { - Params params; + Ethereum::ABI::BaseParams params; if (protocol == Proto::Lido) { - params.emplace_back(std::make_shared()); + params.emplace_back(std::make_shared()); + } + auto funcData = Ethereum::ABI::Function::encodeFunctionCall(gEVMLiquidStakingRegistry.at(protocol).at({blockchain, Action::Stake}), params); + if (funcData.has_value()) { + payload = funcData.value(); } - auto func = Ethereum::ABI::Function(gEVMLiquidStakingRegistry.at(protocol).at({blockchain, Action::Stake}), params); - func.encode(payload); amount = uint256_t(stake.amount()); } void handleUnstake(const Proto::Unstake& unstake, const Proto::Blockchain& blockchain, Data& payload) { - Params params; - params.emplace_back(std::make_shared(uint256_t(unstake.amount()))); + Ethereum::ABI::BaseParams params; + params.emplace_back(std::make_shared(uint256_t(unstake.amount()))); auto functionName = gStraderFunctionRegistry.at({blockchain, Action::Unstake}); - auto func = Ethereum::ABI::Function(functionName, params); - func.encode(payload); + auto funcData = Ethereum::ABI::Function::encodeFunctionCall(functionName, params); + if (funcData.has_value()) { + payload = funcData.value(); + } } void handleWithdraw(const Proto::Withdraw& withdraw, const Proto::Blockchain& blockchain, Data& payload) { - Params params; - params.emplace_back(std::make_shared(uint256_t(withdraw.idx()))); + Ethereum::ABI::BaseParams params; + params.emplace_back(std::make_shared(uint256_t(withdraw.idx()))); auto functionName = gStraderFunctionRegistry.at({blockchain, Action::Withdraw}); - auto func = Ethereum::ABI::Function(functionName, params); - func.encode(payload); + auto funcData = Ethereum::ABI::Function::encodeFunctionCall(functionName, params); + if (funcData.has_value()) { + payload = funcData.value(); + } } } diff --git a/src/PrivateKey.cpp b/src/PrivateKey.cpp index 3e5a7787428..5707103c8d1 100644 --- a/src/PrivateKey.cpp +++ b/src/PrivateKey.cpp @@ -25,6 +25,36 @@ using namespace TW; +Data rust_get_public_from_private(const Data& key, TWPublicKeyType public_type) { + auto* privkey = Rust::tw_private_key_create_with_data(key.data(), key.size()); + if (privkey == nullptr) { + return {}; + } + Data toReturn; + + auto* pubkey = Rust::tw_private_key_get_public_key_by_type(privkey, static_cast(public_type)); + if (pubkey == nullptr) { + Rust::tw_private_key_delete(privkey); + return {}; + } + + Rust::CByteArrayWrapper res = Rust::tw_public_key_data(pubkey); + + Rust::tw_public_key_delete(pubkey); + Rust::tw_private_key_delete(privkey); + return res.data; +} + +Data rust_private_key_sign(const Data& key, const Data& hash, TWCurve curve) { + auto* priv = Rust::tw_private_key_create_with_data(key.data(), key.size()); + if (priv == nullptr) { + return {}; + } + Rust::CByteArrayWrapper res = Rust::tw_private_key_sign(priv, hash.data(), hash.size(), static_cast(curve)); + Rust::tw_private_key_delete(priv); + return res.data; +} + bool PrivateKey::isValid(const Data& data) { // Check length if (data.size() != _size && data.size() != cardanoKeySize) { @@ -162,10 +192,7 @@ PublicKey PrivateKey::getPublicKey(TWPublicKeyType type) const { } case TWPublicKeyTypeStarkex: { - result = ImmutableX::getPublicKeyFromPrivateKey(this->bytes); - if (result.size() == PublicKey::starkexSize - 1) { - result.insert(result.begin(), 0); - } + result = rust_get_public_from_private(this->bytes, type); break; } } @@ -184,43 +211,42 @@ Data PrivateKey::sign(const Data& digest, TWCurve curve) const { Data result; bool success = false; switch (curve) { - case TWCurveSECP256k1: { - result.resize(65); - success = ecdsa_sign_digest_checked(&secp256k1, key().data(), digest.data(), digest.size(), result.data(), result.data() + 64, nullptr) == 0; - } break; - case TWCurveED25519: { - result.resize(64); - ed25519_sign(digest.data(), digest.size(), key().data(), result.data()); - success = true; - } break; - case TWCurveED25519Blake2bNano: { - result.resize(64); - ed25519_sign_blake2b(digest.data(), digest.size(), key().data(), result.data()); - success = true; - } break; - case TWCurveED25519ExtendedCardano: { - result.resize(64); - ed25519_sign_ext(digest.data(), digest.size(), key().data(), extension().data(), result.data()); - success = true; - } break; - case TWCurveCurve25519: { - result.resize(64); - const auto publicKey = getPublicKey(TWPublicKeyTypeED25519); - ed25519_sign(digest.data(), digest.size(), key().data(), result.data()); - const auto sign_bit = publicKey.bytes[31] & 0x80; - result[63] = result[63] & 127; - result[63] |= sign_bit; - success = true; - } break; - case TWCurveNIST256p1: { - result.resize(65); - success = ecdsa_sign_digest_checked(&nist256p1, key().data(), digest.data(), digest.size(), result.data(), result.data() + 64, nullptr) == 0; - } break; + case TWCurveSECP256k1: { + result.resize(65); + success = ecdsa_sign_digest_checked(&secp256k1, key().data(), digest.data(), digest.size(), result.data(), result.data() + 64, nullptr) == 0; + } break; + case TWCurveED25519: { + result.resize(64); + ed25519_sign(digest.data(), digest.size(), key().data(), result.data()); + success = true; + } break; + case TWCurveED25519Blake2bNano: { + result.resize(64); + ed25519_sign_blake2b(digest.data(), digest.size(), key().data(), result.data()); + success = true; + } break; + case TWCurveED25519ExtendedCardano: { + result.resize(64); + ed25519_sign_ext(digest.data(), digest.size(), key().data(), extension().data(), result.data()); + success = true; + } break; + case TWCurveCurve25519: { + result.resize(64); + const auto publicKey = getPublicKey(TWPublicKeyTypeED25519); + ed25519_sign(digest.data(), digest.size(), key().data(), result.data()); + const auto sign_bit = publicKey.bytes[31] & 0x80; + result[63] = result[63] & 127; + result[63] |= sign_bit; + success = true; + } break; + case TWCurveNIST256p1: { + result.resize(65); + success = ecdsa_sign_digest_checked(&nist256p1, key().data(), digest.data(), digest.size(), result.data(), result.data() + 64, nullptr) == 0; + } break; case TWCurveStarkex: { - result = ImmutableX::sign(this->bytes, digest); - success = true; - break; - } + result = rust_private_key_sign(key(), digest, curve); + success = result.size() == 64; + } break; case TWCurveNone: default: break; diff --git a/src/PublicKey.cpp b/src/PublicKey.cpp index 8c6fd35c569..2609b4216da 100644 --- a/src/PublicKey.cpp +++ b/src/PublicKey.cpp @@ -7,6 +7,7 @@ #include "PublicKey.h" #include "PrivateKey.h" #include "Data.h" +#include "rust/bindgen/WalletCoreRSBindgen.h" #include #include @@ -132,6 +133,16 @@ PublicKey PublicKey::extended() const { } } +bool rust_public_key_verify(const Data& key, TWPublicKeyType type, const Data& sig, const Data& msgHash) { + auto* pubkey = Rust::tw_public_key_create_with_data(key.data(), key.size(), static_cast(type)); + if (pubkey == nullptr) { + return {}; + } + bool verified = Rust::tw_public_key_verify(pubkey, sig.data(), sig.size(), msgHash.data(), msgHash.size()); + Rust::tw_public_key_delete(pubkey); + return verified; +} + bool PublicKey::verify(const Data& signature, const Data& message) const { switch (type) { case TWPublicKeyTypeSECP256k1: @@ -163,7 +174,7 @@ bool PublicKey::verify(const Data& signature, const Data& message) const { return ed25519_sign_open(message.data(), message.size(), ed25519PublicKey.data(), verifyBuffer.data()) == 0; } case TWPublicKeyTypeStarkex: - return ImmutableX::verify(this->bytes, signature, message); + return rust_public_key_verify(bytes, type, signature, message); default: throw std::logic_error("Not yet implemented"); } diff --git a/src/Ronin/Address.cpp b/src/Ronin/Address.cpp deleted file mode 100644 index a64e9d0383c..00000000000 --- a/src/Ronin/Address.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Address.h" -#include "Ethereum/Address.h" -#include "Ethereum/AddressChecksum.h" -#include "../Hash.h" -#include "../HexCoding.h" - -const std::string prefix = "ronin:"; - -namespace TW::Ronin { - -bool Address::isValid(const std::string& string) { - // check prefix - if (string.compare(0, prefix.length(), prefix) == 0) { - const auto suffix = string.substr(prefix.length()); - const auto data = parse_hex(suffix); - return Ethereum::Address::isValid(data); - } - // accept Ethereum format as well - if (Ethereum::Address::isValid(string)) { - return true; - } - return false; -} - -Address::Address(const std::string& string) { - // check prefix - if (string.compare(0, prefix.length(), prefix) == 0) { - const auto suffix = string.substr(prefix.length()); - const auto data = parse_hex(suffix); - std::copy(data.begin(), data.end(), bytes.begin()); - } else if (Ethereum::Address::isValid(string)) { - // accept Ethereum format as well - Ethereum::Address ethereumAddress(string); - bytes = ethereumAddress.bytes; - } else { - throw std::invalid_argument("Invalid address data"); - } -} - -// Normalized: with ronin prefix, checksummed hex address, no 0x prefix -std::string Address::string() const { - std::string address = Ethereum::checksumed(*this); - if (address.size() >= 2 && address.substr(0, 2) == "0x") { - address = address.substr(2); - } // skip 0x - return prefix + address; -} - -} // namespace TW::Ronin diff --git a/src/Ronin/Address.h b/src/Ronin/Address.h deleted file mode 100644 index 24369e09d87..00000000000 --- a/src/Ronin/Address.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "../PublicKey.h" -#include "../Ethereum/Address.h" -#include -#include - -namespace TW::Ronin { - -class Address: public Ethereum::Address { - public: - /// Determines whether a string makes a valid address. - static bool isValid(const std::string& string); - - /// Initializes an address with a string representation. - explicit Address(const std::string& string); - - /// Initializes an address with a public key. - explicit Address(const PublicKey& publicKey): Ethereum::Address(publicKey) {} - - /// Returns a string representation of the address. - std::string string() const; -}; - -} // namespace TW::Ronin diff --git a/src/Ronin/Entry.cpp b/src/Ronin/Entry.cpp index c70d2cca7b4..7889d28c786 100644 --- a/src/Ronin/Entry.cpp +++ b/src/Ronin/Entry.cpp @@ -6,37 +6,34 @@ #include "Entry.h" -#include "Address.h" -#include "../Ethereum/Signer.h" - -using namespace TW; -using namespace std; +#include "Ethereum/Entry.h" +#include "HexCoding.h" namespace TW::Ronin { -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - return Address::isValid(address); +bool Entry::validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const { + return validateAddressRust(coin, address, addressPrefix); } -string Entry::normalizeAddress([[maybe_unused]] TWCoinType coin, const string& address) const { - return Address(address).string(); +std::string Entry::normalizeAddress(TWCoinType coin, const std::string& address) const { + return normalizeAddressRust(coin, address); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - return Address(publicKey).string(); +std::string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const { + return deriveAddressRust(coin, publicKey, derivation, addressPrefix); } -Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { - const auto addr = Address(address); - return {addr.bytes.begin(), addr.bytes.end()}; +Data Entry::addressToData(TWCoinType coin, const std::string& address) const { + return addressToDataRust(coin, address); } -void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { - signTemplate(dataIn, dataOut); +void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { + signRust(dataIn, coin, dataOut); } -string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { - return Ethereum::Signer::signJSON(json, key); +// TODO call `signRustJSON` when it's done. +std::string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { + return Ethereum::Entry().signJSON(coin, json, key); } } // namespace TW::Ronin diff --git a/src/Ronin/Entry.h b/src/Ronin/Entry.h index e8ea769ff6e..356c1940d7c 100644 --- a/src/Ronin/Entry.h +++ b/src/Ronin/Entry.h @@ -14,12 +14,12 @@ namespace TW::Ronin { class Entry final : public CoinEntry { public: bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string normalizeAddress(TWCoinType coin, const std::string& address) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; - Data addressToData(TWCoinType coin, const std::string& address) const; - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - bool supportsJSONSigning() const { return true; } - std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; + std::string normalizeAddress(TWCoinType coin, const std::string& address) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool supportsJSONSigning() const { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; }; } // namespace TW::Ronin diff --git a/src/THORChain/Swap.cpp b/src/THORChain/Swap.cpp index 9d2f4278524..dea097d1333 100644 --- a/src/THORChain/Swap.cpp +++ b/src/THORChain/Swap.cpp @@ -19,8 +19,6 @@ #include "../proto/Bitcoin.pb.h" // ETH #include "Ethereum/ABI/Function.h" -#include "Ethereum/ABI/ParamAddress.h" -#include "Ethereum/ABI/ParamBase.h" #include "Ethereum/Address.h" #include "uint256.h" #include "../proto/Ethereum.pb.h" @@ -273,15 +271,19 @@ SwapBundled SwapBuilder::buildEth(const uint256_t& amount, const std::string& me mExpirationPolicy = std::chrono::duration_cast(in_15_minutes.time_since_epoch()).count(); } auto& transfer = *input.mutable_transaction()->mutable_contract_generic(); - auto func = Ethereum::ABI::Function("depositWithExpiry", std::vector>{ - std::make_shared(vaultAddressBin), - std::make_shared(toAssetAddressBin), - std::make_shared(uint256_t(amount)), - std::make_shared(memo), - std::make_shared(uint256_t(*mExpirationPolicy))}); - Data payload; - func.encode(payload); - transfer.set_data(payload.data(), payload.size()); + + // Ethereum::ABI::AbiProto::NamedParam + auto payload = Ethereum::ABI::Function::encodeFunctionCall("depositWithExpiry", { + std::make_shared(mVaultAddress), + std::make_shared(toTokenId), + std::make_shared(amount), + std::make_shared(memo), + std::make_shared(uint256_t(*mExpirationPolicy)), + }); + if (payload.has_value()) { + transfer.set_data(payload.value().data(), payload.value().size()); + } + Data amountData = store(uint256_t(0)); // if tokenId is set to 0x0000000000000000000000000000000000000000 this means we are sending ethereum and transfer amount also need to be set if (toTokenId == "0x0000000000000000000000000000000000000000") { diff --git a/src/Theta/Entry.cpp b/src/Theta/Entry.cpp index 7590fba906c..803877dc1e3 100644 --- a/src/Theta/Entry.cpp +++ b/src/Theta/Entry.cpp @@ -12,6 +12,24 @@ namespace TW::Theta { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { + return Ethereum::Address::isValid(address); +} + +std::string Entry::normalizeAddress([[maybe_unused]] TWCoinType coin, const std::string& address) const { + // normalized with EIP55 checksum + return Ethereum::Address(address).string(); +} + +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { + return Ethereum::Address(publicKey).string(); +} + +Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { + const auto addr = Ethereum::Address(address); + return {addr.bytes.begin(), addr.bytes.end()}; +} + void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } diff --git a/src/Theta/Entry.h b/src/Theta/Entry.h index 7c6e2eeabfb..bcb79ac69fe 100644 --- a/src/Theta/Entry.h +++ b/src/Theta/Entry.h @@ -6,19 +6,21 @@ #pragma once -#include "Ethereum/Entry.h" -#include "../CoinEntry.h" -#include "Ethereum/Entry.h" +#include "CoinEntry.h" namespace TW::Theta { /// Entry point for Theta. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry final : public Ethereum::Entry { +class Entry final : public CoinEntry { public: - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - Data preImageHashes(TWCoinType coin, const Data& txInputData) const; - void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const override; + std::string normalizeAddress(TWCoinType coin, const std::string& address) const override; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const override; + Data addressToData(TWCoinType coin, const std::string& address) const override; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const override; + Data preImageHashes(TWCoinType coin, const Data& txInputData) const override; + void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const override; }; } // namespace TW::Theta diff --git a/src/Theta/Signer.cpp b/src/Theta/Signer.cpp index 01d46f037f6..050deff969b 100755 --- a/src/Theta/Signer.cpp +++ b/src/Theta/Signer.cpp @@ -31,7 +31,7 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto output = Proto::SigningOutput(); transaction.setSignature(from, signature); - auto encoded = transaction.encode(); + auto encoded = transaction.encodePayload(); output.set_encoded(encoded.data(), encoded.size()); output.set_signature(signature.data(), signature.size()); return output; @@ -39,25 +39,25 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { Data Signer::encode(const Transaction& transaction) const { const uint64_t nonce = 0; - const uint256_t gasPrice = 0; + const uint64_t gasPrice = 0; const uint64_t gasLimit = 0; - const Ethereum::Address to = Ethereum::Address("0x0000000000000000000000000000000000000000"); - const uint256_t amount = 0; + const auto* to = "0x0000000000000000000000000000000000000000"; + const uint64_t amount = 0; + auto txData = transaction.encode(chainID); + + EthereumRlp::Proto::EncodingInput encodingInput; + auto* rlpList = encodingInput.mutable_item()->mutable_list(); - auto encoded = Data(); /// Need to add the following prefix to the tx signbytes to be compatible with /// the Ethereum tx format - append(encoded, RLP::encode(nonce)); - append(encoded, RLP::encode(gasPrice)); - append(encoded, RLP::encode(gasLimit)); - append(encoded, RLP::encode(to.bytes)); - append(encoded, RLP::encode(amount)); - /// Chain ID - auto payload = Data(); - append(payload, RLP::encode(chainID)); - append(payload, transaction.encode()); - append(encoded, RLP::encode(payload)); - return RLP::encodeList(encoded); + rlpList->add_items()->set_number_u64(nonce); + rlpList->add_items()->set_number_u64(gasPrice); + rlpList->add_items()->set_number_u64(gasLimit); + rlpList->add_items()->set_address(to); + rlpList->add_items()->set_number_u64(amount); + rlpList->add_items()->set_data(txData.data(), txData.size()); + + return RLP::encode(encodingInput); } Data Signer::sign(const PrivateKey& privateKey, const Transaction& transaction) noexcept { @@ -102,7 +102,7 @@ Proto::SigningOutput Signer::compile(const Data& signature, const PublicKey& pub auto protoOutput = Proto::SigningOutput(); auto transaction = buildTransaction(); transaction.setSignature(from, signature); - auto encoded = transaction.encode(); + auto encoded = transaction.encodePayload(); protoOutput.set_encoded(encoded.data(), encoded.size()); protoOutput.set_signature(signature.data(), signature.size()); diff --git a/src/Theta/Transaction.cpp b/src/Theta/Transaction.cpp index b9a70c0611e..939c54aa7b3 100644 --- a/src/Theta/Transaction.cpp +++ b/src/Theta/Transaction.cpp @@ -12,43 +12,61 @@ namespace TW::Theta { using RLP = Ethereum::RLP; -Data encode(const Coins& coins) noexcept { - auto encoded = Data(); - append(encoded, RLP::encode(coins.thetaWei)); - append(encoded, RLP::encode(coins.tfuelWei)); - return RLP::encodeList(encoded); +EthereumRlp::Proto::RlpItem prepare(const Coins& coins) noexcept { + auto thetaWei = store(coins.thetaWei); + auto tfuelWei = store(coins.tfuelWei); + + EthereumRlp::Proto::RlpItem item; + auto* rlpList = item.mutable_list(); + + rlpList->add_items()->set_number_u256(thetaWei.data(), thetaWei.size()); + rlpList->add_items()->set_number_u256(tfuelWei.data(), tfuelWei.size()); + + return item; } -Data encode(const TxInput& input) noexcept { - auto encoded = Data(); - append(encoded, RLP::encode(input.address.bytes)); - append(encoded, encode(input.coins)); - append(encoded, RLP::encode(input.sequence)); - append(encoded, RLP::encode(input.signature)); - return RLP::encodeList(encoded); +EthereumRlp::Proto::RlpItem prepare(const TxInput& input) noexcept { + EthereumRlp::Proto::RlpItem item; + auto* rlpList = item.mutable_list(); + + rlpList->add_items()->set_data(input.address.bytes.data(), input.address.bytes.size()); + *rlpList->add_items() = prepare(input.coins); + rlpList->add_items()->set_number_u64(input.sequence); + rlpList->add_items()->set_data(input.signature.data(), input.signature.size()); + + return item; } -Data encode(const std::vector& inputs) noexcept { - auto encoded = Data(); +EthereumRlp::Proto::RlpItem prepare(const std::vector& inputs) noexcept { + EthereumRlp::Proto::RlpItem item; + auto* rlpList = item.mutable_list(); + for (const auto& input : inputs) { - append(encoded, encode(input)); + *rlpList->add_items() = prepare(input); } - return RLP::encodeList(encoded); + + return item; } -Data encode(const TxOutput& output) noexcept { - auto encoded = Data(); - append(encoded, RLP::encode(output.address.bytes)); - append(encoded, encode(output.coins)); - return RLP::encodeList(encoded); +EthereumRlp::Proto::RlpItem prepare(const TxOutput& output) noexcept { + EthereumRlp::Proto::RlpItem item; + auto* rlpList = item.mutable_list(); + + rlpList->add_items()->set_data(output.address.bytes.data(), output.address.bytes.size()); + *rlpList->add_items() = prepare(output.coins); + + return item; } -Data encode(const std::vector& outputs) noexcept { - auto encoded = Data(); +EthereumRlp::Proto::RlpItem prepare(const std::vector& outputs) noexcept { + EthereumRlp::Proto::RlpItem item; + auto* rlpList = item.mutable_list(); + for (const auto& output : outputs) { - append(encoded, encode(output)); + *rlpList->add_items() = prepare(output); } - return RLP::encodeList(encoded); + + return item; } Transaction::Transaction(Ethereum::Address from, Ethereum::Address to, @@ -65,15 +83,29 @@ Transaction::Transaction(Ethereum::Address from, Ethereum::Address to, this->outputs.push_back(output); } -Data Transaction::encode() const noexcept { - auto encoded = Data(); - uint16_t txType = 2; // TxSend - append(encoded, RLP::encode(txType)); - auto encodedData = Data(); - append(encodedData, Theta::encode(_fee)); - append(encodedData, Theta::encode(inputs)); - append(encodedData, Theta::encode(outputs)); - append(encoded, RLP::encodeList(encodedData)); +Data Transaction::encodePayload() const noexcept { + const uint64_t txType = 2; // TxSend + + EthereumRlp::Proto::EncodingInput txInput; + auto* txPropertiesList = txInput.mutable_item()->mutable_list(); + + *txPropertiesList->add_items() = prepare(_fee); + *txPropertiesList->add_items() = prepare(inputs); + *txPropertiesList->add_items() = prepare(outputs); + + auto txPropertiesEncoded = RLP::encode(txInput); + + Data payload; + append(payload, RLP::encodeU256(static_cast(txType))); + append(payload, txPropertiesEncoded); + + return payload; +} + +Data Transaction::encode(const std::string& chainId) const noexcept { + Data encoded; + append(encoded, RLP::encodeString(chainId)); + append(encoded, encodePayload()); return encoded; } diff --git a/src/Theta/Transaction.h b/src/Theta/Transaction.h index 770bcc1cf75..5e1a1973d12 100644 --- a/src/Theta/Transaction.h +++ b/src/Theta/Transaction.h @@ -11,7 +11,7 @@ #include "Coins.h" #include "Data.h" -#include "../Ethereum/Address.h" +#include "Ethereum/Address.h" namespace TW::Theta { @@ -51,8 +51,11 @@ class Transaction { const uint256_t& thetaAmount, const uint256_t& tfuelAmount, uint64_t sequence, const uint256_t& feeAmount = 1000000000000); - /// Encodes the transaction - Data encode() const noexcept; + /// Encodes the essential part of the transaction without a Chain ID. + Data encodePayload() const noexcept; + + /// Encodes the transaction with the given `chainId`. + Data encode(const std::string& chainId) const noexcept; /// Sets signature bool setSignature(const Ethereum::Address& address, const Data& signature) noexcept; diff --git a/src/VeChain/Entry.cpp b/src/VeChain/Entry.cpp index 1d15dcfa22b..c9724dfae75 100644 --- a/src/VeChain/Entry.cpp +++ b/src/VeChain/Entry.cpp @@ -6,13 +6,29 @@ #include "Entry.h" #include -#include "../proto/Common.pb.h" -#include "../Hash.h" #include "Ethereum/Address.h" #include "Signer.h" namespace TW::VeChain { + +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { + return Ethereum::Address::isValid(address); +} + +std::string Entry::normalizeAddress([[maybe_unused]] TWCoinType coin, const std::string& address) const { + // normalized with EIP55 checksum + return Ethereum::Address(address).string(); +} + +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { + return Ethereum::Address(publicKey).string(); +} + +Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { + const auto addr = Ethereum::Address(address); + return {addr.bytes.begin(), addr.bytes.end()}; +} void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); diff --git a/src/VeChain/Entry.h b/src/VeChain/Entry.h index f1977330557..8a06ae5434c 100644 --- a/src/VeChain/Entry.h +++ b/src/VeChain/Entry.h @@ -6,18 +6,21 @@ #pragma once -#include "Ethereum/Entry.h" +#include "CoinEntry.h" namespace TW::VeChain { /// Entry point for VeChain. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry final : public Ethereum::Entry { +class Entry final : public CoinEntry { public: - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - - Data preImageHashes(TWCoinType coin, const Data& txInputData) const; - void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const override; + std::string normalizeAddress(TWCoinType coin, const std::string& address) const override; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const override; + Data addressToData(TWCoinType coin, const std::string& address) const override; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const override; + Data preImageHashes(TWCoinType coin, const Data& txInputData) const override; + void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const override; }; } // namespace TW::VeChain diff --git a/src/VeChain/Transaction.cpp b/src/VeChain/Transaction.cpp index 8c28ea8becf..48030e74f77 100644 --- a/src/VeChain/Transaction.cpp +++ b/src/VeChain/Transaction.cpp @@ -12,38 +12,50 @@ namespace TW::VeChain { using RLP = Ethereum::RLP; -Data encode(const Clause& clause) noexcept { - auto encoded = Data(); - append(encoded, RLP::encode(clause.to.bytes)); - append(encoded, RLP::encode(clause.value)); - append(encoded, RLP::encode(clause.data)); - return RLP::encodeList(encoded); +EthereumRlp::Proto::RlpItem prepareClause(const Clause& clause) noexcept { + auto value = store(clause.value); + + EthereumRlp::Proto::RlpItem item; + auto* rlpList = item.mutable_list(); + + rlpList->add_items()->set_data(clause.to.bytes.data(), clause.to.bytes.size()); + rlpList->add_items()->set_number_u256(value.data(), value.size()); + rlpList->add_items()->set_data(clause.data.data(), clause.data.size()); + + return item; } -Data encodeClauses(std::vector clauses) noexcept { - auto encoded = Data(); +EthereumRlp::Proto::RlpItem prepareClauses(const std::vector& clauses) noexcept { + EthereumRlp::Proto::RlpItem item; + + auto* rlpList = item.mutable_list(); for (const auto& clause : clauses) { - auto encodedClause = encode(clause); - append(encoded, encodedClause); + *rlpList->add_items() = prepareClause(clause); } - return RLP::encodeList(encoded); + + return item; } Data Transaction::encode() const noexcept { - auto encoded = Data(); - append(encoded, RLP::encode(chainTag)); - append(encoded, RLP::encode(blockRef)); - append(encoded, RLP::encode(expiration)); - append(encoded, encodeClauses(clauses)); - append(encoded, RLP::encode(gasPriceCoef)); - append(encoded, RLP::encode(gas)); - append(encoded, RLP::encode(dependsOn)); - append(encoded, RLP::encode(nonce)); - append(encoded, RLP::encodeList(reserved)); + EthereumRlp::Proto::EncodingInput input; + auto* rlpList = input.mutable_item()->mutable_list(); + + rlpList->add_items()->set_number_u64(chainTag); + rlpList->add_items()->set_number_u64(blockRef); + rlpList->add_items()->set_number_u64(expiration); + *rlpList->add_items() = prepareClauses(clauses); + rlpList->add_items()->set_number_u64(gasPriceCoef); + rlpList->add_items()->set_number_u64(gas); + rlpList->add_items()->set_data(dependsOn.data(), dependsOn.size()); + rlpList->add_items()->set_number_u64(nonce); + // Put an empty list - reserved field for backward compatibility. + rlpList->add_items()->mutable_list(); + if (!signature.empty()) { - append(encoded, RLP::encode(signature)); + rlpList->add_items()->set_data(signature.data(), signature.size()); } - return RLP::encodeList(encoded); + + return RLP::encode(input); } } // namespace TW::VeChain diff --git a/src/interface/TWEthereumAbi.cpp b/src/interface/TWEthereumAbi.cpp index 24ed89d8138..e8eceb53344 100644 --- a/src/interface/TWEthereumAbi.cpp +++ b/src/interface/TWEthereumAbi.cpp @@ -8,44 +8,69 @@ #include #include "Data.h" -#include "Ethereum/ABI.h" +#include "Ethereum/ABI/Function.h" #include "Ethereum/ContractCall.h" +#include "Ethereum/MessageSigner.h" #include "HexCoding.h" -#include "uint256.h" #include -#include #include using namespace TW; namespace EthAbi = TW::Ethereum::ABI; +template +static TWData* _Nonnull ethereumAbiForwardToRust(F rustFunction, enum TWCoinType coin, TWData* _Nonnull input) { + const Data& inputData = *(reinterpret_cast(input)); + + const Rust::TWDataWrapper dataInPtr(inputData); + Rust::TWDataWrapper dataOutPtr = rustFunction(static_cast(coin), dataInPtr.get()); + + auto dataOut = dataOutPtr.toDataOrDefault(); + return TWDataCreateWithBytes(dataOut.data(), dataOut.size()); +} + +TWData* _Nonnull TWEthereumAbiDecodeContractCall(enum TWCoinType coin, TWData* _Nonnull input) { + return ethereumAbiForwardToRust(Rust::tw_ethereum_abi_decode_contract_call, coin, input); +} + +TWData* _Nonnull TWEthereumAbiDecodeParams(enum TWCoinType coin, TWData* _Nonnull input) { + return ethereumAbiForwardToRust(Rust::tw_ethereum_abi_decode_params, coin, input); +} + +TWData* _Nonnull TWEthereumAbiDecodeValue(enum TWCoinType coin, TWData* _Nonnull input) { + return ethereumAbiForwardToRust(Rust::tw_ethereum_abi_decode_value, coin, input); +} + +TWData* _Nonnull TWEthereumAbiEncodeFunction(enum TWCoinType coin, TWData* _Nonnull input) { + return ethereumAbiForwardToRust(Rust::tw_ethereum_abi_encode_function, coin, input); +} + TWData* _Nonnull TWEthereumAbiEncode(struct TWEthereumAbiFunction* _Nonnull func_in) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - Data encoded; - function.encode(encoded); - return TWDataCreateWithData(&encoded); + Data encodedData; + auto encoded = func_in->impl.encodeInput(); + if (encoded.has_value()) { + encodedData = encoded.value(); + } + return TWDataCreateWithData(&encodedData); } bool TWEthereumAbiDecodeOutput(struct TWEthereumAbiFunction* _Nonnull func_in, TWData* _Nonnull encoded) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; assert(encoded != nullptr); - Data encData = data(TWDataBytes(encoded), TWDataSize(encoded)); + const Data& encData = *(reinterpret_cast(encoded)); - size_t offset = 0; - return function.decodeOutput(encData, offset); + bool isOutput = true; + return func_in->impl.decode(encData, isOutput); } TWString* _Nullable TWEthereumAbiDecodeCall(TWData* _Nonnull callData, TWString* _Nonnull abiString) { const Data& call = *(reinterpret_cast(callData)); const auto& jsonString = *reinterpret_cast(abiString); try { - auto abi = nlohmann::json::parse(jsonString); - auto string = EthAbi::decodeCall(call, abi); + auto string = EthAbi::decodeCall(call, jsonString); if (!string.has_value()) { return nullptr; } @@ -59,7 +84,7 @@ TWString* _Nullable TWEthereumAbiDecodeCall(TWData* _Nonnull callData, TWString* TWData* _Nonnull TWEthereumAbiEncodeTyped(TWString* _Nonnull messageJson) { Data data; try { - data = EthAbi::ParamStruct::hashStructJson(TWStringUTF8Bytes(messageJson)); + data = Ethereum::MessageSigner::typedDataPreImageHash(TWStringUTF8Bytes(messageJson)); } catch (...) {} // return empty return TWDataCreateWithBytes(data.data(), data.size()); } diff --git a/src/interface/TWEthereumAbiFunction.cpp b/src/interface/TWEthereumAbiFunction.cpp index 01fe3ef7816..9a544c533e8 100644 --- a/src/interface/TWEthereumAbiFunction.cpp +++ b/src/interface/TWEthereumAbiFunction.cpp @@ -6,12 +6,10 @@ #include -#include "Ethereum/ABI.h" +#include "Ethereum/ABI/Function.h" #include "Data.h" #include "HexCoding.h" -#include "../uint256.h" -#include #include #include @@ -30,8 +28,7 @@ void TWEthereumAbiFunctionDelete(struct TWEthereumAbiFunction *_Nonnull func_in) TWString *_Nonnull TWEthereumAbiFunctionGetType(struct TWEthereumAbiFunction *_Nonnull func_in) { assert(func_in != nullptr); - auto function = func_in->impl; - std::string sign = function.getType(); + std::string sign = func_in->impl.getType(); return TWStringCreateWithUTF8Bytes(sign.c_str()); } @@ -39,418 +36,369 @@ TWString *_Nonnull TWEthereumAbiFunctionGetType(struct TWEthereumAbiFunction *_N int TWEthereumAbiFunctionAddParamUInt8(struct TWEthereumAbiFunction *_Nonnull func_in, uint8_t val, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - auto param = std::make_shared(val); - auto idx = function.addParam(param, isOutput); - return idx; + uint32_t bits = 8; + auto encodedValue = store(val); + return func_in->impl.addUintParam(bits, encodedValue, isOutput); } int TWEthereumAbiFunctionAddParamUInt16(struct TWEthereumAbiFunction *_Nonnull func_in, uint16_t val, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - auto param = std::make_shared(val); - auto idx = function.addParam(param, isOutput); - return idx; + uint32_t bits = 16; + auto encodedValue = store(val); + return func_in->impl.addUintParam(bits, encodedValue, isOutput); } int TWEthereumAbiFunctionAddParamUInt32(struct TWEthereumAbiFunction *_Nonnull func_in, uint32_t val, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - auto param = std::make_shared(val); - auto idx = function.addParam(param, isOutput); - return idx; + uint32_t bits = 32; + auto encodedValue = store(val); + return func_in->impl.addUintParam(bits, encodedValue, isOutput); } int TWEthereumAbiFunctionAddParamUInt64(struct TWEthereumAbiFunction *_Nonnull func_in, uint64_t val, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - auto param = std::make_shared(val); - auto idx = function.addParam(param, isOutput); - return idx; + uint32_t bits = 64; + auto encodedValue = store(val); + return func_in->impl.addUintParam(bits, encodedValue, isOutput); } int TWEthereumAbiFunctionAddParamUInt256(struct TWEthereumAbiFunction *_Nonnull func_in, TWData *_Nonnull val, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - uint256_t val2 = load(*static_cast(val)); - auto param = std::make_shared(val2); - auto idx = function.addParam(param, isOutput); - return idx; + uint32_t bits = 256; + const Data& encodedValue = *(reinterpret_cast(val)); + return func_in->impl.addUintParam(bits, encodedValue, isOutput); } int TWEthereumAbiFunctionAddParamUIntN(struct TWEthereumAbiFunction *_Nonnull func_in, int bits, TWData *_Nonnull val, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - uint256_t val2 = load(*static_cast(val)); - auto param = std::make_shared(bits, val2); - auto idx = function.addParam(param, isOutput); - return idx; + const Data& encodedValue = *(reinterpret_cast(val)); + return func_in->impl.addUintParam(static_cast(bits), encodedValue, isOutput); } int TWEthereumAbiFunctionAddParamInt8(struct TWEthereumAbiFunction *_Nonnull func_in, int8_t val, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - auto param = std::make_shared(val); - auto idx = function.addParam(param, isOutput); - return idx; + uint32_t bits = 8; + auto encodedValue = store(val); + return func_in->impl.addIntParam(bits, encodedValue, isOutput); } int TWEthereumAbiFunctionAddParamInt16(struct TWEthereumAbiFunction *_Nonnull func_in, int16_t val, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - auto param = std::make_shared(val); - auto idx = function.addParam(param, isOutput); - return idx; + uint32_t bits = 16; + auto encodedValue = store(val); + return func_in->impl.addIntParam(bits, encodedValue, isOutput); } int TWEthereumAbiFunctionAddParamInt32(struct TWEthereumAbiFunction *_Nonnull func_in, int32_t val, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - auto param = std::make_shared(val); - auto idx = function.addParam(param, isOutput); - return idx; + uint32_t bits = 32; + auto encodedValue = store(val); + return func_in->impl.addIntParam(bits, encodedValue, isOutput); } int TWEthereumAbiFunctionAddParamInt64(struct TWEthereumAbiFunction *_Nonnull func_in, int64_t val, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - auto param = std::make_shared(val); - auto idx = function.addParam(param, isOutput); - return idx; + uint32_t bits = 64; + auto encodedValue = store(val); + return func_in->impl.addIntParam(bits, encodedValue, isOutput); } int TWEthereumAbiFunctionAddParamInt256(struct TWEthereumAbiFunction *_Nonnull func_in, TWData *_Nonnull val, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - assert(val != nullptr); - int256_t val2 = EthAbi::ValueEncoder::int256FromUint256(load(*static_cast(val))); - auto param = std::make_shared(val2); - auto idx = function.addParam(param, isOutput); - return idx; + uint32_t bits = 256; + const Data& encodedValue = *(reinterpret_cast(val)); + return func_in->impl.addIntParam(bits, encodedValue, isOutput); } int TWEthereumAbiFunctionAddParamIntN(struct TWEthereumAbiFunction *_Nonnull func_in, int bits, TWData *_Nonnull val, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - assert(val != nullptr); - int256_t val2 = EthAbi::ValueEncoder::int256FromUint256(load(*static_cast(val))); - auto param = std::make_shared(bits, val2); - auto idx = function.addParam(param, isOutput); - return idx; + const Data& encodedValue = *(reinterpret_cast(val)); + return func_in->impl.addIntParam(static_cast(bits), encodedValue, isOutput); } int TWEthereumAbiFunctionAddParamBool(struct TWEthereumAbiFunction *_Nonnull func_in, bool val, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - auto param = std::make_shared(val); - auto idx = function.addParam(param, isOutput); - return idx; + EthereumAbi::Proto::Param paramType; + // Declare the `boolean` type. + paramType.mutable_param()->mutable_boolean(); + + EthereumAbi::Proto::Token token; + token.set_boolean(val); + + return func_in->impl.addParam(std::move(paramType), std::move(token), isOutput); } int TWEthereumAbiFunctionAddParamString(struct TWEthereumAbiFunction *_Nonnull func_in, TWString *_Nonnull val, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - assert(val != nullptr); - auto param = std::make_shared(TWStringUTF8Bytes(val)); - auto idx = function.addParam(param, isOutput); - return idx; + EthereumAbi::Proto::Param paramType; + // Declare the `string` type. + paramType.mutable_param()->mutable_string_param(); + + EthereumAbi::Proto::Token token; + auto* s = reinterpret_cast(val); + token.set_string_value(*s); + + return func_in->impl.addParam(std::move(paramType), std::move(token), isOutput); } int TWEthereumAbiFunctionAddParamAddress(struct TWEthereumAbiFunction *_Nonnull func_in, TWData *_Nonnull val, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - assert(val != nullptr); - Data data = TW::data(TWDataBytes(val), TWDataSize(val)); - auto param = std::make_shared(data); - auto idx = function.addParam(param, isOutput); - return idx; + EthereumAbi::Proto::Param paramType; + // Declare the `address` type. + paramType.mutable_param()->mutable_address(); + + EthereumAbi::Proto::Token token; + const Data& addressData = *(reinterpret_cast(val)); + bool prefixed = true; + auto addressStr = hex(addressData, prefixed); + token.set_address(addressStr); + + return func_in->impl.addParam(std::move(paramType), std::move(token), isOutput); } int TWEthereumAbiFunctionAddParamBytes(struct TWEthereumAbiFunction *_Nonnull func_in, TWData *_Nonnull val, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - Data data = TW::data(TWDataBytes(val), TWDataSize(val)); - auto param = std::make_shared(data); - auto idx = function.addParam(param, isOutput); - return idx; + EthereumAbi::Proto::Param paramType; + // Declare the `byte_array` type. + paramType.mutable_param()->mutable_byte_array(); + + EthereumAbi::Proto::Token token; + const Data& bytesData = *(reinterpret_cast(val)); + token.set_byte_array(bytesData.data(), bytesData.size()); + + return func_in->impl.addParam(std::move(paramType), std::move(token), isOutput); } int TWEthereumAbiFunctionAddParamBytesFix(struct TWEthereumAbiFunction *_Nonnull func_in, size_t count, TWData *_Nonnull val, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - Data data = TW::data(TWDataBytes(val), TWDataSize(val)); - auto param = std::make_shared(count, data); - auto idx = function.addParam(param, isOutput); - return idx; + EthereumAbi::Proto::Param paramType; + // Declare the `byte_array_fix` type. + paramType.mutable_param()->mutable_byte_array_fix()->set_size(static_cast(count)); + + EthereumAbi::Proto::Token token; + Data bytesData = *(reinterpret_cast(val)); + bytesData.resize(count); + token.set_byte_array_fix(bytesData.data(), bytesData.size()); + + return func_in->impl.addParam(std::move(paramType), std::move(token), isOutput); } int TWEthereumAbiFunctionAddParamArray(struct TWEthereumAbiFunction *_Nonnull func_in, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - auto param = std::make_shared(); - auto idx = function.addParam(param, isOutput); - return idx; + EthereumAbi::Proto::Param paramType; + // Declare the `array` type. + paramType.mutable_param()->mutable_array(); + + EthereumAbi::Proto::Token token; + // Declare the `array` empty value. + token.mutable_array(); + + return func_in->impl.addParam(std::move(paramType), std::move(token), isOutput); } ///// GetParam uint8_t TWEthereumAbiFunctionGetParamUInt8(struct TWEthereumAbiFunction *_Nonnull func_in, int idx, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - std::shared_ptr param; - if (!function.getParam(idx, param, isOutput)) { - return 0; - } - auto param2 = std::dynamic_pointer_cast(param); - if (param2 == nullptr) { - return 0; - } - return param2->getVal(); + return func_in->impl.getUintParam(idx, 8, isOutput); } uint64_t TWEthereumAbiFunctionGetParamUInt64(struct TWEthereumAbiFunction *_Nonnull func_in, int idx, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - std::shared_ptr param; - if (!function.getParam(idx, param, isOutput)) { - return 0; - } - auto param2 = std::dynamic_pointer_cast(param); - if (param2 == nullptr) { - return 0; - } - return param2->getVal(); + return func_in->impl.getUintParam(idx, 64, isOutput); } TWData *_Nonnull TWEthereumAbiFunctionGetParamUInt256(struct TWEthereumAbiFunction *_Nonnull func_in, int idx, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - uint256_t val256 = 0; - std::shared_ptr param; - if (!function.getParam(idx, param, isOutput)) { - TW::Data valData = TW::store(val256); - return TWDataCreateWithData(&valData); - } - auto param2 = std::dynamic_pointer_cast(param); - if (param2 == nullptr) { - TW::Data valData = TW::store(val256); - return TWDataCreateWithData(&valData); - } - val256 = param2->getVal(); - TW::Data valData = TW::store(val256); + auto valData = func_in->impl.getUintParamData(idx, 256, isOutput); return TWDataCreateWithData(&valData); } bool TWEthereumAbiFunctionGetParamBool(struct TWEthereumAbiFunction *_Nonnull func_in, int idx, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - std::shared_ptr param; - if (!function.getParam(idx, param, isOutput)) { + auto param = func_in->impl.getParam(idx, isOutput); + if (!param.has_value() || !param->has_boolean()) { return false; } - auto param2 = std::dynamic_pointer_cast(param); - if (param2 == nullptr) { - return false; - } - return param2->getVal(); + return param->boolean(); } TWString *_Nonnull TWEthereumAbiFunctionGetParamString(struct TWEthereumAbiFunction *_Nonnull func_in, int idx, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - std::string valStr; - std::shared_ptr param; - if (!function.getParam(idx, param, isOutput)) { - return TWStringCreateWithUTF8Bytes(valStr.c_str()); - } - auto param2 = std::dynamic_pointer_cast(param); - if (param2 == nullptr) { + + auto param = func_in->impl.getParam(idx, isOutput); + if (!param.has_value() || !param->has_string_value()) { return TWStringCreateWithUTF8Bytes(valStr.c_str()); } - valStr = param2->getVal(); + valStr = param->string_value(); return TWStringCreateWithUTF8Bytes(valStr.c_str()); } TWData *_Nonnull TWEthereumAbiFunctionGetParamAddress(struct TWEthereumAbiFunction *_Nonnull func_in, int idx, bool isOutput) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; + Data addressData; - Data valData; - std::shared_ptr param; - if (!function.getParam(idx, param, isOutput)) { - return TWDataCreateWithData(&valData); + auto param = func_in->impl.getParam(idx, isOutput); + if (!param.has_value() || !param->has_address()) { + return TWDataCreateWithData(&addressData); } - auto param2 = std::dynamic_pointer_cast(param); - if (param2 == nullptr) { - return TWDataCreateWithData(&valData); + auto addressStr = param->address(); + try { + addressData = parse_hex(addressStr); + return TWDataCreateWithData(&addressData); + } catch (...) { + return TWDataCreateWithData(&addressData); } - valData = param2->getData(); - return TWDataCreateWithData(&valData); } ///// AddInArrayParam -int addInArrayParam(EthAbi::Function& function, int arrayIdx, const std::shared_ptr& childParam) { - std::shared_ptr param; - if (!function.getInParam(arrayIdx, param)) { - return -1; - } - std::shared_ptr paramArr = std::dynamic_pointer_cast(param); - if (paramArr == nullptr) { - return -1; // not an array - } - return paramArr->addParam(childParam); -} - int TWEthereumAbiFunctionAddInArrayParamUInt8(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, uint8_t val) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - return addInArrayParam(function, arrayIdx, std::make_shared(val)); + auto encodedVal = store(val); + return func_in->impl.addInArrayUintParam(arrayIdx, 8, encodedVal); } int TWEthereumAbiFunctionAddInArrayParamUInt16(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, uint16_t val) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - return addInArrayParam(function, arrayIdx, std::make_shared(val)); + auto encodedVal = store(val); + return func_in->impl.addInArrayUintParam(arrayIdx, 16, encodedVal); } int TWEthereumAbiFunctionAddInArrayParamUInt32(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, uint32_t val) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - return addInArrayParam(function, arrayIdx, std::make_shared(val)); + auto encodedVal = store(val); + return func_in->impl.addInArrayUintParam(arrayIdx, 32, encodedVal); } int TWEthereumAbiFunctionAddInArrayParamUInt64(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, uint64_t val) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - return addInArrayParam(function, arrayIdx, std::make_shared(val)); + auto encodedVal = store(val); + return func_in->impl.addInArrayUintParam(arrayIdx, 64, encodedVal); } int TWEthereumAbiFunctionAddInArrayParamUInt256(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, TWData *_Nonnull val) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - uint256_t val2 = load(*static_cast(val)); - return addInArrayParam(function, arrayIdx, std::make_shared(val2)); + const Data& bytesData = *(reinterpret_cast(val)); + return func_in->impl.addInArrayUintParam(arrayIdx, 256, bytesData); } int TWEthereumAbiFunctionAddInArrayParamUIntN(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, int bits, TWData *_Nonnull val) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - uint256_t val2 = load(*static_cast(val)); - return addInArrayParam(function, arrayIdx, std::make_shared(bits, val2)); + const Data& bytesData = *(reinterpret_cast(val)); + return func_in->impl.addInArrayUintParam(arrayIdx, bits, bytesData); } int TWEthereumAbiFunctionAddInArrayParamInt8(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, int8_t val) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - return addInArrayParam(function, arrayIdx, std::make_shared(val)); + auto encodedVal = store(val); + return func_in->impl.addInArrayIntParam(arrayIdx, 8, encodedVal); } int TWEthereumAbiFunctionAddInArrayParamInt16(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, int16_t val) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - return addInArrayParam(function, arrayIdx, std::make_shared(val)); + auto encodedVal = store(val); + return func_in->impl.addInArrayIntParam(arrayIdx, 16, encodedVal); } int TWEthereumAbiFunctionAddInArrayParamInt32(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, int32_t val) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - return addInArrayParam(function, arrayIdx, std::make_shared(val)); + auto encodedVal = store(val); + return func_in->impl.addInArrayIntParam(arrayIdx, 32, encodedVal); } int TWEthereumAbiFunctionAddInArrayParamInt64(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, int64_t val) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - return addInArrayParam(function, arrayIdx, std::make_shared(val)); + auto encodedVal = store(val); + return func_in->impl.addInArrayIntParam(arrayIdx, 64, encodedVal); } int TWEthereumAbiFunctionAddInArrayParamInt256(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, TWData *_Nonnull val) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - assert(val != nullptr); - int256_t val2 = EthAbi::ValueEncoder::int256FromUint256(load(*static_cast(val))); - return addInArrayParam(function, arrayIdx, std::make_shared(val2)); + const Data& bytesData = *(reinterpret_cast(val)); + return func_in->impl.addInArrayIntParam(arrayIdx, 256, bytesData); } int TWEthereumAbiFunctionAddInArrayParamIntN(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, int bits, TWData *_Nonnull val) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - - assert(val != nullptr); - int256_t val2 = EthAbi::ValueEncoder::int256FromUint256(load(*static_cast(val))); - return addInArrayParam(function, arrayIdx, std::make_shared(bits, val2)); + const Data& bytesData = *(reinterpret_cast(val)); + return func_in->impl.addInArrayIntParam(arrayIdx, bits, bytesData); } int TWEthereumAbiFunctionAddInArrayParamBool(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, bool val) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - return addInArrayParam(function, arrayIdx, std::make_shared(val)); + EthereumAbi::Proto::ParamType paramType; + // Declare the boolean type. + paramType.mutable_boolean(); + + EthereumAbi::Proto::Token token; + token.set_boolean(val); + + return func_in->impl.addInArrayParam(arrayIdx, std::move(paramType), std::move(token)); } int TWEthereumAbiFunctionAddInArrayParamString(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, TWString *_Nonnull val) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - assert(val != nullptr); - return addInArrayParam(function, arrayIdx, std::make_shared(TWStringUTF8Bytes(val))); + EthereumAbi::Proto::ParamType paramType; + // Declare the boolean type. + paramType.mutable_string_param(); + + EthereumAbi::Proto::Token token; + token.set_string_value(TWStringUTF8Bytes(val)); + + return func_in->impl.addInArrayParam(arrayIdx, std::move(paramType), std::move(token)); } int TWEthereumAbiFunctionAddInArrayParamAddress(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, TWData *_Nonnull val) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - assert(val != nullptr); - Data data = TW::data(TWDataBytes(val), TWDataSize(val)); - return addInArrayParam(function, arrayIdx, std::make_shared(data)); + EthereumAbi::Proto::ParamType paramType; + // Declare the boolean type. + paramType.mutable_address(); + + EthereumAbi::Proto::Token token; + const Data& addressData = *(reinterpret_cast(val)); + bool prefixed = true; + auto addressStr = hex(addressData, prefixed); + token.set_address(addressStr); + + return func_in->impl.addInArrayParam(arrayIdx, std::move(paramType), std::move(token)); } int TWEthereumAbiFunctionAddInArrayParamBytes(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, TWData *_Nonnull val) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - Data data = TW::data(TWDataBytes(val), TWDataSize(val)); - return addInArrayParam(function, arrayIdx, std::make_shared(data)); + EthereumAbi::Proto::ParamType paramType; + // Declare the boolean type. + paramType.mutable_byte_array(); + + EthereumAbi::Proto::Token token; + const Data& bytesData = *(reinterpret_cast(val)); + token.set_byte_array(bytesData.data(), bytesData.size()); + + return func_in->impl.addInArrayParam(arrayIdx, std::move(paramType), std::move(token)); } int TWEthereumAbiFunctionAddInArrayParamBytesFix(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, size_t count, TWData *_Nonnull val) { assert(func_in != nullptr); - EthAbi::Function& function = func_in->impl; - Data data = TW::data(TWDataBytes(val), TWDataSize(val)); - return addInArrayParam(function, arrayIdx, std::make_shared(count, data)); + EthereumAbi::Proto::ParamType paramType; + // Declare the boolean type. + paramType.mutable_byte_array_fix()->set_size(static_cast(count)); + + EthereumAbi::Proto::Token token; + Data bytesData = *(reinterpret_cast(val)); + bytesData.resize(count); + token.set_byte_array_fix(bytesData.data(), bytesData.size()); + + return func_in->impl.addInArrayParam(arrayIdx, std::move(paramType), std::move(token)); } diff --git a/src/interface/TWEthereumRlp.cpp b/src/interface/TWEthereumRlp.cpp new file mode 100644 index 00000000000..4c405d54882 --- /dev/null +++ b/src/interface/TWEthereumRlp.cpp @@ -0,0 +1,22 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include + +#include "rust/Wrapper.h" +#include "Data.h" + +using namespace TW; + +TWData* _Nonnull TWEthereumRlpEncode(enum TWCoinType coin, TWData* _Nonnull input) { + const Data& dataIn = *(reinterpret_cast(input)); + + const Rust::TWDataWrapper dataInPtr(dataIn); + Rust::TWDataWrapper dataOutPtr = Rust::tw_ethereum_rlp_encode(static_cast(coin), dataInPtr.get()); + + auto dataOut = dataOutPtr.toDataOrDefault(); + return TWDataCreateWithBytes(dataOut.data(), dataOut.size()); +} diff --git a/src/interface/TWWebAuthn.cpp b/src/interface/TWWebAuthn.cpp index ce7cd514f36..ad1829e0c14 100644 --- a/src/interface/TWWebAuthn.cpp +++ b/src/interface/TWWebAuthn.cpp @@ -30,4 +30,4 @@ TWData *_Nonnull TWWebAuthnReconstructOriginalMessage(TWData* _Nonnull authentic const auto& clientDataJSONConverted = *reinterpret_cast(clientDataJSON); const auto& message = TW::WebAuthn::reconstructSignedMessage(authenticatorDataConverted, clientDataJSONConverted); return TWDataCreateWithData(&message); -} \ No newline at end of file +} diff --git a/src/proto/BitcoinV2.proto b/src/proto/BitcoinV2.proto new file mode 100644 index 00000000000..c55810557bf --- /dev/null +++ b/src/proto/BitcoinV2.proto @@ -0,0 +1,430 @@ +syntax = "proto3"; + +package TW.BitcoinV2.Proto; +option java_package = "wallet.core.jni.proto"; + +import "Utxo.proto"; + +enum Error { + OK = 0; + // `tx_utxo` related errors. + Error_utxo_invalid_leaf_hash = 2; + Error_utxo_invalid_sighash_type = 3; + Error_utxo_invalid_lock_time = 4; + Error_utxo_invalid_txid = 5; + Error_utxo_sighash_failed = 6; + Error_utxo_missing_sighash_method = 7; + Error_utxo_failed_encoding = 8; + Error_utxo_insufficient_inputs = 9; + Error_utxo_missing_change_script_pubkey = 10; + // `tw_bitcoin` related errors. + Error_zero_sequence_not_enabled = 11; + Error_unmatched_input_signature_count = 12; + Error_missing_input_builder = 13; + Error_missing_output_builder = 14; + Error_missing_recipient = 15; + Error_missing_inscription = 41; + Error_missing_tagged_output = 42; + Error_legacy_p2tr_invalid_variant = 16; + Error_legacy_no_spending_script_provided = 17; + Error_legacy_expected_redeem_script = 18; + Error_legacy_outpoint_not_set = 19; + Error_legacy_no_private_key = 36; + Error_legacy_no_plan_provided = 37; + Error_invalid_private_key = 20; + Error_invalid_public_key = 21; + Error_invalid_sighash = 22; + Error_invalid_witness_pubkey_hash = 23; + Error_invalid_brc20_ticker = 24; + Error_invalid_ecdsa_signature = 25; + Error_invalid_schnorr_signature = 26; + Error_invalid_control_block = 27; + Error_invalid_pubkey_hash = 28; + Error_invalid_taproot_root = 29; + Error_invalid_redeem_script = 30; + Error_invalid_wpkh_script_code = 1; + Error_invalid_witness_redeem_script_hash = 31; + Error_invalid_witness_encoding = 39; + Error_invalid_taproot_tweaked_pubkey = 32; + Error_invalid_change_output = 33; + Error_unsupported_address_recipient = 34; + Error_bad_address_recipient = 35; + Error_ordinal_mime_type_too_large = 38; + Error_ordinal_payload_too_large = 40; +} + +message SigningInput { + // (optional) The protocol version, is currently expected to be 1 or 2. + // Version 2 by default. + int32 version = 1; + + // Only required if the `sign` method is called. + bytes private_key = 2; + + // (optional) Block height or timestamp indicating at what point transactions can be + // included in a block. None by default (zero value). + Utxo.Proto.LockTime lock_time = 3; + + // The inputs to spend. + repeated Input inputs = 5; + + // The output of the transaction. Note that the change output is specified + // in the `change_output` field. + repeated Output outputs = 6; + + // How the inputs should be selected. + Utxo.Proto.InputSelector input_selector = 7; + + // (optional) The amount of satoshis per vbyte ("satVb"), used for fee calculation. + uint64 fee_per_vb = 8; + + // The change output to be added (return to sender). + // The `value` can be left at 0. + Output change_output = 9; + + // Explicility disable change output creation. + bool disable_change_output = 10; + + bool dangerous_use_fixed_schnorr_rng = 11; +} + +message Input { + // Use an individual private key for this input. Only required if the `sign` + // method is called. + bytes private_key = 1; + + // The referenced transaction ID in REVERSED order. + bytes txid = 2; + + // The position in the previous transactions output that this input + // references. + uint32 vout = 3; + + // The sequence number, used for timelocks, replace-by-fee, etc. Normally + // this number is simply 4294967295 (0xFFFFFFFF) . + uint32 sequence = 4; + + // If the sequence is a zero value, this field must be set to `true`. + bool sequence_enable_zero = 5; + + // The amount of satoshis of this input. Required for producing + // Segwit/Taproot transactions. + uint64 value = 6; + + // The sighash type, normally `SighashType::UseDefault` (All). + Utxo.Proto.SighashType sighash_type = 7; + + // The reciepient of this input (the spender) + oneof to_recipient { + // Construct input with a buildler pattern. + InputBuilder builder = 8; + // Construct input by providing raw spending information directly. + InputScriptWitness custom_script = 9; + } + + message InputBuilder { + oneof variant { + // Pay-to-Script-Hash, specify the redeem script. + bytes p2sh = 1; + // Pay-to-Public-Key-Hash, specify the public key. + bytes p2pkh = 2; + // Pay-to-Witness-Script-Hash, specify the redeem script. + bytes p2wsh = 3; + // Pay-to-Public-Key-Hash, specify the public key. + bytes p2wpkh = 6; + // Pay-to-Taproot-key-path (balance transfers). + InputTaprootKeyPath p2tr_key_path = 7; + // Pay-to-Taproot-script-path (complex transfers). + InputTaprootScriptPath p2tr_script_path = 8; + // Create a BRC20 inscription. + InputBrc20Inscription brc20_inscribe = 9; + // Create an Ordinal (NFT) inscriptiohn. + InputOrdinalInscription ordinal_inscribe = 10; + } + } + + message InputScriptWitness { + // The spending condition of this input. + bytes script_pubkey = 1; + // The claiming script for this input (non-Segwit/non-Taproot) + bytes script_sig = 2; + // The claiming script for this input (Segwit/Taproot) + repeated bytes witness_items = 3; + // The signing method. + Utxo.Proto.SigningMethod signing_method = 5; + } + + message InputTaprootKeyPath { + // Whether only one prevout should be used to calculate the Sighash. + // Normally this is `false`. + bool one_prevout = 1; + // The recipient. + bytes public_key = 2; + } + + message InputTaprootScriptPath { + // Whether only one prevout should be used to calculate the Sighash. + // Normally this is `false`. + bool one_prevout = 1; + // The payload of the Taproot transaction. + bytes payload = 2; + // The control block of the Taproot transaction required for claiming. + bytes control_block = 3; + } + + message InputOrdinalInscription { + // Whether only one prevout should be used to calculate the Sighash. + // Normally this is `false`. + bool one_prevout = 1; + // The recipient of the inscription, usually the sender. + bytes inscribe_to = 2; + // The MIME type of the inscription, such as `image/png`, etc. + string mime_type = 3; + // The actual inscription content. + bytes payload = 4; + } + + message InputBrc20Inscription { + bool one_prevout = 1; + // The recipient of the inscription, usually the sender. + bytes inscribe_to = 2; + // The ticker of the BRC20 inscription. + string ticker = 3; + // The BRC20 token transfer amount. + uint64 transfer_amount = 4; + } +} + +message Output { + // The amount of satoshis to spend. + uint64 value = 1; + + oneof to_recipient { + // Construct output with builder pattern. + OutputBuilder builder = 2; + // Construct output by providing the scriptPubkey directly. + bytes custom_script_pubkey = 3; + // Derive the expected output from the provided address. + string from_address = 4; + } + + message OutputBuilder { + oneof variant { + // Pay-to-Script-Hash, specify the hash. + OutputRedeemScriptOrHash p2sh = 1; + // Pay-to-Public-Key-Hash + ToPublicKeyOrHash p2pkh = 2; + // Pay-to-Witness-Script-Hash, specify the hash. + OutputRedeemScriptOrHash p2wsh = 3; + // Pay-to-Public-Key-Hash + ToPublicKeyOrHash p2wpkh = 4; + // Pay-to-Taproot-key-path (balance transfers), specify the public key. + bytes p2tr_key_path = 5; + // Pay-to-Taproot-script-path (complex transfers) + OutputTaprootScriptPath p2tr_script_path = 6; + bytes p2tr_dangerous_assume_tweaked = 7; + OutputBrc20Inscription brc20_inscribe = 8; + OutputOrdinalInscription ordinal_inscribe = 9; + } + } + + message OutputRedeemScriptOrHash { + oneof variant { + bytes redeem_script = 1; + bytes hash = 2; + } + } + + message OutputTaprootScriptPath { + // The internal key, usually the public key of the recipient. + bytes internal_key = 1; + // The merkle root of the Taproot script(s), required to compute the sighash. + bytes merkle_root = 2; + } + + message OutputOrdinalInscription { + // The recipient of the inscription, usually the sender. + bytes inscribe_to = 1; + // The MIME type of the inscription, such as `image/png`, etc. + string mime_type = 2; + // The actual inscription content. + bytes payload = 3; + } + + message OutputBrc20Inscription { + // The recipient of the inscription, usually the sender. + bytes inscribe_to = 1; + // The ticker of the BRC20 inscription. + string ticker = 2; + // The BRC20 token transfer amount. + uint64 transfer_amount = 3; + } +} + +message ToPublicKeyOrHash { + oneof to_address { + bytes pubkey = 1; + bytes hash = 2; + } +} + +message PreSigningOutput { + // A possible error, `OK` if none. + Error error = 1; + + string error_message = 2; + + // The transaction ID in NON-reversed order. Note that this must be reversed + // when referencing in future transactions. + bytes txid = 3; + + /// The sighashes to be signed; ECDSA for legacy and Segwit, Schnorr for Taproot. + repeated Utxo.Proto.Sighash sighashes = 4; + + // The raw inputs. + repeated Utxo.Proto.TxIn utxo_inputs = 5; + + // The raw outputs. + repeated TxOut utxo_outputs = 6; + + // The estimated weight of the transaction. + uint64 weight_estimate = 7; + + // The estimated fees of the transaction in satoshis. + uint64 fee_estimate = 8; + + // The output of a transaction. + message TxOut { + // The value of the output (in satoshis). + uint64 value = 1; + // The spending condition of the output. + bytes script_pubkey = 2; + // The payload of the Taproot script. + bytes taproot_payload = 3; + // The optional control block for a Taproot output (P2TR script-path). + bytes control_block = 4; + } +} + +message SigningOutput { + // A possible error, `OK` if none. + Error error = 1; + + string error_message = 2; + + Transaction transaction = 3; + + // The encoded transaction that submitted to the network. + bytes encoded = 4; + + // The transaction ID in NON-reversed order. Note that this must be reversed + // when referencing in future transactions. + bytes txid = 5; + + // The total and final weight of the transaction. + uint64 weight = 6; + + // The total and final fee of the transaction in satoshis. + uint64 fee = 7; +} + +message Transaction { + // The protocol version, is currently expected to be 1 or 2 (BIP68) + int32 version = 1; + + // Block height or timestamp indicating at what point transactions can be + // included in a block. None by default (zero value). + Utxo.Proto.LockTime lock_time = 2; + + // The transaction inputs. + repeated TransactionInput inputs = 3; + + // The transaction outputs. + repeated TransactionOutput outputs = 4; +} + +message TransactionInput { + // The referenced transaction ID in REVERSED order. + bytes txid = 1; + + // The position in the previous transactions output that this input + // references. + uint32 vout = 3; + + // The sequence number, used for timelocks, replace-by-fee, etc. Normally + // this number is simply 4294967295 (0xFFFFFFFF) . + uint32 sequence = 4; + + // The script for claiming the input (non-Segwit/non-Taproot). + bytes script_sig = 5; + + // The script for claiming the input (Segit/Taproot). + repeated bytes witness_items = 6; +} + +message TransactionOutput { + // The condition for claiming the output. + bytes script_pubkey = 1; + + // The amount of satoshis to spend. + uint64 value = 2; + + // In case of P2TR script-path (complex scripts), this is the payload that + // must later be revealed and is required for claiming. + bytes taproot_payload = 3; + + // In case of P2TR script-path (complex scripts), this is the control block + // required for claiming. + bytes control_block = 4; +} + +message ComposePlan { + oneof compose { + ComposeBrc20Plan brc20 = 1; + } + + message ComposeBrc20Plan { + // (optional) Sets the private key in the composed transactions. Can + // also be added manually. + bytes private_key = 1; + + // The inputs for the commit transaction. + repeated Input inputs = 2; + + // How the inputs for the commit transaction should be selected. + Utxo.Proto.InputSelector input_selector = 3; + + // The tagged output of the inscription. Commonly a P2WPKH transaction + // with the value of 546 (dust limit). + Output tagged_output = 4; + + // The BRC20 payload to inscribe. + Input.InputBrc20Inscription inscription = 5; + + // The amount of satoshis per vbyte ("satVb"), used for fee calculation. + uint64 fee_per_vb = 6; + + // The change output to be added (return to sender). + // The `value` can be left at 0. + Output change_output = 7; + + // Explicility disable change output creation. + bool disable_change_output = 8; + } +} + +message TransactionPlan { + // A possible error, `OK` if none. + Error error = 1; + + string error_message = 2; + + oneof plan { + Brc20Plan brc20 = 3; + } + + message Brc20Plan { + SigningInput commit = 1; + SigningInput reveal = 2; + } +} diff --git a/src/proto/Common.proto b/src/proto/Common.proto index cedf89feee4..0a9944bff49 100644 --- a/src/proto/Common.proto +++ b/src/proto/Common.proto @@ -67,4 +67,6 @@ enum SigningError { Error_invalid_params = 22; // Invalid input token amount Error_invalid_requested_token_amount = 23; + // Operation not supported for the chain. + Error_not_supported = 24; } diff --git a/src/proto/Ethereum.proto b/src/proto/Ethereum.proto index 504e229fa53..52255fc6b0b 100644 --- a/src/proto/Ethereum.proto +++ b/src/proto/Ethereum.proto @@ -197,3 +197,59 @@ message SigningOutput { // Encoded transaction bytes. bytes pre_hash = 8; } + +enum MessageType { + // Sign a message following EIP-191. + MessageType_legacy = 0; + // Sign a message following EIP-191 with EIP-155 replay attack protection. + MessageType_eip155 = 1; + // Sign a typed message EIP-712 V4. + MessageType_typed = 2; + // Sign a typed message EIP-712 V4 with EIP-155 replay attack protection. + MessageType_typed_eip155 = 3; + // Sign a message with Immutable X msg type. + MessageType_immutable_x = 4; +} + +message MaybeChainId { + // Chain ID. + uint64 chain_id = 3; +} + +message MessageSigningInput { + // The secret private key used for signing (32 bytes). + bytes private_key = 1; + + // Message to sign. Either a regular message or a typed data structured message in JSON format. + // Message type should be declared at `message_type`. + string message = 2; + + // Optional. Used in replay protection and to check Typed Structured Data input. + // Eg. should be set if `message_type` is `MessageType_eip155`, or MessageType_typed, or `MessageType_typed_eip155`. + MaybeChainId chain_id = 3; + + // Message type. + MessageType message_type = 4; +} + +message MessageSigningOutput { + // The signature, Hex-encoded. + string signature = 1; + + // error code, 0 is ok, other codes will be treated as errors + Common.Proto.SigningError error = 2; + + // error code description + string error_message = 3; +} + +message MessageVerifyingInput { + // The message signed. + string message = 1; + + // Public key that will verify and recover the message from the signature. + bytes public_key = 2; + + // The signature, Hex-encoded. + string signature = 3; +} diff --git a/src/proto/EthereumAbi.proto b/src/proto/EthereumAbi.proto new file mode 100644 index 00000000000..8ee2c0692a1 --- /dev/null +++ b/src/proto/EthereumAbi.proto @@ -0,0 +1,318 @@ +syntax = "proto3"; + +package TW.EthereumAbi.Proto; +option java_package = "wallet.core.jni.proto"; + +enum AbiError { + // This is the OK case, with value=0 + OK = 0; + + // Internal error. + Error_internal = 1; + + // Unexpected function signature or ABI mismatch. + Error_abi_mismatch = 2; + // Invalid ABI. + Error_invalid_abi = 3; + // Invalid parameter type. + Error_invalid_param_type = 4; + // Invalid address value. + Error_invalid_address_value = 5; + // Invalid UInt value. + Error_invalid_uint_value = 6; + // Missing parameter type. + Error_missing_param_type = 7; + // Missing parameter value. + Error_missing_param_value = 8; + // Invalid encoded data. + Error_decoding_data = 9; + // Invalid empty type. + // For example, bytes0, address[0]. + Error_empty_type = 10; +} + +// ABI type parameters excluding values. + +// Indicates a boolean type. +message BoolType {} + +// Generic number type for all bit sizes, like UInt24, 40, 48, ... 248. +message NumberNType { + // The number of bits of an integer. + uint32 bits = 1; +} + +// Indicates a string type. +message StringType {} + +// Indicates an address type. +message AddressType {} + +// Indicates an array type with an inner `element_type`. +message ArrayType { + // The type of array elements. + ParamType element_type = 1; +} + +// Indicates a fixed-size array type with an inner `element_type`. +message FixedArrayType { + // The fixed-size of the array. + uint64 size = 1; + // The type of array elements. + ParamType element_type = 2; +} + +// Indicates a byte array type. +message ByteArrayType {} + +// Indicates a fixed-size byte array type. +message ByteArrayFixType { + // The fixed-size of the array. + uint64 size = 1; +} + +// Indicates a tuple with inner type parameters. +message TupleType { + // Tuple named parameters. + repeated Param params = 1; +} + +// Named parameter with type. +message Param { + // Name of the parameter. + string name = 1; + + // Type of the parameter. + ParamType param = 2; +} + +message ParamType { + oneof param { + BoolType boolean = 1; + NumberNType number_int = 2; + NumberNType number_uint = 3; + // Nested values. Gap in field numbering is intentional. + StringType string_param = 7; + AddressType address = 8; + ByteArrayType byte_array = 9; + ByteArrayFixType byte_array_fix = 10; + + // Nested values. Gap in field numbering is intentional. + ArrayType array = 14; + FixedArrayType fixed_array = 15; + + // Nested values. Gap in field numbering is intentional. + TupleType tuple = 19; + } +} + +// ABI parameters including values. + +// Generic number parameter for all other bit sizes, like UInt24, 40, 48, ... 248. +message NumberNParam { + // Count of bits of the number. + // 0 < bits <= 256, bits % 8 == 0 + uint32 bits = 1; + + // Serialized big endian. + bytes value = 2; +} + +// A byte array of arbitrary size. +message ArrayParam { + // The type of array elements. + ParamType element_type = 1; + + // Array elements. + repeated Token elements = 2; +} + +// A tuple with various parameters similar to a structure. +message TupleParam { + // Tokens (values) of the tuple parameters. + repeated Token params = 1; +} + +// A value of an ABI parameter. +message Token { + // Optional. Name of a corresponding parameter. + string name = 1; + + oneof token { + // Integer values. + bool boolean = 2; + NumberNParam number_int = 3; + NumberNParam number_uint = 4; + + // Simple values. Gap in field numbering is intentional. + string string_value = 7; + string address = 8; + bytes byte_array = 9; + bytes byte_array_fix = 10; + + // Nested values. Gap in field numbering is intentional. + ArrayParam array = 14; + ArrayParam fixed_array = 15; + + // Nested values. Gap in field numbering is intentional. + TupleParam tuple = 19; + } +} + +//// TWEthereumAbiDecodeContractCall + +// Decode a contract call (function input) according to the given ABI json. +message ContractCallDecodingInput { + // An encoded smart contract call with a prefixed function signature (4 bytes). + bytes encoded = 1; + + // A smart contract ABI in JSON. + // Each ABI function must be mapped to a short signature. + // Expected to be a set of functions mapped to corresponding short signatures. + // Example: + // ``` + // { + // "1896f70a": { + // "name": "setResolver", + // "inputs": [...], + // ... + // }, + // "ac9650d8": { + // "name": "multicall", + // "inputs": [...], + // ... + // } + // } + // ``` + string smart_contract_abi_json = 2; +} + +message ContractCallDecodingOutput { + // Human readable json format, according to the input `ContractCallDecodingInput::smart_contract_abi_json`. + string decoded_json = 1; + + // Decoded parameters. + repeated Token tokens = 2; + + // error code, 0 is ok, other codes will be treated as errors + AbiError error = 3; + + // error code description + string error_message = 4; +} + +//// TWEthereumAbiDecodeParams + +// A set of ABI type parameters. +message AbiParams { + // ABI type parameters. + repeated Param params = 1; +} + +// Decode a function input or output data according to the given ABI json. +message ParamsDecodingInput { + // An encoded ABI. + bytes encoded = 1; + + oneof abi { + // A set of ABI parameters in JSON. + // Expected to be a JSON array at the entry level. + // Example: + // ``` + // [ + // { + // "name": "_to', + // "type": "address" + // }, + // { + // "name": "_value", + // "type": "uint256" + // } + // ] + // ``` + string abi_json = 2; + + // A set of ABI type parameters. + AbiParams abi_params = 3; + } +} + +message ParamsDecodingOutput { + // Decoded parameters. + repeated Token tokens = 1; + + // error code, 0 is ok, other codes will be treated as errors + AbiError error = 2; + + // error code description + string error_message = 3; +} + +//// TWEthereumAbiDecodeValue + +// Decode an Eth ABI value. +message ValueDecodingInput { + // An encoded value to be decoded. + bytes encoded = 1; + + // A type of the parameter. + // Example: "bytes[32]". + // Please note `tuple` is not supported. + string param_type = 2; +} + +message ValueDecodingOutput { + // Decoded parameter. + Token token = 1; + + // Decoded parameter as a string. + string param_str = 2; + + // error code, 0 is ok, other codes will be treated as errors + AbiError error = 3; + + // error code description + string error_message = 4; +} + +//// TWEthereumAbiEncodeFunction + +// Encode a function call to Eth ABI binary. +message FunctionEncodingInput { + // Function name. + string function_name = 1; + + // Parameters to be encoded. + repeated Token tokens = 2; +} + +message FunctionEncodingOutput { + // The function type signature. + // Example: "baz(int32,uint256)" + string function_type = 1; + + // An encoded smart contract call with a prefixed function signature (4 bytes). + bytes encoded = 2; + + // error code, 0 is ok, other codes will be treated as errors + AbiError error = 3; + + // error code description + string error_message = 4; +} + +//// TWEthereumAbiFunctionGetType + +// Return the function type signature, of the form "baz(int32,uint256)". +message FunctionGetTypeInput { + // Function signature. Includes function inputs if they are. + // Examples: + // - `functionName()` + // - `functionName()` + // - `functionName(bool)` + // - `functionName(uint256,bytes32)` + string function_name = 1; + + // A set of ABI type parameters. + repeated Param inputs = 2; +} diff --git a/src/proto/EthereumRlp.proto b/src/proto/EthereumRlp.proto new file mode 100644 index 00000000000..0655ff37064 --- /dev/null +++ b/src/proto/EthereumRlp.proto @@ -0,0 +1,49 @@ +syntax = "proto3"; + +package TW.EthereumRlp.Proto; +option java_package = "wallet.core.jni.proto"; + +import "Common.proto"; + +// List of elements. +message RlpList { + repeated RlpItem items = 1; +} + +// RLP item. +message RlpItem { + oneof item { + // A string to be encoded. + string string_item = 1; + // A U64 number to be encoded. + uint64 number_u64 = 2; + // A U256 number to be encoded. + bytes number_u256 = 3; + // An address to be encoded. + string address = 4; + // A data to be encoded. + bytes data = 5; + // A list of items to be encoded. + RlpList list = 6; + // An RLP encoded item to be appended as it is. + bytes raw_encoded = 7; + } +} + +// RLP encoding input. +message EncodingInput { + // An item or a list to encode. + RlpItem item = 1; +} + +/// RLP encoding output. +message EncodingOutput { + // An item RLP encoded. + bytes encoded = 1; + + // Error code, 0 is ok, other codes will be treated as errors. + Common.Proto.SigningError error = 2; + + // Error code description. + string error_message = 3; +} diff --git a/src/proto/Utxo.proto b/src/proto/Utxo.proto new file mode 100644 index 00000000000..ada5c6bad9a --- /dev/null +++ b/src/proto/Utxo.proto @@ -0,0 +1,221 @@ +syntax = "proto3"; + +package TW.Utxo.Proto; +option java_package = "wallet.core.jni.proto"; + +enum Error { + OK = 0; + Error_invalid_leaf_hash = 1; + Error_invalid_sighash_type = 2; + Error_invalid_lock_time = 3; + Error_invalid_txid = 4; + Error_sighash_failed = 5; + Error_missing_sighash_method = 6; + Error_failed_encoding = 7; + Error_insufficient_inputs = 8; + Error_missing_change_script_pubkey = 9; +} + +message SigningInput { + // The protocol version. + int32 version = 1; + + // Block height or timestamp indicating at what point transactions can be + // included in a block. + LockTime lock_time = 2; + + // The inputs of the transaction. + repeated TxIn inputs = 3; + + // The outputs of the transaction. + repeated TxOut outputs = 4; + + // How inputs should be selected. + InputSelector input_selector = 5; + + // The base unit per weight. In the case of Bitcoin, that would refer to + // satoshis by vbyte ("satVb"). + uint64 weight_base = 6; + + // The change output where to send left-over funds to (usually the sender). + bytes change_script_pubkey = 7; + + // Explicility disable change output creation. + bool disable_change_output = 8; +} + +enum InputSelector { + // Use all the inputs provided in the given order. + UseAll = 0; + // Automatically select enough inputs in the given order to cover the + // outputs of the transaction. + SelectInOrder = 1; + // Automatically select enough inputs in an ascending order to cover the + // outputs of the transaction. + SelectAscending = 2; +} + +message LockTime { + oneof variant { + uint32 blocks = 1; + uint32 seconds = 2; + } +} + +message TxIn { + // The referenced transaction ID in REVERSED order. + bytes txid = 1; + + // The position in the previous transactions output that this input + // references. + uint32 vout = 2; + + // The value of this input, such as satoshis. Required for producing + // Segwit/Taproot transactions. + uint64 value = 3; + + // The sequence number, used for timelocks, replace-by-fee, etc. Normally + // this number is simply 4294967295 (0xFFFFFFFF) . + uint32 sequence = 4; + + // The spending condition of the referenced output. + bytes script_pubkey = 7; + + // The sighash type, normally `SighashType::UseDefault` (All). + SighashType sighash_type = 8; + + // The signing method. + SigningMethod signing_method = 9; + + // The estimated weight of the input, required for estimating fees. + uint64 weight_estimate = 10; + + // If this input is a Taproot script-path (complex transaction), then this + // leaf hash is required in order to compute the sighash. + bytes leaf_hash = 11; +} + +enum SigningMethod { + // Used for P2SH and P2PKH + Legacy = 0; + // Used for P2WSH and P2WPKH + Segwit = 1; + // Used for P2TR key-path and P2TR script-paty + TaprootAll = 2; + // Used for P2TR key-path and P2TR script-paty if only one prevout should be + // used to calculate the Sighash. Normally this is not used. + TaprootOnePrevout = 3; +} + +enum SighashType { + // Use default (All) + UseDefault = 0; // 0x00 + // Sign all outputs (default). + All = 1; // 0x01 + // Sign no outputs, anyone can choose the destination. + None = 2; // 0x02 + // Sign the output whose index matches this inputs index. + Single = 3; // 0x03 + //Sign all outputs but only this input. + AllPlusAnyoneCanPay = 129; // 0x81 + // Sign no outputs and only this input. + NonePlusAnyoneCanPay = 130; // 0x82 + // Sign one output and only this input. + SinglePlusAnyoneCanPay = 131; // 0x83 +} + +// The output of a transaction. +message TxOut { + // The value of the output. + uint64 value = 1; + // The spending condition of the output. + bytes script_pubkey = 2; +} + +message PreSigningOutput { + // error code, 0 is ok, other codes will be treated as errors + Error error = 1; + + // The transaction ID in NON-reversed order. Note that this must be reversed + // when referencing in future transactions. + bytes txid = 2; + + /// Sighashes to be signed; ECDSA for legacy and Segwit, Schnorr for Taproot. + repeated Sighash sighashes = 3; + + // The raw inputs. + repeated TxIn inputs = 4; + + // The raw outputs. + repeated TxOut outputs = 5; + + // The estimated weight of the transaction. + uint64 weight_estimate = 6; + + // The estimated fee of the transaction denominated in the base unit (such + // as satoshis). + uint64 fee_estimate = 7; +} + +message Sighash { + // The sighash to be signed. + bytes sighash = 1; + // The used signing method for this sighash. + SigningMethod signing_method = 2; + // The used sighash type for this sighash. + SighashType sighash_type = 3; +} + +message PreSerialization { + // The protocol version, is currently expected to be 1 or 2 (BIP68) + int32 version = 1; + + // Block height or timestamp indicating at what point transactions can be + // included in a block. + LockTime lock_time = 2; + + // The transaction inputs containing the serialized claim scripts. + repeated TxInClaim inputs = 3; + + // The transaction outputs. + repeated TxOut outputs = 4; + + // The base unit per weight. In the case of Bitcoin, that would refer to + // satoshis ("satVb"). + uint64 weight_base = 5; +} + +message TxInClaim { + // The referenced transaction hash. + bytes txid = 1; + + // The index of the referenced output. + uint32 vout = 2; + + // The sequence number (TODO). + uint32 sequence = 3; + + // The script used for claiming an input. + bytes script_sig = 4; + + // The script used for claiming an input. + repeated bytes witness_items = 5; +} + +message SerializedTransaction { + // error code, 0 is ok, other codes will be treated as errors + Error error = 1; + + // The encoded transaction, ready to be submitted to the network. + bytes encoded = 2; + + // The transaction ID. + bytes txid = 3; + + // The total and final weight of the transaction. + uint64 weight = 4; + + // The total and final fee of the transaction denominated in the base unit + // (such as satoshis). + uint64 fee = 5; +} diff --git a/src/rust/Wrapper.h b/src/rust/Wrapper.h index 906b7343dce..3b4f9436f39 100644 --- a/src/rust/Wrapper.h +++ b/src/rust/Wrapper.h @@ -6,13 +6,102 @@ #pragma once +#include #include -#include "Data.h" -#include "rust/bindgen/WalletCoreRSBindgen.h" +#include "../Data.h" +#include "bindgen/WalletCoreRSBindgen.h" namespace TW::Rust { +inline std::shared_ptr wrapTWAnyAddress(TWAnyAddress* anyAddress) { + return std::shared_ptr(anyAddress, tw_any_address_delete); +} + +inline std::shared_ptr wrapTWPublicKey(TWPublicKey* publicKey) { + return std::shared_ptr(publicKey, tw_public_key_delete); +} + +struct TWDataVectorWrapper { + /// Implicit constructor. + TWDataVectorWrapper(const std::vector& vec) { + ptr = std::shared_ptr(tw_data_vector_create(), Rust::tw_data_vector_delete); + + for (const auto& item : vec) { + auto* itemData = tw_data_create_with_bytes(item.data(), item.size()); + Rust::tw_data_vector_add(ptr.get(), itemData); + Rust::tw_data_delete(itemData); + } + } + + ~TWDataVectorWrapper() = default; + + TWDataVector* get() const { + return ptr.get(); + } + + std::shared_ptr ptr; +}; + +struct TWDataWrapper { + /// Implicit constructor. + TWDataWrapper(const Data& bytes) { + auto* dataRaw = tw_data_create_with_bytes(bytes.data(), bytes.size()); + ptr = std::shared_ptr(dataRaw, tw_data_delete); + } + + /// Implicit constructor. + TWDataWrapper(TWData *ptr): ptr(std::shared_ptr(ptr, tw_data_delete)) { + } + + ~TWDataWrapper() = default; + + TWData* get() const { + return ptr.get(); + } + + Data toDataOrDefault() const { + if (!ptr) { + return {}; + } + + auto* bytes = tw_data_bytes(ptr.get()); + Data out(bytes, bytes + tw_data_size(ptr.get())); + return out; + } + + std::shared_ptr ptr; +}; + +struct TWStringWrapper { + /// Implicit constructor. + TWStringWrapper(const std::string& string) { + auto* stringRaw = tw_string_create_with_utf8_bytes(string.c_str()); + ptr = std::shared_ptr(stringRaw, tw_string_delete); + } + + /// Implicit constructor. + TWStringWrapper(TWString *ptr): ptr(std::shared_ptr(ptr, tw_string_delete)) { + } + + ~TWStringWrapper() = default; + + TWString* get() const { + return ptr.get(); + } + + std::string toStringOrDefault() const { + if (!ptr) { + return {}; + } + + auto* bytes = tw_string_utf8_bytes(ptr.get()); + return {bytes}; + } + + std::shared_ptr ptr; +}; + struct CByteArrayWrapper { CByteArrayWrapper() = default; diff --git a/swift/Tests/BarzTests.swift b/swift/Tests/BarzTests.swift index 8cfd526b459..d86ae0b1aba 100644 --- a/swift/Tests/BarzTests.swift +++ b/swift/Tests/BarzTests.swift @@ -96,7 +96,7 @@ class BarzTests: XCTestCase { } let output: EthereumSigningOutput = AnySigner.sign(input: input, coin: .ethereum) XCTAssertEqual(output.preHash.hexString, "2d37191a8688f69090451ed90a0a9ba69d652c2062ee9d023b3ebe964a3ed2ae") - XCTAssertEqual(String(data: output.encoded, encoding: .utf8), "{\"callData\":\"0xb61d27f600000000000000000000000061061fcae11fd5461535e134eff67a98cfff44e9000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"100000\",\"initCode\":\"0x\",\"maxFeePerGas\":\"7033440745\",\"maxPriorityFeePerGas\":\"7033440745\",\"nonce\":\"2\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"46856\",\"sender\":\"0xb16db98b365b1f89191996942612b14f1da4bd5f\",\"signature\":\"0x80e84992ebf8d5f71180231163ed150a7557ed0aa4b4bcee23d463a09847e4642d0fbf112df2e5fa067adf4b2fa17fc4a8ac172134ba5b78e3ec9c044e7f28d71c\",\"verificationGasLimit\":\"100000\"}") + XCTAssertEqual(String(data: output.encoded, encoding: .utf8), "{\"callData\":\"0xb61d27f600000000000000000000000061061fcae11fd5461535e134eff67a98cfff44e9000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"100000\",\"initCode\":\"0x\",\"maxFeePerGas\":\"7033440745\",\"maxPriorityFeePerGas\":\"7033440745\",\"nonce\":\"2\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"46856\",\"sender\":\"0xb16Db98B365B1f89191996942612B14F1Da4Bd5f\",\"signature\":\"0x80e84992ebf8d5f71180231163ed150a7557ed0aa4b4bcee23d463a09847e4642d0fbf112df2e5fa067adf4b2fa17fc4a8ac172134ba5b78e3ec9c044e7f28d71c\",\"verificationGasLimit\":\"100000\"}") } // https://testnet.bscscan.com/tx/0xea1f5cddc0653e116327cbcb3bc770360a642891176eff2ec69c227e46791c31 @@ -135,7 +135,7 @@ class BarzTests: XCTestCase { } let output: EthereumSigningOutput = AnySigner.sign(input: input, coin: .ethereum) XCTAssertEqual(output.preHash.hexString, "548c13a0bb87981d04a3a24a78ad5e4ba8d0afbf3cfe9311250e07b54cd38937") - XCTAssertEqual(String(data: output.encoded, encoding: .utf8), "{\"callData\":\"0xb61d27f600000000000000000000000061061fcae11fd5461535e134eff67a98cfff44e9000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"2500000\",\"initCode\":\"0x3fc708630d85a3b5ec217e53100ec2b735d4f800296601cd0000000000000000000000005034534efe9902779ed6ea6983f435c00f3bc51000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004104b173a6a812025c40c38bac46343646bd0a8137c807aae6e04aac238cc24d2ad2116ca14d23d357588ff2aabd7db29d5976f4ecc8037775db86f67e873a306b1f00000000000000000000000000000000000000000000000000000000000000\",\"maxFeePerGas\":\"7033440745\",\"maxPriorityFeePerGas\":\"7033440745\",\"nonce\":\"0\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"46856\",\"sender\":\"0x1392ae041bfbdbaa0cff9234a0c8f64df97b7218\",\"signature\":\"0xbf1b68323974e71ad9bd6dfdac07dc062599d150615419bb7876740d2bcf3c8909aa7e627bb0e08a2eab930e2e7313247c9b683c884236dd6ea0b6834fb2cb0a1b\",\"verificationGasLimit\":\"3000000\"}"); + XCTAssertEqual(String(data: output.encoded, encoding: .utf8), "{\"callData\":\"0xb61d27f600000000000000000000000061061fcae11fd5461535e134eff67a98cfff44e9000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"2500000\",\"initCode\":\"0x3fc708630d85a3b5ec217e53100ec2b735d4f800296601cd0000000000000000000000005034534efe9902779ed6ea6983f435c00f3bc51000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004104b173a6a812025c40c38bac46343646bd0a8137c807aae6e04aac238cc24d2ad2116ca14d23d357588ff2aabd7db29d5976f4ecc8037775db86f67e873a306b1f00000000000000000000000000000000000000000000000000000000000000\",\"maxFeePerGas\":\"7033440745\",\"maxPriorityFeePerGas\":\"7033440745\",\"nonce\":\"0\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"46856\",\"sender\":\"0x1392Ae041BfBdBAA0cFF9234a0C8F64df97B7218\",\"signature\":\"0xbf1b68323974e71ad9bd6dfdac07dc062599d150615419bb7876740d2bcf3c8909aa7e627bb0e08a2eab930e2e7313247c9b683c884236dd6ea0b6834fb2cb0a1b\",\"verificationGasLimit\":\"3000000\"}"); } // https://testnet.bscscan.com/tx/0x872f709815a9f79623a349f2f16d93b52c4d5136967bab53a586f045edbe9203 @@ -185,7 +185,7 @@ class BarzTests: XCTestCase { } let output: EthereumSigningOutput = AnySigner.sign(input: input, coin: .ethereum) XCTAssertEqual(output.preHash.hexString, "84d0464f5a2b191e06295443970ecdcd2d18f565d0d52b5a79443192153770ab") - XCTAssertEqual(String(data: output.encoded, encoding: .utf8), "{\"callData\":\"0x47e1da2a000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000003bbb5660b8687c2aa453a0e42dcb6e0732b126600000000000000000000000003bbb5660b8687c2aa453a0e42dcb6e0732b12660000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27890000000000000000000000000000000000000000000000008ac7230489e80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27890000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"88673\",\"initCode\":\"0x\",\"maxFeePerGas\":\"10000000000\",\"maxPriorityFeePerGas\":\"10000000000\",\"nonce\":\"3\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"56060\",\"sender\":\"0x1e6c542ebc7c960c6a155a9094db838cef842cf5\",\"signature\":\"0x0747b665fe9f3a52407f95a35ac3e76de37c9b89483ae440431244e89a77985f47df712c7364c1a299a5ef62d0b79a2cf4ed63d01772275dd61f72bd1ad5afce1c\",\"verificationGasLimit\":\"522180\"}") + XCTAssertEqual(String(data: output.encoded, encoding: .utf8), "{\"callData\":\"0x47e1da2a000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000003bbb5660b8687c2aa453a0e42dcb6e0732b126600000000000000000000000003bbb5660b8687c2aa453a0e42dcb6e0732b12660000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27890000000000000000000000000000000000000000000000008ac7230489e80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27890000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"88673\",\"initCode\":\"0x\",\"maxFeePerGas\":\"10000000000\",\"maxPriorityFeePerGas\":\"10000000000\",\"nonce\":\"3\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"56060\",\"sender\":\"0x1E6c542ebC7c960c6A155A9094DB838cEf842cf5\",\"signature\":\"0x0747b665fe9f3a52407f95a35ac3e76de37c9b89483ae440431244e89a77985f47df712c7364c1a299a5ef62d0b79a2cf4ed63d01772275dd61f72bd1ad5afce1c\",\"verificationGasLimit\":\"522180\"}") } let factory = "0x3fC708630d85A3B5ec217E53100eC2b735d4f800" diff --git a/swift/Tests/Blockchains/EthereumAbiTests.swift b/swift/Tests/Blockchains/EthereumAbiTests.swift index a6a18430e5b..ef7e9f413df 100644 --- a/swift/Tests/Blockchains/EthereumAbiTests.swift +++ b/swift/Tests/Blockchains/EthereumAbiTests.swift @@ -57,7 +57,7 @@ class EthereumAbiTests: XCTestCase { func testValueDecoderValue() { XCTAssertEqual("42", EthereumAbiValue.decodeValue(input: Data(hexString: "000000000000000000000000000000000000000000000000000000000000002a")!, type: "uint")) XCTAssertEqual("24", EthereumAbiValue.decodeValue(input: Data(hexString: "0000000000000000000000000000000000000000000000000000000000000018")!, type: "uint8")) - XCTAssertEqual("0xf784682c82526e245f50975190ef0fff4e4fc077", EthereumAbiValue.decodeValue(input: Data(hexString: "000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077")!, type: "address")) + XCTAssertEqual("0xF784682C82526e245F50975190EF0fff4E4fC077", EthereumAbiValue.decodeValue(input: Data(hexString: "000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077")!, type: "address")) XCTAssertEqual("Hello World! Hello World! Hello World!", EthereumAbiValue.decodeValue(input: Data(hexString: "000000000000000000000000000000000000000000000000000000000000002c48656c6c6f20576f726c64212020202048656c6c6f20576f726c64212020202048656c6c6f20576f726c64210000000000000000000000000000000000000000" )!, type: "string")) @@ -71,7 +71,7 @@ class EthereumAbiTests: XCTestCase { func testValueDecoderArray_address() { let input = Data(hexString: "0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc0770000000000000000000000002e00cd222cb42b616d86d037cc494e8ab7f5c9a3") - XCTAssertEqual("[\"0xf784682c82526e245f50975190ef0fff4e4fc077\",\"0x2e00cd222cb42b616d86d037cc494e8ab7f5c9a3\"]", EthereumAbiValue.decodeArray(input: input!, type: "address[]")) + XCTAssertEqual("[\"0xF784682C82526e245F50975190EF0fff4E4fC077\",\"0x2e00CD222Cb42B616D86D037Cc494e8ab7F5c9a3\"]", EthereumAbiValue.decodeArray(input: input!, type: "address[]")) } func testValueDecoderArray_bytes() { @@ -90,7 +90,7 @@ class EthereumAbiTests: XCTestCase { "inputs": [{ "name": "_spender", "type": "address", - "value": "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed" + "value": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed" }, { "name": "_value", "type": "uint256", @@ -113,24 +113,24 @@ class EthereumAbiTests: XCTestCase { "inputs": [{ "name": "caller", "type": "address", - "value": "0x27239549dd40e1d60f5b80b0c4196923745b1fd2" + "value": "0x27239549DD40E1D60F5B80B0C4196923745B1FD2" }, { "components": [{ "name": "srcToken", "type": "address", - "value": "0x2b591e99afe9f32eaa6214f7b7629768c40eeb39" + "value": "0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39" }, { "name": "dstToken", "type": "address", - "value": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" + "value": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" }, { "name": "srcReceiver", "type": "address", - "value": "0x27239549dd40e1d60f5b80b0c4196923745b1fd2" + "value": "0x27239549DD40E1D60F5B80B0C4196923745B1FD2" }, { "name": "dstReceiver", "type": "address", - "value": "0x1611c227725c5e420ef058275ae772b41775e261" + "value": "0x1611C227725c5E420Ef058275AE772b41775e261" }, { "name": "amount", "type": "uint256", @@ -221,4 +221,135 @@ class EthereumAbiTests: XCTestCase { XCTAssertEqual(hash.hexString, "54140d99a864932cbc40fd8a2d1d1706c3923a79c183a3b151e929ac468064db") } + + func testEthereumAbiEncodeFunction() throws { + let amountIn = EthereumAbiNumberNParam.with { + $0.bits = 256 + $0.value = Data(hexString: "0x0de0b6b3a7640000")! // 1000000000000000000 + } + let amountOutMin = EthereumAbiNumberNParam.with { + $0.bits = 256 + $0.value = Data(hexString: "0x229f7e501ad62bdb")! // 2494851601099271131 + } + let deadline = EthereumAbiNumberNParam.with { + $0.bits = 256 + $0.value = Data(hexString: "0x5f0ed070")! // 1594806384 + } + + let arr = EthereumAbiArrayParam.with { + $0.elementType = EthereumAbiParamType.with { + $0.address = EthereumAbiAddressType.init() + } + $0.elements = [ + EthereumAbiToken.with { $0.address = "0x6B175474E89094C44Da98b954EedeAC495271d0F" }, + EthereumAbiToken.with { $0.address = "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2" }, + EthereumAbiToken.with { $0.address = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" }, + EthereumAbiToken.with { $0.address = "0xE41d2489571d322189246DaFA5ebDe1F4699F498" }, + ] + } + let encodingInput = EthereumAbiFunctionEncodingInput.with { + $0.functionName = "swapExactTokensForTokens" + $0.tokens = [ + EthereumAbiToken.with { $0.numberUint = amountIn }, + EthereumAbiToken.with { $0.numberUint = amountOutMin }, + EthereumAbiToken.with { $0.array = arr }, + EthereumAbiToken.with { $0.address = "0x7d8bf18C7cE84b3E175b339c4Ca93aEd1dD166F1" }, + EthereumAbiToken.with { $0.numberUint = deadline }, + ] + } + + let inputData = try encodingInput.serializedData() + let outputData = EthereumAbi.encodeFunction(coin: .ethereum, input: inputData) + + let encodingOutput = try EthereumAbiFunctionEncodingOutput(serializedData: outputData) + XCTAssertEqual(encodingOutput.error, EthereumAbiAbiError.ok) + XCTAssertEqual(encodingOutput.functionType, "swapExactTokensForTokens(uint256,uint256,address[],address,uint256)") + + let expectedEncoded = Data(hexString: "0x38ed17390000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000229f7e501ad62bdb00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000007d8bf18c7ce84b3e175b339c4ca93aed1dd166f1000000000000000000000000000000000000000000000000000000005f0ed07000000000000000000000000000000000000000000000000000000000000000040000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000009f8f72aa9304c8b593d555f12ef6589cc3a579a2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498")! + XCTAssertEqual(encodingOutput.encoded, expectedEncoded) + } + + func testEthereumAbiDecodeParams() throws { + let encoded = Data(hexString: "00000000000000000000000088341d1a8f672d2780c8dc725902aae72f143b0c0000000000000000000000000000000000000000000000000000000000000001")! + + let abiParams = [ + EthereumAbiParam.with { + $0.name = "to" + $0.param = EthereumAbiParamType.with { $0.address = EthereumAbiAddressType.init() } + }, + EthereumAbiParam.with { + $0.name = "approved" + $0.param = EthereumAbiParamType.with { $0.boolean = EthereumAbiBoolType.init() } + } + ] + let decodingInput = EthereumAbiParamsDecodingInput.with { + $0.encoded = encoded + $0.abiParams = EthereumAbiAbiParams.with { $0.params = abiParams } + } + + let inputData = try decodingInput.serializedData() + let outputData = EthereumAbi.decodeParams(coin: .ethereum, input: inputData) + + let decodingOutput = try EthereumAbiParamsDecodingOutput(serializedData: outputData) + XCTAssertEqual(decodingOutput.error, EthereumAbiAbiError.ok) + + XCTAssertEqual(decodingOutput.tokens[0].name, "to") + XCTAssertEqual(decodingOutput.tokens[0].address, "0x88341d1a8F672D2780C8dC725902AAe72F143B0c") + XCTAssertEqual(decodingOutput.tokens[1].name, "approved") + XCTAssertEqual(decodingOutput.tokens[1].boolean, true) + } + + func testEthereumAbiDecodeValue() throws { + let encoded = Data(hexString: "000000000000000000000000000000000000000000000000000000000000002c48656c6c6f20576f726c64212020202048656c6c6f20576f726c64212020202048656c6c6f20576f726c64210000000000000000000000000000000000000000")! + + let decodingInput = EthereumAbiValueDecodingInput.with { + $0.encoded = encoded + $0.paramType = "string" + } + + let inputData = try decodingInput.serializedData() + let outputData = EthereumAbi.decodeValue(coin: .ethereum, input: inputData) + + let decodingOutput = try EthereumAbiValueDecodingOutput(serializedData: outputData) + XCTAssertEqual(decodingOutput.error, EthereumAbiAbiError.ok) + XCTAssertEqual(decodingOutput.token.stringValue, "Hello World! Hello World! Hello World!") + } + + func testEthereumAbiDecodeContractCall() throws { + let encoded = Data(hexString: "c47f0027000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000086465616462656566000000000000000000000000000000000000000000000000")! + let abiJson = """ + { + "c47f0027": { + "constant": false, + "inputs": [ + { + "name": "name", + "type": "string" + } + ], + "name": "setName", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + } + """ + + let decodingInput = EthereumAbiContractCallDecodingInput.with { + $0.encoded = encoded + $0.smartContractAbiJson = abiJson + } + + let inputData = try decodingInput.serializedData() + let outputData = EthereumAbi.decodeContractCall(coin: .ethereum, input: inputData) + + let decodingOutput = try EthereumAbiContractCallDecodingOutput(serializedData: outputData) + XCTAssertEqual(decodingOutput.error, EthereumAbiAbiError.ok) + + let expectedJson = #"{"function":"setName(string)","inputs":[{"name":"name","type":"string","value":"deadbeef"}]}"# + XCTAssertEqual(decodingOutput.decodedJson, expectedJson) + XCTAssertEqual(decodingOutput.tokens[0].name, "name") + XCTAssertEqual(decodingOutput.tokens[0].stringValue, "deadbeef") + } } diff --git a/swift/Tests/Blockchains/EthereumRlpTests.swift b/swift/Tests/Blockchains/EthereumRlpTests.swift new file mode 100644 index 00000000000..3690657bab9 --- /dev/null +++ b/swift/Tests/Blockchains/EthereumRlpTests.swift @@ -0,0 +1,47 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import XCTest +import WalletCore + +class EthereumRlpTests: XCTestCase { + func testRlpEncodeEip1559() throws { + let chainId = Data(hexString: "0x0a")! + let nonce = Data(hexString: "0x06")! + let maxInclusionFeePerGas = Data(hexString: "0x77359400")! // 2000000000 + let maxFeePerGas = Data(hexString: "0xb2d05e00")! // 3000000000 + let gasLimit = Data(hexString: "0x526c")! // 21100 + let to = "0x6b175474e89094c44da98b954eedeac495271d0f" + let amount = Data(hexString: "0x00")! + let payload = Data(hexString: "a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000001ee0c29f50cb1")! + // Empty `access_list`. + let accessList = EthereumRlpRlpList() + + let rlpList = EthereumRlpRlpList.with { + $0.items = [ + EthereumRlpRlpItem.with { $0.numberU256 = chainId }, + EthereumRlpRlpItem.with { $0.numberU256 = nonce }, + EthereumRlpRlpItem.with { $0.numberU256 = maxInclusionFeePerGas }, + EthereumRlpRlpItem.with { $0.numberU256 = maxFeePerGas }, + EthereumRlpRlpItem.with { $0.numberU256 = gasLimit }, + EthereumRlpRlpItem.with { $0.address = to }, + EthereumRlpRlpItem.with { $0.numberU256 = amount }, + EthereumRlpRlpItem.with { $0.data = payload }, + EthereumRlpRlpItem.with { $0.list = accessList }, + ] + } + + let encodingInput = EthereumRlpEncodingInput.with { + $0.item.list = rlpList + } + let inputData = try encodingInput.serializedData() + let outputData = EthereumRlp.encode(coin: .ethereum, input: inputData) + + let encodingOutput = try EthereumRlpEncodingOutput(serializedData: outputData) + XCTAssertEqual(encodingOutput.error, CommonSigningError.ok) + XCTAssertEqual(encodingOutput.encoded.hexString, "f86c0a06847735940084b2d05e0082526c946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000001ee0c29f50cb1c0") + } +} diff --git a/swift/Tests/Blockchains/NEOTests.swift b/swift/Tests/Blockchains/NEOTests.swift index 89754e3e28e..74f87b6102e 100644 --- a/swift/Tests/Blockchains/NEOTests.swift +++ b/swift/Tests/Blockchains/NEOTests.swift @@ -95,7 +95,12 @@ class NEOTests: XCTestCase { let result = signedTx.encoded.hexString // https://testnet-explorer.o3.network/transactions/0x7b138c753c24f474d0f70af30a9d79756e0ee9c1f38c12ed07fbdf6fc5132eaf + XCTAssertEqual("8000001efb50cb3be3e08917b308a1dbdb2408109394560ec67518af43035d8c260815c601000bd791a26120eef181d8162bd6cb7495dee1299aa67bb796dcd4a03769f9b24e00000bea299e6a243c9379c3e8884c9176b1456b3017611772b2fadc55d10901ee3f000026c413526bbd45cca355683db9f39d6864a7e298f481f2cdeefe8b578ccea96e00002b2647616d4f4143700f8e862aa8427efd7fa9998fe040e23ed877d2cbd35af700003159b899275e2f0e0b1314acddc7e1ec5948598fca40a9733e2b448fe9344705000036509c8a487005aa8e16663613d2d767461ee2f8dc4f678cc22f9148d4420c8b0000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040100385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040200385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040300385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040400385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040500385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040600385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040700385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040800385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f04090040ec871088beb680f5b149767dbb0b8ac7ec1a1c5836e177606b6200e6bc83cf00004e393bd89d886ae116ed9e6b49be427b21f7247d896923265e68dfa82b57d79b00005b99bf2caacf20bfc9cd51b3d3472499383c803c2d781d00f1e2dd970325eeb4000062ac42685ef8b856291bb0264fcb767b00706a15243326775f61a159a29c01e100006f011d435ef43c066689d1222f4eada1d4590ebaaa190960ac26a5acf29d37bd00007dea63ea47a6c9e8318f3b19a0df5ccb3a348f54a176736afa7b9b3b843f4c160000925e50254e8056bfd540f3d45f171dbab504f5b191070ee7af1e16764ac7ce4a00009677a6869128961a1a3b17e609e915d2d9a29ceaab5689dccb841ca729665c8900009692e4e512eb2e04b10042bcc28910140b2229ede40291b0e1a0c3c44381825400009dc6c119d0f4bacb1b1e9faffcba33581729c1915a2f1147ce7a6fc8abe4455300009f6b635afee02b5db0c93a5b1bfcace34a18c78d76c73b7bf90d21d4d0193ec80000b11bbb613e36b2bcc6c3a76c888c6c139957a1b7091dab26ce88b65c3fb056340000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040a00039b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500ba1dd205000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50083064905000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ace72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c605013cf0617000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac014140dc261ac093a87640441bf0c3ad4a55ec727932b9175f600618bb5275f31aacf122956bc88746dc666759a2d67f120fe3ce1659f916d22a91e0b02421d3bddbd1232102a41c2aea8568864b106553729d32b1317ec463aa23e7a3521455d95992e17a7aac", result) + + // TODO uncomment when nist256p1 Rust implementation is enabled. + // XCTAssertEqual("8000001efb50cb3be3e08917b308a1dbdb2408109394560ec67518af43035d8c260815c601000bd791a26120eef181d8162bd6cb7495dee1299aa67bb796dcd4a03769f9b24e00000bea299e6a243c9379c3e8884c9176b1456b3017611772b2fadc55d10901ee3f000026c413526bbd45cca355683db9f39d6864a7e298f481f2cdeefe8b578ccea96e00002b2647616d4f4143700f8e862aa8427efd7fa9998fe040e23ed877d2cbd35af700003159b899275e2f0e0b1314acddc7e1ec5948598fca40a9733e2b448fe9344705000036509c8a487005aa8e16663613d2d767461ee2f8dc4f678cc22f9148d4420c8b0000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040100385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040200385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040300385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040400385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040500385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040600385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040700385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040800385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f04090040ec871088beb680f5b149767dbb0b8ac7ec1a1c5836e177606b6200e6bc83cf00004e393bd89d886ae116ed9e6b49be427b21f7247d896923265e68dfa82b57d79b00005b99bf2caacf20bfc9cd51b3d3472499383c803c2d781d00f1e2dd970325eeb4000062ac42685ef8b856291bb0264fcb767b00706a15243326775f61a159a29c01e100006f011d435ef43c066689d1222f4eada1d4590ebaaa190960ac26a5acf29d37bd00007dea63ea47a6c9e8318f3b19a0df5ccb3a348f54a176736afa7b9b3b843f4c160000925e50254e8056bfd540f3d45f171dbab504f5b191070ee7af1e16764ac7ce4a00009677a6869128961a1a3b17e609e915d2d9a29ceaab5689dccb841ca729665c8900009692e4e512eb2e04b10042bcc28910140b2229ede40291b0e1a0c3c44381825400009dc6c119d0f4bacb1b1e9faffcba33581729c1915a2f1147ce7a6fc8abe4455300009f6b635afee02b5db0c93a5b1bfcace34a18c78d76c73b7bf90d21d4d0193ec80000b11bbb613e36b2bcc6c3a76c888c6c139957a1b7091dab26ce88b65c3fb056340000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040a00039b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500ba1dd205000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50083064905000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ace72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c605013cf0617000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac014140dc261ac093a87640441bf0c3ad4a55ec727932b9175f600618bb5275f31aacf1dd6a943678b9239a98a65d2980edf01beed0a0b4904573f31309a6a128a54980232102a41c2aea8568864b106553729d32b1317ec463aa23e7a3521455d95992e17a7aac", + // result) } } diff --git a/swift/Tests/Blockchains/OntologyTests.swift b/swift/Tests/Blockchains/OntologyTests.swift index 3779918b0f7..4daec3a7403 100644 --- a/swift/Tests/Blockchains/OntologyTests.swift +++ b/swift/Tests/Blockchains/OntologyTests.swift @@ -53,7 +53,11 @@ class OntologyTests: XCTestCase { let output: OntologySigningOutput = AnySigner.sign(input: input, coin: .ontology) let result = output.encoded.hexString + XCTAssertEqual("00d102d45c8bf401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df67100c66b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc814feec06b79ed299ea06fcb94abac41aaf3ead76586a7cc8516a7cc86c51c1087472616e736665721400000000000000000000000000000000000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b6500024140301766d925382a6ebb2ebeb18d3741954c9370dcf6d9c45b34ce7b18bc42dcdb7cff28ddaf7f1048822c0ca21a0c4926323a2497875b963f3b8cbd3717aa6e7c2321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac414038466b25ac49a22ba8c301328ef049a61711b257987e85e25d63e0444a14e860305a4cd3bb6ea2fe80fd293abb3c592e679c42c546cbf3baa051a07b28b374a6232103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", result) + + // TODO uncomment when nist256p1 Rust implementation is enabled. + // XCTAssertEqual("00d102d45c8bf401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df67100c66b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc814feec06b79ed299ea06fcb94abac41aaf3ead76586a7cc8516a7cc86c51c1087472616e736665721400000000000000000000000000000000000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b6500024140301766d925382a6ebb2ebeb18d3741954c9370dcf6d9c45b34ce7b18bc42dcdb8300d7215080efb87dd3f35de5f3b6d98aacd6161fbc0845b82d0d8be4b8b6d52321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac414038466b25ac49a22ba8c301328ef049a61711b257987e85e25d63e0444a14e860305a4cd3bb6ea2fe80fd293abb3c592e679c42c546cbf3baa051a07b28b374a6232103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", result) } func testSignOngTransfer() { @@ -72,7 +76,11 @@ class OntologyTests: XCTestCase { let output: OntologySigningOutput = AnySigner.sign(input: input, coin: .ontology) let result = output.encoded.hexString + XCTAssertEqual("00d19d3182a8f401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df67100c66b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc814feec06b79ed299ea06fcb94abac41aaf3ead76586a7cc8516a7cc86c51c1087472616e736665721400000000000000000000000000000000000000020068164f6e746f6c6f67792e4e61746976652e496e766f6b6500024140e27e935b87855efad62bb76b21c7b591f445f867eff86f888ca6ee1870ecd80f73b8ab199a4d757b4c7b9ed46c4ff8cfa8aefaa90b7fb6485e358034448cba752321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac4140450047b2efb384129a16ec4c707790e9379b978cc7085170071d8d7c5c037d743b078bd4e21bb4404c0182a32ee05260e22454dffb34dacccf458dfbee6d32db232103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", result) + + // TODO uncomment when nist256p1 Rust implementation is enabled. + // XCTAssertEqual("00d19d3182a8f401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df67100c66b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc814feec06b79ed299ea06fcb94abac41aaf3ead76586a7cc8516a7cc86c51c1087472616e736665721400000000000000000000000000000000000000020068164f6e746f6c6f67792e4e61746976652e496e766f6b6500024140e27e935b87855efad62bb76b21c7b591f445f867eff86f888ca6ee1870ecd80f8c4754e565b28a85b384612b93b00730143800049b97e83c95844a8eb7d66adc2321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac4140450047b2efb384129a16ec4c707790e9379b978cc7085170071d8d7c5c037d74c4f8742a1de44bc0b3fe7d5cd11fad9edac2a5cdabe2c3b824743cc70df5f276232103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", result) } } diff --git a/tests/chains/Aion/RLPTests.cpp b/tests/chains/Aion/RLPTests.cpp index cda4a23fbbd..7ad104a9bdf 100644 --- a/tests/chains/Aion/RLPTests.cpp +++ b/tests/chains/Aion/RLPTests.cpp @@ -12,16 +12,23 @@ namespace TW::Aion::tests { using boost::multiprecision::uint128_t; +// Function helper over `RLP::prepareLong`. +Data encodeLong(const uint128_t& l) { + EthereumRlp::Proto::EncodingInput input; + *input.mutable_item() = RLP::prepareLong(l); + return Ethereum::RLP::encode(input); +} + TEST(AionRLP, EncodeLong) { - EXPECT_EQ(hex(RLP::encodeLong(uint128_t(1))), "01"); - EXPECT_EQ(hex(RLP::encodeLong(uint128_t(21000))), "825208"); - EXPECT_EQ(hex(RLP::encodeLong(uint128_t(1000000))), "830f4240"); - EXPECT_EQ(hex(RLP::encodeLong(uint128_t(20000000000))), "8800000004a817c800"); - EXPECT_EQ(hex(RLP::encodeLong(uint128_t(9007199254740991))), "88001fffffffffffff"); - EXPECT_EQ(hex(RLP::encodeLong(uint128_t(9007199254740990))), "88001ffffffffffffe"); - EXPECT_EQ(hex(RLP::encodeLong(uint128_t(4294967296L))), "880000000100000000"); - EXPECT_EQ(hex(RLP::encodeLong(uint128_t(4295000060L))), "880000000100007ffc"); - EXPECT_EQ(hex(RLP::encodeLong(uint128_t(72057594037927935L))), "8800ffffffffffffff"); + EXPECT_EQ(hex(encodeLong(uint128_t(1))), "01"); + EXPECT_EQ(hex(encodeLong(uint128_t(21000))), "825208"); + EXPECT_EQ(hex(encodeLong(uint128_t(1000000))), "830f4240"); + EXPECT_EQ(hex(encodeLong(uint128_t(20000000000))), "8800000004a817c800"); + EXPECT_EQ(hex(encodeLong(uint128_t(9007199254740991))), "88001fffffffffffff"); + EXPECT_EQ(hex(encodeLong(uint128_t(9007199254740990))), "88001ffffffffffffe"); + EXPECT_EQ(hex(encodeLong(uint128_t(4294967296L))), "880000000100000000"); + EXPECT_EQ(hex(encodeLong(uint128_t(4295000060L))), "880000000100007ffc"); + EXPECT_EQ(hex(encodeLong(uint128_t(72057594037927935L))), "8800ffffffffffffff"); } } // namespace TW::Aion::tests diff --git a/tests/chains/BinanceSmartChain/SignerTests.cpp b/tests/chains/BinanceSmartChain/SignerTests.cpp index 1ce61b7fd27..c970d687488 100644 --- a/tests/chains/BinanceSmartChain/SignerTests.cpp +++ b/tests/chains/BinanceSmartChain/SignerTests.cpp @@ -5,14 +5,13 @@ // file LICENSE at the root of the source code distribution tree. #include -#include "Ethereum/Signer.h" -#include "Ethereum/Transaction.h" #include "Ethereum/Address.h" -#include "Ethereum/ABI.h" +#include "Ethereum/ABI/Function.h" #include "proto/Ethereum.pb.h" #include "HexCoding.h" #include "uint256.h" #include "TestUtilities.h" +#include "PrivateKey.h" #include @@ -21,33 +20,39 @@ namespace TW::Binance { TEST(BinanceSmartChain, SignNativeTransfer) { // https://explorer.binance.org/smart-testnet/tx/0x6da28164f7b3bc255d749c3ae562e2a742be54c12bf1858b014cc2fe5700684e - auto toAddress = parse_hex("0x31BE00EB1fc8e14A696DBC72f746ec3e95f49683"); - auto transaction = Ethereum::TransactionNonTyped::buildNativeTransfer( - /* nonce: */ 0, - /* gasPrice: */ 20000000000, - /* gasLimit: */ 21000, - /* to: */ toAddress, - /* amount: */ 10000000000000000 // 0.01 - ); + Ethereum::Proto::SigningInput input; + auto chainId = store(uint256_t(97)); + auto nonce = store(uint256_t(0)); + auto gasPrice = store(uint256_t(20000000000)); + auto gasLimit = store(uint256_t(21000)); + auto toAddress = "0x31BE00EB1fc8e14A696DBC72f746ec3e95f49683"; + auto amount = store(uint256_t(10000000000000000)); // 0.01 // addr: 0xB9F5771C27664bF2282D98E09D7F50cEc7cB01a7 mnemonic: isolate dismiss ... cruel note - auto privateKey = PrivateKey(parse_hex("4f96ed80e9a7555a6f74b3d658afdd9c756b0a40d4ca30c42c2039eb449bb904")); - uint256_t chainID = 97; - auto signature = Ethereum::Signer::sign(privateKey, chainID, transaction); + auto privateKey = parse_hex("4f96ed80e9a7555a6f74b3d658afdd9c756b0a40d4ca30c42c2039eb449bb904"); + + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + input.set_gas_price(gasPrice.data(), gasPrice.size()); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_to_address(toAddress); + input.set_private_key(privateKey.data(), privateKey.size()); + auto& transfer = *input.mutable_transaction()->mutable_transfer(); + transfer.set_amount(amount.data(), amount.size()); + + Ethereum::Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSmartChain); - auto encoded = transaction->encoded(signature, chainID); - ASSERT_EQ(hex(encoded), "f86c808504a817c8008252089431be00eb1fc8e14a696dbc72f746ec3e95f49683872386f26fc100008081e5a057806b486844c5d0b7b5ce34b289f4e8776aa1fe24a3311cef5053995c51050ca07697aa0695de27da817625df0e7e4c64b0ab22d9df30aec92299a7b380be8db7"); + ASSERT_EQ(hex(output.encoded()), "f86c808504a817c8008252089431be00eb1fc8e14a696dbc72f746ec3e95f49683872386f26fc100008081e5a057806b486844c5d0b7b5ce34b289f4e8776aa1fe24a3311cef5053995c51050ca07697aa0695de27da817625df0e7e4c64b0ab22d9df30aec92299a7b380be8db7"); } TEST(BinanceSmartChain, SignTokenTransfer) { - auto toAddress = parse_hex("0x31BE00EB1fc8e14A696DBC72f746ec3e95f49683"); - auto func = Ethereum::ABI::Function("transfer", std::vector>{ - std::make_shared(toAddress), - std::make_shared(uint256_t(10000000000000000)) - }); - Data payloadFunction; - func.encode(payloadFunction); - EXPECT_EQ(hex(payloadFunction), "a9059cbb00000000000000000000000031be00eb1fc8e14a696dbc72f746ec3e95f49683000000000000000000000000000000000000000000000000002386f26fc10000"); + auto toAddress = "0x31BE00EB1fc8e14A696DBC72f746ec3e95f49683"; + auto funcData = Ethereum::ABI::Function::encodeFunctionCall("transfer", Ethereum::ABI::BaseParams{ + std::make_shared(toAddress), + std::make_shared(uint256_t(10000000000000000)) + }).value(); + EXPECT_EQ(hex(funcData), "a9059cbb00000000000000000000000031be00eb1fc8e14a696dbc72f746ec3e95f49683000000000000000000000000000000000000000000000000002386f26fc10000"); auto input = Ethereum::Proto::SigningInput(); auto chainId = store(uint256_t(97)); @@ -66,7 +71,7 @@ TEST(BinanceSmartChain, SignTokenTransfer) { input.set_to_address(tokenContractAddress); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); auto& transfer = *input.mutable_transaction()->mutable_contract_generic(); - transfer.set_data(payloadFunction.data(), payloadFunction.size()); + transfer.set_data(funcData.data(), funcData.size()); const std::string expected = "f8ab1e8504a817c800830f424094ed24fc36d5ee211ea25a80239fb8c4cfd80f12ee80b844a9059cbb00000000000000000000000031be00eb1fc8e14a696dbc72f746ec3e95f49683000000000000000000000000000000000000000000000000002386f26fc1000081e6a0aa9d5e9a947e96f728fe5d3e6467000cd31a693c00270c33ec64b4abddc29516a00bf1d5646139b2bcca1ad64e6e79f45b7d1255de603b5a3765cbd9544ae148d0"; diff --git a/tests/chains/Cosmos/THORChain/SwapTests.cpp b/tests/chains/Cosmos/THORChain/SwapTests.cpp index 32db1f54988..d64ffed7565 100644 --- a/tests/chains/Cosmos/THORChain/SwapTests.cpp +++ b/tests/chains/Cosmos/THORChain/SwapTests.cpp @@ -8,8 +8,6 @@ #include "Bitcoin/Script.h" #include "Bitcoin/SegwitAddress.h" #include "Ethereum/ABI/Function.h" -#include "Ethereum/ABI/ParamAddress.h" -#include "Ethereum/ABI/ParamBase.h" #include "Ethereum/Address.h" #include "THORChain/Swap.h" #include "proto/Binance.pb.h" @@ -484,19 +482,17 @@ TEST(THORChainSwap, SwapErc20Rune) { EXPECT_EQ(tx.to_address(), "0x8f66c4ae756bebc49ec8b81966dd8bba9f127549"); ASSERT_TRUE(tx.transaction().has_contract_generic()); - Data vaultAddressBin = SwapTest_ethAddressStringToData("0xa56f6Cb1D66cd80150b1ea79643b4C5900D6E36E"); - EXPECT_EQ(hex(vaultAddressBin), "a56f6cb1d66cd80150b1ea79643b4c5900d6e36e"); - auto func = Ethereum::ABI::Function("depositWithExpiry", std::vector>{ - std::make_shared(vaultAddressBin), - std::make_shared(parse_hex("0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E")), - std::make_shared(uint256_t(1000000)), - std::make_shared("=:THOR.RUNE:thor1ad6hapypumu7su5ad9qry2d74yt9d56fssa774:51638857:t:0"), - std::make_shared(uint256_t(1775669796))}); - Data payload; - func.encode(payload); - EXPECT_EQ(hex(payload), "44bc937b000000000000000000000000a56f6cb1d66cd80150b1ea79643b4c5900d6e36e000000000000000000000000b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e00000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000069d6922400000000000000000000000000000000000000000000000000000000000000443d3a54484f522e52554e453a74686f72316164366861707970756d753773753561643971727932643734797439643536667373613737343a35313633383835373a743a3000000000000000000000000000000000000000000000000000000000"); + auto vaultAddress = "0xa56f6Cb1D66cd80150b1ea79643b4C5900D6E36E"; + auto funcData = Ethereum::ABI::Function::encodeFunctionCall("depositWithExpiry", Ethereum::ABI::BaseParams{ + std::make_shared(vaultAddress), + std::make_shared("0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E"), + std::make_shared(uint256_t(1000000)), + std::make_shared("=:THOR.RUNE:thor1ad6hapypumu7su5ad9qry2d74yt9d56fssa774:51638857:t:0"), + std::make_shared(uint256_t(1775669796)) + }).value(); + EXPECT_EQ(hex(funcData), "44bc937b000000000000000000000000a56f6cb1d66cd80150b1ea79643b4c5900d6e36e000000000000000000000000b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e00000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000069d6922400000000000000000000000000000000000000000000000000000000000000443d3a54484f522e52554e453a74686f72316164366861707970756d753773753561643971727932643734797439643536667373613737343a35313633383835373a743a3000000000000000000000000000000000000000000000000000000000"); EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().amount())), "00"); - EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().data())), hex(payload)); + EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().data())), hex(funcData)); EXPECT_EQ(hex(TW::data(tx.private_key())), ""); diff --git a/tests/chains/Ethereum/AbiStructTests.cpp b/tests/chains/Ethereum/AbiStructTests.cpp deleted file mode 100644 index 923e853fd1b..00000000000 --- a/tests/chains/Ethereum/AbiStructTests.cpp +++ /dev/null @@ -1,930 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Ethereum/ABI.h" -#include "Ethereum/Address.h" -#include "Ethereum/Signer.h" -#include "TestUtilities.h" -#include -#include - -#include -#include - -using namespace TW; - -extern std::string TESTS_ROOT; - -namespace TW::Ethereum::tests { - -using namespace ABI; - -std::string load_file(const std::string path) { - std::ifstream stream(path); - std::string content((std::istreambuf_iterator(stream)), (std::istreambuf_iterator())); - return content; -} - -// https://github.com/MetaMask/eth-sig-util/blob/main/test/index.ts -// clang-format off -ParamStruct msgPersonCow2("Person", std::vector>{ - std::make_shared("name", std::make_shared("Cow")), - std::make_shared("wallets", std::make_shared(std::vector>{ - std::make_shared(parse_hex("CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826")), - std::make_shared(parse_hex("DeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF")) - })) -}); -ParamStruct msgPersonBob3("Person", std::vector>{ - std::make_shared("name", std::make_shared("Bob")), - std::make_shared("wallets", std::make_shared(std::vector>{ - std::make_shared(parse_hex("bBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB")), - std::make_shared(parse_hex("B0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57")), - std::make_shared(parse_hex("B0B0b0b0b0b0B000000000000000000000000000")) - })) -}); -ParamStruct msgGroup("Group", std::vector>{ - std::make_shared("name", std::make_shared("")), - std::make_shared("members", std::make_shared(std::vector>{ - std::make_shared(msgPersonCow2) - })) -}); -ParamStruct msgMailCow1Bob1("Mail", std::vector>{ - std::make_shared("from", std::make_shared("Person", std::vector>{ - std::make_shared("name", std::make_shared("Cow")), - std::make_shared("wallet", std::make_shared(parse_hex("CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"))) - })), - std::make_shared("to", std::make_shared("Person", std::vector>{ - std::make_shared("name", std::make_shared("Bob")), - std::make_shared("wallet", std::make_shared(parse_hex("bBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"))) - })), - std::make_shared("contents", std::make_shared("Hello, Bob!")) -}); -ParamStruct msgMailCow2Bob3("Mail", std::vector>{ - std::make_shared("from", std::make_shared(msgPersonCow2)), - std::make_shared("to", std::make_shared(std::make_shared(msgPersonBob3))), - std::make_shared("contents", std::make_shared("Hello, Bob!")) -}); -ParamStruct gMsgEIP712Domain("EIP712Domain", std::vector>{ - std::make_shared("name", std::make_shared("Ether Mail")), - std::make_shared("version", std::make_shared("1")), - std::make_shared("chainId", std::make_shared(1)), - std::make_shared("verifyingContract", std::make_shared(parse_hex("CcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"))) -}); - -PrivateKey privateKeyCow = PrivateKey(Hash::keccak256(TW::data("cow"))); -PrivateKey privateKeyDragon = PrivateKey(Hash::keccak256(TW::data("dragon"))); -PrivateKey privateKeyOilTimes12 = PrivateKey(parse_hex("b0f20d59451a2fac1be6d458e036adfa5d83ebd4c21f9a76de3c4a3a65671eba")); // 0x60c2A43Cc69658eC4b02a65A07623D7192166F4e - -// See 'signedTypeData' in https://github.com/MetaMask/eth-sig-util/blob/main/test/index.ts -TEST(EthereumAbiStruct, encodeTypes) { - EXPECT_EQ(msgMailCow1Bob1.encodeType(), "Mail(Person from,Person to,string contents)Person(string name,address wallet)"); - - EXPECT_EQ(hex(msgMailCow1Bob1.hashType()), "a0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2"); - - EXPECT_EQ(hex(msgMailCow1Bob1.encodeHashes()), "a0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8cd54f074a4af31b4411ff6a60c9719dbd559c221c8ac3492d9d872b041d703d1b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8"); - - EXPECT_EQ(hex(msgMailCow1Bob1.hashStruct()), "c52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e"); - - EXPECT_EQ(hex(gMsgEIP712Domain.hashStruct()), "f2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f"); - - Address address = Address(privateKeyCow.getPublicKey(TWPublicKeyTypeSECP256k1Extended)); - EXPECT_EQ(address.string(), "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"); -} - -TEST(EthereumAbiStruct, encodeTypes_Json) { - auto hash = ParamStruct::hashStructJson( - R"({ - "types": { - "EIP712Domain": [ - {"name": "name", "type": "string"}, - {"name": "version", "type": "string"}, - {"name": "chainId", "type": "uint256"}, - {"name": "verifyingContract", "type": "address"} - ], - "Person": [ - {"name": "name", "type": "string"}, - {"name": "wallet", "type": "address"} - ], - "Mail": [ - {"name": "from", "type": "Person"}, - {"name": "to", "type": "Person"}, - {"name": "contents", "type": "string"} - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "0x01", - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { - "name": "Cow", - "wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - "to": { - "name": "Bob", - "wallet": "bBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" - }, - "contents": "Hello, Bob!" - } - })"); - ASSERT_EQ(hex(hash), "be609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2"); - - // sign the hash - const auto rsv = Signer::sign(privateKeyCow, hash, true, 0); - EXPECT_EQ(hex(store(rsv.r)), "4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d"); - EXPECT_EQ(hex(store(rsv.s)), "07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b91562"); - EXPECT_EQ(hex(store(rsv.v)), "1c"); -} - -// See 'signedTypeData with V3 string' in https://github.com/MetaMask/eth-sig-util/blob/main/test/index.ts -TEST(EthereumAbiStruct, encodeTypes_v3) { - EXPECT_EQ(msgMailCow1Bob1.encodeType(), "Mail(Person from,Person to,string contents)Person(string name,address wallet)"); - - EXPECT_EQ(hex(msgMailCow1Bob1.hashType()), "a0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2"); - - EXPECT_EQ(hex(msgMailCow1Bob1.encodeHashes()), "a0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8cd54f074a4af31b4411ff6a60c9719dbd559c221c8ac3492d9d872b041d703d1b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8"); - - EXPECT_EQ(hex(msgMailCow1Bob1.hashStruct()), "c52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e"); - - EXPECT_EQ(hex(gMsgEIP712Domain.hashStruct()), "f2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f"); - - Address address = Address(privateKeyCow.getPublicKey(TWPublicKeyTypeSECP256k1Extended)); - EXPECT_EQ(address.string(), "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"); -} - -TEST(EthereumAbiStruct, encodeTypes_v3_Json) { - auto hash = ParamStruct::hashStructJson( - R"({ - "types": { - "EIP712Domain": [ - {"name": "name", "type": "string"}, - {"name": "version", "type": "string"}, - {"name": "chainId", "type": "uint256"}, - {"name": "verifyingContract", "type": "address"} - ], - "Person": [ - {"name": "name", "type": "string"}, - {"name": "wallet", "type": "address"} - ], - "Mail": [ - {"name": "from", "type": "Person"}, - {"name": "to", "type": "Person"}, - {"name": "contents", "type": "string"} - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": 1, - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { - "name": "Cow", - "wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - "to": { - "name": "Bob", - "wallet": "bBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" - }, - "contents": "Hello, Bob!" - } - })"); - ASSERT_EQ(hex(hash), "be609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2"); - - // sign the hash - const auto rsv = Signer::sign(privateKeyCow, hash, true, 0); - EXPECT_EQ(hex(store(rsv.r)), "4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d"); - EXPECT_EQ(hex(store(rsv.s)), "07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b91562"); - EXPECT_EQ(hex(store(rsv.v)), "1c"); -} - -// See 'signedTypeData_v4' in https://github.com/MetaMask/eth-sig-util/blob/main/test/index.ts -TEST(EthereumAbiStruct, encodeTypes_v4) { - EXPECT_EQ(msgGroup.encodeType(), "Group(string name,Person[] members)Person(string name,address[] wallets)"); - - EXPECT_EQ(msgPersonCow2.encodeType(), "Person(string name,address[] wallets)"); - - EXPECT_EQ(hex(msgPersonCow2.hashType()), "fabfe1ed996349fc6027709802be19d047da1aa5d6894ff5f6486d92db2e6860"); - - EXPECT_EQ(hex(msgPersonCow2.encodeHashes()), - "fabfe1ed996349fc6027709802be19d047da1aa5d6894ff5f6486d92db2e6860" - "8c1d2bd5348394761719da11ec67eedae9502d137e8940fee8ecd6f641ee1648" - "8a8bfe642b9fc19c25ada5dadfd37487461dc81dd4b0778f262c163ed81b5e2a"); - - EXPECT_EQ(hex(msgPersonCow2.hashStruct()), "9b4846dd48b866f0ac54d61b9b21a9e746f921cefa4ee94c4c0a1c49c774f67f"); - - EXPECT_EQ(hex(msgPersonBob3.encodeHashes()), - "fabfe1ed996349fc6027709802be19d047da1aa5d6894ff5f6486d92db2e6860" - "28cac318a86c8a0a6a9156c2dba2c8c2363677ba0514ef616592d81557e679b6" - "d2734f4c86cc3bd9cabf04c3097589d3165d95e4648fc72d943ed161f651ec6d"); - - EXPECT_EQ(hex(msgPersonBob3.hashStruct()), "efa62530c7ae3a290f8a13a5fc20450bdb3a6af19d9d9d2542b5a94e631a9168"); - - EXPECT_EQ(msgMailCow2Bob3.encodeType(), "Mail(Person from,Person[] to,string contents)Person(string name,address[] wallets)"); - - EXPECT_EQ(hex(msgMailCow2Bob3.hashType()), "4bd8a9a2b93427bb184aca81e24beb30ffa3c747e2a33d4225ec08bf12e2e753"); - - EXPECT_EQ(hex(msgMailCow2Bob3.encodeHashes()), - "4bd8a9a2b93427bb184aca81e24beb30ffa3c747e2a33d4225ec08bf12e2e753" - "9b4846dd48b866f0ac54d61b9b21a9e746f921cefa4ee94c4c0a1c49c774f67f" - "ca322beec85be24e374d18d582a6f2997f75c54e7993ab5bc07404ce176ca7cd" - "b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8"); - - EXPECT_EQ(hex(msgMailCow2Bob3.hashStruct()), "eb4221181ff3f1a83ea7313993ca9218496e424604ba9492bb4052c03d5c3df8"); - - EXPECT_EQ(hex(gMsgEIP712Domain.hashStruct()), "f2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f"); - - Address address = Address(privateKeyCow.getPublicKey(TWPublicKeyTypeSECP256k1Extended)); - EXPECT_EQ(address.string(), "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"); -} - -TEST(EthereumAbiStruct, encodeTypes_v4_Json) { - auto hash = ParamStruct::hashStructJson( - R"({ - "types": { - "EIP712Domain": [ - {"name": "name", "type": "string"}, - {"name": "version", "type": "string"}, - {"name": "chainId", "type": "uint256"}, - {"name": "verifyingContract", "type": "address"} - ], - "Person": [ - {"name": "name", "type": "string"}, - {"name": "wallets", "type": "address[]"} - ], - "Mail": [ - {"name": "from", "type": "Person"}, - {"name": "to", "type": "Person[]"}, - {"name": "contents", "type": "string"} - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": 1, - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { - "name": "Cow", - "wallets": [ - "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", - "DeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF" - ] - }, - "to": [ - { - "name": "Bob", - "wallets": [ - "bBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", - "B0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57", - "B0B0b0b0b0b0B000000000000000000000000000" - ] - } - ], - "contents": "Hello, Bob!" - } - })"); - ASSERT_EQ(hex(hash), "a85c2e2b118698e88db68a8105b794a8cc7cec074e89ef991cb4f5f533819cc2"); - - // sign the hash - const auto rsv = Signer::sign(privateKeyCow, hash, true, 0); - EXPECT_EQ(hex(store(rsv.r)), "65cbd956f2fae28a601bebc9b906cea0191744bd4c4247bcd27cd08f8eb6b71c"); - EXPECT_EQ(hex(store(rsv.s)), "78efdf7a31dc9abee78f492292721f362d296cf86b4538e07b51303b67f74906"); - EXPECT_EQ(hex(store(rsv.v)), "1b"); -} - -// See 'signedTypeData_v4 with recursive types' in https://github.com/MetaMask/eth-sig-util/blob/main/test/index.ts -TEST(EthereumAbiStruct, encodeTypes_v4Rec) { - ParamStruct msgPersonRecursiveMother("Person", std::vector>{ - std::make_shared("name", std::make_shared("Lyanna")), - std::make_shared("mother", std::make_shared("Person", std::vector>{})), - std::make_shared("father", std::make_shared("Person", std::vector>{ - std::make_shared("name", std::make_shared("Rickard")), - std::make_shared("mother", std::make_shared("Person", std::vector>{})), - std::make_shared("father", std::make_shared("Person", std::vector>{})) - })) - }); - ParamStruct msgPersonRecursiveFather("Person", std::vector>{ - std::make_shared("name", std::make_shared("Rhaegar")), - std::make_shared("mother", std::make_shared("Person", std::vector>{})), - std::make_shared("father", std::make_shared("Person", std::vector>{ - std::make_shared("name", std::make_shared("Aeris II")), - std::make_shared("mother", std::make_shared("Person", std::vector>{})), - std::make_shared("father", std::make_shared("Person", std::vector>{})) - })) - }); - ParamStruct msgPersonRecursive("Person", std::vector>{ - std::make_shared("name", std::make_shared("Jon")), - std::make_shared("mother", std::make_shared(msgPersonRecursiveMother)), - std::make_shared("father", std::make_shared(msgPersonRecursiveFather)) - }); - - EXPECT_EQ(msgPersonRecursive.encodeType(), "Person(string name,Person mother,Person father)"); - - EXPECT_EQ(hex(msgPersonRecursive.hashType()), "7c5c8e90cb92c8da53b893b24962513be98afcf1b57b00327ae4cc14e3a64116"); - - EXPECT_EQ(hex(msgPersonRecursiveMother.encodeHashes()), - "7c5c8e90cb92c8da53b893b24962513be98afcf1b57b00327ae4cc14e3a64116" - "afe4142a2b3e7b0503b44951e6030e0e2c5000ef83c61857e2e6003e7aef8570" - "0000000000000000000000000000000000000000000000000000000000000000" - "88f14be0dd46a8ec608ccbff6d3923a8b4e95cdfc9648f0db6d92a99a264cb36"); - - EXPECT_EQ(hex(msgPersonRecursiveMother.hashStruct()), "9ebcfbf94f349de50bcb1e3aa4f1eb38824457c99914fefda27dcf9f99f6178b"); - - EXPECT_EQ(hex(msgPersonRecursiveFather.encodeHashes()), - "7c5c8e90cb92c8da53b893b24962513be98afcf1b57b00327ae4cc14e3a64116" - "b2a7c7faba769181e578a391a6a6811a3e84080c6a3770a0bf8a856dfa79d333" - "0000000000000000000000000000000000000000000000000000000000000000" - "02cc7460f2c9ff107904cff671ec6fee57ba3dd7decf999fe9fe056f3fd4d56e"); - - EXPECT_EQ(hex(msgPersonRecursiveFather.hashStruct()), "b852e5abfeff916a30cb940c4e24c43cfb5aeb0fa8318bdb10dd2ed15c8c70d8"); - - EXPECT_EQ(hex(msgPersonRecursive.encodeHashes()), - "7c5c8e90cb92c8da53b893b24962513be98afcf1b57b00327ae4cc14e3a64116" - "e8d55aa98b6b411f04dbcf9b23f29247bb0e335a6bc5368220032fdcb9e5927f" - "9ebcfbf94f349de50bcb1e3aa4f1eb38824457c99914fefda27dcf9f99f6178b" - "b852e5abfeff916a30cb940c4e24c43cfb5aeb0fa8318bdb10dd2ed15c8c70d8"); - - EXPECT_EQ(hex(msgPersonRecursive.hashStruct()), "fdc7b6d35bbd81f7fa78708604f57569a10edff2ca329c8011373f0667821a45"); - - ParamStruct msgEIP712Domain("EIP712Domain", std::vector>{ - std::make_shared("name", std::make_shared("Family Tree")), - std::make_shared("version", std::make_shared("1")), - std::make_shared("chainId", std::make_shared(1)), - std::make_shared("verifyingContract", std::make_shared(parse_hex("CcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"))) - }); - - EXPECT_EQ(hex(msgEIP712Domain.hashStruct()), "facb2c1888f63a780c84c216bd9a81b516fc501a19bae1fc81d82df590bbdc60"); - - Address address = Address(privateKeyDragon.getPublicKey(TWPublicKeyTypeSECP256k1Extended)); - EXPECT_EQ(address.string(), "0x065a687103C9F6467380beE800ecD70B17f6b72F"); -} - -TEST(EthereumAbiStruct, encodeTypes_v4Rec_Json) { - auto hash = ParamStruct::hashStructJson( - R"({ - "types": { - "EIP712Domain": [ - {"name": "name", "type": "string"}, - {"name": "version", "type": "string"}, - {"name": "chainId", "type": "uint256"}, - {"name": "verifyingContract", "type": "address"} - ], - "Person": [ - {"name": "name", "type": "string"}, - {"name": "mother", "type": "Person"}, - {"name": "father", "type": "Person"} - ] - }, - "primaryType": "Person", - "domain": { - "name": "Family Tree", - "version": "1", - "chainId": 1, - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "name": "Jon", - "mother": { - "name": "Lyanna", - "father": { - "name": "Rickard" - } - }, - "father": { - "name": "Rhaegar", - "father": { - "name": "Aeris II" - } - } - } - })"); - ASSERT_EQ(hex(hash), "807773b9faa9879d4971b43856c4d60c2da15c6f8c062bd9d33afefb756de19c"); - - // sign the hash - const auto rsv = Signer::sign(privateKeyDragon, hash, true, 0); - EXPECT_EQ(hex(store(rsv.r)), "f2ec61e636ff7bb3ac8bc2a4cc2c8b8f635dd1b2ec8094c963128b358e79c85c"); - EXPECT_EQ(hex(store(rsv.s)), "5ca6dd637ed7e80f0436fe8fce39c0e5f2082c9517fe677cc2917dcd6c84ba88"); - EXPECT_EQ(hex(store(rsv.v)), "1c"); -} - -// See 'signedTypeData' in https://github.com/MetaMask/eth-sig-util/blob/main/test/index.ts -TEST(EthereumAbiStruct, encodeTypeCow1) { - ParamStruct msgPersonCow1("Person", std::vector>{ - std::make_shared("name", std::make_shared("Cow")), - std::make_shared("wallet", std::make_shared(parse_hex("CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"))) - }); - - EXPECT_EQ(msgPersonCow1.encodeType(), "Person(string name,address wallet)"); - - EXPECT_EQ(hex(msgPersonCow1.hashType()), "b9d8c78acf9b987311de6c7b45bb6a9c8e1bf361fa7fd3467a2163f994c79500"); - - EXPECT_EQ(hex(msgPersonCow1.encodeHashes()), "b9d8c78acf9b987311de6c7b45bb6a9c8e1bf361fa7fd3467a2163f994c795008c1d2bd5348394761719da11ec67eedae9502d137e8940fee8ecd6f641ee1648000000000000000000000000cd2a3d9f938e13cd947ec05abc7fe734df8dd826"); - - EXPECT_EQ(hex(msgPersonCow1.hashStruct()), "fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8"); -} - -TEST(EthereumAbiStruct, hashStructJson) { - { - auto hash = ParamStruct::hashStructJson( - R"({ - "types": { - "EIP712Domain": [ - {"name": "name", "type": "string"}, - {"name": "version", "type": "string"}, - {"name": "chainId", "type": "uint256"}, - {"name": "verifyingContract", "type": "address"} - ], - "Person": [ - {"name": "name", "type": "string"}, - {"name": "wallet", "type": "address"} - ] - }, - "primaryType": "Person", - "domain": { - "name": "Ether Person", - "version": "1", - "chainId": 1, - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "name": "Cow", - "wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - } - })"); - ASSERT_EQ(hex(hash), "0b4bb85394b9ebb1c2425e283c9e734a9a7a832622e97c998f77e1c7a3f01a20"); - } - { // edge cases - EXPECT_EXCEPTION(ParamStruct::hashStructJson("NOT_A_JSON"), "Could not parse Json"); - EXPECT_EXCEPTION(ParamStruct::hashStructJson("+/{\\"), "Could not parse Json"); - EXPECT_EXCEPTION(ParamStruct::hashStructJson(""), "Could not parse Json"); - EXPECT_EXCEPTION(ParamStruct::hashStructJson("0"), "Expecting Json object"); - EXPECT_EXCEPTION(ParamStruct::hashStructJson("[]"), "Expecting Json object"); - EXPECT_EXCEPTION(ParamStruct::hashStructJson(R"({})"), "Top-level string field 'primaryType' missing"); - EXPECT_EXCEPTION(ParamStruct::hashStructJson(R"({"domain": {}, "message": {}, "types": {}})"), "Top-level string field 'primaryType' missing"); - EXPECT_EXCEPTION(ParamStruct::hashStructJson(R"({"primaryType": [], "domain": {}, "message": {}, "types": {}})"), "Top-level string field 'primaryType' missing"); - EXPECT_EXCEPTION(ParamStruct::hashStructJson(R"({"primaryType": "v1", "message": {}, "types": {}})"), "Top-level object field 'domain' missing"); - EXPECT_EXCEPTION(ParamStruct::hashStructJson(R"({"primaryType": "v1", "domain": "vDomain", "message": {}, "types": {}})"), "Top-level object field 'domain' missing"); - EXPECT_EXCEPTION(ParamStruct::hashStructJson(R"({"primaryType": "v1", "domain": {}, "types": {}})"), "Top-level object field 'message' missing"); - EXPECT_EXCEPTION(ParamStruct::hashStructJson(R"({"primaryType": "v1", "domain": {}, "message": "v2", "types": {}})"), "Top-level object field 'message' missing"); - EXPECT_EXCEPTION(ParamStruct::hashStructJson(R"({"primaryType": "v1", "domain": {}, "message": {}})"), "Top-level object field 'types' missing"); - EXPECT_EXCEPTION(ParamStruct::hashStructJson(R"({"primaryType": "v1", "domain": {}, "message": {}, "types": "vTypes"})"), "Top-level object field 'types' missing"); - EXPECT_EXCEPTION(ParamStruct::hashStructJson(R"({"primaryType": "v1", "domain": {}, "message": {}, "types": {}})"), "Type not found, EIP712Domain"); - EXPECT_EXCEPTION(ParamStruct::hashStructJson(R"({"primaryType": "v1", "domain": {}, "message": {}, "types": {"EIP712Domain": []}})"), "No valid params found"); - EXPECT_EXCEPTION(ParamStruct::hashStructJson(R"({"primaryType": "v1", "domain": {}, "message": {}, "types": {"EIP712Domain": [{"name": "param", "type": "type"}]}})"), "Unknown type type"); - EXPECT_EXCEPTION(ParamStruct::hashStructJson(R"({"primaryType": "v1", "domain": {"param": "val"}, "message": {}, "types": {"EIP712Domain": [{"name": "param", "type": "string"}]}})"), "Type not found, v1"); - } -} - -TEST(EthereumAbiStruct, hashStruct_emptyString) { - auto path = TESTS_ROOT + "/chains/Ethereum/Data/eip712_emptyString.json"; - auto typeData = load_file(path); - auto hash = ParamStruct::hashStructJson(typeData); - EXPECT_EQ(hex(hash), "bc9d33285c5e42b00571f5deaf9636d2e498a6fa50e0d1be81095bded070117a"); - - // sign the hash - const auto rsv = Signer::sign(privateKeyOilTimes12, hash, true, 0); - EXPECT_EQ(hex(store(rsv.r)), "5df6cb46d874bc0acc519695f393008a837ca9d2e316836b669b8f0de7673638"); - EXPECT_EQ(hex(store(rsv.s)), "54cc0bcc0ad657f9222f7e7be3fbe0ec4a8edb9385c39d578dfac8d38727af12"); - EXPECT_EQ(hex(store(rsv.v)), "1c"); -} - -TEST(EthereumAbiStruct, hashStruct_emptyArray) { - auto path = TESTS_ROOT + "/chains/Ethereum/Data/eip712_emptyArray.json"; - auto typeData = load_file(path); - auto hash = ParamStruct::hashStructJson(typeData); - EXPECT_EQ(hex(hash), "9f1a1bc718e966d683c544aef6fd0b73c85a1d6244af9b64bb8f4a6fa6716086"); - - // sign the hash - const auto rsv = Signer::sign(privateKeyOilTimes12, hash, true, 0); - EXPECT_EQ(hex(store(rsv.r)), "de47efd592493f7189d44f071424ecb24b50d80750d3bd2bb6fc80451c13a52f"); - EXPECT_EQ(hex(store(rsv.s)), "202b8a2be1ef3c466853e8cd5275a6af15b11e7e1cc0ae4a7e249bc9bad591eb"); - EXPECT_EQ(hex(store(rsv.v)), "1c"); -} - -TEST(EthereumAbiStruct, hashStruct_walletConnect) { - // https://github.com/WalletConnect/walletconnect-example-dapp/blob/master/src/helpers/eip712.ts - auto path = TESTS_ROOT + "/chains/Ethereum/Data/eip712_walletconnect.json"; - auto typeData = load_file(path); - auto hash = ParamStruct::hashStructJson(typeData); - EXPECT_EQ(hex(hash), "abc79f527273b9e7bca1b3f1ac6ad1a8431fa6dc34ece900deabcd6969856b5e"); - - // sign the hash - const auto rsv = Signer::sign(privateKeyOilTimes12, hash, true, 0); - EXPECT_EQ(hex(store(rsv.r)), "e9c1ce1307593c378c7e38e8aa00dfb42b5a1ce543b59a138a12f29bd7fea75c"); - EXPECT_EQ(hex(store(rsv.s)), "3fe71ef91c37abea29fe14b5f0de805f924af19d71bcef09e74aef2f0ccdf52a"); - EXPECT_EQ(hex(store(rsv.v)), "1c"); -} - -TEST(EthereumAbiStruct, hashStruct_cryptofights) { - auto path = TESTS_ROOT + "/chains/Ethereum/Data/eip712_cryptofights.json"; - auto typeData = load_file(path); - auto hash = ParamStruct::hashStructJson(typeData); - EXPECT_EQ(hex(hash), "db12328a6d193965801548e1174936c3aa7adbe1b54b3535a3c905bd4966467c"); - - // sign the hash - const auto rsv = Signer::sign(privateKeyOilTimes12, hash, true, 0); - EXPECT_EQ(hex(store(rsv.r)), "9e26bdf0d113a72805acb1c2c8b0734d264290fd1cfbdf5e6502ae65a2f2bd83"); - EXPECT_EQ(hex(store(rsv.s)), "11512c15ad0833fd457ae5dd59c3bcb3d03f35b3d33c1c5a575852163db42369"); - EXPECT_EQ(hex(store(rsv.v)), "1b"); -} - -TEST(EthereumAbiStruct, hashStruct_rarible) { - auto path = TESTS_ROOT + "/chains/Ethereum/Data/eip712_rarible.json"; - auto typeData = load_file(path); - auto hash = ParamStruct::hashStructJson(typeData); - EXPECT_EQ(hex(hash), "df0200de55c05eb55af2597012767ea3af653d68000be49580f8e05acd91d366"); - - // sign the hash - const auto rsv = Signer::sign(privateKeyCow, hash, true, 0); - EXPECT_EQ(hex(store(rsv.r)), "9e6155c62a55d3dc6034973d93821dace5a0c66bfbd8413ad29205c2fb079e84"); - EXPECT_EQ(hex(store(rsv.s)), "3ca5906f24b82672304302a0e42e5dc090acc800060bad51fb81cc4469f69930"); - EXPECT_EQ(hex(store(rsv.v)), "1b"); -} - -TEST(EthereumAbiStruct, hashStruct_snapshot) { - auto path = TESTS_ROOT + "/chains/Ethereum/Data/eip712_snapshot_v4.json"; - auto typeData = load_file(path); - auto hash = ParamStruct::hashStructJson(typeData); - EXPECT_EQ(hex(hash), "f558d08ad4a7651dbc9ec028cfcb4a8e6878a249073ef4fa694f85ee95f61c0f"); - - // sign the hash - const auto rsv = Signer::sign(privateKeyOilTimes12, hash, true, 0); - EXPECT_EQ(hex(store(rsv.r)), "9da563ffcafe9fa8809540ebcc4bcf8bbc26874e192f430432e06547593e8681"); - EXPECT_EQ(hex(store(rsv.s)), "164808603aca259775bdf511124b58651f1b3ce9ccbcd5a8d63df02e2359bb8b"); - EXPECT_EQ(hex(store(rsv.v)), "1b"); -} - -TEST(EthereumAbiStruct, ParamFactoryMakeNamed) { - std::shared_ptr p = ParamFactory::makeNamed("firstparam", "uint256"); - EXPECT_EQ(p->getName(), "firstparam"); - ASSERT_NE(p->getParam().get(), nullptr); - EXPECT_EQ(p->getParam()->getType(), "uint256"); -} - -TEST(EthereumAbiStruct, ParamStructMakeStruct) { - { - std::shared_ptr s = ParamStruct::makeStruct("Person", - R"( - {"name": "Cow", "wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"} - )", - R"({ - "Person": [{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}] - })"); - ASSERT_NE(s.get(), nullptr); - EXPECT_EQ(s->getType(), "Person"); - ASSERT_EQ(s->getCount(), 2ul); - EXPECT_EQ(s->encodeType(), "Person(string name,address wallet)"); - EXPECT_EQ(hex(s->hashStruct()), "fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8"); - } - { - std::shared_ptr s = ParamStruct::makeStruct("Person", - R"( - {"name": "Cow", "wallets": ["CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", "DeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"]} - )", - R"({ - "Person": [{"name": "name", "type": "string"}, {"name": "wallets", "type": "address[]"}] - })"); - ASSERT_NE(s.get(), nullptr); - EXPECT_EQ(s->getType(), "Person"); - ASSERT_EQ(s->getCount(), 2ul); - EXPECT_EQ(s->encodeType(), "Person(string name,address[] wallets)"); - EXPECT_EQ(hex(s->hashStruct()), "9b4846dd48b866f0ac54d61b9b21a9e746f921cefa4ee94c4c0a1c49c774f67f"); - } - { - std::shared_ptr s = ParamStruct::makeStruct("Mail", - R"({"from": {"name": "Cow", "wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"}, "to": {"name": "Bob", "wallet": "bBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"}, "contents": "Hello, Bob!"})", - R"({"Person": [{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}],"Mail": [{"name": "from", "type": "Person"},{"name": "to", "type": "Person"},{"name": "contents", "type": "string"}]})"); - ASSERT_NE(s.get(), nullptr); - EXPECT_EQ(s->getType(), "Mail"); - ASSERT_EQ(s->getCount(), 3ul); - EXPECT_EQ(s->encodeType(), "Mail(Person from,Person to,string contents)Person(string name,address wallet)"); - EXPECT_EQ(hex(s->hashStruct()), "c52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e"); - } - - { // extra param - std::shared_ptr s = ParamStruct::makeStruct("Person", - R"({"extra_param": "extra_value", "name": "Cow", "wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"})", - R"({"Person": [{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}]})"); - ASSERT_NE(s.get(), nullptr); - EXPECT_EQ(s->encodeType(), "Person(string name,address wallet)"); - EXPECT_EQ(hex(s->hashStruct()), "fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8"); - } - { // empty array - std::shared_ptr s = ParamStruct::makeStruct("Person", - R"({"extra_param": "extra_value", "name": "Cow", "wallets": []})", - R"({"Person": [{"name": "name", "type": "string"}, {"name": "wallets", "type": "address[]"}]})"); - ASSERT_NE(s.get(), nullptr); - EXPECT_EQ(s->encodeType(), "Person(string name,address[] wallets)"); - } - { // missing param - std::shared_ptr s = ParamStruct::makeStruct("Person", - R"({"wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"})", - R"({"Person": [{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}]})"); - ASSERT_NE(s.get(), nullptr); - EXPECT_EQ(s->encodeType(), "Person(string name,address wallet)"); - } - - { - EXPECT_EXCEPTION(ParamStruct::makeStruct("Person", - "NOT_A_JSON+/{\\", - R"({"Person": [{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}]})"), - "Could not parse value Json"); - } - { - EXPECT_EXCEPTION(ParamStruct::makeStruct("Person", - "0", - R"({"Person": [{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}]})"), - "Expecting object"); - } - { - EXPECT_EXCEPTION(ParamStruct::makeStruct("Person", - // params mixed up - R"({"wallets": "Cow", "name": ["CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", "DeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"]})", - R"({"Person": [{"name": "name", "type": "string"}, {"name": "wallets", "type": "address[]"}]})"), - "Could not set type for param wallets"); - } - { - EXPECT_EXCEPTION(ParamStruct::makeStruct("Person", - R"({"name": "Cow", "wallets": ["CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", "DeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"]})", - R"({"Person": [{"name": "name", "type": "string"}, {"name": "wallets", "type": "missingtype[]"}]})"), - "Unknown struct array type missingtype"); - } - { - EXPECT_EXCEPTION(ParamStruct::makeStruct("Person", - R"({"name": "Cow", "wallets": "NOT_AN_ARRAY"})", - R"({"Person": [{"name": "name", "type": "string"}, {"name": "wallets", "type": "address[]"}]})"), - "Could not set type for param wallets"); - } - { - EXPECT_EXCEPTION(ParamStruct::makeStruct("Mail", - R"({"from": {"name": "Cow", "wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"}, "to": {"name": "Bob", "wallet": "bBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"}, "contents": "Hello, Bob!"})", - R"({"Mail": [{"name": "from", "type": "Person"},{"name": "to", "type": "Person"},{"name": "contents", "type": "string"}]})"), - "Unknown type Person"); - } -} - -TEST(EthereumAbiStruct, ParamFactoryMakeTypes) { - { - std::vector> tt = ParamStruct::makeTypes(R"({"Person": [{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}]})"); - ASSERT_EQ(tt.size(), 1ul); - EXPECT_EQ(tt[0]->encodeType(), "Person(string name,address wallet)"); - } - { - std::vector> tt = ParamStruct::makeTypes( - R"({ - "Person": [{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}], - "Mail": [{"name": "from", "type": "Person"}, {"name": "to", "type": "Person"}, {"name": "contents", "type": "string"}] - })"); - ASSERT_EQ(tt.size(), 2ul); - EXPECT_EQ(tt[0]->encodeType(), "Mail(Person from,Person to,string contents)Person(string name,address wallet)"); - EXPECT_EQ(tt[1]->encodeType(), "Person(string name,address wallet)"); - } - { // edge cases - EXPECT_EXCEPTION(ParamStruct::makeTypes("NOT_A_JSON"), "Could not parse types Json"); - EXPECT_EXCEPTION(ParamStruct::makeTypes("+/{\\"), "Could not parse types Json"); - EXPECT_EXCEPTION(ParamStruct::makeTypes(""), "Could not parse types Json"); - EXPECT_EXCEPTION(ParamStruct::makeTypes("0"), "Expecting object"); - EXPECT_EXCEPTION(ParamStruct::makeTypes("[]"), "Expecting object"); - EXPECT_EXCEPTION(ParamStruct::makeTypes("[{}]"), "Expecting object"); - EXPECT_EQ(ParamStruct::makeTypes("{}").size(), 0ul); - EXPECT_EXCEPTION(ParamStruct::makeTypes("{\"a\": 0}"), "Expecting array"); - EXPECT_EXCEPTION(ParamStruct::makeTypes(R"({"name": 0})"), "Expecting array"); - // order does not matter - EXPECT_EQ(ParamStruct::makeTypes(R"({"Mail": [{"name": "from", "type": "Person"}, {"name": "to", "type": "Person"}, {"name": "contents", "type": "string"}], "Person": [{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}]})").size(), 2ul); - } -} - -TEST(EthereumAbiStruct, ParamFactoryMakeType) { - { - std::shared_ptr t = ParamStruct::makeType("Person", R"([{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}])"); - EXPECT_NE(t.get(), nullptr); - EXPECT_EQ(t->getType(), "Person"); - ASSERT_EQ(t->getCount(), 2ul); - ASSERT_EQ(t->encodeType(), "Person(string name,address wallet)"); - } - { // edge cases - EXPECT_EXCEPTION(ParamStruct::makeType("", ""), "Missing type name"); - EXPECT_EXCEPTION(ParamStruct::makeType("Person", "NOT_A_JSON"), "Could not parse type Json"); - EXPECT_EXCEPTION(ParamStruct::makeType("Person", "+/{\\"), "Could not parse type Json"); - EXPECT_EXCEPTION(ParamStruct::makeType("Person", ""), "Could not parse type Json"); - EXPECT_EXCEPTION(ParamStruct::makeType("Person", "0"), "Expecting array"); - EXPECT_EXCEPTION(ParamStruct::makeType("Person", "{}"), "Expecting array"); - EXPECT_EXCEPTION(ParamStruct::makeType("Person", "[]"), "No valid params found"); - EXPECT_EXCEPTION(ParamStruct::makeType("Person", R"([{"dummy": 0}])"), "Could not process Json: "); - EXPECT_EXCEPTION(ParamStruct::makeType("Person", R"([{"name": "val"}])"), "Could not process Json: "); - EXPECT_EXCEPTION(ParamStruct::makeType("Person", R"([{"type": "val"}])"), "Could not process Json: "); - EXPECT_EXCEPTION(ParamStruct::makeType("Person", R"([{"name": "", "type": "type"}])"), "Expecting 'name' and 'type', in Person"); - EXPECT_EXCEPTION(ParamStruct::makeType("Person", R"([{"name": "name", "type": ""}])"), "Expecting 'name' and 'type', in Person"); - EXPECT_EXCEPTION(ParamStruct::makeType("Person", R"([{"name": "name", "type": "UNKNOWN_TYPE"}, {"name": "wallet", "type": "address"}])"), "Unknown type UNKNOWN_TYPE"); - EXPECT_EQ(ParamStruct::makeType("Person", R"([{"name": "name", "type": "UNKNOWN_TYPE"}, {"name": "wallet", "type": "address"}])", {}, true)->encodeType(), "Person(UNKNOWN_TYPE name,address wallet)UNKNOWN_TYPE()"); - } -} - -TEST(EthereumAbiStruct, ParamNamedMethods) { - const auto ps = std::make_shared("Hello"); - auto pn = std::make_shared("name", ps); - - EXPECT_EQ(pn->getSize(), ps->getSize()); - EXPECT_EQ(pn->isDynamic(), ps->isDynamic()); - Data encoded; - pn->encode(encoded); - EXPECT_EQ(hex(encoded), "000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000"); - size_t offset = 0; - EXPECT_EQ(pn->decode(encoded, offset), true); - EXPECT_EQ(offset, 64ul); - pn->setValueJson("World"); - EXPECT_EQ(ps->getVal(), "World"); -} - -TEST(EthereumAbiStruct, ParamSetNamed) { - const auto pn1 = std::make_shared("param1", std::make_shared("Hello")); - const auto pn2 = std::make_shared("param2", std::make_shared("World")); - auto ps = std::make_shared(std::vector>{pn1, pn2}); - EXPECT_EQ(ps->getCount(), 2ul); - EXPECT_EQ(ps->addParam(std::shared_ptr(nullptr)), -1); - EXPECT_EQ(ps->findParamByName("NO_SUCH_PARAM"), nullptr); - auto pf1 = ps->findParamByName("param2"); - ASSERT_NE(pf1.get(), nullptr); - EXPECT_EQ(pf1->getName(), "param2"); -} - -TEST(EthereumAbiStruct, ParamStructMethods) { - const auto pn1 = std::make_shared("param1", std::make_shared("Hello")); - const auto pn2 = std::make_shared("param2", std::make_shared("World")); - auto ps = std::make_shared("struct", std::vector>{pn1, pn2}); - - EXPECT_EQ(ps->getSize(), 2ul); - EXPECT_EQ(ps->isDynamic(), true); - Data encoded; - ps->encode(encoded); - EXPECT_EQ(hex(encoded), ""); - size_t offset = 0; - EXPECT_EQ(ps->decode(encoded, offset), true); - EXPECT_EQ(offset, 0ul); - EXPECT_FALSE(ps->setValueJson("dummy")); - EXPECT_EQ(ps->findParamByName("param2")->getName(), "param2"); -} - -TEST(EthereumAbiStruct, ParamHashStruct) { - { - auto p = std::make_shared(); - EXPECT_TRUE(p->setValueJson("13")); - EXPECT_EQ(hex(p->hashStruct()), "000000000000000000000000000000000000000000000000000000000000000d"); - } - { - auto p = std::make_shared(); - EXPECT_TRUE(p->setValueJson("1234")); - EXPECT_EQ(hex(p->hashStruct()), "00000000000000000000000000000000000000000000000000000000000004d2"); - } - { - auto p = std::make_shared(); - EXPECT_TRUE(p->setValueJson("1234567")); - EXPECT_EQ(hex(p->hashStruct()), "000000000000000000000000000000000000000000000000000000000012d687"); - } - { - auto p = std::make_shared(); - EXPECT_TRUE(p->setValueJson("1234567")); - EXPECT_EQ(hex(p->hashStruct()), "000000000000000000000000000000000000000000000000000000000012d687"); - } - { - auto p = std::make_shared(128); - EXPECT_TRUE(p->setValueJson("1234567890123456789")); - EXPECT_EQ(hex(p->hashStruct()), "000000000000000000000000000000000000000000000000112210f47de98115"); - } - { - auto p = std::make_shared(168); - EXPECT_TRUE(p->setValueJson("1234567890123456789")); - EXPECT_EQ(hex(p->hashStruct()), "000000000000000000000000000000000000000000000000112210f47de98115"); - } - { - auto p = std::make_shared(); - EXPECT_TRUE(p->setValueJson("1234567890123456789")); - EXPECT_EQ(hex(p->hashStruct()), "000000000000000000000000000000000000000000000000112210f47de98115"); - } - { - auto p = std::make_shared(); - EXPECT_TRUE(p->setValueJson("13")); - EXPECT_EQ(hex(p->hashStruct()), "000000000000000000000000000000000000000000000000000000000000000d"); - } - { - auto p = std::make_shared(); - EXPECT_TRUE(p->setValueJson("1234")); - EXPECT_EQ(hex(p->hashStruct()), "00000000000000000000000000000000000000000000000000000000000004d2"); - } - { - auto p = std::make_shared(); - EXPECT_TRUE(p->setValueJson("1234567")); - EXPECT_EQ(hex(p->hashStruct()), "000000000000000000000000000000000000000000000000000000000012d687"); - } - { - auto p = std::make_shared(); - EXPECT_TRUE(p->setValueJson("1234567")); - EXPECT_EQ(hex(p->hashStruct()), "000000000000000000000000000000000000000000000000000000000012d687"); - } - { - auto p = std::make_shared(128); - EXPECT_TRUE(p->setValueJson("1234567890123456789")); - EXPECT_EQ(hex(p->hashStruct()), "000000000000000000000000000000000000000000000000112210f47de98115"); - } - { - auto p = std::make_shared(168); - EXPECT_TRUE(p->setValueJson("1234567890123456789")); - EXPECT_EQ(hex(p->hashStruct()), "000000000000000000000000000000000000000000000000112210f47de98115"); - } - { - auto p = std::make_shared(); - EXPECT_TRUE(p->setValueJson("1234567890123456789")); - EXPECT_EQ(hex(p->hashStruct()), "000000000000000000000000000000000000000000000000112210f47de98115"); - } - { - auto p = std::make_shared(); - EXPECT_TRUE(p->setValueJson("true")); - EXPECT_EQ(hex(p->hashStruct()), "0000000000000000000000000000000000000000000000000000000000000001"); - } - { - auto p = std::make_shared(); - EXPECT_TRUE(p->setValueJson("ABCdefGHI")); - EXPECT_EQ(hex(p->hashStruct()), "3a2aa9c027187dbf5a2f0c980281da43e810ecbe4d32e0b5c22211882c691889"); - } - { - auto p = std::make_shared(); - EXPECT_TRUE(p->setValueJson("0123456789")); - EXPECT_EQ(hex(p->hashStruct()), "79fad56e6cf52d0c8c2c033d568fc36856ba2b556774960968d79274b0e6b944"); - EXPECT_TRUE(p->setValueJson("0xa9059cbb0000000000000000000000002e0d94754b348d208d64d52d78bcd443afa9fa520000000000000000000000000000000000000000000000000000000000000007")); - EXPECT_EQ(hex(p->hashStruct()), "a9485354dd9d340e02789cfc540c6c4a2ff5511beb414b64634a5e11c6a7168c"); - EXPECT_TRUE(p->setValueJson("0x0000000000000000000000000000000000000000000000000000000123456789")); - EXPECT_EQ(hex(p->hashStruct()), "c8243991757dc8723e4976248127e573da4a2cbfad54b776d5a7c8d92b6e2a6b"); - EXPECT_TRUE(p->setValueJson("0x00")); - EXPECT_EQ(hex(p->hashStruct()), "bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a"); - EXPECT_TRUE(p->setValueJson("0x")); - EXPECT_EQ(hex(p->hashStruct()), "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); - EXPECT_TRUE(p->setValueJson("")); - EXPECT_EQ(hex(p->hashStruct()), "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); - } - { - auto p = std::make_shared(36); - EXPECT_TRUE(p->setValueJson("0x000000000000000000000000000000000000000000000000000000000000000123456789")); - EXPECT_EQ(hex(p->hashStruct()), "3deb4663f580c622d668f2121c29c3f4dacf06e40a3a76d1dea25e90bcd63b5d"); - } - { - auto p = std::make_shared(20); - EXPECT_TRUE(p->setValueJson("0x0000000000000000000000000000000123456789")); - EXPECT_EQ(hex(p->hashStruct()), "0000000000000000000000000000000123456789000000000000000000000000"); - } - { - auto p = std::make_shared(32); - EXPECT_TRUE(p->setValueJson("0x0000000000000000000000000000000000000000000000000000000123456789")); - EXPECT_EQ(hex(p->hashStruct()), "0000000000000000000000000000000000000000000000000000000123456789"); - } - { - auto p = std::make_shared(); - EXPECT_TRUE(p->setValueJson("0x0000000000000000000000000000000123456789")); - EXPECT_EQ(hex(p->hashStruct()), "0000000000000000000000000000000000000000000000000000000123456789"); - } - { - using collection = std::vector>; - auto p = std::make_shared(collection{std::make_shared(), std::make_shared(), std::make_shared()}); - EXPECT_TRUE(p->setValueJson("[1,0,1]")); - EXPECT_EQ(hex(p->hashStruct()), "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"); - } - { - using collection = std::vector>; - auto p = std::make_shared(collection{std::make_shared(), std::make_shared(), std::make_shared()}); - EXPECT_TRUE(p->setValueJson("[13,14,15]")); - EXPECT_EQ(hex(p->hashStruct()), "000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f"); - - // Coverage - EXPECT_FALSE(p->setValueJson("NotValidJson")); - EXPECT_FALSE(p->setValueJson("{}")); - EXPECT_FALSE(p->setValueJson("[1,2,3,4]")); - EXPECT_FALSE(p->setValueJson("[1,2]")); - } - { - auto p = std::make_shared(std::make_shared()); - EXPECT_TRUE(p->setValueJson("[13,14,15]")); - EXPECT_EQ(hex(p->hashStruct()), "71494e9b6acbff3356f1292cc149101310110b6b13f835ae4665e4b00892fa83"); - } - { - auto p = std::make_shared(std::make_shared()); - EXPECT_TRUE(p->setValueJson("[\"0x0000000000000000000000000000000123456789\"]")); - EXPECT_EQ(hex(p->hashStruct()), "c8243991757dc8723e4976248127e573da4a2cbfad54b776d5a7c8d92b6e2a6b"); - } - { - auto p = std::make_shared(std::make_shared()); - EXPECT_TRUE(p->setValueJson("[true,false,true]")); - EXPECT_EQ(hex(p->hashStruct()), "5c6090c0461491a2941743bda5c3658bf1ea53bbd3edcde54e16205e18b45792"); - } -} -// clang-format on -} // namespace TW::Ethereum::tests diff --git a/tests/chains/Ethereum/AbiTests.cpp b/tests/chains/Ethereum/AbiTests.cpp deleted file mode 100644 index 14e9e971fdb..00000000000 --- a/tests/chains/Ethereum/AbiTests.cpp +++ /dev/null @@ -1,1652 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Ethereum/ABI.h" -#include "HexCoding.h" -#include "TestUtilities.h" - -#include - -namespace TW::Ethereum::ABI::tests { - -///// Parameter types - -TEST(EthereumAbi, ParamTypeNames) { - EXPECT_EQ("uint8", ParamUInt8().getType()); - EXPECT_EQ("uint16", ParamUInt16().getType()); - EXPECT_EQ("uint32", ParamUInt32().getType()); - EXPECT_EQ("uint64", ParamUInt64().getType()); - EXPECT_EQ("uint256", ParamUInt256().getType()); - EXPECT_EQ("uint168", ParamUIntN(168).getType()); - EXPECT_EQ("int8", ParamInt8().getType()); - EXPECT_EQ("int16", ParamInt16().getType()); - EXPECT_EQ("int32", ParamInt32().getType()); - EXPECT_EQ("int64", ParamInt64().getType()); - EXPECT_EQ("int256", ParamInt256().getType()); - EXPECT_EQ("int168", ParamIntN(168).getType()); - EXPECT_EQ("bool", ParamBool().getType()); - EXPECT_EQ("string", ParamString().getType()); - EXPECT_EQ("address", ParamAddress().getType()); - EXPECT_EQ("bytes", ParamByteArray().getType()); - EXPECT_EQ("bytes168", ParamByteArrayFix(168).getType()); - { - // ParamArray, non-empty - auto paramArray = ParamArray(); - paramArray.addParam(std::make_shared()); - EXPECT_EQ("bool[]", paramArray.getType()); - } - { - // ParamArray, empty with prototype - auto paramArray = ParamArray(); - paramArray.setProto(std::make_shared()); - EXPECT_EQ("bool[]", paramArray.getType()); - } - { - // ParamArray, empty, no prototype - auto paramArray = ParamArray(); - EXPECT_EQ("__empty__[]", paramArray.getType()); - } - EXPECT_EQ("()", ParamTuple().getType()); -} - -TEST(EthereumAbi, ParamBool) { - { - auto param = ParamBool(false); - EXPECT_FALSE(param.getVal()); - param.setVal(true); - EXPECT_TRUE(param.getVal()); - - EXPECT_EQ("bool", param.getType()); - EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32ul, param.getSize()); - } - { - auto param = ParamBool(false); - Data encoded; - param.encode(encoded); - EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000000", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_FALSE(param.getVal()); - } - { - auto param = ParamBool(true); - Data encoded; - param.encode(encoded); - EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000001", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_TRUE(param.getVal()); - } -} - -TEST(EthereumAbi, ParamUInt8) { - { - auto param = ParamUInt8(101); - EXPECT_EQ(101, param.getVal()); - param.setVal(1); - EXPECT_EQ(1, param.getVal()); - - EXPECT_EQ("uint8", param.getType()); - EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32ul, param.getSize()); - } - { - auto param = ParamUInt8(101); - Data encoded; - param.encode(encoded); - EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000065", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(101, param.getVal()); - } - { - auto param = ParamUInt8(1); - Data encoded; - param.encode(encoded); - EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000001", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(1, param.getVal()); - } -} - -TEST(EthereumAbi, ParamUInt16) { - { - auto param = ParamUInt16(101); - EXPECT_EQ(101, param.getVal()); - param.setVal(1234); - EXPECT_EQ(1234, param.getVal()); - - EXPECT_EQ("uint16", param.getType()); - EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32ul, param.getSize()); - } - { - auto param = ParamUInt16(101); - Data encoded; - param.encode(encoded); - EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000065", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(101, param.getVal()); - } - { - auto param = ParamUInt16(1234); - Data encoded; - param.encode(encoded); - EXPECT_EQ("00000000000000000000000000000000000000000000000000000000000004d2", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(1234, param.getVal()); - } -} - -TEST(EthereumAbi, ParamUInt32) { - { - auto param = ParamUInt32(101); - EXPECT_EQ(101ul, param.getVal()); - param.setVal(1234); - EXPECT_EQ(1234ul, param.getVal()); - - EXPECT_EQ("uint32", param.getType()); - EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32ul, param.getSize()); - } - { - auto param = ParamUInt32(101); - Data encoded; - param.encode(encoded); - EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000065", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(101ul, param.getVal()); - } - { - auto param = ParamUInt32(1234); - Data encoded; - param.encode(encoded); - EXPECT_EQ("00000000000000000000000000000000000000000000000000000000000004d2", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(1234ul, param.getVal()); - } -} - -TEST(EthereumAbi, ParamUInt64) { - { - auto param = ParamUInt64(101); - EXPECT_EQ(101ul, param.getVal()); - param.setVal(1234); - EXPECT_EQ(1234ul, param.getVal()); - - EXPECT_EQ("uint64", param.getType()); - EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32ul, param.getSize()); - } - { - auto param = ParamUInt64(101); - Data encoded; - param.encode(encoded); - EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000065", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(101ul, param.getVal()); - } - { - auto param = ParamUInt64(1234); - Data encoded; - param.encode(encoded); - EXPECT_EQ("00000000000000000000000000000000000000000000000000000000000004d2", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(1234ul, param.getVal()); - } -} - -TEST(EthereumAbi, ParamUInt256) { - const uint256_t bigInt = uint256_t("0x1234567890123456789012345678901234567890"); - { - auto param = ParamUInt256(uint256_t(101)); - EXPECT_EQ(uint256_t(101), param.getVal()); - param.setVal(uint256_t(1234)); - EXPECT_EQ(uint256_t(1234), param.getVal()); - - EXPECT_EQ("uint256", param.getType()); - EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32ul, param.getSize()); - } - { - auto param = ParamUInt256(uint256_t(101)); - Data encoded; - param.encode(encoded); - EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000065", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(uint256_t(101), param.getVal()); - } - { - auto param = ParamUInt256(uint256_t(1234)); - Data encoded; - param.encode(encoded); - EXPECT_EQ("00000000000000000000000000000000000000000000000000000000000004d2", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(uint256_t(1234), param.getVal()); - } - { - auto param = ParamUInt256(bigInt); - Data encoded; - param.encode(encoded); - EXPECT_EQ("0000000000000000000000001234567890123456789012345678901234567890", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(bigInt, param.getVal()); - } - { - auto param = ParamUInt256(uint256_t(-1)); - Data encoded; - param.encode(encoded); - EXPECT_EQ("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(uint256_t(-1), param.getVal()); - } -} - -TEST(EthereumAbi, ParamUInt80) { - { - auto param = ParamUIntN(80, 0); - EXPECT_EQ(0, param.getVal()); - param.setVal(100); - EXPECT_EQ(100, param.getVal()); - - EXPECT_EQ("uint80", param.getType()); - EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32ul, param.getSize()); - - // above number of bits, masked - param.setVal(load(Data(parse_hex("1010101010101010101010101010101010101010101010101010101010101010")))); - EXPECT_EQ(load(Data(parse_hex("00000010101010101010101010"))), param.getVal()); - } - { - auto param = ParamUIntN(80, 1); - Data encoded; - param.encode(encoded); - EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000001", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(1, param.getVal()); - } - { - auto param = ParamUIntN(80, 0x123); - Data encoded; - param.encode(encoded); - EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000123", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(0x123, param.getVal()); - } -} - -TEST(EthereumAbi, ParamInt80) { - // large negative, above number of bits, and its counterpart truncated to 80 bits - int256_t largeNeg2 = ValueEncoder::int256FromUint256(load(Data(parse_hex("ffff101010101010101010101010101010101010101010101010101010101010")))); - int256_t largeNeg1 = ValueEncoder::int256FromUint256(load(Data(parse_hex("ffffffffffffffffffffffffffffffffffffffffffff10101010101010101010")))); - { - auto param = ParamIntN(80, 0); - EXPECT_EQ(0, param.getVal()); - param.setVal(int256_t(101)); - EXPECT_EQ(int256_t(101), param.getVal()); - - EXPECT_EQ("int80", param.getType()); - EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32ul, param.getSize()); - - param.setVal(int256_t(-101)); - EXPECT_EQ(int256_t(-101), param.getVal()); - param.setVal(largeNeg1); - EXPECT_EQ(largeNeg1, param.getVal()); - // large negative, above number of bits, masked - param.setVal(largeNeg2); - EXPECT_EQ(largeNeg1, param.getVal()); - } - { - auto param = ParamIntN(80, 1); - Data encoded; - param.encode(encoded); - EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000001", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(1, param.getVal()); - } - { - auto param = ParamIntN(80, int256_t(-1234)); - Data encoded; - param.encode(encoded); - EXPECT_EQ("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb2e", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(int256_t(-1234), param.getVal()); - } - { - auto param = ParamIntN(80, largeNeg1); - Data encoded; - param.encode(encoded); - EXPECT_EQ("ffffffffffffffffffffffffffffffffffffffffffff10101010101010101010", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(int256_t(largeNeg1), param.getVal()); - } - { - auto param = ParamIntN(80, largeNeg2); - Data encoded; - param.encode(encoded); - EXPECT_EQ("ffffffffffffffffffffffffffffffffffffffffffff10101010101010101010", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(int256_t(largeNeg1), param.getVal()); - } -} - -TEST(EthereumAbi, ParamString) { - const std::string helloStr = "Hello World! Hello World! Hello World!"; - { - auto param = ParamString(helloStr); - EXPECT_EQ(helloStr, param.getVal()); - - EXPECT_EQ("string", param.getType()); - EXPECT_TRUE(param.isDynamic()); - EXPECT_EQ(3 * 32ul, param.getSize()); - } - { - auto param = ParamString(helloStr); - Data encoded; - param.encode(encoded); - EXPECT_EQ( - "000000000000000000000000000000000000000000000000000000000000002c" - "48656c6c6f20576f726c64212020202048656c6c6f20576f726c642120202020" - "48656c6c6f20576f726c64210000000000000000000000000000000000000000", - hex(encoded)); - EXPECT_EQ(3 * 32ul, encoded.size()); - EXPECT_EQ(3 * 32ul, param.getSize()); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(helloStr, param.getVal()); - } -} - -TEST(EthereumAbi, ParamAddress) { - std::string val1Str("f784682c82526e245f50975190ef0fff4e4fc077"); - Data val1(parse_hex(val1Str)); - { - auto param = ParamAddress(val1); - EXPECT_EQ(val1, param.getData()); - - EXPECT_EQ("address", param.getType()); - EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32ul, param.getSize()); - } - { - auto param = ParamAddress(val1); - Data encoded; - param.encode(encoded); - EXPECT_EQ("000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077", hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(val1, param.getData()); - } - { - auto param = ParamAddress(parse_hex("0000000000000000000000000000000000000012")); - EXPECT_EQ("0000000000000000000000000000000000000012", hex(param.getData())); - Data encoded; - param.encode(encoded); - EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000012", hex(encoded)); - } - { - auto param = ParamAddress(parse_hex("4300000000000000000000000000000000000000")); - EXPECT_EQ("4300000000000000000000000000000000000000", hex(param.getData())); - Data encoded; - param.encode(encoded); - EXPECT_EQ("0000000000000000000000004300000000000000000000000000000000000000", hex(encoded)); - } -} - -TEST(EthereumAbi, ParamByteArray) { - Data data10 = parse_hex("31323334353637383930"); - { - auto param = ParamByteArray(data10); - EXPECT_EQ(data10, param.getVal()); - - EXPECT_EQ("bytes", param.getType()); - EXPECT_TRUE(param.isDynamic()); - EXPECT_EQ(2 * 32ul, param.getSize()); - } - { - auto param = ParamByteArray(data10); - Data encoded; - param.encode(encoded); - EXPECT_EQ(2 * 32ul, encoded.size()); - EXPECT_EQ(2 * 32ul, param.getSize()); - EXPECT_EQ( - "000000000000000000000000000000000000000000000000000000000000000a" - "3132333435363738393000000000000000000000000000000000000000000000", - hex(encoded)); - EXPECT_EQ(2 * 32ul, encoded.size()); - EXPECT_EQ(2 * 32ul, param.getSize()); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(data10, param.getVal()); - } -} - -TEST(EthereumAbi, ParamByteArrayFix) { - Data data10 = parse_hex("31323334353637383930"); - { - auto param = ParamByteArrayFix(10, data10); - EXPECT_EQ(data10, param.getVal()); - - EXPECT_EQ("bytes10", param.getType()); - EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32ul, param.getSize()); - } - { - auto param = ParamByteArrayFix(10, data10); - Data encoded; - param.encode(encoded); - EXPECT_EQ(32ul, encoded.size()); - EXPECT_EQ(32ul, param.getSize()); - EXPECT_EQ( - "3132333435363738393000000000000000000000000000000000000000000000", - hex(encoded)); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(data10, param.getVal()); - } -} - -TEST(EthereumAbi, ParamArrayByte) { - { - auto param = ParamArray(); - param.addParam(std::make_shared(49)); - param.addParam(std::make_shared(50)); - param.addParam(std::make_shared(51)); - EXPECT_EQ(3ul, param.getVal().size()); - EXPECT_EQ(49, (std::dynamic_pointer_cast(param.getVal()[0]))->getVal()); - EXPECT_EQ(51, (std::dynamic_pointer_cast(param.getVal()[2]))->getVal()); - - EXPECT_EQ("uint8[]", param.getType()); - EXPECT_TRUE(param.isDynamic()); - EXPECT_EQ((3 + 1) * 32ul, param.getSize()); - EXPECT_EQ(3ul, param.getCount()); - } - { - auto param = ParamArray(); - param.addParam(std::make_shared(49)); - param.addParam(std::make_shared(50)); - param.addParam(std::make_shared(51)); - Data encoded; - param.encode(encoded); - EXPECT_EQ(4 * 32ul, encoded.size()); - EXPECT_EQ(4 * 32ul, param.getSize()); - EXPECT_EQ( - "0000000000000000000000000000000000000000000000000000000000000003" - "0000000000000000000000000000000000000000000000000000000000000031" - "0000000000000000000000000000000000000000000000000000000000000032" - "0000000000000000000000000000000000000000000000000000000000000033", - hex(encoded)); - EXPECT_EQ(4 * 32ul, encoded.size()); - EXPECT_EQ(4 * 32ul, param.getSize()); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(3ul, param.getVal().size()); - EXPECT_EQ(49, (std::dynamic_pointer_cast(param.getVal()[0]))->getVal()); - EXPECT_EQ(51, (std::dynamic_pointer_cast(param.getVal()[2]))->getVal()); - } -} - -TEST(EthereumAbi, ParamArrayEmpty) { - auto param = ParamArray(); - param.setProto(std::make_shared(0)); - - EXPECT_EQ(0ul, param.getCount()); - Data encoded; - param.encode(encoded); - EXPECT_EQ(32ul, encoded.size()); - EXPECT_EQ(32ul, param.getSize()); - EXPECT_EQ( - "0000000000000000000000000000000000000000000000000000000000000000", - hex(encoded)); - EXPECT_EQ("uint8[]", param.getType()); - EXPECT_EQ("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", hex(param.hashStruct())); -} - -TEST(EthereumAbi, ParamFixedArrayAddress) { - { - auto param = ParamArrayFix({std::make_shared(Data(parse_hex("f784682c82526e245f50975190ef0fff4e4fc077")))}); - EXPECT_EQ(param.getType(), "address[1]"); - EXPECT_EQ(param.getCount(), 1ul); - EXPECT_EQ(param.getSize(), 32ul); - EXPECT_FALSE(param.isDynamic()); - Data encoded; - param.encode(encoded); - EXPECT_EQ(hex(encoded), "000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077"); - std::size_t offset{0}; - EXPECT_TRUE(param.decode(encoded, offset)); - } - { - auto param = ParamArrayFix({std::make_shared(Data(parse_hex("f784682c82526e245f50975190ef0fff4e4fc077"))), - std::make_shared(Data(parse_hex("2e00cd222cb42b616d86d037cc494e8ab7f5c9a3")))}); - EXPECT_EQ(param.getType(), "address[2]"); - EXPECT_EQ(param.getCount(), 2ul); - Data encoded; - param.encode(encoded); - EXPECT_EQ(hex(encoded), "000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc0770000000000000000000000002e00cd222cb42b616d86d037cc494e8ab7f5c9a3"); - std::size_t offset{0}; - EXPECT_TRUE(param.decode(encoded, offset)); - } - { - // nullptr - EXPECT_THROW(ParamArrayFix({nullptr}), std::runtime_error); - // Should be the same type - EXPECT_THROW( - ParamArrayFix({std::make_shared(Data(parse_hex("f784682c82526e245f50975190ef0fff4e4fc077"))), - std::make_shared("Foo")}), - std::runtime_error); - } -} - -TEST(EthereumAbi, ParamArrayAddress) { - { - auto param = ParamArray(); - param.addParam(std::make_shared(Data(parse_hex("f784682c82526e245f50975190ef0fff4e4fc077")))); - param.addParam(std::make_shared(Data(parse_hex("2e00cd222cb42b616d86d037cc494e8ab7f5c9a3")))); - EXPECT_EQ(2ul, param.getVal().size()); - - EXPECT_EQ("address[]", param.getType()); - EXPECT_TRUE(param.isDynamic()); - EXPECT_EQ((2 + 1) * 32ul, param.getSize()); - EXPECT_EQ(2ul, param.getCount()); - } - { - auto param = ParamArray(); - param.addParam(std::make_shared(Data(parse_hex("f784682c82526e245f50975190ef0fff4e4fc077")))); - param.addParam(std::make_shared(Data(parse_hex("2e00cd222cb42b616d86d037cc494e8ab7f5c9a3")))); - Data encoded; - param.encode(encoded); - EXPECT_EQ(3 * 32ul, encoded.size()); - EXPECT_EQ(3 * 32ul, param.getSize()); - EXPECT_EQ( - "0000000000000000000000000000000000000000000000000000000000000002" - "000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077" - "0000000000000000000000002e00cd222cb42b616d86d037cc494e8ab7f5c9a3", - hex(encoded)); - EXPECT_EQ(3 * 32ul, encoded.size()); - EXPECT_EQ(3 * 32ul, param.getSize()); - size_t offset = 0; - EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(2ul, param.getCount()); - EXPECT_EQ(2ul, param.getVal().size()); - EXPECT_EQ( - "2e00cd222cb42b616d86d037cc494e8ab7f5c9a3", - hex((std::dynamic_pointer_cast(param.getVal()[1]))->getData())); - } -} - -TEST(EthereumAbi, ParamArrayOfByteArray) { - auto param = ParamArray(); - param.addParam(std::make_shared(parse_hex("1011"))); - param.addParam(std::make_shared(parse_hex("102222"))); - param.addParam(std::make_shared(parse_hex("10333333"))); - EXPECT_EQ(3ul, param.getVal().size()); - - EXPECT_EQ("bytes[]", param.getType()); - EXPECT_TRUE(param.isDynamic()); - EXPECT_EQ((1 + 3 + 3 * 2) * 32ul, param.getSize()); - EXPECT_EQ(3ul, param.getCount()); -} - -TEST(EthereumAbi, ParamArrayBytesContract) { - auto param = ParamArray(); - param.addParam(std::make_shared(parse_hex("0xd5fa2b00e71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f00000000000000000000000047331175b23c2f067204b506ca1501c26731c990"))); - param.addParam(std::make_shared(parse_hex("0x304e6adee71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000"))); - param.addParam(std::make_shared(parse_hex("0x8b95dd71e71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f000000000000000000000000000000000000000000000000000000000000003c0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000001447331175b23c2f067204b506ca1501c26731c990000000000000000000000000"))); - param.addParam(std::make_shared(parse_hex("0x8b95dd71e71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f00000000000000000000000000000000000000000000000000000000000002ca00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000014d30f834b53d8f7e851e87b90ffa65757a35b8505000000000000000000000000"))); - EXPECT_EQ(4ul, param.getCount()); - EXPECT_EQ(4ul, param.getVal().size()); - - EXPECT_EQ("bytes[]", param.getType()); - EXPECT_TRUE(param.isDynamic()); - - Data encoded; - param.encode(encoded); - EXPECT_EQ(896ul, encoded.size()); - - EXPECT_EQ(896ul, param.getSize()); -} - -TEST(EthereumAbi, ParamTupleStatic) { - { - auto param = ParamTuple(); - param.addParam(std::make_shared(true)); - param.addParam(std::make_shared(123)); - EXPECT_EQ("(bool,uint64)", param.getType()); - EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(2ul, param.getCount()); - EXPECT_EQ(64ul, param.getSize()); - Data encoded; - param.encode(encoded); - EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000007b", hex(encoded)); - { // decode - size_t offset = 0; - auto param2 = ParamTuple(); - param2.addParam(std::make_shared()); - param2.addParam(std::make_shared()); - EXPECT_TRUE(param2.decode(encoded, offset)); - EXPECT_EQ(2ul, param2.getCount()); - Data encoded2; - param2.encode(encoded2); - EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000007b", hex(encoded)); - } - } - { - auto param = ParamTuple(); - param.addParam(std::make_shared(456)); - param.addParam(std::make_shared(parse_hex("000102030405060708090a0b0c0d0e0f10111213"))); - EXPECT_EQ("(uint64,address)", param.getType()); - EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(2ul, param.getCount()); - EXPECT_EQ(64ul, param.getSize()); - Data encoded; - param.encode(encoded); - EXPECT_EQ("00000000000000000000000000000000000000000000000000000000000001c8000000000000000000000000000102030405060708090a0b0c0d0e0f10111213", hex(encoded)); - } -} - -TEST(EthereumAbi, ParamTupleDynamic) { - { - auto param = ParamTuple(); - param.addParam(std::make_shared("Don't trust, verify!")); - param.addParam(std::make_shared(13)); - param.addParam(std::make_shared(parse_hex("00010203040506070809"))); - EXPECT_EQ("(string,uint64,bytes)", param.getType()); - EXPECT_TRUE(param.isDynamic()); - EXPECT_EQ(3ul, param.getCount()); - EXPECT_EQ(7 * 32ul, param.getSize()); - Data encoded; - param.encode(encoded); - EXPECT_EQ( - "0000000000000000000000000000000000000000000000000000000000000060" // offet 3*32 - "000000000000000000000000000000000000000000000000000000000000000d" - "00000000000000000000000000000000000000000000000000000000000000a0" // offet 5*32 - "0000000000000000000000000000000000000000000000000000000000000014" // len - "446f6e27742074727573742c2076657269667921000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000a" // len - "0001020304050607080900000000000000000000000000000000000000000000", - hex(encoded)); - } -} - -///// Direct encode & decode - -TEST(EthereumAbi, EncodeVectorByte10) { - auto p = ParamByteArrayFix(10, parse_hex("31323334353637383930")); - EXPECT_EQ("bytes10", p.getType()); - Data encoded; - p.encode(encoded); - EXPECT_EQ("3132333435363738393000000000000000000000000000000000000000000000", hex(encoded)); -} - -TEST(EthereumAbi, EncodeVectorByte) { - auto p = ParamByteArray(parse_hex("31323334353637383930")); - EXPECT_EQ("bytes", p.getType()); - Data encoded; - p.encode(encoded); - EXPECT_EQ( - "000000000000000000000000000000000000000000000000000000000000000a" - "3132333435363738393000000000000000000000000000000000000000000000", - hex(encoded)); -} - -TEST(EthereumAbi, EncodeArrayByte) { - auto p = ParamArray(std::vector>{ - std::make_shared(parse_hex("1011")), - std::make_shared(parse_hex("102222"))}); - EXPECT_EQ("bytes[]", p.getType()); - Data encoded; - p.encode(encoded); - EXPECT_EQ( - "0000000000000000000000000000000000000000000000000000000000000002" - "0000000000000000000000000000000000000000000000000000000000000040" - "0000000000000000000000000000000000000000000000000000000000000080" - "0000000000000000000000000000000000000000000000000000000000000002" - "1011000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000003" - "1022220000000000000000000000000000000000000000000000000000000000", - hex(encoded)); - EXPECT_EQ((1 + 2 + 2 * 2) * 32ul, encoded.size()); - EXPECT_EQ((1 + 2 + 2 * 2) * 32ul, p.getSize()); -} - -TEST(EthereumAbi, DecodeUInt) { - Data encoded = parse_hex("000000000000000000000000000000000000000000000000000000000000002a"); - size_t offset = 0; - uint256_t decoded; - bool res = ParamNumberType::decodeNumber(encoded, decoded, offset); - EXPECT_TRUE(res); - EXPECT_EQ(uint256_t(42), decoded); - EXPECT_EQ(32ul, offset); -} - -TEST(EthereumAbi, DecodeUInt8) { - Data encoded = parse_hex("0000000000000000000000000000000000000000000000000000000000000018"); - size_t offset = 0; - uint8_t decoded; - bool res = ParamNumberType::decodeNumber(encoded, decoded, offset); - EXPECT_TRUE(res); - EXPECT_EQ(24, decoded); - EXPECT_EQ(32ul, offset); -} - -TEST(EthereumAbi, DecodeUInt8WithOffset) { - Data encoded = parse_hex("abcdef0000000000000000000000000000000000000000000000000000000000000018"); - size_t offset = 3; - uint8_t decoded; - bool res = ParamNumberType::decodeNumber(encoded, decoded, offset); - EXPECT_TRUE(res); - EXPECT_EQ(24, decoded); - EXPECT_EQ(3 + 32ul, offset); -} - -TEST(EthereumAbi, DecodeUIntWithOffset) { - Data encoded = parse_hex("abcdef000000000000000000000000000000000000000000000000000000000000002a"); - size_t offset = 3; - uint256_t decoded; - bool res = decode(encoded, decoded, offset); - EXPECT_TRUE(res); - EXPECT_EQ(uint256_t(42), decoded); - EXPECT_EQ(3 + 32ul, offset); -} - -TEST(EthereumAbi, DecodeUIntErrorTooShort) { - Data encoded = parse_hex("000000000000000000000000000000000000000000000000002a"); - size_t offset = 0; - uint256_t decoded; - bool res = decode(encoded, decoded, offset); - EXPECT_FALSE(res); - EXPECT_EQ(uint256_t(0), decoded); - EXPECT_EQ(0ul, offset); -} - -TEST(EthereumAbi, DecodeArrayUint) { - Data encoded; - append(encoded, parse_hex("000000000000000000000000000000000000000000000000000000000000000a")); - append(encoded, parse_hex("3132333435363738393000000000000000000000000000000000000000000000")); - size_t offset = 0; - std::vector decoded; - bool res = ParamByteArray::decodeBytes(encoded, decoded, offset); - EXPECT_TRUE(res); - EXPECT_EQ(10ul, decoded.size()); - if (decoded.size() >= 2) { - EXPECT_EQ(49u, decoded[0]); - EXPECT_EQ(50u, decoded[1]); - } - EXPECT_EQ(32 + 32ul, offset); -} - -TEST(EthereumAbi, DecodeArrayTooShort) { - Data encoded; - append(encoded, parse_hex("000000000000000000000000000000000000000000000000000000000000000a")); - append(encoded, parse_hex("313233343536373839")); - size_t offset = 0; - std::vector decoded; - bool res = ParamByteArray::decodeBytes(encoded, decoded, offset); - EXPECT_FALSE(res); -} - -TEST(EthereumAbi, DecodeArrayInvalidLen) { - Data encoded = parse_hex("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"); - size_t offset = 0; - std::vector decoded; - bool res = ParamByteArray::decodeBytes(encoded, decoded, offset); - EXPECT_FALSE(res); -} - -TEST(EthereumAbi, DecodeByteArray) { - Data encoded; - append(encoded, parse_hex("000000000000000000000000000000000000000000000000000000000000000a")); - append(encoded, parse_hex("3132333435363738393000000000000000000000000000000000000000000000")); - size_t offset = 0; - Data decoded; - bool res = ParamByteArray::decodeBytes(encoded, decoded, offset); - EXPECT_TRUE(res); - EXPECT_EQ("31323334353637383930", hex(decoded)); - EXPECT_EQ(32 + 32ul, offset); -} - -TEST(EthereumAbi, DecodeByteArray10) { - Data encoded = parse_hex("3132333435363738393000000000000000000000000000000000000000000000"); - size_t offset = 0; - Data decoded; - bool res = ParamByteArrayFix::decodeBytesFix(encoded, 10, decoded, offset); - EXPECT_TRUE(res); - EXPECT_EQ(10ul, decoded.size()); - EXPECT_EQ(49u, decoded[0]); - EXPECT_EQ(50u, decoded[1]); - EXPECT_EQ(32ul, offset); -} - -TEST(EthereumAbi, DecodeArrayOfByteArray) { - Data encoded = parse_hex( - "0000000000000000000000000000000000000000000000000000000000000002" - "0000000000000000000000000000000000000000000000000000000000000040" - "0000000000000000000000000000000000000000000000000000000000000080" - "0000000000000000000000000000000000000000000000000000000000000002" - "1011000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000003" - "1022200000000000000000000000000000000000000000000000000000000000"); - size_t offset = 0; - Data decoded; - auto param = ParamArray(); - param.addParam(std::make_shared(Data())); - param.addParam(std::make_shared(Data())); - bool res = param.decode(encoded, offset); - EXPECT_TRUE(res); - EXPECT_EQ(2ul, param.getCount()); - // `size_of(array.len())` + `size_of(byte_array[0].len())` + `size_of(byte_array[1].len()) - EXPECT_EQ(3 * 32ul, offset); - EXPECT_EQ(2ul, param.getVal().size()); -} - -///// Parameters encode & decode - -TEST(EthereumAbi, EncodeParamsSimple) { - auto p = Parameters(std::vector>{ - std::make_shared(16u), - std::make_shared(17u), - std::make_shared(true)}); - EXPECT_EQ("(uint256,uint256,bool)", p.getType()); - Data encoded; - p.encode(encoded); - - EXPECT_EQ(3 * 32ul, encoded.size()); - EXPECT_EQ(3 * 32ul, p.getSize()); - EXPECT_EQ( - "0000000000000000000000000000000000000000000000000000000000000010" - "0000000000000000000000000000000000000000000000000000000000000011" - "0000000000000000000000000000000000000000000000000000000000000001", - hex(encoded)); -} - -TEST(EthereumAbi, EncodeParamsMixed) { - auto p = Parameters(std::vector>{ - std::make_shared(69u), - std::make_shared(std::vector>{ - std::make_shared(1), - std::make_shared(2), - std::make_shared(3)}), - std::make_shared(true), - std::make_shared("Hello"), - std::make_shared(Data{0x64, 0x61, 0x76, 0x65})}); - EXPECT_EQ("(uint256,uint256[],bool,string,bytes)", p.getType()); - Data encoded; - p.encode(encoded); - - EXPECT_EQ(13 * 32ul, encoded.size()); - EXPECT_EQ(13 * 32ul, p.getSize()); - EXPECT_EQ( - "0000000000000000000000000000000000000000000000000000000000000045" - "00000000000000000000000000000000000000000000000000000000000000a0" - "0000000000000000000000000000000000000000000000000000000000000001" - "0000000000000000000000000000000000000000000000000000000000000120" - "0000000000000000000000000000000000000000000000000000000000000160" - "0000000000000000000000000000000000000000000000000000000000000003" - "0000000000000000000000000000000000000000000000000000000000000001" - "0000000000000000000000000000000000000000000000000000000000000002" - "0000000000000000000000000000000000000000000000000000000000000003" - "0000000000000000000000000000000000000000000000000000000000000005" - "48656c6c6f000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000004" - "6461766500000000000000000000000000000000000000000000000000000000", - hex(encoded)); - /* - * explained: - * uint256 69u - * idx of dynamic vector, 5*32 - * true - * index of dynamic string, 9*32 - * index of dynamic data, 11*32 - * vector size 3 - * vector val1 - * vector val2 - * vector val3 - * string size 5 - * string - * data size 4 - * data - */ -} - -TEST(EthereumAbi, DecodeParamsSimple) { - Data encoded; - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000010")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000011")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000001")); - auto p = Parameters(std::vector>{ - std::make_shared(0), - std::make_shared(0), - std::make_shared(false)}); - EXPECT_EQ("(uint256,uint256,bool)", p.getType()); - size_t offset = 0; - bool res = p.decode(encoded, offset); - EXPECT_TRUE(res); - EXPECT_EQ(uint256_t(16u), (std::dynamic_pointer_cast(p.getParam(0)))->getVal()); - EXPECT_EQ(uint256_t(17u), (std::dynamic_pointer_cast(p.getParam(1)))->getVal()); - EXPECT_EQ(true, (std::dynamic_pointer_cast(p.getParam(2)))->getVal()); - EXPECT_EQ(3 * 32ul, offset); -} - -TEST(EthereumAbi, DecodeParamsMixed) { - Data encoded; - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000045")); - append(encoded, parse_hex("00000000000000000000000000000000000000000000000000000000000000a0")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000001")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000120")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000160")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000003")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000001")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000002")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000003")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000005")); - append(encoded, parse_hex("48656c6c6f000000000000000000000000000000000000000000000000000000")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000004")); - append(encoded, parse_hex("6461766500000000000000000000000000000000000000000000000000000000")); - // clang-format off - auto p = Parameters(std::vector>{ - std::make_shared(), - std::make_shared(std::vector>{ - std::make_shared(), - std::make_shared(), - std::make_shared() - }), - std::make_shared(), - std::make_shared(), - std::make_shared() - }); - // clang-format on - EXPECT_EQ("(uint256,uint256[],bool,string,bytes)", p.getType()); - size_t offset = 0; - bool res = p.decode(encoded, offset); - EXPECT_TRUE(res); - EXPECT_EQ(uint256_t(69u), (std::dynamic_pointer_cast(p.getParam(0)))->getVal()); - EXPECT_EQ(3ul, (std::dynamic_pointer_cast(p.getParam(1)))->getCount()); - EXPECT_EQ(1, (std::dynamic_pointer_cast((std::dynamic_pointer_cast(p.getParam(1)))->getParam(0)))->getVal()); - EXPECT_EQ(3, (std::dynamic_pointer_cast((std::dynamic_pointer_cast(p.getParam(1)))->getParam(2)))->getVal()); - EXPECT_EQ(true, (std::dynamic_pointer_cast(p.getParam(2)))->getVal()); - EXPECT_EQ("Hello", (std::dynamic_pointer_cast(p.getParam(3)))->getVal()); - // Offset of `ParamUInt256`, `ParamBool` static elements and sizes of `ParamArray`, `ParamBool`, `ParamByteArray`. - EXPECT_EQ(5 * 32ul, offset); -} - -///// Function encode & decode - -TEST(EthereumAbi, EncodeSignature) { - // clang-format off - auto func = Function("baz", std::vector>{ - std::make_shared(69u), - std::make_shared(true) - }); - // clang-format on - EXPECT_EQ("baz(uint256,bool)", func.getType()); - Data encoded; - func.encode(encoded); - - EXPECT_EQ(encoded.size(), 32 * 2 + 4ul); - EXPECT_EQ(hex(data(encoded.begin(), encoded.begin() + 4)), "72ed38b6"); - EXPECT_EQ(hex(data(encoded.begin() + 4, encoded.begin() + 36)), "0000000000000000000000000000000000000000000000000000000000000045"); - EXPECT_EQ(hex(data(encoded.begin() + 36, encoded.begin() + 68)), "0000000000000000000000000000000000000000000000000000000000000001"); -} - -TEST(EthereumAbi, EncodeFunctionWithDynamicArgumentsCase1) { - // clang-format off - auto func = Function("sam", std::vector>{ - std::make_shared(Data{0x64, 0x61, 0x76, 0x65}), - std::make_shared(true), - std::make_shared(std::vector>{ - std::make_shared(1), - std::make_shared(2), - std::make_shared(3) - }) - }); - // clang-format on - EXPECT_EQ("sam(bytes,bool,uint256[])", func.getType()); - Data encoded; - func.encode(encoded); - - EXPECT_EQ(encoded.size(), 32 * 9 + 4ul); - EXPECT_EQ(hex(data(encoded.begin() + 0, encoded.begin() + 4)), "a5643bf2"); - EXPECT_EQ(hex(data(encoded.begin() + 4, encoded.begin() + 36)), "0000000000000000000000000000000000000000000000000000000000000060"); - EXPECT_EQ(hex(data(encoded.begin() + 36, encoded.begin() + 68)), "0000000000000000000000000000000000000000000000000000000000000001"); - EXPECT_EQ(hex(data(encoded.begin() + 68, encoded.begin() + 100)), "00000000000000000000000000000000000000000000000000000000000000a0"); - EXPECT_EQ(hex(data(encoded.begin() + 100, encoded.begin() + 132)), "0000000000000000000000000000000000000000000000000000000000000004"); - EXPECT_EQ(hex(data(encoded.begin() + 132, encoded.begin() + 164)), "6461766500000000000000000000000000000000000000000000000000000000"); - EXPECT_EQ(hex(data(encoded.begin() + 164, encoded.begin() + 196)), "0000000000000000000000000000000000000000000000000000000000000003"); - EXPECT_EQ(hex(data(encoded.begin() + 196, encoded.begin() + 228)), "0000000000000000000000000000000000000000000000000000000000000001"); - EXPECT_EQ(hex(data(encoded.begin() + 228, encoded.begin() + 260)), "0000000000000000000000000000000000000000000000000000000000000002"); - EXPECT_EQ(hex(data(encoded.begin() + 260, encoded.begin() + 292)), "0000000000000000000000000000000000000000000000000000000000000003"); -} - -TEST(EthereumAbi, EncodeFunctionWithDynamicArgumentsCase2) { - // clang-format off - auto func = Function("f", std::vector>{ - std::make_shared(0x123), - std::make_shared(std::vector>{ - std::make_shared(0x456), - std::make_shared(0x789)}), - std::make_shared(10, std::vector{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30}), - std::make_shared("Hello, world!") - }); - // clamp-format on - EXPECT_EQ("f(uint256,uint32[],bytes10,string)", func.getType()); - Data encoded; - func.encode(encoded); - - EXPECT_EQ(encoded.size(), 32 * 9 + 4ul); - EXPECT_EQ(hex(data(encoded.begin() + 0, encoded.begin() + 4)), "47b941bf"); - EXPECT_EQ(hex(data(encoded.begin() + 4, encoded.begin() + 36)), "0000000000000000000000000000000000000000000000000000000000000123"); - EXPECT_EQ(hex(data(encoded.begin() + 36, encoded.begin() + 68)), "0000000000000000000000000000000000000000000000000000000000000080"); - EXPECT_EQ(hex(data(encoded.begin() + 68, encoded.begin() + 100)), "3132333435363738393000000000000000000000000000000000000000000000"); - EXPECT_EQ(hex(data(encoded.begin() + 100, encoded.begin() + 132)), "00000000000000000000000000000000000000000000000000000000000000e0"); - EXPECT_EQ(hex(data(encoded.begin() + 132, encoded.begin() + 164)), "0000000000000000000000000000000000000000000000000000000000000002"); - EXPECT_EQ(hex(data(encoded.begin() + 164, encoded.begin() + 196)), "0000000000000000000000000000000000000000000000000000000000000456"); - EXPECT_EQ(hex(data(encoded.begin() + 196, encoded.begin() + 228)), "0000000000000000000000000000000000000000000000000000000000000789"); - EXPECT_EQ(hex(data(encoded.begin() + 228, encoded.begin() + 260)), "000000000000000000000000000000000000000000000000000000000000000d"); - EXPECT_EQ(hex(data(encoded.begin() + 260, encoded.begin() + 292)), "48656c6c6f2c20776f726c642100000000000000000000000000000000000000"); -} - -TEST(EthereumAbi, DecodeFunctionOutputCase1) { - Data encoded; - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000045")); - - // clang-format off - auto func = Function("readout", std::vector>{ - std::make_shared(parse_hex("f784682c82526e245f50975190ef0fff4e4fc077")), - std::make_shared(1000) - }); - // clang-format on - func.addOutParam(std::make_shared()); - EXPECT_EQ("readout(address,uint64)", func.getType()); - - // original output value - std::shared_ptr param; - EXPECT_TRUE(func.getOutParam(0, param)); - EXPECT_EQ(0ul, (std::dynamic_pointer_cast(param))->getVal()); - - size_t offset = 0; - bool res = func.decodeOutput(encoded, offset); - EXPECT_TRUE(res); - EXPECT_EQ(32ul, offset); - - // new output value - EXPECT_EQ(0x45ul, (std::dynamic_pointer_cast(param))->getVal()); -} - -TEST(EthereumAbi, DecodeFunctionOutputCase2) { - // clang-format off - auto func = Function("getAmountsOut", std::vector>{ - std::make_shared(100), - std::make_shared(std::make_shared(parse_hex("000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077"))) - }); - // clang-format on - func.addOutParam(std::make_shared(std::vector>{ - std::make_shared(66), - std::make_shared(67)})); - EXPECT_EQ("getAmountsOut(uint256,address[])", func.getType()); - - Data encoded; - append(encoded, parse_hex( - "0000000000000000000000000000000000000000000000000000000000000020" - "0000000000000000000000000000000000000000000000000000000000000002" - "0000000000000000000000000000000000000000000000000000000000000004" - "0000000000000000000000000000000000000000000000000000000000000005")); - size_t offset = 0; - bool res = func.decodeOutput(encoded, offset); - EXPECT_TRUE(res); - // The offset should only be shifted by the size of the array. - EXPECT_EQ(32ul, offset); - - // new output values - std::shared_ptr param; - EXPECT_TRUE(func.getOutParam(0, param)); - EXPECT_EQ(2ul, (std::dynamic_pointer_cast(param))->getCount()); - EXPECT_EQ(4, (std::dynamic_pointer_cast((std::dynamic_pointer_cast(param))->getParam(0)))->getVal()); - EXPECT_EQ(5, (std::dynamic_pointer_cast((std::dynamic_pointer_cast(param))->getParam(1)))->getVal()); -} - -TEST(EthereumAbi, DecodeInputSignature) { - Data encoded; - append(encoded, parse_hex("72ed38b6")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000045")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000001")); - // clang-format off - auto func = Function("baz", std::vector>{ - std::make_shared(), std::make_shared() - }); - // clang-format on - EXPECT_EQ("baz(uint256,bool)", func.getType()); - size_t offset = 0; - bool res = func.decodeInput(encoded, offset); - EXPECT_TRUE(res); - std::shared_ptr param; - EXPECT_TRUE(func.getInParam(0, param)); - EXPECT_EQ(69u, (std::dynamic_pointer_cast(param))->getVal()); - EXPECT_TRUE(func.getInParam(1, param)); - EXPECT_EQ(true, (std::dynamic_pointer_cast(param))->getVal()); - EXPECT_EQ(4 + 2 * 32ul, offset); -} - -TEST(EthereumAbi, DecodeFunctionInputWithDynamicArgumentsCase1) { - Data encoded; - append(encoded, parse_hex("a5643bf2")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000060")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000001")); - append(encoded, parse_hex("00000000000000000000000000000000000000000000000000000000000000a0")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000004")); - append(encoded, parse_hex("6461766500000000000000000000000000000000000000000000000000000000")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000003")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000001")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000002")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000003")); - - // clang-format off - auto func = Function("sam", std::vector>{ - std::make_shared(Data{0x64, 0x61, 0x76, 0x65}), - std::make_shared(true), - std::make_shared(std::vector>{ - std::make_shared(1), - std::make_shared(2), - std::make_shared(3) - }) - }); - // clang-format on - EXPECT_EQ("sam(bytes,bool,uint256[])", func.getType()); - - size_t offset = 0; - bool res = func.decodeInput(encoded, offset); - EXPECT_TRUE(res); - std::shared_ptr param; - EXPECT_TRUE(func.getInParam(0, param)); - EXPECT_EQ(4ul, (std::dynamic_pointer_cast(param))->getCount()); - EXPECT_EQ(0x64, (std::dynamic_pointer_cast(param))->getVal()[0]); - EXPECT_EQ(0x65, (std::dynamic_pointer_cast(param))->getVal()[3]); - EXPECT_TRUE(func.getInParam(1, param)); - EXPECT_EQ(true, (std::dynamic_pointer_cast(param))->getVal()); - EXPECT_TRUE(func.getInParam(2, param)); - EXPECT_EQ(3ul, (std::dynamic_pointer_cast(param))->getCount()); - EXPECT_EQ(uint256_t(1), (std::dynamic_pointer_cast((std::dynamic_pointer_cast(param))->getVal()[0]))->getVal()); - EXPECT_EQ(uint256_t(3), (std::dynamic_pointer_cast((std::dynamic_pointer_cast(param))->getVal()[2]))->getVal()); - // Offset of `ParamBool` static element and sizes of `ParamByteArray`, `ParamArray` arrays. - EXPECT_EQ(4 + 3 * 32ul, offset); -} - -TEST(EthereumAbi, DecodeFunctionInputWithDynamicArgumentsCase2) { - Data encoded; - append(encoded, parse_hex("47b941bf")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000123")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000080")); - append(encoded, parse_hex("3132333435363738393000000000000000000000000000000000000000000000")); - append(encoded, parse_hex("00000000000000000000000000000000000000000000000000000000000000e0")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000002")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000456")); - append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000789")); - append(encoded, parse_hex("000000000000000000000000000000000000000000000000000000000000000d")); - append(encoded, parse_hex("48656c6c6f2c20776f726c642100000000000000000000000000000000000000")); - - // clang-format off - auto func = Function("f", std::vector>{ - std::make_shared(0x123), - std::make_shared(std::vector>{ - std::make_shared(0x456), - std::make_shared(0x789)}), - std::make_shared(10, std::vector{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30}), - std::make_shared("Hello, world!") - }); - // clang-format on - EXPECT_EQ("f(uint256,uint32[],bytes10,string)", func.getType()); - - size_t offset = 0; - bool res = func.decodeInput(encoded, offset); - EXPECT_TRUE(res); - std::shared_ptr param; - EXPECT_TRUE(func.getInParam(0, param)); - EXPECT_EQ(uint256_t(0x123), (std::dynamic_pointer_cast(param))->getVal()); - EXPECT_TRUE(func.getInParam(1, param)); - EXPECT_EQ(2ul, (std::dynamic_pointer_cast(param))->getCount()); - EXPECT_EQ(0x456ul, (std::dynamic_pointer_cast((std::dynamic_pointer_cast(param))->getVal()[0]))->getVal()); - EXPECT_EQ(0x789ul, (std::dynamic_pointer_cast((std::dynamic_pointer_cast(param))->getVal()[1]))->getVal()); - EXPECT_TRUE(func.getInParam(2, param)); - EXPECT_EQ(10ul, (std::dynamic_pointer_cast(param))->getCount()); - EXPECT_EQ("31323334353637383930", hex((std::dynamic_pointer_cast(param))->getVal())); - EXPECT_TRUE(func.getInParam(3, param)); - EXPECT_EQ(std::string("Hello, world!"), (std::dynamic_pointer_cast(param))->getVal()); - // Offset of `ParamUInt256` static element and sizes of `ParamArray`, `ParamByteArrayFix`, `ParamString` dynamic arrays. - EXPECT_EQ(4 + 4 * 32ul, offset); -} - -TEST(EthereumAbi, DecodeFunctionContractMulticall) { - Data encoded = parse_hex( - "0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000" - "000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000" - "000000000000000000008000000000000000000000000000000000000000000000000000000000000001000000" - "0000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000" - "000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000" - "00000044d5fa2b00e71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f0000000000" - "0000000000000047331175b23c2f067204b506ca1501c26731c990000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000064304e6a" - "dee71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f000000000000000000000000" - "000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000a48b95dd71e71cd96d4ba1c4b512b0c5bee30d2b6becf61e" - "574c32a17a67156fa9ed3c4c6f000000000000000000000000000000000000000000000000000000000000003c" - "000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000" - "0000000000000000000000000000000000001447331175b23c2f067204b506ca1501c26731c990000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000a48b95dd71e71cd96d4ba1c4b512b0c5bee30d2b6becf61e57" - "4c32a17a67156fa9ed3c4c6f00000000000000000000000000000000000000000000000000000000000002ca00" - "000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000" - "000000000000000000000000000000000014d30f834b53d8f7e851e87b90ffa65757a35b850500000000000000" - "000000000000000000000000000000000000000000000000000000000000000000"); - ASSERT_EQ(4 + 928ul, encoded.size()); - - // clang-format off - auto func = Function("multicall", std::vector>{ - std::make_shared(std::vector>{ - std::make_shared(Data()), - std::make_shared(Data()), - std::make_shared(Data()), - std::make_shared(Data()) - }), - }); - // clang-format on - EXPECT_EQ("multicall(bytes[])", func.getType()); - - size_t offset = 0; - bool res = func.decodeInput(encoded, offset); - EXPECT_TRUE(res); - // The offset should only be shifted by the size of the array. - EXPECT_EQ(4 + 32ul, offset); -} - -TEST(EthereumAbi, ParamFactoryMake) { - { - // test for UInt256: ParamUInt256 and ParamUIntN(256), both have type "uint256", factory produces the more specific ParamUInt256 - // there was confusion about this - std::shared_ptr p = ParamFactory::make("uint256"); - EXPECT_EQ("uint256", p->getType()); - EXPECT_TRUE(nullptr != std::dynamic_pointer_cast(p).get()); - EXPECT_EQ(nullptr, std::dynamic_pointer_cast(p).get()); - } - { - // int32 is ParamInt32, not ParamIntN - std::shared_ptr p = ParamFactory::make("int32"); - EXPECT_EQ("int32", p->getType()); - EXPECT_TRUE(nullptr != std::dynamic_pointer_cast(p).get()); - EXPECT_EQ(nullptr, std::dynamic_pointer_cast(p).get()); - } - { - // int168 is ParamIntN - std::shared_ptr p = ParamFactory::make("int168"); - EXPECT_EQ("int168", p->getType()); - EXPECT_TRUE(nullptr != std::dynamic_pointer_cast(p).get()); - } - { - // uint is uint256 - std::shared_ptr p = ParamFactory::make("uint"); - EXPECT_EQ("uint256", p->getType()); - EXPECT_TRUE(nullptr != std::dynamic_pointer_cast(p).get()); - } - { - // int is int256 - std::shared_ptr p = ParamFactory::make("int"); - EXPECT_EQ("int256", p->getType()); - EXPECT_TRUE(nullptr != std::dynamic_pointer_cast(p).get()); - } - { - std::shared_ptr p = ParamFactory::make("uint8[]"); - EXPECT_EQ("uint8[]", p->getType()); - EXPECT_TRUE(nullptr != std::dynamic_pointer_cast(p).get()); - auto elemParam = std::dynamic_pointer_cast(p)->getParam(0); - EXPECT_TRUE(nullptr != elemParam.get()); - } - { - std::shared_ptr p = ParamFactory::make("address[]"); - EXPECT_EQ("address[]", p->getType()); - EXPECT_TRUE(nullptr != std::dynamic_pointer_cast(p).get()); - auto elemParam = std::dynamic_pointer_cast(p)->getParam(0); - EXPECT_TRUE(nullptr != elemParam.get()); - } - { - std::shared_ptr p = ParamFactory::make("bytes[]"); - EXPECT_EQ("bytes[]", p->getType()); - EXPECT_TRUE(nullptr != std::dynamic_pointer_cast(p).get()); - auto elemParam = std::dynamic_pointer_cast(p)->getParam(0); - EXPECT_TRUE(nullptr != elemParam.get()); - } -} - -TEST(EthereumAbi, ParamFactoryMakeException) { - EXPECT_EXCEPTION(ParamFactory::make("uint93"), "invalid bit size"); -} - -TEST(EthereumAbi, ParamFactoryGetArrayValue) { - { - auto pArray = std::make_shared(std::make_shared()); - const auto vals = ParamFactory::getArrayValue(pArray, pArray->getType()); - ASSERT_EQ(vals.size(), 1ul); - EXPECT_EQ(vals[0], "0"); - } - { // wrong type, not array - auto pArray = std::make_shared(std::make_shared()); - const auto vals = ParamFactory::getArrayValue(pArray, "bool"); - EXPECT_EQ(vals.size(), 0ul); - } - { // wrong param, not array - auto pArray = std::make_shared(); - const auto vals = ParamFactory::getArrayValue(pArray, "uint8[]"); - EXPECT_EQ(vals.size(), 0ul); - } -} - -TEST(EthereumAbi, ParamFactorySetGetValue) { - { - auto p = std::make_shared(); - EXPECT_EQ("0", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("13")); - EXPECT_EQ("13", ParamFactory::getValue(p, p->getType())); - EXPECT_FALSE(p->setValueJson("a5")); - EXPECT_FALSE(p->setValueJson("0xa5")); - EXPECT_FALSE(p->setValueJson("0x00")); - } - { - auto p = std::make_shared(); - EXPECT_EQ("0", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("1234")); - EXPECT_EQ("1234", ParamFactory::getValue(p, p->getType())); - EXPECT_FALSE(p->setValueJson("a5")); - EXPECT_FALSE(p->setValueJson("0xa5")); - EXPECT_FALSE(p->setValueJson("0x00")); - } - { - auto p = std::make_shared(); - EXPECT_EQ("0", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("1234567")); - EXPECT_EQ("1234567", ParamFactory::getValue(p, p->getType())); - EXPECT_FALSE(p->setValueJson("a5")); - EXPECT_FALSE(p->setValueJson("0xa5")); - EXPECT_FALSE(p->setValueJson("0x00")); - } - { - auto p = std::make_shared(); - EXPECT_EQ("0", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("1234567")); - EXPECT_EQ("1234567", ParamFactory::getValue(p, p->getType())); - EXPECT_FALSE(p->setValueJson("a5")); - EXPECT_FALSE(p->setValueJson("0xa5")); - EXPECT_FALSE(p->setValueJson("0x00")); - } - { - auto p = std::make_shared(128); - EXPECT_EQ("0", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("1234567890123456789")); - EXPECT_EQ("1234567890123456789", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("0xa5")); - EXPECT_EQ("165", ParamFactory::getValue(p, p->getType())); - EXPECT_FALSE(p->setValueJson("a5")); - } - { - auto p = std::make_shared(168); - EXPECT_EQ("0", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("1234567890123456789")); - EXPECT_EQ("1234567890123456789", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("0xa5")); - EXPECT_EQ("165", ParamFactory::getValue(p, p->getType())); - EXPECT_FALSE(p->setValueJson("a5")); - } - { - auto p = std::make_shared(); - EXPECT_EQ("0", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("1234567890123456789")); - EXPECT_EQ("1234567890123456789", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("0xa5")); - EXPECT_EQ("165", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("0x00")); - EXPECT_FALSE(p->setValueJson("a5")); - EXPECT_FALSE(p->setValueJson("0x")); - } - { - auto p = std::make_shared(); - EXPECT_EQ("0", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("13")); - EXPECT_EQ("13", ParamFactory::getValue(p, p->getType())); - EXPECT_FALSE(p->setValueJson("a5")); - EXPECT_FALSE(p->setValueJson("0xa5")); - EXPECT_FALSE(p->setValueJson("0x00")); - } - { - auto p = std::make_shared(); - EXPECT_EQ("0", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("1234")); - EXPECT_EQ("1234", ParamFactory::getValue(p, p->getType())); - EXPECT_FALSE(p->setValueJson("a5")); - EXPECT_FALSE(p->setValueJson("0xa5")); - EXPECT_FALSE(p->setValueJson("0x00")); - } - { - auto p = std::make_shared(); - EXPECT_EQ("0", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("1234567")); - EXPECT_EQ("1234567", ParamFactory::getValue(p, p->getType())); - EXPECT_FALSE(p->setValueJson("a5")); - EXPECT_FALSE(p->setValueJson("0xa5")); - EXPECT_FALSE(p->setValueJson("0x00")); - } - { - auto p = std::make_shared(); - EXPECT_EQ("0", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("1234567")); - EXPECT_EQ("1234567", ParamFactory::getValue(p, p->getType())); - EXPECT_FALSE(p->setValueJson("a5")); - EXPECT_FALSE(p->setValueJson("0xa5")); - EXPECT_FALSE(p->setValueJson("0x00")); - } - { - auto p = std::make_shared(128); - EXPECT_EQ("0", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("1234567890123456789")); - EXPECT_EQ("1234567890123456789", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("0xa5")); - EXPECT_EQ("165", ParamFactory::getValue(p, p->getType())); - EXPECT_FALSE(p->setValueJson("a5")); - } - { - auto p = std::make_shared(168); - EXPECT_EQ("0", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("1234567890123456789")); - EXPECT_EQ("1234567890123456789", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("0xa5")); - EXPECT_EQ("165", ParamFactory::getValue(p, p->getType())); - EXPECT_FALSE(p->setValueJson("a5")); - } - { - auto p = std::make_shared(); - EXPECT_EQ("0", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("1234567890123456789")); - EXPECT_EQ("1234567890123456789", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("0xa5")); - EXPECT_EQ("165", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("0x00")); - EXPECT_FALSE(p->setValueJson("a5")); - EXPECT_FALSE(p->setValueJson("0x")); - } - { - auto p = std::make_shared(); - EXPECT_EQ("false", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("true")); - EXPECT_EQ("true", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("false")); - EXPECT_TRUE(p->setValueJson("1")); - EXPECT_TRUE(p->setValueJson("0")); - EXPECT_FALSE(p->setValueJson("a5")); - EXPECT_FALSE(p->setValueJson("0xa5")); - EXPECT_FALSE(p->setValueJson("0x00")); - } - { - auto p = std::make_shared(); - EXPECT_EQ("", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("ABCdefGHI")); - EXPECT_EQ("ABCdefGHI", ParamFactory::getValue(p, p->getType())); - EXPECT_EQ(9ul, p->getCount()); - } - { - auto p = std::make_shared(); - EXPECT_EQ("0x", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("0123456789")); - EXPECT_EQ("0x0123456789", ParamFactory::getValue(p, p->getType())); - } - { - auto p = std::make_shared(36); - EXPECT_EQ("0x000000000000000000000000000000000000000000000000000000000000000000000000", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("0x000000000000000000000000000000000000000000000000000000000000000123456789")); - EXPECT_EQ("0x000000000000000000000000000000000000000000000000000000000000000123456789", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("0x0123456789")); // will be padded - EXPECT_EQ("0x012345678900000000000000000000000000000000000000000000000000000000000000", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("0xabcdef0000000000000000000000000000000000000000000000000000000000000123456789")); // will be cropped - EXPECT_EQ("0xabcdef000000000000000000000000000000000000000000000000000000000000012345", ParamFactory::getValue(p, p->getType())); - } - { - auto p = std::make_shared(); - EXPECT_EQ("0x0000000000000000000000000000000000000000", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("0x0000000000000000000000000000000123456789")); - EXPECT_EQ("0x0000000000000000000000000000000123456789", ParamFactory::getValue(p, p->getType())); - } - { - auto p = std::make_shared(std::make_shared()); - EXPECT_EQ("[0]", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("[13,14,15]")); - EXPECT_EQ("[13,14,15]", ParamFactory::getValue(p, p->getType())); - EXPECT_FALSE(p->setValueJson("13")); - } - { - auto p = std::make_shared(std::make_shared()); - EXPECT_EQ("[\"0x0000000000000000000000000000000000000000\"]", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("[\"0x0000000000000000000000000000000123456789\"]")); - EXPECT_EQ("[\"0x0000000000000000000000000000000123456789\"]", ParamFactory::getValue(p, p->getType())); - EXPECT_FALSE(p->setValueJson("0x0000000000000000000000000000000123456789")); - } - { - auto p = std::make_shared(std::make_shared()); - EXPECT_EQ("[false]", ParamFactory::getValue(p, p->getType())); - EXPECT_TRUE(p->setValueJson("[true,false,true]")); - EXPECT_EQ("[true,false,true]", ParamFactory::getValue(p, p->getType())); - EXPECT_FALSE(p->setValueJson("true")); - } -} - -TEST(EthereumAbi, ParamFactoryGetValue) { - const std::vector types = { - "uint8", - "uint16", - "uint32", - "uint64", - "uint128", - "uint168", - "uint256", - "int8", - "int16", - "int32", - "int64", - "int128", - "int168", - "int256", - "bool", - "string", - "bytes", - "bytes168", - "address", - "uint8[]", - "address[]", - "bool[]", - "bytes[]", - }; - for (auto t : types) { - std::shared_ptr p = ParamFactory::make(t); - EXPECT_EQ(t, p->getType()); - - std::string expected = ""; - // for numerical values, value is "0" - if (t == "uint8[]" || t == "int8[]") { - expected = "[0]"; - } else if (t == "bool") { - expected = "false"; - } else if (t == "address[]") { - expected = "[\"0x0000000000000000000000000000000000000000\"]"; - } else if (t == "address") { - expected = "0x0000000000000000000000000000000000000000"; - } else if (t == "bool[]") { - expected = "[false]"; - } else if (t == "bytes[]") { - expected = "[\"0x\"]"; - } else if (t == "bytes") { - expected = "0x"; - } else if (t == "bytes168") { - expected = "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; - } else if (t.substr(0, 3) == "int" || t.substr(0, 4) == "uint") { - expected = "0"; - } - EXPECT_EQ(expected, ParamFactory::getValue(p, t)); - } -} - -TEST(EthereumAbi, MaskForBits) { - EXPECT_EQ(0x000000ffffff, ParamUIntN::maskForBits(24)); - EXPECT_EQ(0x00ffffffffff, ParamUIntN::maskForBits(40)); -} - -TEST(EthereumAbi, ParamSetMethods) { - { - auto p = ParamSet(std::vector>{ - std::make_shared(16u), - std::make_shared(true)}); - EXPECT_EQ(p.getCount(), 2ul); - EXPECT_EQ(p.addParam(std::shared_ptr(nullptr)), -1); - - std::shared_ptr getparam; - EXPECT_TRUE(p.getParam(1, getparam)); - EXPECT_EQ(getparam->getType(), "bool"); - EXPECT_FALSE(p.getParam(2, getparam)); - - EXPECT_EQ(p.getParamUnsafe(0)->getType(), "uint256"); - EXPECT_EQ(p.getParamUnsafe(1)->getType(), "bool"); - EXPECT_EQ(p.getParamUnsafe(2)->getType(), "uint256"); - EXPECT_EQ(p.getParamUnsafe(99)->getType(), "uint256"); - } - { - auto pEmpty = ParamSet(std::vector>{}); - EXPECT_EQ(pEmpty.getParamUnsafe(0).get(), nullptr); - } -} - -TEST(EthereumAbi, ParametersMethods) { - auto p = Parameters(std::vector>{ - std::make_shared(16u), - std::make_shared(true)}); - EXPECT_TRUE(p.isDynamic()); - EXPECT_EQ(p.getCount(), 2ul); - EXPECT_FALSE(p.setValueJson("value")); - EXPECT_EQ(hex(p.hashStruct()), "755311b9e2cee471a91b161ccc5deed933d844b5af2b885543cc3c04eb640983"); -} - -} // namespace TW::Ethereum::ABI::tests diff --git a/tests/chains/Ethereum/BarzTests.cpp b/tests/chains/Ethereum/BarzTests.cpp index 699a7b1b50c..7fbf6d9875e 100644 --- a/tests/chains/Ethereum/BarzTests.cpp +++ b/tests/chains/Ethereum/BarzTests.cpp @@ -177,7 +177,7 @@ TEST(Barz, SignK1TransferAccountDeployed) { auto& transfer = *input.mutable_transaction()->mutable_transfer(); transfer.set_amount(amount.data(), amount.size()); - std::string expected = "{\"callData\":\"0xb61d27f600000000000000000000000061061fcae11fd5461535e134eff67a98cfff44e9000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"100000\",\"initCode\":\"0x\",\"maxFeePerGas\":\"7033440745\",\"maxPriorityFeePerGas\":\"7033440745\",\"nonce\":\"2\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"46856\",\"sender\":\"0xb16db98b365b1f89191996942612b14f1da4bd5f\",\"signature\":\"0x80e84992ebf8d5f71180231163ed150a7557ed0aa4b4bcee23d463a09847e4642d0fbf112df2e5fa067adf4b2fa17fc4a8ac172134ba5b78e3ec9c044e7f28d71c\",\"verificationGasLimit\":\"100000\"}"; + std::string expected = "{\"callData\":\"0xb61d27f600000000000000000000000061061fcae11fd5461535e134eff67a98cfff44e9000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"100000\",\"initCode\":\"0x\",\"maxFeePerGas\":\"7033440745\",\"maxPriorityFeePerGas\":\"7033440745\",\"nonce\":\"2\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"46856\",\"sender\":\"0xb16Db98B365B1f89191996942612B14F1Da4Bd5f\",\"signature\":\"0x80e84992ebf8d5f71180231163ed150a7557ed0aa4b4bcee23d463a09847e4642d0fbf112df2e5fa067adf4b2fa17fc4a8ac172134ba5b78e3ec9c044e7f28d71c\",\"verificationGasLimit\":\"100000\"}"; { // sign test @@ -230,7 +230,7 @@ TEST(Barz, SignR1TransferAccountNotDeployed) { auto& transfer = *input.mutable_transaction()->mutable_transfer(); transfer.set_amount(amount.data(), amount.size()); - std::string expected = "{\"callData\":\"0xb61d27f600000000000000000000000061061fcae11fd5461535e134eff67a98cfff44e9000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"2500000\",\"initCode\":\"0x3fc708630d85a3b5ec217e53100ec2b735d4f800296601cd0000000000000000000000005034534efe9902779ed6ea6983f435c00f3bc51000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004104b173a6a812025c40c38bac46343646bd0a8137c807aae6e04aac238cc24d2ad2116ca14d23d357588ff2aabd7db29d5976f4ecc8037775db86f67e873a306b1f00000000000000000000000000000000000000000000000000000000000000\",\"maxFeePerGas\":\"7033440745\",\"maxPriorityFeePerGas\":\"7033440745\",\"nonce\":\"0\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"46856\",\"sender\":\"0x1392ae041bfbdbaa0cff9234a0c8f64df97b7218\",\"signature\":\"0xbf1b68323974e71ad9bd6dfdac07dc062599d150615419bb7876740d2bcf3c8909aa7e627bb0e08a2eab930e2e7313247c9b683c884236dd6ea0b6834fb2cb0a1b\",\"verificationGasLimit\":\"3000000\"}"; + std::string expected = "{\"callData\":\"0xb61d27f600000000000000000000000061061fcae11fd5461535e134eff67a98cfff44e9000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"2500000\",\"initCode\":\"0x3fc708630d85a3b5ec217e53100ec2b735d4f800296601cd0000000000000000000000005034534efe9902779ed6ea6983f435c00f3bc51000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004104b173a6a812025c40c38bac46343646bd0a8137c807aae6e04aac238cc24d2ad2116ca14d23d357588ff2aabd7db29d5976f4ecc8037775db86f67e873a306b1f00000000000000000000000000000000000000000000000000000000000000\",\"maxFeePerGas\":\"7033440745\",\"maxPriorityFeePerGas\":\"7033440745\",\"nonce\":\"0\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"46856\",\"sender\":\"0x1392Ae041BfBdBAA0cFF9234a0C8F64df97B7218\",\"signature\":\"0xbf1b68323974e71ad9bd6dfdac07dc062599d150615419bb7876740d2bcf3c8909aa7e627bb0e08a2eab930e2e7313247c9b683c884236dd6ea0b6834fb2cb0a1b\",\"verificationGasLimit\":\"3000000\"}"; { // sign test TW::Ethereum::Proto::SigningOutput output; @@ -279,6 +279,7 @@ TEST(Barz, SignR1BatchedTransferAccountDeployed) { TWEthereumAbiFunctionAddParamUInt256(approveFunc, WRAPD(TWDataCreateWithHexString(WRAPS(TWStringCreateWithUTF8Bytes("8AC7230489E80000")).get())).get(), false); auto approveCallEncoded = WRAPD(TWEthereumAbiEncode(approveFunc)); auto approveCall = data(TWDataBytes(approveCallEncoded.get()), TWDataSize(approveCallEncoded.get())); + EXPECT_EQ(hex(approveCall), "095ea7b30000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27890000000000000000000000000000000000000000000000008ac7230489e80000"); // transfer TWEthereumAbiFunction* transferFunc = TWEthereumAbiFunctionCreateWithString(WRAPS(TWStringCreateWithUTF8Bytes("transfer")).get()); @@ -286,6 +287,7 @@ TEST(Barz, SignR1BatchedTransferAccountDeployed) { TWEthereumAbiFunctionAddParamUInt256(transferFunc, WRAPD(TWDataCreateWithHexString(WRAPS(TWStringCreateWithUTF8Bytes("8AC7230489E80000")).get())).get(), false); auto transferCallEncoded = WRAPD(TWEthereumAbiEncode(transferFunc)); auto transferCall = data(TWDataBytes(transferCallEncoded.get()), TWDataSize(transferCallEncoded.get())); + EXPECT_EQ(hex(transferCall), "a9059cbb0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27890000000000000000000000000000000000000000000000008ac7230489e80000"); auto *batch = input.mutable_transaction()->mutable_batch(); auto *c1 = batch->add_calls(); @@ -299,7 +301,7 @@ TEST(Barz, SignR1BatchedTransferAccountDeployed) { input.set_private_key(key.data(), key.size()); - std::string expected = "{\"callData\":\"0x47e1da2a000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000003bbb5660b8687c2aa453a0e42dcb6e0732b126600000000000000000000000003bbb5660b8687c2aa453a0e42dcb6e0732b12660000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27890000000000000000000000000000000000000000000000008ac7230489e80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27890000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"88673\",\"initCode\":\"0x\",\"maxFeePerGas\":\"10000000000\",\"maxPriorityFeePerGas\":\"10000000000\",\"nonce\":\"3\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"56060\",\"sender\":\"0x1e6c542ebc7c960c6a155a9094db838cef842cf5\",\"signature\":\"0x0747b665fe9f3a52407f95a35ac3e76de37c9b89483ae440431244e89a77985f47df712c7364c1a299a5ef62d0b79a2cf4ed63d01772275dd61f72bd1ad5afce1c\",\"verificationGasLimit\":\"522180\"}"; + std::string expected = "{\"callData\":\"0x47e1da2a000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000003bbb5660b8687c2aa453a0e42dcb6e0732b126600000000000000000000000003bbb5660b8687c2aa453a0e42dcb6e0732b12660000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27890000000000000000000000000000000000000000000000008ac7230489e80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27890000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000000000000000\",\"callGasLimit\":\"88673\",\"initCode\":\"0x\",\"maxFeePerGas\":\"10000000000\",\"maxPriorityFeePerGas\":\"10000000000\",\"nonce\":\"3\",\"paymasterAndData\":\"0x\",\"preVerificationGas\":\"56060\",\"sender\":\"0x1E6c542ebC7c960c6A155A9094DB838cEf842cf5\",\"signature\":\"0x0747b665fe9f3a52407f95a35ac3e76de37c9b89483ae440431244e89a77985f47df712c7364c1a299a5ef62d0b79a2cf4ed63d01772275dd61f72bd1ad5afce1c\",\"verificationGasLimit\":\"522180\"}"; { // sign test TW::Ethereum::Proto::SigningOutput output; diff --git a/tests/chains/Ethereum/ContractCallTests.cpp b/tests/chains/Ethereum/ContractCallTests.cpp index 19af2d14dbf..164a9fc332b 100644 --- a/tests/chains/Ethereum/ContractCallTests.cpp +++ b/tests/chains/Ethereum/ContractCallTests.cpp @@ -14,29 +14,36 @@ extern std::string TESTS_ROOT; namespace TW::Ethereum::ABI::tests { -static nlohmann::json load_json(std::string path) { +static nlohmann::json load_json(const std::string& path) { std::ifstream stream(path); nlohmann::json json; stream >> json; return json; } +static std::string load_json_str(const std::string& path) { + std::ifstream stream(path); + std::string json((std::istreambuf_iterator(stream)), + std::istreambuf_iterator()); + return json; +} + TEST(ContractCall, Approval) { auto path = TESTS_ROOT + "/chains/Ethereum/Data/erc20.json"; - auto abi = load_json(path); + auto abi = load_json_str(path); auto call = parse_hex("095ea7b30000000000000000000000005aaeb6053f3e94c9b9a09f33669435e7ef1beaed" "0000000000000000000000000000000000000000000000000000000000000001"); auto decoded = decodeCall(call, abi); auto expected = - R"|({"function":"approve(address,uint256)","inputs":[{"name":"_spender","type":"address","value":"0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed"},{"name":"_value","type":"uint256","value":"1"}]})|"; + R"|({"function":"approve(address,uint256)","inputs":[{"name":"_spender","type":"address","value":"0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"},{"name":"_value","type":"uint256","value":"1"}]})|"; EXPECT_EQ(decoded.value(), expected); } TEST(ContractCall, UniswapSwapTokens) { auto path = TESTS_ROOT + "/chains/Ethereum/Data/uniswap_router_v2.json"; - auto abi = load_json(path); + auto abi = load_json_str(path); // https://etherscan.io/tx/0x57a2414f3cd9ca373b7e663ae67ecf933e40cb77a6e4ed28e4e28b5aa0d8ec63 auto call = parse_hex( "0x38ed17390000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000" @@ -49,14 +56,14 @@ TEST(ContractCall, UniswapSwapTokens) { "6dafa5ebde1f4699f498"); auto decoded = decodeCall(call, abi); auto expected = - R"|({"function":"swapExactTokensForTokens(uint256,uint256,address[],address,uint256)","inputs":[{"name":"amountIn","type":"uint256","value":"1000000000000000000"},{"name":"amountOutMin","type":"uint256","value":"2494851601099271131"},{"name":"path","type":"address[]","value":["0x6b175474e89094c44da98b954eedeac495271d0f","0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2","0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","0xe41d2489571d322189246dafa5ebde1f4699f498"]},{"name":"to","type":"address","value":"0x7d8bf18c7ce84b3e175b339c4ca93aed1dd166f1"},{"name":"deadline","type":"uint256","value":"1594806384"}]})|"; + R"|({"function":"swapExactTokensForTokens(uint256,uint256,address[],address,uint256)","inputs":[{"name":"amountIn","type":"uint256","value":"1000000000000000000"},{"name":"amountOutMin","type":"uint256","value":"2494851601099271131"},{"name":"path","type":"address[]","value":["0x6B175474E89094C44Da98b954EedeAC495271d0F","0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2","0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","0xE41d2489571d322189246DaFA5ebDe1F4699F498"]},{"name":"to","type":"address","value":"0x7d8bf18C7cE84b3E175b339c4Ca93aEd1dD166F1"},{"name":"deadline","type":"uint256","value":"1594806384"}]})|"; EXPECT_EQ(decoded.value(), expected); } TEST(ContractCall, KyberTrade) { auto path = TESTS_ROOT + "/chains/Ethereum/Data/kyber_proxy.json"; - auto abi = load_json(path); + auto abi = load_json_str(path); // https://etherscan.io/tx/0x51ffab782b9a27d754389505d5a50db525c04c68142ce20512d579f10f9e13e4 auto call = parse_hex( @@ -71,14 +78,14 @@ TEST(ContractCall, KyberTrade) { auto decoded = decodeCall(call, abi); auto expected = - R"|({"function":"tradeWithHintAndFee(address,uint256,address,address,uint256,uint256,address,uint256,bytes)","inputs":[{"name":"src","type":"address","value":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"},{"name":"srcAmount","type":"uint256","value":"86000000000000000000"},{"name":"dest","type":"address","value":"0xdac17f958d2ee523a2206206994597c13d831ec7"},{"name":"destAddress","type":"address","value":"0x7755297c6a26d495739206181fe81646dbd0bf39"},{"name":"maxDestAmount","type":"uint256","value":"115792089237316195423570985008687907853269984665640564039457584007913129639935"},{"name":"minConversionRate","type":"uint256","value":"237731504554534883721"},{"name":"platformWallet","type":"address","value":"0x440bbd6a888a36de6e2f6a25f65bc4e16874faa9"},{"name":"platformFeeBps","type":"uint256","value":"8"},{"name":"hint","type":"bytes","value":"0x"}]})|"; + R"|({"function":"tradeWithHintAndFee(address,uint256,address,address,uint256,uint256,address,uint256,bytes)","inputs":[{"name":"src","type":"address","value":"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"},{"name":"srcAmount","type":"uint256","value":"86000000000000000000"},{"name":"dest","type":"address","value":"0xdAC17F958D2ee523a2206206994597C13D831ec7"},{"name":"destAddress","type":"address","value":"0x7755297C6A26D495739206181Fe81646dbD0Bf39"},{"name":"maxDestAmount","type":"uint256","value":"115792089237316195423570985008687907853269984665640564039457584007913129639935"},{"name":"minConversionRate","type":"uint256","value":"237731504554534883721"},{"name":"platformWallet","type":"address","value":"0x440bBd6a888a36DE6e2F6A25f65bc4e16874faa9"},{"name":"platformFeeBps","type":"uint256","value":"8"},{"name":"hint","type":"bytes","value":"0x"}]})|"; EXPECT_EQ(decoded.value(), expected); } TEST(ContractCall, ApprovalForAll) { auto path = TESTS_ROOT + "/chains/Ethereum/Data/erc721.json"; - auto abi = load_json(path); + auto abi = load_json_str(path); // https://etherscan.io/tx/0xc2744000a107aee4761cf8a638657f91c3003a54e2f1818c37d781be7e48187a auto call = parse_hex("0xa22cb46500000000000000000000000088341d1a8f672d2780c8dc725902aae72f143b" @@ -86,19 +93,19 @@ TEST(ContractCall, ApprovalForAll) { auto decoded = decodeCall(call, abi); auto expected = - R"|({"function":"setApprovalForAll(address,bool)","inputs":[{"name":"to","type":"address","value":"0x88341d1a8f672d2780c8dc725902aae72f143b0c"},{"name":"approved","type":"bool","value":true}]})|"; + R"|({"function":"setApprovalForAll(address,bool)","inputs":[{"name":"to","type":"address","value":"0x88341d1a8F672D2780C8dC725902AAe72F143B0c"},{"name":"approved","type":"bool","value":true}]})|"; EXPECT_EQ(decoded.value(), expected); } TEST(ContractCall, CustomCall) { auto path = TESTS_ROOT + "/chains/Ethereum/Data/custom.json"; - auto abi = load_json(path); + auto abi = load_json_str(path); auto call = parse_hex("ec37a4a000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000067472757374790000000000000000000000000000000000000000000000000000"); auto decoded = decodeCall(call, abi); auto expected = - R"|({"function":"setName(string,uint256,int32)","inputs":[{"name":"name","type":"string","value":"trusty"},{"name":"age","type":"uint","value":"3"},{"name":"height","type":"int32","value":"100"}]})|"; + R"|({"function":"setName(string,uint256,int32)","inputs":[{"name":"name","type":"string","value":"trusty"},{"name":"age","type":"uint256","value":"3"},{"name":"height","type":"int32","value":"100"}]})|"; EXPECT_EQ(decoded.value(), expected); } @@ -107,10 +114,10 @@ TEST(ContractCall, SetResolver) { auto call = parse_hex("0x1896f70ae71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c" "6f0000000000000000000000004976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41"); auto path = TESTS_ROOT + "/chains/Ethereum/Data/ens.json"; - auto abi = load_json(path); + auto abi = load_json_str(path); auto decoded = decodeCall(call, abi); auto expected = - R"|({"function":"setResolver(bytes32,address)","inputs":[{"name":"node","type":"bytes32","value":"0xe71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f"},{"name":"resolver","type":"address","value":"0x4976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41"}]})|"; + R"|({"function":"setResolver(bytes32,address)","inputs":[{"name":"node","type":"bytes32","value":"0xe71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f"},{"name":"resolver","type":"address","value":"0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41"}]})|"; EXPECT_EQ(decoded.value(), expected); } @@ -121,7 +128,7 @@ TEST(ContractCall, RenewENS) { "000000000000000000000000000000000000000001e18558000000000000000000000000000000000000000000" "000000000000000000000a68657769676f76656e7300000000000000000000000000000000000000000000"); auto path = TESTS_ROOT + "/chains/Ethereum/Data/ens.json"; - auto abi = load_json(path); + auto abi = load_json_str(path); auto decoded = decodeCall(call, abi); auto expected = R"|({"function":"renew(string,uint256)","inputs":[{"name":"name","type":"string","value":"hewigovens"},{"name":"duration","type":"uint256","value":"31556952"}]})|"; @@ -154,7 +161,7 @@ TEST(ContractCall, Multicall) { "000000000000000000000000000000000000000000000000000000000000000000"); ASSERT_EQ(4 + 928ul, call.size()); auto path = TESTS_ROOT + "/chains/Ethereum/Data/ens.json"; - auto abi = load_json(path); + auto abi = load_json_str(path); auto decoded = decodeCall(call, abi); auto expected = R"|({"function":"multicall(bytes[])","inputs":[{"name":"data","type":"bytes[]","value":["0xd5fa2b00e71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f00000000000000000000000047331175b23c2f067204b506ca1501c26731c990","0x304e6adee71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000","0x8b95dd71e71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f000000000000000000000000000000000000000000000000000000000000003c0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000001447331175b23c2f067204b506ca1501c26731c990000000000000000000000000","0x8b95dd71e71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f00000000000000000000000000000000000000000000000000000000000002ca00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000014d30f834b53d8f7e851e87b90ffa65757a35b8505000000000000000000000000"]}]})|"; @@ -175,32 +182,37 @@ TEST(ContractCall, GetAmountsOut) { "0000000000000000000000000000000000000000000000000000000000000001" "000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077"); auto path = TESTS_ROOT + "/chains/Ethereum/Data/getAmountsOut.json"; - auto abi = load_json(path); + auto abi = load_json_str(path); auto decoded = decodeCall(call, abi); ASSERT_TRUE(decoded.has_value()); auto expected = - R"|({"function":"getAmountsOut(uint256,address[])","inputs":[{"name":"amountIn","type":"uint256","value":"100"},{"name":"path","type":"address[]","value":["0xf784682c82526e245f50975190ef0fff4e4fc077"]}]})|"; + R"|({"function":"getAmountsOut(uint256,address[])","inputs":[{"name":"amountIn","type":"uint256","value":"100"},{"name":"path","type":"address[]","value":["0xF784682C82526e245F50975190EF0fff4E4fC077"]}]})|"; EXPECT_EQ(decoded.value(), expected); } TEST(ContractCall, 1inch) { - auto path = TESTS_ROOT + "/chains/Ethereum/Data/1inch.json"; - auto abi = load_json(path); + auto abiPath = TESTS_ROOT + "/chains/Ethereum/Data/1inch.json"; + auto decodedPath = TESTS_ROOT + "/chains/Ethereum/Data/1inch_decoded.json"; + + auto abi = load_json_str(abiPath); + auto expected = load_json(decodedPath); // https://etherscan.io/tx/0xc2d113151124579c21332d4cc6ab2b7f61e81d62392ed8596174513cb47e35ba auto call = parse_hex( "7c02520000000000000000000000000027239549dd40e1d60f5b80b0c4196923745b1fd2000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001800000000000000000000000002b591e99afe9f32eaa6214f7b7629768c40eeb39000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000027239549dd40e1d60f5b80b0c4196923745b1fd20000000000000000000000001611c227725c5e420ef058275ae772b41775e261000000000000000000000000000000000000000000000000000005d0fadb1c0000000000000000000000000000000000000000000000000000000005c31df1da000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002080000000000000000000000069d91b94f0aaf8e8a2586909fa77a5c2c89818d50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000104128acb080000000000000000000000001611c227725c5e420ef058275ae772b41775e2610000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000005d0fadb1c0000000000000000000000000000000000000000000000000000000001000276a400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000400000000000000000000000002b591e99afe9f32eaa6214f7b7629768c40eeb39000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000"); auto decoded = decodeCall(call, abi); + ASSERT_TRUE(decoded.has_value()); - auto expected = - R"|({"function":"swap(address,(address,address,address,address,uint256,uint256,uint256,bytes),bytes)","inputs":[{"name":"caller","type":"address","value":"0x27239549dd40e1d60f5b80b0c4196923745b1fd2"},{"components":[{"name":"srcToken","type":"address","value":"0x2b591e99afe9f32eaa6214f7b7629768c40eeb39"},{"name":"dstToken","type":"address","value":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"},{"name":"srcReceiver","type":"address","value":"0x27239549dd40e1d60f5b80b0c4196923745b1fd2"},{"name":"dstReceiver","type":"address","value":"0x1611c227725c5e420ef058275ae772b41775e261"},{"name":"amount","type":"uint256","value":"6395120000000"},{"name":"minReturnAmount","type":"uint256","value":"24748356058"},{"name":"flags","type":"uint256","value":"4"},{"name":"permit","type":"bytes","value":"0x"}],"name":"desc","type":"tuple"},{"name":"data","type":"bytes","value":"0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002080000000000000000000000069d91b94f0aaf8e8a2586909fa77a5c2c89818d50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000104128acb080000000000000000000000001611c227725c5e420ef058275ae772b41775e2610000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000005d0fadb1c0000000000000000000000000000000000000000000000000000000001000276a400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000400000000000000000000000002b591e99afe9f32eaa6214f7b7629768c40eeb39000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000"}]})|"; - EXPECT_EQ(decoded.value(), expected); + nlohmann::json decodedJson = nlohmann::json::parse(decoded.value()); + EXPECT_EQ(decodedJson, expected); } TEST(ContractCall, TupleNested) { - auto path = TESTS_ROOT + "/chains/Ethereum/Data/tuple_nested.json"; - auto abi = load_json(path); + auto abiPath = TESTS_ROOT + "/chains/Ethereum/Data/tuple_nested.json"; + auto decodedPath = TESTS_ROOT + "/chains/Ethereum/Data/tuple_nested_decoded.json"; + auto abi = load_json_str(abiPath); + auto expected = load_json(decodedPath); auto call = parse_hex( "74b6ef0b" @@ -212,15 +224,14 @@ TEST(ContractCall, TupleNested) { "0000000000000000000000000000000000000000000000000000000000000001"); auto decoded = decodeCall(call, abi); ASSERT_TRUE(decoded.has_value()); - auto expected = - R"|({"function":"nested_tuple(uint16,(uint16,(uint16,uint64),uint32),bool)","inputs":[{"name":"param1","type":"uint16","value":"1"},{"components":[{"name":"param21","type":"uint16","value":"2"},{"components":[{"name":"param221","type":"uint16","value":"3"},{"name":"param222","type":"uint64","value":"4"}],"name":"param22","type":"tuple"},{"name":"param23","type":"uint32","value":"5"}],"name":"param2","type":"tuple"},{"name":"param3","type":"bool","value":true}]})|"; - EXPECT_EQ(decoded.value(), expected); + nlohmann::json decodedJson = nlohmann::json::parse(decoded.value()); + EXPECT_EQ(decodedJson, expected); } TEST(ContractCall, TupleArray) { auto abiPath = TESTS_ROOT + "/chains/Ethereum/Data/swap_v2.json"; auto decodedPath = TESTS_ROOT + "/chains/Ethereum/Data/swap_v2_decoded.json"; - auto abi = load_json(abiPath); + auto abi = load_json_str(abiPath); auto expectedJson = load_json(decodedPath); auto call = parse_hex("846a1bc6000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000470de4df82000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000740000000000000000000000000000000000000000000000000000000000000078000000000000000000000000000000000000000000000000000000000000007c00000000000000000000000000000000000000000000000000000000000000820000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b300000000000000000000000099a58482bd75cbab83b27ec03ca68ff489b5788f00000000000000000000000000000000000000000000000000470de4df820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099a58482bd75cbab83b27ec03ca68ff489b5788f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000000003840651cb35000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000bebc44782c7db0a1a60cb6fe97d0b483032ff1c7000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000470de4df8200000000000000000000000000000000000000000000000000000000298ce42936ed0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d66600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045553444300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000762696e616e636500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a307863653136463639333735353230616230313337376365374238386635424138433438463844363636000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000072000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000ac000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf300000000000000000000000000000000000000000000000000000000000000010000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb1400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000104414bf3890000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf300000000000000000000000055d398326f99059ff775485246999027b319795500000000000000000000000000000000000000000000000000000000000000640000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd300000000000000000000000000000000000000000000000000000189c04a7044000000000000000000000000000000000000000000000000000029a23529cf68000000000000000000000000000000000000000000005af4f3f913bd553d03b900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000004268b8f0b87b6eae5d897996e6b845ddbd99adf30000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000100000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000001b81d678ffb9c0263b24a97847620c99d213eb14000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000104414bf38900000000000000000000000055d398326f99059ff775485246999027b3197955000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c00000000000000000000000000000000000000000000000000000000000001f40000000000000000000000004fd39c9e151e50580779bd04b1f7ecc310079fd300000000000000000000000000000000000000000000000000000189c04a7045000000000000000000000000000000000000000000005b527785e694f805bdd300000000000000000000000000000000000000000000005f935a1fa5c4a6ec61000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000055d398326f99059ff775485246999027b319795500000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000242e1a7d4d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a140f413c63fbda84e9008607e678258fffbc76b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); diff --git a/tests/chains/Ethereum/Data/1inch_decoded.json b/tests/chains/Ethereum/Data/1inch_decoded.json new file mode 100644 index 00000000000..a9f8a63895e --- /dev/null +++ b/tests/chains/Ethereum/Data/1inch_decoded.json @@ -0,0 +1,61 @@ +{ + "function": "swap(address,(address,address,address,address,uint256,uint256,uint256,bytes),bytes)", + "inputs": [ + { + "name": "caller", + "type": "address", + "value": "0x27239549DD40E1D60F5B80B0C4196923745B1FD2" + }, + { + "components": [ + { + "name": "srcToken", + "type": "address", + "value": "0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39" + }, + { + "name": "dstToken", + "type": "address", + "value": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + }, + { + "name": "srcReceiver", + "type": "address", + "value": "0x27239549DD40E1D60F5B80B0C4196923745B1FD2" + }, + { + "name": "dstReceiver", + "type": "address", + "value": "0x1611C227725c5E420Ef058275AE772b41775e261" + }, + { + "name": "amount", + "type": "uint256", + "value": "6395120000000" + }, + { + "name": "minReturnAmount", + "type": "uint256", + "value": "24748356058" + }, + { + "name": "flags", + "type": "uint256", + "value": "4" + }, + { + "name": "permit", + "type": "bytes", + "value": "0x" + } + ], + "name": "desc", + "type": "tuple" + }, + { + "name": "data", + "type": "bytes", + "value": "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002080000000000000000000000069d91b94f0aaf8e8a2586909fa77a5c2c89818d50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000104128acb080000000000000000000000001611c227725c5e420ef058275ae772b41775e2610000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000005d0fadb1c0000000000000000000000000000000000000000000000000000000001000276a400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000400000000000000000000000002b591e99afe9f32eaa6214f7b7629768c40eeb39000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000" + } + ] +} \ No newline at end of file diff --git a/tests/chains/Ethereum/Data/swap_v2_decoded.json b/tests/chains/Ethereum/Data/swap_v2_decoded.json index 49a95d7806e..be3dab098ec 100644 --- a/tests/chains/Ethereum/Data/swap_v2_decoded.json +++ b/tests/chains/Ethereum/Data/swap_v2_decoded.json @@ -4,7 +4,7 @@ { "name": "token", "type": "address", - "value": "0xdac17f958d2ee523a2206206994597c13d831ec7" + "value": "0xdAC17F958D2ee523a2206206994597C13D831ec7" }, { "name": "amount", @@ -22,7 +22,7 @@ { "name": "target", "type": "address", - "value": "0xdac17f958d2ee523a2206206994597c13d831ec7" + "value": "0xdAC17F958D2ee523a2206206994597C13D831ec7" }, { "name": "value", @@ -49,7 +49,7 @@ { "name": "target", "type": "address", - "value": "0x99a58482bd75cbab83b27ec03ca68ff489b5788f" + "value": "0x99a58482BD75cbab83b27EC03CA68fF489b5788f" }, { "name": "value", @@ -94,7 +94,7 @@ { "name": "gasRefundRecipient", "type": "address", - "value": "0xa140f413c63fbda84e9008607e678258fffbc76b" + "value": "0xa140F413C63FBDA84E9008607E678258ffFbC76b" }, { "name": "enableExpress", diff --git a/tests/chains/Ethereum/Data/tuple_nested_decoded.json b/tests/chains/Ethereum/Data/tuple_nested_decoded.json new file mode 100644 index 00000000000..806049882ad --- /dev/null +++ b/tests/chains/Ethereum/Data/tuple_nested_decoded.json @@ -0,0 +1,47 @@ +{ + "function": "nested_tuple(uint16,(uint16,(uint16,uint64),uint32),bool)", + "inputs": [ + { + "name": "param1", + "type": "uint16", + "value": "1" + }, + { + "components": [ + { + "name": "param21", + "type": "uint16", + "value": "2" + }, + { + "components": [ + { + "name": "param221", + "type": "uint16", + "value": "3" + }, + { + "name": "param222", + "type": "uint64", + "value": "4" + } + ], + "name": "param22", + "type": "tuple" + }, + { + "name": "param23", + "type": "uint32", + "value": "5" + } + ], + "name": "param2", + "type": "tuple" + }, + { + "name": "param3", + "type": "bool", + "value": true + } + ] +} \ No newline at end of file diff --git a/tests/chains/Ethereum/EthereumMessageSignerTests.cpp b/tests/chains/Ethereum/EthereumMessageSignerTests.cpp index 5f43f3b6538..d119f4006da 100644 --- a/tests/chains/Ethereum/EthereumMessageSignerTests.cpp +++ b/tests/chains/Ethereum/EthereumMessageSignerTests.cpp @@ -120,7 +120,7 @@ namespace TW::Ethereum { } })"; auto signature = Ethereum::MessageSigner::signTypedData(ethKey, msg, MessageType::Eip155, 0); - ASSERT_EQ(signature, "EIP712 chainId is different than the current chainID."); + ASSERT_EQ(signature, ""); } TEST(EthereumEip191, SignMessageAndVerifyLegacy) { diff --git a/tests/chains/Ethereum/RLPTests.cpp b/tests/chains/Ethereum/RLPTests.cpp deleted file mode 100644 index e5502d58a2e..00000000000 --- a/tests/chains/Ethereum/RLPTests.cpp +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Ethereum/RLP.h" -#include "HexCoding.h" - -#include - -namespace TW::Ethereum::tests { - -using boost::multiprecision::uint256_t; - -std::string stringifyItems(const RLP::DecodedItem& di); - -std::string stringifyData(const Data& data) { - if (data.size() == 0) - return "0"; - // try if only letters - bool isLettersOnly = true; - for (auto i : data) { - if (!((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z') || i == ' ' || i == ',')) { - isLettersOnly = false; - break; - } - } - if (isLettersOnly) - return std::string("'") + std::string(data.begin(), data.end()) + "'"; - // try if it can be parsed (recursive) - if (data.size() >= 2) { - try { - const auto di = RLP::decode(data); - if (di.decoded.size() > 0 && di.remainder.size() == 0) { - return stringifyItems(di); - } - } catch (...) { - } - } - // any other: as hex string - return hex(data); -} - -std::string stringifyItems(const RLP::DecodedItem& di) { - const auto n = di.decoded.size(); - if (n == 0) { - return "-"; - } - if (n == 1) { - return stringifyData(di.decoded[0]); - } - std::string res = "(" + std::to_string(n) + ": "; - int count = 0; - for (auto i : di.decoded) { - if (count++) - res += " "; - res += stringifyData(i); - } - res += ")"; - return res; -} - -std::string decodeHelper(const std::string& hexData) { - const auto data = parse_hex(hexData); - const auto di = RLP::decode(data); - return stringifyItems(di); -} - -TEST(RLP, EncodeString) { - EXPECT_EQ(hex(RLP::encode("")), "80"); - EXPECT_EQ(hex(RLP::encode("d")), "64"); - EXPECT_EQ(hex(RLP::encode("dog")), "83646f67"); -} - -TEST(RLP, EncodeInteger) { - EXPECT_EQ(hex(RLP::encode(0)), "80"); - EXPECT_EQ(hex(RLP::encode(127)), "7f"); - EXPECT_EQ(hex(RLP::encode(128)), "8180"); - EXPECT_EQ(hex(RLP::encode(255)), "81ff"); - EXPECT_EQ(hex(RLP::encode(256)), "820100"); - EXPECT_EQ(hex(RLP::encode(1024)), "820400"); - EXPECT_EQ(hex(RLP::encode(0xffff)), "82ffff"); - EXPECT_EQ(hex(RLP::encode(0x010000)), "83010000"); - EXPECT_EQ(hex(RLP::encode(0xffffff)), "83ffffff"); - EXPECT_EQ(hex(RLP::encode(static_cast(0xffffffffULL))), "84ffffffff"); - EXPECT_EQ(hex(RLP::encode(static_cast(0xffffffffffffffULL))), "87ffffffffffffff"); -} - -TEST(RLP, EncodeUInt256) { - EXPECT_EQ(hex(RLP::encode(uint256_t(0))), "80"); - EXPECT_EQ(hex(RLP::encode(uint256_t(1))), "01"); - EXPECT_EQ(hex(RLP::encode(uint256_t(127))), "7f"); - EXPECT_EQ(hex(RLP::encode(uint256_t(128))), "8180"); - EXPECT_EQ(hex(RLP::encode(uint256_t(256))), "820100"); - EXPECT_EQ(hex(RLP::encode(uint256_t(1024))), "820400"); - EXPECT_EQ(hex(RLP::encode(uint256_t(0xffffff))), "83ffffff"); - EXPECT_EQ(hex(RLP::encode(uint256_t(0xffffffffULL))), "84ffffffff"); - EXPECT_EQ(hex(RLP::encode(uint256_t(0xffffffffffffffULL))), "87ffffffffffffff"); - EXPECT_EQ( - hex(RLP::encode(uint256_t("0x102030405060708090a0b0c0d0e0f2"))), - "8f102030405060708090a0b0c0d0e0f2"); - EXPECT_EQ( - hex(RLP::encode(uint256_t("0x0100020003000400050006000700080009000a000b000c000d000e01"))), - "9c0100020003000400050006000700080009000a000b000c000d000e01"); - EXPECT_EQ( - hex(RLP::encode(uint256_t("0x0100000000000000000000000000000000000000000000000000000000000000"))), - "a00100000000000000000000000000000000000000000000000000000000000000"); -} - -TEST(RLP, EncodeList) { - EXPECT_EQ(hex(RLP::encodeList(std::vector())), "c0"); - EXPECT_EQ(hex(RLP::encodeList(std::vector{1, 2, 3})), "c3010203"); - EXPECT_EQ(hex(RLP::encodeList(std::vector{"a", "b"})), "c26162"); - EXPECT_EQ(hex(RLP::encodeList(std::vector{"cat", "dog"})), "c88363617483646f67"); - { - const auto encoded = RLP::encodeList(std::vector(1024)); - EXPECT_EQ(hex(subData(encoded, 0, 20)), "f904008080808080808080808080808080808080"); - } -} - -TEST(RLP, EncodeListNested) { - const auto l11 = RLP::encodeList(std::vector{1, 2, 3}); - const auto l12 = RLP::encodeList(std::vector{"apple", "banana", "cherry"}); - const auto l21 = RLP::encodeList(std::vector{parse_hex("abcdef"), parse_hex("00010203040506070809")}); - const auto l22 = RLP::encodeList(std::vector{"bitcoin", "beeenbee", "eth"}); - const auto l1 = RLP::encodeList(std::vector{l11, l12}); - const auto l2 = RLP::encodeList(std::vector{l21, l22}); - const auto encoded = RLP::encodeList(std::vector{l1, l2}); - EXPECT_EQ(hex(encoded), "f8479cdb84c301020395d4856170706c658662616e616e6186636865727279a9e890cf83abcdef8a0001020304050607080996d587626974636f696e88626565656e62656583657468"); -} - -TEST(RLP, EncodeInvalid) { - ASSERT_TRUE(RLP::encode(-1).empty()); - ASSERT_TRUE(RLP::encodeList(std::vector{0, -1}).empty()); -} - -TEST(RLP, DecodeInteger) { - EXPECT_EQ(decodeHelper("00"), "00"); // not the primary encoding for 0 - EXPECT_EQ(decodeHelper("01"), "01"); - EXPECT_EQ(decodeHelper("09"), "09"); - EXPECT_EQ(decodeHelper("7f"), "7f"); - EXPECT_EQ(decodeHelper("80"), "0"); - EXPECT_EQ(decodeHelper("8180"), "80"); - EXPECT_EQ(decodeHelper("81ff"), "ff"); - EXPECT_EQ(decodeHelper("820100"), "0100"); - EXPECT_EQ(decodeHelper("820400"), "0400"); - EXPECT_EQ(decodeHelper("82ffff"), "ffff"); - EXPECT_EQ(decodeHelper("83010000"), "010000"); - EXPECT_EQ(decodeHelper("83ffffff"), "ffffff"); - EXPECT_EQ(decodeHelper("84ffffffff"), "ffffffff"); - EXPECT_EQ(decodeHelper("87ffffffffffffff"), "ffffffffffffff"); -} - -TEST(RLP, DecodeString) { - EXPECT_EQ(decodeHelper("80"), "0"); - EXPECT_EQ(decodeHelper("64"), "'d'"); - EXPECT_EQ(decodeHelper("83646f67"), "'dog'"); - EXPECT_EQ(decodeHelper("83636174"), "'cat'"); - EXPECT_EQ(decodeHelper("8f102030405060708090a0b0c0d0e0f2"), "102030405060708090a0b0c0d0e0f2"); - EXPECT_EQ(decodeHelper("9c0100020003000400050006000700080009000a000b000c000d000e01"), "0100020003000400050006000700080009000a000b000c000d000e01"); - EXPECT_EQ(decodeHelper("a00100000000000000000000000000000000000000000000000000000000000000"), "0100000000000000000000000000000000000000000000000000000000000000"); - // long string - EXPECT_EQ(decodeHelper("b87674686973206973206120612076657279206c6f6e6720737472696e672c2074686973206973206120612076657279206c6f6e6720737472696e672c2074686973206973206120612076657279206c6f6e6720737472696e672c2074686973206973206120612076657279206c6f6e6720737472696e67"), - "'this is a a very long string, this is a a very long string, this is a a very long string, this is a a very long string'"); -} - -TEST(RLP, DecodeList) { - // empty list - EXPECT_EQ(decodeHelper("c0"), "-"); - // short list - EXPECT_EQ(decodeHelper("c3010203"), "(3: 01 02 03)"); - EXPECT_EQ(decodeHelper("c26162"), "(2: 'a' 'b')"); - EXPECT_EQ(decodeHelper("c88363617483646f67"), "(2: 'cat' 'dog')"); - - // long list, raw ether transfer tx - EXPECT_EQ(decodeHelper("f86b81a985051f4d5ce982520894515778891c99e3d2e7ae489980cb7c77b37b5e76861b48eb57e0008025a0ad01c32a7c974df9d0bd48c8d7e0ecab62e90811917aa7dc0c966751a0c3f475a00dc77d9ec68484481bdf87faac14378f4f18d477f84c0810d29480372c1bbc65"), - "(9: " - "a9 " // nonce - "051f4d5ce9 " // gas price - "5208 " // gas limit - "515778891c99e3d2e7ae489980cb7c77b37b5e76 " // to - "1b48eb57e000 " // amount - "0 " // data - "25 " // v - "ad01c32a7c974df9d0bd48c8d7e0ecab62e90811917aa7dc0c966751a0c3f475 " // r - "0dc77d9ec68484481bdf87faac14378f4f18d477f84c0810d29480372c1bbc65" // s - ")"); - - // long list, raw token transfer tx - EXPECT_EQ(decodeHelper("f8aa81d485077359400082db9194dac17f958d2ee523a2206206994597c13d831ec780b844a9059cbb000000000000000000000000c6b6b55c8c4971145a842cc4e5db92d879d0b3e00000000000000000000000000000000000000000000000000000000002faf0801ca02843d8ed66b9623392dc336dd36d5dd5a630b2019962869b6e50fdb4ecb5b6aca05d9ea377bc65e2921f7fc257de8135530cc74e3188b6ba57a4b9cb284393050a"), - "(9: " - "d4 " - "0773594000 " - "db91 " - "dac17f958d2ee523a2206206994597c13d831ec7 " - "0 " - "a9059cbb000000000000000000000000c6b6b55c8c4971145a842cc4e5db92d879d0b3e00000000000000000000000000000000000000000000000000000000002faf080 " - "1c " - "2843d8ed66b9623392dc336dd36d5dd5a630b2019962869b6e50fdb4ecb5b6ac " - "5d9ea377bc65e2921f7fc257de8135530cc74e3188b6ba57a4b9cb284393050a" - ")"); - - { - // long list, with 2-byte size - const std::string elem = "0123"; - const std::size_t n = 500; - std::vector longarr; - for (auto i = 0ul; i < n; ++i) - longarr.push_back(elem); - - const Data encoded = RLP::encodeList(longarr); - ASSERT_EQ(hex(subData(encoded, 0, 20)), "f909c48430313233843031323384303132338430"); - - auto decoded = RLP::decode(encoded); - ASSERT_EQ(decoded.decoded.size(), n); - for (int i = 0; i < 20; i++) { - EXPECT_EQ(hex(decoded.decoded[i]), "30313233"); - } - } - { - // long list, with 3-byte size - const std::string elem = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"; - const std::size_t n = 650; - std::vector longarr; - for (auto i = 0ul; i < n; ++i) - longarr.push_back(elem); - - const Data encoded = RLP::encodeList(longarr); - ASSERT_EQ(encoded.size(), 66304ul); - ASSERT_EQ(hex(subData(encoded, 0, 30)), "fa0102fcb864303132333435363738393031323334353637383930313233"); - - auto decoded = RLP::decode(encoded); - ASSERT_EQ(decoded.decoded.size(), n); - } - - // nested list - EXPECT_EQ(decodeHelper("f8479cdb84c301020395d4856170706c658662616e616e6186636865727279a9e890cf83abcdef8a0001020304050607080996d587626974636f696e88626565656e62656583657468"), - "(2: (2: (3: 01 02 03) (3: 'apple' 'banana' 'cherry')) (2: (2: abcdef 00010203040506070809) (3: 'bitcoin' 'beeenbee' 'eth')))"); -} - -TEST(RLP, DecodeInvalid) { - // decode empty data - EXPECT_THROW(RLP::decode(Data()), std::invalid_argument); - - // incorrect length - EXPECT_THROW(RLP::decode(parse_hex("0x81636174")), std::invalid_argument); - EXPECT_THROW(RLP::decode(parse_hex("0xb9ffff")), std::invalid_argument); - EXPECT_THROW(RLP::decode(parse_hex("0xc883636174")), std::invalid_argument); - - // some tests are from https://github.com/ethereum/tests/blob/develop/RLPTests/invalidRLPTest.json - // int32 overflow - EXPECT_THROW(RLP::decode(parse_hex("0xbf0f000000000000021111")), std::invalid_argument); - - // wrong size list - EXPECT_THROW(RLP::decode(parse_hex("0xf80180")), std::invalid_argument); - - // bytes should be single byte - EXPECT_THROW(RLP::decode(parse_hex("0x8100")), std::invalid_argument); - - // leading zeros in long length list - EXPECT_THROW(RLP::decode(parse_hex("fb00000040000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f")), std::invalid_argument); - EXPECT_THROW(RLP::decode(parse_hex("f800")), std::invalid_argument); -} - -TEST(RLP, putVarInt) { - EXPECT_EQ(hex(RLP::putVarInt(0)), "00"); - EXPECT_EQ(hex(RLP::putVarInt(1)), "01"); - EXPECT_EQ(hex(RLP::putVarInt(0x21)), "21"); - EXPECT_EQ(hex(RLP::putVarInt(0xff)), "ff"); - EXPECT_EQ(hex(RLP::putVarInt(0x100)), "0100"); - EXPECT_EQ(hex(RLP::putVarInt(0x4321)), "4321"); - EXPECT_EQ(hex(RLP::putVarInt(0x654321)), "654321"); - EXPECT_EQ(hex(RLP::putVarInt(0x87654321)), "87654321"); - EXPECT_EQ(hex(RLP::putVarInt(0xa987654321)), "a987654321"); - EXPECT_EQ(hex(RLP::putVarInt(0xcba987654321)), "cba987654321"); - EXPECT_EQ(hex(RLP::putVarInt(0xedcba987654321)), "edcba987654321"); - EXPECT_EQ(hex(RLP::putVarInt(0x21edcba987654321)), "21edcba987654321"); - EXPECT_EQ(hex(RLP::putVarInt(0xffffffffffffffff)), "ffffffffffffffff"); -} - -TEST(RLP, parseVarInt) { - EXPECT_EQ(hex(store(RLP::parseVarInt(1, parse_hex("00"), 0))), "00"); - EXPECT_EQ(hex(store(RLP::parseVarInt(1, parse_hex("01"), 0))), "01"); - EXPECT_EQ(hex(store(RLP::parseVarInt(1, parse_hex("fc"), 0))), "fc"); - EXPECT_EQ(hex(store(RLP::parseVarInt(1, parse_hex("ff"), 0))), "ff"); - EXPECT_EQ(hex(store(RLP::parseVarInt(1, parse_hex("abcd"), 1))), "cd"); - EXPECT_EQ(hex(store(RLP::parseVarInt(2, parse_hex("0102"), 0))), "0102"); - EXPECT_EQ(hex(store(RLP::parseVarInt(2, parse_hex("0100"), 0))), "0100"); - EXPECT_EQ(hex(store(RLP::parseVarInt(2, parse_hex("fedc"), 0))), "fedc"); - EXPECT_EQ(hex(store(RLP::parseVarInt(2, parse_hex("ffff"), 0))), "ffff"); - EXPECT_EQ(hex(store(RLP::parseVarInt(3, parse_hex("010203"), 0))), "010203"); - EXPECT_EQ(hex(store(RLP::parseVarInt(4, parse_hex("01020304"), 0))), "01020304"); - EXPECT_EQ(hex(store(RLP::parseVarInt(5, parse_hex("0102030405"), 0))), "0102030405"); - EXPECT_EQ(hex(store(RLP::parseVarInt(6, parse_hex("010203040506"), 0))), "010203040506"); - EXPECT_EQ(hex(store(RLP::parseVarInt(7, parse_hex("01020304050607"), 0))), "01020304050607"); - EXPECT_EQ(hex(store(RLP::parseVarInt(8, parse_hex("0102030405060708"), 0))), "0102030405060708"); - EXPECT_EQ(hex(store(RLP::parseVarInt(8, parse_hex("abcd0102030405060708"), 2))), "0102030405060708"); - EXPECT_THROW(RLP::parseVarInt(0, parse_hex("01"), 0), std::invalid_argument); // wrong size - EXPECT_THROW(RLP::parseVarInt(9, parse_hex("010203040506070809"), 0), std::invalid_argument); // wrong size - EXPECT_THROW(RLP::parseVarInt(4, parse_hex("0102"), 0), std::invalid_argument); // too short - EXPECT_THROW(RLP::parseVarInt(4, parse_hex("01020304"), 2), std::invalid_argument); // too short - EXPECT_THROW(RLP::parseVarInt(2, parse_hex("0002"), 0), std::invalid_argument); // starts with 0 -} - -TEST(RLP, decodeLenOverflow) { - EXPECT_THROW(RLP::decode(parse_hex("c9bffffffffffffffff7")), std::invalid_argument); // String length overflow (64 bit) - EXPECT_THROW(RLP::decode(parse_hex("c7fbfffffffbc17f")), std::invalid_argument); // List length overflow (32 bit) - EXPECT_THROW(RLP::decode(parse_hex("cbfffffffffffffffff7c17f")), std::invalid_argument); // List length overflow (64 bit) -} - -} // namespace TW::Ethereum::tests diff --git a/tests/chains/Ethereum/SignerTests.cpp b/tests/chains/Ethereum/SignerTests.cpp deleted file mode 100644 index 524542ff212..00000000000 --- a/tests/chains/Ethereum/SignerTests.cpp +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Ethereum/Address.h" -#include "Ethereum/MessageSigner.h" -#include "Ethereum/RLP.h" -#include "Ethereum/Signer.h" -#include "Ethereum/Transaction.h" -#include "HexCoding.h" - -#include - -namespace TW::Ethereum { - -using boost::multiprecision::uint256_t; - -TEST(EthereumTransaction, encodeTransactionNonTyped) { - const auto transaction = TransactionNonTyped::buildERC20Transfer( - /* nonce: */ 0, - /* gasPrice: */ 42000000000, // 0x09c7652400 - /* gasLimit: */ 78009, // 130B9 - /* tokenContract: */ parse_hex("0x6b175474e89094c44da98b954eedeac495271d0f"), // DAI - /* toAddress: */ parse_hex("0x5322b34c88ed0691971bf52a7047448f0f4efc84"), - /* amount: */ 2000000000000000000 - ); - const uint256_t dummyChain = 0x34; - const auto dummySignature = Signature{0, 0, 0}; - - const auto preHash = transaction->preHash(dummyChain); - EXPECT_EQ(hex(preHash), "b3525019dc367d3ecac48905f9a95ff3550c25a24823db765f92cae2dec7ebfd"); - - const auto encoded = transaction->encoded(dummySignature, dummyChain); - EXPECT_EQ(hex(encoded), "f86a808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000808080"); -} - -TEST(EthereumSigner, PreHash) { - const auto address = parse_hex("0x3535353535353535353535353535353535353535"); - const auto transaction = TransactionNonTyped::buildNativeTransfer( - /* nonce: */ 9, - /* gasPrice: */ 20000000000, - /* gasLimit: */ 21000, - /* to: */ address, - /* amount: */ 1000000000000000000 - ); - const auto preHash = transaction->preHash(1); - - ASSERT_EQ(hex(preHash), "daf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53"); -} - -TEST(EthereumSigner, Sign) { - const auto address = parse_hex("0x3535353535353535353535353535353535353535"); - const auto transaction = TransactionNonTyped::buildNativeTransfer( - /* nonce: */ 9, - /* gasPrice: */ 20000000000, - /* gasLimit: */ 21000, - /* to: */ address, - /* amount: */ 1000000000000000000 - ); - - const auto key = PrivateKey(parse_hex("0x4646464646464646464646464646464646464646464646464646464646464646")); - const auto signature = Signer::sign(key, 1, transaction); - - ASSERT_EQ(signature.v, 37); - ASSERT_EQ(signature.r, uint256_t("18515461264373351373200002665853028612451056578545711640558177340181847433846")); - ASSERT_EQ(signature.s, uint256_t("46948507304638947509940763649030358759909902576025900602547168820602576006531")); -} - -TEST(EthereumSigner, SignERC20Transfer) { - const auto transaction = TransactionNonTyped::buildERC20Transfer( - /* nonce: */ 0, - /* gasPrice: */ 42000000000, // 0x09c7652400 - /* gasLimit: */ 78009, // 130B9 - /* tokenContract: */ parse_hex("0x6b175474e89094c44da98b954eedeac495271d0f"), // DAI - /* toAddress: */ parse_hex("0x5322b34c88ed0691971bf52a7047448f0f4efc84"), - /* amount: */ 2000000000000000000 - ); - - const auto key = PrivateKey(parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151")); - const auto signature = Signer::sign(key, 1, transaction); - - ASSERT_EQ(signature.v, 37); - ASSERT_EQ(hex(store(signature.r)), "724c62ad4fbf47346b02de06e603e013f26f26b56fdc0be7ba3d6273401d98ce"); - ASSERT_EQ(hex(store(signature.s)), "032131cae15da7ddcda66963e8bef51ca0d9962bfef0547d3f02597a4a58c931"); -} - -TEST(EthereumSigner, EIP1559_1442) { - const auto transaction = TransactionEip1559::buildNativeTransfer( - /* nonce: */ 6, - /* maxInclusionFeePerGas */ 2000000000, - /* maxFeePerGas */ 3000000000, - /* gasLimit */ 21100, - /* toAddress */ parse_hex("B9F5771C27664bF2282D98E09D7F50cEc7cB01a7"), - /* amount */ 543210987654321, - /* data */ Data() - ); - - const uint256_t chainID = 3; - const auto key = PrivateKey(parse_hex("4f96ed80e9a7555a6f74b3d658afdd9c756b0a40d4ca30c42c2039eb449bb904")); - const auto signature = Signer::sign(key, chainID, transaction); - EXPECT_EQ(signature.v, 0); - EXPECT_EQ(hex(store(signature.r)), "92c336138f7d0231fe9422bb30ee9ef10bf222761fe9e04442e3a11e88880c64"); - EXPECT_EQ(hex(store(signature.s)), "6487026011dae03dc281bc21c7d7ede5c2226d197befb813a4ecad686b559e58"); - - const auto encoded = transaction->encoded(signature, chainID); - // https://ropsten.etherscan.io/tx/0x14429509307efebfdaa05227d84c147450d168c68539351fbc01ed87c916ab2e - EXPECT_EQ(hex(encoded), "02f8710306847735940084b2d05e0082526c94b9f5771c27664bf2282d98e09d7f50cec7cb01a78701ee0c29f50cb180c080a092c336138f7d0231fe9422bb30ee9ef10bf222761fe9e04442e3a11e88880c64a06487026011dae03dc281bc21c7d7ede5c2226d197befb813a4ecad686b559e58"); -} - -TEST(EthereumSigner, SignatureBreakdownNoEip155) { - const auto key = PrivateKey(parse_hex("f9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8")); - const auto hash = parse_hex("0xf86a808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000808080"); - const auto signature = Signer::sign(key, hash, false, 5); - - const auto r = store(signature.r); - EXPECT_EQ(hex(r), "d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47"); - - const auto v = store(signature.v); - EXPECT_EQ(hex(v), "00"); - - const auto s = store(signature.s); - EXPECT_EQ(hex(s), "786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a"); - - const auto converted = Signer::simpleStructToSignatureData(signature); - EXPECT_EQ(hex(converted), "d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a00"); -} - -TEST(EthereumSigner, SignatureBreakdownEip155Legacy) { - const auto key = PrivateKey(parse_hex("f9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8")); - const auto hash = parse_hex("0xf86a808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000808080"); - const auto signature = Signer::sign(key, hash, true, 0); - - const auto r = store(signature.r); - EXPECT_EQ(hex(r), "d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47"); - - const auto v = store(signature.v); - EXPECT_EQ(hex(v), "1b"); - - const auto s = store(signature.s); - EXPECT_EQ(hex(s), "786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a"); - - const auto converted = Signer::simpleStructToSignatureData(signature); - EXPECT_EQ(hex(converted), "d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a1b"); -} - -TEST(EthereumSigner, SignatureBreakdownEip155) { - const auto key = PrivateKey(parse_hex("f9fb27c90dcaa5631f373330eeef62ae7931587a19bd8215d0c2addf28e439c8")); - const auto hash = parse_hex("0xf86a808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000808080"); - const auto signature = Signer::sign(key, hash, true, 1); - - const auto r = store(signature.r); - EXPECT_EQ(hex(r), "d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47"); - - const auto v = store(signature.v); - EXPECT_EQ(hex(v), "25"); - - const auto s = store(signature.s); - EXPECT_EQ(hex(s), "786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a"); - - const auto converted = Signer::simpleStructToSignatureData(signature); - EXPECT_EQ(hex(converted), "d93fc9ae934d4f72db91cb149e7e84b50ca83b5a8a7b873b0fdb009546e3af47786bfaf31af61eea6471dbb1bec7d94f73fb90887e4f04d0e9b85676c47ab02a25"); -} - -} // namespace TW::Ethereum diff --git a/tests/chains/Ethereum/TWAnySignerTests.cpp b/tests/chains/Ethereum/TWAnySignerTests.cpp index 1008271ca04..c26d026c51b 100644 --- a/tests/chains/Ethereum/TWAnySignerTests.cpp +++ b/tests/chains/Ethereum/TWAnySignerTests.cpp @@ -9,10 +9,7 @@ #include "HexCoding.h" #include "uint256.h" #include "proto/Ethereum.pb.h" -#include "Ethereum/Address.h" #include "Ethereum/ABI/Function.h" -#include "Ethereum/ABI/ParamBase.h" -#include "Ethereum/ABI/ParamAddress.h" #include "PrivateKey.h" #include @@ -111,10 +108,10 @@ TEST(TWAnySignerEthereum, SignERC20TransferAsERC20) { // expected payload Data payload; { - auto func = ABI::Function("transfer", std::vector>{ - std::make_shared(parse_hex(toAddress)), - std::make_shared(amount)}); - func.encode(payload); + payload = ABI::Function::encodeFunctionCall("transfer", Ethereum::ABI::BaseParams{ + std::make_shared(toAddress), + std::make_shared(amount) + }).value(); } ASSERT_EQ(hex(output.data()), hex(payload)); ASSERT_EQ(hex(output.data()), "a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000"); @@ -285,13 +282,13 @@ TEST(TWAnySignerEthereum, SignERC1155Transfer) { // expected payload Data payload; { - auto func = ABI::Function("safeTransferFrom", std::vector>{ - std::make_shared(parse_hex(fromAddress)), - std::make_shared(parse_hex(toAddress)), - std::make_shared(uint256_t(0x23c47ee5)), - std::make_shared(value), - std::make_shared(data)}); - func.encode(payload); + auto funcData = ABI::Function::encodeFunctionCall("safeTransferFrom", Ethereum::ABI::BaseParams{ + std::make_shared(fromAddress), + std::make_shared(toAddress), + std::make_shared(uint256_t(0x23c47ee5)), + std::make_shared(value), + std::make_shared(data)}); + payload = funcData.value(); } ASSERT_EQ(hex(output.data()), hex(payload)); ASSERT_EQ(hex(output.data()), "f242432a000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee50000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000"); @@ -503,8 +500,8 @@ TEST(TWAnySignerEthereum, StakeRocketPool) { Data payload; { - auto func = ABI::Function("deposit", std::vector>{ }); - func.encode(payload); + auto funcData = ABI::Function::encodeFunctionCall("deposit", Ethereum::ABI::BaseParams{ }); + payload = funcData.value(); } @@ -551,9 +548,10 @@ TEST(TWAnySignerEthereum, UnstakeRocketPool) { Data payload; { - auto func = ABI::Function("burn", std::vector>{ - std::make_shared(uint256_t(0x21faa32ab2502b))}); - func.encode(payload); + auto funcData = ABI::Function::encodeFunctionCall("burn", ABI::BaseParams{ + std::make_shared(uint256_t(0x21faa32ab2502b)) + }); + payload = funcData.value(); } Proto::SigningInput input; diff --git a/tests/chains/Ethereum/TWEthereumAbiTests.cpp b/tests/chains/Ethereum/TWEthereumAbiTests.cpp index 655b9e2c610..56fc343c08a 100644 --- a/tests/chains/Ethereum/TWEthereumAbiTests.cpp +++ b/tests/chains/Ethereum/TWEthereumAbiTests.cpp @@ -8,9 +8,9 @@ #include #include -#include "Ethereum/ABI.h" #include "Data.h" #include "HexCoding.h" +#include "proto/EthereumAbi.pb.h" #include "uint256.h" #include "TestUtilities.h" @@ -60,7 +60,7 @@ TEST(TWEthereumAbi, FuncCreate2) { auto p1index = TWEthereumAbiFunctionAddParamUInt256(func, p1val.get(), false); EXPECT_EQ(0, p1index); - Data dummy(0); + Data dummy = store(0); auto p2index = TWEthereumAbiFunctionAddParamUInt256(func, &dummy, true); EXPECT_EQ(0, p2index); @@ -154,7 +154,7 @@ TEST(TWEthereumAbi, EncodeFuncMonster) { TWEthereumAbiFunctionAddParamUIntN(func, 168, WRAPD(TWDataCreateWithHexString(WRAPS(TWStringCreateWithUTF8Bytes("0123")).get())).get(), false); TWEthereumAbiFunctionAddParamUInt256(func, WRAPD(TWDataCreateWithHexString(WRAPS(TWStringCreateWithUTF8Bytes("0123")).get())).get(), false); TWEthereumAbiFunctionAddParamInt8(func, 1, false); - TWEthereumAbiFunctionAddParamInt16(func, 2, false); + TWEthereumAbiFunctionAddParamInt16(func, -3, false); TWEthereumAbiFunctionAddParamInt32(func, 3, false); TWEthereumAbiFunctionAddParamInt64(func, 4, false); TWEthereumAbiFunctionAddParamIntN(func, 168, WRAPD(TWDataCreateWithHexString(WRAPS(TWStringCreateWithUTF8Bytes("0123")).get())).get(), false); @@ -173,7 +173,7 @@ TEST(TWEthereumAbi, EncodeFuncMonster) { TWEthereumAbiFunctionAddInArrayParamUIntN(func, TWEthereumAbiFunctionAddParamArray(func, false), 168, WRAPD(TWDataCreateWithHexString(WRAPS(TWStringCreateWithUTF8Bytes("0123")).get())).get()); TWEthereumAbiFunctionAddInArrayParamUInt256(func, TWEthereumAbiFunctionAddParamArray(func, false), WRAPD(TWDataCreateWithHexString(WRAPS(TWStringCreateWithUTF8Bytes("0123")).get())).get()); TWEthereumAbiFunctionAddInArrayParamInt8(func, TWEthereumAbiFunctionAddParamArray(func, false), 1); - TWEthereumAbiFunctionAddInArrayParamInt16(func, TWEthereumAbiFunctionAddParamArray(func, false), 2); + TWEthereumAbiFunctionAddInArrayParamInt16(func, TWEthereumAbiFunctionAddParamArray(func, false), -3); TWEthereumAbiFunctionAddInArrayParamInt32(func, TWEthereumAbiFunctionAddParamArray(func, false), 3); TWEthereumAbiFunctionAddInArrayParamInt64(func, TWEthereumAbiFunctionAddParamArray(func, false), 4); TWEthereumAbiFunctionAddInArrayParamIntN(func, TWEthereumAbiFunctionAddParamArray(func, false), 168, WRAPD(TWDataCreateWithHexString(WRAPS(TWStringCreateWithUTF8Bytes("0123")).get())).get()); @@ -201,6 +201,7 @@ TEST(TWEthereumAbi, EncodeFuncMonster) { auto encoded = WRAPD(TWEthereumAbiEncode(func)); Data enc2 = data(TWDataBytes(encoded.get()), TWDataSize(encoded.get())); EXPECT_EQ(4ul + 76 * 32, enc2.size()); + EXPECT_EQ(hex(enc2), "70efb5a50000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000012300000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000012300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000440000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc0770000000000000000000000000000000000000000000000000000000000000480313233343500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000540000000000000000000000000000000000000000000000000000000000000058000000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000068000000000000000000000000000000000000000000000000000000000000006c000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000740000000000000000000000000000000000000000000000000000000000000078000000000000000000000000000000000000000000000000000000000000007c00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000008c00000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c64210000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000053132333435000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000123000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005313233343500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013132333435000000000000000000000000000000000000000000000000000000"); // delete TWEthereumAbiFunctionDelete(func); @@ -261,13 +262,22 @@ TEST(TWEthereumAbi, GetParamWrongType) { } TEST(TWEthereumAbi, DecodeCall) { - auto callHex = STRING("c47f0027000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000086465616462656566000000000000000000000000000000000000000000000000"); - auto call = WRAPD(TWDataCreateWithHexString(callHex.get())); - auto abi = STRING(R"|({"c47f0027":{"constant":false,"inputs":[{"name":"name","type":"string"}],"name":"setName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}})|"); - auto decoded = WRAPS(TWEthereumAbiDecodeCall(call.get(), abi.get())); - auto expected = R"|({"function":"setName(string)","inputs":[{"name":"name","type":"string","value":"deadbeef"}]})|"; + auto encodedCall = parse_hex("c47f0027000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000086465616462656566000000000000000000000000000000000000000000000000"); + auto abiJson = R"|({"c47f0027":{"constant":false,"inputs":[{"name":"name","type":"string"}],"name":"setName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}})|"; + + EthereumAbi::Proto::ContractCallDecodingInput input; + input.set_encoded(encodedCall.data(), encodedCall.size()); + input.set_smart_contract_abi_json(abiJson); - assertStringsEqual(decoded, expected); + const auto inputData = data(input.SerializeAsString()); + auto inputTWData = WRAPD(TWDataCreateWithBytes((const uint8_t*)inputData.data(), inputData.size())); + auto outputTWData = WRAPD(TWEthereumAbiDecodeContractCall(TWCoinTypeEthereum, inputTWData.get())); + + EthereumAbi::Proto::ContractCallDecodingOutput output; + output.ParseFromArray(TWDataBytes(outputTWData.get()), static_cast(TWDataSize(outputTWData.get()))); + + auto expected = R"|({"function":"setName(string)","inputs":[{"name":"name","type":"string","value":"deadbeef"}]})|"; + EXPECT_EQ(output.decoded_json(), expected); } TEST(TWEthereumAbi, DecodeInvalidCall) { diff --git a/tests/chains/Ethereum/TWEthereumAbiValueDecoderTests.cpp b/tests/chains/Ethereum/TWEthereumAbiValueDecoderTests.cpp index 0ef20806179..b5d7ac0aef8 100644 --- a/tests/chains/Ethereum/TWEthereumAbiValueDecoderTests.cpp +++ b/tests/chains/Ethereum/TWEthereumAbiValueDecoderTests.cpp @@ -49,7 +49,7 @@ TEST(TWEthereumAbiValue, decodeValue) { { const auto input = "000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077"; const auto type = "address"; - const auto expected = "0xf784682c82526e245f50975190ef0fff4e4fc077"; + const auto expected = "0xF784682C82526e245F50975190EF0fff4E4fC077"; auto data = WRAPD(TWDataCreateWithHexString(STRING(input).get())); auto result = WRAPS(TWEthereumAbiValueDecodeValue(data.get(), WRAPS(TWStringCreateWithUTF8Bytes(type)).get())); EXPECT_EQ(std::string(expected), std::string(TWStringUTF8Bytes(result.get()))); @@ -97,8 +97,8 @@ TEST(TWEthereumAbiValue, decodeArray) { "0000000000000000000000002e00cd222cb42b616d86d037cc494e8ab7f5c9a3"; const auto type = "address[]"; const auto expected = - "[\"0xf784682c82526e245f50975190ef0fff4e4fc077\"," - "\"0x2e00cd222cb42b616d86d037cc494e8ab7f5c9a3\"]"; + "[\"0xF784682C82526e245F50975190EF0fff4E4fC077\"," + "\"0x2e00CD222Cb42B616D86D037Cc494e8ab7F5c9a3\"]"; auto data = WRAPD(TWDataCreateWithHexString(STRING(input).get())); auto result = WRAPS(TWEthereumAbiValueDecodeArray(data.get(), WRAPS(TWStringCreateWithUTF8Bytes(type)).get())); EXPECT_EQ(std::string(expected), std::string(TWStringUTF8Bytes(result.get()))); diff --git a/tests/chains/Ethereum/TWRlpTests.cpp b/tests/chains/Ethereum/TWRlpTests.cpp new file mode 100644 index 00000000000..745f3467a59 --- /dev/null +++ b/tests/chains/Ethereum/TWRlpTests.cpp @@ -0,0 +1,51 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TrustWalletCore/TWEthereumRlp.h" +#include "proto/EthereumRlp.pb.h" +#include "HexCoding.h" +#include "TestUtilities.h" +#include "uint256.h" + +#include + +using namespace TW; + +TEST(TWEthereumRlp, Eip1559) { + auto chainId = store(10); + auto nonce = store(6); + auto maxInclusionFeePerGas = 2'000'000'000; + auto maxFeePerGas = store(3'000'000'000); + auto gasLimit = store(21'100); + const auto* to = "0x6b175474e89094c44da98b954eedeac495271d0f"; + auto amount = 0; + auto payload = parse_hex("a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000001ee0c29f50cb1"); + + EthereumRlp::Proto::EncodingInput input; + auto* list = input.mutable_item()->mutable_list(); + + list->add_items()->set_number_u256(chainId.data(), chainId.size()); + list->add_items()->set_number_u256(nonce.data(), nonce.size()); + list->add_items()->set_number_u64(maxInclusionFeePerGas); + list->add_items()->set_number_u256(maxFeePerGas.data(), maxFeePerGas.size()); + list->add_items()->set_number_u256(gasLimit.data(), gasLimit.size()); + list->add_items()->set_address(to); + list->add_items()->set_number_u64(amount); + list->add_items()->set_data(payload.data(), payload.size()); + // Append an empty `access_list`. + list->add_items()->mutable_list(); + + auto inputData = input.SerializeAsString(); + auto inputTWData = WRAPD(TWDataCreateWithBytes((const uint8_t *)inputData.data(), inputData.size())); + auto outputTWData = WRAPD(TWEthereumRlpEncode(TWCoinTypeEthereum, inputTWData.get())); + + EthereumRlp::Proto::EncodingOutput output; + output.ParseFromArray(TWDataBytes(outputTWData.get()), static_cast(TWDataSize(outputTWData.get()))); + + EXPECT_EQ(output.error(), Common::Proto::SigningError::OK); + EXPECT_TRUE(output.error_message().empty()); + EXPECT_EQ(hex(output.encoded()), "f86c0a06847735940084b2d05e0082526c946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000001ee0c29f50cb1c0"); +} diff --git a/tests/chains/Ethereum/TransactionCompilerTests.cpp b/tests/chains/Ethereum/TransactionCompilerTests.cpp index 79c0feb2c7c..5a37950eeb6 100644 --- a/tests/chains/Ethereum/TransactionCompilerTests.cpp +++ b/tests/chains/Ethereum/TransactionCompilerTests.cpp @@ -25,33 +25,22 @@ using namespace TW; TEST(EthereumCompiler, CompileWithSignatures) { /// Step 1: Prepare transaction input (protobuf) const auto coin = TWCoinTypeEthereum; - const auto txInputData0 = - TransactionCompiler::buildInput(coin, - "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F", // from - "0x3535353535353535353535353535353535353535", // to - "1000000000000000000", // amount - "ETH", // asset - "", // memo - "" // chainId - ); - - // Check, by parsing - EXPECT_EQ((int)txInputData0.size(), 61); Ethereum::Proto::SigningInput input; - ASSERT_TRUE(input.ParseFromArray(txInputData0.data(), (int)txInputData0.size())); - EXPECT_EQ(hex(input.chain_id()), "01"); - EXPECT_EQ(input.to_address(), "0x3535353535353535353535353535353535353535"); - ASSERT_TRUE(input.transaction().has_transfer()); - EXPECT_EQ(hex(input.transaction().transfer().amount()), "0de0b6b3a7640000"); - // Set a few other values const auto nonce = store(uint256_t(11)); + const auto chainId = store(uint256_t(1)); const auto gasPrice = store(uint256_t(20000000000)); const auto gasLimit = store(uint256_t(21000)); + const auto amount = store(uint256_t(1'000'000'000'000'000'000)); + input.set_nonce(nonce.data(), nonce.size()); + input.set_chain_id(chainId.data(), chainId.size()); input.set_gas_price(gasPrice.data(), gasPrice.size()); input.set_gas_limit(gasLimit.data(), gasLimit.size()); input.set_tx_mode(Ethereum::Proto::Legacy); + input.set_to_address("0x3535353535353535353535353535353535353535"); + + input.mutable_transaction()->mutable_transfer()->set_amount(amount.data(), amount.size()); // Serialize back, this shows how to serialize SigningInput protobuf to byte array const auto txInputData = data(input.SerializeAsString()); @@ -120,7 +109,7 @@ TEST(EthereumCompiler, CompileWithSignatures) { } } -TEST(EthereumCompiler, BuildTransactionInput) { +TEST(EthereumCompiler, BuildTransactionInputShouldFail) { const auto coin = TWCoinTypeEthereum; const auto txInputData0 = TransactionCompiler::buildInput(coin, @@ -131,27 +120,6 @@ TEST(EthereumCompiler, BuildTransactionInput) { "Memo", // memo "05" // chainId ); - - // Check, by parsing - EXPECT_EQ(txInputData0.size(), 61ul); - Ethereum::Proto::SigningInput input; - ASSERT_TRUE(input.ParseFromArray(txInputData0.data(), (int)txInputData0.size())); - EXPECT_EQ(hex(input.chain_id()), "05"); - EXPECT_EQ(input.to_address(), "0x3535353535353535353535353535353535353535"); - ASSERT_TRUE(input.transaction().has_transfer()); - EXPECT_EQ(hex(input.transaction().transfer().amount()), "0de0b6b3a7640000"); -} - -TEST(EthereumCompiler, BuildTransactionInputInvalidAddress) { - const auto coin = TWCoinTypeEthereum; - EXPECT_EXCEPTION( - TransactionCompiler::buildInput(coin, - "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F", // from - "__INVALID_ADDRESS__", // to - "1000000000000000000", // amount - "ETH", // asset - "", // memo - "" // chainId - ), - "Invalid to address"); + // Ethereum doesn't support `buildInput`. + EXPECT_TRUE(txInputData0.empty()); } diff --git a/tests/chains/Ethereum/ValueDecoderTests.cpp b/tests/chains/Ethereum/ValueDecoderTests.cpp index d2cdc0e8b24..2f9e547dea5 100644 --- a/tests/chains/Ethereum/ValueDecoderTests.cpp +++ b/tests/chains/Ethereum/ValueDecoderTests.cpp @@ -29,7 +29,7 @@ TEST(EthereumAbiValueDecoder, decodeValue) { EXPECT_EQ("42", ABI::ValueDecoder::decodeValue(parse_hex("000000000000000000000000000000000000000000000000000000000000002a"), "uint")); EXPECT_EQ("24", ABI::ValueDecoder::decodeValue(parse_hex("0000000000000000000000000000000000000000000000000000000000000018"), "uint8")); EXPECT_EQ("123456", ABI::ValueDecoder::decodeValue(parse_hex("000000000000000000000000000000000000000000000000000000000001e240"), "uint256")); - EXPECT_EQ("0xf784682c82526e245f50975190ef0fff4e4fc077", ABI::ValueDecoder::decodeValue(parse_hex("000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077"), "address")); + EXPECT_EQ("0xF784682C82526e245F50975190EF0fff4E4fC077", ABI::ValueDecoder::decodeValue(parse_hex("000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077"), "address")); EXPECT_EQ("Hello World! Hello World! Hello World!", ABI::ValueDecoder::decodeValue(parse_hex( "000000000000000000000000000000000000000000000000000000000000002c" @@ -39,46 +39,4 @@ TEST(EthereumAbiValueDecoder, decodeValue) { EXPECT_EQ("0x31323334353637383930", ABI::ValueDecoder::decodeValue(parse_hex("3132333435363738393000000000000000000000000000000000000000000000"), "bytes10")); } -TEST(EthereumAbiValueDecoder, decodeArray) { - { - // Array of UInt8 - Data input = parse_hex( - "0000000000000000000000000000000000000000000000000000000000000003" - "0000000000000000000000000000000000000000000000000000000000000031" - "0000000000000000000000000000000000000000000000000000000000000032" - "0000000000000000000000000000000000000000000000000000000000000033"); - auto res = ABI::ValueDecoder::decodeArray(input, "uint8[]"); - EXPECT_EQ(3ul, res.size()); - EXPECT_EQ("49", res[0]); - EXPECT_EQ("50", res[1]); - EXPECT_EQ("51", res[2]); - } - { - // Array of Address - Data input = parse_hex( - "0000000000000000000000000000000000000000000000000000000000000002" - "000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077" - "0000000000000000000000002e00cd222cb42b616d86d037cc494e8ab7f5c9a3"); - auto res = ABI::ValueDecoder::decodeArray(input, "address[]"); - EXPECT_EQ(2ul, res.size()); - EXPECT_EQ("0xf784682c82526e245f50975190ef0fff4e4fc077", res[0]); - EXPECT_EQ("0x2e00cd222cb42b616d86d037cc494e8ab7f5c9a3", res[1]); - } - { - // Array of ByteArray - Data input = parse_hex( - "0000000000000000000000000000000000000000000000000000000000000002" - "0000000000000000000000000000000000000000000000000000000000000040" - "0000000000000000000000000000000000000000000000000000000000000080" - "0000000000000000000000000000000000000000000000000000000000000002" - "1011000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000003" - "1022220000000000000000000000000000000000000000000000000000000000"); - auto res = ABI::ValueDecoder::decodeArray(input, "bytes[]"); - EXPECT_EQ(2ul, res.size()); - EXPECT_EQ("0x1011", res[0]); - EXPECT_EQ("0x102222", res[1]); - } -} - } // namespace TW::Ethereum::tests diff --git a/tests/chains/ImmutableX/StarkKeyTests.cpp b/tests/chains/ImmutableX/StarkKeyTests.cpp index 72cc24fe1ef..f1ef1fce5b4 100644 --- a/tests/chains/ImmutableX/StarkKeyTests.cpp +++ b/tests/chains/ImmutableX/StarkKeyTests.cpp @@ -5,7 +5,6 @@ // file LICENSE at the root of the source code distribution tree. #include "Ethereum/EIP2645.h" -#include "Ethereum/Signer.h" #include "HexCoding.h" #include "ImmutableX/Constants.h" #include "ImmutableX/StarkKey.h" @@ -26,8 +25,8 @@ TEST(ImmutableX, ExtraGrinding) { auto data = parse_hex(signature); auto path = DerivationPath(Ethereum::accountPathFromAddress(address, gLayer, gApplication, gIndex)); auto privKey = ImmutableX::getPrivateKeyFromRawSignature(parse_hex(signature), path); - auto pubKey = hexEncoded(getPublicKeyFromPrivateKey(privKey.bytes)); - ASSERT_EQ(pubKey, "0x035919acd61e97b3ecdc75ff8beed8d1803f7ea3cad2937926ae59cc3f8070d4"); + auto pubKey = privKey.getPublicKey(TWPublicKeyTypeStarkex); + ASSERT_EQ(hexEncoded(pubKey.bytes), "0x035919acd61e97b3ecdc75ff8beed8d1803f7ea3cad2937926ae59cc3f8070d4"); } TEST(ImmutableX, GrindKey) { @@ -39,8 +38,8 @@ TEST(ImmutableX, GrindKey) { TEST(ImmutableX, GetPrivateKeySignature) { std::string signature = "0x21fbf0696d5e0aa2ef41a2b4ffb623bcaf070461d61cf7251c74161f82fec3a4370854bc0a34b3ab487c1bc021cd318c734c51ae29374f2beb0e6f2dd49b4bf41c"; auto data = parse_hex(signature); - auto ethSignature = Ethereum::Signer::signatureDataToStructSimple(data); - auto seed = store(ethSignature.r); + // The signature is `rsv`, where `r` starts at 0 and is 32 long. + auto seed = subData(data, 0, 32); auto result = grindKey(seed); ASSERT_EQ(result, "766f11e90cd7c7b43085b56da35c781f8c067ac0d578eabdceebc4886435bda"); } @@ -56,32 +55,23 @@ TEST(ImmutableX, GetPrivateKeyFromSignature) { } TEST(ImmutableX, GetPublicKeyFromPrivateKey) { - auto privKey = parse_hex("058ab7989d625b1a690400dcbe6e070627adedceff7bd196e58d4791026a8afe", true); - auto pubKey = hexEncoded(getPublicKeyFromPrivateKey(privKey)); - ASSERT_EQ(pubKey, "0x02a4c7332c55d6c1c510d24272d1db82878f2302f05b53bcc38695ed5f78fffd"); - - { - auto priv = PrivateKey(parse_hex("058ab7989d625b1a690400dcbe6e070627adedceff7bd196e58d4791026a8afe")); - auto pub = priv.getPublicKey(TWPublicKeyTypeStarkex); - ASSERT_EQ(hexEncoded(pub.bytes), "0x02a4c7332c55d6c1c510d24272d1db82878f2302f05b53bcc38695ed5f78fffd"); - } + auto privKeyData = parse_hex("058ab7989d625b1a690400dcbe6e070627adedceff7bd196e58d4791026a8afe", true); + PrivateKey privKey(privKeyData); + auto pubKey = privKey.getPublicKey(TWPublicKeyTypeStarkex); + auto pubKeyHex = hexEncoded(pubKey.bytes); + ASSERT_EQ(pubKeyHex, "0x02a4c7332c55d6c1c510d24272d1db82878f2302f05b53bcc38695ed5f78fffd"); } TEST(ImmutableX, SimpleSign) { - auto privKey = parse_hex("0139fe4d6f02e666e86a6f58e65060f115cd3c185bd9e98bd829636931458f79"); + auto privKeyBytes = parse_hex("0139fe4d6f02e666e86a6f58e65060f115cd3c185bd9e98bd829636931458f79"); + PrivateKey privKey(privKeyBytes); auto digest = parse_hex("06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76"); - auto signature = hex(ImmutableX::sign(privKey, digest)); + auto signature = hex(privKey.sign(digest, TWCurve::TWCurveStarkex)); auto expectedSignature = "061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9a"; ASSERT_EQ(signature.size(), 128ULL); ASSERT_EQ(signature.substr(0, 64), "061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f"); ASSERT_EQ(signature.substr(64, 64), "04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9a"); ASSERT_EQ(signature, expectedSignature); - - { - PrivateKey priv(privKey); - auto result = hex(priv.sign(digest, TWCurveStarkex)); - ASSERT_EQ(result, expectedSignature); - } } TEST(ImmutableX, VerifySign) { @@ -90,7 +80,6 @@ TEST(ImmutableX, VerifySign) { auto pubKeyData = parse_hex("02c5dbad71c92a45cc4b40573ae661f8147869a91d57b8d9b8f48c8af7f83159"); auto hash = parse_hex("06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76"); auto signature = parse_hex("061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9a"); - ASSERT_TRUE(verify(pubKeyData, signature, hash)); auto pubKey = PublicKey(pubKeyData, TWPublicKeyTypeStarkex); ASSERT_TRUE(pubKey.verify(signature, hash)); } @@ -99,7 +88,6 @@ TEST(ImmutableX, VerifySign) { auto pubKeyData = parse_hex("02c5dbad71c92a45cc4b40573ae661f8147869a91d57b8d9b8f48c8af7f83159"); auto hash = parse_hex("06fea80189363a786037ed3e7ba546dad0ef7de49fccae0e31eb658b7dd4ea76"); auto signature = parse_hex("061ec782f76a66f6984efc3a1b6d152a124c701c00abdd2bf76641b4135c770f04e44e759cea02c23568bb4d8a09929bbca8768ab68270d50c18d214166ccd9b"); - ASSERT_FALSE(verify(pubKeyData, signature, hash)); auto pubKey = PublicKey(pubKeyData, TWPublicKeyTypeStarkex); ASSERT_FALSE(pubKey.verify(signature, hash)); } diff --git a/tests/chains/NEO/SignerTests.cpp b/tests/chains/NEO/SignerTests.cpp index b5b54b97f44..643ea53a5b9 100644 --- a/tests/chains/NEO/SignerTests.cpp +++ b/tests/chains/NEO/SignerTests.cpp @@ -4,8 +4,9 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "PublicKey.h" #include "HexCoding.h" +#include "PublicKey.h" +#include "PublicKeyLegacy.h" #include "NEO/Address.h" #include "NEO/Signer.h" @@ -51,7 +52,8 @@ TEST(NEOAccount, validity) { } TEST(NEOSigner, SigningTransaction) { - auto signer = Signer(PrivateKey(parse_hex("F18B2F726000E86B4950EBEA7BFF151F69635951BC4A31C44F28EE6AF7AEC128"))); + auto privateKey = PrivateKey(parse_hex("F18B2F726000E86B4950EBEA7BFF151F69635951BC4A31C44F28EE6AF7AEC128")); + auto signer = Signer(privateKey); auto transaction = Transaction(); transaction.type = TransactionType::TT_ContractTransaction; transaction.version = 0x00; @@ -78,9 +80,12 @@ TEST(NEOSigner, SigningTransaction) { out.scriptHash = load(scriptHash); transaction.outputs.push_back(out); } + signer.sign(transaction); auto signedTx = transaction.serialize(); EXPECT_EQ(hex(signedTx), "800000019c85b39cd5677e2bfd6bf8a711e8da93a2f1d172b2a52c6ca87757a4bccc24de0100029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500e1f50500000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500fcbbc414000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac0141405046619c8e20e1fdeec92ce95f3019f6e7cc057294eb16b2d5e55c105bf32eb27e1fc01c1858576228f1fef8c0945a8ad69688e52a4ed19f5b85f5eff7e961d7232102a41c2aea8568864b106553729d32b1317ec463aa23e7a3521455d95992e17a7aac"); + // TODO uncomment when nist256p1 Rust implementation is enabled. + // EXPECT_EQ(hex(signedTx), "800000019c85b39cd5677e2bfd6bf8a711e8da93a2f1d172b2a52c6ca87757a4bccc24de0100029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500e1f50500000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500fcbbc414000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac0141405046619c8e20e1fdeec92ce95f3019f6e7cc057294eb16b2d5e55c105bf32eb281e03fe2e7a7a89ed70e01073f6ba574e65071c87cc8cce59833d4d30479c37a232102a41c2aea8568864b106553729d32b1317ec463aa23e7a3521455d95992e17a7aac"); } } // namespace TW::NEO::tests diff --git a/tests/chains/NEO/TWAnySignerTests.cpp b/tests/chains/NEO/TWAnySignerTests.cpp index 039940c5e49..3f6770b6fb2 100644 --- a/tests/chains/NEO/TWAnySignerTests.cpp +++ b/tests/chains/NEO/TWAnySignerTests.cpp @@ -4,6 +4,8 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +#include "PrivateKey.h" +#include "PublicKeyLegacy.h" #include "TestUtilities.h" #include #include "HexCoding.h" @@ -13,12 +15,14 @@ namespace TW::NEO::tests { +const std::string SECRET = "F18B2F726000E86B4950EBEA7BFF151F69635951BC4A31C44F28EE6AF7AEC128"; + Proto::SigningInput createInput() { const std::string NEO_ASSET_ID = "9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5"; const std::string GAS_ASSET_ID = "e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c60"; Proto::SigningInput input; - auto privateKey = parse_hex("F18B2F726000E86B4950EBEA7BFF151F69635951BC4A31C44F28EE6AF7AEC128"); + auto privateKey = parse_hex(SECRET); input.set_private_key(privateKey.data(), privateKey.size()); input.set_fee(12345); // too low input.set_gas_asset_id(GAS_ASSET_ID); @@ -165,6 +169,8 @@ TEST(TWAnySignerNEO, Sign) { // https://testnet-explorer.o3.network/transactions/0x7b138c753c24f474d0f70af30a9d79756e0ee9c1f38c12ed07fbdf6fc5132eaf ASSERT_EQ(hex(output.encoded()), "8000001efb50cb3be3e08917b308a1dbdb2408109394560ec67518af43035d8c260815c601000bd791a26120eef181d8162bd6cb7495dee1299aa67bb796dcd4a03769f9b24e00000bea299e6a243c9379c3e8884c9176b1456b3017611772b2fadc55d10901ee3f000026c413526bbd45cca355683db9f39d6864a7e298f481f2cdeefe8b578ccea96e00002b2647616d4f4143700f8e862aa8427efd7fa9998fe040e23ed877d2cbd35af700003159b899275e2f0e0b1314acddc7e1ec5948598fca40a9733e2b448fe9344705000036509c8a487005aa8e16663613d2d767461ee2f8dc4f678cc22f9148d4420c8b0000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040100385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040200385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040300385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040400385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040500385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040600385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040700385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040800385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f04090040ec871088beb680f5b149767dbb0b8ac7ec1a1c5836e177606b6200e6bc83cf00004e393bd89d886ae116ed9e6b49be427b21f7247d896923265e68dfa82b57d79b00005b99bf2caacf20bfc9cd51b3d3472499383c803c2d781d00f1e2dd970325eeb4000062ac42685ef8b856291bb0264fcb767b00706a15243326775f61a159a29c01e100006f011d435ef43c066689d1222f4eada1d4590ebaaa190960ac26a5acf29d37bd00007dea63ea47a6c9e8318f3b19a0df5ccb3a348f54a176736afa7b9b3b843f4c160000925e50254e8056bfd540f3d45f171dbab504f5b191070ee7af1e16764ac7ce4a00009677a6869128961a1a3b17e609e915d2d9a29ceaab5689dccb841ca729665c8900009692e4e512eb2e04b10042bcc28910140b2229ede40291b0e1a0c3c44381825400009dc6c119d0f4bacb1b1e9faffcba33581729c1915a2f1147ce7a6fc8abe4455300009f6b635afee02b5db0c93a5b1bfcace34a18c78d76c73b7bf90d21d4d0193ec80000b11bbb613e36b2bcc6c3a76c888c6c139957a1b7091dab26ce88b65c3fb056340000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040a00039b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500ba1dd205000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50083064905000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ace72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c605013cf0617000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac014140dc261ac093a87640441bf0c3ad4a55ec727932b9175f600618bb5275f31aacf122956bc88746dc666759a2d67f120fe3ce1659f916d22a91e0b02421d3bddbd1232102a41c2aea8568864b106553729d32b1317ec463aa23e7a3521455d95992e17a7aac"); + // TODO uncomment when nist256p1 Rust implementation is enabled. + // ASSERT_EQ(hex(output.encoded()), "8000001efb50cb3be3e08917b308a1dbdb2408109394560ec67518af43035d8c260815c601000bd791a26120eef181d8162bd6cb7495dee1299aa67bb796dcd4a03769f9b24e00000bea299e6a243c9379c3e8884c9176b1456b3017611772b2fadc55d10901ee3f000026c413526bbd45cca355683db9f39d6864a7e298f481f2cdeefe8b578ccea96e00002b2647616d4f4143700f8e862aa8427efd7fa9998fe040e23ed877d2cbd35af700003159b899275e2f0e0b1314acddc7e1ec5948598fca40a9733e2b448fe9344705000036509c8a487005aa8e16663613d2d767461ee2f8dc4f678cc22f9148d4420c8b0000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040100385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040200385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040300385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040400385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040500385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040600385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040700385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040800385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f04090040ec871088beb680f5b149767dbb0b8ac7ec1a1c5836e177606b6200e6bc83cf00004e393bd89d886ae116ed9e6b49be427b21f7247d896923265e68dfa82b57d79b00005b99bf2caacf20bfc9cd51b3d3472499383c803c2d781d00f1e2dd970325eeb4000062ac42685ef8b856291bb0264fcb767b00706a15243326775f61a159a29c01e100006f011d435ef43c066689d1222f4eada1d4590ebaaa190960ac26a5acf29d37bd00007dea63ea47a6c9e8318f3b19a0df5ccb3a348f54a176736afa7b9b3b843f4c160000925e50254e8056bfd540f3d45f171dbab504f5b191070ee7af1e16764ac7ce4a00009677a6869128961a1a3b17e609e915d2d9a29ceaab5689dccb841ca729665c8900009692e4e512eb2e04b10042bcc28910140b2229ede40291b0e1a0c3c44381825400009dc6c119d0f4bacb1b1e9faffcba33581729c1915a2f1147ce7a6fc8abe4455300009f6b635afee02b5db0c93a5b1bfcace34a18c78d76c73b7bf90d21d4d0193ec80000b11bbb613e36b2bcc6c3a76c888c6c139957a1b7091dab26ce88b65c3fb056340000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040a00039b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500ba1dd205000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50083064905000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ace72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c605013cf0617000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac014140dc261ac093a87640441bf0c3ad4a55ec727932b9175f600618bb5275f31aacf1dd6a943678b9239a98a65d2980edf01beed0a0b4904573f31309a6a128a54980232102a41c2aea8568864b106553729d32b1317ec463aa23e7a3521455d95992e17a7aac"); } TEST(TWAnySignerNEO, Plan) { @@ -184,6 +190,8 @@ TEST(TWAnySignerNEO, SignMultiOutput) { ANY_SIGN(input, TWCoinTypeNEO); ASSERT_EQ(hex(output.encoded()), "80000023fb50cb3be3e08917b308a1dbdb2408109394560ec67518af43035d8c260815c601000bd791a26120eef181d8162bd6cb7495dee1299aa67bb796dcd4a03769f9b24e00000bea299e6a243c9379c3e8884c9176b1456b3017611772b2fadc55d10901ee3f000026c413526bbd45cca355683db9f39d6864a7e298f481f2cdeefe8b578ccea96e00002b2647616d4f4143700f8e862aa8427efd7fa9998fe040e23ed877d2cbd35af700003159b899275e2f0e0b1314acddc7e1ec5948598fca40a9733e2b448fe9344705000036509c8a487005aa8e16663613d2d767461ee2f8dc4f678cc22f9148d4420c8b0000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040100385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040200385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040300385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040400385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040500385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040600385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040700385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040800385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f04090040ec871088beb680f5b149767dbb0b8ac7ec1a1c5836e177606b6200e6bc83cf00004e393bd89d886ae116ed9e6b49be427b21f7247d896923265e68dfa82b57d79b00005b99bf2caacf20bfc9cd51b3d3472499383c803c2d781d00f1e2dd970325eeb4000062ac42685ef8b856291bb0264fcb767b00706a15243326775f61a159a29c01e100006f011d435ef43c066689d1222f4eada1d4590ebaaa190960ac26a5acf29d37bd00007dea63ea47a6c9e8318f3b19a0df5ccb3a348f54a176736afa7b9b3b843f4c160000925e50254e8056bfd540f3d45f171dbab504f5b191070ee7af1e16764ac7ce4a00009677a6869128961a1a3b17e609e915d2d9a29ceaab5689dccb841ca729665c8900009692e4e512eb2e04b10042bcc28910140b2229ede40291b0e1a0c3c44381825400009dc6c119d0f4bacb1b1e9faffcba33581729c1915a2f1147ce7a6fc8abe4455300009f6b635afee02b5db0c93a5b1bfcace34a18c78d76c73b7bf90d21d4d0193ec80000b11bbb613e36b2bcc6c3a76c888c6c139957a1b7091dab26ce88b65c3fb056340000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040a00e05bdf39f3b8c377a9801cb3eaa38eefe8abdd176642d5f2aab3b8217588a7e50000ebf6778b3fc3523ebc0acdbdeb29202135737ac0ac47cd814da0d63cdde955140000eeadb902cc55ac4954c1f9551ae6695c2364e0dfd0013857b5115208601271da0000ef7b431058f36524a668a7497c88ad45cc8b2c5b20891ad53d1071d3fe6c48040000f49e21b21a0c87edad0d96673223f47ad3560490613510650edb42a45570f2a50000049b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500ba1dd205000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500e1f50500000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500c2eb0b00000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5e069f3d21c000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac01414057b786872d667a637ff44412e3ccf50933a9c30609016ecbcaec8b9d80f2b0e26eb0cf111674ff0802a42357671867b11c334807c40146419825eed8c45a6eed232102a41c2aea8568864b106553729d32b1317ec463aa23e7a3521455d95992e17a7aac"); + // TODO uncomment when nist256p1 Rust implementation is enabled. + // ASSERT_EQ(hex(output.encoded()), "80000023fb50cb3be3e08917b308a1dbdb2408109394560ec67518af43035d8c260815c601000bd791a26120eef181d8162bd6cb7495dee1299aa67bb796dcd4a03769f9b24e00000bea299e6a243c9379c3e8884c9176b1456b3017611772b2fadc55d10901ee3f000026c413526bbd45cca355683db9f39d6864a7e298f481f2cdeefe8b578ccea96e00002b2647616d4f4143700f8e862aa8427efd7fa9998fe040e23ed877d2cbd35af700003159b899275e2f0e0b1314acddc7e1ec5948598fca40a9733e2b448fe9344705000036509c8a487005aa8e16663613d2d767461ee2f8dc4f678cc22f9148d4420c8b0000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040100385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040200385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040300385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040400385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040500385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040600385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040700385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040800385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f04090040ec871088beb680f5b149767dbb0b8ac7ec1a1c5836e177606b6200e6bc83cf00004e393bd89d886ae116ed9e6b49be427b21f7247d896923265e68dfa82b57d79b00005b99bf2caacf20bfc9cd51b3d3472499383c803c2d781d00f1e2dd970325eeb4000062ac42685ef8b856291bb0264fcb767b00706a15243326775f61a159a29c01e100006f011d435ef43c066689d1222f4eada1d4590ebaaa190960ac26a5acf29d37bd00007dea63ea47a6c9e8318f3b19a0df5ccb3a348f54a176736afa7b9b3b843f4c160000925e50254e8056bfd540f3d45f171dbab504f5b191070ee7af1e16764ac7ce4a00009677a6869128961a1a3b17e609e915d2d9a29ceaab5689dccb841ca729665c8900009692e4e512eb2e04b10042bcc28910140b2229ede40291b0e1a0c3c44381825400009dc6c119d0f4bacb1b1e9faffcba33581729c1915a2f1147ce7a6fc8abe4455300009f6b635afee02b5db0c93a5b1bfcace34a18c78d76c73b7bf90d21d4d0193ec80000b11bbb613e36b2bcc6c3a76c888c6c139957a1b7091dab26ce88b65c3fb056340000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040a00e05bdf39f3b8c377a9801cb3eaa38eefe8abdd176642d5f2aab3b8217588a7e50000ebf6778b3fc3523ebc0acdbdeb29202135737ac0ac47cd814da0d63cdde955140000eeadb902cc55ac4954c1f9551ae6695c2364e0dfd0013857b5115208601271da0000ef7b431058f36524a668a7497c88ad45cc8b2c5b20891ad53d1071d3fe6c48040000f49e21b21a0c87edad0d96673223f47ad3560490613510650edb42a45570f2a50000049b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500ba1dd205000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500e1f50500000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500c2eb0b00000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5e069f3d21c000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac01414057b786872d667a637ff44412e3ccf50933a9c30609016ecbcaec8b9d80f2b0e2914f30ede98b00f8fd5bdca898e7984ea0b3b2a5e31658435b93dbea3808b664232102a41c2aea8568864b106553729d32b1317ec463aa23e7a3521455d95992e17a7aac"); } TEST(TWAnySignerNEO, PlanMultiOutput) { diff --git a/tests/chains/NEO/TransactionCompilerTests.cpp b/tests/chains/NEO/TransactionCompilerTests.cpp index de7339cf848..8ca243df57e 100644 --- a/tests/chains/NEO/TransactionCompilerTests.cpp +++ b/tests/chains/NEO/TransactionCompilerTests.cpp @@ -75,6 +75,10 @@ TEST(NEOCompiler, CompileWithSignatures) { const auto signature = parse_hex("5046619c8e20e1fdeec92ce95f3019f6e7cc057294eb16b2d5e55c105bf32eb27e1fc01c18585762" "28f1fef8c0945a8ad69688e52a4ed19f5b85f5eff7e961d7"); + // TODO uncomment when nist256p1 Rust implementation is enabled. + // const auto signature = + // parse_hex("5046619c8e20e1fdeec92ce95f3019f6e7cc057294eb16b2d5e55c105bf32eb281e03fe2e7a7a89e" + // "d70e01073f6ba574e65071c87cc8cce59833d4d30479c37a"); // Verify signature (pubkey & hash & signature) EXPECT_TRUE(publicKey.verify(signature, TW::data(preImageHash))); @@ -87,6 +91,14 @@ TEST(NEOCompiler, CompileWithSignatures) { "14000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac0141405046619c8e20e1fdeec92ce95f3019f6e7cc" "057294eb16b2d5e55c105bf32eb27e1fc01c1858576228f1fef8c0945a8ad69688e52a4ed19f5b85f5eff7e961" "d7232102a41c2aea8568864b106553729d32b1317ec463aa23e7a3521455d95992e17a7aac"; + // TODO uncomment when nist256p1 Rust implementation is enabled. + // auto expectedTx = + // "800000019c85b39cd5677e2bfd6bf8a711e8da93a2f1d172b2a52c6ca87757a4bccc24de0100029b7cffdaa674" + // "beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500e1f50500000000ea610aa6db39bd8c8556c9" + // "569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500fcbbc4" + // "14000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac0141405046619c8e20e1fdeec92ce95f3019f6e7cc" + // "057294eb16b2d5e55c105bf32eb281e03fe2e7a7a89ed70e01073f6ba574e65071c87cc8cce59833d4d30479c3" + // "7a232102a41c2aea8568864b106553729d32b1317ec463aa23e7a3521455d95992e17a7aac"; auto outputData = TransactionCompiler::compileWithSignatures(coin, inputStrData, {signature}, {publicKeyData}); diff --git a/tests/chains/Ontology/Oep4Tests.cpp b/tests/chains/Ontology/Oep4Tests.cpp index 41984c60e49..a0cb87f8d90 100644 --- a/tests/chains/Ontology/Oep4Tests.cpp +++ b/tests/chains/Ontology/Oep4Tests.cpp @@ -87,11 +87,11 @@ TEST(OntologyOep4, addressHack) { } TEST(OntologyOep4, transfer) { - auto from = Signer( - PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464652"))); + PrivateKey fromPrivate(parse_hex("4646464646464646464646464646464646464646464646464646464646464652")); + Signer from(fromPrivate); - auto payer = Signer( - PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646"))); + PrivateKey payerPrivate(parse_hex("4646464646464646464646464646464646464646464646464646464646464646")); + Signer payer(payerPrivate); auto toAddress = Address("AVY6LfvxauVQAVHDV9hC3ZCv7cQqzfDotH"); @@ -105,10 +105,13 @@ TEST(OntologyOep4, transfer) { Oep4 wing(wing_addr); auto tx = wing.transfer(from, toAddress, amount, payer, gasPrice, gasLimit, nonce); - auto rawTx = hex(tx.serialize()); + auto rawTxBytes = tx.serialize(); + auto rawTx = hex(rawTxBytes); // Transaction Hex tab // https://explorer.ont.io/testnet/tx/710266b8d497e794ecd47e01e269e4aeb6f4ff2b01eaeafc4cd371e062b13757 EXPECT_EQ("00d134120000c40900000000000050c3000000000000fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a4d02e9001496f688657b95be51c11a87b51adfda4ab69e9cbb1457e9d1a61f9aafa798b6c7fbeae35639681d7df653c1087472616e736665726733def739225d0f93dd2aed457d7b1fd074ec31ff00024140bd2923854d7b84b97a107bb3cddf18c8e3dddd2f36b41a1f5f5b23366484daa22871cfb819923fe01e9cb1e9ed16baa2b05c2feb76bcbe2ec125f72701c5e965232103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac41406d638653597774ce45812ea2653250806b657b32b7c6ad3e027ddeba91e9a9da4bb5dacd23dafba868cb31bacb38b4a6ff2607682a426c1dc09b05a1e158d6cd2321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac", rawTx); + // TODO uncomment when nist256p1 Rust implementation is enabled. + // EXPECT_EQ(rawTx, "00d134120000c40900000000000050c3000000000000fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a4d02e9001496f688657b95be51c11a87b51adfda4ab69e9cbb1457e9d1a61f9aafa798b6c7fbeae35639681d7df653c1087472616e736665726733def739225d0f93dd2aed457d7b1fd074ec31ff00024140bd2923854d7b84b97a107bb3cddf18c8e3dddd2f36b41a1f5f5b23366484daa2d78e3046e66dc020e1634e1612e9455d0c8acac2305ae0563293d39bfa9d3bec232103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac41406d638653597774ce45812ea2653250806b657b32b7c6ad3e027ddeba91e9a9dab44a2531dc2504589734ce4534c74b58bdc0f3457cd53267331ec5211b0a4e842321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac"); } TEST(OntologyOep4, transferMainnet) { diff --git a/tests/chains/Ontology/OngTests.cpp b/tests/chains/Ontology/OngTests.cpp index a140e670d00..fcea1ec34e6 100644 --- a/tests/chains/Ontology/OngTests.cpp +++ b/tests/chains/Ontology/OngTests.cpp @@ -35,15 +35,20 @@ TEST(OntologyOng, balanceOf) { } TEST(OntologyOng, transfer) { - auto signer1 = Signer( - PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646"))); - auto signer2 = Signer( - PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464652"))); - auto toAddress = Address("Af1n2cZHhMZumNqKgw9sfCNoTWu9de4NDn"); + PrivateKey privateKey1(parse_hex("4646464646464646464646464646464646464646464646464646464646464646")); + Signer signer1(privateKey1); + + PrivateKey privateKey2(parse_hex("4646464646464646464646464646464646464646464646464646464646464652")); + Signer signer2(privateKey2); + + Address toAddress("Af1n2cZHhMZumNqKgw9sfCNoTWu9de4NDn"); uint32_t nonce = 0; uint64_t amount = 1, gasPrice = 500, gasLimit = 20000; + auto tx = Ong().transfer(signer1, toAddress, amount, signer2, gasPrice, gasLimit, nonce); - auto rawTx = hex(tx.serialize()); + auto rawTx = tx.serialize(); + auto rawTxHex = hex(rawTx); + EXPECT_EQ("00d100000000f401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df6" "7100c66b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc814feec06b79ed299ea06fcb94aba" "c41aaf3ead76586a7cc8516a7cc86c51c1087472616e7366657214000000000000000000000000000000" @@ -53,19 +58,35 @@ TEST(OntologyOng, transfer) { "47125f927b7486ac41406fea9f12b125d7f65a94774e765a796428b3c6c4c46b0470624b9a1cef4ff420" "488828f308c263b35287363e51add8cd49136eb57a397c6ade95df80d9a16282232103d9fd62df332403" "d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", - rawTx); + rawTxHex); + + // TODO uncomment when nist256p1 Rust implementation is enabled. + // EXPECT_EQ("00d100000000f401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df6" + // "7100c66b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc814feec06b79ed299ea06fcb94aba" + // "c41aaf3ead76586a7cc8516a7cc86c51c1087472616e7366657214000000000000000000000000000000" + // "00000000020068164f6e746f6c6f67792e4e61746976652e496e766f6b6500024140ac3edf2d00540f9c" + // "2f3b24878936b409c995c425ab5edf247c5b0d812a50df29c009c1e7c4538e5a32f88d0087bea3b91082" + // "0487db572e9be6ebddc953200b8d2321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e4" + // "47125f927b7486ac41406fea9f12b125d7f65a94774e765a796428b3c6c4c46b0470624b9a1cef4ff420" + // "b777d70bf73d9c4dad78c9c1ae52273273d38bf82cde221a1523eb4222c1c2cf232103d9fd62df332403" + // "d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", + // rawTxHex); } TEST(OntologyOng, withdraw) { - auto signer1 = Signer( - PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646"))); - auto signer2 = Signer( - PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464652"))); + PrivateKey privateKey1(parse_hex("4646464646464646464646464646464646464646464646464646464646464646")); + Signer signer1(privateKey1); + + PrivateKey privateKey2(parse_hex("4646464646464646464646464646464646464646464646464646464646464652")); + Signer signer2(privateKey2); + uint32_t nonce = 0; uint64_t amount = 1, gasPrice = 500, gasLimit = 20000; auto tx = Ong().withdraw(signer1, signer1.getAddress(), amount, signer2, gasPrice, gasLimit, nonce); - auto rawTx = hex(tx.serialize()); + auto rawTx = tx.serialize(); + auto rawTxHex = hex(rawTx); + EXPECT_EQ( "00d100000000f401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df68b00c6" "6b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc81400000000000000000000000000000000000000" @@ -76,7 +97,20 @@ TEST(OntologyOng, withdraw) { "848d0ed92f1203e447125f927b7486ac41406413b060329e133cd13709c361ccd90b3944477cf3937f1459313f" "0ea6435f6f2b1335192a5d1b346fd431e8af912bfa4e1a23ad7d0ab7fc5b808655af5c9043232103d9fd62df33" "2403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", - rawTx); + rawTxHex); + + // TODO uncomment when nist256p1 Rust implementation is enabled. + // EXPECT_EQ( + // "00d100000000f401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df68b00c6" + // "6b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc81400000000000000000000000000000000000000" + // "016a7cc814fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc8516a7cc86c0c7472616e7366657246726f" + // "6d1400000000000000000000000000000000000000020068164f6e746f6c6f67792e4e61746976652e496e766f" + // "6b6500024140b8b859055c744a89ef4d4f6ae7a58e0a99fef2eb0f6cf09d740b56cf4c7c14ab9b1ff3d62164e0" + // "d86de3429d1943292b6a3b623bae21cc5c6ba6cb90cd8030a22321031bec1250aa8f78275f99a6663688f31085" + // "848d0ed92f1203e447125f927b7486ac41406413b060329e133cd13709c361ccd90b3944477cf3937f1459313f" + // "0ea6435f6f2b1335192a5d1b346fd431e8af912bfa4e1a23ad7d0ab7fc5b808655af5c9043232103d9fd62df33" + // "2403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", + // rawTxHex); } } // namespace TW::Ontology::tests diff --git a/tests/chains/Ontology/OntTests.cpp b/tests/chains/Ontology/OntTests.cpp index 310c6f75319..a645eb49708 100644 --- a/tests/chains/Ontology/OntTests.cpp +++ b/tests/chains/Ontology/OntTests.cpp @@ -5,12 +5,9 @@ // file LICENSE at the root of the source code distribution tree. #include "HexCoding.h" - #include "Ontology/Ont.h" #include - -#include #include namespace TW::Ontology::tests { @@ -37,15 +34,19 @@ TEST(OntologyOnt, queryBalance) { } TEST(OntologyOnt, transfer) { - auto signer1 = Signer( - PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646"))); - auto signer2 = Signer( - PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464652"))); + PrivateKey privateKey1(parse_hex("4646464646464646464646464646464646464646464646464646464646464646")); + Signer signer1(privateKey1); + + PrivateKey privateKey2(parse_hex("4646464646464646464646464646464646464646464646464646464646464652")); + Signer signer2(privateKey2); + auto toAddress = Address("Af1n2cZHhMZumNqKgw9sfCNoTWu9de4NDn"); uint32_t nonce = 0; uint64_t amount = 1, gasPrice = 500, gasLimit = 20000; auto tx = Ont().transfer(signer1, toAddress, amount, signer2, gasPrice, gasLimit, nonce); - auto rawTx = hex(tx.serialize()); + auto rawTx = tx.serialize(); + auto rawTxHex = hex(rawTx); + EXPECT_EQ("00d100000000f401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df6" "7100c66b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc814feec06b79ed299ea06fcb94aba" "c41aaf3ead76586a7cc8516a7cc86c51c1087472616e7366657214000000000000000000000000000000" @@ -55,7 +56,60 @@ TEST(OntologyOnt, transfer) { "47125f927b7486ac4140bcc6df81d7f2f3143f152c446643ac5bf7910ef90046be8c89818264a11d360d" "0576d7b092fabafd0913a67ccf8b2f8e3d2bd708f768c2bb67e2d2f759805608232103d9fd62df332403" "d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", - rawTx); + rawTxHex); + + // TODO uncomment when nist256p1 Rust implementation is enabled. + // EXPECT_EQ("00d100000000f401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df6" + // "7100c66b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc814feec06b79ed299ea06fcb94aba" + // "c41aaf3ead76586a7cc8516a7cc86c51c1087472616e7366657214000000000000000000000000000000" + // "00000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b65000241407531e7d5bb9ae138" + // "862585a65c26d624f1a7a61011298809d9ed9cf60d10a450bf9821152ab657ca4b7f3b1b76fb1d7021a4" + // "1d4e05d427b941caa2e9ca783afc2321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e4" + // "47125f927b7486ac4140bcc6df81d7f2f3143f152c446643ac5bf7910ef90046be8c89818264a11d360d" + // "fa89284e6d054503f6ec59833074d0717fbb23a4afaedbc98bd6f7cba2e2cf49232103d9fd62df332403" + // "d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", + // rawTxHex); } +// Successfully broadcasted: https://explorer.ont.io/tx/785af64758886099b995e2aed914c56b15ab63c6e0c5acf42b66f3bbc3e95f98 +// TEST(OntologyOnt, transferUpdatedSign) { +// PrivateKey privateKey1(parse_hex("3b2bca95860af1baf5ef55f60167ef59db098b5871617c889f42dee4ffcb0c6f")); +// Signer signer1(privateKey1); +// +// PrivateKey privateKey2(parse_hex("3b2bca95860af1baf5ef55f60167ef59db098b5871617c889f42dee4ffcb0c6f")); +// Signer signer2(privateKey2); +// +// auto toAddress = Address("AUyL4TZ1zFEcSKDJrjFnD7vsq5iFZMZqT7"); +// uint32_t nonce = 2760697417; +// +// uint64_t amount = 1, gasPrice = 2500, gasLimit = 20000; +// auto tx = Ont().transfer(signer1, toAddress, amount, signer2, gasPrice, gasLimit, nonce); +// auto rawTx = tx.serialize(); +// auto rawTxHex = hex(rawTx); +// +// // The transaction hex signed by using Rust implementation: +// EXPECT_EQ("00d149e68ca4c409000000000000204e0000000000007ae1b6c6e44a518f8bcffc44e75236c7a2a8f3c2" +// "7100c66b147ae1b6c6e44a518f8bcffc44e75236c7a2a8f3c26a7cc81490c44262e0c9740f7b3c0e3d04" +// "6c106901c72cc46a7cc8516a7cc86c51c1087472616e7366657214000000000000000000000000000000" +// "00000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b6500024140362bc7dd7d005b47" +// "464a2a2f84edd146e993267ceefb4174ef30d0cba0a1aa42bcd043b2349ba7030c2a3d7700d6653cc921" +// "d17a9f8942d46a93e84ae7968ed223210332aa8304797de2817ca65ce25aede0b176c3c5a61a20caffaf" +// "2df7fb6bb0b1b4ac4140362bc7dd7d005b47464a2a2f84edd146e993267ceefb4174ef30d0cba0a1aa42" +// "bcd043b2349ba7030c2a3d7700d6653cc921d17a9f8942d46a93e84ae7968ed223210332aa8304797de2" +// "817ca65ce25aede0b176c3c5a61a20caffaf2df7fb6bb0b1b4ac", +// rawTxHex); +// +// // The transaction hex signed by using `trezor-crypto`: +// EXPECT_NE("00d149e68ca4c409000000000000204e0000000000007ae1b6c6e44a518f8bcffc44e75236c7a2a8f3c2" +// "7100c66b147ae1b6c6e44a518f8bcffc44e75236c7a2a8f3c26a7cc81490c44262e0c9740f7b3c0e3d04" +// "6c106901c72cc46a7cc8516a7cc86c51c1087472616e7366657214000000000000000000000000000000" +// "00000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b6500024140362bc7dd7d005b47" +// "464a2a2f84edd146e993267ceefb4174ef30d0cba0a1aa42432fbc4ccb6458fdf3d5c288ff299ac2f3c5" +// "2933078e5bb08925e27814cc967f23210332aa8304797de2817ca65ce25aede0b176c3c5a61a20caffaf" +// "2df7fb6bb0b1b4ac4140362bc7dd7d005b47464a2a2f84edd146e993267ceefb4174ef30d0cba0a1aa42" +// "432fbc4ccb6458fdf3d5c288ff299ac2f3c52933078e5bb08925e27814cc967f23210332aa8304797de2" +// "817ca65ce25aede0b176c3c5a61a20caffaf2df7fb6bb0b1b4ac", +// rawTxHex); +// } + } // namespace TW::Ontology::tests diff --git a/tests/chains/Ontology/TWAnySignerTests.cpp b/tests/chains/Ontology/TWAnySignerTests.cpp index 23569706dbd..64558fc5be3 100644 --- a/tests/chains/Ontology/TWAnySignerTests.cpp +++ b/tests/chains/Ontology/TWAnySignerTests.cpp @@ -87,6 +87,18 @@ TEST(TWAnySingerOntology, OntTransfer) { "305a4cd3bb6ea2fe80fd293abb3c592e679c42c546cbf3baa051a07b28b374a6232103d9fd62df332403" "d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", hex(output.encoded())); + + // TODO uncomment when nist256p1 Rust implementation is enabled. + // EXPECT_EQ("00d102d45c8bf401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df6" + // "7100c66b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc814feec06b79ed299ea06fcb94aba" + // "c41aaf3ead76586a7cc8516a7cc86c51c1087472616e7366657214000000000000000000000000000000" + // "00000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b6500024140301766d925382a6e" + // "bb2ebeb18d3741954c9370dcf6d9c45b34ce7b18bc42dcdb8300d7215080efb87dd3f35de5f3b6d98aac" + // "d6161fbc0845b82d0d8be4b8b6d52321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e4" + // "47125f927b7486ac414038466b25ac49a22ba8c301328ef049a61711b257987e85e25d63e0444a14e860" + // "305a4cd3bb6ea2fe80fd293abb3c592e679c42c546cbf3baa051a07b28b374a6232103d9fd62df332403" + // "d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", + // hex(output.encoded())); } TEST(TWAnySingerOntology, OngDecimals) { @@ -157,6 +169,18 @@ TEST(TWAnySingerOntology, OngTransfer) { "3b078bd4e21bb4404c0182a32ee05260e22454dffb34dacccf458dfbee6d32db232103d9fd62df332403" "d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", hex(output.encoded())); + + // TODO uncomment when nist256p1 Rust implementation is enabled. + // EXPECT_EQ("00d19d3182a8f401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df6" + // "7100c66b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc814feec06b79ed299ea06fcb94aba" + // "c41aaf3ead76586a7cc8516a7cc86c51c1087472616e7366657214000000000000000000000000000000" + // "00000000020068164f6e746f6c6f67792e4e61746976652e496e766f6b6500024140e27e935b87855efa" + // "d62bb76b21c7b591f445f867eff86f888ca6ee1870ecd80f8c4754e565b28a85b384612b93b007301438" + // "00049b97e83c95844a8eb7d66adc2321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e4" + // "47125f927b7486ac4140450047b2efb384129a16ec4c707790e9379b978cc7085170071d8d7c5c037d74" + // "c4f8742a1de44bc0b3fe7d5cd11fad9edac2a5cdabe2c3b824743cc70df5f276232103d9fd62df332403" + // "d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", + // hex(output.encoded())); } TEST(TWAnySingerOntology, OngWithdraw) { @@ -234,7 +258,13 @@ TEST(TWAnySingerOntology, Oep4Transfer) { Proto::SigningOutput output; ANY_SIGN(input, TWCoinTypeOntology); + auto rawTx = data(output.encoded()); + auto rawTxHex = hex(rawTx); + EXPECT_EQ("00d134120000c40900000000000050c3000000000000fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a4d02e9001496f688657b95be51c11a87b51adfda4ab69e9cbb1457e9d1a61f9aafa798b6c7fbeae35639681d7df653c1087472616e736665726733def739225d0f93dd2aed457d7b1fd074ec31ff00024140bd2923854d7b84b97a107bb3cddf18c8e3dddd2f36b41a1f5f5b23366484daa22871cfb819923fe01e9cb1e9ed16baa2b05c2feb76bcbe2ec125f72701c5e965232103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac41406d638653597774ce45812ea2653250806b657b32b7c6ad3e027ddeba91e9a9da4bb5dacd23dafba868cb31bacb38b4a6ff2607682a426c1dc09b05a1e158d6cd2321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac", hex(output.encoded())); + + // TODO uncomment when nist256p1 Rust implementation is enabled. + // EXPECT_EQ("00d134120000c40900000000000050c3000000000000fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a4d02e9001496f688657b95be51c11a87b51adfda4ab69e9cbb1457e9d1a61f9aafa798b6c7fbeae35639681d7df653c1087472616e736665726733def739225d0f93dd2aed457d7b1fd074ec31ff00024140bd2923854d7b84b97a107bb3cddf18c8e3dddd2f36b41a1f5f5b23366484daa2d78e3046e66dc020e1634e1612e9455d0c8acac2305ae0563293d39bfa9d3bec232103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac41406d638653597774ce45812ea2653250806b657b32b7c6ad3e027ddeba91e9a9dab44a2531dc2504589734ce4534c74b58bdc0f3457cd53267331ec5211b0a4e842321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac", rawTxHex); } TEST(TWAnySingerOntology, Oep4TokenBalanceOf) { diff --git a/tests/chains/Ronin/AddressTests.cpp b/tests/chains/Ronin/AddressTests.cpp deleted file mode 100644 index bc2c439fb21..00000000000 --- a/tests/chains/Ronin/AddressTests.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright © 2017-2022 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Ronin/Address.h" - -#include - -namespace TW::Ronin::tests { - -TEST(RoninAddress, Create) { - EXPECT_ANY_THROW(new TW::Ronin::Address("")); -} - -} // namespace TW::Ronin::tests diff --git a/tests/chains/Ronin/TWAnyAddressTests.cpp b/tests/chains/Ronin/TWAnyAddressTests.cpp index f8556a443af..7bb11ed3560 100644 --- a/tests/chains/Ronin/TWAnyAddressTests.cpp +++ b/tests/chains/Ronin/TWAnyAddressTests.cpp @@ -9,7 +9,6 @@ #include #include -#include "Ronin/Address.h" #include "Ronin/Entry.h" #include diff --git a/tests/chains/Theta/SignerTests.cpp b/tests/chains/Theta/SignerTests.cpp index ebb037b5111..6fd92992a84 100644 --- a/tests/chains/Theta/SignerTests.cpp +++ b/tests/chains/Theta/SignerTests.cpp @@ -26,7 +26,7 @@ TEST(Signer, Sign) { ASSERT_EQ(hex(signature), "5190868498d587d074d57298f41853d0109d997f15ddf617f471eb8cbb7fff267cb8" "fe9134ccdef053ec7cabd18070325c9c436efe1abbacd14eb7561d3fc10501"); - ASSERT_EQ(hex(transaction.encode()), + ASSERT_EQ(hex(transaction.encodePayload()), "02f887c78085e8d4a51000f863f861942e833968e5bb786ae419c4d13189fb081cc43babc70a85e8d4a5" "101401b8415190868498d587d074d57298f41853d0109d997f15ddf617f471eb8cbb7fff267cb8fe9134" "ccdef053ec7cabd18070325c9c436efe1abbacd14eb7561d3fc10501d9d8949f1233798e905e17356007" diff --git a/tests/chains/Theta/TransactionTests.cpp b/tests/chains/Theta/TransactionTests.cpp index aae8c5be97b..a5dc2cd456c 100644 --- a/tests/chains/Theta/TransactionTests.cpp +++ b/tests/chains/Theta/TransactionTests.cpp @@ -12,23 +12,23 @@ namespace TW::Theta::tests { -TEST(ThetaTransaction, Encode) { +TEST(ThetaTransaction, EncodePayload) { const auto from = Ethereum::Address("0x2E833968E5bB786Ae419c4d13189fB081Cc43bab"); const auto to = Ethereum::Address("0x9F1233798E905E173560071255140b4A8aBd3Ec6"); auto transaction = Transaction(from, to, 10, 20, 1); - ASSERT_EQ(hex(transaction.encode()), + ASSERT_EQ(hex(transaction.encodePayload()), "02f843c78085e8d4a51000e0df942e833968e5bb786ae419c4d13189fb081cc43babc70a85e8d4a51014" "0180d9d8949f1233798e905e173560071255140b4a8abd3ec6c20a14"); } -TEST(ThetaTransaction, EncodeWithSignature) { +TEST(ThetaTransaction, EncodePayloadWithSignature) { const auto from = Ethereum::Address("0x2E833968E5bB786Ae419c4d13189fB081Cc43bab"); const auto to = Ethereum::Address("0x9F1233798E905E173560071255140b4A8aBd3Ec6"); auto transaction = Transaction(from, to, 10, 20, 1); transaction.setSignature( from, parse_hex("5190868498d587d074d57298f41853d0109d997f15ddf617f471eb8cbb7fff267cb8fe9134" "ccdef053ec7cabd18070325c9c436efe1abbacd14eb7561d3fc10501")); - ASSERT_EQ(hex(transaction.encode()), + ASSERT_EQ(hex(transaction.encodePayload()), "02f887c78085e8d4a51000f863f861942e833968e5bb786ae419c4d13189fb081cc43babc70a85e8d4a5" "101401b8415190868498d587d074d57298f41853d0109d997f15ddf617f471eb8cbb7fff267cb8fe9134" "ccdef053ec7cabd18070325c9c436efe1abbacd14eb7561d3fc10501d9d8949f1233798e905e17356007" diff --git a/tests/chains/ThetaFuel/TWAnySignerTests.cpp b/tests/chains/ThetaFuel/TWAnySignerTests.cpp index 1e1d79ec3d2..610c24c4eea 100644 --- a/tests/chains/ThetaFuel/TWAnySignerTests.cpp +++ b/tests/chains/ThetaFuel/TWAnySignerTests.cpp @@ -9,7 +9,6 @@ #include "HexCoding.h" #include "uint256.h" #include "proto/Ethereum.pb.h" -#include "Ethereum/ABI/ParamBase.h" #include diff --git a/tests/common/HDWallet/HDWalletTests.cpp b/tests/common/HDWallet/HDWalletTests.cpp index e414a510a92..9f8dcec791e 100644 --- a/tests/common/HDWallet/HDWalletTests.cpp +++ b/tests/common/HDWallet/HDWalletTests.cpp @@ -15,7 +15,6 @@ #include "Ethereum/Address.h" #include "Ethereum/EIP2645.h" #include "Ethereum/MessageSigner.h" -#include "Ethereum/Signer.h" #include "HDWallet.h" #include "Hash.h" #include "Hedera/DER.h" @@ -478,11 +477,8 @@ TEST(HDWallet, HederaKey) { } TEST(HDWallet, FromSeedStark) { - std::string signature = "0x5a263fad6f17f23e7c7ea833d058f3656d3fe464baf13f6f5ccba9a2466ba2ce4c4a250231bcac7beb165aec4c9b049b4ba40ad8dd287dc79b92b1ffcf20cdcf1b"; - auto data = parse_hex(signature); - auto ethSignature = Ethereum::Signer::signatureDataToStructSimple(data); - auto seed = store(ethSignature.s); - ASSERT_EQ(ethSignature.s, uint256_t("34506778598894488719068064129252410649539581100963007245393949841529394744783")); + auto seed = parse_hex("4c4a250231bcac7beb165aec4c9b049b4ba40ad8dd287dc79b92b1ffcf20cdcf"); + ASSERT_EQ(load(seed), uint256_t("34506778598894488719068064129252410649539581100963007245393949841529394744783")); auto derivationPath = DerivationPath("m/2645'/579218131'/211006541'/1534045311'/1431804530'/1"); auto key = HDWallet<32>::bip32DeriveRawSeed(TWCoinTypeEthereum, seed, derivationPath); ASSERT_EQ(hex(key.bytes), "57384e99059bb1c0e51d70f0fca22d18d7191398dd39d6b9b4e0521174b2377a"); diff --git a/tests/common/PrivateKeyTests.cpp b/tests/common/PrivateKeyTests.cpp index 81dc49e1da9..38b57cd62d1 100644 --- a/tests/common/PrivateKeyTests.cpp +++ b/tests/common/PrivateKeyTests.cpp @@ -7,7 +7,9 @@ #include "Hash.h" #include "HexCoding.h" #include "PrivateKey.h" +#include #include "PublicKey.h" +#include "PublicKeyLegacy.h" #include @@ -267,6 +269,25 @@ TEST(PrivateKey, SignNIST256p1) { hex(actual)); } +TEST(PrivateKey, SignNIST256p1VerifyLegacy) { + for (auto i = 0; i < 1000; ++i) { + Data secret(32); + random_buffer(secret.data(), 32); + + Data msg(32); + random_buffer(msg.data(), 32); + + PrivateKey privateKey(secret); + auto signature = privateKey.sign(msg, TWCurveNIST256p1); + + auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeNIST256p1); + EXPECT_TRUE(TrezorCrypto::verifyNist256p1Signature(publicKey.bytes, signature, msg)) + << "Error verifying nist256p1 signature" << std::endl + << "Private key: " << hex(secret) << std::endl + << "Message: " << hex(msg); + } +} + int isCanonical([[maybe_unused]] uint8_t by, [[maybe_unused]] uint8_t sig[64]) { return 1; } diff --git a/tests/common/PublicKeyLegacy.h b/tests/common/PublicKeyLegacy.h new file mode 100644 index 00000000000..41ed229526f --- /dev/null +++ b/tests/common/PublicKeyLegacy.h @@ -0,0 +1,20 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include +#include +#include "Data.h" + +namespace TW::TrezorCrypto { + +/// Verifies a signature for the provided message by using `trezor-crypto` library (legacy implementation). +inline bool verifyNist256p1Signature(const Data& publicKey, const Data& signature, const Data& message) { + return ecdsa_verify_digest(&nist256p1, publicKey.data(), signature.data(), message.data()) == 0; +} + +} // namespace TW::TrezorCrypto diff --git a/tests/common/PublicKeyTests.cpp b/tests/common/PublicKeyTests.cpp index 4b452e24adc..7267545091d 100644 --- a/tests/common/PublicKeyTests.cpp +++ b/tests/common/PublicKeyTests.cpp @@ -189,6 +189,18 @@ TEST(PublicKeyTests, Verify) { } } +TEST(PublicKeyTests, ED25519_malleability) { + const auto publicKey = PublicKey(parse_hex("a96e02312b03116ff88a9f3e7cea40f424af43a5c6ca6c8ed4f98969faf46ade"), TWPublicKeyTypeED25519); + + const Data messageData = TW::data("Hello, world!"); + + const Data origSign = parse_hex("ea85a47dcc18b512dfea7c209162abaea4808d77c1ec903dc7ba6e2afa3f9f07e7ed7a20a4e2fa1009db3d1443e937e6abb16ff3c3eaecb798faed7fbb40b008"); + const Data modifiedSign = parse_hex("ea85a47dcc18b512dfea7c209162abaea4808d77c1ec903dc7ba6e2afa3f9f07d4c1707dbe450d69df7735b721e316fbabb16ff3c3eaecb798faed7fbb40b018"); + + EXPECT_TRUE(publicKey.verify(origSign, messageData)); + EXPECT_FALSE(publicKey.verify(modifiedSign, messageData)); +} + TEST(PublicKeyTests, VerifyAsDER) { const auto privateKey = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); diff --git a/tests/interface/TWTransactionCompilerTests.cpp b/tests/interface/TWTransactionCompilerTests.cpp index ceea527b389..a131760e7ce 100644 --- a/tests/interface/TWTransactionCompilerTests.cpp +++ b/tests/interface/TWTransactionCompilerTests.cpp @@ -123,33 +123,22 @@ TEST(TWTransactionCompiler, ExternalSignatureSignBinance) { TEST(TWTransactionCompiler, ExternalSignatureSignEthereum) { /// Step 1: Prepare transaction input (protobuf) const auto coin = TWCoinTypeEthereum; - const auto txInputData0 = WRAPD(TWTransactionCompilerBuildInput( - coin, - STRING("0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F").get(), // from - STRING("0x3535353535353535353535353535353535353535").get(), // to - STRING("1000000000000000000").get(), // amount - STRING("ETH").get(), // asset - STRING("").get(), // memo - STRING("").get() // chainId - )); - - // Check, by parsing - EXPECT_EQ((int)TWDataSize(txInputData0.get()), 61); Ethereum::Proto::SigningInput signingInput; - ASSERT_TRUE(signingInput.ParseFromArray(TWDataBytes(txInputData0.get()), (int)TWDataSize(txInputData0.get()))); - EXPECT_EQ(hex(signingInput.chain_id()), "01"); - EXPECT_EQ(signingInput.to_address(), "0x3535353535353535353535353535353535353535"); - ASSERT_TRUE(signingInput.transaction().has_transfer()); - EXPECT_EQ(hex(signingInput.transaction().transfer().amount()), "0de0b6b3a7640000"); - // Set a few other values const auto nonce = store(uint256_t(11)); + const auto chainId = store(uint256_t(1)); const auto gasPrice = store(uint256_t(20000000000)); const auto gasLimit = store(uint256_t(21000)); + const auto amount = store(uint256_t(1'000'000'000'000'000'000)); + signingInput.set_nonce(nonce.data(), nonce.size()); + signingInput.set_chain_id(chainId.data(), chainId.size()); signingInput.set_gas_price(gasPrice.data(), gasPrice.size()); signingInput.set_gas_limit(gasLimit.data(), gasLimit.size()); signingInput.set_tx_mode(Ethereum::Proto::Legacy); + signingInput.set_to_address("0x3535353535353535353535353535353535353535"); + + signingInput.mutable_transaction()->mutable_transfer()->set_amount(amount.data(), amount.size()); // Serialize back, this shows how to serialize SigningInput protobuf to byte array const auto txInputDataData = data(signingInput.SerializeAsString()); diff --git a/tools/android-build b/tools/android-build index 36425032e9d..bab31390ec8 100755 --- a/tools/android-build +++ b/tools/android-build @@ -5,7 +5,7 @@ set -e source $(dirname $0)/library -version=$(wc_read_version) +version=$(wc_read_version $1) pushd android ./gradlew assembleRelease diff --git a/tools/android-release b/tools/android-release index e37796b2f6e..6371adc941f 100755 --- a/tools/android-release +++ b/tools/android-release @@ -6,7 +6,7 @@ set -e source $(dirname $0)/library -version=$(wc_read_version) +version=$(wc_read_version $1) echo "Building $version" diff --git a/tools/kotlin-release b/tools/kotlin-release index ce934855911..90552fe9ab2 100755 --- a/tools/kotlin-release +++ b/tools/kotlin-release @@ -3,7 +3,7 @@ set -e source $(dirname $0)/library -version=$(wc_read_version) +version=$(wc_read_version $1) echo "Building $version" diff --git a/tools/rust-fuzz b/tools/rust-fuzz new file mode 100755 index 00000000000..5aabfcf96cd --- /dev/null +++ b/tools/rust-fuzz @@ -0,0 +1,39 @@ +#!/bin/bash +# +# This script ensures that all `cargo-fuzz` targets are compilable if the `dry` argument is given. Otherwise it does nothing. +# Prerequisite: install `cargo-fuzz` by running `cargo install cargo-fuzz`. + +set -e + +run_fuzz_target_dry() { + crate=$1 + target=$2 + + pushd rust/$crate + + echo + echo "Running '$crate::$target' fuzz test (dry)" + echo + cargo fuzz run $target -- -runs=0 + echo + + popd # rust/$crate +} + +run_fuzz_crate_dry() { + crate=$1 + + pushd rust/$crate + targets=$(cargo fuzz list) + popd # rust/$crate + + while read -r target; do + run_fuzz_target_dry $crate $target + done < <(printf '%s\n' "$targets") +} + +if [[ $1 == "dry" ]]; then + run_fuzz_crate_dry "tw_encoding" + run_fuzz_crate_dry "tw_hash" + run_fuzz_crate_dry "tw_keypair" +fi diff --git a/tools/rust-test b/tools/rust-test new file mode 100755 index 00000000000..1b20938f589 --- /dev/null +++ b/tools/rust-test @@ -0,0 +1,26 @@ +#!/bin/bash + +# To run Rust tests (home architecture): +# - run unit tests without additional flags: +# ./tools/rust-test +# +# To run Rust tests in WASM: +# - install wasm dependencies +# ./tools/install-wasm-dependencies +# - run unit tests with `wasm` flag: +# ./tools/rust-test wasm + +set -e + +pushd rust + +if [[ "$1" == "wasm" ]]; then + source ../emsdk/emsdk_env.sh + export CARGO_TARGET_WASM32_UNKNOWN_EMSCRIPTEN_RUNNER=node + + cargo test --target wasm32-unknown-emscripten --profile wasm-test +else + cargo test --all +fi + +popd # rust From 91907a335b26ccf988d16f98dc523fea18a80830 Mon Sep 17 00:00:00 2001 From: hewigovens <360470+hewigovens@users.noreply.github.com> Date: Wed, 18 Oct 2023 19:41:10 +0900 Subject: [PATCH 002/128] [Scroll] Update rpc and block explorer for mainnet (#3490) * update rpc and block explorer for mainnet * [KMP]: Bump wc-kotlin to 4.0.0 --------- Co-authored-by: Satoshi Otomakan --- registry.json | 4 ++-- samples/kmp/shared/build.gradle.kts | 2 +- tests/chains/Scroll/TWCoinTypeTests.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/registry.json b/registry.json index 8fad28fe10c..291ad9721db 100644 --- a/registry.json +++ b/registry.json @@ -3274,7 +3274,7 @@ "chainId": "534353", "addressHasher": "keccak256", "explorer": { - "url": "https://blockscout.scroll.io", + "url": "https://scrollscan.com", "txPath": "/tx/", "accountPath": "/address/", "sampleTx": "0xee9196d6840c8d31626324d91c886d20e65711c2026c559133fb23741d3b2f9d", @@ -3283,7 +3283,7 @@ "info": { "url": "https://scroll.io", "source": "https://github.com/scroll-tech", - "rpc": "https://alpha-rpc.scroll.io/l2", + "rpc": "https://rpc.scroll.io", "documentation": "https://guide.scroll.io" } }, diff --git a/samples/kmp/shared/build.gradle.kts b/samples/kmp/shared/build.gradle.kts index fdc3fb1dbc4..14741e42ad6 100644 --- a/samples/kmp/shared/build.gradle.kts +++ b/samples/kmp/shared/build.gradle.kts @@ -35,7 +35,7 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation("com.trustwallet:wallet-core-kotlin:3.3.2") + implementation("com.trustwallet:wallet-core-kotlin:4.0.0") } } val commonTest by getting { diff --git a/tests/chains/Scroll/TWCoinTypeTests.cpp b/tests/chains/Scroll/TWCoinTypeTests.cpp index 4486d9d7370..0d9ca7f3907 100644 --- a/tests/chains/Scroll/TWCoinTypeTests.cpp +++ b/tests/chains/Scroll/TWCoinTypeTests.cpp @@ -32,6 +32,6 @@ TEST(TWScrollCoinType, TWCoinType) { ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); assertStringsEqual(chainId, "534353"); - assertStringsEqual(txUrl, "https://blockscout.scroll.io/tx/0xee9196d6840c8d31626324d91c886d20e65711c2026c559133fb23741d3b2f9d"); - assertStringsEqual(accUrl, "https://blockscout.scroll.io/address/0xFE993660cd35d68D94b6Eba29F4D928d979cd65B"); + assertStringsEqual(txUrl, "https://scrollscan.com/tx/0xee9196d6840c8d31626324d91c886d20e65711c2026c559133fb23741d3b2f9d"); + assertStringsEqual(accUrl, "https://scrollscan.com/address/0xFE993660cd35d68D94b6Eba29F4D928d979cd65B"); } From 829ee4751d0af235c9bbf2640a3fdad2c7918cc4 Mon Sep 17 00:00:00 2001 From: shuoer86 <129674997+shuoer86@users.noreply.github.com> Date: Thu, 19 Oct 2023 16:32:12 +0800 Subject: [PATCH 003/128] Fix typos (#3475) Co-authored-by: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> --- codegen-v2/README.md | 2 +- docs/registry-fields.md | 2 +- samples/cpp/README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/codegen-v2/README.md b/codegen-v2/README.md index 11490caee31..33e018ac407 100644 --- a/codegen-v2/README.md +++ b/codegen-v2/README.md @@ -2,7 +2,7 @@ This is a _work-in-progress_ parser meant to deprecate the existing Ruby parser in `codegen/`. As of now, we only support Swift binding generation. This project -will progess over multiple stages (PRs). +will progress over multiple stages (PRs). ## Execution diff --git a/docs/registry-fields.md b/docs/registry-fields.md index e1c7098e332..ff6b0e539e6 100644 --- a/docs/registry-fields.md +++ b/docs/registry-fields.md @@ -189,7 +189,7 @@ Link to the default implementation of the node or RPC gateway that can be used b Optional URL to an available public RPC service. **`info/documentation`** -Main porject documentation site/subsite. +Main project documentation site/subsite. **`deprecated`** If set to `true`, the project is considered deprecated: its info is kept here, but it will not be supported. diff --git a/samples/cpp/README.md b/samples/cpp/README.md index de231205a0f..5998b7c59d7 100644 --- a/samples/cpp/README.md +++ b/samples/cpp/README.md @@ -2,7 +2,7 @@ ## Overview -This repository contains a simple but complete **C++** sample application, for demostrating usage of the +This repository contains a simple but complete **C++** sample application, for demonstrating usage of the [Wallet Core](https://github.com/trustwallet/wallet-core) library (part of [Trust Wallet](https://trustwallet.com)). ## DISCLAIMER From b08c6f3418fb603c6b665e3648ebf104c3550a40 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Thu, 19 Oct 2023 11:27:46 +0200 Subject: [PATCH 004/128] [Crypto]: Fix salsa20_8(B) when compiled with `-Os` optimisation level (#3491) --- trezor-crypto/crypto/scrypt.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/trezor-crypto/crypto/scrypt.c b/trezor-crypto/crypto/scrypt.c index c1856dcbda5..4e94beab0e8 100644 --- a/trezor-crypto/crypto/scrypt.c +++ b/trezor-crypto/crypto/scrypt.c @@ -42,7 +42,7 @@ #include #include -static void blkcpy(void *, void *, size_t); +static void blkcpy(uint32_t *, const uint32_t *, size_t); static void blkxor(void *, void *, size_t); static void salsa20_8(uint32_t[16]); static void blockmix_salsa8(uint32_t *, uint32_t *, uint32_t *, size_t); @@ -50,15 +50,12 @@ static uint64_t integerify(void *, size_t); static void smix(uint8_t *, size_t, uint64_t, uint32_t *, uint32_t *); static void -blkcpy(void * dest, void * src, size_t len) +blkcpy(uint32_t * dest, const uint32_t * src, size_t len) { - size_t * D = dest; - size_t * S = src; - size_t L = len / sizeof(size_t); - size_t i; + size_t L = len / sizeof(uint32_t); - for (i = 0; i < L; i++) - D[i] = S[i]; + for (size_t i = 0; i < L; i++) + dest[i] = src[i]; } static void From 92fc9bd54d3feaff5e482250eb0eac7d7cefe376 Mon Sep 17 00:00:00 2001 From: Ryan Croote Date: Mon, 23 Oct 2023 09:53:38 -0400 Subject: [PATCH 005/128] [Internet Computer] Add Internet Computer (ICP) Chain (#3470) --- .../blockchains/CoinAddressDerivationTests.kt | 1 + .../TestInternetComputerAddress.kt | 31 + .../TestInternetComputerSigner.kt | 81 + docs/registry.md | 1 + include/TrustWalletCore/TWBlockchain.h | 1 + include/TrustWalletCore/TWCoinType.h | 1 + .../core/test/CoinAddressDerivationTests.kt | 1 + registry.json | 31 +- rust/Cargo.lock | 85 +- rust/Cargo.toml | 1 + .../tests/tw_any_address_ffi_tests.rs | 18 + rust/tw_coin_registry/Cargo.toml | 1 + rust/tw_coin_registry/src/blockchain_type.rs | 2 + rust/tw_coin_registry/src/dispatcher.rs | 4 + rust/tw_encoding/Cargo.toml | 5 + rust/tw_encoding/fuzz/Cargo.toml | 8 + .../fuzz/fuzz_targets/cbor_encode_decode.rs | 23 + rust/tw_encoding/src/cbor.rs | 69 + rust/tw_encoding/src/lib.rs | 1 + rust/tw_hash/fuzz/fuzz_targets/hash_fuzz.rs | 1 + rust/tw_hash/src/crc32.rs | 52 + rust/tw_hash/src/lib.rs | 1 + rust/tw_hash/src/sha2.rs | 6 +- rust/tw_internet_computer/Cargo.toml | 19 + rust/tw_internet_computer/build.rs | 54 + rust/tw_internet_computer/fuzz/.gitignore | 4 + rust/tw_internet_computer/fuzz/Cargo.lock | 1595 +++++++++++++++++ rust/tw_internet_computer/fuzz/Cargo.toml | 31 + .../tw_internet_computer_transfer.rs | 70 + rust/tw_internet_computer/src/address.rs | 184 ++ rust/tw_internet_computer/src/context.rs | 77 + rust/tw_internet_computer/src/entry.rs | 102 ++ rust/tw_internet_computer/src/lib.rs | 12 + .../src/protocol/envelope.rs | 237 +++ .../src/protocol/identity.rs | 116 ++ rust/tw_internet_computer/src/protocol/mod.rs | 35 + .../src/protocol/principal.rs | 256 +++ .../src/protocol/request_id.rs | 127 ++ .../src/protocol/rosetta.rs | 54 + rust/tw_internet_computer/src/signer.rs | 78 + .../src/transactions/mod.rs | 47 + .../src/transactions/proto/ledger.proto | 315 ++++ .../src/transactions/proto/types.proto | 19 + .../src/transactions/transfer.rs | 247 +++ rust/tw_keypair/Cargo.toml | 3 +- rust/tw_keypair/src/ecdsa/secp256k1/public.rs | 23 + src/Coin.cpp | 5 +- src/InternetComputer/Entry.cpp | 33 + src/InternetComputer/Entry.h | 27 + src/proto/InternetComputer.proto | 46 + .../Blockchains/InternetComputerTests.swift | 78 + swift/Tests/CoinAddressDerivationTests.swift | 3 + .../InternetComputer/TWAnyAddressTests.cpp | 20 + .../InternetComputer/TWAnySignerTests.cpp | 36 + .../InternetComputer/TWCoinTypeTests.cpp | 34 + tests/common/CoinAddressDerivationTests.cpp | 3 + .../tests/Blockchain/InternetComputer.test.ts | 139 ++ 57 files changed, 4534 insertions(+), 20 deletions(-) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/internetcomputer/TestInternetComputerAddress.kt create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/internetcomputer/TestInternetComputerSigner.kt create mode 100644 rust/tw_encoding/fuzz/fuzz_targets/cbor_encode_decode.rs create mode 100644 rust/tw_encoding/src/cbor.rs create mode 100644 rust/tw_hash/src/crc32.rs create mode 100644 rust/tw_internet_computer/Cargo.toml create mode 100644 rust/tw_internet_computer/build.rs create mode 100644 rust/tw_internet_computer/fuzz/.gitignore create mode 100644 rust/tw_internet_computer/fuzz/Cargo.lock create mode 100644 rust/tw_internet_computer/fuzz/Cargo.toml create mode 100644 rust/tw_internet_computer/fuzz/fuzz_targets/tw_internet_computer_transfer.rs create mode 100644 rust/tw_internet_computer/src/address.rs create mode 100644 rust/tw_internet_computer/src/context.rs create mode 100644 rust/tw_internet_computer/src/entry.rs create mode 100644 rust/tw_internet_computer/src/lib.rs create mode 100644 rust/tw_internet_computer/src/protocol/envelope.rs create mode 100644 rust/tw_internet_computer/src/protocol/identity.rs create mode 100644 rust/tw_internet_computer/src/protocol/mod.rs create mode 100644 rust/tw_internet_computer/src/protocol/principal.rs create mode 100644 rust/tw_internet_computer/src/protocol/request_id.rs create mode 100644 rust/tw_internet_computer/src/protocol/rosetta.rs create mode 100644 rust/tw_internet_computer/src/signer.rs create mode 100644 rust/tw_internet_computer/src/transactions/mod.rs create mode 100644 rust/tw_internet_computer/src/transactions/proto/ledger.proto create mode 100644 rust/tw_internet_computer/src/transactions/proto/types.proto create mode 100644 rust/tw_internet_computer/src/transactions/transfer.rs create mode 100644 src/InternetComputer/Entry.cpp create mode 100644 src/InternetComputer/Entry.h create mode 100644 src/proto/InternetComputer.proto create mode 100644 swift/Tests/Blockchains/InternetComputerTests.swift create mode 100644 tests/chains/InternetComputer/TWAnyAddressTests.cpp create mode 100644 tests/chains/InternetComputer/TWAnySignerTests.cpp create mode 100644 tests/chains/InternetComputer/TWCoinTypeTests.cpp create mode 100644 wasm/tests/Blockchain/InternetComputer.test.ts diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index eb93523e4eb..89f21271a53 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -146,5 +146,6 @@ class CoinAddressDerivationTests { NOBLE -> assertEquals("noble142j9u5eaduzd7faumygud6ruhdwme98qc8l3wa", address) ROOTSTOCK -> assertEquals("0xA2D7065F94F838a3aB9C04D67B312056846424Df", address) SEI -> assertEquals("sei142j9u5eaduzd7faumygud6ruhdwme98qagm0sj", address) + INTERNETCOMPUTER -> assertEquals("b9a13d974ee9db036d5abc5b66ace23e513cb5676f3996626c7717c339a3ee87", address) } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/internetcomputer/TestInternetComputerAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/internetcomputer/TestInternetComputerAddress.kt new file mode 100644 index 00000000000..f4b9044e339 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/internetcomputer/TestInternetComputerAddress.kt @@ -0,0 +1,31 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +package com.trustwallet.core.app.blockchains.internetcomputer + +import com.trustwallet.core.app.utils.toHex +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.* + +class TestInternetComputerAddress { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testAddress() { + val key = PrivateKey("ee42eaada903e20ef6e5069f0428d552475c1ea7ed940842da6448f6ef9d48e7".toHexByteArray()) + val pubkey = key.getPublicKeySecp256k1(false); + val address = AnyAddress(pubkey, CoinType.INTERNETCOMPUTER) + val expected = AnyAddress("2f25874478d06cf68b9833524a6390d0ba69c566b02f46626979a3d6a4153211", CoinType.INTERNETCOMPUTER) + + assertEquals(pubkey.data().toHex(), "0x048542e6fb4b17d6dfcac3948fe412c00d626728815ee7cc70509603f1bc92128a6e7548f3432d6248bc49ff44a1e50f6389238468d17f7d7024de5be9b181dbc8") + assertEquals(address.description(), expected.description()) + } +} \ No newline at end of file diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/internetcomputer/TestInternetComputerSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/internetcomputer/TestInternetComputerSigner.kt new file mode 100644 index 00000000000..87a4261a158 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/internetcomputer/TestInternetComputerSigner.kt @@ -0,0 +1,81 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +package com.trustwallet.core.app.blockchains.internetcomputer + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.Numeric +import com.trustwallet.core.app.utils.toHex +import com.trustwallet.core.app.utils.toHexByteArray +import com.trustwallet.core.app.utils.toHexBytes +import com.trustwallet.core.app.utils.toHexBytesInByteString +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.java.AnySigner +import wallet.core.jni.* +import wallet.core.jni.proto.InternetComputer +import wallet.core.jni.proto.InternetComputer.SigningOutput + +class TestInternetComputerSigner { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun InternetComputerTransactionSigning() { + val key = PrivateKey("227102911bb99ce7285a55f952800912b7d22ebeeeee59d77fc33a5d7c7080be".toHexByteArray()) + + val input = InternetComputer.SigningInput.newBuilder() + .setTransaction(InternetComputer.Transaction.newBuilder().apply { + transfer = InternetComputer.Transaction.Transfer.newBuilder().apply { + toAccountIdentifier = "943d12e762f43806782f524b8f90297298a6d79e4749b41b585ec427409c826a" + amount = 100000000 + memo = 0 + currentTimestampNanos = 1691709940000000000 + }.build() + }.build()) + .setPrivateKey(ByteString.copyFrom(key.data())) + val output = AnySigner.sign(input.build(), CoinType.INTERNETCOMPUTER, SigningOutput.parser()) + assertEquals(output.signedTransaction.toByteArray().toHex(), "0x81826b5452414e53414354494f4e81a266757064617465a367636f6e74656e74a66c726571756573745f747970656463616c6c6e696e67726573735f6578706972791b177a297215cfe8006673656e646572581d971cd2ddeecd1cf1b28be914d7a5c43441f6296f1f9966a7c8aff68d026b63616e69737465725f69644a000000000000000201016b6d6574686f645f6e616d656773656e645f706263617267583b0a0012070a050880c2d72f2a220a20943d12e762f43806782f524b8f90297298a6d79e4749b41b585ec427409c826a3a0a088090caa5a3a78abd176d73656e6465725f7075626b65799858183018561830100607182a1886184818ce183d02010605182b188104000a0318420004183d18ab183a182118a81838184d184c187e1852188a187e18dc18d8184418ea18cd18c5189518ac188518b518bc181d188515186318bc18e618ab18d2184318d3187c184f18cd18f018de189b18b5181918dd18ef1889187218e71518c40418d4189718881843187218c611182e18cc18e6186b182118630218356a73656e6465725f736967984013186f18b9181c189818b318a8186518b2186118d418971618b1187d18eb185818e01826182f1873183b185018cb185d18ef18d81839186418b3183218da1824182f184e18a01880182718c0189018c918a018fd18c418d9189e189818b318ef1874183b185118e118a51864185918e718ed18c71889186c1822182318ca6a726561645f7374617465a367636f6e74656e74a46c726571756573745f747970656a726561645f73746174656e696e67726573735f6578706972791b177a297215cfe8006673656e646572581d971cd2ddeecd1cf1b28be914d7a5c43441f6296f1f9966a7c8aff68d0265706174687381824e726571756573745f7374617475735820e8fbc2d5b0bf837b3a369249143e50d4476faafb2dd620e4e982586a51ebcf1e6d73656e6465725f7075626b65799858183018561830100607182a1886184818ce183d02010605182b188104000a0318420004183d18ab183a182118a81838184d184c187e1852188a187e18dc18d8184418ea18cd18c5189518ac188518b518bc181d188515186318bc18e618ab18d2184318d3187c184f18cd18f018de189b18b5181918dd18ef1889187218e71518c40418d4189718881843187218c611182e18cc18e6186b182118630218356a73656e6465725f7369679840182d182718201888188618ce187f0c182a187a18d718e818df18fb18d318d41118a5186a184b18341842185318d718e618e8187a1828186c186a183618461418f3183318bd18a618a718bc18d918c818b7189d186e1865188418ff18fd18e418e9187f181b18d705184818b21872187818d6181c161833184318a2") + } + + @Test + fun InternetComputerTransactionSigningWithInvalidToAccountIdentifier() { + val key = PrivateKey("227102911bb99ce7285a55f952800912b7d22ebeeeee59d77fc33a5d7c7080be".toHexByteArray()) + + val input = InternetComputer.SigningInput.newBuilder() + .setTransaction(InternetComputer.Transaction.newBuilder().apply { + transfer = InternetComputer.Transaction.Transfer.newBuilder().apply { + toAccountIdentifier = "643d12e762f43806782f524b8f90297298a6d79e4749b41b585ec427409c826b" + amount = 100000000 + memo = 0 + currentTimestampNanos = 1691709940000000000 + }.build() + }.build()) + .setPrivateKey(ByteString.copyFrom(key.data())) + val output = AnySigner.sign(input.build(), CoinType.INTERNETCOMPUTER, SigningOutput.parser()) + assertEquals(output.error.number, 16) + } + + @Test + fun InternetComputerTransactionSigningWithInvalidAmount() { + val key = PrivateKey("227102911bb99ce7285a55f952800912b7d22ebeeeee59d77fc33a5d7c7080be".toHexByteArray()) + + val input = InternetComputer.SigningInput.newBuilder() + .setTransaction(InternetComputer.Transaction.newBuilder().apply { + transfer = InternetComputer.Transaction.Transfer.newBuilder().apply { + toAccountIdentifier = "943d12e762f43806782f524b8f90297298a6d79e4749b41b585ec427409c826a" + amount = 0 + memo = 0 + currentTimestampNanos = 1691709940000000000 + }.build() + }.build()) + .setPrivateKey(ByteString.copyFrom(key.data())) + val output = AnySigner.sign(input.build(), CoinType.INTERNETCOMPUTER, SigningOutput.parser()) + assertEquals(output.error.number, 23) + } +} \ No newline at end of file diff --git a/docs/registry.md b/docs/registry.md index d1a15c32038..d569da1d826 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -36,6 +36,7 @@ This list is generated from [./registry.json](../registry.json) | 194 | EOS | EOS | | | | 195 | Tron | TRX | | | | 204 | OpBNB | BNB | | | +| 223 | Internet Computer | ICP | | | | 235 | FIO | FIO | | | | 242 | Nimiq | NIM | | | | 283 | Algorand | ALGO | | | diff --git a/include/TrustWalletCore/TWBlockchain.h b/include/TrustWalletCore/TWBlockchain.h index d715176ed7b..06a88beba2e 100644 --- a/include/TrustWalletCore/TWBlockchain.h +++ b/include/TrustWalletCore/TWBlockchain.h @@ -64,6 +64,7 @@ enum TWBlockchain { TWBlockchainTheOpenNetwork = 49, TWBlockchainSui = 50, TWBlockchainGreenfield = 51, + TWBlockchainInternetComputer = 52, }; TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index ad78ddfa90d..1b75255e921 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -177,6 +177,7 @@ enum TWCoinType { TWCoinTypeGreenfield = 5600, TWCoinTypeMantle = 5000, TWCoinTypeZenEON = 7332, + TWCoinTypeInternetComputer = 223, }; /// Returns the blockchain for a coin type. diff --git a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt index dac9e182bf3..f5966bbd65c 100644 --- a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt +++ b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt @@ -142,5 +142,6 @@ class CoinAddressDerivationTests { Noble -> "noble142j9u5eaduzd7faumygud6ruhdwme98qc8l3wa" Rootstock -> "0xA2D7065F94F838a3aB9C04D67B312056846424Df" Sei -> "sei142j9u5eaduzd7faumygud6ruhdwme98qagm0sj" + InternetComputer -> "b9a13d974ee9db036d5abc5b66ace23e513cb5676f3996626c7717c339a3ee87" } } diff --git a/registry.json b/registry.json index 291ad9721db..6117b5d844e 100644 --- a/registry.json +++ b/registry.json @@ -1763,7 +1763,6 @@ "documentation": "https://developer.algorand.org/docs/algod-rest-paths" } }, - { "id": "iotex", "name": "IoTeX", @@ -4446,5 +4445,35 @@ "rpc": "https://neon-proxy-mainnet.solana.p2p.org/", "documentation": "https://docs.neonfoundation.io/docs/quick_start" } + }, + { + "id": "internet_computer", + "name": "Internet Computer", + "coinId": 223, + "symbol": "ICP", + "decimals": 8, + "blockchain": "InternetComputer", + "derivation": [ + { + "path": "m/44'/223'/1'/0/0", + "xpub": "xpub", + "xpriv": "xpriv" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "explorer": { + "url": "https://dashboard.internetcomputer.org", + "txPath": "/transaction/", + "accountPath": "/account/", + "sampleTx": "9e32c54975adf84a1d98f19df41bbc34a752a899c32cc9c0000200b2c4308f85", + "sampleAccount": "529ea51c22e8d66e8302eabd9297b100fdb369109822248bb86939a671fbc55b" + }, + "info": { + "url": "https://internetcomputer.org", + "source": "https://github.com/dfinity/ic", + "rpc": "", + "documentation": "https://internetcomputer.org/docs" + } } ] diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 7ae598db1fd..035cd54ddad 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -296,6 +296,33 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "ciborium" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" + +[[package]] +name = "ciborium-ll" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "clap" version = "2.34.0" @@ -379,9 +406,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "der" @@ -578,6 +605,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + [[package]] name = "hashbrown" version = "0.14.0" @@ -913,9 +946,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] @@ -937,9 +970,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -1133,31 +1166,31 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.164" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_bytes" -version = "0.11.9" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" +checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.164" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.37", ] [[package]] @@ -1270,7 +1303,7 @@ checksum = "e6dc88f1f470d9de1001ffbb90d2344c9dd1a615f5467daf0574e2975dfd9ebd" dependencies = [ "starknet-curve", "starknet-ff", - "syn 2.0.15", + "syn 2.0.37", ] [[package]] @@ -1327,9 +1360,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" dependencies = [ "proc-macro2", "quote", @@ -1465,6 +1498,7 @@ dependencies = [ "tw_coin_entry", "tw_ethereum", "tw_evm", + "tw_internet_computer", "tw_keypair", "tw_memory", "tw_misc", @@ -1477,8 +1511,11 @@ version = "0.1.0" dependencies = [ "arbitrary", "bs58", + "ciborium", "data-encoding", "hex", + "serde", + "serde_bytes", "tw_memory", ] @@ -1534,6 +1571,21 @@ dependencies = [ "zeroize", ] +[[package]] +name = "tw_internet_computer" +version = "0.1.0" +dependencies = [ + "pb-rs", + "quick-protobuf", + "serde", + "tw_coin_entry", + "tw_encoding", + "tw_hash", + "tw_keypair", + "tw_memory", + "tw_proto", +] + [[package]] name = "tw_keypair" version = "0.1.0" @@ -1547,6 +1599,7 @@ dependencies = [ "k256", "lazy_static", "p256", + "pkcs8", "rfc6979", "ring", "serde", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index b8fdf347fca..e5c065055f1 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -8,6 +8,7 @@ members = [ "tw_ethereum", "tw_evm", "tw_hash", + "tw_internet_computer", "tw_keypair", "tw_memory", "tw_misc", diff --git a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs index 829e2b97af0..0449c69ca59 100644 --- a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs +++ b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs @@ -38,6 +38,9 @@ fn test_any_address_derive() { BlockchainType::Bitcoin => "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", BlockchainType::Ethereum => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", BlockchainType::Ronin => "ronin:Ac1ec44E4f0ca7D172B7803f6836De87Fb72b309", + BlockchainType::InternetComputer => { + "290cc7c359f44c8516fc169c5ed4f0f3ae2e24bf5de0d4c51f5e7545b5474faa" + }, BlockchainType::Unsupported => unreachable!(), }; @@ -71,6 +74,10 @@ fn test_any_address_normalize_eth() { "0xb16db98b365b1f89191996942612b14f1da4bd5f", "ronin:b16Db98B365B1f89191996942612B14F1Da4Bd5f", ), + BlockchainType::InternetComputer => ( + "290CC7C359F44C8516FC169C5ED4F0F3AE2E24BF5DE0D4C51F5E7545B5474FAA", + "290cc7c359f44c8516fc169c5ed4f0f3ae2e24bf5de0d4c51f5e7545b5474faa", + ), BlockchainType::Unsupported => unreachable!(), }; @@ -109,6 +116,12 @@ fn test_any_address_is_valid_coin() { "ronin:b16db98b365b1f89191996942612b14f1da4bd5f", "ronin:b16Db98B365B1f89191996942612B14F1Da4Bd5f", ], + BlockchainType::InternetComputer => vec![ + "fb257577279ecac604d4780214af95aa6adc3a814f6f3d6d7ac844d1deca500a", + "e90c48d54847f4758f1d6b589a1db2500757a49a6722d4f775e050107b4b752d", + "a7c5baf393aed527ef6fb3869fbf84dd4e562edf9b04bd8f9bfbd6b8e6a22776", + "4cb2ca5cfcfa1d952f8cd7f0ec46c96e1023ab057b83a2c7ce236b9e71ccca0b", + ], _ => unreachable!(), }; @@ -129,6 +142,11 @@ fn test_any_address_is_valid_coin_invalid() { BlockchainType::Ethereum | BlockchainType::Ronin => { vec!["b16Db98B365B1f89191996942612B14F1Da4Bd5f"] }, + BlockchainType::InternetComputer => vec![ + "3357cba483f268d044d4bbd4639f82c16028a76eebdf62c51bc11fc918d278b", + "3357cba483f268d044d4bbd4639f82c16028a76eebdf62c51bc11fc918d278bce", + "553357cba483f268d044d4bbd4639f82c16028a76eebdf62c51bc11fc918d278", + ], BlockchainType::Unsupported => unreachable!(), }; diff --git a/rust/tw_coin_registry/Cargo.toml b/rust/tw_coin_registry/Cargo.toml index a66a537a37d..4ad4e6d87de 100644 --- a/rust/tw_coin_registry/Cargo.toml +++ b/rust/tw_coin_registry/Cargo.toml @@ -11,6 +11,7 @@ tw_bitcoin = { path = "../tw_bitcoin" } tw_coin_entry = { path = "../tw_coin_entry" } tw_ethereum = { path = "../tw_ethereum" } tw_evm = { path = "../tw_evm" } +tw_internet_computer = { path = "../tw_internet_computer" } tw_keypair = { path = "../tw_keypair" } tw_memory = { path = "../tw_memory" } tw_misc = { path = "../tw_misc" } diff --git a/rust/tw_coin_registry/src/blockchain_type.rs b/rust/tw_coin_registry/src/blockchain_type.rs index 47a19ff54ce..72a4141d1ea 100644 --- a/rust/tw_coin_registry/src/blockchain_type.rs +++ b/rust/tw_coin_registry/src/blockchain_type.rs @@ -15,6 +15,7 @@ use std::str::FromStr; pub enum BlockchainType { Bitcoin, Ethereum, + InternetComputer, Ronin, Unsupported, } @@ -36,6 +37,7 @@ impl FromStr for BlockchainType { match s { "Bitcoin" => Ok(BlockchainType::Bitcoin), "Ethereum" => Ok(BlockchainType::Ethereum), + "InternetComputer" => Ok(BlockchainType::InternetComputer), "Ronin" => Ok(BlockchainType::Ronin), _ => Ok(BlockchainType::Unsupported), } diff --git a/rust/tw_coin_registry/src/dispatcher.rs b/rust/tw_coin_registry/src/dispatcher.rs index 9c47e279a02..e027054e443 100644 --- a/rust/tw_coin_registry/src/dispatcher.rs +++ b/rust/tw_coin_registry/src/dispatcher.rs @@ -13,6 +13,7 @@ use tw_bitcoin::entry::BitcoinEntry; use tw_coin_entry::coin_entry_ext::CoinEntryExt; use tw_ethereum::entry::EthereumEntry; use tw_evm::evm_entry::EvmEntryExt; +use tw_internet_computer::entry::InternetComputerEntry; use tw_ronin::entry::RoninEntry; pub type CoinEntryExtStaticRef = &'static dyn CoinEntryExt; @@ -20,12 +21,14 @@ pub type EvmEntryExtStaticRef = &'static dyn EvmEntryExt; const BITCOIN: BitcoinEntry = BitcoinEntry; const ETHEREUM: EthereumEntry = EthereumEntry; +const INTERNET_COMPUTER: InternetComputerEntry = InternetComputerEntry; const RONIN: RoninEntry = RoninEntry; pub fn blockchain_dispatcher(blockchain: BlockchainType) -> RegistryResult { match blockchain { BlockchainType::Bitcoin => Ok(&BITCOIN), BlockchainType::Ethereum => Ok(ÐEREUM), + BlockchainType::InternetComputer => Ok(&INTERNET_COMPUTER), BlockchainType::Ronin => Ok(&RONIN), BlockchainType::Unsupported => Err(RegistryError::Unsupported), } @@ -45,6 +48,7 @@ pub fn evm_dispatcher(coin: CoinType) -> RegistryResult { match item.blockchain { BlockchainType::Bitcoin => Err(RegistryError::Unsupported), BlockchainType::Ethereum => Ok(ÐEREUM), + BlockchainType::InternetComputer => Err(RegistryError::Unsupported), BlockchainType::Ronin => Ok(&RONIN), BlockchainType::Unsupported => Err(RegistryError::Unsupported), } diff --git a/rust/tw_encoding/Cargo.toml b/rust/tw_encoding/Cargo.toml index 2d32de903f4..2211e12bde9 100644 --- a/rust/tw_encoding/Cargo.toml +++ b/rust/tw_encoding/Cargo.toml @@ -6,6 +6,11 @@ edition = "2021" [dependencies] arbitrary = { version = "1", features = ["derive"], optional = true } bs58 = "0.4.0" +ciborium = "0.2.1" data-encoding = "2.3.3" hex = "0.4.3" +serde = { version = "1.0.136", features = ["derive"] } tw_memory = { path = "../tw_memory" } + +[dev-dependencies] +serde_bytes = "0.11.12" \ No newline at end of file diff --git a/rust/tw_encoding/fuzz/Cargo.toml b/rust/tw_encoding/fuzz/Cargo.toml index eaf660bc073..81d65cd9818 100644 --- a/rust/tw_encoding/fuzz/Cargo.toml +++ b/rust/tw_encoding/fuzz/Cargo.toml @@ -9,6 +9,8 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = { version = "0.4", features = ["arbitrary-derive"] } +serde = { version = "1.0.136", features = ["derive"] } +serde_bytes = "0.11.12" [dependencies.tw_encoding] path = ".." @@ -32,3 +34,9 @@ name = "base_decode" path = "fuzz_targets/base_decode.rs" test = false doc = false + +[[bin]] +name = "cbor_encode_decode" +path = "fuzz_targets/cbor_encode_decode.rs" +test = false +doc = false diff --git a/rust/tw_encoding/fuzz/fuzz_targets/cbor_encode_decode.rs b/rust/tw_encoding/fuzz/fuzz_targets/cbor_encode_decode.rs new file mode 100644 index 00000000000..6c79c61db3c --- /dev/null +++ b/rust/tw_encoding/fuzz/fuzz_targets/cbor_encode_decode.rs @@ -0,0 +1,23 @@ +#![no_main] + +use libfuzzer_sys::{arbitrary, fuzz_target}; + +use serde::{Deserialize, Serialize}; + +use tw_encoding::cbor::{decode, encode}; + +#[derive(arbitrary::Arbitrary, Serialize, Deserialize, PartialEq, Debug)] +struct User { + name: String, + age: u64, + friends: Vec, + #[serde(with = "serde_bytes")] + address: Vec, + key: Vec, +} + +fuzz_target!(|se_user: User| { + let serialized = encode(&se_user).unwrap(); + let de_user = decode::(&serialized).unwrap(); + assert_eq!(se_user, de_user); +}); diff --git a/rust/tw_encoding/src/cbor.rs b/rust/tw_encoding/src/cbor.rs new file mode 100644 index 00000000000..4f5e954fd62 --- /dev/null +++ b/rust/tw_encoding/src/cbor.rs @@ -0,0 +1,69 @@ +use ciborium::{de, ser}; +use serde::{de::DeserializeOwned, Serialize}; + +type CborResult = Result; + +pub fn encode(message: &S) -> CborResult> { + let mut bytes = vec![]; + ser::into_writer(message, &mut bytes).map_err(|e| e.to_string())?; + Ok(bytes) +} + +pub fn decode(data: &[u8]) -> CborResult { + de::from_reader(data).map_err(|e| e.to_string()) +} + +#[cfg(test)] +mod test { + use serde::{Deserialize, Serialize}; + + use super::{decode, encode}; + + const SERIALIZED: [u8; 70] = [ + 165, 100, 110, 97, 109, 101, 101, 65, 108, 105, 99, 101, 99, 97, 103, 101, 24, 42, 103, + 102, 114, 105, 101, 110, 100, 115, 131, 99, 66, 111, 98, 101, 67, 97, 114, 111, 108, 100, + 68, 97, 118, 101, 103, 97, 100, 100, 114, 101, 115, 115, 68, 0, 1, 2, 3, 99, 107, 101, 121, + 138, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, + ]; + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct User { + name: String, + age: u64, + friends: Vec, + #[serde(with = "serde_bytes")] + address: Vec, + key: Vec, + } + + fn test_user() -> User { + User { + name: "Alice".to_string(), + age: 42, + friends: vec!["Bob".to_string(), "Carol".to_string(), "Dave".to_string()], + address: vec![0, 1, 2, 3], + key: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 0], + } + } + + #[test] + fn test_serialize() { + let user = test_user(); + let serialized = encode(&user).unwrap(); + assert_eq!(serialized, SERIALIZED); + } + + #[test] + fn test_deserialize() { + let user = decode::(&SERIALIZED).unwrap(); + assert_eq!(user, test_user()); + } + + #[test] + fn test_serialize_deserialize() { + let se_user = test_user(); + let serialized = encode(&se_user).unwrap(); + let de_user = decode::(&serialized).unwrap(); + assert_eq!(se_user, de_user); + } +} diff --git a/rust/tw_encoding/src/lib.rs b/rust/tw_encoding/src/lib.rs index 648781e8af3..3250e9ed4da 100644 --- a/rust/tw_encoding/src/lib.rs +++ b/rust/tw_encoding/src/lib.rs @@ -7,6 +7,7 @@ pub mod base32; pub mod base58; pub mod base64; +pub mod cbor; pub mod ffi; pub mod hex; diff --git a/rust/tw_hash/fuzz/fuzz_targets/hash_fuzz.rs b/rust/tw_hash/fuzz/fuzz_targets/hash_fuzz.rs index 505c0facbe9..ddd74bce7df 100644 --- a/rust/tw_hash/fuzz/fuzz_targets/hash_fuzz.rs +++ b/rust/tw_hash/fuzz/fuzz_targets/hash_fuzz.rs @@ -19,6 +19,7 @@ fuzz_target!(|input: HashInput<'_>| { tw_hash::blake::blake_256(input.data); tw_hash::blake2::blake2_b(input.data, input.hash_size).ok(); tw_hash::blake2::blake2_b_personal(input.data, input.hash_size, input.additional_data).ok(); + tw_hash::crc32::crc32(input.data); tw_hash::groestl::groestl_512(input.data); tw_hash::hmac::hmac_sha256(input.additional_data, input.data); tw_hash::ripemd::ripemd_160(input.data); diff --git a/rust/tw_hash/src/crc32.rs b/rust/tw_hash/src/crc32.rs new file mode 100644 index 00000000000..f413fd13a19 --- /dev/null +++ b/rust/tw_hash/src/crc32.rs @@ -0,0 +1,52 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +// Table taken from https://web.mit.edu/freebsd/head/sys/libkern/crc32.c (Public Domain code) +// This table is used to speed up the crc calculation. +const CRC32_TABLE: [u32; 256] = [ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +]; + +// Algorithm inspired by this old-style C implementation: +// https://web.mit.edu/freebsd/head/sys/libkern/crc32.c (Public Domain code) +pub fn crc32(input: &[u8]) -> u32 { + let mut c = u32::MAX; + for b in input { + c = CRC32_TABLE[((c ^ (*b as u32)) & 0xFF) as usize] ^ (c >> 8); + } + !c +} diff --git a/rust/tw_hash/src/lib.rs b/rust/tw_hash/src/lib.rs index 8c8e83c80bf..f7cfe16d911 100644 --- a/rust/tw_hash/src/lib.rs +++ b/rust/tw_hash/src/lib.rs @@ -6,6 +6,7 @@ pub mod blake; pub mod blake2; +pub mod crc32; pub mod ffi; pub mod groestl; pub mod hmac; diff --git a/rust/tw_hash/src/sha2.rs b/rust/tw_hash/src/sha2.rs index ad448769bbf..0ac313162c2 100644 --- a/rust/tw_hash/src/sha2.rs +++ b/rust/tw_hash/src/sha2.rs @@ -5,7 +5,11 @@ // file LICENSE at the root of the source code distribution tree. use crate::hash_wrapper::hasher; -use sha2::{Sha256, Sha512, Sha512_256}; +use sha2::{Sha224, Sha256, Sha512, Sha512_256}; + +pub fn sha224(input: &[u8]) -> Vec { + hasher::(input) +} pub fn sha256(input: &[u8]) -> Vec { hasher::(input) diff --git a/rust/tw_internet_computer/Cargo.toml b/rust/tw_internet_computer/Cargo.toml new file mode 100644 index 00000000000..d774d4946ae --- /dev/null +++ b/rust/tw_internet_computer/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "tw_internet_computer" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +quick-protobuf = "0.8.1" +serde = { version = "1.0.136", features = ["derive"] } +tw_coin_entry = { path = "../tw_coin_entry" } +tw_encoding = { path = "../tw_encoding" } +tw_hash = { path = "../tw_hash" } +tw_keypair = { path = "../tw_keypair" } +tw_memory = { path = "../tw_memory" } +tw_proto = { path = "../tw_proto" } + +[build-dependencies] +pb-rs = "0.10.0" diff --git a/rust/tw_internet_computer/build.rs b/rust/tw_internet_computer/build.rs new file mode 100644 index 00000000000..a163f826026 --- /dev/null +++ b/rust/tw_internet_computer/build.rs @@ -0,0 +1,54 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use pb_rs::types::FileDescriptor; +use pb_rs::ConfigBuilder; +use std::path::{Path, PathBuf}; +use std::{env, fs}; + +fn main() { + let proto_ext = Some(Path::new("proto").as_os_str()); + + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()).join("proto"); + + let proto_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()) + .join("src") + .join("transactions") + .join("proto"); + let proto_dir_str = proto_dir.to_str().expect("Invalid proto directory path"); + // Re-run this build.rs if the `proto` directory has been changed (i.e. a new file is added). + println!("cargo:rerun-if-changed={}", proto_dir_str); + + let protos: Vec<_> = fs::read_dir(&proto_dir) + .expect("Expected a valid directory with proto files") + .filter_map(|file| { + let file = file.ok()?; + if file.path().extension() != proto_ext { + return None; + } + + let path = file.path(); + let path_str = path.to_str().expect("Invalid Proto file name"); + println!("cargo:rerun-if-changed={}", path_str); + Some(path) + }) + .collect(); + + // Delete all old generated files before re-generating new ones + if out_dir.exists() { + fs::remove_dir_all(&out_dir).expect("Error removing out directory"); + } + fs::DirBuilder::new() + .create(&out_dir) + .expect("Error creating out directory"); + + let out_protos = ConfigBuilder::new(&protos, None, Some(&out_dir), &[proto_dir]) + .expect("Error configuring pb-rs builder") + .dont_use_cow(true) + .owned(true) + .build(); + FileDescriptor::run(&out_protos).expect("Error generating proto files"); +} diff --git a/rust/tw_internet_computer/fuzz/.gitignore b/rust/tw_internet_computer/fuzz/.gitignore new file mode 100644 index 00000000000..1a45eee7760 --- /dev/null +++ b/rust/tw_internet_computer/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/rust/tw_internet_computer/fuzz/Cargo.lock b/rust/tw_internet_computer/fuzz/Cargo.lock new file mode 100644 index 00000000000..887d622e7ea --- /dev/null +++ b/rust/tw_internet_computer/fuzz/Cargo.lock @@ -0,0 +1,1595 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "arbitrary" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest 0.10.7", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bigdecimal" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d1988118c887f61418940e322d574e8a2dd67165f1f1556eaae22e4019c6af" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "ppv-lite86", +] + +[[package]] +name = "blake2" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a4e37d16930f5459780f5621038b6382b9bb37c19016f39fb6b5808d831f174" +dependencies = [ + "crypto-mac", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "blake2b-ref" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "294d17c72e0ba59fad763caa112368d0672083779cdebbb97164f4bb4c1e339a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ciborium" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" + +[[package]] +name = "ciborium-ll" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "data-encoding" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.31", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "elliptic-curve" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "hkdf", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "groestl" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343cfc165f92a988fd60292f7a0bfde4352a5a0beff9fbec29251ca4e9676e4d" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2 0.10.7", + "signature", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.7", +] + +[[package]] +name = "parity-scale-codec" +version = "3.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d640b40e81625f53b59f9c5812d3fb9e7ce11971d3fb34268eb7a83adf98051" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ac464815d51fff2f64d690bf6ea4442d365e53495d50737685cfecfa3dd3f62" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pb-rs" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "354a34df9c65b596152598001c0fe3393379ec2db03ae30b9985659422e2607e" +dependencies = [ + "clap", + "env_logger", + "log", + "nom", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "primeorder" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c2fcef82c0ec6eefcc179b978446c399b3cdf73c392c35604e399eee6df1ee3" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "primitive-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-protobuf" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" +dependencies = [ + "byteorder", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.10", +] + +[[package]] +name = "regex" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" + +[[package]] +name = "serde" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.31", +] + +[[package]] +name = "serde_json" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "starknet-crypto" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693e6362f150f9276e429a910481fb7f3bcb8d6aa643743f587cfece0b374874" +dependencies = [ + "crypto-bigint", + "hex", + "hmac", + "num-bigint", + "num-integer", + "num-traits", + "rfc6979", + "sha2 0.10.7", + "starknet-crypto-codegen", + "starknet-curve 0.3.0", + "starknet-ff", + "zeroize", +] + +[[package]] +name = "starknet-crypto-codegen" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af6527b845423542c8a16e060ea1bc43f67229848e7cd4c4d80be994a84220ce" +dependencies = [ + "starknet-curve 0.4.0", + "starknet-ff", + "syn 2.0.31", +] + +[[package]] +name = "starknet-curve" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "252610baff59e4c4332ce3569f7469c5d3f9b415a2240d698fb238b2b4fc0942" +dependencies = [ + "starknet-ff", +] + +[[package]] +name = "starknet-curve" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68a0d87ae56572abf83ddbfd44259a7c90dbeeee1629a1ffe223e7f9a8f3052" +dependencies = [ + "starknet-ff", +] + +[[package]] +name = "starknet-ff" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2cb1d9c0a50380cddab99cb202c6bfb3332728a2769bd0ca2ee80b0b390dd4" +dependencies = [ + "ark-ff", + "bigdecimal", + "crypto-bigint", + "getrandom 0.2.10", + "hex", + "serde", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tw_coin_entry" +version = "0.1.0" +dependencies = [ + "serde_json", + "tw_keypair", + "tw_memory", + "tw_misc", + "tw_number", + "tw_proto", +] + +[[package]] +name = "tw_encoding" +version = "0.1.0" +dependencies = [ + "bs58", + "ciborium", + "data-encoding", + "hex", + "serde", + "tw_memory", +] + +[[package]] +name = "tw_hash" +version = "0.1.0" +dependencies = [ + "blake-hash", + "blake2b-ref", + "digest 0.10.7", + "groestl", + "hmac", + "ripemd", + "serde", + "sha1", + "sha2 0.10.7", + "sha3", + "tw_encoding", + "tw_memory", + "zeroize", +] + +[[package]] +name = "tw_internet_computer" +version = "0.1.0" +dependencies = [ + "pb-rs", + "quick-protobuf", + "serde", + "tw_coin_entry", + "tw_encoding", + "tw_hash", + "tw_keypair", + "tw_memory", + "tw_proto", +] + +[[package]] +name = "tw_internet_computer-fuzz" +version = "0.0.0" +dependencies = [ + "libfuzzer-sys", + "tw_internet_computer", + "tw_keypair", +] + +[[package]] +name = "tw_keypair" +version = "0.1.0" +dependencies = [ + "blake2", + "curve25519-dalek", + "der", + "digest 0.9.0", + "ecdsa", + "k256", + "lazy_static", + "p256", + "pkcs8", + "rfc6979", + "serde", + "sha2 0.9.9", + "starknet-crypto", + "starknet-ff", + "tw_encoding", + "tw_hash", + "tw_memory", + "tw_misc", + "zeroize", +] + +[[package]] +name = "tw_memory" +version = "0.1.0" + +[[package]] +name = "tw_misc" +version = "0.1.0" +dependencies = [ + "zeroize", +] + +[[package]] +name = "tw_number" +version = "0.1.0" +dependencies = [ + "primitive-types", + "tw_hash", + "tw_memory", +] + +[[package]] +name = "tw_proto" +version = "0.1.0" +dependencies = [ + "pb-rs", + "quick-protobuf", + "tw_encoding", + "tw_memory", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.31", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.31", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winnow" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.31", +] diff --git a/rust/tw_internet_computer/fuzz/Cargo.toml b/rust/tw_internet_computer/fuzz/Cargo.toml new file mode 100644 index 00000000000..a31848778a8 --- /dev/null +++ b/rust/tw_internet_computer/fuzz/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "tw_internet_computer-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = { version = "0.4", features = ["arbitrary-derive"] } + +[dependencies.tw_internet_computer] +path = ".." + + +[dependencies.tw_keypair] +path = "../../tw_keypair" + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[profile.release] +debug = 1 + +[[bin]] +name = "tw_internet_computer_transfer" +path = "fuzz_targets/tw_internet_computer_transfer.rs" +test = false +doc = false diff --git a/rust/tw_internet_computer/fuzz/fuzz_targets/tw_internet_computer_transfer.rs b/rust/tw_internet_computer/fuzz/fuzz_targets/tw_internet_computer_transfer.rs new file mode 100644 index 00000000000..cfd4b494762 --- /dev/null +++ b/rust/tw_internet_computer/fuzz/fuzz_targets/tw_internet_computer_transfer.rs @@ -0,0 +1,70 @@ +#![no_main] + +use libfuzzer_sys::{arbitrary, fuzz_target}; +use tw_internet_computer::{ + address::AccountIdentifier, + protocol::principal::Principal, + transactions::transfer::{transfer, TransferArgs}, +}; +use tw_keypair::ecdsa::secp256k1; + +#[derive(Debug, arbitrary::Arbitrary)] +struct ArbitraryTransferArgs { + memo: u64, + amount: u64, + max_fee: Option, + #[arbitrary(with = arbitrary_to_field)] + to: String, + current_timestamp_nanos: u64, +} + +fn arbitrary_to_field(u: &mut arbitrary::Unstructured) -> arbitrary::Result { + let mut buf = [0; 29]; + u.fill_buffer(&mut buf)?; + let principal = Principal::from_slice(&buf); + let account_identifier = AccountIdentifier::new(&principal); + Ok(account_identifier.to_hex()) +} + +impl From<&ArbitraryTransferArgs> for TransferArgs { + fn from(prev: &ArbitraryTransferArgs) -> TransferArgs { + TransferArgs { + memo: prev.memo, + amount: prev.amount, + max_fee: prev.max_fee, + to: prev.to.clone(), + current_timestamp_nanos: prev.current_timestamp_nanos, + } + } +} + +#[derive(Debug, arbitrary::Arbitrary)] +struct TWInternetComputerTransactionsTransferInput { + #[arbitrary(with = arbitrary_private_key)] + private: Vec, + #[arbitrary(with = arbitrary_canister_id)] + canister_id: Principal, + args: ArbitraryTransferArgs, +} + +fn arbitrary_private_key(u: &mut arbitrary::Unstructured) -> arbitrary::Result> { + let mut buf = [0; 32]; + u.fill_buffer(&mut buf)?; + Ok(Vec::from(buf.as_slice())) +} + +fn arbitrary_canister_id(u: &mut arbitrary::Unstructured) -> arbitrary::Result { + let mut buf = [0; 29]; + u.fill_buffer(&mut buf)?; + let principal = Principal::from_slice(&buf); + Ok(principal) +} + +fuzz_target!(|input: TWInternetComputerTransactionsTransferInput| { + let Ok(private_key) = secp256k1::PrivateKey::try_from(input.private.as_slice()) else { + return; + }; + + let args = TransferArgs::from(&input.args); + transfer(private_key, input.canister_id, args).ok(); +}); diff --git a/rust/tw_internet_computer/src/address.rs b/rust/tw_internet_computer/src/address.rs new file mode 100644 index 00000000000..17ba0ed3ee4 --- /dev/null +++ b/rust/tw_internet_computer/src/address.rs @@ -0,0 +1,184 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::{ + coin_entry::CoinAddress, + error::{AddressError, AddressResult}, +}; +use tw_encoding::hex; +use tw_hash::{crc32::crc32, sha2::sha224, H256}; +use tw_keypair::ecdsa::secp256k1::PublicKey; + +use crate::protocol::principal::Principal; + +pub trait IcpAddress: std::str::FromStr + Into { + fn from_str_optional(s: &str) -> AddressResult> { + if s.is_empty() { + return Ok(None); + } + + Self::from_str(s).map(Some) + } +} + +/// The ICP ledger keeps track of accounts using account identifiers. +/// An account identifier is created by `SHA-224` hashing: +/// * \x0Aaccount-id +/// * the owner's principal ID +/// * subaccount (32-bytes) +/// Then, +/// * CRC32 the SHA-224 hash +/// * Prepend the CRC32 to the SHA-224. +/// https://internetcomputer.org/docs/current/references/ledger/#_accounts +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AccountIdentifier { + bytes: H256, +} + +impl AccountIdentifier { + /// Create a default account identifier from the given principal owner. + pub fn new(owner: &Principal) -> Self { + let mut input = vec![]; + input.extend_from_slice(b"\x0Aaccount-id"); + input.extend_from_slice(owner.as_slice()); + input.extend_from_slice(&H256::new()[..]); + + let hash = sha224(&input); + let crc32_bytes = crc32(&hash).to_be_bytes(); + + let mut bytes = H256::new(); + bytes[0..4].copy_from_slice(&crc32_bytes); + bytes[4..32].copy_from_slice(&hash); + Self { bytes } + } + + /// Return the textual-encoded account identifier. + pub fn to_hex(&self) -> String { + hex::encode(self.bytes, false) + } + + /// Instantiate an account identifier from a hex-encoded string. + pub fn from_hex(hex_str: &str) -> AddressResult { + if hex_str.len() != 64 { + return Err(AddressError::FromHexError); + } + + let hex = H256::try_from( + hex::decode(hex_str) + .map_err(|_| AddressError::FromHexError)? + .as_slice(), + ) + .map_err(|_| AddressError::FromHexError)?; + + if !is_check_sum_valid(hex) { + return Err(AddressError::FromHexError); + } + + Ok(Self { bytes: hex }) + } +} + +impl From<&PublicKey> for AccountIdentifier { + /// Takes a Secp256k1 public key, DER-encodes the public key, + /// and creates a principal from the encoding. + fn from(public_key: &PublicKey) -> Self { + let principal = Principal::from(public_key); + AccountIdentifier::new(&principal) + } +} + +impl std::str::FromStr for AccountIdentifier { + type Err = AddressError; + + fn from_str(s: &str) -> Result { + AccountIdentifier::from_hex(s).map_err(|_| AddressError::FromHexError) + } +} + +impl std::fmt::Display for AccountIdentifier { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.to_hex()) + } +} + +impl CoinAddress for AccountIdentifier { + fn data(&self) -> tw_memory::Data { + self.bytes.into_vec() + } +} + +impl IcpAddress for AccountIdentifier {} + +impl AsRef<[u8]> for AccountIdentifier { + fn as_ref(&self) -> &[u8] { + self.bytes.as_slice() + } +} + +fn is_check_sum_valid(hash: H256) -> bool { + let found_checksum = &hash[0..4]; + let expected_checksum = crc32(&hash[4..]).to_be_bytes(); + found_checksum == expected_checksum +} + +#[cfg(test)] +mod test { + use tw_keypair::ecdsa::secp256k1::PublicKey; + + use super::*; + + const VALID_ADDRESSES: [&str; 10] = [ + "fb257577279ecac604d4780214af95aa6adc3a814f6f3d6d7ac844d1deca500a", + "e90c48d54847f4758f1d6b589a1db2500757a49a6722d4f775e050107b4b752d", + "a7c5baf393aed527ef6fb3869fbf84dd4e562edf9b04bd8f9bfbd6b8e6a22776", + "4cb2ca5cfcfa1d952f8cd7f0ec46c96e1023ab057b83a2c7ce236b9e71ccca0b", + "a93fff2708a6305e8946a0a06cbf559da01a758da58a615d404037b08ea96181", + "6e66c78a45cec01bcd0efd6dd142a82dc63b1a591c4ccb3c5877cd4d667747b4", + "c4ca697b46bb89ebf19eef3ad7b5e7bfa73315c0433a68a12a67f60fe017b9ad", + "7c513ec0de7347555b75cfefe29e56689de36636321fb0c8addf24a4f934ff0b", + "f61e15cdcaf0325bbaeb9a23a9f49d5447b33e6feee9763c2fdfe3a986142912", + "bb3357cba483f268d044d4bbd4639f82c16028a76eebdf62c51bc11fc918d278", + ]; + + const TOO_SHORT_ADDRESS: &str = + "3357cba483f268d044d4bbd4639f82c16028a76eebdf62c51bc11fc918d278b"; + const TOO_LONG_ADDRESS: &str = + "3357cba483f268d044d4bbd4639f82c16028a76eebdf62c51bc11fc918d278bce"; + const INVALID_CHECKSUM_ADDRESS: &str = + "553357cba483f268d044d4bbd4639f82c16028a76eebdf62c51bc11fc918d278"; + + const PUBLIC_KEY_HEX: &str = "048542e6fb4b17d6dfcac3948fe412c00d626728815ee7cc70509603f1bc92128a6e7548f3432d6248bc49ff44a1e50f6389238468d17f7d7024de5be9b181dbc8"; + + #[test] + fn from_hex() { + assert!(VALID_ADDRESSES + .iter() + .all(|s| AccountIdentifier::from_hex(s).is_ok())); + + assert!( + AccountIdentifier::from_hex(TOO_SHORT_ADDRESS).is_err(), + "Address is too short" + ); + assert!( + AccountIdentifier::from_hex(TOO_LONG_ADDRESS).is_err(), + "Address is too long" + ); + assert!( + AccountIdentifier::from_hex(INVALID_CHECKSUM_ADDRESS).is_err(), + "Invalid checksum" + ); + } + + #[test] + fn from_public_key() { + let public_key = PublicKey::try_from(PUBLIC_KEY_HEX).expect("Failed to populate key"); + let address = AccountIdentifier::from(&public_key); + assert_eq!( + address.to_hex(), + "2f25874478d06cf68b9833524a6390d0ba69c566b02f46626979a3d6a4153211" + ); + } +} diff --git a/rust/tw_internet_computer/src/context.rs b/rust/tw_internet_computer/src/context.rs new file mode 100644 index 00000000000..45e2a3d5e5d --- /dev/null +++ b/rust/tw_internet_computer/src/context.rs @@ -0,0 +1,77 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::{ + address::{AccountIdentifier, IcpAddress}, + protocol::principal::Principal, +}; + +pub trait InternetComputerContext { + type Address: IcpAddress; + + fn get_canister_id() -> Principal; +} + +#[derive(Default)] +pub struct StandardInternetComputerContext; + +impl InternetComputerContext for StandardInternetComputerContext { + type Address = AccountIdentifier; + + fn get_canister_id() -> Principal { + // ICP Ledger Canister + Principal::from_text("ryjl3-tyaaa-aaaaa-aaaba-cai").unwrap() + } +} + +#[cfg(test)] +mod test { + + use std::marker::PhantomData; + + use tw_coin_entry::error::AddressResult; + + use super::*; + + const TEST_OWNER_PRINCIPAL_ID: &str = + "t4u4z-y3dur-j63pk-nw4rv-yxdbt-agtt6-nygn7-ywh6y-zm2f4-sdzle-3qe"; + const TEST_TEXTUAL_ICP_ADDRESS: &str = + "943d12e762f43806782f524b8f90297298a6d79e4749b41b585ec427409c826a"; + const TEXTUAL_ICP_LEDGER_CANISTER_ID: &str = "ryjl3-tyaaa-aaaaa-aaaba-cai"; + + pub struct ContextTest { + _phantom: PhantomData, + } + + impl ContextTest { + fn get_canister_id() -> Principal { + Context::get_canister_id() + } + + fn account_identifier_optional(s: &str) -> AddressResult> { + Context::Address::from_str_optional(s) + } + } + + #[test] + fn standard_internet_computer_context_canister_address() { + let owner = Principal::from_text(TEST_OWNER_PRINCIPAL_ID).unwrap(); + let expected_account_identifier = AccountIdentifier::new(&owner); + let account_identifier = + ContextTest::::account_identifier_optional( + TEST_TEXTUAL_ICP_ADDRESS, + ) + .unwrap() + .unwrap(); + assert_eq!(expected_account_identifier, account_identifier); + } + + #[test] + fn standard_internet_computer_context_canister_type() { + let ledger_canister_id = ContextTest::::get_canister_id(); + assert_eq!(ledger_canister_id.to_text(), TEXTUAL_ICP_LEDGER_CANISTER_ID); + } +} diff --git a/rust/tw_internet_computer/src/entry.rs b/rust/tw_internet_computer/src/entry.rs new file mode 100644 index 00000000000..a9542fbb8ea --- /dev/null +++ b/rust/tw_internet_computer/src/entry.rs @@ -0,0 +1,102 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::str::FromStr; + +use tw_coin_entry::{ + coin_context::CoinContext, + coin_entry::CoinEntry, + error::{AddressError, AddressResult, SigningError}, + modules::{ + json_signer::NoJsonSigner, message_signer::NoMessageSigner, plan_builder::NoPlanBuilder, + }, + prefix::NoPrefix, + signing_output_error, +}; + +use tw_proto::{ + Common::Proto::SigningError as CommonError, InternetComputer::Proto, + TxCompiler::Proto as CompilerProto, +}; + +use crate::{address::AccountIdentifier, context::StandardInternetComputerContext, signer::Signer}; + +pub struct InternetComputerEntry; + +impl CoinEntry for InternetComputerEntry { + type AddressPrefix = NoPrefix; + + type Address = AccountIdentifier; + + type SigningInput<'a> = Proto::SigningInput<'a>; + + type SigningOutput = Proto::SigningOutput<'static>; + + type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + + type JsonSigner = NoJsonSigner; + + type PlanBuilder = NoPlanBuilder; + + type MessageSigner = NoMessageSigner; + + #[inline] + fn parse_address( + &self, + _coin: &dyn CoinContext, + address: &str, + _prefix: Option, + ) -> AddressResult { + Self::Address::from_str(address) + } + + #[inline] + fn derive_address( + &self, + _coin: &dyn tw_coin_entry::coin_context::CoinContext, + public_key: tw_keypair::tw::PublicKey, + _derivation: tw_coin_entry::derivation::Derivation, + _prefix: Option, + ) -> tw_coin_entry::error::AddressResult { + let secp256k1_public_key = public_key + .to_secp256k1() + .ok_or(AddressError::PublicKeyTypeMismatch)?; + Ok(Self::Address::from(secp256k1_public_key)) + } + + #[inline] + fn sign( + &self, + _coin: &dyn tw_coin_entry::coin_context::CoinContext, + input: Self::SigningInput<'_>, + ) -> Self::SigningOutput { + Signer::::sign_proto(input) + } + + fn preimage_hashes( + &self, + _coin: &dyn tw_coin_entry::coin_context::CoinContext, + _input: Self::SigningInput<'_>, + ) -> Self::PreSigningOutput { + signing_output_error!( + CompilerProto::PreSigningOutput, + SigningError(CommonError::Error_not_supported) + ) + } + + fn compile( + &self, + _coin: &dyn tw_coin_entry::coin_context::CoinContext, + _input: Self::SigningInput<'_>, + _signatures: Vec, + _public_keys: Vec, + ) -> Self::SigningOutput { + signing_output_error!( + Proto::SigningOutput, + SigningError(CommonError::Error_not_supported) + ) + } +} diff --git a/rust/tw_internet_computer/src/lib.rs b/rust/tw_internet_computer/src/lib.rs new file mode 100644 index 00000000000..c32ab978e54 --- /dev/null +++ b/rust/tw_internet_computer/src/lib.rs @@ -0,0 +1,12 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod address; +pub mod context; +pub mod entry; +pub mod protocol; +pub mod signer; +pub mod transactions; diff --git a/rust/tw_internet_computer/src/protocol/envelope.rs b/rust/tw_internet_computer/src/protocol/envelope.rs new file mode 100644 index 00000000000..cfa00cdd274 --- /dev/null +++ b/rust/tw_internet_computer/src/protocol/envelope.rs @@ -0,0 +1,237 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::collections::BTreeMap; + +use serde::{Serialize, Serializer}; + +use super::{ + principal::Principal, + request_id::{hash_of_map, RawHttpRequestVal, RequestId}, +}; + +pub trait RepresentationHashable { + fn request_id(&self) -> RequestId; +} + +#[derive(Debug, Clone, Serialize)] +pub struct Envelope { + pub content: C, + #[serde(skip_serializing_if = "Option::is_none")] + pub sender_pubkey: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub sender_sig: Option>, +} + +/// A replicated call to a canister method, whether update or query. +#[derive(Debug, Clone, Serialize)] +#[serde(tag = "request_type", rename = "call")] +pub struct EnvelopeCallContent { + /// A random series of bytes to uniquely identify this message. + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "serialize_nonce" + )] + pub nonce: Option>, + /// A nanosecond timestamp after which this request is no longer valid. + pub ingress_expiry: u64, + /// The principal that is sending this request. + pub sender: Principal, + /// The ID of the canister to be called. + pub canister_id: Principal, + /// The name of the canister method to be called. + pub method_name: String, + /// The argument to pass to the canister method. + #[serde(serialize_with = "serialize_arg")] + pub arg: Vec, +} + +impl RepresentationHashable for EnvelopeCallContent { + fn request_id(&self) -> RequestId { + let mut map = vec![ + ( + "request_type".to_string(), + RawHttpRequestVal::String("call".to_string()), + ), + ( + "canister_id".to_string(), + RawHttpRequestVal::Bytes(self.canister_id.as_slice().to_vec()), + ), + ( + "method_name".to_string(), + RawHttpRequestVal::String(self.method_name.to_string()), + ), + ( + "arg".to_string(), + RawHttpRequestVal::Bytes(self.arg.clone()), + ), + ( + "ingress_expiry".to_string(), + RawHttpRequestVal::U64(self.ingress_expiry), + ), + ( + "sender".to_string(), + RawHttpRequestVal::Bytes(self.sender.as_slice().to_vec()), + ), + ] + .into_iter() + .collect::>(); + + if let Some(some_nonce) = &self.nonce { + map.insert( + "nonce".to_string(), + RawHttpRequestVal::Bytes(some_nonce.clone()), + ); + } + RequestId(hash_of_map(&map)) + } +} + +/// A request for information from the [IC state tree](https://internetcomputer.org/docs/current/references/ic-interface-spec#state-tree). +#[derive(Debug, Clone, Serialize)] +#[serde(tag = "request_type", rename = "read_state")] +pub struct EnvelopeReadStateContent { + /// A nanosecond timestamp after which this request is no longer valid. + pub ingress_expiry: u64, + /// The principal that is sending this request. + pub sender: Principal, + /// A list of paths within the state tree to fetch. + pub paths: Vec>, +} + +impl RepresentationHashable for EnvelopeReadStateContent { + fn request_id(&self) -> RequestId { + let map = vec![ + ( + "request_type".to_string(), + RawHttpRequestVal::String("read_state".to_string()), + ), + ( + "ingress_expiry".to_string(), + RawHttpRequestVal::U64(self.ingress_expiry), + ), + ( + "paths".to_string(), + RawHttpRequestVal::Array( + self.paths + .iter() + .map(|p| { + RawHttpRequestVal::Array( + p.iter() + .map(|b| RawHttpRequestVal::Bytes(b.as_slice().to_vec())) + .collect(), + ) + }) + .collect(), + ), + ), + ( + "sender".to_string(), + RawHttpRequestVal::Bytes(self.sender.as_slice().to_vec()), + ), + ] + .into_iter() + .collect::>(); + + RequestId(hash_of_map(&map)) + } +} + +fn serialize_arg(arg: &[u8], s: S) -> Result +where + S: Serializer, +{ + s.serialize_bytes(arg) +} + +fn serialize_nonce(nonce: &Option>, s: S) -> Result +where + S: Serializer, +{ + match nonce { + Some(nonce) => s.serialize_bytes(nonce), + None => s.serialize_none(), + } +} + +#[derive(Debug, Clone)] +pub struct Label(Vec); + +impl Label { + #[inline] + pub fn as_slice(&self) -> &[u8] { + self.0.as_slice() + } +} + +impl From<&str> for Label { + fn from(value: &str) -> Self { + Label(value.as_bytes().to_vec()) + } +} + +impl From for Label { + fn from(value: RequestId) -> Self { + Label(value.0.as_slice().to_vec()) + } +} + +// Serialization +impl serde::Serialize for Label { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_bytes(self.0.as_slice()) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn representation_independent_hash_call_or_query() { + let content = EnvelopeCallContent { + ingress_expiry: 1685570400000000000, + sender: Principal::anonymous(), + canister_id: Principal::from_slice(&[0, 0, 0, 0, 0, 0, 4, 210]), + method_name: "hello".to_string(), + arg: b"DIDL\x00\xFD*".to_vec(), + nonce: None, + }; + + assert_eq!( + tw_encoding::hex::encode(content.request_id().0, false), + "1d1091364d6bb8a6c16b203ee75467d59ead468f523eb058880ae8ec80e2b101" + ); + } + + #[test] + fn representation_independent_hash_read_state() { + let content = EnvelopeCallContent { + ingress_expiry: 1685570400000000000, + sender: Principal::anonymous(), + canister_id: Principal::from_slice(&[0, 0, 0, 0, 0, 0, 4, 210]), + method_name: "hello".to_string(), + arg: b"DIDL\x00\xFD*".to_vec(), + nonce: None, + }; + let update_request_id = content.request_id(); + + let content = EnvelopeReadStateContent { + ingress_expiry: 1685570400000000000, + sender: Principal::anonymous(), + paths: vec![vec![ + Label::from("request_status"), + Label::from(update_request_id), + ]], + }; + let request_id = content.request_id(); + assert_eq!( + tw_encoding::hex::encode(request_id.0, false), + "3cde0f14a953c3afbe1335f22e861bb62389f1449beca02707ab197e6829c2a3" + ); + } +} diff --git a/rust/tw_internet_computer/src/protocol/identity.rs b/rust/tw_internet_computer/src/protocol/identity.rs new file mode 100644 index 00000000000..ed8ce0f7cbb --- /dev/null +++ b/rust/tw_internet_computer/src/protocol/identity.rs @@ -0,0 +1,116 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_hash::H256; +use tw_keypair::{ecdsa::secp256k1::PrivateKey, traits::SigningKeyTrait, KeyPairError}; + +use super::principal::Principal; + +#[derive(Debug)] +pub enum SigningError { + Failed(KeyPairError), +} + +/// Contains the signature and the associated DER-encoded public key from a call to +/// [Identity::sign]. +pub struct IdentitySignature { + pub signature: Vec, + pub public_key: Vec, +} + +/// An identity is a simple way to abstract away signing for envelopes. +/// When creating a request to the IC, the sender and signature are required +/// for authentication purposes. The sender is derived from a DER-encode public key +/// and the signature is created using the private key. +pub struct Identity { + private_key: PrivateKey, + der_encoded_public_key: Vec, +} + +impl Identity { + /// Gets the public key and DER-encodes it and returns an Identity. + pub fn new(private_key: PrivateKey) -> Self { + let public_key = private_key.public(); + let der_encoded_public_key = public_key.der_encoded(); + + Self { + private_key, + der_encoded_public_key, + } + } + + /// Returns the principal of the private key. + /// Sender represents who is sending the request. + pub fn sender(&self) -> Principal { + Principal::self_authenticating(&self.der_encoded_public_key) + } + + /// Signs the given content with the private key. + /// The signatures are encoded as the concatenation of the 32-byte big endian + /// encodings of the two values r and s. + /// + /// See: https://internetcomputer.org/docs/current/references/ic-interface-spec#ecdsa + pub fn sign(&self, content: H256) -> Result { + let signature = self + .private_key + .sign(content) + .map_err(SigningError::Failed)?; + + let r = signature.r(); + let s = signature.s(); + let mut bytes = [0u8; 64]; + bytes[..32].clone_from_slice(r.as_slice()); + bytes[32..].clone_from_slice(s.as_slice()); + + let signature = IdentitySignature { + public_key: self.der_encoded_public_key.clone(), + signature: bytes.to_vec(), //Signature bytes + }; + + Ok(signature) + } +} + +#[cfg(test)] +mod test { + + use tw_encoding::hex; + + use super::*; + + /// Test that the sender is derived from the private key. + #[test] + fn sender() { + let private_key = PrivateKey::try_from( + "227102911bb99ce7285a55f952800912b7d22ebeeeee59d77fc33a5d7c7080be", + ) + .unwrap(); + let identity = Identity::new(private_key); + let sender = identity.sender(); + assert_eq!( + sender.to_text(), + "hpikg-6exdt-jn33w-ndty3-fc7jc-tl2lr-buih3-cs3y7-tftkp-sfp62-gqe" + ) + } + + /// Test signing with the identity. + #[test] + fn sign() { + let private_key = PrivateKey::try_from( + "227102911bb99ce7285a55f952800912b7d22ebeeeee59d77fc33a5d7c7080be", + ) + .unwrap(); + let der_encoded_public_key = private_key.public().der_encoded(); + let identity = Identity::new(private_key); + let content = H256::new(); + let signature = identity.sign(content).unwrap(); + assert_eq!( + hex::encode(signature.signature, false), + "17c0974ee2ae621099389a5e4d0f960925d2e0e23658df03069308fb8edcb7bb120a338ada3e2ede7f41f6ed2f424a8a4f2c8fb68260f27d4f1bf96d19094b9f" + ); + assert_eq!(der_encoded_public_key, signature.public_key); + } +} diff --git a/rust/tw_internet_computer/src/protocol/mod.rs b/rust/tw_internet_computer/src/protocol/mod.rs new file mode 100644 index 00000000000..f21353b57b7 --- /dev/null +++ b/rust/tw_internet_computer/src/protocol/mod.rs @@ -0,0 +1,35 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod envelope; +pub mod identity; +pub mod principal; +pub mod request_id; +pub mod rosetta; + +use std::time::Duration; + +/// This constant defines the maximum amount of time an ingress message can wait +/// to start executing after submission before it is expired. Hence, if an +/// ingress message is submitted at time `t` and it has not been scheduled for +/// execution till time `t+MAX_INGRESS_TTL`, it will be expired. +/// +/// At the time of writing, this constant is also used to control how long the +/// status of a completed ingress message (IngressStatus ∈ [Completed, Failed]) +/// is maintained by the IC before it is deleted from the ingress history. +const MAX_INGRESS_TTL: Duration = Duration::from_secs(5 * 60); + +/// Duration subtracted from `MAX_INGRESS_TTL` by +/// `expiry_time_from_now()` when creating an ingress message. +const PERMITTED_DRIFT: Duration = Duration::from_secs(60); + +/// An upper limit on the validity of the request, expressed in nanoseconds since 1970-01-01. +pub fn get_ingress_expiry(current_timestamp_duration: Duration) -> u64 { + current_timestamp_duration + .saturating_add(MAX_INGRESS_TTL) + .saturating_sub(PERMITTED_DRIFT) + .as_nanos() as u64 +} diff --git a/rust/tw_internet_computer/src/protocol/principal.rs b/rust/tw_internet_computer/src/protocol/principal.rs new file mode 100644 index 00000000000..61680e14f48 --- /dev/null +++ b/rust/tw_internet_computer/src/protocol/principal.rs @@ -0,0 +1,256 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +/// Taken from candid crate and modified to rely upon built-in crc32 and SHA224 functionality. +use std::fmt::Write; + +use tw_hash::{crc32::crc32, sha2::sha224}; +use tw_keypair::ecdsa::secp256k1::PublicKey; + +/// An error happened while encoding, decoding or serializing a [`Principal`]. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum PrincipalError { + BytesTooLong, + InvalidBase32, + TextTooShort, + TextTooLong, + CheckSequenceNotMatch, + AbnormalGrouped(Principal), +} + +/// Generic ID on Internet Computer. +/// +/// Principals are generic identifiers for canisters, users +/// and possibly other concepts in the future. +/// As far as most uses of the IC are concerned they are +/// opaque binary blobs with a length between 0 and 29 bytes, +/// and there is intentionally no mechanism to tell canister ids and user ids apart. +/// +/// Note a principal is not necessarily tied with a public key-pair, +/// yet we need at least a key-pair of a related principal to sign +/// requests. +/// +/// A Principal can be serialized to a byte array ([`Vec`]) or a text +/// representation, but the inner structure of the byte representation +/// is kept private. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Principal { + /// Length. + len: u8, + + /// The content buffer. When returning slices this should always be sized according to + /// `len`. + bytes: [u8; Self::MAX_LENGTH_IN_BYTES], +} + +impl Principal { + const MAX_LENGTH_IN_BYTES: usize = 29; + const CRC_LENGTH_IN_BYTES: usize = 4; + + const SELF_AUTHENTICATING_TAG: u8 = 2; + const ANONYMOUS_TAG: u8 = 4; + + /// Construct a [`Principal`] of the IC management canister + pub const fn management_canister() -> Self { + Self { + len: 0, + bytes: [0; Self::MAX_LENGTH_IN_BYTES], + } + } + + /// Construct a self-authenticating ID from public key + pub fn self_authenticating>(public_key: P) -> Self { + let public_key = public_key.as_ref(); + let hash = sha224(public_key); + let mut bytes = [0; Self::MAX_LENGTH_IN_BYTES]; + bytes[..Self::MAX_LENGTH_IN_BYTES - 1].copy_from_slice(hash.as_slice()); + bytes[Self::MAX_LENGTH_IN_BYTES - 1] = Self::SELF_AUTHENTICATING_TAG; + + Self { + len: Self::MAX_LENGTH_IN_BYTES as u8, + bytes, + } + } + + /// Construct an anonymous ID. + pub const fn anonymous() -> Self { + let mut bytes = [0; Self::MAX_LENGTH_IN_BYTES]; + bytes[0] = Self::ANONYMOUS_TAG; + Self { len: 1, bytes } + } + + /// Construct a [`Principal`] from a slice of bytes. + /// + /// # Panics + /// + /// Panics if the slice is longer than 29 bytes. + pub fn from_slice(slice: &[u8]) -> Self { + match Self::try_from_slice(slice) { + Ok(v) => v, + _ => panic!("slice length exceeds capacity"), + } + } + + /// Construct a [`Principal`] from a slice of bytes. + pub fn try_from_slice(slice: &[u8]) -> Result { + const MAX_LENGTH_IN_BYTES: usize = Principal::MAX_LENGTH_IN_BYTES; + if slice.len() > MAX_LENGTH_IN_BYTES { + return Err(PrincipalError::BytesTooLong); + } + + let mut bytes = [0; MAX_LENGTH_IN_BYTES]; + bytes[0..slice.len()].copy_from_slice(slice); + + Ok(Self { + len: slice.len() as u8, + bytes, + }) + } + + /// Parse a [`Principal`] from text representation. + pub fn from_text>(text: S) -> Result { + // Strategy: Parse very liberally, then pretty-print and compare output + // This is both simpler and yields better error messages + + let mut s = text.as_ref().to_string(); + s.make_ascii_uppercase(); + s.retain(|c| c != '-'); + + let bytes = tw_encoding::base32::decode(&s, None, false) + .map_err(|_| PrincipalError::InvalidBase32)?; + + if bytes.len() < Self::CRC_LENGTH_IN_BYTES { + return Err(PrincipalError::TextTooShort); + } + + let crc_bytes = &bytes[..Self::CRC_LENGTH_IN_BYTES]; + let data_bytes = &bytes[Self::CRC_LENGTH_IN_BYTES..]; + if data_bytes.len() > Self::MAX_LENGTH_IN_BYTES { + return Err(PrincipalError::TextTooLong); + } + + if crc32(data_bytes).to_be_bytes() != crc_bytes { + return Err(PrincipalError::CheckSequenceNotMatch); + } + + // Already checked data_bytes.len() <= MAX_LENGTH_IN_BYTES + // safe to unwrap here + let result = Self::try_from_slice(data_bytes).unwrap(); + let expected = format!("{result}"); + + // In the Spec: + // The textual representation is conventionally printed with lower case letters, + // but parsed case-insensitively. + if text.as_ref().to_ascii_lowercase() != expected { + return Err(PrincipalError::AbnormalGrouped(result)); + } + Ok(result) + } + + /// Convert [`Principal`] to text representation. + pub fn to_text(&self) -> String { + format!("{self}") + } + + /// Return the [`Principal`]'s underlying slice of bytes. + #[inline] + pub fn as_slice(&self) -> &[u8] { + &self.bytes[..self.len as usize] + } +} + +impl std::str::FromStr for Principal { + type Err = PrincipalError; + + fn from_str(s: &str) -> Result { + Principal::from_text(s) + } +} + +impl From<&PublicKey> for Principal { + /// Takes a Secp256k1 public key, DER-encodes the public key, + /// and creates a principal from the encoding. + fn from(public_key: &PublicKey) -> Self { + Self::self_authenticating(public_key.der_encoded()) + } +} + +impl std::fmt::Display for Principal { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let blob: &[u8] = self.as_slice(); + + // calc checksum + let checksum = crc32(blob); + + // combine blobs + let mut bytes = vec![]; + bytes.extend_from_slice(&checksum.to_be_bytes()); + bytes.extend_from_slice(blob); + + // base32 + let mut s = + tw_encoding::base32::encode(&bytes, None, false).map_err(|_| std::fmt::Error)?; + s.make_ascii_lowercase(); + + // write out string with dashes + let mut s = s.as_str(); + while s.len() > 5 { + f.write_str(&s[..5])?; + f.write_char('-')?; + s = &s[5..]; + } + f.write_str(s) + } +} + +impl TryFrom<&str> for Principal { + type Error = PrincipalError; + + fn try_from(s: &str) -> Result { + Principal::from_text(s) + } +} + +impl TryFrom> for Principal { + type Error = PrincipalError; + + fn try_from(bytes: Vec) -> Result { + Self::try_from(bytes.as_slice()) + } +} + +impl TryFrom<&Vec> for Principal { + type Error = PrincipalError; + + fn try_from(bytes: &Vec) -> Result { + Self::try_from(bytes.as_slice()) + } +} + +impl TryFrom<&[u8]> for Principal { + type Error = PrincipalError; + + fn try_from(bytes: &[u8]) -> Result { + Self::try_from_slice(bytes) + } +} + +impl AsRef<[u8]> for Principal { + fn as_ref(&self) -> &[u8] { + self.as_slice() + } +} + +// Serialization +impl serde::Serialize for Principal { + fn serialize(&self, serializer: S) -> Result { + if serializer.is_human_readable() { + self.to_text().serialize(serializer) + } else { + serializer.serialize_bytes(self.as_slice()) + } + } +} diff --git a/rust/tw_internet_computer/src/protocol/request_id.rs b/rust/tw_internet_computer/src/protocol/request_id.rs new file mode 100644 index 00000000000..5f9d2bcf74b --- /dev/null +++ b/rust/tw_internet_computer/src/protocol/request_id.rs @@ -0,0 +1,127 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::collections::BTreeMap; + +use serde::{Deserialize, Serialize}; +use tw_hash::{sha2::sha256, H256}; + +const DOMAIN_IC_REQUEST: &[u8; 11] = b"\x0Aic-request"; + +/// When signing requests or querying the status of a request +/// (see Request status) in the state tree, the user identifies +/// the request using a request id, which is the +/// representation-independent hash of the content map of the +/// original request. A request id must have length of 32 bytes. +pub struct RequestId(pub(crate) H256); + +impl RequestId { + /// Create the prehash from the request ID. + /// See: https://internetcomputer.org/docs/current/references/ic-interface-spec#envelope-authentication + pub fn sig_data(&self) -> H256 { + let mut sig_data = vec![]; + sig_data.extend_from_slice(DOMAIN_IC_REQUEST); + sig_data.extend_from_slice(self.0.as_slice()); + H256::try_from(sha256(&sig_data).as_slice()).unwrap_or_else(|_| H256::new()) + } +} + +/// The different types of values supported in `RawHttpRequest`. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub enum RawHttpRequestVal { + Bytes(#[serde(serialize_with = "serialize_bytes")] Vec), + String(String), + U64(u64), + Array(Vec), +} + +fn serialize_bytes(bytes: &[u8], s: S) -> Result +where + S: serde::Serializer, +{ + s.serialize_bytes(bytes) +} + +fn hash_string(value: String) -> Vec { + sha256(value.as_bytes()) +} + +fn hash_bytes(value: Vec) -> Vec { + sha256(value.as_slice()) +} + +fn hash_u64(value: u64) -> Vec { + // We need at most ⌈ 64 / 7 ⌉ = 10 bytes to encode a 64 bit + // integer in LEB128. + let mut buf = [0u8; 10]; + let mut n = value; + let mut i = 0; + + loop { + let byte = (n & 0x7f) as u8; + n >>= 7; + + if n == 0 { + buf[i] = byte; + break; + } else { + buf[i] = byte | 0x80; + i += 1; + } + } + + hash_bytes(buf[..=i].to_vec()) +} + +// arrays, encoded as the concatenation of the hashes of the encodings of the +// array elements. +fn hash_array(elements: Vec) -> Vec { + let mut buffer = vec![]; + elements + .into_iter() + // Hash the encoding of all the array elements. + .for_each(|e| { + let mut hashed_val = hash_val(e); + buffer.append(&mut hashed_val); + }); + sha256(&buffer) +} + +fn hash_val(val: RawHttpRequestVal) -> Vec { + match val { + RawHttpRequestVal::String(string) => hash_string(string), + RawHttpRequestVal::Bytes(bytes) => hash_bytes(bytes), + RawHttpRequestVal::U64(integer) => hash_u64(integer), + RawHttpRequestVal::Array(elements) => hash_array(elements), + } +} + +fn hash_key_val(key: String, val: RawHttpRequestVal) -> Vec { + let mut key_hash = hash_string(key); + let mut val_hash = hash_val(val); + key_hash.append(&mut val_hash); + key_hash +} + +/// Describes `hash_of_map` as specified in the public spec. +/// See: https://internetcomputer.org/docs/current/references/ic-interface-spec#hash-of-map +pub fn hash_of_map(map: &BTreeMap) -> H256 { + let mut hashes: Vec> = Vec::new(); + for (key, val) in map.iter() { + hashes.push(hash_key_val(key.to_string(), val.clone())); + } + + // Computes hash by first sorting by "field name" hash, which is the + // same as sorting by concatenation of H(field name) · H(field value) + // (although in practice it's actually more stable in the presence of + // duplicated field names). Then concatenate all the hashes. + hashes.sort(); + + let buffer = hashes.into_iter().flatten().collect::>(); + let hash = sha256(&buffer); + + H256::try_from(hash.as_slice()).unwrap_or_else(|_| H256::new()) +} diff --git a/rust/tw_internet_computer/src/protocol/rosetta.rs b/rust/tw_internet_computer/src/protocol/rosetta.rs new file mode 100644 index 00000000000..45e2a0be888 --- /dev/null +++ b/rust/tw_internet_computer/src/protocol/rosetta.rs @@ -0,0 +1,54 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use super::envelope::{Envelope, EnvelopeCallContent, EnvelopeReadStateContent}; +use serde::Serialize; + +/// The types of requests that are available from the Rosetta node. +/// This enum is truncated to include support only for the +/// operations that this crate can currently perform. +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub enum RequestType { + // Aliases for backwards compatibility + #[serde(rename = "TRANSACTION")] + #[serde(alias = "Send")] + Send, +} + +/// The type (encoded as CBOR) returned by the Rosetta node's +/// /construction/combine endpoint. It contains the +/// IC calls to submit the transaction and to check the result. +pub type SignedTransaction = Vec; + +/// A vector of update/read-state calls for different ingress windows +/// of the same call. +pub type Request = (RequestType, Vec); + +#[derive(Debug, Clone)] +pub enum EnvelopePairError { + InvalidUpdateEnvelope, + InvalidReadStateEnvelope, +} + +/// A signed IC update call and the corresponding read-state call for +/// a particular ingress window. +#[derive(Debug, Clone, Serialize)] +pub struct EnvelopePair { + update: Envelope, + read_state: Envelope, +} + +impl EnvelopePair { + pub fn new( + update_envelope: Envelope, + read_state_envelope: Envelope, + ) -> Result { + Ok(Self { + update: update_envelope, + read_state: read_state_envelope, + }) + } +} diff --git a/rust/tw_internet_computer/src/signer.rs b/rust/tw_internet_computer/src/signer.rs new file mode 100644 index 00000000000..113dde0c27f --- /dev/null +++ b/rust/tw_internet_computer/src/signer.rs @@ -0,0 +1,78 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::marker::PhantomData; + +use tw_coin_entry::{ + error::{SigningError, SigningResult}, + signing_output_error, +}; +use tw_keypair::ecdsa::secp256k1; +use tw_proto::{Common::Proto::SigningError as CommonError, InternetComputer::Proto}; + +use crate::{ + context::InternetComputerContext, + protocol::identity, + transactions::{self, sign_transaction}, +}; + +impl From for SigningError { + fn from(error: transactions::SignTransactionError) -> Self { + match error { + transactions::SignTransactionError::InvalidArguments => { + SigningError(CommonError::Error_invalid_params) + }, + transactions::SignTransactionError::Identity(identity_error) => match identity_error { + identity::SigningError::Failed(_) => SigningError(CommonError::Error_signing), + }, + transactions::SignTransactionError::InvalidEnvelopePair + | transactions::SignTransactionError::EncodingArgsFailed => { + SigningError(CommonError::Error_internal) + }, + transactions::SignTransactionError::InvalidToAccountIdentifier => { + SigningError(CommonError::Error_invalid_address) + }, + transactions::SignTransactionError::InvalidAmount => { + SigningError(CommonError::Error_invalid_requested_token_amount) + }, + } + } +} + +pub struct Signer { + _phantom: PhantomData, +} + +impl Signer { + #[inline] + pub fn sign_proto(input: Proto::SigningInput<'_>) -> Proto::SigningOutput<'static> { + Self::sign_proto_impl(input) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn sign_proto_impl( + input: Proto::SigningInput<'_>, + ) -> SigningResult> { + let private_key = secp256k1::PrivateKey::try_from(input.private_key.as_ref())?; + + let Some(ref transaction) = input.transaction else { + return Err(SigningError(CommonError::Error_invalid_params)); + }; + + let canister_id = Context::get_canister_id(); + let signed_transaction = + sign_transaction(private_key, canister_id, &transaction.transaction_oneof) + .map_err(SigningError::from)?; + + let cbor_encoded_signed_transaction = tw_encoding::cbor::encode(&signed_transaction) + .map_err(|_| SigningError(CommonError::Error_internal))?; + + Ok(Proto::SigningOutput { + signed_transaction: cbor_encoded_signed_transaction.into(), + ..Proto::SigningOutput::default() + }) + } +} diff --git a/rust/tw_internet_computer/src/transactions/mod.rs b/rust/tw_internet_computer/src/transactions/mod.rs new file mode 100644 index 00000000000..e401c7e08f5 --- /dev/null +++ b/rust/tw_internet_computer/src/transactions/mod.rs @@ -0,0 +1,47 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod transfer; + +pub mod proto { + include!(concat!(env!("OUT_DIR"), "/proto/mod.rs")); +} + +use tw_keypair::ecdsa::secp256k1::PrivateKey; +use tw_proto::InternetComputer::Proto::mod_Transaction::OneOftransaction_oneof as Tx; + +use crate::protocol::{identity, principal::Principal, rosetta}; + +#[derive(Debug)] +pub enum SignTransactionError { + InvalidAmount, + InvalidArguments, + Identity(identity::SigningError), + EncodingArgsFailed, + InvalidToAccountIdentifier, + InvalidEnvelopePair, +} + +pub fn sign_transaction( + private_key: PrivateKey, + canister_id: Principal, + transaction: &Tx, +) -> Result { + match transaction { + Tx::transfer(transfer_args) => transfer::transfer( + private_key, + canister_id, + transfer::TransferArgs { + memo: transfer_args.memo, + amount: transfer_args.amount, + max_fee: None, + to: transfer_args.to_account_identifier.to_string(), + current_timestamp_nanos: transfer_args.current_timestamp_nanos, + }, + ), + Tx::None => Err(SignTransactionError::InvalidArguments), + } +} diff --git a/rust/tw_internet_computer/src/transactions/proto/ledger.proto b/rust/tw_internet_computer/src/transactions/proto/ledger.proto new file mode 100644 index 00000000000..726d2ffd74b --- /dev/null +++ b/rust/tw_internet_computer/src/transactions/proto/ledger.proto @@ -0,0 +1,315 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +// -*- c-basic-offset: 2 -*- +// Source: https://github.com/dfinity/ic/blob/master/rs/rosetta-api/icp_ledger/proto/ic_ledger/pb/v1/types.proto +// Commit Hash: 703eb96fea44ad2c82740e4360a526a2a127a960 +// Striped annotations related to the use of hardware wallets. + +syntax = "proto3"; + +package ic_ledger.pb.v1; + +import "./types.proto"; + +// Annotations related to the use of hardware wallets. The annotated messages are +// parsed on hardware wallets and marked fields are displayed in a trusted user +// interface (TUI). We must not, for instance, add fields that would change the +// semantics of the message such that old hardware wallets would not display +// appropriate information to users. + +// ** LEDGER CANISTER ENDPOINTS + +// Initialise the ledger canister +message LedgerInit { + AccountIdentifier minting_account = 1; + repeated Account initial_values = 2; + ic_base_types.pb.v1.PrincipalId archive_canister = 3; + uint32 max_message_size_bytes = 4; +} + +// The format of values serialized to/from the stable memory during and upgrade +message LedgerUpgrade {} + +// Make a payment +message SendRequest { + Memo memo = 1; + Payment payment = 2; + Tokens max_fee = 3; + Subaccount from_subaccount = 4; + AccountIdentifier to = 5; + BlockIndex created_at = 6; + TimeStamp created_at_time = 7; +} + +message SendResponse { + BlockIndex resulting_height = 1; +} + +// Notify a canister that it has received a payment +message NotifyRequest { + BlockIndex block_height = 1; + Tokens max_fee = 2; + Subaccount from_subaccount = 3; + ic_base_types.pb.v1.PrincipalId to_canister = 4; + Subaccount to_subaccount = 5; +} + +message NotifyResponse {} + +message TransactionNotificationRequest { + ic_base_types.pb.v1.PrincipalId from = 1; + Subaccount from_subaccount = 2; + ic_base_types.pb.v1.PrincipalId to = 3; + Subaccount to_subaccount = 4; + BlockIndex block_height = 5; + Tokens amount = 6; + Memo memo = 7; +} + +message TransactionNotificationResponse { + bytes response = 1; +} + +message CyclesNotificationResponse { + oneof response { + ic_base_types.pb.v1.PrincipalId created_canister_id = 1; + Refund refund = 2; + ToppedUp topped_up = 3; + } +} + +// Get the balance of an account +message AccountBalanceRequest { + AccountIdentifier account = 1; +} + +message AccountBalanceResponse { + Tokens balance = 1; +} + +// Get the length of the chain with a certification +message TipOfChainRequest {} + +message TipOfChainResponse { + Certification certification = 1; + BlockIndex chain_length = 2; +} + +// How many Tokens are there not in the minting account +message TotalSupplyRequest {} + +message TotalSupplyResponse { + Tokens total_supply = 1; +} + +// Archive any blocks older than this +message LedgerArchiveRequest { + TimeStamp timestamp = 1; +} + +// * Shared Endpoints * + +// Get a single block +message BlockRequest { + uint64 block_height = 1; +} + +message EncodedBlock { + bytes block = 1; +} + +message BlockResponse { + oneof block_content { + EncodedBlock block = 1; + ic_base_types.pb.v1.PrincipalId canister_id = 2; + } +} + +// Get a set of blocks +message GetBlocksRequest { + uint64 start = 1; + uint64 length = 2; +} + +message Refund { + BlockIndex refund = 2; + string error = 3; +} + +message ToppedUp {} + +message EncodedBlocks { + repeated EncodedBlock blocks = 1; +} + +message GetBlocksResponse { + oneof get_blocks_content { + EncodedBlocks blocks = 1; + string error = 2; + } +} + +// Iterate through blocks +message IterBlocksRequest { + uint64 start = 1; + uint64 length = 2; +} + +message IterBlocksResponse { + repeated EncodedBlock blocks = 1; +} + +message ArchiveIndexEntry { + uint64 height_from = 1; + uint64 height_to = 2; + ic_base_types.pb.v1.PrincipalId canister_id = 3; +} + +message ArchiveIndexResponse { + repeated ArchiveIndexEntry entries = 1; +} + +// ** ARCHIVE CANISTER ENDPOINTS ** + +// * Archive canister * +// Init the archive canister +message ArchiveInit { + uint32 node_max_memory_size_bytes = 1; + uint32 max_message_size_bytes = 2; +} + +// Add blocks to the archive canister +message ArchiveAddRequest { + repeated Block blocks = 1; +} + +message ArchiveAddResponse {} + +// Fetch a list of all of the archive nodes +message GetNodesRequest {} + +message GetNodesResponse { + repeated ic_base_types.pb.v1.PrincipalId nodes = 1; +} + +// ** BASIC TYPES ** +message Tokens { + uint64 e8s = 1; +} + +message Payment { + Tokens receiver_gets = 1; +} + +message BlockIndex { + uint64 height = 1; +} + +// This is the +message Block { + Hash parent_hash = 1; + TimeStamp timestamp = 2; + Transaction transaction = 3; +} + +message Hash { + bytes hash = 1; +} + +message Account { + AccountIdentifier identifier = 1; + Tokens balance = 2; +} + +message Transaction { + oneof transfer { + Burn burn = 1; + Mint mint = 2; + Send send = 3; + } + Memo memo = 4; + Icrc1Memo icrc1_memo = 7; + BlockIndex created_at = 5; // obsolete + TimeStamp created_at_time = 6; +} + +message Send { + // The meaning of the [from] field depends on the transaction type: + // - Transfer: [from] is the source account. + // - TransferFrom: [from] is the approver. + // - Approve: [from] is the approver. + AccountIdentifier from = 1; + // The meaning of the [to] field depends on the transaction type: + // - Transfer: [to] is the destination account. + // - TransferFrom: [to] is the destination account. + // - Approve: [to] is the default account id of the approved principal. + AccountIdentifier to = 2; + // If the transaction type is Approve, the amount must be zero. + Tokens amount = 3; + Tokens max_fee = 4; + + // We represent metadata of new operation types as submessages for + // backward compatibility with old clients. + oneof extension { + Approve approve = 5; + TransferFrom transfer_from = 6; + } +} + +message TransferFrom { + // The default account id of the principal who sent the transaction. + AccountIdentifier spender = 1; +} + +message Approve { + Tokens allowance = 1; + TimeStamp expires_at = 2; + Tokens expected_allowance = 3; +} + +message Mint { + AccountIdentifier to = 2; + Tokens amount = 3; +} + +message Burn { + AccountIdentifier from = 1; + Tokens amount = 3; +} + +message AccountIdentifier { + // Can contain either: + // * the 32 byte identifier (4 byte checksum + 28 byte hash) + // * the 28 byte hash + bytes hash = 1; +} + +message Subaccount { + bytes sub_account = 1; +} + +message Memo { + uint64 memo = 1; +} + +message Icrc1Memo { + bytes memo = 1; +} + +message TimeStamp { + uint64 timestamp_nanos = 1; +} + +message Certification { + bytes certification = 1; +} + +message TransferFeeRequest {} + +message TransferFeeResponse { + Tokens transfer_fee = 1; +} \ No newline at end of file diff --git a/rust/tw_internet_computer/src/transactions/proto/types.proto b/rust/tw_internet_computer/src/transactions/proto/types.proto new file mode 100644 index 00000000000..2c63ceb555e --- /dev/null +++ b/rust/tw_internet_computer/src/transactions/proto/types.proto @@ -0,0 +1,19 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +// Source: https://github.com/dfinity/ic/blob/master/rs/rosetta-api/icp_ledger/proto/ic_ledger/pb/v1/types.proto +// Commit Hash: 703eb96fea44ad2c82740e4360a526a2a127a960 +// Striped annotations related to the use of hardware wallets. + +syntax = "proto3"; + +package ic_base_types.pb.v1; + +// A PB container for a PrincipalId, which uniquely identifies +// a principal. +message PrincipalId { + bytes serialized_id = 1; +} \ No newline at end of file diff --git a/rust/tw_internet_computer/src/transactions/transfer.rs b/rust/tw_internet_computer/src/transactions/transfer.rs new file mode 100644 index 00000000000..0b57b56d058 --- /dev/null +++ b/rust/tw_internet_computer/src/transactions/transfer.rs @@ -0,0 +1,247 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::time::Duration; + +use tw_keypair::ecdsa::secp256k1::PrivateKey; + +use crate::{ + address::AccountIdentifier, + protocol::{ + envelope::{ + Envelope, EnvelopeCallContent, EnvelopeReadStateContent, Label, RepresentationHashable, + }, + get_ingress_expiry, + identity::Identity, + principal::Principal, + request_id::RequestId, + rosetta, + }, + transactions::proto::ic_ledger::pb::v1::{ + AccountIdentifier as ProtoAccountIdentifier, Memo, Payment, SendRequest, TimeStamp, Tokens, + }, +}; + +use super::SignTransactionError; + +/// Arguments to be used with [transfer] to create a signed transaction enveloper pair. +#[derive(Clone, Debug)] +pub struct TransferArgs { + /// The memo field is used as a method to help identify the transaction. + pub memo: u64, + /// The amount of ICP to send as e8s. + pub amount: u64, + /// The maximum fee will to be paid to complete the transfer. + /// If not provided, the minimum fee will be applied to the transaction. Currently 10_000 e8s (0.00010000 ICP). + pub max_fee: Option, + /// The address to send the amount to. + pub to: String, + /// The current timestamp in nanoseconds. + pub current_timestamp_nanos: u64, +} + +impl TryFrom for SendRequest { + type Error = SignTransactionError; + + fn try_from(args: TransferArgs) -> Result { + let current_timestamp_duration = Duration::from_nanos(args.current_timestamp_nanos); + let timestamp_nanos = current_timestamp_duration.as_nanos() as u64; + + let to_account_identifier = AccountIdentifier::from_hex(&args.to) + .map_err(|_| SignTransactionError::InvalidToAccountIdentifier)?; + let to_hash = to_account_identifier.as_ref().to_vec(); + + let request = Self { + memo: Some(Memo { memo: args.memo }), + payment: Some(Payment { + receiver_gets: Some(Tokens { e8s: args.amount }), + }), + max_fee: args.max_fee.map(|fee| Tokens { e8s: fee }), + from_subaccount: None, + to: Some(ProtoAccountIdentifier { hash: to_hash }), + created_at: None, + created_at_time: Some(TimeStamp { timestamp_nanos }), + }; + Ok(request) + } +} + +/// The endpoint on the ledger canister that is used to make transfers. +const METHOD_NAME: &str = "send_pb"; + +/// Given a secp256k1 private key, the canister ID of an ICP-based ledger canister, and the actual transfer args, +/// this function creates a signed transaction to be sent to a Rosetta API node. +pub fn transfer( + private_key: PrivateKey, + canister_id: Principal, + args: TransferArgs, +) -> Result { + if args.amount < 1 { + return Err(SignTransactionError::InvalidAmount); + } + + let current_timestamp_duration = Duration::from_nanos(args.current_timestamp_nanos); + let ingress_expiry = get_ingress_expiry(current_timestamp_duration); + let identity = Identity::new(private_key); + + // Encode the arguments for the ledger `send_pb` endpoint. + let send_request = SendRequest::try_from(args)?; + let arg = + tw_proto::serialize(&send_request).map_err(|_| SignTransactionError::EncodingArgsFailed)?; + // Create the update envelope. + let (request_id, update_envelope) = + create_update_envelope(&identity, canister_id, arg, ingress_expiry)?; + + // Create the read state envelope. + let (_, read_state_envelope) = + create_read_state_envelope(&identity, request_id, ingress_expiry)?; + + // Create a new EnvelopePair with the update call and read_state envelopes. + let envelope_pair = rosetta::EnvelopePair::new(update_envelope, read_state_envelope) + .map_err(|_| SignTransactionError::InvalidEnvelopePair)?; + + // Create a signed transaction containing the envelope pair. + let request: rosetta::Request = (rosetta::RequestType::Send, vec![envelope_pair]); + Ok(vec![request]) +} + +#[inline] +fn create_update_envelope( + identity: &Identity, + canister_id: Principal, + arg: Vec, + ingress_expiry: u64, +) -> Result<(RequestId, Envelope), SignTransactionError> { + let sender = identity.sender(); + let content = EnvelopeCallContent { + nonce: None, + ingress_expiry, + sender, + canister_id, + method_name: METHOD_NAME.to_string(), + arg, + }; + + let request_id = content.request_id(); + let signature = identity + .sign(request_id.sig_data()) + .map_err(SignTransactionError::Identity)?; + + let env = Envelope { + content, + sender_pubkey: Some(signature.public_key), + sender_sig: Some(signature.signature), + }; + Ok((request_id, env)) +} + +#[inline] +fn create_read_state_envelope( + identity: &Identity, + update_request_id: RequestId, + ingress_expiry: u64, +) -> Result<(RequestId, Envelope), SignTransactionError> { + let sender = identity.sender(); + + let content = EnvelopeReadStateContent { + ingress_expiry, + sender, + paths: vec![vec![ + Label::from("request_status"), + Label::from(update_request_id), + ]], + }; + + let request_id = content.request_id(); + let signature = identity + .sign(request_id.sig_data()) + .map_err(SignTransactionError::Identity)?; + + let env = Envelope { + content, + sender_pubkey: Some(signature.public_key), + sender_sig: Some(signature.signature), + }; + Ok((request_id, env)) +} + +#[cfg(test)] +mod test { + use tw_encoding::hex; + + use crate::address::AccountIdentifier; + + use super::*; + + pub const SIGNED_TRANSACTION: &str = "81826b5452414e53414354494f4e81a266757064617465a367636f6e74656e74a66c726571756573745f747970656463616c6c6e696e67726573735f6578706972791b177a297215cfe8006673656e646572581d971cd2ddeecd1cf1b28be914d7a5c43441f6296f1f9966a7c8aff68d026b63616e69737465725f69644a000000000000000201016b6d6574686f645f6e616d656773656e645f706263617267583b0a0012070a050880c2d72f2a220a20943d12e762f43806782f524b8f90297298a6d79e4749b41b585ec427409c826a3a0a088090caa5a3a78abd176d73656e6465725f7075626b65799858183018561830100607182a1886184818ce183d02010605182b188104000a0318420004183d18ab183a182118a81838184d184c187e1852188a187e18dc18d8184418ea18cd18c5189518ac188518b518bc181d188515186318bc18e618ab18d2184318d3187c184f18cd18f018de189b18b5181918dd18ef1889187218e71518c40418d4189718881843187218c611182e18cc18e6186b182118630218356a73656e6465725f736967984013186f18b9181c189818b318a8186518b2186118d418971618b1187d18eb185818e01826182f1873183b185018cb185d18ef18d81839186418b3183218da1824182f184e18a01880182718c0189018c918a018fd18c418d9189e189818b318ef1874183b185118e118a51864185918e718ed18c71889186c1822182318ca6a726561645f7374617465a367636f6e74656e74a46c726571756573745f747970656a726561645f73746174656e696e67726573735f6578706972791b177a297215cfe8006673656e646572581d971cd2ddeecd1cf1b28be914d7a5c43441f6296f1f9966a7c8aff68d0265706174687381824e726571756573745f7374617475735820e8fbc2d5b0bf837b3a369249143e50d4476faafb2dd620e4e982586a51ebcf1e6d73656e6465725f7075626b65799858183018561830100607182a1886184818ce183d02010605182b188104000a0318420004183d18ab183a182118a81838184d184c187e1852188a187e18dc18d8184418ea18cd18c5189518ac188518b518bc181d188515186318bc18e618ab18d2184318d3187c184f18cd18f018de189b18b5181918dd18ef1889187218e71518c40418d4189718881843187218c611182e18cc18e6186b182118630218356a73656e6465725f7369679840182d182718201888188618ce187f0c182a187a18d718e818df18fb18d318d41118a5186a184b18341842185318d718e618e8187a1828186c186a183618461418f3183318bd18a618a718bc18d918c818b7189d186e1865188418ff18fd18e418e9187f181b18d705184818b21872187818d6181c161833184318a2"; + + fn make_transfer_args() -> TransferArgs { + let current_timestamp_nanos = Duration::from_secs(1_691_709_940).as_nanos() as u64; + let owner = + Principal::from_text("t4u4z-y3dur-j63pk-nw4rv-yxdbt-agtt6-nygn7-ywh6y-zm2f4-sdzle-3qe") + .unwrap(); + let to_account_identifier = AccountIdentifier::new(&owner); + + TransferArgs { + memo: 0, + amount: 100_000_000, + max_fee: None, + to: to_account_identifier.to_hex(), + current_timestamp_nanos, + } + } + + #[test] + fn transfer_successful() { + let private_key = PrivateKey::try_from( + "227102911bb99ce7285a55f952800912b7d22ebeeeee59d77fc33a5d7c7080be", + ) + .unwrap(); + let canister_id = Principal::from_text("ryjl3-tyaaa-aaaaa-aaaba-cai").unwrap(); + let transfer_args = make_transfer_args(); + + let signed_transaction = transfer(private_key, canister_id, transfer_args).unwrap(); + // Encode the signed transaction. + let cbor_encoded_signed_transaction = + tw_encoding::cbor::encode(&signed_transaction).unwrap(); + let hex_encoded_signed_transaction = hex::encode(&cbor_encoded_signed_transaction, false); + assert_eq!(hex_encoded_signed_transaction, SIGNED_TRANSACTION); + } + + #[test] + fn transfer_invalid_amount() { + let private_key = PrivateKey::try_from( + "227102911bb99ce7285a55f952800912b7d22ebeeeee59d77fc33a5d7c7080be", + ) + .unwrap(); + let canister_id = Principal::from_text("ryjl3-tyaaa-aaaaa-aaaba-cai").unwrap(); + let mut transfer_args = make_transfer_args(); + transfer_args.amount = 0; + + let signed_transaction = transfer(private_key, canister_id, transfer_args); + assert!(matches!( + signed_transaction, + Err(SignTransactionError::InvalidAmount) + )); + } + + #[test] + fn transfer_invalid_to_account_identifier() { + let private_key = PrivateKey::try_from( + "227102911bb99ce7285a55f952800912b7d22ebeeeee59d77fc33a5d7c7080be", + ) + .unwrap(); + let canister_id = Principal::from_text("ryjl3-tyaaa-aaaaa-aaaba-cai").unwrap(); + let mut transfer_args = make_transfer_args(); + transfer_args.to = "invalid".to_string(); + + let signed_transaction = transfer(private_key, canister_id, transfer_args); + assert!(matches!( + signed_transaction, + Err(SignTransactionError::InvalidToAccountIdentifier) + )); + } +} diff --git a/rust/tw_keypair/Cargo.toml b/rust/tw_keypair/Cargo.toml index 0328396aedf..52b0a350fa8 100644 --- a/rust/tw_keypair/Cargo.toml +++ b/rust/tw_keypair/Cargo.toml @@ -20,8 +20,9 @@ zeroize = "1.6.0" # ECDSA specific: ecdsa = "0.16.6" der = "0.7.3" -k256 = { version = "0.13.0", features = ["ecdh", "ecdsa", "schnorr", "std"], default-features = false } +k256 = { version = "0.13.0", features = ["ecdh", "ecdsa", "pkcs8", "schnorr", "std"], default-features = false } p256 = { version = "0.13.0", features = ["ecdsa", "std"], default-features = false } +pkcs8 = "0.10.2" rfc6979 = "0.4.0" # ED25519 specific: blake2 = "0.9" diff --git a/rust/tw_keypair/src/ecdsa/secp256k1/public.rs b/rust/tw_keypair/src/ecdsa/secp256k1/public.rs index 68a48621a6e..92dc6b0cc52 100644 --- a/rust/tw_keypair/src/ecdsa/secp256k1/public.rs +++ b/rust/tw_keypair/src/ecdsa/secp256k1/public.rs @@ -7,6 +7,7 @@ use crate::ecdsa::secp256k1::{Signature, VerifySignature}; use crate::traits::VerifyingKeyTrait; use crate::{KeyPairError, KeyPairResult}; +use der::Document; use k256::ecdsa::signature::hazmat::PrehashVerifier; use k256::ecdsa::VerifyingKey; use tw_encoding::hex; @@ -57,6 +58,28 @@ impl PublicKey { let (_prefix, public): (Hash<1>, H512) = self.uncompressed().split(); public } + + /// Returns the public key as DER-encoded bytes. + pub fn der_encoded(&self) -> Vec { + let compressed = false; + let public_key_bytes = self.public.to_encoded_point(compressed); + let subject_public_key = + der::asn1::BitStringRef::new(0, public_key_bytes.as_bytes()).unwrap(); + + let oid: pkcs8::ObjectIdentifier = pkcs8::ObjectIdentifier::new_unwrap("1.2.840.10045.2.1"); + let associated_oid: pkcs8::ObjectIdentifier = + pkcs8::ObjectIdentifier::new_unwrap("1.3.132.0.10"); + let spki = pkcs8::SubjectPublicKeyInfo { + algorithm: pkcs8::spki::AlgorithmIdentifier { + oid, + parameters: Some(associated_oid), + }, + subject_public_key, + }; + + let doc = Document::try_from(&spki).unwrap(); + doc.into_vec() + } } impl VerifyingKeyTrait for PublicKey { diff --git a/src/Coin.cpp b/src/Coin.cpp index d7a0d45a2a8..29b91e2e9a7 100644 --- a/src/Coin.cpp +++ b/src/Coin.cpp @@ -65,6 +65,7 @@ #include "TheOpenNetwork/Entry.h" #include "Sui/Entry.h" #include "Greenfield/Entry.h" +#include "InternetComputer/Entry.h" // end_of_coin_includes_marker_do_not_modify using namespace TW; @@ -121,6 +122,7 @@ Hedera::Entry HederaDP; TheOpenNetwork::Entry tonDP; Sui::Entry SuiDP; Greenfield::Entry GreenfieldDP; +InternetComputer::Entry InternetComputerDP; // end_of_coin_dipatcher_declarations_marker_do_not_modify CoinEntry* coinDispatcher(TWCoinType coinType) { @@ -179,6 +181,7 @@ CoinEntry* coinDispatcher(TWCoinType coinType) { case TWBlockchainTheOpenNetwork: entry = &tonDP; break; case TWBlockchainSui: entry = &SuiDP; break; case TWBlockchainGreenfield: entry = &GreenfieldDP; break; + case TWBlockchainInternetComputer: entry = &InternetComputerDP; break; // end_of_coin_dipatcher_switch_marker_do_not_modify default: entry = nullptr; break; @@ -237,7 +240,7 @@ namespace TW::internal { assert(dispatcher != nullptr); return dispatcher->normalizeAddress(coin, address); } -} +} // namespace TW::internal std::string TW::normalizeAddress(TWCoinType coin, const string& address) {; if (!TW::validateAddress(coin, address)) { diff --git a/src/InternetComputer/Entry.cpp b/src/InternetComputer/Entry.cpp new file mode 100644 index 00000000000..a4d7e6bff38 --- /dev/null +++ b/src/InternetComputer/Entry.cpp @@ -0,0 +1,33 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Entry.h" + +namespace TW::InternetComputer { + +// Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. + +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { + return validateAddressRust(coin, address, addressPrefix); +} + +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { + return deriveAddressRust(coin, publicKey, derivation, addressPrefix); +} + +void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { + signRust(dataIn, coin, dataOut); +} + +TW::Data Entry::preImageHashes(TWCoinType coin, const Data& txInputData) const { + return preImageHashesRust(coin, txInputData); +} + +void Entry::compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const { + compileRust(coin, txInputData, signatures, publicKeys, dataOut); +} + +} // namespace TW::InternetComputer diff --git a/src/InternetComputer/Entry.h b/src/InternetComputer/Entry.h new file mode 100644 index 00000000000..e997bddb2d6 --- /dev/null +++ b/src/InternetComputer/Entry.h @@ -0,0 +1,27 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "../CoinEntry.h" + +namespace TW::InternetComputer { + +/// Entry point for implementation of InternetComputer coin. +/// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file +class Entry final : public CoinEntry { +public: + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + // normalizeAddress(): implement this if needed, e.g. Ethereum address is EIP55 checksummed + // plan(): implement this if the blockchain is UTXO based + + Data preImageHashes(TWCoinType coin, const Data& txInputData) const; + void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const; +}; + +} // namespace TW::InternetComputer diff --git a/src/proto/InternetComputer.proto b/src/proto/InternetComputer.proto new file mode 100644 index 00000000000..00cd513baf0 --- /dev/null +++ b/src/proto/InternetComputer.proto @@ -0,0 +1,46 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +syntax = "proto3"; + +package TW.InternetComputer.Proto; +option java_package = "wallet.core.jni.proto"; + +import "Common.proto"; + +// Internet Computer Transactions +message Transaction { + + // ICP ledger transfer arguments + message Transfer { + string to_account_identifier = 1; + uint64 amount = 2; + uint64 memo = 3; + uint64 current_timestamp_nanos = 4; + } + + // Payload transfer + oneof transaction_oneof { + Transfer transfer = 1; + } +} + +// Input data necessary to create a signed transaction. +message SigningInput { + bytes private_key = 1; + Transaction transaction = 2; +} + +// Transaction signing output. +message SigningOutput { + // Signed and encoded transaction bytes. + // NOTE: Before sending to the Rosetta node, this value should be hex-encoded before using with the JSON structure. + bytes signed_transaction = 1; + + Common.Proto.SigningError error = 2; + + string error_message = 3; +} \ No newline at end of file diff --git a/swift/Tests/Blockchains/InternetComputerTests.swift b/swift/Tests/Blockchains/InternetComputerTests.swift new file mode 100644 index 00000000000..0ea025dd0c9 --- /dev/null +++ b/swift/Tests/Blockchains/InternetComputerTests.swift @@ -0,0 +1,78 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import WalletCore +import XCTest + +class InternetComputerTests: XCTestCase { + // TODO: Check and finalize implementation + + func testAddress() { + // TODO: Check and finalize implementation + + let key = PrivateKey(data: Data(hexString: "ee42eaada903e20ef6e5069f0428d552475c1ea7ed940842da6448f6ef9d48e7")!)! + let pubkey = key.getPublicKeySecp256k1(compressed: false) + let address = AnyAddress(publicKey: pubkey, coin: .internetComputer) + let addressFromString = AnyAddress(string: "2f25874478d06cf68b9833524a6390d0ba69c566b02f46626979a3d6a4153211", coin: .internetComputer)! + + XCTAssertEqual(pubkey.data.hexString, "048542e6fb4b17d6dfcac3948fe412c00d626728815ee7cc70509603f1bc92128a6e7548f3432d6248bc49ff44a1e50f6389238468d17f7d7024de5be9b181dbc8") + XCTAssertEqual(address.description, addressFromString.description) + } + + func testSign() { + let key = PrivateKey(data: Data(hexString: "227102911bb99ce7285a55f952800912b7d22ebeeeee59d77fc33a5d7c7080be")!)! + let input = InternetComputerSigningInput.with { + $0.privateKey = key.data + $0.transaction = InternetComputerTransaction.with { + $0.transfer = InternetComputerTransaction.Transfer.with { + $0.toAccountIdentifier = "943d12e762f43806782f524b8f90297298a6d79e4749b41b585ec427409c826a" + $0.amount = 100000000 + $0.memo = 0 + $0.currentTimestampNanos = 1691709940000000000 + } + } + + } + let output: InternetComputerSigningOutput = AnySigner.sign(input: input, coin: .internetComputer) + XCTAssertEqual(output.signedTransaction.hexString, "81826b5452414e53414354494f4e81a266757064617465a367636f6e74656e74a66c726571756573745f747970656463616c6c6e696e67726573735f6578706972791b177a297215cfe8006673656e646572581d971cd2ddeecd1cf1b28be914d7a5c43441f6296f1f9966a7c8aff68d026b63616e69737465725f69644a000000000000000201016b6d6574686f645f6e616d656773656e645f706263617267583b0a0012070a050880c2d72f2a220a20943d12e762f43806782f524b8f90297298a6d79e4749b41b585ec427409c826a3a0a088090caa5a3a78abd176d73656e6465725f7075626b65799858183018561830100607182a1886184818ce183d02010605182b188104000a0318420004183d18ab183a182118a81838184d184c187e1852188a187e18dc18d8184418ea18cd18c5189518ac188518b518bc181d188515186318bc18e618ab18d2184318d3187c184f18cd18f018de189b18b5181918dd18ef1889187218e71518c40418d4189718881843187218c611182e18cc18e6186b182118630218356a73656e6465725f736967984013186f18b9181c189818b318a8186518b2186118d418971618b1187d18eb185818e01826182f1873183b185018cb185d18ef18d81839186418b3183218da1824182f184e18a01880182718c0189018c918a018fd18c418d9189e189818b318ef1874183b185118e118a51864185918e718ed18c71889186c1822182318ca6a726561645f7374617465a367636f6e74656e74a46c726571756573745f747970656a726561645f73746174656e696e67726573735f6578706972791b177a297215cfe8006673656e646572581d971cd2ddeecd1cf1b28be914d7a5c43441f6296f1f9966a7c8aff68d0265706174687381824e726571756573745f7374617475735820e8fbc2d5b0bf837b3a369249143e50d4476faafb2dd620e4e982586a51ebcf1e6d73656e6465725f7075626b65799858183018561830100607182a1886184818ce183d02010605182b188104000a0318420004183d18ab183a182118a81838184d184c187e1852188a187e18dc18d8184418ea18cd18c5189518ac188518b518bc181d188515186318bc18e618ab18d2184318d3187c184f18cd18f018de189b18b5181918dd18ef1889187218e71518c40418d4189718881843187218c611182e18cc18e6186b182118630218356a73656e6465725f7369679840182d182718201888188618ce187f0c182a187a18d718e818df18fb18d318d41118a5186a184b18341842185318d718e618e8187a1828186c186a183618461418f3183318bd18a618a718bc18d918c818b7189d186e1865188418ff18fd18e418e9187f181b18d705184818b21872187818d6181c161833184318a2") + } + + func testSignWithInvalidToAccountIdentifier() { + let key = PrivateKey(data: Data(hexString: "227102911bb99ce7285a55f952800912b7d22ebeeeee59d77fc33a5d7c7080be")!)! + let input = InternetComputerSigningInput.with { + $0.privateKey = key.data + $0.transaction = InternetComputerTransaction.with { + $0.transfer = InternetComputerTransaction.Transfer.with { + $0.toAccountIdentifier = "643d12e762f43806782f524b8f90297298a6d79e4749b41b585ec427409c826b" + $0.amount = 100000000 + $0.memo = 0 + $0.currentTimestampNanos = 1691709940000000000 + } + } + + } + let output: InternetComputerSigningOutput = AnySigner.sign(input: input, coin: .internetComputer) + XCTAssertEqual(output.error.rawValue, 16) + } + + func testSignWithInvalidAmount() { + let key = PrivateKey(data: Data(hexString: "227102911bb99ce7285a55f952800912b7d22ebeeeee59d77fc33a5d7c7080be")!)! + let input = InternetComputerSigningInput.with { + $0.privateKey = key.data + $0.transaction = InternetComputerTransaction.with { + $0.transfer = InternetComputerTransaction.Transfer.with { + $0.toAccountIdentifier = "943d12e762f43806782f524b8f90297298a6d79e4749b41b585ec427409c826a" + $0.amount = 0 + $0.memo = 0 + $0.currentTimestampNanos = 1691709940000000000 + } + } + + } + let output: InternetComputerSigningOutput = AnySigner.sign(input: input, coin: .internetComputer) + XCTAssertEqual(output.error.rawValue, 23) + } +} diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 14e069f08b4..395a7a6e2ab 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -383,6 +383,9 @@ class CoinAddressDerivationTests: XCTestCase { case .sei: let expectedResult = "sei142j9u5eaduzd7faumygud6ruhdwme98qagm0sj" assertCoinDerivation(coin, expectedResult, derivedAddress, address) + case .internetComputer: + let expectedResult = "b9a13d974ee9db036d5abc5b66ace23e513cb5676f3996626c7717c339a3ee87" + assertCoinDerivation(coin, expectedResult, derivedAddress, address) @unknown default: fatalError() } diff --git a/tests/chains/InternetComputer/TWAnyAddressTests.cpp b/tests/chains/InternetComputer/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..013ff0f158e --- /dev/null +++ b/tests/chains/InternetComputer/TWAnyAddressTests.cpp @@ -0,0 +1,20 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include + +#include "TestUtilities.h" +#include + +using namespace TW; + +TEST(TWInternetComputer, Address) { + auto string = STRING("58b26ace22a36a0011608a130e84c7cf34ba469c38d24ccf606152ce7de91f4e"); + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeInternetComputer)); + auto string2 = WRAPS(TWAnyAddressDescription(addr.get())); + EXPECT_TRUE(TWStringEqual(string.get(), string2.get())); +} diff --git a/tests/chains/InternetComputer/TWAnySignerTests.cpp b/tests/chains/InternetComputer/TWAnySignerTests.cpp new file mode 100644 index 00000000000..71e4ba77e62 --- /dev/null +++ b/tests/chains/InternetComputer/TWAnySignerTests.cpp @@ -0,0 +1,36 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "proto/Common.pb.h" +#include "proto/InternetComputer.pb.h" +#include + +#include "TestUtilities.h" +#include + +using namespace TW; + +namespace TW::InternetComputer { + +TEST(TWAnySignerInternetComputer, Sign) { + auto key = parse_hex("227102911bb99ce7285a55f952800912b7d22ebeeeee59d77fc33a5d7c7080be"); + auto input = Proto::SigningInput(); + input.set_private_key(key.data(), key.size()); + input.mutable_transaction()->mutable_transfer()->set_to_account_identifier("943d12e762f43806782f524b8f90297298a6d79e4749b41b585ec427409c826a"); + input.mutable_transaction()->mutable_transfer()->set_amount(100'000'000); + input.mutable_transaction()->mutable_transfer()->set_memo(0); + input.mutable_transaction()->mutable_transfer()->set_current_timestamp_nanos(1'691'709'940'000'000'000); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeInternetComputer); + + const auto signed_transaction = output.signed_transaction(); + const auto hex_encoded = hex(signed_transaction); + ASSERT_EQ(hex_encoded, "81826b5452414e53414354494f4e81a266757064617465a367636f6e74656e74a66c726571756573745f747970656463616c6c6e696e67726573735f6578706972791b177a297215cfe8006673656e646572581d971cd2ddeecd1cf1b28be914d7a5c43441f6296f1f9966a7c8aff68d026b63616e69737465725f69644a000000000000000201016b6d6574686f645f6e616d656773656e645f706263617267583b0a0012070a050880c2d72f2a220a20943d12e762f43806782f524b8f90297298a6d79e4749b41b585ec427409c826a3a0a088090caa5a3a78abd176d73656e6465725f7075626b65799858183018561830100607182a1886184818ce183d02010605182b188104000a0318420004183d18ab183a182118a81838184d184c187e1852188a187e18dc18d8184418ea18cd18c5189518ac188518b518bc181d188515186318bc18e618ab18d2184318d3187c184f18cd18f018de189b18b5181918dd18ef1889187218e71518c40418d4189718881843187218c611182e18cc18e6186b182118630218356a73656e6465725f736967984013186f18b9181c189818b318a8186518b2186118d418971618b1187d18eb185818e01826182f1873183b185018cb185d18ef18d81839186418b3183218da1824182f184e18a01880182718c0189018c918a018fd18c418d9189e189818b318ef1874183b185118e118a51864185918e718ed18c71889186c1822182318ca6a726561645f7374617465a367636f6e74656e74a46c726571756573745f747970656a726561645f73746174656e696e67726573735f6578706972791b177a297215cfe8006673656e646572581d971cd2ddeecd1cf1b28be914d7a5c43441f6296f1f9966a7c8aff68d0265706174687381824e726571756573745f7374617475735820e8fbc2d5b0bf837b3a369249143e50d4476faafb2dd620e4e982586a51ebcf1e6d73656e6465725f7075626b65799858183018561830100607182a1886184818ce183d02010605182b188104000a0318420004183d18ab183a182118a81838184d184c187e1852188a187e18dc18d8184418ea18cd18c5189518ac188518b518bc181d188515186318bc18e618ab18d2184318d3187c184f18cd18f018de189b18b5181918dd18ef1889187218e71518c40418d4189718881843187218c611182e18cc18e6186b182118630218356a73656e6465725f7369679840182d182718201888188618ce187f0c182a187a18d718e818df18fb18d318d41118a5186a184b18341842185318d718e618e8187a1828186c186a183618461418f3183318bd18a618a718bc18d918c818b7189d186e1865188418ff18fd18e418e9187f181b18d705184818b21872187818d6181c161833184318a2"); +} + +} // namespace TW::InternetComputer diff --git a/tests/chains/InternetComputer/TWCoinTypeTests.cpp b/tests/chains/InternetComputer/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..ea7bbc624d1 --- /dev/null +++ b/tests/chains/InternetComputer/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + +TEST(TWInternetComputerCoinType, TWCoinType) { + const auto coin = TWCoinTypeInternetComputer; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("9e32c54975adf84a1d98f19df41bbc34a752a899c32cc9c0000200b2c4308f85")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("529ea51c22e8d66e8302eabd9297b100fdb369109822248bb86939a671fbc55b")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "internet_computer"); + assertStringsEqual(name, "Internet Computer"); + assertStringsEqual(symbol, "ICP"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 8); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainInternetComputer); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); + assertStringsEqual(txUrl, "https://dashboard.internetcomputer.org/transaction/9e32c54975adf84a1d98f19df41bbc34a752a899c32cc9c0000200b2c4308f85"); + assertStringsEqual(accUrl, "https://dashboard.internetcomputer.org/account/529ea51c22e8d66e8302eabd9297b100fdb369109822248bb86939a671fbc55b"); +} diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index 9ee3b5e73b4..233e4962907 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -378,6 +378,9 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeSei: EXPECT_EQ(address, "sei1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z05hw42q"); break; + case TWCoinTypeInternetComputer: + EXPECT_EQ(address, "cb3aa6a0471a417fc33d8e71f1d241750dfa29b4dc8f084265ce1301fb03b65b"); + break; // no default branch here, intentionally, to better notice any missing coins } } diff --git a/wasm/tests/Blockchain/InternetComputer.test.ts b/wasm/tests/Blockchain/InternetComputer.test.ts new file mode 100644 index 00000000000..1adf5a8fa52 --- /dev/null +++ b/wasm/tests/Blockchain/InternetComputer.test.ts @@ -0,0 +1,139 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import "mocha"; +import { assert } from "chai"; +import { Buffer } from "buffer"; +import { TW } from "../../dist"; +import Long = require("long"); + +describe("InternetComputer", () => { + + it("test address", () => { + const { PrivateKey, HexCoding, AnyAddress, CoinType, Curve } = globalThis.core; + const privateKeyBytes = HexCoding.decode("ee42eaada903e20ef6e5069f0428d552475c1ea7ed940842da6448f6ef9d48e7"); + + assert.isTrue(PrivateKey.isValid(privateKeyBytes, Curve.secp256k1)); + + const privateKey = PrivateKey.createWithData(privateKeyBytes); + const publicKey = privateKey.getPublicKeySecp256k1(false); + + assert.equal( + HexCoding.encode(publicKey.data()), + "0x048542e6fb4b17d6dfcac3948fe412c00d626728815ee7cc70509603f1bc92128a6e7548f3432d6248bc49ff44a1e50f6389238468d17f7d7024de5be9b181dbc8" + ); + + const address = AnyAddress.createWithPublicKey(publicKey, CoinType.internetComputer); + + assert.equal(address.description(), "2f25874478d06cf68b9833524a6390d0ba69c566b02f46626979a3d6a4153211"); + + privateKey.delete(); + publicKey.delete(); + address.delete(); + }); + + it("test sign", () => { + const { HexCoding, AnySigner, CoinType } = globalThis.core; + const privateKeyBytes = HexCoding.decode("227102911bb99ce7285a55f952800912b7d22ebeeeee59d77fc33a5d7c7080be"); + + const input = TW.InternetComputer.Proto.SigningInput.create({ + transaction: TW.InternetComputer.Proto.Transaction.create({ + transfer: TW.InternetComputer.Proto.Transaction.Transfer.create({ + toAccountIdentifier: "943d12e762f43806782f524b8f90297298a6d79e4749b41b585ec427409c826a", + amount: new Long(100000000), + memo: new Long(0), + currentTimestampNanos: Long.fromString("1691709940000000000") + }) + }), + privateKey: privateKeyBytes + }); + + const encoded = TW.InternetComputer.Proto.SigningInput.encode(input).finish(); + + const outputData = AnySigner.sign(encoded, CoinType.internetComputer); + const output = TW.InternetComputer.Proto.SigningOutput.decode(outputData); + const signedTransaction = HexCoding.encode(output.signedTransaction); + + assert.equal(signedTransaction, "0x81826b5452414e53414354494f4e81a266757064617465a367636f6e74656e74a66c726571756573745f747970656463616c6c6e696e67726573735f6578706972791b177a297215cfe8006673656e646572581d971cd2ddeecd1cf1b28be914d7a5c43441f6296f1f9966a7c8aff68d026b63616e69737465725f69644a000000000000000201016b6d6574686f645f6e616d656773656e645f706263617267583b0a0012070a050880c2d72f2a220a20943d12e762f43806782f524b8f90297298a6d79e4749b41b585ec427409c826a3a0a088090caa5a3a78abd176d73656e6465725f7075626b65799858183018561830100607182a1886184818ce183d02010605182b188104000a0318420004183d18ab183a182118a81838184d184c187e1852188a187e18dc18d8184418ea18cd18c5189518ac188518b518bc181d188515186318bc18e618ab18d2184318d3187c184f18cd18f018de189b18b5181918dd18ef1889187218e71518c40418d4189718881843187218c611182e18cc18e6186b182118630218356a73656e6465725f736967984013186f18b9181c189818b318a8186518b2186118d418971618b1187d18eb185818e01826182f1873183b185018cb185d18ef18d81839186418b3183218da1824182f184e18a01880182718c0189018c918a018fd18c418d9189e189818b318ef1874183b185118e118a51864185918e718ed18c71889186c1822182318ca6a726561645f7374617465a367636f6e74656e74a46c726571756573745f747970656a726561645f73746174656e696e67726573735f6578706972791b177a297215cfe8006673656e646572581d971cd2ddeecd1cf1b28be914d7a5c43441f6296f1f9966a7c8aff68d0265706174687381824e726571756573745f7374617475735820e8fbc2d5b0bf837b3a369249143e50d4476faafb2dd620e4e982586a51ebcf1e6d73656e6465725f7075626b65799858183018561830100607182a1886184818ce183d02010605182b188104000a0318420004183d18ab183a182118a81838184d184c187e1852188a187e18dc18d8184418ea18cd18c5189518ac188518b518bc181d188515186318bc18e618ab18d2184318d3187c184f18cd18f018de189b18b5181918dd18ef1889187218e71518c40418d4189718881843187218c611182e18cc18e6186b182118630218356a73656e6465725f7369679840182d182718201888188618ce187f0c182a187a18d718e818df18fb18d318d41118a5186a184b18341842185318d718e618e8187a1828186c186a183618461418f3183318bd18a618a718bc18d918c818b7189d186e1865188418ff18fd18e418e9187f181b18d705184818b21872187818d6181c161833184318a2"); + }); + + it("test sign with invalid private key", () => { + const { HexCoding, AnySigner, CoinType } = globalThis.core; + const privateKeyBytes = HexCoding.decode("227102911bb99ce7285a55f952800912b7d22ebeeeee59d77fc33a5d7c7000000"); + + const input = TW.InternetComputer.Proto.SigningInput.create({ + transaction: TW.InternetComputer.Proto.Transaction.create({ + transfer: TW.InternetComputer.Proto.Transaction.Transfer.create({ + toAccountIdentifier: "943d12e762f43806782f524b8f90297298a6d79e4749b41b585ec427409c826a", + amount: new Long(100000000), + memo: new Long(0), + currentTimestampNanos: Long.fromString("1691709940000000000") + }) + }), + privateKey: privateKeyBytes + }); + + const encoded = TW.InternetComputer.Proto.SigningInput.encode(input).finish(); + + const outputData = AnySigner.sign(encoded, CoinType.internetComputer); + const output = TW.InternetComputer.Proto.SigningOutput.decode(outputData); + const signedTransaction = HexCoding.encode(output.signedTransaction); + + // Invalid private key + assert.equal(output.error, 15); + }); + + it("test sign with invalid to account identifier", () => { + const { HexCoding, AnySigner, CoinType } = globalThis.core; + const privateKeyBytes = HexCoding.decode("227102911bb99ce7285a55f952800912b7d22ebeeeee59d77fc33a5d7c7080be"); + + const input = TW.InternetComputer.Proto.SigningInput.create({ + transaction: TW.InternetComputer.Proto.Transaction.create({ + transfer: TW.InternetComputer.Proto.Transaction.Transfer.create({ + toAccountIdentifier: "643d12e762f43806782f524b8f90297298a6d79e4749b41b585ec427409c826b", + amount: new Long(100000000), + memo: new Long(0), + currentTimestampNanos: Long.fromString("1691709940000000000") + }) + }), + privateKey: privateKeyBytes + }); + + const encoded = TW.InternetComputer.Proto.SigningInput.encode(input).finish(); + + const outputData = AnySigner.sign(encoded, CoinType.internetComputer); + const output = TW.InternetComputer.Proto.SigningOutput.decode(outputData); + const signedTransaction = HexCoding.encode(output.signedTransaction); + + // Invalid account identifier + assert.equal(output.error, 16); + }); + + it("test sign with invalid amount", () => { + const { HexCoding, AnySigner, CoinType } = globalThis.core; + const privateKeyBytes = HexCoding.decode("227102911bb99ce7285a55f952800912b7d22ebeeeee59d77fc33a5d7c7080be"); + + const input = TW.InternetComputer.Proto.SigningInput.create({ + transaction: TW.InternetComputer.Proto.Transaction.create({ + transfer: TW.InternetComputer.Proto.Transaction.Transfer.create({ + toAccountIdentifier: "943d12e762f43806782f524b8f90297298a6d79e4749b41b585ec427409c826a", + amount: new Long(0), + memo: new Long(0), + currentTimestampNanos: Long.fromString("1691709940000000000") + }) + }), + privateKey: privateKeyBytes + }); + + const encoded = TW.InternetComputer.Proto.SigningInput.encode(input).finish(); + + const outputData = AnySigner.sign(encoded, CoinType.internetComputer); + const output = TW.InternetComputer.Proto.SigningOutput.decode(outputData); + + assert.equal(output.error, 23); + assert.equal(output.errorMessage, 'Invalid input token amount'); + }); +}); \ No newline at end of file From 0ce124ee698b9b8d7e400d9b0adc8a2cc40ba8bd Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Mon, 30 Oct 2023 06:36:34 -0500 Subject: [PATCH 006/128] feat(linux): downgrade (#3519) --- .github/workflows/linux-ci.yml | 6 ++++++ .github/workflows/linux-sampleapp-ci.yml | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml index 8eb3d94358e..239858760cc 100644 --- a/.github/workflows/linux-ci.yml +++ b/.github/workflows/linux-ci.yml @@ -15,6 +15,12 @@ jobs: runs-on: ubuntu-latest if: github.event.pull_request.draft == false steps: + - name: Remove GCC 13 from runner image + shell: bash + run: | + sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r-ubuntu-test-jammy.list + sudo apt-get update + sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.4 libc6-dev=2.35-0ubuntu3.4 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 - uses: actions/checkout@v3 - name: Install system dependencies run: | diff --git a/.github/workflows/linux-sampleapp-ci.yml b/.github/workflows/linux-sampleapp-ci.yml index ec1b8ec665d..fa44291f2f4 100644 --- a/.github/workflows/linux-sampleapp-ci.yml +++ b/.github/workflows/linux-sampleapp-ci.yml @@ -15,6 +15,12 @@ jobs: runs-on: ubuntu-latest if: github.event.pull_request.draft == false steps: + - name: Remove GCC 13 from runner image + shell: bash + run: | + sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r-ubuntu-test-jammy.list + sudo apt-get update + sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.4 libc6-dev=2.35-0ubuntu3.4 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 - uses: actions/checkout@v3 - name: Install system dependencies run: | From b391586d39d41e263b7616b7dce33046597b6096 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Mon, 30 Oct 2023 14:03:15 +0100 Subject: [PATCH 007/128] [Sec]: Zeroize mnemonic (#3520) --- rust/coverage.stats | 2 +- src/HDWallet.cpp | 6 +++--- src/Keystore/StoredKey.cpp | 9 +++++++-- src/interface/TWString.cpp | 9 +++++++-- tests/chains/Bitcoin/TWSegwitAddressTests.cpp | 2 +- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/rust/coverage.stats b/rust/coverage.stats index 7d7ab43dc7c..8670b15529f 100644 --- a/rust/coverage.stats +++ b/rust/coverage.stats @@ -1 +1 @@ -92.0 \ No newline at end of file +91.0 \ No newline at end of file diff --git a/src/HDWallet.cpp b/src/HDWallet.cpp index 74d6e4585bd..b4674f56dcf 100644 --- a/src/HDWallet.cpp +++ b/src/HDWallet.cpp @@ -99,9 +99,9 @@ HDWallet::HDWallet(const Data& entropy, const std::string& passphrase) template HDWallet::~HDWallet() { - std::fill(seed.begin(), seed.end(), 0); - std::fill(mnemonic.begin(), mnemonic.end(), 0); - std::fill(passphrase.begin(), passphrase.end(), 0); + memzero(seed.data(), seed.size()); + memzero(mnemonic.data(), mnemonic.size()); + memzero(passphrase.data(), passphrase.size()); } template diff --git a/src/Keystore/StoredKey.cpp b/src/Keystore/StoredKey.cpp index 3e35bc7aa9b..a3fca3bb91f 100644 --- a/src/Keystore/StoredKey.cpp +++ b/src/Keystore/StoredKey.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -32,7 +33,9 @@ StoredKey StoredKey::createWithMnemonic(const std::string& name, const Data& pas } Data mnemonicData = TW::Data(mnemonic.begin(), mnemonic.end()); - return StoredKey(StoredKeyType::mnemonicPhrase, name, password, mnemonicData, encryptionLevel, encryption); + StoredKey key(StoredKeyType::mnemonicPhrase, name, password, mnemonicData, encryptionLevel, encryption); + memzero(mnemonicData.data(), mnemonic.size()); + return key; } StoredKey StoredKey::createWithMnemonicRandom(const std::string& name, const Data& password, TWStoredKeyEncryptionLevel encryptionLevel, TWStoredKeyEncryption encryption) { @@ -40,7 +43,9 @@ StoredKey StoredKey::createWithMnemonicRandom(const std::string& name, const Dat const auto& mnemonic = wallet.getMnemonic(); assert(Mnemonic::isValid(mnemonic)); Data mnemonicData = TW::Data(mnemonic.begin(), mnemonic.end()); - return StoredKey(StoredKeyType::mnemonicPhrase, name, password, mnemonicData, encryptionLevel, encryption); + StoredKey key(StoredKeyType::mnemonicPhrase, name, password, mnemonicData, encryptionLevel, encryption); + memzero(mnemonicData.data(), mnemonic.size()); + return key; } StoredKey StoredKey::createWithMnemonicAddDefaultAddress(const std::string& name, const Data& password, const std::string& mnemonic, TWCoinType coin, TWStoredKeyEncryption encryption) { diff --git a/src/interface/TWString.cpp b/src/interface/TWString.cpp index ccd0f267dfd..ac118e581a0 100644 --- a/src/interface/TWString.cpp +++ b/src/interface/TWString.cpp @@ -6,6 +6,7 @@ #include +#include #include TWString *_Nonnull TWStringCreateWithUTF8Bytes(const char *_Nonnull bytes) { @@ -34,8 +35,12 @@ const char *_Nonnull TWStringUTF8Bytes(TWString *_Nonnull string) { } void TWStringDelete(TWString *_Nonnull string) { - auto* s = reinterpret_cast(string); - delete s; + auto *sConst = reinterpret_cast(string); + // `const_cast` is safe here despite that the pointer to the string is const + // but `std::string` is not a constant value. + auto *s = const_cast(sConst); + memzero(s->data(), s->size()); + delete sConst; } bool TWStringEqual(TWString *_Nonnull lhs, TWString *_Nonnull rhs) { diff --git a/tests/chains/Bitcoin/TWSegwitAddressTests.cpp b/tests/chains/Bitcoin/TWSegwitAddressTests.cpp index 6b21a066205..9723244292c 100644 --- a/tests/chains/Bitcoin/TWSegwitAddressTests.cpp +++ b/tests/chains/Bitcoin/TWSegwitAddressTests.cpp @@ -60,7 +60,7 @@ TEST(TWSegwitAddress, InitWithAddress) { ASSERT_EQ(TWHRPBitcoin, TWSegwitAddressHRP(address.get())); - auto witness = WRAPS(TWSegwitAddressWitnessProgram(address.get())); + auto witness = WRAPD(TWSegwitAddressWitnessProgram(address.get())); ASSERT_EQ(TW::hex(TW::data(TWDataBytes(witness.get()), TWDataSize(witness.get()))), "751e76e8199196d454941c45d1b3a323f1433bd6"); } From 6a93cb809d822c5c626a4d2b05210171d902fc58 Mon Sep 17 00:00:00 2001 From: zzq0826 <770166635@qq.com> Date: Tue, 31 Oct 2023 18:03:36 +0800 Subject: [PATCH 008/128] Update scroll chainId (#3514) --- docs/registry.md | 2 +- include/TrustWalletCore/TWCoinType.h | 2 +- registry.json | 8 ++++---- tests/chains/Scroll/TWAnySignerTests.cpp | 4 ++-- tests/chains/Scroll/TWCoinTypeTests.cpp | 10 +++++----- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/registry.md b/docs/registry.md index d569da1d826..57cff87373f 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -98,7 +98,7 @@ This list is generated from [./registry.json](../registry.json) | 52752 | Celo | CELO | | | | 59144 | Linea | ETH | | | | 105105 | Stratis | STRAX | | | -| 534353 | Scroll | ETH | | | +| 534352 | Scroll | ETH | | | | 5718350 | Wanchain | WAN | | | | 5741564 | Waves | WAVES | | | | 10000025 | Cronos Chain | CRO | | | diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 1b75255e921..e993c7f1680 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -162,7 +162,7 @@ enum TWCoinType { TWCoinTypePersistence = 16000118, TWCoinTypeAkash = 17000118, TWCoinTypeNoble = 18000118, - TWCoinTypeScroll = 534353, + TWCoinTypeScroll = 534352, TWCoinTypeRootstock = 137, TWCoinTypeThetaFuel = 361, TWCoinTypeConfluxeSpace = 1030, diff --git a/registry.json b/registry.json index 6117b5d844e..0487d8b03e6 100644 --- a/registry.json +++ b/registry.json @@ -3258,7 +3258,7 @@ "id": "scroll", "name": "Scroll", "displayName": "Scroll", - "coinId": 534353, + "coinId": 534352, "slip44": 60, "symbol": "ETH", "decimals": 18, @@ -3270,14 +3270,14 @@ ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", - "chainId": "534353", + "chainId": "534352", "addressHasher": "keccak256", "explorer": { "url": "https://scrollscan.com", "txPath": "/tx/", "accountPath": "/address/", - "sampleTx": "0xee9196d6840c8d31626324d91c886d20e65711c2026c559133fb23741d3b2f9d", - "sampleAccount": "0xFE993660cd35d68D94b6Eba29F4D928d979cd65B" + "sampleTx": "0xa2062a4530b194a438bb9f9e87cdce59f70775a52e8336892364445847c43ca2", + "sampleAccount": "0xf9062b8a30e0d7722960e305049fa50b86ba6253" }, "info": { "url": "https://scroll.io", diff --git a/tests/chains/Scroll/TWAnySignerTests.cpp b/tests/chains/Scroll/TWAnySignerTests.cpp index 8d712b8f39d..8d00ce21d66 100644 --- a/tests/chains/Scroll/TWAnySignerTests.cpp +++ b/tests/chains/Scroll/TWAnySignerTests.cpp @@ -18,7 +18,7 @@ namespace TW::Scroll::tests { /// https://blockscout.scroll.io/tx/0x5a7ba291e0490079bddda54ca5592e5990d6b0eb49f8d239202941e3f63d32bc TEST(TWAnySignerScroll, Sign) { Ethereum::Proto::SigningInput input; - auto chainId = store(uint256_t(534353)); + auto chainId = store(uint256_t(534352)); auto nonce = store(uint256_t(1)); auto gasPrice = store(uint256_t(1000000)); auto gasLimit = store(uint256_t(200000)); @@ -35,7 +35,7 @@ TEST(TWAnySignerScroll, Sign) { auto amount = store(uint256_t(200000000000000)); transfer.set_amount(amount.data(), amount.size()); - std::string expected = "f86c01830f424083030d4094a6bc5ee0b1e904dd0773c5555d2f6833fe937a6886b5e620f480008083104ec6a0a30f1e29c3d1447d7f7f76c7112af3fb86eb3cb7cc536c932e720cd9813ae274a048ffcadba06d3645829d74fa1cf28744111cfc35893761239fa6b510c84c396e"; + std::string expected = "f86c01830f424083030d4094a6bc5ee0b1e904dd0773c5555d2f6833fe937a6886b5e620f480008083104ec3a0c43ee3d34f7758e05e2f54df227eb7780ad97d06e91e03ef6a3c91d4bea6e42fa07d075f20776f7f485faca6f057110fd2745a5cdd6cf121682ef7791619a03ade"; // sign test Ethereum::Proto::SigningOutput output; diff --git a/tests/chains/Scroll/TWCoinTypeTests.cpp b/tests/chains/Scroll/TWCoinTypeTests.cpp index 0d9ca7f3907..fe07ff53096 100644 --- a/tests/chains/Scroll/TWCoinTypeTests.cpp +++ b/tests/chains/Scroll/TWCoinTypeTests.cpp @@ -19,9 +19,9 @@ TEST(TWScrollCoinType, TWCoinType) { const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); const auto chainId = WRAPS(TWCoinTypeChainId(coin)); - const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xee9196d6840c8d31626324d91c886d20e65711c2026c559133fb23741d3b2f9d")); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xa2062a4530b194a438bb9f9e87cdce59f70775a52e8336892364445847c43ca2")); const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); - const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xFE993660cd35d68D94b6Eba29F4D928d979cd65B")); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xf9062b8a30e0d7722960e305049fa50b86ba6253")); const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); assertStringsEqual(id, "scroll"); @@ -31,7 +31,7 @@ TEST(TWScrollCoinType, TWCoinType) { ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainEthereum); ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); - assertStringsEqual(chainId, "534353"); - assertStringsEqual(txUrl, "https://scrollscan.com/tx/0xee9196d6840c8d31626324d91c886d20e65711c2026c559133fb23741d3b2f9d"); - assertStringsEqual(accUrl, "https://scrollscan.com/address/0xFE993660cd35d68D94b6Eba29F4D928d979cd65B"); + assertStringsEqual(chainId, "534352"); + assertStringsEqual(txUrl, "https://scrollscan.com/tx/0xa2062a4530b194a438bb9f9e87cdce59f70775a52e8336892364445847c43ca2"); + assertStringsEqual(accUrl, "https://scrollscan.com/address/0xf9062b8a30e0d7722960e305049fa50b86ba6253"); } From 6846db56b8dccb6d503f12ff6d6b8e7c94325868 Mon Sep 17 00:00:00 2001 From: weixuefeng Date: Tue, 31 Oct 2023 18:04:01 +0800 Subject: [PATCH 009/128] feat: add celestia like cosmos (#3510) --- .../blockchains/CoinAddressDerivationTests.kt | 1 + docs/registry.md | 1 + include/TrustWalletCore/TWCoinType.h | 1 + .../core/test/CoinAddressDerivationTests.kt | 1 + registry.json | 30 +++++++++++++++ swift/Tests/CoinAddressDerivationTests.swift | 3 ++ tests/chains/Cosmos/Tia/TWAnyAddressTests.cpp | 20 ++++++++++ tests/chains/Cosmos/Tia/TWCoinTypeTests.cpp | 37 +++++++++++++++++++ tests/common/CoinAddressDerivationTests.cpp | 3 ++ 9 files changed, 97 insertions(+) create mode 100644 tests/chains/Cosmos/Tia/TWAnyAddressTests.cpp create mode 100644 tests/chains/Cosmos/Tia/TWCoinTypeTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index 89f21271a53..135547a4b09 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -147,5 +147,6 @@ class CoinAddressDerivationTests { ROOTSTOCK -> assertEquals("0xA2D7065F94F838a3aB9C04D67B312056846424Df", address) SEI -> assertEquals("sei142j9u5eaduzd7faumygud6ruhdwme98qagm0sj", address) INTERNETCOMPUTER -> assertEquals("b9a13d974ee9db036d5abc5b66ace23e513cb5676f3996626c7717c339a3ee87", address) + TIA -> assertEquals("celestia142j9u5eaduzd7faumygud6ruhdwme98qpwmfv7", address) } } diff --git a/docs/registry.md b/docs/registry.md index 57cff87373f..74a8d9b829e 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -140,6 +140,7 @@ This list is generated from [./registry.json](../registry.json) | 20000118 | Stargaze | STARS | | | | 20000714 | BNB Smart Chain | BNB | | | | 20009001 | Native Evmos | EVMOS | | | +| 21000118 | Celestia | TIA | | | | 30000118 | Juno | JUNO | | | | 30000714 | TBNB | BNB | | | | 40000118 | Stride | STRD | | | diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index e993c7f1680..cd2780d8a55 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -178,6 +178,7 @@ enum TWCoinType { TWCoinTypeMantle = 5000, TWCoinTypeZenEON = 7332, TWCoinTypeInternetComputer = 223, + TWCoinTypeTia = 21000118, }; /// Returns the blockchain for a coin type. diff --git a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt index f5966bbd65c..351e7d0aa8e 100644 --- a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt +++ b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt @@ -143,5 +143,6 @@ class CoinAddressDerivationTests { Rootstock -> "0xA2D7065F94F838a3aB9C04D67B312056846424Df" Sei -> "sei142j9u5eaduzd7faumygud6ruhdwme98qagm0sj" InternetComputer -> "b9a13d974ee9db036d5abc5b66ace23e513cb5676f3996626c7717c339a3ee87" + Tia -> "celestia142j9u5eaduzd7faumygud6ruhdwme98qpwmfv7" } } diff --git a/registry.json b/registry.json index 0487d8b03e6..f8dc1115bd1 100644 --- a/registry.json +++ b/registry.json @@ -1166,6 +1166,36 @@ "documentation": "https://docs.sei.io/" } }, + { + "id": "tia", + "name": "Tia", + "displayName": "Celestia", + "coinId": 21000118, + "symbol": "TIA", + "decimals": 6, + "blockchain": "Cosmos", + "chainId": "mocha-4", + "derivation": [ + { + "path": "m/44'/118'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1", + "hrp": "celestia", + "addressHasher": "sha256ripemd", + "explorer": { + "url": "https://www.mintscan.io/tia", + "txPath": "/txs/", + "accountPath": "/account/", + "sampleTx": "943B1A59EB724A1258CF167834A83558DE5A31E8C1E31FCBCBB99404222E5FBF", + "sampleAccount": "celestia142j9u5eaduzd7faumygud6ruhdwme98qpwmfv7" + }, + "info": { + "url": "https://celestia.org/", + "documentation": "https://docs.celestia.org/" + } + }, { "id": "coreum", "name": "Coreum", diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 395a7a6e2ab..468aa0c5fbf 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -386,6 +386,9 @@ class CoinAddressDerivationTests: XCTestCase { case .internetComputer: let expectedResult = "b9a13d974ee9db036d5abc5b66ace23e513cb5676f3996626c7717c339a3ee87" assertCoinDerivation(coin, expectedResult, derivedAddress, address) + case .tia: + let expectedResult = "celestia142j9u5eaduzd7faumygud6ruhdwme98qpwmfv7" + assertCoinDerivation(coin, expectedResult, derivedAddress, address) @unknown default: fatalError() } diff --git a/tests/chains/Cosmos/Tia/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Tia/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..7a2c17bad5b --- /dev/null +++ b/tests/chains/Cosmos/Tia/TWAnyAddressTests.cpp @@ -0,0 +1,20 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "../CosmosTestHelpers.h" + +namespace TW::Cosmos::tests { + +static const std::string gTiaAddr = "celestia1mry47pkga5tdswtluy0m8teslpalkdq00tp7xc"; +static const std::string gTiaHrp = "celestia"; + +TEST(TWTiaAnyAddress, AllTiaAddressTests) { + CosmosAddressParameters parameters{.hrp = gTiaHrp, .coinType = TWCoinTypeTia, .address = gTiaAddr}; + TestCosmosAddressParameters(parameters); +} + +} + \ No newline at end of file diff --git a/tests/chains/Cosmos/Tia/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Tia/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..2a22712580a --- /dev/null +++ b/tests/chains/Cosmos/Tia/TWCoinTypeTests.cpp @@ -0,0 +1,37 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + +namespace TW::Cosmos::tests { + TEST(TWTiaCoinType, TWCoinType) { + const auto coin = TWCoinTypeTia; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto chainId = WRAPS(TWCoinTypeChainId(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("943B1A59EB724A1258CF167834A83558DE5A31E8C1E31FCBCBB99404222E5FBF")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("celestia142j9u5eaduzd7faumygud6ruhdwme98qpwmfv7")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + assertStringsEqual(id, "tia"); + assertStringsEqual(name, "Celestia"); + assertStringsEqual(symbol, "TIA"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 6); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainCosmos); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); + assertStringsEqual(chainId, "mocha-4"); + assertStringsEqual(txUrl, "https://www.mintscan.io/tia/txs/943B1A59EB724A1258CF167834A83558DE5A31E8C1E31FCBCBB99404222E5FBF"); + assertStringsEqual(accUrl, "https://www.mintscan.io/tia/account/celestia142j9u5eaduzd7faumygud6ruhdwme98qpwmfv7"); + } +} diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index 233e4962907..d9a8736b834 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -381,6 +381,9 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeInternetComputer: EXPECT_EQ(address, "cb3aa6a0471a417fc33d8e71f1d241750dfa29b4dc8f084265ce1301fb03b65b"); break; + case TWCoinTypeTia: + EXPECT_EQ(address, "celestia1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0g3wnkv"); + break; // no default branch here, intentionally, to better notice any missing coins } } From 72c67becb5f22ca3d6492cc7577aa3b8911ad7cb Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Tue, 31 Oct 2023 11:04:53 +0100 Subject: [PATCH 010/128] [Eth]: Fix EIP712 message hashing when fixed byte array is `0x0` (#3522) --- rust/tw_encoding/src/hex.rs | 23 ++++- .../src/message/eip712/eip712_message.rs | 9 +- .../tw_evm/tests/data/eip712_fixed_bytes.json | 83 +++++++++++++++++++ rust/tw_evm/tests/message_signer.rs | 13 +++ 4 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 rust/tw_evm/tests/data/eip712_fixed_bytes.json diff --git a/rust/tw_encoding/src/hex.rs b/rust/tw_encoding/src/hex.rs index d7cb5a5da95..38dc8b2e44f 100644 --- a/rust/tw_encoding/src/hex.rs +++ b/rust/tw_encoding/src/hex.rs @@ -5,6 +5,9 @@ // file LICENSE at the root of the source code distribution tree. pub use hex::FromHexError; +use tw_memory::Data; + +pub type FromHexResult = Result; pub trait ToHex { fn to_hex(&self) -> String; @@ -26,20 +29,34 @@ where } pub trait DecodeHex { - fn decode_hex(&self) -> Result, FromHexError>; + fn decode_hex(&self) -> FromHexResult; } impl<'a> DecodeHex for &'a str { - fn decode_hex(&self) -> Result, FromHexError> { + fn decode_hex(&self) -> FromHexResult { decode(self) } } -pub fn decode(data: &str) -> Result, FromHexError> { +/// Decodes the given hexadecimal string. +pub fn decode(data: &str) -> FromHexResult { let hex_string = data.trim_start_matches("0x"); hex::decode(hex_string) } +/// Decodes the given hexadecimal string leniently allowing to pass odd number of chars. +/// For example, `0x0` is extended to `0x00`, `0x123` is extended to `0x0123`. +pub fn decode_lenient(data: &str) -> FromHexResult { + let hex_string = data.trim_start_matches("0x"); + if hex_string.len() % 2 == 0 { + hex::decode(hex_string) + } else { + // Insert a leading 0. + let standard_hex = format!("0{hex_string}"); + hex::decode(standard_hex) + } +} + pub fn encode>(data: T, prefixed: bool) -> String { let encoded = hex::encode(data.as_ref()); if prefixed { diff --git a/rust/tw_evm/src/message/eip712/eip712_message.rs b/rust/tw_evm/src/message/eip712/eip712_message.rs index 7be57299689..9f21b15efaa 100644 --- a/rust/tw_evm/src/message/eip712/eip712_message.rs +++ b/rust/tw_evm/src/message/eip712/eip712_message.rs @@ -15,7 +15,7 @@ use serde::Deserialize; use serde_json::Value as Json; use std::collections::HashMap; use std::str::FromStr; -use tw_encoding::hex::DecodeHex; +use tw_encoding::hex::{self, DecodeHex}; use tw_hash::sha3::keccak256; use tw_hash::{H160, H256}; use tw_memory::Data; @@ -158,10 +158,9 @@ fn encode_fix_bytes(value: &Json, expected_len: usize) -> MessageSigningResult expected_len { return Err(MessageSigningError::TypeValueMismatch); } let checked_bytes = diff --git a/rust/tw_evm/tests/data/eip712_fixed_bytes.json b/rust/tw_evm/tests/data/eip712_fixed_bytes.json new file mode 100644 index 00000000000..563c8eaaa37 --- /dev/null +++ b/rust/tw_evm/tests/data/eip712_fixed_bytes.json @@ -0,0 +1,83 @@ +{ + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "salt", + "type": "bytes32" + } + ], + "Trade": [ + { + "name": "nonce", + "type": "bytes32" + }, + { + "name": "firstParty", + "type": "address" + }, + { + "name": "askingId", + "type": "uint256" + }, + { + "name": "askingQty", + "type": "uint256" + }, + { + "name": "offeringId", + "type": "uint256" + }, + { + "name": "offeringQty", + "type": "uint256" + }, + { + "name": "maxFee", + "type": "uint256" + }, + { + "name": "secondParty", + "type": "address" + }, + { + "name": "count", + "type": "uint8" + } + ] + }, + "domain": { + "name": "CryptoFights Trading", + "version": "1", + "chainId": 1, + "verifyingContract": "0xdc45529aC0FA3185f79A005e57deF64F600c4e97", + "salt": "0x0" + }, + "primaryType": "Trade", + "message": { + "count": 1, + "offeringQty": 1, + "askingQty": 2, + "nonce": "0xcfe49aa546453df3f2e768a97204a3268cef7c27df53cc2f2d47385cfeaf", + "firstParty": "0xC36edF48e21cf395B206352A1819DE658fD7f988", + "askingId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "offeringId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "maxFee": "1000000000000000000", + "secondParty": "0x0000000000000000000000000000000000000000" + } +} \ No newline at end of file diff --git a/rust/tw_evm/tests/message_signer.rs b/rust/tw_evm/tests/message_signer.rs index a1491c61dcd..7be25764c2a 100644 --- a/rust/tw_evm/tests/message_signer.rs +++ b/rust/tw_evm/tests/message_signer.rs @@ -19,6 +19,7 @@ const EIP712_WITH_CUSTOM_ARRAY: &str = include_str!("data/eip712_with_custom_arr const EIP712_UNEQUAL_ARRAY_LEN: &str = include_str!("data/eip712_unequal_array_lengths.json"); const EIP712_WITH_CHAIN_ID_STR: &str = include_str!("data/eip712_with_chain_id_string.json"); const EIP712_GREENFIELD: &str = include_str!("data/eip712_greenfield.json"); +const EIP712_FIXED_BYTES: &str = include_str!("data/eip712_fixed_bytes.json"); struct SignVerifyTestInput { private_key: &'static str, @@ -256,3 +257,15 @@ fn test_message_signer_sign_verify_eip712_greenfield() { signature: "cb3a4684a991014a387a04a85b59227ebb79567c2025addcb296b4ca856e9f810d3b526f2a0d0fad6ad1b126b3b9516f8b3be020a7cca9c03ce3cf47f4199b6d1b", }); } + +// The test checks if `0x0` is a valid `bytes32` value. +#[test] +fn test_message_signer_sign_verify_eip712_fixed_bytes() { + test_message_signer_sign_verify(SignVerifyTestInput { + private_key: "c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4", + msg: EIP712_FIXED_BYTES, + msg_type: Proto::MessageType::MessageType_typed, + chain_id: None, + signature: "7ee9b54fedf355e40fa86bbe23e63b318ef797bd8fdbc5bb714edbace042d4cb60111912218234e856f2cf300b3b47c91383b98e263ecf69c6c10193fef6c9581b", + }); +} From 1382e3c8ac6d8e956e25c0475039f6c3988f9355 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 1 Nov 2023 15:54:17 +0100 Subject: [PATCH 011/128] feat(Celestia): Add support for Celestia mainnet (#3528) --- registry.json | 8 ++-- tests/chains/Cosmos/Tia/TWCoinTypeTests.cpp | 44 +++++++++++---------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/registry.json b/registry.json index f8dc1115bd1..966fc0a2d71 100644 --- a/registry.json +++ b/registry.json @@ -1174,7 +1174,7 @@ "symbol": "TIA", "decimals": 6, "blockchain": "Cosmos", - "chainId": "mocha-4", + "chainId": "celestia", "derivation": [ { "path": "m/44'/118'/0'/0/0" @@ -1185,11 +1185,11 @@ "hrp": "celestia", "addressHasher": "sha256ripemd", "explorer": { - "url": "https://www.mintscan.io/tia", + "url": "https://www.mintscan.io/celestia", "txPath": "/txs/", "accountPath": "/account/", - "sampleTx": "943B1A59EB724A1258CF167834A83558DE5A31E8C1E31FCBCBB99404222E5FBF", - "sampleAccount": "celestia142j9u5eaduzd7faumygud6ruhdwme98qpwmfv7" + "sampleTx": "FF370C65D8D67B8236F9D3A8D2B1256337C60C1965092CADD1FA970288FCE99B", + "sampleAccount": "celestia1tt4tv4jrs4twdtzwywxd8u65duxgk8y73wvfu2" }, "info": { "url": "https://celestia.org/", diff --git a/tests/chains/Cosmos/Tia/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Tia/TWCoinTypeTests.cpp index 2a22712580a..a3d656fd5f6 100644 --- a/tests/chains/Cosmos/Tia/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Tia/TWCoinTypeTests.cpp @@ -13,25 +13,27 @@ #include namespace TW::Cosmos::tests { - TEST(TWTiaCoinType, TWCoinType) { - const auto coin = TWCoinTypeTia; - const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); - const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); - const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); - const auto chainId = WRAPS(TWCoinTypeChainId(coin)); - const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("943B1A59EB724A1258CF167834A83558DE5A31E8C1E31FCBCBB99404222E5FBF")); - const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); - const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("celestia142j9u5eaduzd7faumygud6ruhdwme98qpwmfv7")); - const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); - assertStringsEqual(id, "tia"); - assertStringsEqual(name, "Celestia"); - assertStringsEqual(symbol, "TIA"); - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 6); - ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainCosmos); - ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); - ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); - assertStringsEqual(chainId, "mocha-4"); - assertStringsEqual(txUrl, "https://www.mintscan.io/tia/txs/943B1A59EB724A1258CF167834A83558DE5A31E8C1E31FCBCBB99404222E5FBF"); - assertStringsEqual(accUrl, "https://www.mintscan.io/tia/account/celestia142j9u5eaduzd7faumygud6ruhdwme98qpwmfv7"); - } + +TEST(TWTiaCoinType, TWCoinType) { + const auto coin = TWCoinTypeTia; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto chainId = WRAPS(TWCoinTypeChainId(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("FF370C65D8D67B8236F9D3A8D2B1256337C60C1965092CADD1FA970288FCE99B")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("celestia1tt4tv4jrs4twdtzwywxd8u65duxgk8y73wvfu2")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + assertStringsEqual(id, "tia"); + assertStringsEqual(name, "Celestia"); + assertStringsEqual(symbol, "TIA"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 6); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainCosmos); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); + assertStringsEqual(chainId, "celestia"); + assertStringsEqual(txUrl, "https://www.mintscan.io/celestia/txs/FF370C65D8D67B8236F9D3A8D2B1256337C60C1965092CADD1FA970288FCE99B"); + assertStringsEqual(accUrl, "https://www.mintscan.io/celestia/account/celestia1tt4tv4jrs4twdtzwywxd8u65duxgk8y73wvfu2"); } + +} // namespace TW::Cosmos::tests From 21768763f640c96350bf44ce2523bc36898de9b7 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 15 Nov 2023 13:32:52 +0100 Subject: [PATCH 012/128] [Swap]: Fix BSC swap (#3548) * [THORSwap]: Fix memo for ->BSC swap * TODO broadcast a transaction * [CI] Trigger CI --- src/THORChain/Swap.cpp | 3 +- tests/chains/Cosmos/THORChain/SwapTests.cpp | 53 +++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/THORChain/Swap.cpp b/src/THORChain/Swap.cpp index dea097d1333..92ef7a79c95 100644 --- a/src/THORChain/Swap.cpp +++ b/src/THORChain/Swap.cpp @@ -78,8 +78,9 @@ std::string chainName(Chain chain) { case Chain::ETH: return "ETH"; case Chain::BNB: - case Chain::BSC: return "BNB"; + case Chain::BSC: + return "BSC"; case Chain::BTC: return "BTC"; case Chain::DOGE: diff --git a/tests/chains/Cosmos/THORChain/SwapTests.cpp b/tests/chains/Cosmos/THORChain/SwapTests.cpp index d64ffed7565..69901c310b2 100644 --- a/tests/chains/Cosmos/THORChain/SwapTests.cpp +++ b/tests/chains/Cosmos/THORChain/SwapTests.cpp @@ -391,6 +391,59 @@ TEST(THORChainSwap, SwapBtcBnb) { // https://explorer.binance.org/tx/8D78469069118E9B9546696214CCD46E63D3FA0D7E854C094D63C8F6061278B7 } +TEST(THORChainSwap, SwapUsdtBsc) { + auto myAddress = "0x0d6aA74992eDDaaf430eadca63B87f4C99Aef8dE"; + auto vaultAddress = "0x1f3b3c6ac151bf32409fe139a5d55f3d9444729c"; + auto routerAddress = "0xD37BbE5744D730a1d98d8DC97c42F0Ca46aD7146"; + auto usdtTokenId = "0xdAC17F958D2ee523a2206206994597C13D831ec7"; + auto amount = 70000000; + auto expirationTime = 1775669796; + + Proto::Asset fromAsset; + fromAsset.set_chain(static_cast(Chain::ETH)); + fromAsset.set_symbol("USDT"); + fromAsset.set_token_id(usdtTokenId); + Proto::Asset toAsset; + toAsset.set_chain(static_cast(Chain::BSC)); + toAsset.set_symbol("BSC"); + toAsset.set_token_id("BNB"); + + auto&& [out, errorCode, error] = SwapBuilder::builder() + .from(fromAsset) + .to(toAsset) + .fromAddress(myAddress) + .toAddress(myAddress) + .vault(vaultAddress) + .router(routerAddress) + .fromAmount(std::to_string(amount)) + .expirationPolicy(expirationTime) + .affFeeAddress("tr") + .affFeeRate("0") + .streamInterval("1") + .streamQuantity("0") + .build(); + ASSERT_EQ(errorCode, 0); + ASSERT_EQ(error, ""); + + auto tx = Ethereum::Proto::SigningInput(); + ASSERT_TRUE(tx.ParseFromArray(out.data(), (int)out.size())); + + // check fields + EXPECT_EQ(tx.to_address(), routerAddress); + ASSERT_TRUE(tx.transaction().has_contract_generic()); + + auto funcData = Ethereum::ABI::Function::encodeFunctionCall("depositWithExpiry", Ethereum::ABI::BaseParams{ + std::make_shared(vaultAddress), + std::make_shared(usdtTokenId), + std::make_shared(uint256_t(amount)), + std::make_shared("=:BSC.BNB:0x0d6aA74992eDDaaf430eadca63B87f4C99Aef8dE:0/1/0:tr:0"), + std::make_shared(uint256_t(expirationTime)) + }).value(); + EXPECT_EQ(hex(funcData), "44bc937b0000000000000000000000001f3b3c6ac151bf32409fe139a5d55f3d9444729c000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000042c1d8000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000069d69224000000000000000000000000000000000000000000000000000000000000003f3d3a4253432e424e423a3078306436614137343939326544446161663433306561646361363342383766344339394165663864453a302f312f303a74723a3000"); + EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().amount())), "00"); + EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().data())), hex(funcData)); +} + TEST(THORChainSwap, SwapAtomBnb) { Proto::Asset fromAsset; fromAsset.set_chain(static_cast(Chain::ATOM)); From 4a5fc03108436a9ab674a96c4b98a463d8f56f26 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Wed, 15 Nov 2023 14:05:34 -0500 Subject: [PATCH 013/128] [Rust/Aptos]: Move Aptos blockchain into Rust (#3497) --- .../blockchains/CoinAddressDerivationTests.kt | 2 +- .../core/test/CoinAddressDerivationTests.kt | 2 +- rust/Cargo.lock | 230 ++++- rust/Cargo.toml | 2 +- rust/coverage.stats | 2 +- rust/tw_any_coin/Cargo.toml | 3 + .../tests/chain_tests.rs} | 2 +- .../tests/chains/aptos/aptos_compile.rs | 86 ++ .../tests/chains/aptos/aptos_sign.rs | 43 + rust/tw_any_coin/tests/chains/aptos/mod.rs | 11 + .../tests/chains/aptos/test_cases.rs | 64 ++ rust/tw_any_coin/tests/chains/mod.rs | 7 + .../tests/tw_any_address_ffi_tests.rs | 23 + rust/tw_aptos/Cargo.toml | 22 + rust/tw_aptos/fuzz/.gitignore | 4 + rust/tw_aptos/fuzz/Cargo.toml | 29 + rust/tw_aptos/fuzz/fuzz_targets/sign.rs | 9 + rust/tw_aptos/src/address.rs | 139 +++ rust/tw_aptos/src/aptos_move_packages.rs | 209 +++++ rust/tw_aptos/src/bcs_encoding.rs | 0 rust/tw_aptos/src/compiler.rs | 74 ++ rust/tw_aptos/src/constants.rs | 9 + rust/tw_aptos/src/entry.rs | 94 ++ rust/tw_aptos/src/lib.rs | 20 + rust/tw_aptos/src/liquid_staking.rs | 154 +++ rust/tw_aptos/src/nft.rs | 165 ++++ rust/tw_aptos/src/serde_helper/mod.rs | 5 + rust/tw_aptos/src/serde_helper/vec_bytes.rs | 30 + rust/tw_aptos/src/signer.rs | 43 + rust/tw_aptos/src/transaction.rs | 220 +++++ rust/tw_aptos/src/transaction_builder.rs | 263 ++++++ rust/tw_aptos/src/transaction_payload.rs | 268 ++++++ rust/tw_aptos/tests/signer.rs | 873 ++++++++++++++++++ rust/tw_coin_entry/Cargo.toml | 1 + rust/tw_coin_entry/src/error.rs | 15 +- rust/tw_coin_registry/Cargo.toml | 1 + rust/tw_coin_registry/src/blockchain_type.rs | 2 + rust/tw_coin_registry/src/dispatcher.rs | 4 + rust/tw_encoding/Cargo.toml | 1 + rust/tw_encoding/src/bcs.rs | 16 + rust/tw_encoding/src/lib.rs | 1 + rust/tw_evm/fuzz/Cargo.toml | 2 +- rust/tw_keypair/src/tw/public.rs | 7 + rust/tw_misc/Cargo.toml | 5 + rust/tw_misc/src/lib.rs | 2 + rust/tw_misc/src/test_utils/json.rs | 44 + rust/tw_misc/src/test_utils/mod.rs | 7 + rust/tw_move_parser/Cargo.toml | 10 - rust/tw_move_parser/src/ffi.rs | 88 -- .../tests/move_parser_ffi_tests.rs | 49 - rust/wallet_core_rs/Cargo.toml | 2 +- rust/wallet_core_rs/src/lib.rs | 2 +- src/Aptos/Address.cpp | 30 - src/Aptos/Address.h | 43 - src/Aptos/Entry.cpp | 24 +- src/Aptos/MoveTypes.cpp | 146 --- src/Aptos/MoveTypes.h | 107 --- src/Aptos/Signer.cpp | 280 ------ src/Aptos/Signer.h | 32 - src/Aptos/TransactionBuilder.h | 195 ---- src/Aptos/TransactionPayload.cpp | 124 --- src/Aptos/TransactionPayload.h | 47 - src/BCS.cpp | 42 - src/BCS.h | 222 ----- swift/Tests/CoinAddressDerivationTests.swift | 2 +- tests/chains/Aptos/AddressTests.cpp | 53 +- tests/chains/Aptos/CompilerTests.cpp | 56 +- tests/chains/Aptos/MoveTypesTests.cpp | 59 -- tests/chains/Aptos/SignerTests.cpp | 730 --------------- tests/chains/Aptos/TWAnySignerTests.cpp | 7 +- tests/chains/Aptos/TWAptosAddressTests.cpp | 2 +- .../chains/Aptos/TransactionPayloadTests.cpp | 60 -- tests/common/BCSTests.cpp | 165 ---- .../common/rust/bindgen/WalletCoreRsTests.cpp | 9 - 74 files changed, 3216 insertions(+), 2585 deletions(-) rename rust/{tw_move_parser/src/lib.rs => tw_any_coin/tests/chain_tests.rs} (95%) create mode 100644 rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs create mode 100644 rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs create mode 100644 rust/tw_any_coin/tests/chains/aptos/mod.rs create mode 100644 rust/tw_any_coin/tests/chains/aptos/test_cases.rs create mode 100644 rust/tw_any_coin/tests/chains/mod.rs create mode 100644 rust/tw_aptos/Cargo.toml create mode 100644 rust/tw_aptos/fuzz/.gitignore create mode 100644 rust/tw_aptos/fuzz/Cargo.toml create mode 100644 rust/tw_aptos/fuzz/fuzz_targets/sign.rs create mode 100644 rust/tw_aptos/src/address.rs create mode 100644 rust/tw_aptos/src/aptos_move_packages.rs create mode 100644 rust/tw_aptos/src/bcs_encoding.rs create mode 100644 rust/tw_aptos/src/compiler.rs create mode 100644 rust/tw_aptos/src/constants.rs create mode 100644 rust/tw_aptos/src/entry.rs create mode 100644 rust/tw_aptos/src/lib.rs create mode 100644 rust/tw_aptos/src/liquid_staking.rs create mode 100644 rust/tw_aptos/src/nft.rs create mode 100644 rust/tw_aptos/src/serde_helper/mod.rs create mode 100644 rust/tw_aptos/src/serde_helper/vec_bytes.rs create mode 100644 rust/tw_aptos/src/signer.rs create mode 100644 rust/tw_aptos/src/transaction.rs create mode 100644 rust/tw_aptos/src/transaction_builder.rs create mode 100644 rust/tw_aptos/src/transaction_payload.rs create mode 100644 rust/tw_aptos/tests/signer.rs create mode 100644 rust/tw_encoding/src/bcs.rs create mode 100644 rust/tw_misc/src/test_utils/json.rs create mode 100644 rust/tw_misc/src/test_utils/mod.rs delete mode 100644 rust/tw_move_parser/Cargo.toml delete mode 100644 rust/tw_move_parser/src/ffi.rs delete mode 100644 rust/tw_move_parser/tests/move_parser_ffi_tests.rs delete mode 100644 src/Aptos/Address.cpp delete mode 100644 src/Aptos/Address.h delete mode 100644 src/Aptos/MoveTypes.cpp delete mode 100644 src/Aptos/MoveTypes.h delete mode 100644 src/Aptos/Signer.cpp delete mode 100644 src/Aptos/Signer.h delete mode 100644 src/Aptos/TransactionBuilder.h delete mode 100644 src/Aptos/TransactionPayload.cpp delete mode 100644 src/Aptos/TransactionPayload.h delete mode 100644 src/BCS.cpp delete mode 100644 src/BCS.h delete mode 100644 tests/chains/Aptos/MoveTypesTests.cpp delete mode 100644 tests/chains/Aptos/SignerTests.cpp delete mode 100644 tests/chains/Aptos/TransactionPayloadTests.cpp delete mode 100644 tests/common/BCSTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index 135547a4b09..0b0b62d0834 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -119,7 +119,7 @@ class CoinAddressDerivationTests { NERVOS -> assertEquals("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02wectaumxn0664yw2jd53lqk4mxg3", address) EVERSCALE -> assertEquals("0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04", address) TON -> assertEquals("EQDgEMqToTacHic7SnvnPFmvceG5auFkCcAw0mSCvzvKUfk9", address) - APTOS -> assertEquals("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", address) + APTOS -> assertEquals("0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", address) NEBL -> assertEquals("NgDVaXAwNgBwb88xLiFKomfBmPkEh9F2d7", address) SUI -> assertEquals("0xada112cfb90b44ba889cc5d39ac2bf46281e4a91f7919c693bcd9b8323e81ed2", address) HEDERA -> assertEquals("0.0.302a300506032b657003210049eba62f64d0d941045595d9433e65d84ecc46bcdb1421de55e05fcf2d8357d5", address) diff --git a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt index 351e7d0aa8e..1cbfd41590e 100644 --- a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt +++ b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt @@ -115,7 +115,7 @@ class CoinAddressDerivationTests { Nervos -> "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02wectaumxn0664yw2jd53lqk4mxg3" Everscale -> "0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04" TON -> "EQDgEMqToTacHic7SnvnPFmvceG5auFkCcAw0mSCvzvKUfk9" - Aptos -> "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30" + Aptos -> "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30" Nebl -> "NgDVaXAwNgBwb88xLiFKomfBmPkEh9F2d7" Sui -> "0xada112cfb90b44ba889cc5d39ac2bf46281e4a91f7919c693bcd9b8323e81ed2" Hedera -> "0.0.302a300506032b657003210049eba62f64d0d941045595d9433e65d84ecc46bcdb1421de55e05fcf2d8357d5" diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 035cd54ddad..c6fdc8c55a6 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -22,9 +22,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.69" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arbitrary" @@ -136,9 +136,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bcs" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b06b4c1f053002b70e7084ac944c77d58d5d92b2110dbc5e852735e00ad3ccc" +checksum = "85b6598a2f5d564fb7855dc6b06fd1c38cff5a72bd8b863a4d021938497b440a" dependencies = [ "serde", "thiserror", @@ -196,16 +196,28 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitvec" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" +dependencies = [ + "funty 1.1.0", + "radium 0.6.2", + "tap", + "wyz 0.2.0", +] + [[package]] name = "bitvec" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ - "funty", - "radium", + "funty 2.0.0", + "radium 0.7.0", "tap", - "wyz", + "wyz 0.5.1", ] [[package]] @@ -521,6 +533,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" +[[package]] +name = "ethnum" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8ff382b2fa527fb7fb06eeebfc5bbb3f17e3cc6b9d70b006c41daa8824adac" + [[package]] name = "ff" version = "0.13.0" @@ -531,6 +549,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + [[package]] name = "fixed-hash" version = "0.8.0" @@ -544,6 +574,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + [[package]] name = "funty" version = "2.0.0" @@ -665,13 +701,31 @@ dependencies = [ "quick-error", ] +[[package]] +name = "impl-codec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" +dependencies = [ + "parity-scale-codec 2.3.1", +] + [[package]] name = "impl-codec" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 3.5.0", +] + +[[package]] +name = "impl-serde" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +dependencies = [ + "serde", ] [[package]] @@ -778,16 +832,20 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "move-core-types" version = "0.0.4" -source = "git+https://github.com/move-language/move?rev=f7137eabc2046f76fdad3ded2c51e03a3b1fbd01#f7137eabc2046f76fdad3ded2c51e03a3b1fbd01" +source = "git+https://github.com/move-language/move?rev=ea70797099baea64f05194a918cebd69ed02b285#ea70797099baea64f05194a918cebd69ed02b285" dependencies = [ "anyhow", "bcs", + "ethnum", "hex", + "num", "once_cell", + "primitive-types 0.10.1", "rand", "ref-cast", "serde", "serde_bytes", + "uint", ] [[package]] @@ -800,6 +858,20 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -811,6 +883,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-complex" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +dependencies = [ + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -821,6 +902,29 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -854,6 +958,20 @@ dependencies = [ "sha2 0.10.6", ] +[[package]] +name = "parity-scale-codec" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" +dependencies = [ + "arrayvec", + "bitvec 0.20.4", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive 2.3.1", + "serde", +] + [[package]] name = "parity-scale-codec" version = "3.5.0" @@ -861,13 +979,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ddb756ca205bd108aee3c62c6d3c994e1df84a59b9d6d4a5ea42ee1fd5a9a28" dependencies = [ "arrayvec", - "bitvec", + "bitvec 1.0.1", "byte-slice-cast", "impl-trait-for-tuples", - "parity-scale-codec-derive", + "parity-scale-codec-derive 3.1.4", "serde", ] +[[package]] +name = "parity-scale-codec-derive" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.107", +] + [[package]] name = "parity-scale-codec-derive" version = "3.1.4" @@ -923,14 +1053,26 @@ dependencies = [ "elliptic-curve", ] +[[package]] +name = "primitive-types" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" +dependencies = [ + "fixed-hash 0.7.0", + "impl-codec 0.5.1", + "impl-serde", + "uint", +] + [[package]] name = "primitive-types" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" dependencies = [ - "fixed-hash", - "impl-codec", + "fixed-hash 0.8.0", + "impl-codec 0.6.0", "uint", ] @@ -977,6 +1119,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" + [[package]] name = "radium" version = "0.7.0" @@ -1166,9 +1314,9 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] @@ -1184,9 +1332,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", @@ -1195,9 +1343,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -1446,6 +1594,8 @@ dependencies = [ name = "tw_any_coin" version = "0.1.0" dependencies = [ + "serde", + "serde_json", "tw_any_coin", "tw_coin_entry", "tw_coin_registry", @@ -1457,6 +1607,23 @@ dependencies = [ "tw_proto", ] +[[package]] +name = "tw_aptos" +version = "0.1.0" +dependencies = [ + "move-core-types", + "serde", + "serde_bytes", + "serde_json", + "tw_coin_entry", + "tw_encoding", + "tw_hash", + "tw_keypair", + "tw_memory", + "tw_number", + "tw_proto", +] + [[package]] name = "tw_bitcoin" version = "0.1.0" @@ -1480,6 +1647,7 @@ name = "tw_coin_entry" version = "0.1.0" dependencies = [ "serde_json", + "tw_encoding", "tw_keypair", "tw_memory", "tw_misc", @@ -1494,6 +1662,7 @@ dependencies = [ "lazy_static", "serde", "serde_json", + "tw_aptos", "tw_bitcoin", "tw_coin_entry", "tw_ethereum", @@ -1510,6 +1679,7 @@ name = "tw_encoding" version = "0.1.0" dependencies = [ "arbitrary", + "bcs", "bs58", "ciborium", "data-encoding", @@ -1622,26 +1792,18 @@ version = "0.1.0" name = "tw_misc" version = "0.1.0" dependencies = [ + "serde", + "serde_json", "zeroize", ] -[[package]] -name = "tw_move_parser" -version = "0.1.0" -dependencies = [ - "bcs", - "hex", - "move-core-types", - "tw_memory", -] - [[package]] name = "tw_number" version = "0.1.0" dependencies = [ "arbitrary", "lazy_static", - "primitive-types", + "primitive-types 0.12.1", "serde", "tw_encoding", "tw_hash", @@ -1746,6 +1908,7 @@ version = "0.1.0" dependencies = [ "serde_json", "tw_any_coin", + "tw_aptos", "tw_bitcoin", "tw_coin_entry", "tw_coin_registry", @@ -1755,7 +1918,6 @@ dependencies = [ "tw_keypair", "tw_memory", "tw_misc", - "tw_move_parser", "tw_number", "tw_proto", ] @@ -1876,6 +2038,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + [[package]] name = "wyz" version = "0.5.1" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index e5c065055f1..872986a6a6a 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "tw_any_coin", + "tw_aptos", "tw_bitcoin", "tw_coin_entry", "tw_coin_registry", @@ -12,7 +13,6 @@ members = [ "tw_keypair", "tw_memory", "tw_misc", - "tw_move_parser", "tw_number", "tw_proto", "tw_ronin", diff --git a/rust/coverage.stats b/rust/coverage.stats index 8670b15529f..7d7ab43dc7c 100644 --- a/rust/coverage.stats +++ b/rust/coverage.stats @@ -1 +1 @@ -91.0 \ No newline at end of file +92.0 \ No newline at end of file diff --git a/rust/tw_any_coin/Cargo.toml b/rust/tw_any_coin/Cargo.toml index 7ea537e73c2..17ba71621f1 100644 --- a/rust/tw_any_coin/Cargo.toml +++ b/rust/tw_any_coin/Cargo.toml @@ -14,9 +14,12 @@ tw_misc = { path = "../tw_misc" } test-utils = [] [dev-dependencies] +serde = { version = "1.0.163", features = ["derive"] } +serde_json = { version = "1.0.96" } tw_any_coin = { path = "./", features = ["test-utils"] } tw_encoding = { path = "../tw_encoding" } tw_keypair = { path = "../tw_keypair", features = ["test-utils"] } tw_memory = { path = "../tw_memory", features = ["test-utils"] } +tw_misc = { path = "../tw_misc", features = ["test-utils"] } tw_number = { path = "../tw_number" } tw_proto = { path = "../tw_proto" } diff --git a/rust/tw_move_parser/src/lib.rs b/rust/tw_any_coin/tests/chain_tests.rs similarity index 95% rename from rust/tw_move_parser/src/lib.rs rename to rust/tw_any_coin/tests/chain_tests.rs index c9f1c100548..c1b24a82062 100644 --- a/rust/tw_move_parser/src/lib.rs +++ b/rust/tw_any_coin/tests/chain_tests.rs @@ -4,4 +4,4 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -pub mod ffi; +mod chains; diff --git a/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs b/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs new file mode 100644 index 00000000000..9f21f68d317 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs @@ -0,0 +1,86 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::chains::aptos::test_cases::transfer_b4d62afd::{ + aptos_sign_transfer_input, expected_json, DATA_TO_SIGN, ENCODED, PRIVATE_KEY, RAW_TXN, + SIGNATURE, +}; +use crate::chains::aptos::APTOS_COIN_TYPE; +use tw_any_coin::ffi::tw_transaction_compiler::{ + tw_transaction_compiler_compile, tw_transaction_compiler_pre_image_hashes, +}; +use tw_coin_entry::error::SigningErrorType; +use tw_encoding::hex::ToHex; +use tw_keypair::ed25519; +use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_memory::test_utils::tw_data_vector_helper::TWDataVectorHelper; +use tw_misc::assert_eq_json; +use tw_misc::traits::ToBytesVec; +use tw_proto::Aptos::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; +use tw_proto::{deserialize, serialize}; + +#[test] +fn test_any_signer_sign_aptos() { + let input = aptos_sign_transfer_input(); + + // Step 2: Obtain preimage hash + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + let preimage_data = TWDataHelper::wrap(unsafe { + tw_transaction_compiler_pre_image_hashes(APTOS_COIN_TYPE, input_data.ptr()) + }) + .to_vec() + .expect("!tw_transaction_compiler_pre_image_hashes returned nullptr"); + + let preimage: CompilerProto::PreSigningOutput = + deserialize(&preimage_data).expect("Coin entry returned an invalid output"); + + assert_eq!(preimage.error, SigningErrorType::OK); + assert!(preimage.error_message.is_empty()); + assert_eq!(preimage.data.to_hex(), DATA_TO_SIGN); + + // Step 3: Sign the data "externally" + + let private_key = ed25519::sha512::KeyPair::try_from(PRIVATE_KEY).unwrap(); + let public_key = private_key.public().to_vec(); + + let signature = private_key + .sign(preimage.data.to_vec()) + .expect("Error signing data") + .to_vec(); + assert_eq!(signature.to_hex(), SIGNATURE); + + // Step 4: Compile transaction info + + let signatures = TWDataVectorHelper::create([signature]); + let public_keys = TWDataVectorHelper::create([public_key]); + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + let output_data = TWDataHelper::wrap(unsafe { + tw_transaction_compiler_compile( + APTOS_COIN_TYPE, + input_data.ptr(), + signatures.ptr(), + public_keys.ptr(), + ) + }) + .to_vec() + .expect("!tw_transaction_compiler_compile returned nullptr"); + + let output: Proto::SigningOutput = + deserialize(&output_data).expect("Coin entry returned an invalid output"); + + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let authenticator = output.authenticator.unwrap(); + assert_eq!(authenticator.signature.to_hex(), SIGNATURE); + assert_eq!(output.raw_txn.to_hex(), RAW_TXN); + assert_eq!(output.encoded.to_hex(), ENCODED); + + assert_eq_json!(output.json, expected_json()); +} diff --git a/rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs b/rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs new file mode 100644 index 00000000000..6021ce8bb92 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs @@ -0,0 +1,43 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::chains::aptos::test_cases::transfer_b4d62afd::{ + aptos_sign_transfer_input, expected_json, ENCODED, PRIVATE_KEY, RAW_TXN, SIGNATURE, +}; +use crate::chains::aptos::APTOS_COIN_TYPE; +use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; +use tw_coin_entry::error::SigningErrorType; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_misc::assert_eq_json; +use tw_proto::Aptos::Proto; +use tw_proto::{deserialize, serialize}; + +#[test] +fn test_any_signer_sign_aptos() { + let input = Proto::SigningInput { + private_key: PRIVATE_KEY.decode_hex().unwrap().into(), + ..aptos_sign_transfer_input() + }; + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output = + TWDataHelper::wrap(unsafe { tw_any_signer_sign(input_data.ptr(), APTOS_COIN_TYPE) }) + .to_vec() + .expect("!tw_any_signer_sign returned nullptr"); + + let output: Proto::SigningOutput = deserialize(&output).unwrap(); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let authenticator = output.authenticator.unwrap(); + assert_eq!(authenticator.signature.to_hex(), SIGNATURE); + assert_eq!(output.raw_txn.to_hex(), RAW_TXN); + assert_eq!(output.encoded.to_hex(), ENCODED); + + assert_eq_json!(output.json, expected_json()); +} diff --git a/rust/tw_any_coin/tests/chains/aptos/mod.rs b/rust/tw_any_coin/tests/chains/aptos/mod.rs new file mode 100644 index 00000000000..0e046226232 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/aptos/mod.rs @@ -0,0 +1,11 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +mod aptos_compile; +mod aptos_sign; +mod test_cases; + +const APTOS_COIN_TYPE: u32 = 637; diff --git a/rust/tw_any_coin/tests/chains/aptos/test_cases.rs b/rust/tw_any_coin/tests/chains/aptos/test_cases.rs new file mode 100644 index 00000000000..81b166f7247 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/aptos/test_cases.rs @@ -0,0 +1,64 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde_json::{json, Value as Json}; +use tw_proto::Aptos::Proto; +use tw_proto::Aptos::Proto::mod_SigningInput::OneOftransaction_payload as TransactionPayloadEnum; + +pub(super) mod transfer_b4d62afd { + use super::*; + + /// Expected private key. + pub const PRIVATE_KEY: &str = + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec"; + pub const ENCODED: &str = "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"; + /// Expected `raw_txn`. + pub const RAW_TXN: &str = "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021"; + /// Expected signature. + pub const SIGNATURE: &str = "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"; + /// Expected preimage data to be signed. + pub const DATA_TO_SIGN: &str = "b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021"; + + pub fn aptos_sign_transfer_input() -> Proto::SigningInput<'static> { + let transfer = Proto::TransferMessage { + to: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30".into(), + amount: 1000, + }; + + Proto::SigningInput { + sender: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30".into(), + sequence_number: 99, + max_gas_amount: 3296766, + gas_unit_price: 100, + expiration_timestamp_secs: 3664390082, + chain_id: 33, + any_encoded: Default::default(), + transaction_payload: TransactionPayloadEnum::transfer(transfer), + ..Proto::SigningInput::default() + } + } + + pub fn expected_json() -> Json { + json!({ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], + "function": "0x1::aptos_account::transfer", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "99", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", + "type": "ed25519_signature" + } + }) + } +} diff --git a/rust/tw_any_coin/tests/chains/mod.rs b/rust/tw_any_coin/tests/chains/mod.rs new file mode 100644 index 00000000000..fa5b3a9718c --- /dev/null +++ b/rust/tw_any_coin/tests/chains/mod.rs @@ -0,0 +1,7 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +mod aptos; diff --git a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs index 0449c69ca59..10013b4e6d3 100644 --- a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs +++ b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs @@ -34,6 +34,9 @@ fn test_any_address_derive() { // TODO match `CoinType` when it's generated. let expected_address = match coin.blockchain { + BlockchainType::Aptos => { + "0x9006fa46f038224e8004bdda97f2e7a60c2c3d135bce7cb15541e5c0aae907a4" + }, // By default, Bitcoin will return a P2PKH address. BlockchainType::Bitcoin => "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", BlockchainType::Ethereum => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", @@ -62,6 +65,10 @@ fn test_any_address_derive() { fn test_any_address_normalize_eth() { for coin in supported_coin_items() { let (denormalized, expected_normalized) = match coin.blockchain { + BlockchainType::Aptos => ( + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + ), BlockchainType::Bitcoin => ( "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", @@ -101,6 +108,14 @@ fn test_any_address_normalize_eth() { fn test_any_address_is_valid_coin() { for coin in supported_coin_items() { let valid = match coin.blockchain { + BlockchainType::Aptos => vec![ + "0x1", + "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", + "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", + "19aadeca9388e009d136245b9a67423f3eee242b03142849eb4f81a4a409e59c", + "0x777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb", + "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175", + ], BlockchainType::Bitcoin => vec![ "1MrZNGN7mfWZiZNQttrzHjfw72jnJC2JNx", "bc1qunq74p3h8425hr6wllevlvqqr6sezfxj262rff", @@ -136,6 +151,14 @@ fn test_any_address_is_valid_coin() { fn test_any_address_is_valid_coin_invalid() { for coin in supported_coin_items() { let invalid = match coin.blockchain { + BlockchainType::Aptos => { + vec![ + "", // Empty + "Seff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", // Invalid Hex + "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175bb", // Too long + "0xSeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", + ] + }, BlockchainType::Bitcoin => { vec!["0xb16db98b365b1f89191996942612b14f1da4bd5f"] }, diff --git a/rust/tw_aptos/Cargo.toml b/rust/tw_aptos/Cargo.toml new file mode 100644 index 00000000000..49e4d120138 --- /dev/null +++ b/rust/tw_aptos/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "tw_aptos" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde_json = "1.0.108" +tw_coin_entry = { path = "../tw_coin_entry" } +tw_encoding = { path = "../tw_encoding" } +tw_keypair = { path = "../tw_keypair" } +tw_proto = { path = "../tw_proto" } +tw_number = { path = "../tw_number" } +tw_hash = { path = "../tw_hash" } +tw_memory = { path = "../tw_memory" } +move-core-types = { git = "https://github.com/move-language/move", rev = "ea70797099baea64f05194a918cebd69ed02b285", features = ["address32"] } +serde = { version = "1.0.189", features = ["derive"] } +serde_bytes = "0.11.12" + +[dev-dependencies] +tw_coin_entry = { path = "../tw_coin_entry", features = ["test-utils"] } +tw_encoding = { path = "../tw_encoding" } +tw_number = { path = "../tw_number", features = ["helpers"] } diff --git a/rust/tw_aptos/fuzz/.gitignore b/rust/tw_aptos/fuzz/.gitignore new file mode 100644 index 00000000000..1a45eee7760 --- /dev/null +++ b/rust/tw_aptos/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/rust/tw_aptos/fuzz/Cargo.toml b/rust/tw_aptos/fuzz/Cargo.toml new file mode 100644 index 00000000000..2d3506a0ea6 --- /dev/null +++ b/rust/tw_aptos/fuzz/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "tw_aptos-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" +tw_number = { path = "../../tw_number" } +tw_proto = { path = "../../tw_proto", features = ["fuzz"] } + +[dependencies.tw_aptos] +path = ".." + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[profile.release] +debug = 1 + +[[bin]] +name = "sign" +path = "fuzz_targets/sign.rs" +test = false +doc = false diff --git a/rust/tw_aptos/fuzz/fuzz_targets/sign.rs b/rust/tw_aptos/fuzz/fuzz_targets/sign.rs new file mode 100644 index 00000000000..5d55e467d14 --- /dev/null +++ b/rust/tw_aptos/fuzz/fuzz_targets/sign.rs @@ -0,0 +1,9 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; +use tw_aptos::signer::Signer; +use tw_proto::Aptos::Proto; + +fuzz_target!(|input: Proto::SigningInput<'_>| { + let _ = Signer::sign_proto(input); +}); diff --git a/rust/tw_aptos/src/address.rs b/rust/tw_aptos/src/address.rs new file mode 100644 index 00000000000..3f5cf1d3217 --- /dev/null +++ b/rust/tw_aptos/src/address.rs @@ -0,0 +1,139 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use move_core_types::account_address::{AccountAddress, AccountAddressParseError}; +use std::fmt::{Display, Formatter}; +use std::str::FromStr; +use tw_coin_entry::coin_entry::CoinAddress; +use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_hash::sha3::sha3_256; +use tw_keypair::ed25519; +use tw_memory::Data; + +pub trait AptosAddress: FromStr + Into
{ + /// Tries to parse an address from the string representation. + /// Returns `Ok(None)` if the given `s` string is empty. + #[inline] + fn from_str_optional(s: &str) -> AddressResult> { + if s.is_empty() { + return Ok(None); + } + + Self::from_str(s).map(Some) + } +} + +impl AptosAddress for Address {} + +#[repr(u8)] +pub enum Scheme { + Ed25519 = 0, +} + +#[derive(Clone)] +pub struct Address { + addr: AccountAddress, +} + +impl Address { + pub const LENGTH: usize = AccountAddress::LENGTH; + /// Initializes an address with a `ed25519` public key. + + pub fn with_ed25519_pubkey( + pubkey: &ed25519::sha512::PublicKey, + ) -> Result { + let mut to_hash = pubkey.as_slice().to_vec(); + to_hash.push(Scheme::Ed25519 as u8); + let hashed = sha3_256(to_hash.as_slice()); + let addr = AccountAddress::from_bytes(hashed).map_err(from_account_error)?; + Ok(Address { addr }) + } + + pub fn inner(&self) -> AccountAddress { + self.addr + } +} + +impl Display for Address { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.addr.to_hex_literal()) + } +} + +impl CoinAddress for Address { + #[inline] + fn data(&self) -> Data { + self.addr.to_vec() + } +} + +#[inline] +pub fn from_account_error(_err: AccountAddressParseError) -> AddressError { + AddressError::InvalidInput +} + +impl FromStr for Address { + type Err = AddressError; + + // https://github.com/aptos-labs/aptos-core/blob/261019cbdafe1c514c60c2b74357ea2c77d25e67/types/src/account_address.rs#L44 + fn from_str(s: &str) -> Result { + const NUM_CHARS: usize = AccountAddress::LENGTH * 2; + let mut has_0x = false; + let mut working = s.trim(); + + // Checks if it has a 0x at the beginning, which is okay + if working.starts_with("0x") { + has_0x = true; + working = &working[2..]; + } + + if working.len() > NUM_CHARS || (!has_0x && working.len() < NUM_CHARS) { + return Err(AddressError::InvalidInput); + } + + if !working.chars().all(|c| char::is_ascii_hexdigit(&c)) { + return Err(AddressError::InvalidInput); + } + + let addr = if has_0x { + AccountAddress::from_hex_literal(s.trim()) + } else { + AccountAddress::from_str(s.trim()) + } + .map_err(from_account_error)?; + + Ok(Address { addr }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tw_keypair::ed25519::sha512::PrivateKey; + + #[test] + fn test_from_public_key() { + let private = PrivateKey::try_from( + "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", + ) + .unwrap(); + let public = private.public(); + let addr = Address::with_ed25519_pubkey(&public); + assert_eq!( + addr.as_ref().unwrap().to_string(), + "0x9006fa46f038224e8004bdda97f2e7a60c2c3d135bce7cb15541e5c0aae907a4" + ); + assert_eq!(addr.unwrap().data().len(), Address::LENGTH); + } + + #[test] + fn test_from_account_error() { + assert_eq!( + from_account_error(AccountAddressParseError {}), + AddressError::InvalidInput + ); + } +} diff --git a/rust/tw_aptos/src/aptos_move_packages.rs b/rust/tw_aptos/src/aptos_move_packages.rs new file mode 100644 index 00000000000..2e8f7aff378 --- /dev/null +++ b/rust/tw_aptos/src/aptos_move_packages.rs @@ -0,0 +1,209 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::transaction_payload::{EntryFunction, TransactionPayload}; +use move_core_types::account_address::AccountAddress; +use move_core_types::ident_str; +use move_core_types::language_storage::{ModuleId, TypeTag}; +use serde_json::json; +use tw_coin_entry::error::SigningResult; +use tw_encoding::bcs; + +pub fn aptos_account_transfer( + to: AccountAddress, + amount: u64, +) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ]), + ident_str!("aptos_account").to_owned(), + ), + ident_str!("transfer").to_owned(), + vec![], + vec![bcs::encode(&to)?, bcs::encode(&amount)?], + json!([to.to_hex_literal(), amount.to_string()]), + ))) +} + +pub fn aptos_account_create_account(auth_key: AccountAddress) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ]), + ident_str!("aptos_account").to_owned(), + ), + ident_str!("create_account").to_owned(), + vec![], + vec![bcs::encode(&auth_key)?], + json!([auth_key.to_hex_literal()]), + ))) +} + +pub fn coin_transfer( + coin_type: TypeTag, + to: AccountAddress, + amount: u64, +) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ]), + ident_str!("coin").to_owned(), + ), + ident_str!("transfer").to_owned(), + vec![coin_type], + vec![bcs::encode(&to)?, bcs::encode(&amount)?], + json!([to.to_hex_literal(), amount.to_string()]), + ))) +} + +pub fn aptos_account_transfer_coins( + coin_type: TypeTag, + to: AccountAddress, + amount: u64, +) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ]), + ident_str!("aptos_account").to_owned(), + ), + ident_str!("transfer_coins").to_owned(), + vec![coin_type], + vec![bcs::encode(&to)?, bcs::encode(&amount)?], + json!([to.to_hex_literal(), amount.to_string()]), + ))) +} + +pub fn token_transfers_offer_script( + receiver: AccountAddress, + creator: AccountAddress, + collection: Vec, + name: Vec, + property_version: u64, + amount: u64, +) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, + ]), + ident_str!("token_transfers").to_owned(), + ), + ident_str!("offer_script").to_owned(), + vec![], + vec![ + bcs::encode(&receiver)?, + bcs::encode(&creator)?, + bcs::encode(&collection)?, + bcs::encode(&name)?, + bcs::encode(&property_version)?, + bcs::encode(&amount)?, + ], + json!([ + receiver.to_hex_literal(), + creator.to_hex_literal(), + String::from_utf8_lossy(&collection), + String::from_utf8_lossy(&name), + property_version.to_string(), + amount.to_string() + ]), + ))) +} + +pub fn token_transfers_cancel_offer_script( + receiver: AccountAddress, + creator: AccountAddress, + collection: Vec, + name: Vec, + property_version: u64, +) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, + ]), + ident_str!("token_transfers").to_owned(), + ), + ident_str!("cancel_offer_script").to_owned(), + vec![], + vec![ + bcs::encode(&receiver)?, + bcs::encode(&creator)?, + bcs::encode(&collection)?, + bcs::encode(&name)?, + bcs::encode(&property_version)?, + ], + json!([ + receiver.to_hex_literal(), + creator.to_hex_literal(), + String::from_utf8_lossy(&collection), + String::from_utf8_lossy(&name), + property_version.to_string() + ]), + ))) +} + +pub fn token_transfers_claim_script( + sender: AccountAddress, + creator: AccountAddress, + collection: Vec, + name: Vec, + property_version: u64, +) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, + ]), + ident_str!("token_transfers").to_owned(), + ), + ident_str!("claim_script").to_owned(), + vec![], + vec![ + bcs::encode(&sender)?, + bcs::encode(&creator)?, + bcs::encode(&collection)?, + bcs::encode(&name)?, + bcs::encode(&property_version)?, + ], + json!([ + sender.to_hex_literal(), + creator.to_hex_literal(), + String::from_utf8_lossy(&collection), + String::from_utf8_lossy(&name), + property_version.to_string() + ]), + ))) +} + +pub fn managed_coin_register(coin_type: TypeTag) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ]), + ident_str!("managed_coin").to_owned(), + ), + ident_str!("register").to_owned(), + vec![coin_type], + vec![], + json!([]), + )) +} diff --git a/rust/tw_aptos/src/bcs_encoding.rs b/rust/tw_aptos/src/bcs_encoding.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/rust/tw_aptos/src/compiler.rs b/rust/tw_aptos/src/compiler.rs new file mode 100644 index 00000000000..73954261f19 --- /dev/null +++ b/rust/tw_aptos/src/compiler.rs @@ -0,0 +1,74 @@ +use crate::address::Address; +use crate::transaction_builder; +use std::str::FromStr; +use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::signing_output_error; +use tw_proto::Aptos::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct Compiler; + +impl Compiler { + #[inline] + pub fn preimage_hashes( + input: Proto::SigningInput<'_>, + ) -> CompilerProto::PreSigningOutput<'static> { + Self::preimage_hashes_impl(input) + .unwrap_or_else(|e| signing_output_error!(CompilerProto::PreSigningOutput, e)) + } + + fn preimage_hashes_impl( + input: Proto::SigningInput<'_>, + ) -> SigningResult> { + let builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone())?; + let sender = Address::from_str(&input.sender)?; + let signed_tx = builder + .sender(sender.inner()) + .sequence_number(input.sequence_number as u64) + .build()? + .pre_image()?; + Ok(CompilerProto::PreSigningOutput { + data: signed_tx.into(), + ..CompilerProto::PreSigningOutput::default() + }) + } + + #[inline] + pub fn compile( + input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Proto::SigningOutput<'static> { + Self::compile_impl(input, signatures, public_keys) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn compile_impl( + input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> SigningResult> { + let builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone())?; + let sender = Address::from_str(&input.sender)?; + let signature = signatures + .first() + .ok_or(SigningError(SigningErrorType::Error_signatures_count))?; + let public_key = public_keys + .first() + .ok_or(SigningError(SigningErrorType::Error_signatures_count))?; + + let signed_tx = builder + .sender(sender.inner()) + .sequence_number(input.sequence_number as u64) + .build()? + .compile(signature.to_vec(), public_key.to_vec())?; + Ok(Proto::SigningOutput { + raw_txn: signed_tx.raw_txn_bytes().clone().into(), + encoded: signed_tx.encoded().clone().into(), + authenticator: Some((*signed_tx.authenticator()).clone().into()), + json: signed_tx.to_json().to_string().into(), + ..Proto::SigningOutput::default() + }) + } +} diff --git a/rust/tw_aptos/src/constants.rs b/rust/tw_aptos/src/constants.rs new file mode 100644 index 00000000000..6c30d3b68bc --- /dev/null +++ b/rust/tw_aptos/src/constants.rs @@ -0,0 +1,9 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub const GAS_UNIT_PRICE: u64 = 100; +pub const MAX_GAS_AMOUNT: u64 = 100_000_000; +pub const APTOS_SALT: &[u8] = b"APTOS::RawTransaction"; diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs new file mode 100644 index 00000000000..4438a15ae2b --- /dev/null +++ b/rust/tw_aptos/src/entry.rs @@ -0,0 +1,94 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::Address; +use crate::compiler::Compiler; +use crate::signer::Signer; +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::derivation::Derivation; +use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::modules::json_signer::NoJsonSigner; +use tw_coin_entry::modules::message_signer::NoMessageSigner; +use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::prefix::NoPrefix; +use tw_keypair::tw::PublicKey; +use tw_proto::Aptos::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct AptosEntry; + +impl CoinEntry for AptosEntry { + type AddressPrefix = NoPrefix; + type Address = Address; + type SigningInput<'a> = Proto::SigningInput<'a>; + type SigningOutput = Proto::SigningOutput<'static>; + type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + + // Optional modules: + type JsonSigner = NoJsonSigner; + type PlanBuilder = NoPlanBuilder; + type MessageSigner = NoMessageSigner; + + #[inline] + fn parse_address( + &self, + _coin: &dyn CoinContext, + address: &str, + _prefix: Option, + ) -> AddressResult { + Address::from_str(address) + } + + fn derive_address( + &self, + _coin: &dyn CoinContext, + public_key: PublicKey, + _derivation: Derivation, + _prefix: Option, + ) -> AddressResult { + let public_key = public_key + .to_ed25519() + .ok_or(AddressError::PublicKeyTypeMismatch)?; + Address::with_ed25519_pubkey(public_key) + } + + #[inline] + fn sign(&self, _coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput { + Signer::sign_proto(input) + } + + #[inline] + fn preimage_hashes( + &self, + _coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + ) -> Self::PreSigningOutput { + Compiler::preimage_hashes(input) + } + + #[inline] + fn compile( + &self, + _coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Self::SigningOutput { + Compiler::compile(input, signatures, public_keys) + } + + #[inline] + fn json_signer(&self) -> Option { + None + } + + #[inline] + fn message_signer(&self) -> Option { + None + } +} diff --git a/rust/tw_aptos/src/lib.rs b/rust/tw_aptos/src/lib.rs new file mode 100644 index 00000000000..b85498f42c2 --- /dev/null +++ b/rust/tw_aptos/src/lib.rs @@ -0,0 +1,20 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod address; +pub mod aptos_move_packages; +pub mod constants; +pub mod entry; +mod serde_helper; + +pub mod nft; + +pub mod compiler; +pub mod liquid_staking; +pub mod signer; +pub mod transaction; +pub mod transaction_builder; +pub mod transaction_payload; diff --git a/rust/tw_aptos/src/liquid_staking.rs b/rust/tw_aptos/src/liquid_staking.rs new file mode 100644 index 00000000000..2ac80692050 --- /dev/null +++ b/rust/tw_aptos/src/liquid_staking.rs @@ -0,0 +1,154 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::from_account_error; +use crate::transaction_payload::{EntryFunction, TransactionPayload}; +use move_core_types::{account_address::AccountAddress, ident_str, language_storage::ModuleId}; +use serde_json::json; +use std::str::FromStr; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_encoding::bcs; +use tw_proto::{ + Aptos::Proto::mod_LiquidStaking::OneOfliquid_stake_transaction_payload, + Aptos::Proto::{LiquidStaking, TortugaClaim, TortugaStake, TortugaUnstake}, +}; + +pub fn tortuga_stake( + smart_contract_address: AccountAddress, + amount: u64, +) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + smart_contract_address, + ident_str!("stake_router").to_owned(), + ), + ident_str!("stake").to_owned(), + vec![], + vec![bcs::encode(&amount)?], + json!([amount.to_string()]), + ))) +} + +pub fn tortuga_unstake( + smart_contract_address: AccountAddress, + amount: u64, +) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + smart_contract_address, + ident_str!("stake_router").to_owned(), + ), + ident_str!("unstake").to_owned(), + vec![], + vec![bcs::encode(&amount)?], + json!([amount.to_string()]), + ))) +} + +pub fn tortuga_claim( + smart_contract_address: AccountAddress, + idx: u64, +) -> SigningResult { + Ok(TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + smart_contract_address, + ident_str!("stake_router").to_owned(), + ), + ident_str!("claim").to_owned(), + vec![], + vec![bcs::encode(&idx)?], + json!([idx.to_string()]), + ))) +} + +pub struct Stake { + pub amount: u64, + pub smart_contract_address: AccountAddress, +} + +pub struct Unstake { + pub amount: u64, + pub smart_contract_address: AccountAddress, +} + +pub struct Claim { + pub idx: u64, + pub smart_contract_address: AccountAddress, +} + +pub enum LiquidStakingOperation { + Stake(Stake), + Unstake(Unstake), + Claim(Claim), +} + +impl TryFrom> for LiquidStakingOperation { + type Error = SigningError; + + fn try_from(value: LiquidStaking) -> SigningResult { + match value.liquid_stake_transaction_payload { + OneOfliquid_stake_transaction_payload::stake(stake_msg) => { + let smart_contract_address = + AccountAddress::from_str(&value.smart_contract_address) + .map_err(from_account_error)?; + Ok(LiquidStakingOperation::Stake(Stake { + amount: stake_msg.amount, + smart_contract_address, + })) + }, + OneOfliquid_stake_transaction_payload::unstake(unstake_msg) => { + let smart_contract_address = + AccountAddress::from_str(&value.smart_contract_address) + .map_err(from_account_error)?; + Ok(LiquidStakingOperation::Unstake(Unstake { + amount: unstake_msg.amount, + smart_contract_address, + })) + }, + OneOfliquid_stake_transaction_payload::claim(claim) => { + let smart_contract_address = + AccountAddress::from_str(&value.smart_contract_address) + .map_err(from_account_error)?; + Ok(LiquidStakingOperation::Claim(Claim { + idx: claim.idx, + smart_contract_address, + })) + }, + OneOfliquid_stake_transaction_payload::None => { + Err(SigningError(SigningErrorType::Error_invalid_params)) + }, + } + } +} + +impl From for LiquidStaking<'_> { + fn from(value: LiquidStakingOperation) -> Self { + match value { + LiquidStakingOperation::Stake(stake) => LiquidStaking { + smart_contract_address: stake.smart_contract_address.to_hex_literal().into(), + liquid_stake_transaction_payload: OneOfliquid_stake_transaction_payload::stake( + TortugaStake { + amount: stake.amount, + }, + ), + }, + LiquidStakingOperation::Unstake(unstake) => LiquidStaking { + smart_contract_address: unstake.smart_contract_address.to_hex_literal().into(), + liquid_stake_transaction_payload: OneOfliquid_stake_transaction_payload::unstake( + TortugaUnstake { + amount: unstake.amount, + }, + ), + }, + LiquidStakingOperation::Claim(claim) => LiquidStaking { + smart_contract_address: claim.smart_contract_address.to_hex_literal().into(), + liquid_stake_transaction_payload: OneOfliquid_stake_transaction_payload::claim( + TortugaClaim { idx: claim.idx }, + ), + }, + } + } +} diff --git a/rust/tw_aptos/src/nft.rs b/rust/tw_aptos/src/nft.rs new file mode 100644 index 00000000000..fe6f0375146 --- /dev/null +++ b/rust/tw_aptos/src/nft.rs @@ -0,0 +1,165 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::from_account_error; +use move_core_types::account_address::AccountAddress; +use std::str::FromStr; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_proto::Aptos::Proto::mod_NftMessage::OneOfnft_transaction_payload; +use tw_proto::Aptos::Proto::{CancelOfferNftMessage, ClaimNftMessage, NftMessage, OfferNftMessage}; + +pub struct Offer { + pub receiver: AccountAddress, + pub creator: AccountAddress, + pub collection: Vec, + pub name: Vec, + pub property_version: u64, + pub amount: u64, +} + +pub struct Claim { + pub sender: AccountAddress, + pub creator: AccountAddress, + pub collection: Vec, + pub name: Vec, + pub property_version: u64, +} + +pub enum NftOperation { + Claim(Claim), + Offer(Offer), + Cancel(Offer), +} + +impl TryFrom> for NftOperation { + type Error = SigningError; + + fn try_from(value: NftMessage) -> SigningResult { + match value.nft_transaction_payload { + OneOfnft_transaction_payload::offer_nft(msg) => { + Ok(NftOperation::Offer(Offer::try_from(msg)?)) + }, + OneOfnft_transaction_payload::cancel_offer_nft(msg) => { + Ok(NftOperation::Cancel(Offer::try_from(msg)?)) + }, + OneOfnft_transaction_payload::claim_nft(msg) => { + Ok(NftOperation::Claim(Claim::try_from(msg)?)) + }, + OneOfnft_transaction_payload::None => { + Err(SigningError(SigningErrorType::Error_invalid_params)) + }, + } + } +} + +impl From for NftMessage<'_> { + fn from(value: NftOperation) -> Self { + match value { + NftOperation::Claim(claim) => NftMessage { + nft_transaction_payload: OneOfnft_transaction_payload::claim_nft(claim.into()), + }, + NftOperation::Offer(offer) => NftMessage { + nft_transaction_payload: OneOfnft_transaction_payload::offer_nft(offer.into()), + }, + NftOperation::Cancel(cancel) => NftMessage { + nft_transaction_payload: OneOfnft_transaction_payload::cancel_offer_nft( + cancel.into(), + ), + }, + } + } +} + +impl TryFrom> for Offer { + type Error = SigningError; + + fn try_from(value: OfferNftMessage) -> SigningResult { + Ok(Offer { + receiver: AccountAddress::from_str(&value.receiver).map_err(from_account_error)?, + creator: AccountAddress::from_str(&value.creator).map_err(from_account_error)?, + collection: value.collectionName.as_bytes().to_vec(), + name: value.name.as_bytes().to_vec(), + property_version: value.property_version, + amount: value.amount, + }) + } +} + +impl From for OfferNftMessage<'_> { + fn from(value: Offer) -> Self { + OfferNftMessage { + receiver: value.receiver.to_hex_literal().into(), + creator: value.creator.to_hex_literal().into(), + collectionName: String::from_utf8_lossy(value.collection.as_slice()) + .to_string() + .into(), + name: String::from_utf8_lossy(&value.name).to_string().into(), + property_version: value.property_version, + amount: value.amount, + } + } +} + +impl TryFrom> for Offer { + type Error = SigningError; + + fn try_from(value: CancelOfferNftMessage) -> SigningResult { + Ok(Offer { + receiver: AccountAddress::from_str(&value.receiver).map_err(from_account_error)?, + creator: AccountAddress::from_str(&value.creator).map_err(from_account_error)?, + collection: value.collectionName.as_bytes().to_vec(), + name: value.name.as_bytes().to_vec(), + property_version: value.property_version, + amount: 0, + }) + } +} + +impl From for CancelOfferNftMessage<'_> { + fn from(value: Offer) -> Self { + CancelOfferNftMessage { + receiver: value.receiver.to_hex_literal().into(), + creator: value.creator.to_hex_literal().into(), + collectionName: String::from_utf8_lossy(value.collection.as_slice()) + .to_string() + .into(), + name: String::from_utf8_lossy(value.name.as_slice()) + .to_string() + .into(), + property_version: value.property_version, + } + } +} + +impl TryFrom> for Claim { + type Error = SigningError; + + fn try_from(value: ClaimNftMessage) -> SigningResult { + Ok(Claim { + sender: AccountAddress::from_str(&value.sender).map_err(from_account_error)?, + creator: AccountAddress::from_str(&value.creator).map_err(from_account_error)?, + collection: value.collectionName.as_bytes().to_vec(), + name: value.name.as_bytes().to_vec(), + property_version: value.property_version, + }) + } +} + +impl From for ClaimNftMessage<'_> { + fn from(value: Claim) -> Self { + ClaimNftMessage { + sender: value.sender.to_hex_literal().into(), + creator: value.creator.to_hex_literal().into(), + collectionName: String::from_utf8_lossy(value.collection.as_slice()) + .to_string() + .into(), + name: String::from_utf8_lossy(value.name.as_slice()) + .to_string() + .into(), + property_version: value.property_version, + } + } +} diff --git a/rust/tw_aptos/src/serde_helper/mod.rs b/rust/tw_aptos/src/serde_helper/mod.rs new file mode 100644 index 00000000000..876f018225e --- /dev/null +++ b/rust/tw_aptos/src/serde_helper/mod.rs @@ -0,0 +1,5 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +pub mod vec_bytes; diff --git a/rust/tw_aptos/src/serde_helper/vec_bytes.rs b/rust/tw_aptos/src/serde_helper/vec_bytes.rs new file mode 100644 index 00000000000..f57311d19dd --- /dev/null +++ b/rust/tw_aptos/src/serde_helper/vec_bytes.rs @@ -0,0 +1,30 @@ +// Copyright © Aptos Foundation +// Parts of the project are originally copyright © Meta Platforms, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use serde::{ + de::Deserializer, + ser::{SerializeSeq, Serializer}, + Deserialize, +}; + +pub fn serialize(data: &[Vec], serializer: S) -> Result +where + S: Serializer, +{ + let mut seq = serializer.serialize_seq(Some(data.len()))?; + for e in data { + seq.serialize_element(serde_bytes::Bytes::new(e.as_slice()))?; + } + seq.end() +} + +pub fn deserialize<'de, D>(deserializer: D) -> Result>, D::Error> +where + D: Deserializer<'de>, +{ + Ok(>::deserialize(deserializer)? + .into_iter() + .map(serde_bytes::ByteBuf::into_vec) + .collect()) +} diff --git a/rust/tw_aptos/src/signer.rs b/rust/tw_aptos/src/signer.rs new file mode 100644 index 00000000000..91431569c73 --- /dev/null +++ b/rust/tw_aptos/src/signer.rs @@ -0,0 +1,43 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::Address; +use crate::transaction_builder; +use std::str::FromStr; +use tw_coin_entry::error::SigningResult; +use tw_coin_entry::signing_output_error; +use tw_keypair::ed25519; +use tw_proto::Aptos::Proto; + +pub struct Signer; + +impl Signer { + #[inline] + pub fn sign_proto(input: Proto::SigningInput<'_>) -> Proto::SigningOutput<'static> { + Self::sign_proto_impl(input) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn sign_proto_impl( + input: Proto::SigningInput<'_>, + ) -> SigningResult> { + let key_pair = ed25519::sha512::KeyPair::try_from(input.private_key.as_ref())?; + let builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone())?; + let sender = Address::from_str(&input.sender)?; + let signed_tx = builder + .sender(sender.inner()) + .sequence_number(input.sequence_number as u64) + .build()? + .sign(key_pair)?; + Ok(Proto::SigningOutput { + raw_txn: signed_tx.raw_txn_bytes().clone().into(), + encoded: signed_tx.encoded().clone().into(), + authenticator: Some((*signed_tx.authenticator()).clone().into()), + json: signed_tx.to_json().to_string().into(), + ..Proto::SigningOutput::default() + }) + } +} diff --git a/rust/tw_aptos/src/transaction.rs b/rust/tw_aptos/src/transaction.rs new file mode 100644 index 00000000000..b48d7950bbc --- /dev/null +++ b/rust/tw_aptos/src/transaction.rs @@ -0,0 +1,220 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::constants::APTOS_SALT; +use crate::transaction_payload::TransactionPayload; +use move_core_types::account_address::AccountAddress; +use serde::Serialize; +use serde_json::{json, Value}; +use std::borrow::Cow; +use tw_coin_entry::error::SigningResult; +use tw_encoding::hex::encode; +use tw_encoding::{bcs, EncodingResult}; +use tw_keypair::ed25519::sha512::KeyPair; +use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; +use tw_memory::Data; +use tw_proto::Aptos::Proto; + +#[derive(Clone, Serialize)] +pub enum TransactionAuthenticator { + /// Single Ed25519 signature + Ed25519 { + public_key: Vec, + signature: Vec, + }, +} + +impl From for Proto::TransactionAuthenticator<'_> { + fn from(from: TransactionAuthenticator) -> Self { + Proto::TransactionAuthenticator { + signature: Cow::from(from.get_signature()), + public_key: Cow::from(from.get_public_key()), + } + } +} + +impl TransactionAuthenticator { + pub fn get_signature(&self) -> Vec { + match self { + TransactionAuthenticator::Ed25519 { + public_key: _public_key, + signature, + } => signature.clone(), + } + } + + pub fn get_public_key(&self) -> Vec { + match self { + TransactionAuthenticator::Ed25519 { + public_key, + signature: _signature, + } => public_key.clone(), + } + } + + pub fn to_json(&self) -> Value { + match self { + TransactionAuthenticator::Ed25519 { + public_key, + signature, + } => { + json!({"public_key": encode(public_key, true), + "signature": encode(signature, true), + "type": "ed25519_signature"}) + }, + } + } +} + +/// RawTransaction is the portion of a transaction that a client signs. +#[derive(Clone, Serialize)] +pub struct RawTransaction { + /// Sender's address. + sender: AccountAddress, + + /// Sequence number of this transaction. This must match the sequence number + /// stored in the sender's account at the time the transaction executes. + sequence_number: u64, + + /// The transaction payload, e.g., a script to execute. + payload: TransactionPayload, + + /// Maximal total gas to spend for this transaction. + max_gas_amount: u64, + + /// Price to be paid per gas unit. + gas_unit_price: u64, + + /// Expiration timestamp for this transaction, represented + /// as seconds from the Unix Epoch. If the current blockchain timestamp + /// is greater than or equal to this time, then the transaction has + /// expired and will be discarded. This can be set to a large value far + /// in the future to indicate that a transaction does not expire. + expiration_timestamp_secs: u64, + + /// Chain ID of the Aptos network this transaction is intended for. + chain_id: u8, +} + +impl RawTransaction { + /// Create a new `RawTransaction` with a payload. + /// + /// It can be either to publish a module, to execute a script, or to issue a writeset + /// transaction. + pub fn new( + sender: AccountAddress, + sequence_number: u64, + payload: TransactionPayload, + max_gas_amount: u64, + gas_unit_price: u64, + expiration_timestamp_secs: u64, + chain_id: u8, + ) -> Self { + RawTransaction { + sender, + sequence_number, + payload, + max_gas_amount, + gas_unit_price, + expiration_timestamp_secs, + chain_id, + } + } + + /// Create a new `RawTransaction` with an entry function + fn serialize(&self) -> EncodingResult { + bcs::encode(&self) + } + + fn msg_to_sign(&self) -> SigningResult { + let serialized = self.serialize()?; + let mut preimage = tw_hash::sha3::sha3_256(APTOS_SALT); + preimage.extend_from_slice(serialized.as_slice()); + Ok(preimage) + } + + pub fn pre_image(&self) -> SigningResult> { + self.msg_to_sign() + } + + pub fn compile( + &self, + signature: Vec, + public_key: Vec, + ) -> SigningResult { + let serialized = self.serialize()?; + let auth = TransactionAuthenticator::Ed25519 { + public_key, + signature, + }; + let mut encoded = serialized.clone(); + encoded.extend_from_slice(bcs::encode(&auth)?.as_slice()); + Ok(SignedTransaction { + raw_txn: self.clone(), + authenticator: auth, + raw_txn_bytes: serialized.to_vec(), + encoded, + }) + } + + pub fn sign(self, key_pair: KeyPair) -> SigningResult { + let to_sign = self.pre_image()?; + let signature = key_pair.private().sign(to_sign)?.to_bytes().into_vec(); + let pubkey = key_pair.public().as_slice().to_vec(); + self.compile(signature, pubkey) + } + + pub fn to_json(&self) -> Value { + json!({ + "expiration_timestamp_secs": self.expiration_timestamp_secs.to_string(), + "gas_unit_price": self.gas_unit_price.to_string(), + "max_gas_amount": self.max_gas_amount.to_string(), + "payload": self.payload.to_json(), + "sender": self.sender.to_hex_literal(), + "sequence_number": self.sequence_number.to_string() + }) + } +} + +/// A transaction that has been signed. +/// +/// A `SignedTransaction` is a single transaction that can be atomically executed. Clients submit +/// these to validator nodes, and the validator and executor submits these to the VM. +/// +#[derive(Clone, Serialize)] +pub struct SignedTransaction { + /// The raw transaction + raw_txn: RawTransaction, + + /// Public key and signature to authenticate + authenticator: TransactionAuthenticator, + + #[serde(skip_serializing)] + /// Raw txs bytes + raw_txn_bytes: Vec, + + #[serde(skip_serializing)] + /// Encoded bytes to be broadcast + encoded: Vec, +} + +impl SignedTransaction { + pub fn authenticator(&self) -> &TransactionAuthenticator { + &self.authenticator + } + pub fn raw_txn_bytes(&self) -> &Vec { + &self.raw_txn_bytes + } + pub fn encoded(&self) -> &Vec { + &self.encoded + } + + pub fn to_json(&self) -> Value { + let mut json_value = self.raw_txn.to_json(); + json_value["signature"] = self.authenticator.to_json(); + json_value + } +} diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs new file mode 100644 index 00000000000..53f72d32936 --- /dev/null +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -0,0 +1,263 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::from_account_error; +use crate::aptos_move_packages::{ + aptos_account_create_account, aptos_account_transfer, aptos_account_transfer_coins, + coin_transfer, managed_coin_register, token_transfers_cancel_offer_script, + token_transfers_claim_script, token_transfers_offer_script, +}; +use crate::constants::{GAS_UNIT_PRICE, MAX_GAS_AMOUNT}; +use crate::liquid_staking::{ + tortuga_claim, tortuga_stake, tortuga_unstake, LiquidStakingOperation, +}; +use crate::nft::NftOperation; +use crate::transaction::RawTransaction; +use crate::transaction_payload::{ + convert_proto_struct_tag_to_type_tag, EntryFunction, TransactionPayload, +}; +use move_core_types::account_address::AccountAddress; +use move_core_types::language_storage::TypeTag; +use serde_json::Value; +use std::str::FromStr; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_proto::Aptos::Proto::mod_SigningInput::OneOftransaction_payload; +use tw_proto::Aptos::Proto::SigningInput; + +pub struct TransactionBuilder { + sender: Option, + sequence_number: Option, + payload: TransactionPayload, + max_gas_amount: u64, + gas_unit_price: u64, + expiration_timestamp_secs: u64, + chain_id: u8, +} + +impl TransactionBuilder { + pub fn sender(mut self, sender: AccountAddress) -> Self { + self.sender = Some(sender); + self + } + + pub fn sequence_number(mut self, sequence_number: u64) -> Self { + self.sequence_number = Some(sequence_number); + self + } + + pub fn build(self) -> SigningResult { + let sender = self + .sender + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + let sequence_number = self + .sequence_number + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + Ok(RawTransaction::new( + sender, + sequence_number, + self.payload, + self.max_gas_amount, + self.gas_unit_price, + self.expiration_timestamp_secs, + self.chain_id, + )) + } +} + +#[derive(Clone, Debug)] +pub struct TransactionFactory { + max_gas_amount: u64, + gas_unit_price: u64, + transaction_expiration_time: u64, + chain_id: u8, +} + +impl TransactionFactory { + pub fn new(chain_id: u8) -> Self { + Self { + max_gas_amount: MAX_GAS_AMOUNT, + gas_unit_price: GAS_UNIT_PRICE, + transaction_expiration_time: 30, + chain_id, + } + } + + pub fn new_from_protobuf(input: SigningInput) -> SigningResult { + let factory = TransactionFactory::new(input.chain_id as u8) + .with_gas_unit_price(input.gas_unit_price) + .with_max_gas_amount(input.max_gas_amount) + .with_transaction_expiration_time(input.expiration_timestamp_secs); + match input.transaction_payload { + OneOftransaction_payload::transfer(transfer) => factory + .implicitly_create_user_account_and_transfer( + AccountAddress::from_str(&transfer.to).map_err(from_account_error)?, + transfer.amount, + ), + OneOftransaction_payload::token_transfer(token_transfer) => { + let func = token_transfer + .function + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + factory.coins_transfer( + AccountAddress::from_str(&token_transfer.to).map_err(from_account_error)?, + token_transfer.amount, + convert_proto_struct_tag_to_type_tag(func)?, + ) + }, + OneOftransaction_payload::create_account(create_account) => { + let address = AccountAddress::from_str(&create_account.auth_key) + .map_err(from_account_error)?; + factory.create_user_account(address) + }, + OneOftransaction_payload::nft_message(nft_message) => { + factory.nft_ops(NftOperation::try_from(nft_message)?) + }, + OneOftransaction_payload::register_token(register_token) => { + let function = register_token + .function + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + Ok(factory.register_token(convert_proto_struct_tag_to_type_tag(function)?)) + }, + OneOftransaction_payload::liquid_staking_message(msg) => { + factory.liquid_staking_ops(LiquidStakingOperation::try_from(msg)?) + }, + OneOftransaction_payload::token_transfer_coins(token_transfer_coins) => { + let func = token_transfer_coins + .function + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + factory.implicitly_create_user_and_coins_transfer( + AccountAddress::from_str(&token_transfer_coins.to) + .map_err(from_account_error)?, + token_transfer_coins.amount, + convert_proto_struct_tag_to_type_tag(func)?, + ) + }, + OneOftransaction_payload::None => { + let is_blind_sign = !input.any_encoded.is_empty(); + let v = serde_json::from_str::(&input.any_encoded)?; + if is_blind_sign { + let entry_function = EntryFunction::try_from(v)?; + Ok(factory.payload(TransactionPayload::EntryFunction(entry_function))) + } else { + Err(SigningError(SigningErrorType::Error_input_parse)) + } + }, + } + } + + pub fn with_max_gas_amount(mut self, max_gas_amount: u64) -> Self { + self.max_gas_amount = max_gas_amount; + self + } + + pub fn with_gas_unit_price(mut self, gas_unit_price: u64) -> Self { + self.gas_unit_price = gas_unit_price; + self + } + + pub fn with_transaction_expiration_time(mut self, transaction_expiration_time: u64) -> Self { + self.transaction_expiration_time = transaction_expiration_time; + self + } + + pub fn payload(&self, payload: TransactionPayload) -> TransactionBuilder { + self.transaction_builder(payload) + } + + pub fn create_user_account(&self, to: AccountAddress) -> SigningResult { + Ok(self.payload(aptos_account_create_account(to)?)) + } + + pub fn register_token(&self, coin_type: TypeTag) -> TransactionBuilder { + self.payload(managed_coin_register(coin_type)) + } + + pub fn nft_ops(&self, operation: NftOperation) -> SigningResult { + match operation { + NftOperation::Claim(claim) => Ok(self.payload(token_transfers_claim_script( + claim.sender, + claim.creator, + claim.collection, + claim.name, + claim.property_version, + )?)), + NftOperation::Cancel(offer) => Ok(self.payload(token_transfers_cancel_offer_script( + offer.receiver, + offer.creator, + offer.collection, + offer.name, + offer.property_version, + )?)), + NftOperation::Offer(offer) => Ok(self.payload(token_transfers_offer_script( + offer.receiver, + offer.creator, + offer.collection, + offer.name, + offer.property_version, + offer.amount, + )?)), + } + } + + pub fn liquid_staking_ops( + &self, + operation: LiquidStakingOperation, + ) -> SigningResult { + match operation { + LiquidStakingOperation::Stake(stake) => { + Ok(self.payload(tortuga_stake(stake.smart_contract_address, stake.amount)?)) + }, + LiquidStakingOperation::Unstake(unstake) => Ok(self.payload(tortuga_unstake( + unstake.smart_contract_address, + unstake.amount, + )?)), + LiquidStakingOperation::Claim(claim) => { + Ok(self.payload(tortuga_claim(claim.smart_contract_address, claim.idx)?)) + }, + } + } + + pub fn implicitly_create_user_account_and_transfer( + &self, + to: AccountAddress, + amount: u64, + ) -> SigningResult { + Ok(self.payload(aptos_account_transfer(to, amount)?)) + } + + pub fn coins_transfer( + &self, + to: AccountAddress, + amount: u64, + coin_type: TypeTag, + ) -> SigningResult { + Ok(self.payload(coin_transfer(coin_type, to, amount)?)) + } + + pub fn implicitly_create_user_and_coins_transfer( + &self, + to: AccountAddress, + amount: u64, + coin_type: TypeTag, + ) -> SigningResult { + Ok(self.payload(aptos_account_transfer_coins(coin_type, to, amount)?)) + } + + fn transaction_builder(&self, payload: TransactionPayload) -> TransactionBuilder { + TransactionBuilder { + sender: None, + sequence_number: None, + payload, + max_gas_amount: self.max_gas_amount, + gas_unit_price: self.gas_unit_price, + expiration_timestamp_secs: self.expiration_timestamp(), + chain_id: self.chain_id, + } + } + + fn expiration_timestamp(&self) -> u64 { + self.transaction_expiration_time + } +} diff --git a/rust/tw_aptos/src/transaction_payload.rs b/rust/tw_aptos/src/transaction_payload.rs new file mode 100644 index 00000000000..5e33e71d465 --- /dev/null +++ b/rust/tw_aptos/src/transaction_payload.rs @@ -0,0 +1,268 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::serde_helper::vec_bytes; +use move_core_types::identifier::Identifier; +use move_core_types::language_storage::{ModuleId, StructTag, TypeTag}; +use move_core_types::parser::parse_transaction_argument; +use move_core_types::transaction_argument::TransactionArgument; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; +use std::default::Default; +use std::str::FromStr; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_encoding::{bcs, EncodingError, EncodingResult}; +use tw_memory::Data; +use tw_proto::Aptos; + +pub type EntryFunctionResult = Result; + +#[derive(Debug)] +pub enum EntryFunctionError { + MissingFunctionName, + InvalidFunctionName, + MissingArguments, + InvalidArguments, + EncodingError, + MissingTypeArguments, + InvalidTypeArguments, +} + +impl From for EntryFunctionError { + fn from(_error: EncodingError) -> Self { + EntryFunctionError::EncodingError + } +} + +impl From for SigningError { + fn from(_: EntryFunctionError) -> Self { + SigningError(SigningErrorType::Error_invalid_params) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct EntryFunction { + module: ModuleId, + function: Identifier, + ty_args: Vec, + #[serde(with = "vec_bytes")] + args: Vec>, + #[serde(skip_serializing)] + json_args: Value, +} + +impl TryFrom for EntryFunction { + type Error = EntryFunctionError; + + fn try_from(value: Value) -> EntryFunctionResult { + let function_str = value["function"] + .as_str() + .ok_or(EntryFunctionError::MissingFunctionName)?; + let tag = StructTag::from_str(function_str) + .map_err(|_| EntryFunctionError::InvalidFunctionName)?; + + let args = value["arguments"] + .as_array() + .ok_or(EntryFunctionError::MissingArguments)? + .iter() + .map(|element| { + let arg_str = element.to_string(); + let arg = parse_transaction_argument( + arg_str.trim_start_matches('"').trim_end_matches('"'), + ) + .map_err(|_| EntryFunctionError::InvalidArguments)?; + serialize_argument(&arg).map_err(EntryFunctionError::from) + }) + .collect::>>()?; + + let ty_args = value["type_arguments"] + .as_array() + .ok_or(EntryFunctionError::MissingTypeArguments)? + .iter() + .map(|element| { + let ty_arg_str = element + .as_str() + .ok_or(EntryFunctionError::InvalidTypeArguments)?; + TypeTag::from_str(ty_arg_str).map_err(|_| EntryFunctionError::InvalidTypeArguments) + }) + .collect::>>()?; + + Ok(EntryFunction { + module: tag.module_id(), + function: tag.name, + ty_args, + args, + json_args: value["arguments"].clone(), + }) + } +} + +fn serialize_argument(arg: &TransactionArgument) -> EncodingResult { + 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) => { + let serialized_v = bcs::encode(v)?; + bcs::encode(&serialized_v) + }, + } +} + +pub fn convert_proto_struct_tag_to_type_tag( + struct_tag: Aptos::Proto::StructTag, +) -> SigningResult { + TypeTag::from_str(&format!( + "{}::{}::{}", + struct_tag.account_address, struct_tag.module, struct_tag.name + )) + .map_err(|_| SigningError(SigningErrorType::Error_invalid_params)) +} + +pub fn convert_type_tag_to_struct_tag(type_tag: TypeTag) -> Aptos::Proto::StructTag<'static> { + if let TypeTag::Struct(st) = type_tag { + Aptos::Proto::StructTag { + account_address: st.address.to_hex_literal().into(), + module: st.module.to_string().into(), + name: st.name.to_string().into(), + } + } else { + Aptos::Proto::StructTag::default() + } +} + +impl EntryFunction { + fn to_json(&self) -> Value { + // Create a JSON array from the `ty_args` field by filtering and mapping + // the items that match `TypeTag::Struct` to their string representation. + let type_arguments: Value = self + .ty_args + .iter() + .map(|item| Some(json!(item.to_string()))) + .collect(); + + // Construct the final JSON value + json!({ + "type": "entry_function_payload", + "function": format!("{}::{}", self.module.short_str_lossless(), self.function.clone().into_string()), + "arguments": self.json_args, + "type_arguments": type_arguments + }) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub enum TransactionPayload { + Script, + ModuleBundle, + /// A transaction that executes an existing entry function published on-chain. + EntryFunction(EntryFunction), +} + +impl TransactionPayload { + pub fn to_json(&self) -> Value { + match self { + TransactionPayload::Script => Value::default(), + TransactionPayload::ModuleBundle => Value::default(), + TransactionPayload::EntryFunction(entry) => entry.to_json(), + } + } +} + +impl EntryFunction { + pub fn new( + module: ModuleId, + function: Identifier, + ty_args: Vec, + args: Vec>, + json_args: Value, + ) -> Self { + EntryFunction { + module, + function, + ty_args, + args, + json_args, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::address::Address; + use move_core_types::account_address::AccountAddress; + use move_core_types::identifier::Identifier; + use move_core_types::language_storage::{ModuleId, TypeTag}; + use serde_json::{json, Value}; + use std::str::FromStr; + use tw_encoding::hex; + + #[test] + fn test_payload_from_json() { + let payload_value: Value = json!({ + "arguments": ["0xc95db29a67a848940829b3df6119b5e67b788ff0248676e4484c7c6f29c0f5e6"], + "function": "0xc23c3b70956ce8d88fb18ad9ed3b463fe873cb045db3f6d2e2fb15b9aab71d50::IFO::release", + "type": "entry_function_payload", + "type_arguments": [ + "0x48e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced90517::coins::BUSD", + "0x48e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced90517::coins::DAI", + "0x9936836587ca33240d3d3f91844651b16cb07802faf5e34514ed6f78580deb0a::uints::U1" + ] + }); + + let v = EntryFunction::try_from(payload_value.clone()).unwrap(); + assert_eq!(payload_value, v.to_json()); + } + + #[test] + fn test_payload_from_json_with_arg_non_str() { + let payload_value: Value = json!({ + "type":"entry_function_payload", + "function":"0xd11107bdf0d6d7040c6c0bfbdecb6545191fdf13e8d8d259952f53e1713f61b5::ditto_staking::stake_aptos", + "type_arguments":[], + "arguments": [1000000] + }); + + let v = EntryFunction::try_from(payload_value.clone()).unwrap(); + assert_eq!(payload_value, v.to_json()); + } + + #[test] + fn test_basic_payload() { + let addr = + Address::from_str("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b") + .unwrap() + .inner(); + let amount: i64 = 1000; + let args = vec![bcs::encode(&addr).unwrap(), bcs::encode(&amount).unwrap()]; + let module = ModuleId::new(AccountAddress::ONE, Identifier::from_str("coin").unwrap()); + let function = Identifier::from_str("transfer").unwrap(); + let type_tag = vec![TypeTag::from_str("0x1::aptos_coin::AptosCoin").unwrap()]; + let entry = EntryFunction::new( + module, + function, + type_tag, + args, + json!([addr.to_hex_literal(), amount.to_string()]), + ); + let tp = TransactionPayload::EntryFunction(entry); + let serialized = bcs::encode(&tp).unwrap(); + assert_eq!(hex::encode(serialized, false), "02000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b08e803000000000000"); + let payload_value: Value = json!({ + "function": "0x1::coin::transfer", + "type": "entry_function_payload", + "arguments": ["0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", "1000"], + "type_arguments": ["0x1::aptos_coin::AptosCoin"] + }); + assert_eq!(tp.to_json(), payload_value); + } +} diff --git a/rust/tw_aptos/tests/signer.rs b/rust/tw_aptos/tests/signer.rs new file mode 100644 index 00000000000..09f80e2e26a --- /dev/null +++ b/rust/tw_aptos/tests/signer.rs @@ -0,0 +1,873 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use move_core_types::account_address::AccountAddress; +use move_core_types::language_storage::TypeTag; +use std::str::FromStr; +use tw_aptos::liquid_staking; +use tw_aptos::liquid_staking::{LiquidStakingOperation, Stake, Unstake}; +use tw_aptos::nft::{Claim, NftOperation, Offer}; +use tw_aptos::signer::Signer; +use tw_aptos::transaction_payload::convert_type_tag_to_struct_tag; +use tw_coin_entry::error::SigningErrorType; +use tw_encoding::hex; +use tw_proto::Aptos::Proto; +use tw_proto::Aptos::Proto::{SigningInput, SigningOutput}; + +pub struct AccountCreation { + to: String, +} + +pub struct Transfer { + to: String, + amount: u64, +} + +pub struct TokenTransfer { + transfer: Transfer, + tag: TypeTag, +} + +pub struct RegisterToken { + coin_type: TypeTag, +} + +pub enum OpsDetails { + RegisterToken(RegisterToken), + LiquidStakingOps(LiquidStakingOperation), + AccountCreation(AccountCreation), + Transfer(Transfer), + TokenTransfer(TokenTransfer), + ImplicitTokenTransfer(TokenTransfer), + NftOps(NftOperation), +} + +fn setup_proto_transaction<'a>( + sender: &'a str, + keypair_str: &'a str, + transaction_type: &'a str, + sequence_number: i64, + chain_id: u32, + max_gas_amount: u64, + timestamp: u64, + gas_unit_price: u64, + any_encoded: &'a str, + ops_details: Option, +) -> SigningInput<'a> { + let private = hex::decode(keypair_str).unwrap(); + + let payload: Proto::mod_SigningInput::OneOftransaction_payload = match transaction_type { + "transfer" => { + if let OpsDetails::Transfer(transfer) = ops_details.unwrap() { + Proto::mod_SigningInput::OneOftransaction_payload::transfer( + Proto::TransferMessage { + to: transfer.to.into(), + amount: transfer.amount, + }, + ) + } else { + panic!("Unsupported arguments") + } + }, + "create_account" => { + if let OpsDetails::AccountCreation(account) = ops_details.unwrap() { + Proto::mod_SigningInput::OneOftransaction_payload::create_account( + Proto::CreateAccountMessage { + auth_key: account.to.into(), + }, + ) + } else { + panic!("Unsupported arguments") + } + }, + "coin_transfer" => { + if let OpsDetails::TokenTransfer(token_transfer) = ops_details.unwrap() { + Proto::mod_SigningInput::OneOftransaction_payload::token_transfer( + Proto::TokenTransferMessage { + to: token_transfer.transfer.to.into(), + amount: token_transfer.transfer.amount, + function: Some(convert_type_tag_to_struct_tag(token_transfer.tag)), + }, + ) + } else { + panic!("Unsupported arguments") + } + }, + "implicit_coin_transfer" => { + if let OpsDetails::ImplicitTokenTransfer(token_transfer) = ops_details.unwrap() { + Proto::mod_SigningInput::OneOftransaction_payload::token_transfer_coins( + Proto::TokenTransferCoinsMessage { + to: token_transfer.transfer.to.into(), + amount: token_transfer.transfer.amount, + function: Some(convert_type_tag_to_struct_tag(token_transfer.tag)), + }, + ) + } else { + panic!("Unsupported arguments") + } + }, + "nft_ops" => { + if let OpsDetails::NftOps(nft) = ops_details.unwrap() { + Proto::mod_SigningInput::OneOftransaction_payload::nft_message(nft.into()) + } else { + panic!("Unsupported arguments") + } + }, + "register_token" => { + if let OpsDetails::RegisterToken(register_token) = ops_details.unwrap() { + Proto::mod_SigningInput::OneOftransaction_payload::register_token( + Proto::ManagedTokensRegisterMessage { + function: Some(convert_type_tag_to_struct_tag(register_token.coin_type)), + }, + ) + } else { + panic!("Unsupported arguments") + } + }, + "liquid_staking_ops" => { + if let OpsDetails::LiquidStakingOps(liquid_staking_ops) = ops_details.unwrap() { + Proto::mod_SigningInput::OneOftransaction_payload::liquid_staking_message( + liquid_staking_ops.into(), + ) + } else { + panic!("Unsupported arguments") + } + }, + "blind_sign_json" => Proto::mod_SigningInput::OneOftransaction_payload::None, + _ => Proto::mod_SigningInput::OneOftransaction_payload::None, + }; + + let input = SigningInput { + chain_id, + sender: sender.into(), + sequence_number, + max_gas_amount, + gas_unit_price, + expiration_timestamp_secs: timestamp, + private_key: private.into(), + any_encoded: any_encoded.into(), + transaction_payload: payload, + }; + + input +} + +fn test_tx_result( + output: SigningOutput, + expected_raw_txn_bytes_str: &str, + expected_signature_str: &str, + expected_encoded_txn_str: &str, + json_literal: &str, +) { + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + assert_eq!( + hex::encode(output.raw_txn.to_vec(), false), + expected_raw_txn_bytes_str + ); + assert_eq!( + hex::encode(output.authenticator.unwrap().signature.to_vec(), false), + expected_signature_str + ); + assert_eq!( + hex::encode(output.encoded.to_vec(), false), + expected_encoded_txn_str + ); + + let json_value_expected: serde_json::Value = serde_json::from_str(json_literal).unwrap(); + let json_value: serde_json::Value = serde_json::from_str(output.json.as_ref()).unwrap(); + assert_eq!(json_value, json_value_expected); +} + +// Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb4d62afd3862116e060dd6ad9848ccb50c2bc177799819f1d29c059ae2042467?network=devnet +#[test] +fn test_aptos_sign_transaction_transfer() { + let input = setup_proto_transaction( + "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", + "transfer", + 99, + 33, + 3296766, + 3664390082, + 100, + "", + Some(OpsDetails::Transfer(Transfer { + to: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30".to_string(), + amount: 1000, + })), + ); + let output = Signer::sign_proto(input); + test_tx_result(output, + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021", + "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], + "function": "0x1::aptos_account::transfer", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "99", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", + "type": "ed25519_signature" + } + }"#); +} + +// Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x477141736de6b0936a6c3734e4d6fd018c7d21f1f28f99028ef0bc6881168602?network=Devnet +#[test] +fn test_aptos_sign_create_account() { + let input = setup_proto_transaction( + "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "create_account", + 0, // Sequence number + 33, + 3296766, + 3664390082, + 100, + "", + Some(OpsDetails::AccountCreation(AccountCreation { + to: "0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e".to_string(), + })), + ); + let output = Signer::sign_proto(input); + test_tx_result(output, + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada0000000021", // Expected raw transaction bytes + "fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", // Expected signature + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e"], + "function": "0x1::aptos_account::create_account", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "0", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xfcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", + "type": "ed25519_signature" + } + }"#); +} + +// Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb5b383a5c7f99b2edb3bed9533f8169a89051b149d65876a82f4c0b9bf78a15b?network=Devnet +#[test] +fn test_aptos_sign_coin_transfer() { + let input = setup_proto_transaction( + "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "coin_transfer", + 24, // Sequence number + 32, + 3296766, + 3664390082, + 100, + "", + Some(OpsDetails::TokenTransfer(TokenTransfer { + transfer: Transfer { + to: "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30" + .to_string(), + amount: 100000, + }, + tag: TypeTag::from_str( + "0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC", + ) + .unwrap(), + })), + ); + let output = Signer::sign_proto(input); + test_tx_result(output, + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada0000000020", // Expected raw transaction bytes + "7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", // Expected signature + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c407643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","100000"], + "function": "0x1::coin::transfer", + "type": "entry_function_payload", + "type_arguments": ["0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC"] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "24", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", + "type": "ed25519_signature" + } + }"#); +} + +// Successfully broadcasted https://explorer.aptoslabs.com/txn/0x197d40ea12e2bfc65a0a913b9f4ca3b0b0208fe0c1514d3d55cef3d5bcf25211?network=mainnet +#[test] +fn test_implicit_aptos_sign_coin_transfer() { + let input = setup_proto_transaction("0x1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf25", // Sender's address + "e7f56c77189e03699a75d8ec5c090e41f3d9d4783bc49c33df8a93d915e10de8", // Keypair + "implicit_coin_transfer", + 2, // Sequence number + 1, + 2000, + 3664390082, + 100, + "", + Some(OpsDetails::ImplicitTokenTransfer(TokenTransfer { transfer: Transfer { to: "0xb7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c".to_string(), amount: 10000 }, tag: TypeTag::from_str("0xe9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9::mee_coin::MeeCoin").unwrap() })), + ); + let output = Signer::sign_proto(input); + test_tx_result(output, + "1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001", // Expected raw transaction bytes + "30ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d", // Expected signature + "1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001002062e7a6a486553b56a53e89dfae3f780693e537e5b0a7ed33290780e581ca83694030ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "2000", + "payload": { + "arguments": ["0xb7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c","10000"], + "function": "0x1::aptos_account::transfer_coins", + "type": "entry_function_payload", + "type_arguments": ["0xe9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9::mee_coin::MeeCoin"] + }, + "sender": "0x1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf25", + "sequence_number": "2", + "signature": { + "public_key": "0x62e7a6a486553b56a53e89dfae3f780693e537e5b0a7ed33290780e581ca8369", + "signature": "0x30ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d", + "type": "ed25519_signature" + } + }"#); +} + +// Successfully broadcasted https://explorer.aptoslabs.com/txn/0x514e473618bd3cb89a2b110b7c473db9a2e10532f98eb42d02d86fb31c00525d?network=testnet +#[test] +fn test_aptos_nft_offer() { + let input = setup_proto_transaction( + "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", // Sender's address + "7bebb6d543d17f6fe4e685cfab239fa37896edd594ff859f1df32f244fb707e2", // Keypair + "nft_ops", + 1, // Sequence number + 2, + 3296766, + 3664390082, + 100, + "", + Some(OpsDetails::NftOps(NftOperation::Offer(Offer { + receiver: AccountAddress::from_str( + "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + ) + .unwrap(), + creator: AccountAddress::from_str( + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + ) + .unwrap(), + collection: "Topaz Troopers".as_bytes().to_vec(), + name: "Topaz Trooper #20068".as_bytes().to_vec(), + property_version: 0, + amount: 1, + }))), + ); + let output = Signer::sign_proto(input); + test_tx_result(output, + "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes + "af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", // Expected signature + "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada00000000020020d1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a411340af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": [ + "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + "Topaz Troopers", "Topaz Trooper #20068", "0", "1"], + "function": "0x3::token_transfers::offer_script", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", + "sequence_number": "1", + "signature": { + "public_key": "0xd1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a4113", + "signature": "0xaf5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", + "type": "ed25519_signature" + } + }"#); +} + +// Successfully broadcasted https://explorer.aptoslabs.com/txn/0x0b8c64e6847c368e4c6bd2cce0e9eab378971b0ef2e3bc40cbd292910a80201d?network=testnet +#[test] +fn test_aptos_cancel_nft_offer() { + let input = setup_proto_transaction( + "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "nft_ops", + 21, // Sequence number + 2, + 3296766, + 3664390082, + 100, + "", + Some(OpsDetails::NftOps(NftOperation::Cancel(Offer { + receiver: AccountAddress::from_str( + "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", + ) + .unwrap(), + creator: AccountAddress::from_str( + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + ) + .unwrap(), + collection: "Topaz Troopers".as_bytes().to_vec(), + name: "Topaz Trooper #20068".as_bytes().to_vec(), + property_version: 0, + amount: 0, + }))), + ); + let output = Signer::sign_proto(input); + test_tx_result(output, + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes + "826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", // Expected signature + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": [ + "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + "Topaz Troopers", "Topaz Trooper #20068", "0"], + "function": "0x3::token_transfers::cancel_offer_script", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "21", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", + "type": "ed25519_signature" + } + }"#); +} + +// Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x60b51e15140ec0b7650334e948fb447ce3cb13ae63492260461ebfa9d02e85c4?network=testnet +#[test] +fn test_aptos_nft_claim() { + let input = setup_proto_transaction( + "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "nft_ops", + 19, // Sequence number + 2, + 3296766, + 3664390082, + 100, + "", + Some(OpsDetails::NftOps(NftOperation::Claim(Claim { + sender: AccountAddress::from_str( + "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", + ) + .unwrap(), + creator: AccountAddress::from_str( + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + ) + .unwrap(), + collection: "Topaz Troopers".as_bytes().to_vec(), + name: "Topaz Trooper #20068".as_bytes().to_vec(), + property_version: 0, + }))), + ); + let output = Signer::sign_proto(input); + test_tx_result(output, + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes + "ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", // Expected signature + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": [ + "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + "Topaz Troopers", "Topaz Trooper #20068", "0"], + "function": "0x3::token_transfers::claim_script", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "19", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", + "type": "ed25519_signature" + } + }"#); +} + +// Successfully broadcasted https://explorer.aptoslabs.com/txn/0xe591252daed785641bfbbcf72a5d17864568cf32e04c0cc9129f3a13834d0e8e?network=testnet +#[test] +fn test_aptos_register_token() { + let input = setup_proto_transaction("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "register_token", + 23, // Sequence number + 2, + 2000000, + 3664390082, + 100, + "", + Some(OpsDetails::RegisterToken(RegisterToken { coin_type: TypeTag::from_str("0xe4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379::move_coin::MoveCoin").unwrap() })), + ); + let output = Signer::sign_proto(input); + test_tx_result(output, + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada0000000002", // Expected raw transaction bytes + "e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", // Expected signature + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "2000000", + "payload": { + "arguments": [], + "function": "0x1::managed_coin::register", + "type": "entry_function_payload", + "type_arguments": ["0xe4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379::move_coin::MoveCoin"] + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "23", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xe230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", + "type": "ed25519_signature" + } + }"#); +} + +// Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x25dca849cb4ebacbff223139f7ad5d24c37c225d9506b8b12a925de70429e685/userTxnOverview?network=mainnet +#[test] +fn test_aptos_tortuga_stake() { + let input = setup_proto_transaction( + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address + "786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05", // Keypair + "liquid_staking_ops", + 19, // Sequence number + 1, + 5554, + 1670240203, + 100, + "", + Some(OpsDetails::LiquidStakingOps(LiquidStakingOperation::Stake( + Stake { + amount: 100000000, + smart_contract_address: AccountAddress::from_str( + "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f", + ) + .unwrap(), + }, + ))), + ); + let output = Signer::sign_proto(input); + test_tx_result(output, + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1300000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000b2150000000000006400000000000000cbd78d630000000001", // Expected raw transaction bytes + "22d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d", // Expected signature + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1300000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000b2150000000000006400000000000000cbd78d630000000001002089e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc44022d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d", // Expected encoded transaction + r#"{ + "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + "sequence_number": "19", + "max_gas_amount": "5554", + "gas_unit_price": "100", + "expiration_timestamp_secs": "1670240203", + "payload": { + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::stake", + "type_arguments": [], + "arguments": [ + "100000000" + ], + "type": "entry_function_payload" + }, + "signature": { + "public_key": "0x89e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4", + "signature": "0x22d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d", + "type": "ed25519_signature" + } + }"#); +} + +// Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x92edb4f756fe86118e34a0e64746c70260ee02c2ae2cf402b3e39f6a282ce968?network=mainnet +#[test] +fn test_aptos_tortuga_unstake() { + let input = setup_proto_transaction( + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address + "786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05", // Keypair + "liquid_staking_ops", + 20, // Sequence number + 1, + 2371, + 1670304949, + 120, + "", + Some(OpsDetails::LiquidStakingOps( + LiquidStakingOperation::Unstake(Unstake { + amount: 99178100, + smart_contract_address: AccountAddress::from_str( + "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f", + ) + .unwrap(), + }), + )), + ); + let output = Signer::sign_proto(input); + test_tx_result(output, + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1400000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e9050000000043090000000000007800000000000000b5d48e630000000001", // Expected raw transaction bytes + "6994b917432ad70ae84d2ce1484e6aece589a68aad1b7c6e38c9697f2a012a083a3a755c5e010fd3d0f149a75dd8d257acbd09f10800e890074e5ad384314d0c", // Expected signature + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1400000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e9050000000043090000000000007800000000000000b5d48e630000000001002089e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4406994b917432ad70ae84d2ce1484e6aece589a68aad1b7c6e38c9697f2a012a083a3a755c5e010fd3d0f149a75dd8d257acbd09f10800e890074e5ad384314d0c", // Expected encoded transaction + r#"{ + "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + "sequence_number": "20", + "max_gas_amount": "2371", + "gas_unit_price": "120", + "expiration_timestamp_secs": "1670304949", + "payload": { + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::unstake", + "type_arguments": [], + "arguments": [ + "99178100" + ], + "type": "entry_function_payload" + }, + "signature": { + "public_key": "0x89e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4", + "signature": "0x6994b917432ad70ae84d2ce1484e6aece589a68aad1b7c6e38c9697f2a012a083a3a755c5e010fd3d0f149a75dd8d257acbd09f10800e890074e5ad384314d0c", + "type": "ed25519_signature" + } + }"#); +} + +// // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x9fc874de7a7d3e813d9a1658d896023de270a0096a5e258c196005656ace7d54?network=mainnet +#[test] +fn test_aptos_tortuga_claim() { + let input = setup_proto_transaction( + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address + "786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05", // Keypair + "liquid_staking_ops", + 28, // Sequence number + 1, + 10, + 1682066783, + 148, + "", + Some(OpsDetails::LiquidStakingOps(LiquidStakingOperation::Claim( + liquid_staking::Claim { + idx: 0, + smart_contract_address: AccountAddress::from_str( + "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f", + ) + .unwrap(), + }, + ))), + ); + let output = Signer::sign_proto(input); + test_tx_result(output, + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657205636c61696d00010800000000000000000a0000000000000094000000000000005f4d42640000000001", // Expected raw transaction bytes + "c936584f89777e1fe2d5dd75cd8d9c514efc445810ba22f462b6fe7229c6ec7fc1c8b25d3e233eafaa8306433b3220235e563498ba647be38cac87ff618e3d03", // Expected signature + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657205636c61696d00010800000000000000000a0000000000000094000000000000005f4d42640000000001002089e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc440c936584f89777e1fe2d5dd75cd8d9c514efc445810ba22f462b6fe7229c6ec7fc1c8b25d3e233eafaa8306433b3220235e563498ba647be38cac87ff618e3d03", // Expected encoded transaction + r#"{ + "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + "sequence_number": "28", + "max_gas_amount": "10", + "gas_unit_price": "148", + "expiration_timestamp_secs": "1682066783", + "payload": { + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::claim", + "type_arguments": [], + "arguments": [ + "0" + ], + "type": "entry_function_payload" + }, + "signature": { + "public_key": "0x89e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4", + "signature": "0xc936584f89777e1fe2d5dd75cd8d9c514efc445810ba22f462b6fe7229c6ec7fc1c8b25d3e233eafaa8306433b3220235e563498ba647be38cac87ff618e3d03", + "type": "ed25519_signature" + } + }"#); +} + +// Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x7efd69e7f9462774b932ce500ab51c0d0dcc004cf272e09f8ffd5804c2a84e33?network=mainnet +#[test] +fn test_aptos_blind_sign() { + let input = setup_proto_transaction( + "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "blind_sign_json", + 42, // Sequence number + 1, + 100011, + 3664390082, + 100, + r#"{ + "function": "0x16fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c::AnimeSwapPoolV1::swap_exact_coins_for_coins_3_pair_entry", + "type_arguments": [ + "0x1::aptos_coin::AptosCoin", + "0x881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f4::coin::MOJO", + "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDT", + "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC" + ], + "arguments": [ + "1000000", + "49329" + ], + "type": "entry_function_payload" + }"#, + None, + ); + let output = Signer::sign_proto(input); + test_tx_result(output, + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f302a000000000000000216fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c0f416e696d6553776170506f6f6c563127737761705f65786163745f636f696e735f666f725f636f696e735f335f706169725f656e747279040700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e0007881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f404636f696e044d4f4a4f0007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa05617373657404555344540007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa056173736574045553444300020840420f000000000008b1c0000000000000ab860100000000006400000000000000c2276ada0000000001", // Expected raw transaction bytes + "42cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b", // Expected signature + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f302a000000000000000216fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c0f416e696d6553776170506f6f6c563127737761705f65786163745f636f696e735f666f725f636f696e735f335f706169725f656e747279040700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e0007881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f404636f696e044d4f4a4f0007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa05617373657404555344540007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa056173736574045553444300020840420f000000000008b1c0000000000000ab860100000000006400000000000000c2276ada00000000010020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c4042cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "100011", + "payload": { + "function": "0x16fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c::AnimeSwapPoolV1::swap_exact_coins_for_coins_3_pair_entry", + "type_arguments": [ + "0x1::aptos_coin::AptosCoin", + "0x881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f4::coin::MOJO", + "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDT", + "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC" + ], + "arguments": [ + "1000000", + "49329" + ], + "type": "entry_function_payload" + }, + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "42", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x42cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b", + "type": "ed25519_signature" + } + }"#); +} + +// Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x25dca849cb4ebacbff223139f7ad5d24c37c225d9506b8b12a925de70429e685/payload +#[test] +fn test_aptos_blind_sign_staking() { + let input = setup_proto_transaction( + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "blind_sign_json", + 43, // Sequence number + 1, + 100011, + 3664390082, + 100, + r#"{ + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::stake", + "type_arguments": [], + "arguments": [ + "100000000" + ], + "type": "entry_function_payload" + }"#, + None, + ); + let output = Signer::sign_proto(input); + test_tx_result(output, + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2b00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000ab860100000000006400000000000000c2276ada0000000001", // Expected raw transaction bytes + "a41b7440a50f36e8491319508734acb55488abc6d88fbc9cb2b37ba23210f01f5d08c856cb7abf18c414cf9302ee144450bd99495a7e21e61f624764db91eb0b", // Expected signature + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2b00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000ab860100000000006400000000000000c2276ada00000000010020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40a41b7440a50f36e8491319508734acb55488abc6d88fbc9cb2b37ba23210f01f5d08c856cb7abf18c414cf9302ee144450bd99495a7e21e61f624764db91eb0b", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "100011", + "payload": { + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::stake", + "type_arguments": [], + "arguments": [ + "100000000" + ], + "type": "entry_function_payload" + }, + "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + "sequence_number": "43", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xa41b7440a50f36e8491319508734acb55488abc6d88fbc9cb2b37ba23210f01f5d08c856cb7abf18c414cf9302ee144450bd99495a7e21e61f624764db91eb0b", + "type": "ed25519_signature" + } + }"#); +} + +// Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x92edb4f756fe86118e34a0e64746c70260ee02c2ae2cf402b3e39f6a282ce968/payload +#[test] +fn test_aptos_blind_sign_unstaking() { + let input = setup_proto_transaction( + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", // Sender's address + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec", // Keypair + "blind_sign_json", + 44, // Sequence number + 1, + 100011, + 3664390082, + 100, + r#"{ + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::unstake", + "type_arguments": [], + "arguments": [ + "99178100" + ], + "type": "entry_function_payload" + }"#, + None, + ); + let output = Signer::sign_proto(input); + test_tx_result(output, + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e90500000000ab860100000000006400000000000000c2276ada0000000001", // Expected raw transaction bytes + "a58ad5e3331beb8c0212a18a1f932207cb664b78f5aad3cb1fe7435e0e0e053247ce49b38fd67b064bed34ed643eb6a03165d77c681d7d73ac3161ab984a960a", // Expected signature + "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e90500000000ab860100000000006400000000000000c2276ada00000000010020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40a58ad5e3331beb8c0212a18a1f932207cb664b78f5aad3cb1fe7435e0e0e053247ce49b38fd67b064bed34ed643eb6a03165d77c681d7d73ac3161ab984a960a", // Expected encoded transaction + r#"{ + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "100011", + "payload": { + "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::unstake", + "type_arguments": [], + "arguments": [ + "99178100" + ], + "type": "entry_function_payload" + }, + "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + "sequence_number": "44", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xa58ad5e3331beb8c0212a18a1f932207cb664b78f5aad3cb1fe7435e0e0e053247ce49b38fd67b064bed34ed643eb6a03165d77c681d7d73ac3161ab984a960a", + "type": "ed25519_signature" + } + }"#); +} diff --git a/rust/tw_coin_entry/Cargo.toml b/rust/tw_coin_entry/Cargo.toml index 8b55efc5c0e..41ab8b48576 100644 --- a/rust/tw_coin_entry/Cargo.toml +++ b/rust/tw_coin_entry/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] serde_json = "1.0.95" +tw_encoding = { path = "../tw_encoding" } tw_keypair = { path = "../tw_keypair" } tw_memory = { path = "../tw_memory" } tw_misc = { path = "../tw_misc" } diff --git a/rust/tw_coin_entry/src/error.rs b/rust/tw_coin_entry/src/error.rs index c31b495576b..0e39825ce3b 100644 --- a/rust/tw_coin_entry/src/error.rs +++ b/rust/tw_coin_entry/src/error.rs @@ -6,6 +6,7 @@ use std::fmt; use std::fmt::Formatter; +use tw_encoding::EncodingError; use tw_keypair::KeyPairError; use tw_number::NumberError; use tw_proto::Common::Proto; @@ -26,7 +27,7 @@ macro_rules! signing_output_error { pub type AddressResult = Result; -#[derive(Debug)] +#[derive(Debug, Eq, PartialEq)] pub enum AddressError { UnknownCoinType, MissingPrefix, @@ -57,6 +58,18 @@ impl From for SigningError { } } +impl From for SigningError { + fn from(_value: serde_json::Error) -> Self { + SigningError(SigningErrorType::Error_input_parse) + } +} + +impl From for SigningError { + fn from(_e: EncodingError) -> Self { + SigningError(SigningErrorType::Error_input_parse) + } +} + impl From for SigningError { fn from(err: KeyPairError) -> Self { match err { diff --git a/rust/tw_coin_registry/Cargo.toml b/rust/tw_coin_registry/Cargo.toml index 4ad4e6d87de..b45a573c5a1 100644 --- a/rust/tw_coin_registry/Cargo.toml +++ b/rust/tw_coin_registry/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" lazy_static = "1.4.0" serde = { version = "1.0.163", features = ["derive"] } serde_json = "1.0.96" +tw_aptos = { path = "../tw_aptos" } tw_bitcoin = { path = "../tw_bitcoin" } tw_coin_entry = { path = "../tw_coin_entry" } tw_ethereum = { path = "../tw_ethereum" } diff --git a/rust/tw_coin_registry/src/blockchain_type.rs b/rust/tw_coin_registry/src/blockchain_type.rs index 72a4141d1ea..2139a373eb3 100644 --- a/rust/tw_coin_registry/src/blockchain_type.rs +++ b/rust/tw_coin_registry/src/blockchain_type.rs @@ -13,6 +13,7 @@ use std::str::FromStr; /// Extend this enum when adding new blockchains. #[derive(Copy, Clone, Debug)] pub enum BlockchainType { + Aptos, Bitcoin, Ethereum, InternetComputer, @@ -35,6 +36,7 @@ impl FromStr for BlockchainType { fn from_str(s: &str) -> Result { match s { + "Aptos" => Ok(BlockchainType::Aptos), "Bitcoin" => Ok(BlockchainType::Bitcoin), "Ethereum" => Ok(BlockchainType::Ethereum), "InternetComputer" => Ok(BlockchainType::InternetComputer), diff --git a/rust/tw_coin_registry/src/dispatcher.rs b/rust/tw_coin_registry/src/dispatcher.rs index e027054e443..68970653371 100644 --- a/rust/tw_coin_registry/src/dispatcher.rs +++ b/rust/tw_coin_registry/src/dispatcher.rs @@ -9,6 +9,7 @@ use crate::coin_context::CoinRegistryContext; use crate::coin_type::CoinType; use crate::error::{RegistryError, RegistryResult}; use crate::registry::get_coin_item; +use tw_aptos::entry::AptosEntry; use tw_bitcoin::entry::BitcoinEntry; use tw_coin_entry::coin_entry_ext::CoinEntryExt; use tw_ethereum::entry::EthereumEntry; @@ -19,6 +20,7 @@ use tw_ronin::entry::RoninEntry; pub type CoinEntryExtStaticRef = &'static dyn CoinEntryExt; pub type EvmEntryExtStaticRef = &'static dyn EvmEntryExt; +const APTOS: AptosEntry = AptosEntry; const BITCOIN: BitcoinEntry = BitcoinEntry; const ETHEREUM: EthereumEntry = EthereumEntry; const INTERNET_COMPUTER: InternetComputerEntry = InternetComputerEntry; @@ -26,6 +28,7 @@ const RONIN: RoninEntry = RoninEntry; pub fn blockchain_dispatcher(blockchain: BlockchainType) -> RegistryResult { match blockchain { + BlockchainType::Aptos => Ok(&APTOS), BlockchainType::Bitcoin => Ok(&BITCOIN), BlockchainType::Ethereum => Ok(ÐEREUM), BlockchainType::InternetComputer => Ok(&INTERNET_COMPUTER), @@ -46,6 +49,7 @@ pub fn coin_dispatcher( pub fn evm_dispatcher(coin: CoinType) -> RegistryResult { let item = get_coin_item(coin)?; match item.blockchain { + BlockchainType::Aptos => Err(RegistryError::Unsupported), BlockchainType::Bitcoin => Err(RegistryError::Unsupported), BlockchainType::Ethereum => Ok(ÐEREUM), BlockchainType::InternetComputer => Err(RegistryError::Unsupported), diff --git a/rust/tw_encoding/Cargo.toml b/rust/tw_encoding/Cargo.toml index 2211e12bde9..4a1b666a551 100644 --- a/rust/tw_encoding/Cargo.toml +++ b/rust/tw_encoding/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] arbitrary = { version = "1", features = ["derive"], optional = true } +bcs = "0.1.6" bs58 = "0.4.0" ciborium = "0.2.1" data-encoding = "2.3.3" diff --git a/rust/tw_encoding/src/bcs.rs b/rust/tw_encoding/src/bcs.rs new file mode 100644 index 00000000000..8051ce4ac03 --- /dev/null +++ b/rust/tw_encoding/src/bcs.rs @@ -0,0 +1,16 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::{EncodingError, EncodingResult}; +use serde::Serialize; +use tw_memory::Data; + +pub fn encode(value: &T) -> EncodingResult +where + T: ?Sized + Serialize, +{ + bcs::to_bytes(value).map_err(|_| EncodingError::InvalidInput) +} diff --git a/rust/tw_encoding/src/lib.rs b/rust/tw_encoding/src/lib.rs index 3250e9ed4da..f658d9b019f 100644 --- a/rust/tw_encoding/src/lib.rs +++ b/rust/tw_encoding/src/lib.rs @@ -7,6 +7,7 @@ pub mod base32; pub mod base58; pub mod base64; +pub mod bcs; pub mod cbor; pub mod ffi; pub mod hex; diff --git a/rust/tw_evm/fuzz/Cargo.toml b/rust/tw_evm/fuzz/Cargo.toml index 6fad417a7d8..e1ac2e6c65c 100644 --- a/rust/tw_evm/fuzz/Cargo.toml +++ b/rust/tw_evm/fuzz/Cargo.toml @@ -9,7 +9,7 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = { version = "0.4", features = ["arbitrary-derive"] } -serde_json = "1.0.95" +serde_json = "1.0.96" tw_number = { path = "../../tw_number" } tw_proto = { path = "../../tw_proto", features = ["fuzz"] } diff --git a/rust/tw_keypair/src/tw/public.rs b/rust/tw_keypair/src/tw/public.rs index e8a31f0fc20..8b527050944 100644 --- a/rust/tw_keypair/src/tw/public.rs +++ b/rust/tw_keypair/src/tw/public.rs @@ -135,4 +135,11 @@ impl PublicKey { _ => None, } } + + pub fn to_ed25519(&self) -> Option<&ed25519::sha512::PublicKey> { + match self { + PublicKey::Ed25519(ed25519) => Some(ed25519), + _ => None, + } + } } diff --git a/rust/tw_misc/Cargo.toml b/rust/tw_misc/Cargo.toml index ac5867c20dc..60fe9a1033d 100644 --- a/rust/tw_misc/Cargo.toml +++ b/rust/tw_misc/Cargo.toml @@ -3,5 +3,10 @@ name = "tw_misc" version = "0.1.0" edition = "2021" +[features] +test-utils = ["serde", "serde_json"] + [dependencies] +serde = { version = "1.0.163", features = ["derive"], optional = true } +serde_json = { version = "1.0.96", optional = true } zeroize = "1.6.0" diff --git a/rust/tw_misc/src/lib.rs b/rust/tw_misc/src/lib.rs index 1f73752d7a1..314dedda6dc 100644 --- a/rust/tw_misc/src/lib.rs +++ b/rust/tw_misc/src/lib.rs @@ -1,2 +1,4 @@ pub mod macros; +#[cfg(feature = "test-utils")] +pub mod test_utils; pub mod traits; diff --git a/rust/tw_misc/src/test_utils/json.rs b/rust/tw_misc/src/test_utils/json.rs new file mode 100644 index 00000000000..bcab8c3f6d3 --- /dev/null +++ b/rust/tw_misc/src/test_utils/json.rs @@ -0,0 +1,44 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde_json::Value as Json; +use std::borrow::Cow; + +pub trait ToJson { + fn to_json(&self) -> Json; +} + +impl ToJson for Json { + #[track_caller] + fn to_json(&self) -> Json { + self.clone() + } +} + +impl<'a> ToJson for Cow<'a, str> { + #[track_caller] + fn to_json(&self) -> Json { + self.as_ref().to_json() + } +} + +impl<'a> ToJson for &'a str { + #[track_caller] + fn to_json(&self) -> Json { + serde_json::from_str(self).expect("Error on deserializing JSON from string") + } +} + +#[macro_export] +macro_rules! assert_eq_json { + ($left:expr, $right:expr) => {{ + use $crate::test_utils::json::ToJson; + + let left = $left.to_json(); + let right = $right.to_json(); + assert_eq!(left, right); + }}; +} diff --git a/rust/tw_misc/src/test_utils/mod.rs b/rust/tw_misc/src/test_utils/mod.rs new file mode 100644 index 00000000000..019606a0fca --- /dev/null +++ b/rust/tw_misc/src/test_utils/mod.rs @@ -0,0 +1,7 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod json; diff --git a/rust/tw_move_parser/Cargo.toml b/rust/tw_move_parser/Cargo.toml deleted file mode 100644 index b674b9a8908..00000000000 --- a/rust/tw_move_parser/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "tw_move_parser" -version = "0.1.0" -edition = "2021" - -[dependencies] -bcs = "0.1.4" -hex = "0.4.3" -move-core-types = { git = "https://github.com/move-language/move", rev = "f7137eabc2046f76fdad3ded2c51e03a3b1fbd01", features = ["address32"] } -tw_memory = { path = "../tw_memory" } diff --git a/rust/tw_move_parser/src/ffi.rs b/rust/tw_move_parser/src/ffi.rs deleted file mode 100644 index 7552e37f457..00000000000 --- a/rust/tw_move_parser/src/ffi.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#![allow(clippy::missing_safety_doc)] - -use move_core_types::language_storage::TypeTag; -use move_core_types::transaction_argument::TransactionArgument; -use move_core_types::*; -use std::{ffi::c_char, ffi::CStr}; -use tw_memory::ffi::c_result::{CStrResult, ErrorCode}; - -#[repr(C)] -pub enum CMoveParserCode { - Ok = 0, - InvalidInput = 1, - ErrorParsing = 2, -} - -impl From for ErrorCode { - fn from(code: CMoveParserCode) -> Self { - code as ErrorCode - } -} - -#[repr(C)] -#[derive(PartialEq, Debug)] -pub enum ETypeTag { - Bool = 1, - U8 = 2, - U64 = 3, - U128 = 4, - Address = 5, - Signer = 6, - Vector = 7, - Struct = 8, - Error = 9, -} - -/// Parses a Move type tag. -/// \param input *non-null* C-compatible, nul-terminated string. -/// \return `ETypeTag` enumeration. -#[no_mangle] -pub unsafe extern "C" fn parse_type_tag(input: *const c_char) -> ETypeTag { - let s = CStr::from_ptr(input).to_str().unwrap(); - let transaction_argument = match parser::parse_type_tag(s) { - Ok(v) => v, - Err(_) => return ETypeTag::Error, - }; - match transaction_argument { - TypeTag::Bool => ETypeTag::Bool, - TypeTag::U8 => ETypeTag::U8, - TypeTag::U64 => ETypeTag::U64, - TypeTag::U128 => ETypeTag::U128, - TypeTag::Address => ETypeTag::Address, - TypeTag::Signer => ETypeTag::Signer, - TypeTag::Vector(_) => ETypeTag::Vector, - TypeTag::Struct(_) => ETypeTag::Struct, - } -} - -/// Parses `input` as a Move function argument. -/// \param input *non-null* C-compatible, nul-terminated string. -/// \return *non-null* C-compatible, nul-terminated string, Binary Canonical Serialization (BCS). -#[no_mangle] -pub unsafe extern "C" fn parse_function_argument_to_bcs(input: *const c_char) -> CStrResult { - let s = match CStr::from_ptr(input).to_str() { - Ok(input) => input, - Err(_) => return CStrResult::error(CMoveParserCode::InvalidInput), - }; - let transaction_argument = match parser::parse_transaction_argument(s) { - Ok(v) => v, - Err(_) => return CStrResult::error(CMoveParserCode::ErrorParsing), - }; - let v = match transaction_argument { - TransactionArgument::U8(v) => hex::encode(bcs::to_bytes(&v).unwrap()), - TransactionArgument::U64(v) => hex::encode(bcs::to_bytes(&v).unwrap()), - TransactionArgument::U128(v) => hex::encode(bcs::to_bytes(&v).unwrap()), - TransactionArgument::Address(v) => { - hex::encode(bcs::to_bytes(&bcs::to_bytes(&v).unwrap()).unwrap()) - }, - TransactionArgument::U8Vector(v) => hex::encode(bcs::to_bytes(&v).unwrap()), - TransactionArgument::Bool(v) => hex::encode(bcs::to_bytes(&v).unwrap()), - }; - CStrResult::ok(tw_memory::c_string_standalone(v)) -} diff --git a/rust/tw_move_parser/tests/move_parser_ffi_tests.rs b/rust/tw_move_parser/tests/move_parser_ffi_tests.rs deleted file mode 100644 index 3cb0d528f5e..00000000000 --- a/rust/tw_move_parser/tests/move_parser_ffi_tests.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -use std::ffi::{c_char, CString}; -use tw_move_parser::ffi::{parse_function_argument_to_bcs, parse_type_tag, ETypeTag}; - -#[test] -fn tests_type_tag() { - let tag = unsafe { parse_type_tag("0x1::aptos_coin::AptosCoin\0".as_ptr() as *const c_char) }; - assert_eq!(tag, ETypeTag::Struct); -} - -#[test] -fn tests_function_argument_to_bcs() { - let str = unsafe { - let input = "10000000\0".as_ptr() as *const c_char; - CString::from_raw(parse_function_argument_to_bcs(input).unwrap() as *mut c_char) - .into_string() - .unwrap() - }; - assert_eq!(str, "8096980000000000"); - - let str = unsafe { - let input = "5047445908\0".as_ptr() as *const c_char; - CString::from_raw(parse_function_argument_to_bcs(input).unwrap() as *mut c_char) - .into_string() - .unwrap() - }; - assert_eq!(str, "94e9d92c01000000"); -} - -#[test] -fn tests_function_argument_to_bcs_another() { - let str = unsafe { - let input = "0xc95db29a67a848940829b3df6119b5e67b788ff0248676e4484c7c6f29c0f5e6\0".as_ptr() - as *const c_char; - CString::from_raw(parse_function_argument_to_bcs(input).unwrap() as *mut c_char) - .into_string() - .unwrap() - }; - let decoded = hex::decode(str).unwrap(); - let v = vec![decoded]; - let actual = hex::encode(bcs::to_bytes(&v).unwrap()); - let expected = "012120c95db29a67a848940829b3df6119b5e67b788ff0248676e4484c7c6f29c0f5e6"; - assert_eq!(actual, expected); -} diff --git a/rust/wallet_core_rs/Cargo.toml b/rust/wallet_core_rs/Cargo.toml index 90586221f9a..37b8c878d1d 100644 --- a/rust/wallet_core_rs/Cargo.toml +++ b/rust/wallet_core_rs/Cargo.toml @@ -15,6 +15,7 @@ ethereum-rlp = [] [dependencies] tw_any_coin = { path = "../tw_any_coin" } +tw_aptos = { path = "../tw_aptos" } tw_bitcoin = { path = "../tw_bitcoin" } tw_coin_entry = { path = "../tw_coin_entry", features = ["test-utils"] } tw_coin_registry = { path = "../tw_coin_registry" } @@ -23,7 +24,6 @@ tw_ethereum = { path = "../tw_ethereum" } tw_hash = { path = "../tw_hash" } tw_keypair = { path = "../tw_keypair" } tw_memory = { path = "../tw_memory" } -tw_move_parser = { path = "../tw_move_parser" } tw_misc = { path = "../tw_misc" } tw_proto = { path = "../tw_proto" } diff --git a/rust/wallet_core_rs/src/lib.rs b/rust/wallet_core_rs/src/lib.rs index 6335fa4a0f4..eefcb93f8c8 100644 --- a/rust/wallet_core_rs/src/lib.rs +++ b/rust/wallet_core_rs/src/lib.rs @@ -5,6 +5,7 @@ // file LICENSE at the root of the source code distribution tree. pub extern crate tw_any_coin; +pub extern crate tw_aptos; pub extern crate tw_bitcoin; pub extern crate tw_coin_registry; pub extern crate tw_encoding; @@ -12,7 +13,6 @@ pub extern crate tw_ethereum; pub extern crate tw_hash; pub extern crate tw_keypair; pub extern crate tw_memory; -pub extern crate tw_move_parser; pub extern crate tw_proto; pub mod ffi; diff --git a/src/Aptos/Address.cpp b/src/Aptos/Address.cpp deleted file mode 100644 index 18242423a9e..00000000000 --- a/src/Aptos/Address.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// Author: Clement Doumergue -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Address.h" -#include "HexCoding.h" - -namespace TW::Aptos { - -Address::Address(const std::string& string) : Address::AptosAddress(string) { -} - -Address::Address(const PublicKey& publicKey): Address::AptosAddress(publicKey) { -} - -Data Address::getDigest(const PublicKey& publicKey) { - auto key_data = publicKey.bytes; - append(key_data, 0x00); - return key_data; -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, Address addr) noexcept { - stream.add_bytes(addr.bytes.begin(), addr.bytes.end()); - return stream; -} - -} // namespace TW::Aptos diff --git a/src/Aptos/Address.h b/src/Aptos/Address.h deleted file mode 100644 index db735283ddf..00000000000 --- a/src/Aptos/Address.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// Author: Clement Doumergue -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "BCS.h" -#include "Data.h" -#include "Move/Address.h" -#include "PublicKey.h" - -#include - -namespace TW::Aptos { - -class Address : public Move::Address { -public: - using AptosAddress = Move::Address; - using AptosAddress::size; - using AptosAddress::bytes; - - /// Initializes an Aptos address with a string representation. - explicit Address(const std::string& string); - - /// Initializes an Aptos address with a public key. - explicit Address(const PublicKey& publicKey); - - /// Constructor that allow factory programming; - Address() noexcept = default; - - Data getDigest(const PublicKey& publicKey); -}; - -constexpr inline bool operator==(const Address& lhs, const Address& rhs) noexcept { - return lhs.bytes == rhs.bytes; -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, Address) noexcept; - -} // namespace TW::Aptos diff --git a/src/Aptos/Entry.cpp b/src/Aptos/Entry.cpp index 8c7fafced8e..a0d362910ac 100644 --- a/src/Aptos/Entry.cpp +++ b/src/Aptos/Entry.cpp @@ -6,36 +6,26 @@ #include "Entry.h" -#include "Address.h" -#include "Signer.h" - namespace TW::Aptos { -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - return Address::isValid(address); +bool Entry::validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const { + return validateAddressRust(coin, address, addressPrefix); } -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - return Address(publicKey).string(); +std::string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const { + return deriveAddressRust(coin, publicKey, derivation, addressPrefix); } void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { - signTemplate(dataIn, dataOut); + signRust(dataIn, coin, dataOut); } Data Entry::preImageHashes([[maybe_unused]] TWCoinType coin, const Data& txInputData) const { - return txCompilerTemplate( - txInputData, [](const auto& input, auto& output) { - output = Signer::preImageHashes(input); - }); + return preImageHashesRust(coin, txInputData); } void Entry::compile([[maybe_unused]] TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const { - dataOut = txCompilerSingleTemplate( - txInputData, signatures, publicKeys, - [](const auto& input, auto& output, const auto& signature, const auto& publicKey) { - output = Signer::compile(input, signature, publicKey); - }); + compileRust(coin, txInputData, signatures, publicKeys, dataOut); } } // namespace TW::Aptos diff --git a/src/Aptos/MoveTypes.cpp b/src/Aptos/MoveTypes.cpp deleted file mode 100644 index ccde47c9439..00000000000 --- a/src/Aptos/MoveTypes.cpp +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include -#include - -namespace TW::Aptos { - -Aptos::ModuleId::ModuleId(Address accountAddress, Identifier name) noexcept - : mAccountAddress(accountAddress), mName(std::move(name)) { -} - -Data ModuleId::accessVector() const noexcept { - BCS::Serializer serializer; - serializer << static_cast(gCodeTag) << mAccountAddress << mName; - return serializer.bytes; -} - -std::string ModuleId::string() const noexcept { - std::stringstream ss; - ss << mAccountAddress.string() << "::" << mName; - return ss.str(); -} - -std::string ModuleId::shortString() const noexcept { - std::stringstream ss; - ss << "0x" << mAccountAddress.shortString() << "::" << mName; - return ss.str(); -} - -Data StructTag::serialize(bool withResourceTag) const noexcept { - BCS::Serializer serializer; - if (withResourceTag) - { - serializer << gResourceTag; - } - serializer << mAccountAddress << mModule << mName << mTypeParams; - return serializer.bytes; -} - -StructTag::StructTag(Address accountAddress, Identifier module, Identifier name, std::vector typeParams) noexcept - : mAccountAddress(accountAddress), mModule(std::move(module)), mName(std::move(name)), mTypeParams(std::move(typeParams)) { -} -std::string StructTag::string() const noexcept { - std::stringstream ss; - ss << "0x" << mAccountAddress.shortString() << "::" << mModule << "::" << mName; - if (!mTypeParams.empty()) { - ss << "<"; - ss << TypeTagToString(*mTypeParams.begin()); - std::for_each(begin(mTypeParams) + 1, end(mTypeParams), [&ss](auto&& cur) { - ss << ", " << TypeTagToString(cur); - }); - ss << ">"; - } - return ss.str(); -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, Bool) noexcept { - stream << Bool::value; - return stream; -} -BCS::Serializer& operator<<(BCS::Serializer& stream, U8) noexcept { - stream << U8::value; - return stream; -} -BCS::Serializer& operator<<(BCS::Serializer& stream, U64) noexcept { - stream << U64::value; - return stream; -} -BCS::Serializer& operator<<(BCS::Serializer& stream, U128) noexcept { - stream << U128::value; - return stream; -} -BCS::Serializer& operator<<(BCS::Serializer& stream, TAddress) noexcept { - stream << TAddress::value; - return stream; -} -BCS::Serializer& operator<<(BCS::Serializer& stream, TSigner) noexcept { - stream << TSigner::value; - return stream; -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, const StructTag& st) noexcept { - auto res = st.serialize(); - stream.add_bytes(begin(res), end(res)); - return stream; -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, const TStructTag& st) noexcept { - stream << TStructTag::value; - auto res = st.st.serialize(false); - stream.add_bytes(begin(res), end(res)); - return stream; -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, const Vector& t) noexcept { - stream << Vector::value; - for (auto&& cur: t.tags) { - stream << cur; - } - return stream; -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, const TypeTag& t) noexcept { - std::visit([&stream](auto&& arg) { stream << arg; }, t.tags); - return stream; -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, const ModuleId& module) noexcept { - stream << module.address() << module.name(); - return stream; -} -std::string TypeTagToString(const TypeTag& typeTag) noexcept { - auto visit_functor = [](const TypeTag::TypeTagVariant& value) -> std::string { - if (std::holds_alternative(value)) { - return "bool"; - } else if (std::holds_alternative(value)) { - return "u8"; - } else if (std::holds_alternative(value)) { - return "u64"; - } else if (std::holds_alternative(value)) { - return "u128"; - } else if (std::holds_alternative(value)) { - return "address"; - } else if (std::holds_alternative(value)) { - return "signer"; - } else if (auto* vectorData = std::get_if(&value); vectorData != nullptr && !vectorData->tags.empty()) { - std::stringstream ss; - ss << "vector<" << TypeTagToString(*vectorData->tags.begin()) << ">"; - return ss.str(); - } else if (auto* structData = std::get_if(&value); structData) { - return structData->string(); - } else if (auto* tStructData = std::get_if(&value); tStructData) { - return tStructData->st.string(); - } else { - return ""; - } - }; - - return std::visit(visit_functor, typeTag.tags); -} - -} // namespace TW::Aptos diff --git a/src/Aptos/MoveTypes.h b/src/Aptos/MoveTypes.h deleted file mode 100644 index 26dfe1b8477..00000000000 --- a/src/Aptos/MoveTypes.h +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "Aptos/Address.h" -#include "BCS.h" -#include - -namespace TW::Aptos { - -constexpr std::uint8_t gCodeTag{0}; -constexpr std::uint8_t gResourceTag{1}; -using Identifier = std::string; - -class ModuleId { -public: - ///< Constructor - ModuleId(Address accountAddress, Identifier name) noexcept; - - ///< Getters - [[nodiscard]] const std::string& name() const noexcept { return mName; } - [[nodiscard]] const Address& address() const noexcept { return mAccountAddress; } - [[nodiscard]] Data accessVector() const noexcept; - [[nodiscard]] std::string string() const noexcept; - [[nodiscard]] std::string shortString() const noexcept; - -private: - Address mAccountAddress; - Identifier mName; -}; - -inline ModuleId gAptosAccountModule{Address::one(), "aptos_account"}; -inline ModuleId gAptosCoinModule{Address::one(), "coin"}; -inline ModuleId gAptosManagedCoinsModule{Address::one(), "managed_coin"}; -inline ModuleId gAptosTokenTransfersModule{Address::three(), "token_transfers"}; - -BCS::Serializer& operator<<(BCS::Serializer& stream, const ModuleId& module) noexcept; - -struct TypeTag; - -struct Bool { - static constexpr std::uint8_t value = 0; -}; -struct U8 { - static constexpr std::uint8_t value = 1; -}; -struct U64 { - static constexpr std::uint8_t value = 2; -}; -struct U128 { - static constexpr std::uint8_t value = 3; -}; -struct TAddress { - static constexpr std::uint8_t value = 4; -}; -struct TSigner { - static constexpr std::uint8_t value = 5; -}; -struct Vector { - static constexpr std::uint8_t value = 6; - std::vector tags; -}; - -class StructTag { -public: - explicit StructTag(Address accountAddress, Identifier module, Identifier name, std::vector typeParams) noexcept; - [[nodiscard]] Data serialize(bool withResourceTag = true) const noexcept; - [[nodiscard]] ModuleId moduleID() const noexcept { return {mAccountAddress, mName}; }; - [[nodiscard]] std::string string() const noexcept; - -private: - Address mAccountAddress; - Identifier mModule; - Identifier mName; - std::vector mTypeParams; -}; - -// C++ limitation, the first StructTag will serialize with ResourceTag, the inner one will use the value 7 instead. Tweaking by wrapping the struct -struct TStructTag { - static constexpr std::uint8_t value = 7; - StructTag st; -}; - -struct TypeTag { - using TypeTagVariant = std::variant; - TypeTagVariant tags; -}; - -std::string TypeTagToString(const TypeTag& typeTag) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, const StructTag& st) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, Bool) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, U8) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, U64) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, U128) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, TAddress) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, TSigner) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, const Vector& t) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, const TStructTag& t) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, const TypeTag& t) noexcept; -static const TypeTag gTransferTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(Address::one(), "aptos_coin", "AptosCoin", {})})}; -static const TypeTag gOfferNftTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(Address::three(), "token_transfers", "offer_script", {})})}; - -} // namespace TW::Aptos diff --git a/src/Aptos/Signer.cpp b/src/Aptos/Signer.cpp deleted file mode 100644 index 992d91dd6f6..00000000000 --- a/src/Aptos/Signer.cpp +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Signer.h" -#include "Address.h" -#include "Hash.h" -#include "MoveTypes.h" -#include "TransactionBuilder.h" -#include "TransactionPayload.h" - -namespace { -template -void serializeToArgs(std::vector& args, T&& toSerialize) { - TW::BCS::Serializer serializer; - serializer << std::forward(toSerialize); - args.emplace_back(serializer.bytes); -} -} // namespace - -namespace TW::Aptos { - -template -std::pair, nlohmann::json> commonTransferPayload(const TPayload& input) { - std::vector args; - serializeToArgs(args, Address(input.to())); - serializeToArgs(args, input.amount()); - nlohmann::json argsJson = nlohmann::json::array({input.to(), std::to_string(input.amount())}); - return std::make_pair(args, argsJson); -} - -TransactionPayload transferPayload(const Proto::SigningInput& input) { - auto&& [args, argsJson] = commonTransferPayload(input.transfer()); - TransactionPayload payload = EntryFunction(gAptosAccountModule, "transfer", {}, args, argsJson); - return payload; -} - -TransactionPayload createAccountPayload(const Proto::SigningInput& input) { - std::vector args; - serializeToArgs(args, Address(input.create_account().auth_key())); - nlohmann::json argsJson = nlohmann::json::array({input.create_account().auth_key()}); - TransactionPayload payload = EntryFunction(gAptosAccountModule, "create_account", {}, args, argsJson); - return payload; -} - -TransactionPayload claimNftPayload(const Proto::ClaimNftMessage& msg) { - std::vector args; - serializeToArgs(args, Address(msg.sender())); - serializeToArgs(args, Address(msg.creator())); - serializeToArgs(args, msg.collectionname()); - serializeToArgs(args, msg.name()); - serializeToArgs(args, msg.property_version()); - // clang-format off - nlohmann::json argsJson = nlohmann::json::array( - { - msg.sender(), - msg.creator(), - msg.collectionname(), - msg.name(), - std::to_string(msg.property_version()), - }); - // clang-format on - TransactionPayload payload = EntryFunction(gAptosTokenTransfersModule, "claim_script", {}, args, argsJson); - return payload; -} - -TransactionPayload nftOfferPayload(const Proto::OfferNftMessage& msg) { - std::vector args; - serializeToArgs(args, Address(msg.receiver())); - serializeToArgs(args, Address(msg.creator())); - serializeToArgs(args, msg.collectionname()); - serializeToArgs(args, msg.name()); - serializeToArgs(args, msg.property_version()); - serializeToArgs(args, msg.amount()); - // clang-format off - nlohmann::json argsJson = nlohmann::json::array( - { - msg.receiver(), - msg.creator(), - msg.collectionname(), - msg.name(), - std::to_string(msg.property_version()), - std::to_string(msg.amount()) - }); - // clang-format on - TransactionPayload payload = EntryFunction(gAptosTokenTransfersModule, "offer_script", {}, args, argsJson); - return payload; -} - -TransactionPayload tortugaClaimPayload(const std::string& smart_contract_address, const Proto::TortugaClaim& msg) { - std::vector args; - serializeToArgs(args, msg.idx()); - // clang-format off - nlohmann::json argsJson = nlohmann::json::array( - { - std::to_string(msg.idx()) - }); - // clang-format on - ModuleId tortugaStakeModule{Address(smart_contract_address), "stake_router"}; - TransactionPayload payload = EntryFunction(tortugaStakeModule, "claim", {}, args, argsJson); - return payload; -} - -TransactionPayload tortugaStakePayload(const std::string& smart_contract_address, const Proto::TortugaStake& msg) { - std::vector args; - serializeToArgs(args, msg.amount()); - // clang-format off - nlohmann::json argsJson = nlohmann::json::array( - { - std::to_string(msg.amount()) - }); - // clang-format on - ModuleId tortugaStakeModule{Address(smart_contract_address), "stake_router"}; - TransactionPayload payload = EntryFunction(tortugaStakeModule, "stake", {}, args, argsJson); - return payload; -} - -TransactionPayload tortugaUnStakePayload(const std::string& smart_contract_address, const Proto::TortugaUnstake& msg) { - std::vector args; - serializeToArgs(args, msg.amount()); - // clang-format off - nlohmann::json argsJson = nlohmann::json::array( - { - std::to_string(msg.amount()) - }); - // clang-format on - ModuleId tortugaStakeModule{Address(smart_contract_address), "stake_router"}; - TransactionPayload payload = EntryFunction(tortugaStakeModule, "unstake", {}, args, argsJson); - return payload; -} - -TransactionPayload cancelNftOfferPayload(const Proto::CancelOfferNftMessage& msg) { - std::vector args; - serializeToArgs(args, Address(msg.receiver())); - serializeToArgs(args, Address(msg.creator())); - serializeToArgs(args, msg.collectionname()); - serializeToArgs(args, msg.name()); - serializeToArgs(args, msg.property_version()); - // clang-format off - nlohmann::json argsJson = nlohmann::json::array( - { - msg.receiver(), - msg.creator(), - msg.collectionname(), - msg.name(), - std::to_string(msg.property_version()), - }); - // clang-format on - TransactionPayload payload = EntryFunction(gAptosTokenTransfersModule, "cancel_offer_script", {}, args, argsJson); - return payload; -} - -TransactionPayload tokenTransferPayload(const Proto::SigningInput& input) { - - auto&& [args, argsJson] = commonTransferPayload(input.token_transfer()); - auto& function = input.token_transfer().function(); - TypeTag tokenTransferTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(Address(function.account_address()), - function.module(), function.name(), {})})}; - TransactionPayload payload = EntryFunction(gAptosCoinModule, "transfer", {tokenTransferTag}, args, argsJson); - return payload; -} - -TransactionPayload tokenTransferCoinsPayload(const Proto::SigningInput& input) { - auto&& [args, argsJson] = commonTransferPayload(input.token_transfer_coins()); - auto& function = input.token_transfer_coins().function(); - TypeTag tokenTransferTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(Address(function.account_address()), - function.module(), function.name(), {})})}; - TransactionPayload payload = EntryFunction(gAptosAccountModule, "transfer_coins", {tokenTransferTag}, args, argsJson); - return payload; -} - -TransactionPayload registerTokenPayload(const Proto::SigningInput& input) { - - auto& function = input.register_token().function(); - TypeTag tokenRegisterTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(Address(function.account_address()), - function.module(), function.name(), {})})}; - TransactionPayload payload = EntryFunction(gAptosManagedCoinsModule, "register", {tokenRegisterTag}, {}); - return payload; -} - -TransactionBasePtr buildBlindTx(const Proto::SigningInput& input) { - if (nlohmann::json j = nlohmann::json::parse(input.any_encoded(), nullptr, false); j.is_discarded()) { - auto blindBuilder = std::make_unique(); - blindBuilder->encodedCallHex(input.any_encoded()); - return blindBuilder; - } else { - auto txBuilder = std::make_unique(); - txBuilder->sender(Address(input.sender())) - .sequenceNumber(input.sequence_number()) - .payload(EntryFunction::from_json(j)) - .maxGasAmount(input.max_gas_amount()) - .gasUnitPrice(input.gas_unit_price()) - .expirationTimestampSecs(input.expiration_timestamp_secs()) - .chainId(static_cast(input.chain_id())); - return txBuilder; - } -} - -TransactionBasePtr buildTx(const Proto::SigningInput& input) { - if (!input.any_encoded().empty()) { - return buildBlindTx(input); - } - - auto nftPayloadFunctor = [](const Proto::NftMessage& nftMessage) { - switch (nftMessage.nft_transaction_payload_case()) { - case Proto::NftMessage::kOfferNft: - return nftOfferPayload(nftMessage.offer_nft()); - case Proto::NftMessage::kCancelOfferNft: - return cancelNftOfferPayload(nftMessage.cancel_offer_nft()); - case Proto::NftMessage::kClaimNft: - return claimNftPayload(nftMessage.claim_nft()); - case Proto::NftMessage::NFT_TRANSACTION_PAYLOAD_NOT_SET: - throw std::runtime_error("Nft message payload not set"); - } - }; - auto liquidStakingFunctor = [](const Proto::LiquidStaking& liquidStakingMessage) { - switch (liquidStakingMessage.liquid_stake_transaction_payload_case()) { - case Proto::LiquidStaking::kStake: - return tortugaStakePayload(liquidStakingMessage.smart_contract_address(), liquidStakingMessage.stake()); - case Proto::LiquidStaking::kUnstake: - return tortugaUnStakePayload(liquidStakingMessage.smart_contract_address(), liquidStakingMessage.unstake()); - case Proto::LiquidStaking::kClaim: - return tortugaClaimPayload(liquidStakingMessage.smart_contract_address(), liquidStakingMessage.claim()); - case Proto::LiquidStaking::LIQUID_STAKE_TRANSACTION_PAYLOAD_NOT_SET: - return TransactionPayload(); - } - }; - auto payloadFunctor = [&input, &nftPayloadFunctor, &liquidStakingFunctor]() { - switch (input.transaction_payload_case()) { - case Proto::SigningInput::kTransfer: { - return transferPayload(input); - } - case Proto::SigningInput::kTokenTransfer: { - return tokenTransferPayload(input); - } - case Proto::SigningInput::kNftMessage: { - return nftPayloadFunctor(input.nft_message()); - } - case Proto::SigningInput::kCreateAccount: { - return createAccountPayload(input); - } - case Proto::SigningInput::kRegisterToken: { - return registerTokenPayload(input); - } - case Proto::SigningInput::kLiquidStakingMessage: { - return liquidStakingFunctor(input.liquid_staking_message()); - } - case Proto::SigningInput::kTokenTransferCoins: { - return tokenTransferCoinsPayload(input); - } - case Proto::SigningInput::TRANSACTION_PAYLOAD_NOT_SET: - throw std::runtime_error("Transaction payload should be set"); - } - }; - auto txBuilder = std::make_unique(); - txBuilder->sender(Address(input.sender())) - .sequenceNumber(input.sequence_number()) - .payload(payloadFunctor()) - .maxGasAmount(input.max_gas_amount()) - .gasUnitPrice(input.gas_unit_price()) - .expirationTimestampSecs(input.expiration_timestamp_secs()) - .chainId(static_cast(input.chain_id())); - return txBuilder; -} - -Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) { - return buildTx(input)->sign(input); -} - -TxCompiler::Proto::PreSigningOutput Signer::preImageHashes(const Proto::SigningInput& input) { - return buildTx(input)->preImage(); -} - -Proto::SigningOutput Signer::compile(const Proto::SigningInput& input, const Data& signature, const PublicKey& publicKey) { - return buildTx(input)->compile(signature, publicKey); -} - -} // namespace TW::Aptos diff --git a/src/Aptos/Signer.h b/src/Aptos/Signer.h deleted file mode 100644 index e0222515ca0..00000000000 --- a/src/Aptos/Signer.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "Data.h" -#include "../PrivateKey.h" -#include "../proto/Aptos.pb.h" -#include "../proto/TransactionCompiler.pb.h" - -namespace TW::Aptos { - -inline const Data gAptosSalt = data("APTOS::RawTransaction"); - -/// Helper class that performs Aptos transaction signing. -class Signer { -public: - /// Hide default constructor - Signer() = delete; - - /// Signs a Proto::SigningInput transaction - static Proto::SigningOutput sign(const Proto::SigningInput& input); - - static TxCompiler::Proto::PreSigningOutput preImageHashes(const Proto::SigningInput& input); - - static Proto::SigningOutput compile(const Proto::SigningInput& input, const Data& signature, const PublicKey& publicKey); -}; - -} // namespace TW::Aptos diff --git a/src/Aptos/TransactionBuilder.h b/src/Aptos/TransactionBuilder.h deleted file mode 100644 index 46bf3fbaa12..00000000000 --- a/src/Aptos/TransactionBuilder.h +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "HexCoding.h" -#include "TransactionPayload.h" - -#include -#include - -namespace TW::Aptos { - -struct TransactionBase; - -using TransactionBasePtr = std::unique_ptr; - -struct TransactionBase { - virtual ~TransactionBase() = default; - - virtual TxCompiler::Proto::PreSigningOutput preImage() noexcept = 0; - - virtual Proto::SigningOutput compile(const Data& signature, const PublicKey& publicKey) = 0; - - virtual Proto::SigningOutput sign(const Proto::SigningInput& input) = 0; -}; - -class BlindBuilder final : public TransactionBase { -public: - BlindBuilder() noexcept = default; - - BlindBuilder& encodedCallHex(const std::string& encodedCallHex) { - mEncodedCall = parse_hex(encodedCallHex); - return *this; - } - - TxCompiler::Proto::PreSigningOutput preImage() noexcept override { - TxCompiler::Proto::PreSigningOutput output; - // Aptos has no preImageHash. - output.set_data(mEncodedCall.data(), mEncodedCall.size()); - return output; - } - - Proto::SigningOutput compile(const Data& signature, const PublicKey& publicKey) override { - Proto::SigningOutput output; - const auto& pubKeyData = publicKey.bytes; - - BCS::Serializer serializer; - serializer.add_bytes(begin(mEncodedCall), end(mEncodedCall)); - - output.set_raw_txn(mEncodedCall.data(), mEncodedCall.size()); - output.mutable_authenticator()->set_public_key(pubKeyData.data(), pubKeyData.size()); - output.mutable_authenticator()->set_signature(signature.data(), signature.size()); - serializer << BCS::uleb128{.value = 0} << pubKeyData << signature; - output.set_encoded(serializer.bytes.data(), serializer.bytes.size()); - - // clang-format off - nlohmann::json json = { - {"type", "ed25519_signature"}, - {"public_key", hexEncoded(pubKeyData)}, - {"signature", hexEncoded(signature)} - }; - // clang-format on - output.set_json(json.dump()); - - return output; - } - - Proto::SigningOutput sign(const Proto::SigningInput& input) override { - auto privateKey = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - auto signature = privateKey.sign(mEncodedCall, TWCurveED25519); - return compile(signature, publicKey); - } - -private: - Data mEncodedCall; -}; - -// Standard transaction builder. -class TransactionBuilder final : public TransactionBase { -public: - TransactionBuilder() noexcept = default; - - TransactionBuilder& sender(Address sender) noexcept { - mSender = sender; - return *this; - } - - TransactionBuilder& sequenceNumber(std::uint64_t sequenceNumber) noexcept { - mSequenceNumber = sequenceNumber; - return *this; - } - - TransactionBuilder& payload(TransactionPayload payload) noexcept { - mPayload = std::move(payload); - return *this; - } - - TransactionBuilder& maxGasAmount(std::uint64_t maxGasAmount) noexcept { - mMaxGasAmount = maxGasAmount; - return *this; - } - - TransactionBuilder& gasUnitPrice(std::uint64_t gasUnitPrice) noexcept { - mGasUnitPrice = gasUnitPrice; - return *this; - } - - TransactionBuilder& expirationTimestampSecs(std::uint64_t expirationTimestampSecs) noexcept { - mExpirationTimestampSecs = expirationTimestampSecs; - return *this; - } - - TransactionBuilder& chainId(std::uint8_t chainId) noexcept { - mChainId = chainId; - return *this; - } - - BCS::Serializer prepareSerializer() noexcept { - BCS::Serializer serializer; - serializer << mSender << mSequenceNumber << mPayload << mMaxGasAmount << mGasUnitPrice << mExpirationTimestampSecs << mChainId; - return serializer; - } - - Data msgToSign() noexcept { - auto serialized = prepareSerializer().bytes; - auto preImageOutput = TW::Hash::sha3_256(gAptosSalt.data(), gAptosSalt.size()); - append(preImageOutput, serialized); - return preImageOutput; - } - - TxCompiler::Proto::PreSigningOutput preImage() noexcept override { - TxCompiler::Proto::PreSigningOutput output; - auto signingMsg = msgToSign(); - // Aptos has no preImageHash. - output.set_data(signingMsg.data(), signingMsg.size()); - return output; - } - - Proto::SigningOutput compile(const Data& signature, const PublicKey& publicKey) noexcept override { - Proto::SigningOutput output; - const auto& pubKeyData = publicKey.bytes; - - auto serializer = prepareSerializer(); - - output.set_raw_txn(serializer.bytes.data(), serializer.bytes.size()); - output.mutable_authenticator()->set_public_key(pubKeyData.data(), pubKeyData.size()); - output.mutable_authenticator()->set_signature(signature.data(), signature.size()); - - serializer << BCS::uleb128{.value = 0} << pubKeyData << signature; - output.set_encoded(serializer.bytes.data(), serializer.bytes.size()); - - // https://fullnode.devnet.aptoslabs.com/v1/spec#/operations/submit_transaction - // clang-format off - nlohmann::json json = { - {"sender", mSender.string()}, - {"sequence_number", std::to_string(mSequenceNumber)}, - {"max_gas_amount", std::to_string(mMaxGasAmount)}, - {"gas_unit_price", std::to_string(mGasUnitPrice)}, - {"expiration_timestamp_secs", std::to_string(mExpirationTimestampSecs)}, - {"payload", payloadToJson(mPayload)}, - {"signature", { - {"type", "ed25519_signature"}, - {"public_key", hexEncoded(pubKeyData)}, - {"signature", hexEncoded(signature)}} - } - }; - // clang-format on - output.set_json(json.dump()); - return output; - } - - Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept override { - auto signingMsg = msgToSign(); - auto privateKey = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); - auto signature = privateKey.sign(signingMsg, TWCurveED25519); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - return compile(signature, publicKey); - } - -private: - Address mSender{}; - std::uint64_t mSequenceNumber{}; - TransactionPayload mPayload{}; - std::uint64_t mMaxGasAmount{}; - std::uint64_t mGasUnitPrice{}; - std::uint64_t mExpirationTimestampSecs{}; - std::uint8_t mChainId{}; -}; - -} // namespace TW::Aptos diff --git a/src/Aptos/TransactionPayload.cpp b/src/Aptos/TransactionPayload.cpp deleted file mode 100644 index e3bf73bccb7..00000000000 --- a/src/Aptos/TransactionPayload.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "rust/bindgen/WalletCoreRSBindgen.h" -#include -#include - -namespace TW::Aptos { - -EntryFunction::EntryFunction(ModuleId module, Identifier function, std::vector tyArgs, std::vector args, nlohmann::json jsonArgs) noexcept - : mModule(std::move(module)), mFunction(std::move(function)), mTyArgs(std::move(tyArgs)), mArgs(std::move(args)), mJsonArgs(std::move(jsonArgs)) { -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, const EntryFunction& entryFunction) noexcept { - stream << entryFunction.module() << entryFunction.function() << entryFunction.tyArgs() << entryFunction.args(); - return stream; -} - -nlohmann::json payloadToJson(const TransactionPayload& payload) { - auto visit_functor = [](const TransactionPayload& value) -> nlohmann::json { - if (auto* entryFunction = std::get_if(&value); entryFunction) { - return entryFunction->json(); - } else { - return {}; - } - }; - - return std::visit(visit_functor, payload); -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, [[maybe_unused]] const Script& script) noexcept { - return stream; -} - -BCS::Serializer& operator<<(BCS::Serializer& stream, [[maybe_unused]] const ModuleBundle& moduleBundle) noexcept { - return stream; -} - -nlohmann::json EntryFunction::json() const noexcept { - nlohmann::json tyArgsJson = nlohmann::json::array(); - for (auto&& cur : mTyArgs) { - tyArgsJson.emplace_back(TypeTagToString(cur)); - } - // clang-format off - nlohmann::json out = { - {"type", "entry_function_payload"}, - {"function", mModule.shortString() + "::" + mFunction}, - {"type_arguments", tyArgsJson}, - {"arguments", mJsonArgs.empty() ? nlohmann::json::array() : mJsonArgs} - }; - // clang-format on - return out; -} - -EntryFunction EntryFunction::from_json(const nlohmann::json& payload) noexcept { - auto splitFunctor = [](std::string s, std::string_view delimiter) { - size_t pos_start = 0, pos_end, delim_len = delimiter.size(); - std::string token; - std::vector output; - - while ((pos_end = s.find(delimiter, pos_start)) != std::string::npos) { - token = s.substr(pos_start, pos_end - pos_start); - pos_start = pos_end + delim_len; - output.emplace_back(token); - } - - output.emplace_back(s.substr(pos_start)); - return output; - }; - auto functionSplitted = splitFunctor(payload.at("function").get(), "::"); - auto moduleId = ModuleId(Address(functionSplitted[0]), functionSplitted[1]); - std::vector args; - for (auto&& cur : payload.at("arguments")) { - auto curStr = cur.get(); - auto res = Rust::parse_function_argument_to_bcs(curStr.c_str()); - if (res.code != Rust::OK_CODE) { - // TODO consider exiting this function. - args.emplace_back(); - continue; - } - args.emplace_back(parse_hex(res.result)); - Rust::free_string(res.result); - } - - std::vector tags; - - for (auto&& cur : payload.at("type_arguments")) { - auto curStr = cur.get(); - switch (Rust::parse_type_tag(curStr.c_str())) { - case Rust::ETypeTag::Bool: - break; - case Rust::ETypeTag::U8: - break; - case Rust::ETypeTag::U64: - break; - case Rust::ETypeTag::U128: - break; - case Rust::ETypeTag::Address: - break; - case Rust::ETypeTag::Signer: - break; - case Rust::ETypeTag::Vector: - break; - case Rust::ETypeTag::Struct: { - auto structSplitted = splitFunctor(curStr, "::"); - auto addr = Address(structSplitted[0]); - TypeTag tag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(addr, structSplitted[1], structSplitted[2], {})})}; - tags.emplace_back(tag); - break; - } - case Rust::ETypeTag::Error: - break; - default: - break; - } - } - - return EntryFunction(moduleId, functionSplitted[2], tags, {args}, payload.at("arguments")); -} - -} // namespace TW::Aptos diff --git a/src/Aptos/TransactionPayload.h b/src/Aptos/TransactionPayload.h deleted file mode 100644 index 6041fc0ed8d..00000000000 --- a/src/Aptos/TransactionPayload.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include -#include -#include - -namespace TW::Aptos { - -/// Call a Move entry function. -class EntryFunction { -public: - explicit EntryFunction(ModuleId module, Identifier function, std::vector tyArgs, std::vector args, nlohmann::json jsonArgs = {}) noexcept; - [[nodiscard]] const ModuleId& module() const noexcept { return mModule; } - [[nodiscard]] const Identifier& function() const noexcept { return mFunction; } - [[nodiscard]] const std::vector& tyArgs() const noexcept { return mTyArgs; } - [[nodiscard]] const std::vector& args() const noexcept { return mArgs; } - [[nodiscard]] nlohmann::json json() const noexcept; - static EntryFunction from_json(const nlohmann::json& json) noexcept; - -private: - ModuleId mModule; - Identifier mFunction; - std::vector mTyArgs; - std::vector mArgs; - nlohmann::json mJsonArgs; -}; - - -class Script { -}; - -class ModuleBundle { -}; - -BCS::Serializer& operator<<(BCS::Serializer& stream, const EntryFunction& entryFunction) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, const Script& script) noexcept; -BCS::Serializer& operator<<(BCS::Serializer& stream, const ModuleBundle& moduleBundle) noexcept; -using TransactionPayload = std::variant; -nlohmann::json payloadToJson(const TransactionPayload& payload); - -} // namespace TW::Aptos diff --git a/src/BCS.cpp b/src/BCS.cpp deleted file mode 100644 index fb6015cdf1b..00000000000 --- a/src/BCS.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// Created by Clément Doumergue - -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "BCS.h" - -namespace TW::BCS { - -Serializer& operator<<(Serializer& stream, std::byte b) noexcept { - stream.add_byte(b); - return stream; -} - -Serializer& operator<<(Serializer& stream, uleb128 t) noexcept { - integral auto value = t.value; - - while (value >= 0x80) { - // Add the 7 lowest bits of data and set highest bit to 1 - stream << static_cast((value & 0x7f) | 0x80); - value >>= 7; - } - - // Add the remaining bits of data (highest bit is already 0 at this point) - stream << static_cast(value); - return stream; -} - -Serializer& operator<<(Serializer& stream, std::string_view sv) noexcept { - stream << uleb128{static_cast(sv.size())}; - stream.add_bytes(sv.begin(), sv.end()); - return stream; -} - -Serializer& operator<<(Serializer& stream, std::nullopt_t) noexcept { - stream << false; - return stream; -} - -} // namespace TW::BCS diff --git a/src/BCS.h b/src/BCS.h deleted file mode 100644 index 563c204b8cb..00000000000 --- a/src/BCS.h +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// Created by Clément Doumergue - -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "Data.h" -#include "concepts/tw_concepts.h" - -namespace TW::BCS { - -/// Implementation of BCS encoding (as specified by the Diem project, see github.com/diem/bcs#detailed-specifications) - -struct Serializer { - Data bytes; - - void add_byte(std::byte b) noexcept { - bytes.emplace_back(static_cast(b)); - } - - template - void add_bytes(Iterator first, Iterator last) noexcept { - std::transform(first, last, std::back_inserter(bytes), [](auto&& c) { - return static_cast(c); - }); - } - - void clear() noexcept { - bytes.clear(); - } -}; - -struct uleb128 { - uint32_t value; -}; - -namespace details { - -template -concept aggregate_struct = std::is_class_v> && std::is_aggregate_v>; - -template -concept map_container = requires(T t) { - typename T::key_type; - typename T::mapped_type; - { std::declval().size() } -> std::same_as; - }; - -template - requires integral || - floating_point || - std::same_as || - std::same_as || - std::same_as || - aggregate_struct || - map_container -struct is_serializable { - static constexpr auto value = true; -}; - -template -struct is_serializable> { - static constexpr auto value = is_serializable::value; -}; - -template -struct is_serializable> { - static constexpr auto value = (is_serializable::value && ...); -}; - -template -struct is_serializable> { - static constexpr auto value = is_serializable::value && is_serializable::value; -}; - -template -struct is_serializable> { - static constexpr auto value = is_serializable::value; -}; - -template -struct is_serializable> { - static constexpr auto value = (is_serializable::value && ...); -}; - -template -Serializer& serialize_integral_impl(Serializer& stream, T t, std::index_sequence) noexcept { - const char* bytes = reinterpret_cast(&t); - // Add each byte in little-endian order - return (stream << ... << static_cast(bytes[Is])); -} - -template -Serializer& serialize_tuple_impl(Serializer& stream, const T& t, std::index_sequence) noexcept { - return (stream << ... << std::get(t)); -} - -template -struct dependent_false { - static constexpr auto value = false; -}; - -template -constexpr auto to_tuple(T&& t) { - if constexpr (std::is_empty_v) { - return std::make_tuple(); - } else if constexpr (requires { [&t] { auto&& [a0] = t; }; }) { - auto&& [a0] = std::forward(t); - return std::make_tuple(a0); - } else if constexpr (requires { [&t] { auto&& [a0, a1] = t; }; }) { - auto&& [a0, a1] = std::forward(t); - return std::make_tuple(a0, a1); - } else if constexpr (requires { [&t] { auto&& [a0, a1, a2] = t; }; }) { - auto&& [a0, a1, a2] = std::forward(t); - return std::make_tuple(a0, a1, a2); - } else if constexpr (requires { [&t] { auto&& [a0, a1, a2, a3] = t; }; }) { - auto&& [a0, a1, a2, a3] = std::forward(t); - return std::make_tuple(a0, a1, a2, a3); - } else if constexpr (requires { [&t] { auto&& [a0, a1, a2, a3, a4] = t; }; }) { - auto&& [a0, a1, a2, a3, a4] = std::forward(t); - return std::make_tuple(a0, a1, a2, a3, a4); - } else if constexpr (requires { [&t] { auto&& [a0, a1, a2, a3, a4, a5] = t; }; }) { - auto&& [a0, a1, a2, a3, a4, a5] = std::forward(t); - return std::make_tuple(a0, a1, a2, a3, a4, a5); - } else { - static_assert(dependent_false::value, "the structure has more than 6 members"); - } -} - -template -Serializer& serialize_struct_impl(Serializer& stream, const T& t) noexcept { - return stream << to_tuple(t); -} -} // namespace details - -template -concept PrimitiveSerializable = details::is_serializable::value; - -template -concept CustomSerializable = requires(T t) { - { std::declval() << t } -> std::same_as; - }; - -template -concept Serializable = PrimitiveSerializable || CustomSerializable; - -Serializer& operator<<(Serializer& stream, std::byte b) noexcept; - -template -Serializer& operator<<(Serializer& stream, T t) noexcept { - return details::serialize_integral_impl(stream, t, std::make_index_sequence{}); -} - -Serializer& operator<<(Serializer& stream, uleb128 t) noexcept; - -Serializer& operator<<(Serializer& stream, std::string_view sv) noexcept; - -template -Serializer& operator<<(Serializer& stream, const std::optional o) noexcept { - if (o.has_value()) { - stream << true; - stream << o.value(); - } else { - stream << false; - } - return stream; -} - -Serializer& operator<<(Serializer& stream, std::nullopt_t) noexcept; - -template -Serializer& operator<<(Serializer& stream, const std::tuple& t) noexcept { - return details::serialize_tuple_impl(stream, t, std::make_index_sequence{}); -} - -template -Serializer& operator<<(Serializer& stream, const std::pair& t) noexcept { - return details::serialize_tuple_impl(stream, t, std::make_index_sequence<2>{}); -} - -template -Serializer& operator<<(Serializer& stream, const T& t) noexcept { - return details::serialize_struct_impl(stream, t); -} - -template -Serializer& operator<<(Serializer& stream, const std::vector& t) noexcept { - stream << uleb128{static_cast(t.size())}; - for (auto&& cur: t) { - stream << cur; - } - return stream; -} - -template -Serializer& operator<<(Serializer& stream, const std::variant& t) noexcept { - stream << uleb128{static_cast(t.index())}; - std::visit([&stream](auto&& value) { stream << value; }, t); - return stream; -} - -template -Serializer& operator<<(Serializer& stream, const T& t) noexcept { - stream << uleb128{static_cast(t.size())}; - for (auto&& [k, v] : t) { - stream << std::make_tuple(k, v); - } - return stream; -} - -} // namespace TW::BCS diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 468aa0c5fbf..df0a6c09048 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -306,7 +306,7 @@ class CoinAddressDerivationTests: XCTestCase { let expectedResult = "EQDgEMqToTacHic7SnvnPFmvceG5auFkCcAw0mSCvzvKUfk9"; assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .aptos: - let expectedResult = "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"; + let expectedResult = "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"; assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .nebl: let expectedResult = "NgDVaXAwNgBwb88xLiFKomfBmPkEh9F2d7"; diff --git a/tests/chains/Aptos/AddressTests.cpp b/tests/chains/Aptos/AddressTests.cpp index 8c758428154..fc68f647a88 100644 --- a/tests/chains/Aptos/AddressTests.cpp +++ b/tests/chains/Aptos/AddressTests.cpp @@ -6,7 +6,7 @@ // file LICENSE at the root of the source code distribution tree. #include "HexCoding.h" -#include "Aptos/Address.h" +#include "Aptos/Entry.h" #include "PublicKey.h" #include "PrivateKey.h" #include @@ -15,49 +15,38 @@ namespace TW::Aptos::tests { TEST(AptosAddress, Valid) { - ASSERT_TRUE(Address::isValid("0x1")); - ASSERT_TRUE(Address::isValid(Address::one().string())); - ASSERT_TRUE(Address::isValid(Address::zero().string())); - ASSERT_TRUE(Address::isValid("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b")); - ASSERT_TRUE(Address::isValid("eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b")); - ASSERT_TRUE(Address::isValid("19aadeca9388e009d136245b9a67423f3eee242b03142849eb4f81a4a409e59c")); - ASSERT_TRUE(Address::isValid("777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb")); - ASSERT_TRUE(Address::isValid("0x777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb")); - ASSERT_TRUE(Address::isValid("eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175")); // too short -> automatically padded + Entry entry; + ASSERT_TRUE(entry.validateAddress(TWCoinTypeAptos, "0x1", std::monostate{})); + ASSERT_TRUE(entry.validateAddress(TWCoinTypeAptos, "0x0", std::monostate{})); + ASSERT_TRUE(entry.validateAddress(TWCoinTypeAptos, "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", std::monostate{})); + ASSERT_TRUE(entry.validateAddress(TWCoinTypeAptos, "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", std::monostate{})); + ASSERT_TRUE(entry.validateAddress(TWCoinTypeAptos, "19aadeca9388e009d136245b9a67423f3eee242b03142849eb4f81a4a409e59c", std::monostate{})); + ASSERT_TRUE(entry.validateAddress(TWCoinTypeAptos, "0x777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb", std::monostate{})); + ASSERT_TRUE(entry.validateAddress(TWCoinTypeAptos, "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175", std::monostate{})); } TEST(AptosAddress, Invalid) { - ASSERT_FALSE(Address::isValid("Seff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b")); // Invalid hex character - ASSERT_FALSE(Address::isValid("eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175bb")); // Invalid length: too long + Entry entry; + ASSERT_FALSE(entry.validateAddress(TWCoinTypeAptos, "", std::monostate{})); + ASSERT_FALSE(entry.validateAddress(TWCoinTypeAptos, "Seff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", std::monostate{})); + ASSERT_FALSE(entry.validateAddress(TWCoinTypeAptos, "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175bb", std::monostate{})); + ASSERT_FALSE(entry.validateAddress(TWCoinTypeAptos, "0xSeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", std::monostate{})); } TEST(AptosAddress, FromPrivateKey) { auto privateKey = PrivateKey(parse_hex("088baa019f081d6eab8dff5c447f9ce2f83c1babf3d03686299eaf6a1e89156e")); - auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeED25519)); - ASSERT_EQ(address.string(), "0xe9c4d0b6fe32a5cc8ebd1e9ad5b54a0276a57f2d081dcb5e30342319963626c3"); + auto pubkey = privateKey.getPublicKey(TWPublicKeyTypeED25519); + Entry entry; + auto address = entry.deriveAddress(TWCoinTypeAptos, pubkey, TWDerivationDefault, std::monostate{}); + ASSERT_EQ(address, "0xe9c4d0b6fe32a5cc8ebd1e9ad5b54a0276a57f2d081dcb5e30342319963626c3"); } TEST(AptosAddress, FromPublicKey) { auto publicKey = PublicKey(parse_hex("ad0e293a56c9fc648d1872a00521d97e6b65724519a2676c2c47cb95d131cf5a"), TWPublicKeyTypeED25519); - auto address = Address(publicKey); - ASSERT_EQ(address.string(), "0xe9c4d0b6fe32a5cc8ebd1e9ad5b54a0276a57f2d081dcb5e30342319963626c3"); -} - -TEST(AptosAddress, FromString) { - auto address = Address("eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b"); - ASSERT_EQ(address.string(), "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b"); - - - address = Address("0x777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb"); - ASSERT_EQ(address.string(), "0x0000777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb"); - address = Address("777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb"); - ASSERT_EQ(address.string(), "0x0000777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb"); -} - -TEST(AptosAddress, ShortString) { - ASSERT_EQ(Address::one().string(), "0x0000000000000000000000000000000000000000000000000000000000000001"); - ASSERT_EQ(Address::one().shortString(), "1"); + Entry entry; + auto address = entry.deriveAddress(TWCoinTypeAptos, publicKey, TWDerivationDefault, std::monostate{}); + ASSERT_EQ(address, "0xe9c4d0b6fe32a5cc8ebd1e9ad5b54a0276a57f2d081dcb5e30342319963626c3"); } } // namespace TW::Aptos::tests diff --git a/tests/chains/Aptos/CompilerTests.cpp b/tests/chains/Aptos/CompilerTests.cpp index 6f731cfbfe5..16d7ae5dc11 100644 --- a/tests/chains/Aptos/CompilerTests.cpp +++ b/tests/chains/Aptos/CompilerTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "Aptos/Signer.h" +#include "proto/Aptos.pb.h" #include "HexCoding.h" #include "PrivateKey.h" #include "PublicKey.h" @@ -66,12 +66,12 @@ TEST(AptosCompiler, StandardTransaction) { "gas_unit_price": "100", "max_gas_amount": "3296766", "payload": { - "arguments": ["0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], + "arguments": ["0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], "function": "0x1::aptos_account::transfer", "type": "entry_function_payload", "type_arguments": [] }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", "sequence_number": "99", "signature": { "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", @@ -160,7 +160,7 @@ TEST(AptosCompiler, BlindTransactionJson) { ], "type": "entry_function_payload" }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", "sequence_number": "42", "signature": { "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", @@ -173,52 +173,4 @@ TEST(AptosCompiler, BlindTransactionJson) { assertJSONEqual(expectedJson, parsedJson); } -TEST(AptosCompiler, BlindTransactionHex) { - // successfully broadcasted https://explorer.aptoslabs.com/txn/0xd95857a9e644528708778a3a0a6e13986751944fca30eaac98853c1655de0422?network=Devnet - auto anyEncoded = "0xb5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300200000000000000024633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b29883704706f6f6c0b737761705f795f746f5f780207deae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e010c6465766e65745f636f696e730a4465766e657455534454000700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00020800e1f50500000000080000000000000000fe4d3200000000006400000000000000c2276ada0000000021"; - - Proto::SigningInput input; - input.set_any_encoded(anyEncoded); - - auto inputString = input.SerializeAsString(); - auto inputStrData = TW::Data(inputString.begin(), inputString.end()); - - // Pre-hash the transaction. - - auto preImageHashesData = TransactionCompiler::preImageHashes(TWCoinTypeAptos, inputStrData); - TxCompiler::Proto::PreSigningOutput preSigningOutput; - preSigningOutput.ParseFromArray(preImageHashesData.data(), static_cast(preImageHashesData.size())); - auto actualDataToSign = data(preSigningOutput.data()); - - EXPECT_EQ(preSigningOutput.error(), Common::Proto::OK); - EXPECT_EQ(actualDataToSign, parse_hex(anyEncoded)); - - // Sign the pre-hash data. - - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519).bytes; - auto signature = privateKey.sign(actualDataToSign, TWCurveED25519); - EXPECT_EQ(hex(signature), "9e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b"); - - // Compile the transaction. - - auto outputData = TransactionCompiler::compileWithSignatures(TWCoinTypeAptos, inputStrData, {signature}, {publicKey}); - Proto::SigningOutput output; - output.ParseFromArray(outputData.data(), static_cast(outputData.size())); - - EXPECT_EQ(output.error(), Common::Proto::OK); - ASSERT_EQ(hex(output.raw_txn()), "b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300200000000000000024633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b29883704706f6f6c0b737761705f795f746f5f780207deae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e010c6465766e65745f636f696e730a4465766e657455534454000700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00020800e1f50500000000080000000000000000fe4d3200000000006400000000000000c2276ada0000000021"); - ASSERT_EQ(hex(output.authenticator().signature()), "9e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b"); - ASSERT_EQ(hex(output.encoded()), "b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300200000000000000024633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b29883704706f6f6c0b737761705f795f746f5f780207deae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e010c6465766e65745f636f696e730a4465766e657455534454000700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00020800e1f50500000000080000000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c409e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b"); - nlohmann::json expectedJson = R"( - { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0x9e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b", - "type": "ed25519_signature" - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(output.json()); - assertJSONEqual(expectedJson, parsedJson); -} - } diff --git a/tests/chains/Aptos/MoveTypesTests.cpp b/tests/chains/Aptos/MoveTypesTests.cpp deleted file mode 100644 index 06ef076991c..00000000000 --- a/tests/chains/Aptos/MoveTypesTests.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include -#include -#include - -namespace TW::Aptos::tests { - -TEST(AptosMoveTypes, ModuleId) { - ModuleId module(Address::one(), "coin"); - ASSERT_EQ(module.address(), Address::one()); - ASSERT_EQ(module.name(), "coin"); - ASSERT_EQ(hex(module.accessVector()), "00000000000000000000000000000000000000000000000000000000000000000104636f696e"); - ASSERT_EQ(module.string(), "0x0000000000000000000000000000000000000000000000000000000000000001::coin"); - ASSERT_EQ(module.shortString(), "0x1::coin"); -} - -TEST(AptosMoveTypes, StructTag) { - auto functorTest = [](T value, const std::string expectedHex) { - TypeTag t{.tags = value}; - StructTag st(Address::one(), "abc", "abc", std::vector{{t}}); - ASSERT_EQ(st.moduleID().name(), "abc"); - ASSERT_EQ(st.moduleID().address(), Address::one()); - ASSERT_EQ(hex(st.serialize()), expectedHex); - }; - functorTest(Bool{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630100"); - functorTest(U8{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630101"); - functorTest(U64{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630102"); - functorTest(U128{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630103"); - functorTest(TAddress{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630104"); - functorTest(TSigner{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630105"); - functorTest(Vector{.tags = std::vector{{TypeTag{.tags = U8{}}}}}, "0100000000000000000000000000000000000000000000000000000000000000010361626303616263010601"); - StructTag stInner(Address::one(), "foo", "bar", std::vector{{U8{}}}); - functorTest(TStructTag{stInner}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630107000000000000000000000000000000000000000000000000000000000000000103666f6f036261720101"); -} - -TEST(AptosMoveTypes, TypeTagDisplay) { - auto functorTest = [](const TypeTag &value, const std::string& expected) { - ASSERT_EQ(TypeTagToString(value), expected); - }; - functorTest(TypeTag{.tags = Bool{}}, "bool"); - functorTest(TypeTag{.tags = U8{}}, "u8"); - functorTest(TypeTag{.tags = U64{}}, "u64"); - functorTest(TypeTag{.tags = U128{}}, "u128"); - functorTest(TypeTag{.tags = TAddress{}}, "address"); - functorTest(TypeTag{.tags = TSigner{}}, "signer"); - TypeTag t{.tags = TypeTag::TypeTagVariant(Vector{.tags = {{U8{}}}})}; - functorTest(t, "vector"); - StructTag st(Address::one(), "foo", "bar", std::vector{{U8{}}}); - TypeTag anotherT{.tags = TypeTag::TypeTagVariant(st)}; - functorTest(anotherT, "0x1::foo::bar"); - functorTest(gTransferTag, "0x1::aptos_coin::AptosCoin"); -} - -} // namespace TW::Aptos::tests diff --git a/tests/chains/Aptos/SignerTests.cpp b/tests/chains/Aptos/SignerTests.cpp deleted file mode 100644 index 65c0bc87089..00000000000 --- a/tests/chains/Aptos/SignerTests.cpp +++ /dev/null @@ -1,730 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Aptos/Address.h" -#include "Aptos/Signer.h" -#include "HexCoding.h" -#include "PrivateKey.h" -#include "PublicKey.h" -#include "TestUtilities.h" -#include - -#include - -namespace TW::Aptos::tests { - -TEST(AptosSigner, ClaimNftTxSign) { - // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x60b51e15140ec0b7650334e948fb447ce3cb13ae63492260461ebfa9d02e85c4?network=testnet - Proto::SigningInput input; - input.set_sender("0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - input.set_sequence_number(19); - auto& tf = *input.mutable_nft_message()->mutable_claim_nft(); - tf.set_sender("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee"); - tf.set_creator("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac"); - tf.set_collectionname("Topaz Troopers"); - tf.set_name("Topaz Trooper #20068"); - tf.set_property_version(0); - input.set_max_gas_amount(3296766); - input.set_gas_unit_price(100); - input.set_expiration_timestamp_secs(3664390082); - input.set_chain_id(2); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002"); - ASSERT_EQ(hex(result.authenticator().signature()), "ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a"); - ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a"); - nlohmann::json expectedJson = R"( - { - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "3296766", - "payload": { - "arguments": [ - "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", - "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", - "Topaz Troopers", "Topaz Trooper #20068", "0"], - "function": "0x3::token_transfers::claim_script", - "type": "entry_function_payload", - "type_arguments": [] - }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "19", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0xede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, NftOfferTxSign) { - // Successfully broadcasted https://explorer.aptoslabs.com/txn/0x514e473618bd3cb89a2b110b7c473db9a2e10532f98eb42d02d86fb31c00525d?network=testnet - Proto::SigningInput input; - input.set_sender("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee"); - input.set_sequence_number(1); - auto& tf = *input.mutable_nft_message()->mutable_offer_nft(); - tf.set_receiver("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - tf.set_creator("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac"); - tf.set_collectionname("Topaz Troopers"); - tf.set_name("Topaz Trooper #20068"); - tf.set_property_version(0); - tf.set_amount(1); - input.set_max_gas_amount(3296766); - input.set_gas_unit_price(100); - input.set_expiration_timestamp_secs(3664390082); - input.set_chain_id(2); - auto privateKey = PrivateKey(parse_hex("7bebb6d543d17f6fe4e685cfab239fa37896edd594ff859f1df32f244fb707e2")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada0000000002"); - ASSERT_EQ(hex(result.authenticator().signature()), "af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f"); - ASSERT_EQ(hex(result.encoded()), "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada00000000020020d1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a411340af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f"); - nlohmann::json expectedJson = R"( - { - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "3296766", - "payload": { - "arguments": [ - "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", - "Topaz Troopers", "Topaz Trooper #20068", "0", "1"], - "function": "0x3::token_transfers::offer_script", - "type": "entry_function_payload", - "type_arguments": [] - }, - "sender": "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", - "sequence_number": "1", - "signature": { - "public_key": "0xd1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a4113", - "signature": "0xaf5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, CancelNftOfferTxSign) { - // Successfully broadcasted https://explorer.aptoslabs.com/txn/0x0b8c64e6847c368e4c6bd2cce0e9eab378971b0ef2e3bc40cbd292910a80201d?network=testnet - Proto::SigningInput input; - input.set_sender("0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - input.set_sequence_number(21); - auto& tf = *input.mutable_nft_message()->mutable_cancel_offer_nft(); - tf.set_receiver("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee"); - tf.set_creator("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac"); - tf.set_collectionname("Topaz Troopers"); - tf.set_name("Topaz Trooper #20068"); - tf.set_property_version(0); - input.set_max_gas_amount(3296766); - input.set_gas_unit_price(100); - input.set_expiration_timestamp_secs(3664390082); - input.set_chain_id(2); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002"); - ASSERT_EQ(hex(result.authenticator().signature()), "826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a"); - ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a"); - nlohmann::json expectedJson = R"( - { - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "3296766", - "payload": { - "arguments": [ - "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", - "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", - "Topaz Troopers", "Topaz Trooper #20068", "0"], - "function": "0x3::token_transfers::cancel_offer_script", - "type": "entry_function_payload", - "type_arguments": [] - }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "21", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0x826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, TxSign) { - // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb4d62afd3862116e060dd6ad9848ccb50c2bc177799819f1d29c059ae2042467?network=devnet - Proto::SigningInput input; - input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - input.set_sequence_number(99); - auto& tf = *input.mutable_transfer(); - tf.set_to("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - tf.set_amount(1000); - input.set_max_gas_amount(3296766); - input.set_gas_unit_price(100); - input.set_expiration_timestamp_secs(3664390082); - input.set_chain_id(33); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021"); - ASSERT_EQ(hex(result.authenticator().signature()), "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"); - ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"); - nlohmann::json expectedJson = R"( - { - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "3296766", - "payload": { - "arguments": ["0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], - "function": "0x1::aptos_account::transfer", - "type": "entry_function_payload", - "type_arguments": [] - }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "99", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0x5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, CreateAccount) { - // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x477141736de6b0936a6c3734e4d6fd018c7d21f1f28f99028ef0bc6881168602?network=Devnet - Proto::SigningInput input; - input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - input.set_sequence_number(0); - auto& tf = *input.mutable_create_account(); - tf.set_auth_key("0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e"); - input.set_max_gas_amount(3296766); - input.set_gas_unit_price(100); - input.set_expiration_timestamp_secs(3664390082); - input.set_chain_id(33); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada0000000021"); - ASSERT_EQ(hex(result.authenticator().signature()), "fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501"); - ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501"); - nlohmann::json expectedJson = R"( - { - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "3296766", - "payload": { - "arguments": ["0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e"], - "function": "0x1::aptos_account::create_account", - "type": "entry_function_payload", - "type_arguments": [] - }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "0", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0xfcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, BlindSignFromJson) { - // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x7efd69e7f9462774b932ce500ab51c0d0dcc004cf272e09f8ffd5804c2a84e33?network=mainnet - auto payloadJson = R"( - { - "function": "0x16fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c::AnimeSwapPoolV1::swap_exact_coins_for_coins_3_pair_entry", - "type_arguments": [ - "0x1::aptos_coin::AptosCoin", - "0x881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f4::coin::MOJO", - "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDT", - "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC" - ], - "arguments": [ - "1000000", - "49329" - ], - "type": "entry_function_payload" - })"_json; - Proto::SigningInput input; - input.set_sequence_number(42); - input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - input.set_gas_unit_price(100); - input.set_max_gas_amount(100011); - input.set_expiration_timestamp_secs(3664390082); - input.set_any_encoded(payloadJson.dump()); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_chain_id(1); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f302a000000000000000216fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c0f416e696d6553776170506f6f6c563127737761705f65786163745f636f696e735f666f725f636f696e735f335f706169725f656e747279040700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e0007881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f404636f696e044d4f4a4f0007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa05617373657404555344540007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa056173736574045553444300020840420f000000000008b1c0000000000000ab860100000000006400000000000000c2276ada0000000001"); - ASSERT_EQ(hex(result.authenticator().signature()), "42cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b"); - ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f302a000000000000000216fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c0f416e696d6553776170506f6f6c563127737761705f65786163745f636f696e735f666f725f636f696e735f335f706169725f656e747279040700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e0007881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f404636f696e044d4f4a4f0007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa05617373657404555344540007f22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa056173736574045553444300020840420f000000000008b1c0000000000000ab860100000000006400000000000000c2276ada00000000010020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c4042cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b"); - nlohmann::json expectedJson = R"( -{ - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "100011", - "payload": { - "function": "0x16fe2df00ea7dde4a63409201f7f4e536bde7bb7335526a35d05111e68aa322c::AnimeSwapPoolV1::swap_exact_coins_for_coins_3_pair_entry", - "type_arguments": [ - "0x1::aptos_coin::AptosCoin", - "0x881ac202b1f1e6ad4efcff7a1d0579411533f2502417a19211cfc49751ddb5f4::coin::MOJO", - "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDT", - "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC" - ], - "arguments": [ - "1000000", - "49329" - ], - "type": "entry_function_payload" - }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "42", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0x42cd67406e85afd1e948e7ad7f5f484fb4c60d82b267c6b6b28a92301e228b983206d2b87cd5487cf9acfb0effbd183ab90123570eb2e047cb152d337152210b", - "type": "ed25519_signature" - } -} - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, TortugaLiquidStakingStake) { - // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x25dca849cb4ebacbff223139f7ad5d24c37c225d9506b8b12a925de70429e685/userTxnOverview?network=mainnet - Proto::SigningInput input; - input.set_sender("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc"); - input.set_sequence_number(19); - auto& ls = *input.mutable_liquid_staking_message(); - ls.set_smart_contract_address("0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f"); - auto& ls_stake = *ls.mutable_stake(); - ls_stake.set_amount(100000000); - input.set_max_gas_amount(5554); - input.set_gas_unit_price(100); - input.set_expiration_timestamp_secs(1670240203); - input.set_chain_id(1); - auto privateKey = PrivateKey(parse_hex("786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - - EXPECT_EQ(hex(result.raw_txn()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1300000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000b2150000000000006400000000000000cbd78d630000000001"); - EXPECT_EQ(hex(result.authenticator().signature()), "22d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d"); - EXPECT_EQ(hex(result.encoded()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1300000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000b2150000000000006400000000000000cbd78d630000000001002089e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc44022d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d"); - nlohmann::json expectedJson = R"( - { - "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", - "sequence_number": "19", - "max_gas_amount": "5554", - "gas_unit_price": "100", - "expiration_timestamp_secs": "1670240203", - "payload": { - "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::stake", - "type_arguments": [], - "arguments": [ - "100000000" - ], - "type": "entry_function_payload" - }, - "signature": { - "public_key": "0x89e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4", - "signature": "0x22d3166c3003f9c24a35fd39c71eb27e0d2bb82541be610822165c9283f56fefe5a9d46421b9caf174995bd8f83141e60ea8cff521ecf4741fe19e6ae9a5680d", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, TortugaLiquidStakingUnstake) { - // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x92edb4f756fe86118e34a0e64746c70260ee02c2ae2cf402b3e39f6a282ce968?network=mainnet - Proto::SigningInput input; - input.set_sender("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc"); - input.set_sequence_number(20); - auto& ls = *input.mutable_liquid_staking_message(); - ls.set_smart_contract_address("0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f"); - auto& ls_unstake = *ls.mutable_unstake(); - ls_unstake.set_amount(99178100); - input.set_max_gas_amount(2371); - input.set_gas_unit_price(120); - input.set_expiration_timestamp_secs(1670304949); - input.set_chain_id(1); - auto privateKey = PrivateKey(parse_hex("786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - - EXPECT_EQ(hex(result.raw_txn()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1400000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e9050000000043090000000000007800000000000000b5d48e630000000001"); - EXPECT_EQ(hex(result.authenticator().signature()), "6994b917432ad70ae84d2ce1484e6aece589a68aad1b7c6e38c9697f2a012a083a3a755c5e010fd3d0f149a75dd8d257acbd09f10800e890074e5ad384314d0c"); - EXPECT_EQ(hex(result.encoded()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1400000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e9050000000043090000000000007800000000000000b5d48e630000000001002089e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4406994b917432ad70ae84d2ce1484e6aece589a68aad1b7c6e38c9697f2a012a083a3a755c5e010fd3d0f149a75dd8d257acbd09f10800e890074e5ad384314d0c"); - nlohmann::json expectedJson = R"( - { - "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", - "sequence_number": "20", - "max_gas_amount": "2371", - "gas_unit_price": "120", - "expiration_timestamp_secs": "1670304949", - "payload": { - "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::unstake", - "type_arguments": [], - "arguments": [ - "99178100" - ], - "type": "entry_function_payload" - }, - "signature": { - "public_key": "0x89e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4", - "signature": "0x6994b917432ad70ae84d2ce1484e6aece589a68aad1b7c6e38c9697f2a012a083a3a755c5e010fd3d0f149a75dd8d257acbd09f10800e890074e5ad384314d0c", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, TortugaLiquidStakingClaim) { - // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x9fc874de7a7d3e813d9a1658d896023de270a0096a5e258c196005656ace7d54?network=mainnet - Proto::SigningInput input; - input.set_sender("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc"); - input.set_sequence_number(28); - auto& ls = *input.mutable_liquid_staking_message(); - ls.set_smart_contract_address("0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f"); - auto& ls_claim = *ls.mutable_claim(); - ls_claim.set_idx(0); - input.set_max_gas_amount(10); - input.set_gas_unit_price(148); - input.set_expiration_timestamp_secs(1682066783); - input.set_chain_id(1); - auto privateKey = PrivateKey(parse_hex("786fc7ceca43b4c1da018fea5d96f35dfdf5605f220b1205ff29c5c6d9eccf05")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - - EXPECT_EQ(hex(result.raw_txn()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657205636c61696d00010800000000000000000a0000000000000094000000000000005f4d42640000000001"); - EXPECT_EQ(hex(result.authenticator().signature()), "c936584f89777e1fe2d5dd75cd8d9c514efc445810ba22f462b6fe7229c6ec7fc1c8b25d3e233eafaa8306433b3220235e563498ba647be38cac87ff618e3d03"); - EXPECT_EQ(hex(result.encoded()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc1c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657205636c61696d00010800000000000000000a0000000000000094000000000000005f4d42640000000001002089e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc440c936584f89777e1fe2d5dd75cd8d9c514efc445810ba22f462b6fe7229c6ec7fc1c8b25d3e233eafaa8306433b3220235e563498ba647be38cac87ff618e3d03"); - nlohmann::json expectedJson = R"( - { - "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", - "sequence_number": "28", - "max_gas_amount": "10", - "gas_unit_price": "148", - "expiration_timestamp_secs": "1682066783", - "payload": { - "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::claim", - "type_arguments": [], - "arguments": [ - "0" - ], - "type": "entry_function_payload" - }, - "signature": { - "public_key": "0x89e0211d7e19c7d3a8e2030fe16c936a690ca9b95569098c5d2bf1031ff44bc4", - "signature": "0xc936584f89777e1fe2d5dd75cd8d9c514efc445810ba22f462b6fe7229c6ec7fc1c8b25d3e233eafaa8306433b3220235e563498ba647be38cac87ff618e3d03", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, BlindSignStaking) { - // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x25dca849cb4ebacbff223139f7ad5d24c37c225d9506b8b12a925de70429e685/payload - auto payloadJson = R"( - { - "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::stake", - "type_arguments": [], - "arguments": [ - "100000000" - ], - "type": "entry_function_payload" - })"_json; - - Proto::SigningInput input; - input.set_sequence_number(43); - input.set_sender("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc"); - input.set_gas_unit_price(100); - input.set_max_gas_amount(100011); - input.set_expiration_timestamp_secs(3664390082); - input.set_any_encoded(payloadJson.dump()); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_chain_id(1); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2b00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000ab860100000000006400000000000000c2276ada0000000001"); - ASSERT_EQ(hex(result.authenticator().signature()), "a41b7440a50f36e8491319508734acb55488abc6d88fbc9cb2b37ba23210f01f5d08c856cb7abf18c414cf9302ee144450bd99495a7e21e61f624764db91eb0b"); - ASSERT_EQ(hex(result.encoded()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2b00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f75746572057374616b6500010800e1f50500000000ab860100000000006400000000000000c2276ada00000000010020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40a41b7440a50f36e8491319508734acb55488abc6d88fbc9cb2b37ba23210f01f5d08c856cb7abf18c414cf9302ee144450bd99495a7e21e61f624764db91eb0b"); - nlohmann::json expectedJson = R"( -{ - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "100011", - "payload": { - "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::stake", - "type_arguments": [], - "arguments": [ - "100000000" - ], - "type": "entry_function_payload" - }, - "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", - "sequence_number": "43", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0xa41b7440a50f36e8491319508734acb55488abc6d88fbc9cb2b37ba23210f01f5d08c856cb7abf18c414cf9302ee144450bd99495a7e21e61f624764db91eb0b", - "type": "ed25519_signature" - } -} - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, BlindSignUnStaking) { - // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x92edb4f756fe86118e34a0e64746c70260ee02c2ae2cf402b3e39f6a282ce968/payload - auto payloadJson = R"( - { - "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::unstake", - "type_arguments": [], - "arguments": [ - "99178100" - ], - "type": "entry_function_payload" - })"_json; - Proto::SigningInput input; - input.set_sequence_number(44); - input.set_sender("0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc"); - input.set_gas_unit_price(100); - input.set_max_gas_amount(100011); - input.set_expiration_timestamp_secs(3664390082); - input.set_any_encoded(payloadJson.dump()); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_chain_id(1); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e90500000000ab860100000000006400000000000000c2276ada0000000001"); - ASSERT_EQ(hex(result.authenticator().signature()), "a58ad5e3331beb8c0212a18a1f932207cb664b78f5aad3cb1fe7435e0e0e053247ce49b38fd67b064bed34ed643eb6a03165d77c681d7d73ac3161ab984a960a"); - ASSERT_EQ(hex(result.encoded()), "f3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc2c00000000000000028f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f0c7374616b655f726f7574657207756e7374616b650001087456e90500000000ab860100000000006400000000000000c2276ada00000000010020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40a58ad5e3331beb8c0212a18a1f932207cb664b78f5aad3cb1fe7435e0e0e053247ce49b38fd67b064bed34ed643eb6a03165d77c681d7d73ac3161ab984a960a"); - nlohmann::json expectedJson = R"( -{ - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "100011", - "payload": { - "function": "0x8f396e4246b2ba87b51c0739ef5ea4f26515a98375308c31ac2ec1e42142a57f::stake_router::unstake", - "type_arguments": [], - "arguments": [ - "99178100" - ], - "type": "entry_function_payload" - }, - "sender": "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", - "sequence_number": "44", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0xa58ad5e3331beb8c0212a18a1f932207cb664b78f5aad3cb1fe7435e0e0e053247ce49b38fd67b064bed34ed643eb6a03165d77c681d7d73ac3161ab984a960a", - "type": "ed25519_signature" - } -} - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - - -TEST(AptosSigner, BlindSign) { - // successfully broadcasted https://explorer.aptoslabs.com/txn/0xd95857a9e644528708778a3a0a6e13986751944fca30eaac98853c1655de0422?network=Devnet - // encoded submission with: - // curl --location --request POST 'https://fullnode.devnet.aptoslabs.com/v1/transactions/encode_submission' \ - //--header 'Content-Type: application/json' \ - //--header 'Cookie: AWSALB=0zI2zWypvEr0I3sGM6vnyHSxYO1D0aaMXfyA/2VwhA291aJJ80Yz67Fur50sXPFBI8dKKID4p8DShj1KkEXPY/NGAylpOj1EG2M2Qjuu1B38Q5C+dZW2CHT+IAZ5; AWSALBCORS=0zI2zWypvEr0I3sGM6vnyHSxYO1D0aaMXfyA/2VwhA291aJJ80Yz67Fur50sXPFBI8dKKID4p8DShj1KkEXPY/NGAylpOj1EG2M2Qjuu1B38Q5C+dZW2CHT+IAZ5' \ - //--data-raw '{ - // "expiration_timestamp_secs": "3664390082", - // "gas_unit_price": "100", - // "max_gas_amount": "3296766", - // "payload": { - // "function": "0x4633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b298837::pool::swap_y_to_x", - // "type_arguments": [ - // "0xdeae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e01::devnet_coins::DevnetUSDT", - // "0x1::aptos_coin::AptosCoin" - // ], - // "arguments": [ - // "100000000", - // "0" - // ], - // "type": "entry_function_payload" - // }, - // "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - // "sequence_number": "2" - //}' - Proto::SigningInput input; - input.set_any_encoded("0xb5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300200000000000000024633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b29883704706f6f6c0b737761705f795f746f5f780207deae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e010c6465766e65745f636f696e730a4465766e657455534454000700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00020800e1f50500000000080000000000000000fe4d3200000000006400000000000000c2276ada0000000021"); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300200000000000000024633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b29883704706f6f6c0b737761705f795f746f5f780207deae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e010c6465766e65745f636f696e730a4465766e657455534454000700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00020800e1f50500000000080000000000000000fe4d3200000000006400000000000000c2276ada0000000021"); - ASSERT_EQ(hex(result.authenticator().signature()), "9e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b"); - ASSERT_EQ(hex(result.encoded()), "b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300200000000000000024633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b29883704706f6f6c0b737761705f795f746f5f780207deae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e010c6465766e65745f636f696e730a4465766e657455534454000700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00020800e1f50500000000080000000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c409e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b"); - nlohmann::json expectedJson = R"( - { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0x9e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b", - "type": "ed25519_signature" - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, TokenRegisterTxSign) { - // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xe591252daed785641bfbbcf72a5d17864568cf32e04c0cc9129f3a13834d0e8e?network=testnet - Proto::SigningInput input; - input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - input.set_sequence_number(23); - auto& tf = *input.mutable_register_token(); - tf.mutable_function()->set_account_address("0xe4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379"); - tf.mutable_function()->set_module("move_coin"); - tf.mutable_function()->set_name("MoveCoin"); - input.set_max_gas_amount(2000000); - input.set_gas_unit_price(100); - input.set_expiration_timestamp_secs(3664390082); - input.set_chain_id(2); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada0000000002"); - ASSERT_EQ(hex(result.authenticator().signature()), "e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100"); - ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100"); - nlohmann::json expectedJson = R"( - { - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "2000000", - "payload": { - "arguments": [], - "function": "0x1::managed_coin::register", - "type": "entry_function_payload", - "type_arguments": ["0xe4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379::move_coin::MoveCoin"] - }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "23", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0xe230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, TokenTxSign) { - // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb5b383a5c7f99b2edb3bed9533f8169a89051b149d65876a82f4c0b9bf78a15b?network=Devnet - Proto::SigningInput input; - input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - input.set_sequence_number(24); - auto& tf = *input.mutable_token_transfer(); - tf.set_to("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); - tf.set_amount(100000); - tf.mutable_function()->set_account_address("0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9"); - tf.mutable_function()->set_module("coins"); - tf.mutable_function()->set_name("BTC"); - input.set_max_gas_amount(3296766); - input.set_gas_unit_price(100); - input.set_expiration_timestamp_secs(3664390082); - input.set_chain_id(32); - auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada0000000020"); - ASSERT_EQ(hex(result.authenticator().signature()), "7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a"); - ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c407643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a"); - nlohmann::json expectedJson = R"( - { - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "3296766", - "payload": { - "arguments": ["0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","100000"], - "function": "0x1::coin::transfer", - "type": "entry_function_payload", - "type_arguments": ["0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC"] - }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", - "sequence_number": "24", - "signature": { - "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", - "signature": "0x7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -TEST(AptosSigner, TokenTransferCoins) { - // Successfully broadcasted https://explorer.aptoslabs.com/txn/0x197d40ea12e2bfc65a0a913b9f4ca3b0b0208fe0c1514d3d55cef3d5bcf25211?network=mainnet - Proto::SigningInput input; - input.set_sender("0x1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf25"); - input.set_sequence_number(2); - auto& tf = *input.mutable_token_transfer_coins(); - tf.set_to("0xb7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c"); - tf.set_amount(10000); - tf.mutable_function()->set_account_address("0xe9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9"); - tf.mutable_function()->set_module("mee_coin"); - tf.mutable_function()->set_name("MeeCoin"); - input.set_max_gas_amount(2000); - input.set_gas_unit_price(100); - input.set_expiration_timestamp_secs(3664390082); - input.set_chain_id(1); - auto privateKey = PrivateKey(parse_hex("e7f56c77189e03699a75d8ec5c090e41f3d9d4783bc49c33df8a93d915e10de8")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - - ASSERT_EQ(hex(result.raw_txn()), "1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001"); - ASSERT_EQ(hex(result.authenticator().signature()), "30ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d"); - ASSERT_EQ(hex(result.encoded()), "1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001002062e7a6a486553b56a53e89dfae3f780693e537e5b0a7ed33290780e581ca83694030ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d"); - nlohmann::json expectedJson = R"( - { - "expiration_timestamp_secs": "3664390082", - "gas_unit_price": "100", - "max_gas_amount": "2000", - "payload": { - "arguments": ["0xb7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c","10000"], - "function": "0x1::aptos_account::transfer_coins", - "type": "entry_function_payload", - "type_arguments": ["0xe9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9::mee_coin::MeeCoin"] - }, - "sender": "0x1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf25", - "sequence_number": "2", - "signature": { - "public_key": "0x62e7a6a486553b56a53e89dfae3f780693e537e5b0a7ed33290780e581ca8369", - "signature": "0x30ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d", - "type": "ed25519_signature" - } - } - )"_json; - nlohmann::json parsedJson = nlohmann::json::parse(result.json()); - assertJSONEqual(expectedJson, parsedJson); -} - -} // namespace TW::Aptos::tests diff --git a/tests/chains/Aptos/TWAnySignerTests.cpp b/tests/chains/Aptos/TWAnySignerTests.cpp index 28f260970b6..44885c9f872 100644 --- a/tests/chains/Aptos/TWAnySignerTests.cpp +++ b/tests/chains/Aptos/TWAnySignerTests.cpp @@ -4,11 +4,10 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "Aptos/Address.h" -#include "Aptos/Signer.h" #include "HexCoding.h" #include "PrivateKey.h" #include "PublicKey.h" +#include "proto/Aptos.pb.h" #include #include #include "TestUtilities.h" @@ -42,12 +41,12 @@ TEST(TWAnySignerAptos, TxSign) { "gas_unit_price": "100", "max_gas_amount": "3296766", "payload": { - "arguments": ["0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], + "arguments": ["0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], "function": "0x1::aptos_account::transfer", "type": "entry_function_payload", "type_arguments": [] }, - "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sender": "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", "sequence_number": "99", "signature": { "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", diff --git a/tests/chains/Aptos/TWAptosAddressTests.cpp b/tests/chains/Aptos/TWAptosAddressTests.cpp index 2b9e3fd8047..ec6612d0960 100644 --- a/tests/chains/Aptos/TWAptosAddressTests.cpp +++ b/tests/chains/Aptos/TWAptosAddressTests.cpp @@ -28,7 +28,7 @@ TEST(TWAptosAddress, HDWallet) { auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeAptos)); auto addressStr = WRAPS(TWAnyAddressDescription(address.get())); - assertStringsEqual(addressStr, "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + assertStringsEqual(addressStr, "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); } } // namespace TW::Aptos::tests diff --git a/tests/chains/Aptos/TransactionPayloadTests.cpp b/tests/chains/Aptos/TransactionPayloadTests.cpp deleted file mode 100644 index 45b501230c3..00000000000 --- a/tests/chains/Aptos/TransactionPayloadTests.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include -#include -#include - -namespace TW::Aptos::tests { - -TEST(AptosTransactionPayload, PancakeSwapPayload) { - auto pancakeSwapPayload=R"( - { -"arguments": [ - "0xc95db29a67a848940829b3df6119b5e67b788ff0248676e4484c7c6f29c0f5e6" -], -"function": "0xc23c3b70956ce8d88fb18ad9ed3b463fe873cb045db3f6d2e2fb15b9aab71d50::IFO::release", -"type": "entry_function_payload", -"type_arguments": [ - "0x48e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced90517::coins::BUSD", - "0x48e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced90517::coins::DAI", - "0x9936836587ca33240d3d3f91844651b16cb07802faf5e34514ed6f78580deb0a::uints::U1" -] -} -)"_json; - - TransactionPayload payload = EntryFunction::from_json(pancakeSwapPayload); - BCS::Serializer serializer; - Address sender("0x2ce519d8cd60e0870e874e8000e8cbc87c8172e6acdbec83662b4c8cc3fc3de9"); - std::uint64_t sequenceNumber{75}; - std::uint64_t gasAmount{488130}; - std::uint64_t gasPrice{100}; - std::uint64_t expirationTime{199940521552}; - std::uint8_t chainId{1}; - serializer << sender << sequenceNumber << payload << gasAmount << gasPrice << expirationTime << chainId; - ASSERT_EQ(hex(serializer.bytes), "2ce519d8cd60e0870e874e8000e8cbc87c8172e6acdbec83662b4c8cc3fc3de94b0000000000000002c23c3b70956ce8d88fb18ad9ed3b463fe873cb045db3f6d2e2fb15b9aab71d500349464f0772656c65617365030748e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced9051705636f696e730442555344000748e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced9051705636f696e730344414900079936836587ca33240d3d3f91844651b16cb07802faf5e34514ed6f78580deb0a0575696e747302553100012120c95db29a67a848940829b3df6119b5e67b788ff0248676e4484c7c6f29c0f5e6c2720700000000006400000000000000503e628d2e00000001"); -} - -TEST(AptosTransactionPayload, PayLoadBasis) { - ModuleId module(Address::one(), "coin"); - std::uint64_t amount{1000}; - Address to("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b"); - BCS::Serializer serializer; - serializer << to; - std::vector args; - args.emplace_back(serializer.bytes); - serializer.clear(); - serializer << amount; - args.emplace_back(serializer.bytes); - TransactionPayload payload = EntryFunction(module, "transfer", {gTransferTag}, args); - ASSERT_EQ(std::get(payload).module().name(), "coin"); - ASSERT_EQ(std::get(payload).module().shortString(), "0x1::coin"); - serializer.clear(); - serializer << payload; - ASSERT_EQ(hex(serializer.bytes), "02000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b08e803000000000000"); -} - -} // namespace TW::Aptos::tests diff --git a/tests/common/BCSTests.cpp b/tests/common/BCSTests.cpp deleted file mode 100644 index f12f8242612..00000000000 --- a/tests/common/BCSTests.cpp +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// Created by Clément Doumergue -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "BCS.h" -#include "HexCoding.h" - -#include - -namespace TW::BCS::tests { - -TEST(BCS, Integral) { - Serializer os; - os << uint32_t(0xAABBCCDD); - ASSERT_EQ(os.bytes, parse_hex("0xDDCCBBAA")); - - os.clear(); - os << int32_t(-305419896); - ASSERT_EQ(os.bytes, parse_hex("0x88A9CBED")); -} - -TEST(BCS, ULEB128) { - Serializer os; - os << uleb128{0x00000001}; - ASSERT_EQ(os.bytes, parse_hex("0x01")); - - os.clear(); - os << uleb128{0x00000080}; - ASSERT_EQ(os.bytes, parse_hex("0x8001")); - - os.clear(); - os << uleb128{0x00004000}; - ASSERT_EQ(os.bytes, parse_hex("0x808001")); - - os.clear(); - os << uleb128{0x00200000}; - ASSERT_EQ(os.bytes, parse_hex("0x80808001")); - - os.clear(); - os << uleb128{0x10000000}; - ASSERT_EQ(os.bytes, parse_hex("0x8080808001")); - - os.clear(); - os << uleb128{0x0000250F}; - ASSERT_EQ(os.bytes, parse_hex("0x8F4A")); -} - -TEST(BCS, String) { - Serializer os; - os << std::string_view("abcd"); - ASSERT_EQ(os.bytes, parse_hex("0x0461626364")); - - os.clear(); - os << std::string_view(""); - ASSERT_EQ(os.bytes, parse_hex("0x00")); -} - -TEST(BCS, Optional) { - Serializer os; - os << std::optional{0xBBCCDD}; - ASSERT_EQ(os.bytes, parse_hex("0x01DDCCBB00")); - - os.clear(); - os << std::optional{}; - ASSERT_EQ(os.bytes, parse_hex("0x00")); - - os.clear(); - os << std::nullopt; - ASSERT_EQ(os.bytes, parse_hex("0x00")); -} - -TEST(BCS, Tuple) { - Serializer os; - os << std::tuple{uint16_t(1), 'a'}; - ASSERT_EQ(os.bytes, parse_hex("0x010061")); - - os.clear(); - os << std::tuple{std::optional{123}, std::string_view("abcd"), uint8_t(0x0E)}; - ASSERT_EQ(os.bytes, parse_hex("0x017b00000004616263640e")); - - os.clear(); - os << std::tuple{}; - ASSERT_EQ(os.bytes, (Data{})); -} - -TEST(BCS, Pair) { - Serializer os; - os << std::pair{uint16_t(1), 'a'}; - ASSERT_EQ(os.bytes, parse_hex("0x010061")); - - os.clear(); - os << std::pair{std::optional{123}, std::string_view("abcd")}; - ASSERT_EQ(os.bytes, parse_hex("0x017b0000000461626364")); -} - -struct my_struct { - std::optional first; - std::string_view second; - uint8_t third; -}; - -TEST(BCS, Struct) { - Serializer os; - os << my_struct{{123}, "abcd", 0x0E}; - ASSERT_EQ(os.bytes, parse_hex("0x017b00000004616263640e")); -} - -TEST(BCS, Variant) { - using V = std::variant; - - Serializer os; - os << V{uint32_t(1)}; - ASSERT_EQ(os.bytes, parse_hex("0x0001000000")); - - os.clear(); - os << V{char('a')}; - ASSERT_EQ(os.bytes, parse_hex("0x0161")); - - os.clear(); - os << V{true}; - ASSERT_EQ(os.bytes, parse_hex("0x0201")); -} - -TEST(BCS, Map) { - Serializer os; - os << std::map{{'a', 0}, {'b', 1}, {'c', 2}}; - ASSERT_EQ(os.bytes, parse_hex("0x03610062016302")); -} - -class my_number { -private: - int value; - -public: - explicit my_number(int value) noexcept - : value(value) { - } - - [[nodiscard]] auto get_value() const { - return value; - } -}; - -Serializer& operator<<(Serializer& stream, my_number n) noexcept { - return stream << n.get_value(); -} - -static_assert(CustomSerializable, "my_number does not model the CustomSerializable concept"); - -TEST(BCS, Custom) { - Serializer os; - os << my_number{0xBBCCDD}; - ASSERT_EQ(os.bytes, parse_hex("0xDDCCBB00")); -} - -TEST(BCS, Vector) { - Serializer os; - os << std::vector{1}; - ASSERT_EQ(os.bytes, parse_hex("0101")); -} - -} diff --git a/tests/common/rust/bindgen/WalletCoreRsTests.cpp b/tests/common/rust/bindgen/WalletCoreRsTests.cpp index 402fbfb6050..bd5cd298a41 100644 --- a/tests/common/rust/bindgen/WalletCoreRsTests.cpp +++ b/tests/common/rust/bindgen/WalletCoreRsTests.cpp @@ -13,15 +13,6 @@ #include "proto/Polkadot.pb.h" #include "uint256.h" -TEST(RustBindgen, MoveParseFunctionArgument) { - using namespace TW; - std::string arg = "10000000"; - auto str_result = Rust::parse_function_argument_to_bcs(arg.c_str()); - ASSERT_EQ(str_result.code, Rust::OK_CODE); - ASSERT_EQ(std::string(str_result.result), "8096980000000000"); - Rust::free_string(str_result.result); -} - TEST(RustBindgen, EthSigningMessageProto) { using namespace TW; From a60033f797e33628e557af7c66be539c8d78bc61 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Thu, 16 Nov 2023 15:32:49 +0100 Subject: [PATCH 014/128] [Actions/CI]: Switch to macos-latest-xlarge CI runner for Android, iOS pipelines (#3474) --- .github/workflows/android-ci.yml | 30 ++++++++++++-- .github/workflows/ios-ci.yml | 16 +++++++- .github/workflows/kotlin-ci.yml | 14 ++++++- .github/workflows/kotlin-sample-ci.yml | 5 ++- samples/kmp/shared/build.gradle.kts | 2 +- swift/Tests/Keystore/KeyStoreTests.swift | 1 - tests/chains/Aptos/CompilerTests.cpp | 1 + tools/android-build | 4 ++ tools/android-release | 4 ++ tools/android-sdk | 50 ++++++++++++++++++++++++ tools/android-test | 10 ++++- tools/install-android-dependencies | 10 +++-- tools/install-kotlin-dependencies | 7 +++- tools/kotlin-build | 4 ++ tools/kotlin-release | 1 + tools/kotlin-test | 4 ++ tools/rust-bindgen | 36 +++-------------- 17 files changed, 154 insertions(+), 45 deletions(-) create mode 100644 tools/android-sdk diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index a575fe792ea..b8d6bb34c8a 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -12,7 +12,7 @@ concurrency: jobs: build: - runs-on: [ self-hosted, macOS, ARM64, wallet-core ] + runs-on: macos-latest-large if: github.event.pull_request.draft == false steps: - uses: actions/checkout@v3 @@ -29,6 +29,15 @@ jobs: - name: Install system dependencies run: | tools/install-sys-dependencies-mac + + - name: Cache Rust + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + rust + + - name: Install Rust dependencies + run: | tools/install-rust-dependencies - name: Install Android Dependencies @@ -39,7 +48,7 @@ jobs: uses: actions/cache@v3 with: path: build/local - key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-dependencies') }} + key: ${{ runner.os }}-${{ runner.arch }}-internal-${{ hashFiles('tools/install-dependencies') }} - name: Install internal dependencies run: tools/install-dependencies @@ -51,8 +60,21 @@ jobs: - name: Build Kotlin doc run: tools/kotlin-doc - - name: Run test - run: tools/android-test + - name: Build tests + run: | + pushd android + ./gradlew assembleAndroidTest + popd + + - name: Run tests + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: 30 + target: google_apis + arch: x86 + ndk: 23.1.7779620 + cmake: 3.18.1 + script: cd android; ./gradlew connectedAndroidTest - name: Build sample app run: tools/samples-build android diff --git a/.github/workflows/ios-ci.yml b/.github/workflows/ios-ci.yml index 7dcc89a194b..649b15e84a2 100644 --- a/.github/workflows/ios-ci.yml +++ b/.github/workflows/ios-ci.yml @@ -12,24 +12,37 @@ concurrency: jobs: build: - runs-on: [ self-hosted, macOS, ARM64, wallet-core ] + runs-on: macos-latest-xlarge if: github.event.pull_request.draft == false steps: - uses: actions/checkout@v3 + - name: Install system dependencies run: | tools/install-sys-dependencies-mac + + - name: Cache Rust + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + rust + + - name: Install Rust dependencies + run: | tools/install-rust-dependencies + - name: Cache internal dependencies id: internal_cache uses: actions/cache@v3 with: path: build/local key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-dependencies') }} + - name: Install internal dependencies run: | tools/install-dependencies if: steps.internal_cache.outputs.cache-hit != 'true' + - name: Run codegen tests run: tools/codegen-test @@ -37,6 +50,7 @@ jobs: run: | tools/generate-files ios tools/ios-test + - name: Build sample app run: | tools/samples-build ios diff --git a/.github/workflows/kotlin-ci.yml b/.github/workflows/kotlin-ci.yml index 0a517a03ae8..1198c20ae3c 100644 --- a/.github/workflows/kotlin-ci.yml +++ b/.github/workflows/kotlin-ci.yml @@ -12,7 +12,7 @@ concurrency: jobs: build: - runs-on: [ self-hosted, macOS, ARM64, wallet-core ] + runs-on: macos-latest-xlarge if: github.event.pull_request.draft == false steps: - uses: actions/checkout@v3 @@ -23,6 +23,9 @@ jobs: java-version: '17' distribution: 'temurin' + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + - name: Setup Gradle uses: gradle/gradle-build-action@v2 with: @@ -31,6 +34,15 @@ jobs: - name: Install system dependencies run: | tools/install-sys-dependencies-mac + + - name: Cache Rust + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + rust + + - name: Install Rust dependencies + run: | tools/install-rust-dependencies - name: Install emsdk diff --git a/.github/workflows/kotlin-sample-ci.yml b/.github/workflows/kotlin-sample-ci.yml index 4c42e44dcd5..f96b2a4b280 100644 --- a/.github/workflows/kotlin-sample-ci.yml +++ b/.github/workflows/kotlin-sample-ci.yml @@ -12,7 +12,7 @@ concurrency: jobs: build: - runs-on: [ self-hosted, macOS, ARM64, wallet-core ] + runs-on: macos-latest-xlarge if: github.event.pull_request.draft == false steps: - uses: actions/checkout@v3 @@ -23,6 +23,9 @@ jobs: java-version: '17' distribution: 'temurin' + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + - name: Install Kotlin Dependencies run: tools/install-kotlin-dependencies diff --git a/samples/kmp/shared/build.gradle.kts b/samples/kmp/shared/build.gradle.kts index 14741e42ad6..dc4b6fbb8b1 100644 --- a/samples/kmp/shared/build.gradle.kts +++ b/samples/kmp/shared/build.gradle.kts @@ -35,7 +35,7 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation("com.trustwallet:wallet-core-kotlin:4.0.0") + implementation("com.trustwallet:wallet-core-kotlin:4.0.5") } } val commonTest by getting { diff --git a/swift/Tests/Keystore/KeyStoreTests.swift b/swift/Tests/Keystore/KeyStoreTests.swift index 62d907ccfaf..3017d10c304 100755 --- a/swift/Tests/Keystore/KeyStoreTests.swift +++ b/swift/Tests/Keystore/KeyStoreTests.swift @@ -234,7 +234,6 @@ class KeyStoreTests: XCTestCase { } func testImportJSON() throws { - let expected = """ { "activeAccounts": [{ diff --git a/tests/chains/Aptos/CompilerTests.cpp b/tests/chains/Aptos/CompilerTests.cpp index 16d7ae5dc11..fd00034931a 100644 --- a/tests/chains/Aptos/CompilerTests.cpp +++ b/tests/chains/Aptos/CompilerTests.cpp @@ -5,6 +5,7 @@ // file LICENSE at the root of the source code distribution tree. #include "proto/Aptos.pb.h" +#include "proto/TransactionCompiler.pb.h" #include "HexCoding.h" #include "PrivateKey.h" #include "PublicKey.h" diff --git a/tools/android-build b/tools/android-build index bab31390ec8..8c4b1344af8 100755 --- a/tools/android-build +++ b/tools/android-build @@ -7,6 +7,10 @@ set -e source $(dirname $0)/library version=$(wc_read_version $1) +if [[ `uname` == "Darwin" ]]; then + export BOOST_ROOT=$(brew --prefix boost) +fi + pushd android ./gradlew assembleRelease cp wallet-core/build/outputs/aar/wallet-core-release.aar ../build/wallet-core.aar diff --git a/tools/android-release b/tools/android-release index 6371adc941f..2b4bca2da1d 100755 --- a/tools/android-release +++ b/tools/android-release @@ -8,6 +8,10 @@ set -e source $(dirname $0)/library version=$(wc_read_version $1) +if [[ `uname` == "Darwin" ]]; then + export BOOST_ROOT=$(brew --prefix boost) +fi + echo "Building $version" export ANDROID_HOME="$HOME/Library/Android/sdk" diff --git a/tools/android-sdk b/tools/android-sdk new file mode 100644 index 00000000000..2ee8025fdb4 --- /dev/null +++ b/tools/android-sdk @@ -0,0 +1,50 @@ +#!/bin/bash + +export NDK_API_LEVEL=21 + +find_android_ndk() { + if [[ "$ANDROID_NDK_HOME" != "" ]]; then + >&2 echo "Use ANDROID_NDK_HOME" + elif [[ "$ANDROID_HOME" != "" ]]; then + >&2 echo "ANDROID_NDK_HOME is not set. Use ANDROID_HOME value instead" + ANDROID_NDK_HOME="$ANDROID_HOME/ndk" + else + >&2 echo "WARNING: ANDROID_HOME is not set. Use a default path" + ANDROID_NDK_HOME="$HOME/Library/Android/sdk/ndk" + fi + + TEST_CLANG="aarch64-linux-android$NDK_API_LEVEL-clang" + PATH_TO_CLANG=$(find "$ANDROID_NDK_HOME" -iname $TEST_CLANG -print -quit) + + if [[ "$PATH_TO_CLANG" == "" ]]; then + >&2 echo "ERROR: cannot find NDK tools within '$ANDROID_NDK_HOME'" + exit 22 + fi + + echo $(dirname "$PATH_TO_CLANG") +} + +find_android_cmdline_tools() { + # Default version of cmdline tools is 11. + # https://github.com/android-actions/setup-android/blob/9584f05408b63719e3464df8ac85bdbe58f88a64/src/main.ts#L9 + CMDLINE_TOOLS_VERSION="11.0" + + if [[ "$ANDROID_HOME" == "" ]]; then + >&2 echo "ANDROID_HOME is not set. Use a default path" + ANDROID_HOME="$HOME/Library/Android/sdk" + fi + + # cmdline-tools could have a 'latest' version, but if it was installed 2 years ago it may not be 'latest' as of today + if [ -x "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" ]; then + echo "$ANDROID_HOME/cmdline-tools/latest/bin" + return 0 + fi + + if [ -x "$ANDROID_HOME/cmdline-tools/$CMDLINE_TOOLS_VERSION/bin/sdkmanager" ]; then + echo "$ANDROID_HOME/cmdline-tools/$CMDLINE_TOOLS_VERSION/bin" + return 0 + fi + + >&2 echo "ERROR: cannot find SDK cmdline tools within '$ANDROID_HOME'" + exit 22 +} diff --git a/tools/android-test b/tools/android-test index e2f2288d09f..7185d860711 100755 --- a/tools/android-test +++ b/tools/android-test @@ -4,9 +4,17 @@ set -e +source "$(dirname $0)/android-sdk" + +ANDROID_CMDTOOLS=$(find_android_cmdline_tools) + AVD_NAME="integration-tests" PORT=5556 +if [[ `uname` == "Darwin" ]]; then + export BOOST_ROOT=$(brew --prefix boost) +fi + # Make sure it builds before starting an emulator pushd android ./gradlew assembleAndroidTest @@ -17,7 +25,7 @@ SERIAL=emulator-${PORT} # We have to echo "no" because it will ask us if we want to use a custom hardware profile, and we don't. echo -e "\nCreating Android emulator...\n" -echo "no" | "$ANDROID_HOME/cmdline-tools/latest/bin/avdmanager" create avd \ +echo "no" | "$ANDROID_CMDTOOLS/avdmanager" create avd \ -n "${AVD_NAME}" \ -k "system-images;android-33;google_apis;arm64-v8a" \ -f diff --git a/tools/install-android-dependencies b/tools/install-android-dependencies index b91ad4d6520..667e8c4342d 100755 --- a/tools/install-android-dependencies +++ b/tools/install-android-dependencies @@ -2,6 +2,10 @@ set -e +source "$(dirname $0)/android-sdk" + +ANDROID_CMDTOOLS=$(find_android_cmdline_tools) + # Dokka stuff ROOT="$PWD" DOKKA_CLI_JAR=https://repo1.maven.org/maven2/org/jetbrains/dokka/dokka-cli/1.7.20/dokka-cli-1.7.20.jar @@ -16,10 +20,10 @@ https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-html-jvm/0.8.0/kotl https://repo1.maven.org/maven2/org/freemarker/freemarker/2.3.31/freemarker-2.3.31.jar ) -$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --verbose "cmake;3.18.1" "ndk;23.1.7779620" -$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager "system-images;android-26;google_apis;x86" +$ANDROID_CMDTOOLS/sdkmanager --verbose "cmake;3.18.1" "ndk;23.1.7779620" +$ANDROID_CMDTOOLS/sdkmanager "system-images;android-30;google_apis;x86" -echo -e "y\ny\ny\ny\ny\n" | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses +echo -e "y\ny\ny\ny\ny\n" | $ANDROID_CMDTOOLS/sdkmanager --licenses echo "Downloading Dokka..." DOKKA_DIR="$ROOT/build/dokka" diff --git a/tools/install-kotlin-dependencies b/tools/install-kotlin-dependencies index e837297f31e..42c24dca325 100755 --- a/tools/install-kotlin-dependencies +++ b/tools/install-kotlin-dependencies @@ -2,6 +2,9 @@ set -e -"$ANDROID_HOME"/cmdline-tools/latest/bin/sdkmanager --verbose "cmake;3.22.1" "ndk;25.2.9519653" +source "$(dirname $0)/android-sdk" -yes | "$ANDROID_HOME"/cmdline-tools/latest/bin/sdkmanager --licenses +ANDROID_CMDTOOLS=$(find_android_cmdline_tools) + +$ANDROID_CMDTOOLS/sdkmanager --verbose "cmake;3.22.1" "ndk;25.2.9519653" +yes | $ANDROID_CMDTOOLS/sdkmanager --licenses diff --git a/tools/kotlin-build b/tools/kotlin-build index a64537b101f..4681910d30a 100755 --- a/tools/kotlin-build +++ b/tools/kotlin-build @@ -2,6 +2,10 @@ set -e +if [[ `uname` == "Darwin" ]]; then + export BOOST_ROOT=$(brew --prefix boost) +fi + pushd kotlin ./gradlew :wallet-core-kotlin:generateProtos ./gradlew :wallet-core-kotlin:assemble diff --git a/tools/kotlin-release b/tools/kotlin-release index 90552fe9ab2..c6e5f40afa8 100755 --- a/tools/kotlin-release +++ b/tools/kotlin-release @@ -8,6 +8,7 @@ version=$(wc_read_version $1) echo "Building $version" export ANDROID_HOME="$HOME/Library/Android/sdk" +export BOOST_ROOT=$(brew --prefix boost) pushd kotlin ./gradlew :wallet-core-kotlin:generateProtos diff --git a/tools/kotlin-test b/tools/kotlin-test index b6218d532b5..2c97cef3d00 100755 --- a/tools/kotlin-test +++ b/tools/kotlin-test @@ -2,6 +2,10 @@ set -e +if [[ `uname` == "Darwin" ]]; then + export BOOST_ROOT=$(brew --prefix boost) +fi + pushd kotlin ./gradlew :wallet-core-kotlin:jvmTest popd diff --git a/tools/rust-bindgen b/tools/rust-bindgen index 02546de6b1a..0c61c54f467 100755 --- a/tools/rust-bindgen +++ b/tools/rust-bindgen @@ -2,12 +2,13 @@ set -e +source "$(dirname $0)/android-sdk" + TARGET_NAME="libwallet_core_rs.a" TARGET_XCFRAMEWORK_NAME=../swift/WalletCoreRs.xcframework BUILD_FOLDER=../rust/target CRATE="wallet-core-rs" HEADER_NAME="WalletCoreRSBindgen.h" -NDK_API_LEVEL=21 create_xc_framework() { rm -rf $TARGET_XCFRAMEWORK_NAME @@ -16,31 +17,6 @@ create_xc_framework() { cp $BUILD_FOLDER/catalyst/$TARGET_NAME $TARGET_XCFRAMEWORK_NAME/ios-arm64_x86_64-maccatalyst } -find_android_ndk() { - if [[ "$ANDROID_NDK_HOME" != "" ]]; then - echo "Use ANDROID_NDK_HOME" - elif [[ "$ANDROID_HOME" != "" ]]; then - echo "ANDROID_NDK_HOME is not set. Use ANDROID_HOME value instead" - ANDROID_NDK_HOME="$ANDROID_HOME/ndk" - else - echo "WARNING: ANDROID_HOME is not set. Use a default path" - ANDROID_NDK_HOME="$HOME/Library/Android/sdk/ndk" - fi - - TEST_CLANG="aarch64-linux-android$NDK_API_LEVEL-clang" - PATH_TO_CLANG=$(find "$ANDROID_NDK_HOME" -iname $TEST_CLANG -print -quit) - - if [[ "$PATH_TO_CLANG" == "" ]]; then - echo "ERROR: cannot find NDK tools within '$ANDROID_NDK_HOME'" - return 1 - fi - - NDK_BIN_PATH=$(dirname "$PATH_TO_CLANG") - export NDK_BIN_PATH - - return 0 -} - cd rust NATIVE="false" @@ -53,13 +29,13 @@ if [[ "$1" == "native" ]] || [[ "$1" == "" ]]; then NATIVE="true" fi -# Generate bindings for mobile platforms on MacOS only. -if [[ `uname` == "Darwin" ]]; then - # Whether to generate bindings for Android. +# Whether to generate bindings for Android. if [[ "$1" == "android" ]] || [[ "$1" == "" ]]; then ANDROID="true" fi +# Generate bindings for ios platforms on MacOS only. +if [[ `uname` == "Darwin" ]]; then # Whether to generate bindings for iOS. if [[ "$1" == "ios" ]] || [[ "$1" == "" ]]; then IOS="true" @@ -82,7 +58,7 @@ if [[ "$WASM" == "true" ]]; then fi if [[ "$ANDROID" == "true" ]]; then - find_android_ndk + NDK_BIN_PATH=$(find_android_ndk) export AR="$NDK_BIN_PATH/llvm-ar" export CC_aarch64_linux_android="$NDK_BIN_PATH/aarch64-linux-android$NDK_API_LEVEL-clang" From 7119750b33f9a40ea21be7b2478edc5239db9b28 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Thu, 16 Nov 2023 19:02:45 +0100 Subject: [PATCH 015/128] [Rust/Cosmos]: Move Cosmos blockchain into Rust (#3498) --- .../cosmos/TestCosmosTransactions.kt | 2 +- codegen-v2/Cargo.toml | 2 - include/TrustWalletCore/TWBlockchain.h | 2 + registry.json | 5 +- rust/Cargo.lock | 84 +++ rust/Cargo.toml | 6 + rust/chains/tw_cosmos/Cargo.toml | 13 + rust/chains/tw_cosmos/src/entry.rs | 94 +++ rust/chains/tw_cosmos/src/lib.rs | 7 + rust/chains/tw_native_evmos/Cargo.toml | 14 + rust/chains/tw_native_evmos/src/context.rs | 22 + rust/chains/tw_native_evmos/src/entry.rs | 89 +++ .../src/ethermint_public_key.rs | 67 ++ rust/chains/tw_native_evmos/src/lib.rs | 9 + rust/chains/tw_native_injective/Cargo.toml | 14 + .../chains/tw_native_injective/src/context.rs | 22 + rust/chains/tw_native_injective/src/entry.rs | 94 +++ .../src/injective_public_key.rs | 57 ++ rust/chains/tw_native_injective/src/lib.rs | 9 + rust/chains/tw_thorchain/Cargo.toml | 14 + rust/chains/tw_thorchain/src/compiler.rs | 40 ++ rust/chains/tw_thorchain/src/entry.rs | 88 +++ rust/chains/tw_thorchain/src/lib.rs | 10 + rust/chains/tw_thorchain/src/signer.rs | 23 + rust/chains/tw_thorchain/src/signing_input.rs | 23 + rust/tw_any_coin/Cargo.toml | 2 + rust/tw_any_coin/src/any_address.rs | 16 +- rust/tw_any_coin/src/ffi/tw_any_address.rs | 76 ++ .../tests/chains/aptos/aptos_compile.rs | 2 +- rust/tw_any_coin/tests/chains/mod.rs | 3 + .../tests/chains/native_evmos/mod.rs | 7 + .../chains/native_evmos/native_evmos_sign.rs | 122 ++++ .../tests/chains/native_injective/mod.rs | 8 + .../native_injective_compile.rs | 96 +++ .../native_injective/native_injective_sign.rs | 97 +++ .../tw_any_coin/tests/chains/thorchain/mod.rs | 11 + .../tests/chains/thorchain/test_cases.rs | 41 ++ .../chains/thorchain/thorchain_compile.rs | 97 +++ .../tests/chains/thorchain/thorchain_sign.rs | 41 ++ .../tests/tw_any_address_ffi_tests.rs | 116 +++- .../tests/tw_any_signer_ffi_tests.rs | 53 ++ rust/tw_aptos/fuzz/Cargo.toml | 1 - rust/tw_aptos/src/entry.rs | 9 + rust/tw_bech32_address/Cargo.toml | 12 + rust/tw_bech32_address/src/bech32_prefix.rs | 22 + rust/tw_bech32_address/src/lib.rs | 441 ++++++++++++ rust/tw_bitcoin/Cargo.toml | 2 - rust/tw_bitcoin/src/entry.rs | 13 +- .../src/modules/legacy/build_and_sign.rs | 6 +- rust/tw_bitcoin/tests/brc20.rs | 4 +- rust/tw_bitcoin/tests/free_estimate.rs | 10 +- rust/tw_bitcoin/tests/ordinal_nft.rs | 4 +- rust/tw_bitcoin/tests/p2pkh.rs | 6 +- rust/tw_bitcoin/tests/p2sh.rs | 4 +- rust/tw_bitcoin/tests/p2tr_key_path.rs | 4 +- rust/tw_bitcoin/tests/p2tr_script_path.rs | 4 +- rust/tw_bitcoin/tests/p2wpkh.rs | 4 +- rust/tw_bitcoin/tests/p2wsh.rs | 4 +- rust/tw_bitcoin/tests/plan_builder.rs | 4 +- rust/tw_bitcoin/tests/send_to_address.rs | 12 +- rust/tw_coin_entry/Cargo.toml | 1 + rust/tw_coin_entry/src/coin_context.rs | 9 +- rust/tw_coin_entry/src/coin_entry.rs | 8 + rust/tw_coin_entry/src/coin_entry_ext.rs | 36 +- .../tw_coin_entry/src/common/compile_input.rs | 20 +- rust/tw_coin_entry/src/derivation.rs | 2 + rust/tw_coin_entry/src/error.rs | 2 + .../src/test_utils/empty_context.rs | 17 - rust/tw_coin_entry/src/test_utils/mod.rs | 2 +- .../src/test_utils/test_context.rs | 44 ++ rust/tw_coin_registry/Cargo.toml | 5 + rust/tw_coin_registry/src/blockchain_type.rs | 8 + rust/tw_coin_registry/src/coin_context.rs | 11 + rust/tw_coin_registry/src/dispatcher.rs | 16 + rust/tw_coin_registry/src/registry.rs | 5 + rust/tw_cosmos_sdk/Cargo.toml | 29 + rust/tw_cosmos_sdk/build.rs | 71 ++ rust/tw_cosmos_sdk/fuzz/.gitignore | 5 + rust/tw_cosmos_sdk/fuzz/Cargo.toml | 30 + rust/tw_cosmos_sdk/fuzz/fuzz_targets/sign.rs | 16 + rust/tw_cosmos_sdk/src/address.rs | 29 + rust/tw_cosmos_sdk/src/context.rs | 34 + .../src/hasher/keccak256_hasher.rs | 21 + rust/tw_cosmos_sdk/src/hasher/mod.rs | 16 + .../tw_cosmos_sdk/src/hasher/sha256_hasher.rs | 21 + rust/tw_cosmos_sdk/src/lib.rs | 25 + .../src/modules/broadcast_msg.rs | 60 ++ .../src/modules/compiler/json_preimager.rs | 40 ++ .../tw_cosmos_sdk/src/modules/compiler/mod.rs | 9 + .../modules/compiler/protobuf_preimager.rs | 49 ++ .../src/modules/compiler/tw_compiler.rs | 185 +++++ rust/tw_cosmos_sdk/src/modules/mod.rs | 11 + .../src/modules/serializer/json_serializer.rs | 125 ++++ .../src/modules/serializer/mod.rs | 8 + .../modules/serializer/protobuf_serializer.rs | 165 +++++ rust/tw_cosmos_sdk/src/modules/signer/mod.rs | 7 + .../src/modules/signer/tw_signer.rs | 61 ++ rust/tw_cosmos_sdk/src/modules/tx_builder.rs | 655 ++++++++++++++++++ rust/tw_cosmos_sdk/src/private_key/mod.rs | 18 + .../src/private_key/secp256k1.rs | 36 + rust/tw_cosmos_sdk/src/public_key/mod.rs | 32 + .../tw_cosmos_sdk/src/public_key/secp256k1.rs | 67 ++ rust/tw_cosmos_sdk/src/signature/mod.rs | 16 + rust/tw_cosmos_sdk/src/signature/secp256k1.rs | 34 + rust/tw_cosmos_sdk/src/test_utils/mod.rs | 8 + .../src/test_utils/proto_utils.rs | 33 + .../src/test_utils/sign_utils.rs | 167 +++++ .../message/cosmos_auth_message.rs | 57 ++ .../message/cosmos_bank_message.rs | 45 ++ .../message/cosmos_generic_message.rs | 25 + .../transaction/message/cosmos_gov_message.rs | 46 ++ .../message/cosmos_staking_message.rs | 163 +++++ .../src/transaction/message/ibc_message.rs | 53 ++ .../src/transaction/message/mod.rs | 56 ++ .../src/transaction/message/stride_message.rs | 48 ++ .../transaction/message/terra_wasm_message.rs | 54 ++ .../transaction/message/thorchain_message.rs | 82 +++ .../src/transaction/message/wasm_message.rs | 105 +++ rust/tw_cosmos_sdk/src/transaction/mod.rs | 72 ++ rust/tw_cosmos_sdk/tests/compile.rs | 101 +++ rust/tw_cosmos_sdk/tests/sign.rs | 361 ++++++++++ rust/tw_cosmos_sdk/tests/sign_staking.rs | 418 +++++++++++ rust/tw_cosmos_sdk/tests/sign_stride.rs | 104 +++ rust/tw_cosmos_sdk/tests/sign_terra_wasm.rs | 272 ++++++++ rust/tw_cosmos_sdk/tests/sign_thorchain.rs | 135 ++++ .../tw_cosmos_sdk/tests/sign_wasm_contract.rs | 253 +++++++ rust/tw_encoding/Cargo.toml | 1 + rust/tw_encoding/src/base64.rs | 15 + rust/tw_encoding/src/bech32.rs | 31 + rust/tw_encoding/src/lib.rs | 1 + rust/tw_ethereum/src/entry.rs | 10 + rust/tw_ethereum/tests/compiler.rs | 10 +- rust/tw_ethereum/tests/signer.rs | 6 +- rust/tw_evm/src/modules/compiler.rs | 3 +- rust/tw_evm/tests/message_signer.rs | 20 +- rust/tw_hash/src/hasher.rs | 40 ++ rust/tw_hash/src/lib.rs | 1 + rust/tw_internet_computer/Cargo.toml | 2 - rust/tw_internet_computer/src/entry.rs | 9 + rust/tw_keypair/src/traits.rs | 6 +- rust/tw_keypair/src/tw/public.rs | 15 + rust/tw_misc/src/lib.rs | 6 + rust/tw_misc/src/traits.rs | 4 + rust/tw_proto/src/common/google/mod.rs | 7 + .../src/common/google/protobuf/any.proto | 169 +++++ .../src/common/google/protobuf/any.rs | 51 ++ .../src/common/google/protobuf/mod.rs | 11 + .../common/google/protobuf/timestamp.proto | 151 ++++ .../src/common/google/protobuf/timestamp.rs | 51 ++ rust/tw_proto/src/common/mod.rs | 7 + rust/tw_proto/src/lib.rs | 16 +- rust/tw_ronin/src/entry.rs | 9 + rust/tw_ronin/tests/compiler.rs | 8 +- rust/tw_ronin/tests/signer.rs | 12 +- rust/tw_utxo/Cargo.toml | 2 - src/Aptos/Entry.cpp | 31 - src/Aptos/Entry.h | 10 +- src/Coin.cpp | 6 + src/CoinEntry.cpp | 97 --- src/CoinEntry.h | 27 - src/Cosmos/Entry.cpp | 71 +- src/Cosmos/Entry.h | 14 +- src/Cosmos/JsonSerialization.cpp | 275 -------- src/Cosmos/JsonSerialization.h | 35 - src/Cosmos/Protobuf/tx.proto | 2 +- src/Cosmos/ProtobufSerialization.cpp | 577 --------------- src/Cosmos/ProtobufSerialization.h | 43 -- src/Cosmos/Signer.cpp | 102 --- src/Cosmos/Signer.h | 36 - src/Ethereum/Entry.cpp | 32 +- src/Ethereum/Entry.h | 16 +- src/Greenfield/ProtobufSerialization.cpp | 6 +- src/InternetComputer/Entry.cpp | 33 - src/InternetComputer/Entry.h | 13 +- src/NativeEvmos/Entry.h | 18 + src/NativeInjective/Entry.h | 18 + src/Ronin/Entry.cpp | 39 -- src/Ronin/Entry.h | 12 +- src/THORChain/Entry.cpp | 29 - src/THORChain/Entry.h | 3 - src/THORChain/Signer.cpp | 36 - src/THORChain/Signer.h | 24 - src/proto/Cosmos.proto | 10 +- src/rust/RustCoinEntry.cpp | 114 +++ src/rust/RustCoinEntry.h | 26 + swift/Tests/Blockchains/CosmosTests.swift | 2 +- tests/chains/Cosmos/CryptoOrg/SignerTests.cpp | 7 +- tests/chains/Cosmos/Juno/TWAnySignerTests.cpp | 4 +- .../Cosmos/NativeEvmos/TWCoinTypeTests.cpp | 2 +- .../Cosmos/NativeInjective/SignerTests.cpp | 7 +- .../NativeInjective/TWCoinTypeTests.cpp | 2 +- .../TransactionCompilerTests.cpp | 4 +- tests/chains/Cosmos/Osmosis/SignerTests.cpp | 5 +- tests/chains/Cosmos/ProtobufTests.cpp | 93 --- tests/chains/Cosmos/Secret/SignerTests.cpp | 6 +- tests/chains/Cosmos/SignerTests.cpp | 77 +- tests/chains/Cosmos/StakingTests.cpp | 38 +- tests/chains/Cosmos/THORChain/SignerTests.cpp | 15 +- tests/chains/Cosmos/Terra/SignerTests.cpp | 40 +- tests/chains/Cosmos/TerraV2/SignerTests.cpp | 37 +- .../Cosmos/TransactionCompilerTests.cpp | 8 +- tests/chains/Evmos/SignerTests.cpp | 16 +- .../chains/Evmos/TransactionCompilerTests.cpp | 4 +- 203 files changed, 7743 insertions(+), 1936 deletions(-) create mode 100644 rust/chains/tw_cosmos/Cargo.toml create mode 100644 rust/chains/tw_cosmos/src/entry.rs create mode 100644 rust/chains/tw_cosmos/src/lib.rs create mode 100644 rust/chains/tw_native_evmos/Cargo.toml create mode 100644 rust/chains/tw_native_evmos/src/context.rs create mode 100644 rust/chains/tw_native_evmos/src/entry.rs create mode 100644 rust/chains/tw_native_evmos/src/ethermint_public_key.rs create mode 100644 rust/chains/tw_native_evmos/src/lib.rs create mode 100644 rust/chains/tw_native_injective/Cargo.toml create mode 100644 rust/chains/tw_native_injective/src/context.rs create mode 100644 rust/chains/tw_native_injective/src/entry.rs create mode 100644 rust/chains/tw_native_injective/src/injective_public_key.rs create mode 100644 rust/chains/tw_native_injective/src/lib.rs create mode 100644 rust/chains/tw_thorchain/Cargo.toml create mode 100644 rust/chains/tw_thorchain/src/compiler.rs create mode 100644 rust/chains/tw_thorchain/src/entry.rs create mode 100644 rust/chains/tw_thorchain/src/lib.rs create mode 100644 rust/chains/tw_thorchain/src/signer.rs create mode 100644 rust/chains/tw_thorchain/src/signing_input.rs create mode 100644 rust/tw_any_coin/tests/chains/native_evmos/mod.rs create mode 100644 rust/tw_any_coin/tests/chains/native_evmos/native_evmos_sign.rs create mode 100644 rust/tw_any_coin/tests/chains/native_injective/mod.rs create mode 100644 rust/tw_any_coin/tests/chains/native_injective/native_injective_compile.rs create mode 100644 rust/tw_any_coin/tests/chains/native_injective/native_injective_sign.rs create mode 100644 rust/tw_any_coin/tests/chains/thorchain/mod.rs create mode 100644 rust/tw_any_coin/tests/chains/thorchain/test_cases.rs create mode 100644 rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs create mode 100644 rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs create mode 100644 rust/tw_bech32_address/Cargo.toml create mode 100644 rust/tw_bech32_address/src/bech32_prefix.rs create mode 100644 rust/tw_bech32_address/src/lib.rs delete mode 100644 rust/tw_coin_entry/src/test_utils/empty_context.rs create mode 100644 rust/tw_coin_entry/src/test_utils/test_context.rs create mode 100644 rust/tw_cosmos_sdk/Cargo.toml create mode 100644 rust/tw_cosmos_sdk/build.rs create mode 100644 rust/tw_cosmos_sdk/fuzz/.gitignore create mode 100644 rust/tw_cosmos_sdk/fuzz/Cargo.toml create mode 100644 rust/tw_cosmos_sdk/fuzz/fuzz_targets/sign.rs create mode 100644 rust/tw_cosmos_sdk/src/address.rs create mode 100644 rust/tw_cosmos_sdk/src/context.rs create mode 100644 rust/tw_cosmos_sdk/src/hasher/keccak256_hasher.rs create mode 100644 rust/tw_cosmos_sdk/src/hasher/mod.rs create mode 100644 rust/tw_cosmos_sdk/src/hasher/sha256_hasher.rs create mode 100644 rust/tw_cosmos_sdk/src/lib.rs create mode 100644 rust/tw_cosmos_sdk/src/modules/broadcast_msg.rs create mode 100644 rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs create mode 100644 rust/tw_cosmos_sdk/src/modules/compiler/mod.rs create mode 100644 rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs create mode 100644 rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs create mode 100644 rust/tw_cosmos_sdk/src/modules/mod.rs create mode 100644 rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs create mode 100644 rust/tw_cosmos_sdk/src/modules/serializer/mod.rs create mode 100644 rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs create mode 100644 rust/tw_cosmos_sdk/src/modules/signer/mod.rs create mode 100644 rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs create mode 100644 rust/tw_cosmos_sdk/src/modules/tx_builder.rs create mode 100644 rust/tw_cosmos_sdk/src/private_key/mod.rs create mode 100644 rust/tw_cosmos_sdk/src/private_key/secp256k1.rs create mode 100644 rust/tw_cosmos_sdk/src/public_key/mod.rs create mode 100644 rust/tw_cosmos_sdk/src/public_key/secp256k1.rs create mode 100644 rust/tw_cosmos_sdk/src/signature/mod.rs create mode 100644 rust/tw_cosmos_sdk/src/signature/secp256k1.rs create mode 100644 rust/tw_cosmos_sdk/src/test_utils/mod.rs create mode 100644 rust/tw_cosmos_sdk/src/test_utils/proto_utils.rs create mode 100644 rust/tw_cosmos_sdk/src/test_utils/sign_utils.rs create mode 100644 rust/tw_cosmos_sdk/src/transaction/message/cosmos_auth_message.rs create mode 100644 rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs create mode 100644 rust/tw_cosmos_sdk/src/transaction/message/cosmos_generic_message.rs create mode 100644 rust/tw_cosmos_sdk/src/transaction/message/cosmos_gov_message.rs create mode 100644 rust/tw_cosmos_sdk/src/transaction/message/cosmos_staking_message.rs create mode 100644 rust/tw_cosmos_sdk/src/transaction/message/ibc_message.rs create mode 100644 rust/tw_cosmos_sdk/src/transaction/message/mod.rs create mode 100644 rust/tw_cosmos_sdk/src/transaction/message/stride_message.rs create mode 100644 rust/tw_cosmos_sdk/src/transaction/message/terra_wasm_message.rs create mode 100644 rust/tw_cosmos_sdk/src/transaction/message/thorchain_message.rs create mode 100644 rust/tw_cosmos_sdk/src/transaction/message/wasm_message.rs create mode 100644 rust/tw_cosmos_sdk/src/transaction/mod.rs create mode 100644 rust/tw_cosmos_sdk/tests/compile.rs create mode 100644 rust/tw_cosmos_sdk/tests/sign.rs create mode 100644 rust/tw_cosmos_sdk/tests/sign_staking.rs create mode 100644 rust/tw_cosmos_sdk/tests/sign_stride.rs create mode 100644 rust/tw_cosmos_sdk/tests/sign_terra_wasm.rs create mode 100644 rust/tw_cosmos_sdk/tests/sign_thorchain.rs create mode 100644 rust/tw_cosmos_sdk/tests/sign_wasm_contract.rs create mode 100644 rust/tw_encoding/src/bech32.rs create mode 100644 rust/tw_hash/src/hasher.rs create mode 100644 rust/tw_proto/src/common/google/mod.rs create mode 100644 rust/tw_proto/src/common/google/protobuf/any.proto create mode 100644 rust/tw_proto/src/common/google/protobuf/any.rs create mode 100644 rust/tw_proto/src/common/google/protobuf/mod.rs create mode 100644 rust/tw_proto/src/common/google/protobuf/timestamp.proto create mode 100644 rust/tw_proto/src/common/google/protobuf/timestamp.rs create mode 100644 rust/tw_proto/src/common/mod.rs delete mode 100644 src/Aptos/Entry.cpp delete mode 100644 src/Cosmos/JsonSerialization.cpp delete mode 100644 src/Cosmos/JsonSerialization.h delete mode 100644 src/Cosmos/ProtobufSerialization.cpp delete mode 100644 src/Cosmos/ProtobufSerialization.h delete mode 100644 src/Cosmos/Signer.cpp delete mode 100644 src/Cosmos/Signer.h delete mode 100644 src/InternetComputer/Entry.cpp create mode 100644 src/NativeEvmos/Entry.h create mode 100644 src/NativeInjective/Entry.h delete mode 100644 src/Ronin/Entry.cpp delete mode 100644 src/THORChain/Entry.cpp delete mode 100644 src/THORChain/Signer.cpp delete mode 100644 src/THORChain/Signer.h create mode 100644 src/rust/RustCoinEntry.cpp create mode 100644 src/rust/RustCoinEntry.h delete mode 100644 tests/chains/Cosmos/ProtobufTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cosmos/TestCosmosTransactions.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cosmos/TestCosmosTransactions.kt index ed829d1ce76..4de24f9b60f 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cosmos/TestCosmosTransactions.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cosmos/TestCosmosTransactions.kt @@ -67,7 +67,7 @@ class TestCosmosTransactions { assertEquals( output.serialized, - "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CvgBCvUBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQS0gEKLWNvc21vczEzazBxMGw3bGcya3IzMmt2dDdseTIzNnBwbGR5OHY5ZHp3aDNnZBItY29zbW9zMWZzN2x1MjhoeDVtOWFrbTdycDBjMjQyMmNuOHIyZjdndXJ1amhmGnIKaAoqL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuU3Rha2VBdXRob3JpemF0aW9uEjoSNgo0Y29zbW9zdmFsb3BlcjFnanR2bHk5bGVsNnpza3Z3dHZsZzV2aHdwdTljOXdhdzdzeHp3eCABEgYI4LD6pgYSZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA/fcQw1hCVUx904t+kCXTiiziaLIY8lyssu1ENfzaN1KEgQKAggBGAUSEwoNCgV1YXRvbRIEMjQxOBCp8wUaQIFyfuijGKf87Hz61ZqxasfLI1PZnNge4RDq/tRyB/tZI6p80iGRqHecoV6+84EQkc9GTlNRQOSlApRCsivT9XI=\"}" + "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CvgBCvUBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQS0gEKLWNvc21vczEzazBxMGw3bGcya3IzMmt2dDdseTIzNnBwbGR5OHY5ZHp3aDNnZBItY29zbW9zMWZzN2x1MjhoeDVtOWFrbTdycDBjMjQyMmNuOHIyZjdndXJ1amhmGnIKaAoqL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuU3Rha2VBdXRob3JpemF0aW9uEjogARI2CjRjb3Ntb3N2YWxvcGVyMWdqdHZseTlsZWw2enNrdnd0dmxnNXZod3B1OWM5d2F3N3N4end4EgYI4LD6pgYSZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA/fcQw1hCVUx904t+kCXTiiziaLIY8lyssu1ENfzaN1KEgQKAggBGAUSEwoNCgV1YXRvbRIEMjQxOBCp8wUaQEAN1nIfDawlHnep2bNEm14w+g7tYybJJT3htcGVS6s9D7va3ed1OUEIk9LZoc3G//VenJ+KLw26SRVBaRukgVI=\"}" ) assertEquals(output.errorMessage, "") } diff --git a/codegen-v2/Cargo.toml b/codegen-v2/Cargo.toml index a8906c0ae94..7598a3ad756 100644 --- a/codegen-v2/Cargo.toml +++ b/codegen-v2/Cargo.toml @@ -3,8 +3,6 @@ name = "codegen-v2" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [lib] name = "libparser" path = "src/lib.rs" diff --git a/include/TrustWalletCore/TWBlockchain.h b/include/TrustWalletCore/TWBlockchain.h index 06a88beba2e..75da789d240 100644 --- a/include/TrustWalletCore/TWBlockchain.h +++ b/include/TrustWalletCore/TWBlockchain.h @@ -65,6 +65,8 @@ enum TWBlockchain { TWBlockchainSui = 50, TWBlockchainGreenfield = 51, TWBlockchainInternetComputer = 52, + TWBlockchainNativeEvmos = 55, // Cosmos + TWBlockchainNativeInjective = 56, // Cosmos }; TW_EXTERN_C_END diff --git a/registry.json b/registry.json index 966fc0a2d71..65e637c59b4 100644 --- a/registry.json +++ b/registry.json @@ -3177,6 +3177,7 @@ "curve": "secp256k1", "publicKeyType": "secp256k1", "hrp": "thor", + "addressHasher": "sha256ripemd", "chainId": "thorchain-mainnet-v1", "explorer": { "url": "https://viewblock.io/thorchain", @@ -3973,7 +3974,7 @@ "coinId": 20009001, "symbol": "EVMOS", "decimals": 18, - "blockchain": "Cosmos", + "blockchain": "NativeEvmos", "chainId": "evmos_9001-2", "derivation": [ { @@ -4365,7 +4366,7 @@ "coinId": 10000060, "symbol": "INJ", "decimals": 18, - "blockchain": "Cosmos", + "blockchain": "NativeInjective", "derivation": [ { "path": "m/44'/60'/0'/0/0" diff --git a/rust/Cargo.lock b/rust/Cargo.lock index c6fdc8c55a6..d58c01c250c 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1599,7 +1599,9 @@ dependencies = [ "tw_any_coin", "tw_coin_entry", "tw_coin_registry", + "tw_cosmos_sdk", "tw_encoding", + "tw_hash", "tw_keypair", "tw_memory", "tw_misc", @@ -1624,6 +1626,18 @@ dependencies = [ "tw_proto", ] +[[package]] +name = "tw_bech32_address" +version = "0.1.0" +dependencies = [ + "serde", + "tw_coin_entry", + "tw_encoding", + "tw_hash", + "tw_keypair", + "tw_memory", +] + [[package]] name = "tw_bitcoin" version = "0.1.0" @@ -1648,6 +1662,7 @@ version = "0.1.0" dependencies = [ "serde_json", "tw_encoding", + "tw_hash", "tw_keypair", "tw_memory", "tw_misc", @@ -1665,13 +1680,48 @@ dependencies = [ "tw_aptos", "tw_bitcoin", "tw_coin_entry", + "tw_cosmos", "tw_ethereum", "tw_evm", + "tw_hash", "tw_internet_computer", "tw_keypair", "tw_memory", "tw_misc", + "tw_native_evmos", + "tw_native_injective", "tw_ronin", + "tw_thorchain", +] + +[[package]] +name = "tw_cosmos" +version = "0.1.0" +dependencies = [ + "tw_coin_entry", + "tw_cosmos_sdk", + "tw_keypair", + "tw_proto", +] + +[[package]] +name = "tw_cosmos_sdk" +version = "0.1.0" +dependencies = [ + "pb-rs", + "quick-protobuf", + "serde", + "serde_json", + "tw_bech32_address", + "tw_coin_entry", + "tw_cosmos_sdk", + "tw_encoding", + "tw_hash", + "tw_keypair", + "tw_memory", + "tw_misc", + "tw_number", + "tw_proto", ] [[package]] @@ -1680,6 +1730,7 @@ version = "0.1.0" dependencies = [ "arbitrary", "bcs", + "bech32", "bs58", "ciborium", "data-encoding", @@ -1797,6 +1848,28 @@ dependencies = [ "zeroize", ] +[[package]] +name = "tw_native_evmos" +version = "0.1.0" +dependencies = [ + "tw_coin_entry", + "tw_cosmos_sdk", + "tw_keypair", + "tw_memory", + "tw_proto", +] + +[[package]] +name = "tw_native_injective" +version = "0.1.0" +dependencies = [ + "tw_coin_entry", + "tw_cosmos_sdk", + "tw_keypair", + "tw_memory", + "tw_proto", +] + [[package]] name = "tw_number" version = "0.1.0" @@ -1834,6 +1907,17 @@ dependencies = [ "tw_proto", ] +[[package]] +name = "tw_thorchain" +version = "0.1.0" +dependencies = [ + "tw_coin_entry", + "tw_cosmos_sdk", + "tw_keypair", + "tw_memory", + "tw_proto", +] + [[package]] name = "tw_utxo" version = "0.1.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 872986a6a6a..87d437eff4c 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,10 +1,16 @@ [workspace] members = [ + "chains/tw_cosmos", + "chains/tw_native_evmos", + "chains/tw_native_injective", + "chains/tw_thorchain", "tw_any_coin", "tw_aptos", + "tw_bech32_address", "tw_bitcoin", "tw_coin_entry", "tw_coin_registry", + "tw_cosmos_sdk", "tw_encoding", "tw_ethereum", "tw_evm", diff --git a/rust/chains/tw_cosmos/Cargo.toml b/rust/chains/tw_cosmos/Cargo.toml new file mode 100644 index 00000000000..cbeae311ec3 --- /dev/null +++ b/rust/chains/tw_cosmos/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "tw_cosmos" +version = "0.1.0" +edition = "2021" + +[dependencies] +tw_coin_entry = { path = "../../tw_coin_entry" } +tw_cosmos_sdk = { path = "../../tw_cosmos_sdk" } +tw_keypair = { path = "../../tw_keypair" } +tw_proto = { path = "../../tw_proto" } + +[dev-dependencies] +tw_cosmos_sdk = { path = "../../tw_cosmos_sdk", features = ["test-utils"] } diff --git a/rust/chains/tw_cosmos/src/entry.rs b/rust/chains/tw_cosmos/src/entry.rs new file mode 100644 index 00000000000..ca2d84efcf7 --- /dev/null +++ b/rust/chains/tw_cosmos/src/entry.rs @@ -0,0 +1,94 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::derivation::Derivation; +use tw_coin_entry::error::AddressResult; +use tw_coin_entry::modules::json_signer::NoJsonSigner; +use tw_coin_entry::modules::message_signer::NoMessageSigner; +use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_cosmos_sdk::address::{Address, Bech32Prefix}; +use tw_cosmos_sdk::context::StandardCosmosContext; +use tw_cosmos_sdk::modules::compiler::tw_compiler::TWTransactionCompiler; +use tw_cosmos_sdk::modules::signer::tw_signer::TWSigner; +use tw_keypair::tw; +use tw_proto::Cosmos::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct CosmosEntry; + +impl CoinEntry for CosmosEntry { + type AddressPrefix = Bech32Prefix; + type Address = Address; + type SigningInput<'a> = Proto::SigningInput<'a>; + type SigningOutput = Proto::SigningOutput<'static>; + type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + type JsonSigner = NoJsonSigner; + type PlanBuilder = NoPlanBuilder; + type MessageSigner = NoMessageSigner; + + #[inline] + fn parse_address( + &self, + coin: &dyn CoinContext, + address: &str, + prefix: Option, + ) -> AddressResult { + Address::from_str_with_coin_and_prefix(coin, address.to_string(), prefix) + } + + #[inline] + fn parse_address_unchecked( + &self, + _coin: &dyn CoinContext, + address: &str, + ) -> AddressResult { + Address::from_str(address) + } + + #[inline] + fn derive_address( + &self, + coin: &dyn CoinContext, + public_key: tw::PublicKey, + _derivation: Derivation, + prefix: Option, + ) -> AddressResult { + Address::with_public_key_coin_context(coin, &public_key, prefix) + } + + #[inline] + fn sign(&self, coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput { + TWSigner::::sign(coin, input) + } + + #[inline] + fn preimage_hashes( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + ) -> Self::PreSigningOutput { + TWTransactionCompiler::::preimage_hashes(coin, input) + } + + #[inline] + fn compile( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Self::SigningOutput { + TWTransactionCompiler::::compile( + coin, + input, + signatures, + public_keys, + ) + } +} diff --git a/rust/chains/tw_cosmos/src/lib.rs b/rust/chains/tw_cosmos/src/lib.rs new file mode 100644 index 00000000000..1afc00798db --- /dev/null +++ b/rust/chains/tw_cosmos/src/lib.rs @@ -0,0 +1,7 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod entry; diff --git a/rust/chains/tw_native_evmos/Cargo.toml b/rust/chains/tw_native_evmos/Cargo.toml new file mode 100644 index 00000000000..cc4762a7971 --- /dev/null +++ b/rust/chains/tw_native_evmos/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "tw_native_evmos" +version = "0.1.0" +edition = "2021" + +[dependencies] +tw_coin_entry = { path = "../../tw_coin_entry" } +tw_cosmos_sdk = { path = "../../tw_cosmos_sdk" } +tw_keypair = { path = "../../tw_keypair" } +tw_memory = { path = "../../tw_memory" } +tw_proto = { path = "../../tw_proto" } + +[dev-dependencies] +tw_cosmos_sdk = { path = "../../tw_cosmos_sdk", features = ["test-utils"] } diff --git a/rust/chains/tw_native_evmos/src/context.rs b/rust/chains/tw_native_evmos/src/context.rs new file mode 100644 index 00000000000..edb431767ac --- /dev/null +++ b/rust/chains/tw_native_evmos/src/context.rs @@ -0,0 +1,22 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ethermint_public_key::EthermintEthSecp256PublicKey; +use tw_cosmos_sdk::address::Address; +use tw_cosmos_sdk::context::CosmosContext; +use tw_cosmos_sdk::hasher::keccak256_hasher::Keccak256Hasher; +use tw_cosmos_sdk::private_key::secp256k1::Secp256PrivateKey; +use tw_cosmos_sdk::signature::secp256k1::Secp256k1Signature; + +pub struct NativeEvmosContext; + +impl CosmosContext for NativeEvmosContext { + type Address = Address; + type TxHasher = Keccak256Hasher; + type PrivateKey = Secp256PrivateKey; + type PublicKey = EthermintEthSecp256PublicKey; + type Signature = Secp256k1Signature; +} diff --git a/rust/chains/tw_native_evmos/src/entry.rs b/rust/chains/tw_native_evmos/src/entry.rs new file mode 100644 index 00000000000..eb25d7ea816 --- /dev/null +++ b/rust/chains/tw_native_evmos/src/entry.rs @@ -0,0 +1,89 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::context::NativeEvmosContext; +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::derivation::Derivation; +use tw_coin_entry::error::AddressResult; +use tw_coin_entry::modules::json_signer::NoJsonSigner; +use tw_coin_entry::modules::message_signer::NoMessageSigner; +use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_cosmos_sdk::address::{Address, Bech32Prefix}; +use tw_cosmos_sdk::modules::compiler::tw_compiler::TWTransactionCompiler; +use tw_cosmos_sdk::modules::signer::tw_signer::TWSigner; +use tw_keypair::tw; +use tw_proto::Cosmos::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct NativeEvmosEntry; + +impl CoinEntry for NativeEvmosEntry { + type AddressPrefix = Bech32Prefix; + type Address = Address; + type SigningInput<'a> = Proto::SigningInput<'a>; + type SigningOutput = Proto::SigningOutput<'static>; + type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + type JsonSigner = NoJsonSigner; + type PlanBuilder = NoPlanBuilder; + type MessageSigner = NoMessageSigner; + + #[inline] + fn parse_address( + &self, + coin: &dyn CoinContext, + address: &str, + prefix: Option, + ) -> AddressResult { + Address::from_str_with_coin_and_prefix(coin, address.to_string(), prefix) + } + + #[inline] + fn parse_address_unchecked( + &self, + _coin: &dyn CoinContext, + address: &str, + ) -> AddressResult { + Address::from_str(address) + } + + #[inline] + fn derive_address( + &self, + coin: &dyn CoinContext, + public_key: tw::PublicKey, + _derivation: Derivation, + prefix: Option, + ) -> AddressResult { + Address::with_public_key_coin_context(coin, &public_key, prefix) + } + + #[inline] + fn sign(&self, coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput { + TWSigner::::sign(coin, input) + } + + #[inline] + fn preimage_hashes( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + ) -> Self::PreSigningOutput { + TWTransactionCompiler::::preimage_hashes(coin, input) + } + + #[inline] + fn compile( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Self::SigningOutput { + TWTransactionCompiler::::compile(coin, input, signatures, public_keys) + } +} diff --git a/rust/chains/tw_native_evmos/src/ethermint_public_key.rs b/rust/chains/tw_native_evmos/src/ethermint_public_key.rs new file mode 100644 index 00000000000..5f53f261e5b --- /dev/null +++ b/rust/chains/tw_native_evmos/src/ethermint_public_key.rs @@ -0,0 +1,67 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::coin_context::CoinContext; +use tw_cosmos_sdk::proto::ethermint; +use tw_cosmos_sdk::public_key::{CosmosPublicKey, JsonPublicKey, ProtobufPublicKey}; +use tw_keypair::ecdsa::secp256k1; +use tw_keypair::KeyPairResult; +use tw_keypair::{tw, KeyPairError}; +use tw_memory::Data; +use tw_proto::{google, to_any}; + +pub struct EthermintEthSecp256PublicKey { + public_key: Data, +} + +impl EthermintEthSecp256PublicKey { + pub fn new(public_key: &secp256k1::PublicKey) -> KeyPairResult { + Ok(EthermintEthSecp256PublicKey { + // NativeEvmos chain requires the public key to be compressed. + // This trick is needed because `registry.json` contains extended public key type. + public_key: public_key.compressed().to_vec(), + }) + } +} + +impl CosmosPublicKey for EthermintEthSecp256PublicKey { + fn from_private_key(coin: &dyn CoinContext, private_key: &tw::PrivateKey) -> KeyPairResult + where + Self: Sized, + { + let tw_public_key = private_key.get_public_key_by_type(coin.public_key_type())?; + let secp256k1_key = tw_public_key + .to_secp256k1() + .ok_or(KeyPairError::InvalidPublicKey)?; + EthermintEthSecp256PublicKey::new(secp256k1_key) + } + + fn from_bytes(_coin: &dyn CoinContext, public_key_bytes: &[u8]) -> KeyPairResult { + let public_key = secp256k1::PublicKey::try_from(public_key_bytes)?; + EthermintEthSecp256PublicKey::new(&public_key) + } + + fn to_bytes(&self) -> Data { + self.public_key.clone() + } +} + +impl JsonPublicKey for EthermintEthSecp256PublicKey { + fn public_key_type(&self) -> String { + const ETHERMINT_SECP256K1_PUBLIC_KEY_TYPE: &str = "ethermint/PubKeyEthSecp256k1"; + + ETHERMINT_SECP256K1_PUBLIC_KEY_TYPE.to_string() + } +} + +impl ProtobufPublicKey for EthermintEthSecp256PublicKey { + fn to_proto(&self) -> google::protobuf::Any { + let proto = ethermint::crypto::v1::ethsecp256k1::PubKey { + key: self.public_key.clone(), + }; + to_any(&proto) + } +} diff --git a/rust/chains/tw_native_evmos/src/lib.rs b/rust/chains/tw_native_evmos/src/lib.rs new file mode 100644 index 00000000000..d7aa4534c83 --- /dev/null +++ b/rust/chains/tw_native_evmos/src/lib.rs @@ -0,0 +1,9 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod context; +pub mod entry; +pub mod ethermint_public_key; diff --git a/rust/chains/tw_native_injective/Cargo.toml b/rust/chains/tw_native_injective/Cargo.toml new file mode 100644 index 00000000000..d793539a241 --- /dev/null +++ b/rust/chains/tw_native_injective/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "tw_native_injective" +version = "0.1.0" +edition = "2021" + +[dependencies] +tw_coin_entry = { path = "../../tw_coin_entry" } +tw_cosmos_sdk = { path = "../../tw_cosmos_sdk" } +tw_keypair = { path = "../../tw_keypair" } +tw_memory = { path = "../../tw_memory" } +tw_proto = { path = "../../tw_proto" } + +[dev-dependencies] +tw_cosmos_sdk = { path = "../../tw_cosmos_sdk", features = ["test-utils"] } diff --git a/rust/chains/tw_native_injective/src/context.rs b/rust/chains/tw_native_injective/src/context.rs new file mode 100644 index 00000000000..898c034d656 --- /dev/null +++ b/rust/chains/tw_native_injective/src/context.rs @@ -0,0 +1,22 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::injective_public_key::InjectiveEthSecp256PublicKey; +use tw_cosmos_sdk::address::Address; +use tw_cosmos_sdk::context::CosmosContext; +use tw_cosmos_sdk::hasher::keccak256_hasher::Keccak256Hasher; +use tw_cosmos_sdk::private_key::secp256k1::Secp256PrivateKey; +use tw_cosmos_sdk::signature::secp256k1::Secp256k1Signature; + +pub struct NativeInjectiveContext; + +impl CosmosContext for NativeInjectiveContext { + type Address = Address; + type TxHasher = Keccak256Hasher; + type PrivateKey = Secp256PrivateKey; + type PublicKey = InjectiveEthSecp256PublicKey; + type Signature = Secp256k1Signature; +} diff --git a/rust/chains/tw_native_injective/src/entry.rs b/rust/chains/tw_native_injective/src/entry.rs new file mode 100644 index 00000000000..b11d0036c87 --- /dev/null +++ b/rust/chains/tw_native_injective/src/entry.rs @@ -0,0 +1,94 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::context::NativeInjectiveContext; +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::derivation::Derivation; +use tw_coin_entry::error::AddressResult; +use tw_coin_entry::modules::json_signer::NoJsonSigner; +use tw_coin_entry::modules::message_signer::NoMessageSigner; +use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_cosmos_sdk::address::{Address, Bech32Prefix, CosmosAddress}; +use tw_cosmos_sdk::modules::compiler::tw_compiler::TWTransactionCompiler; +use tw_cosmos_sdk::modules::signer::tw_signer::TWSigner; +use tw_keypair::tw; +use tw_proto::Cosmos::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct NativeInjectiveEntry; + +impl CoinEntry for NativeInjectiveEntry { + type AddressPrefix = Bech32Prefix; + type Address = Address; + type SigningInput<'a> = Proto::SigningInput<'a>; + type SigningOutput = Proto::SigningOutput<'static>; + type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + type JsonSigner = NoJsonSigner; + type PlanBuilder = NoPlanBuilder; + type MessageSigner = NoMessageSigner; + + #[inline] + fn parse_address( + &self, + coin: &dyn CoinContext, + address: &str, + _prefix: Option, + ) -> AddressResult { + Address::from_str_with_coin(coin, address) + } + + #[inline] + fn parse_address_unchecked( + &self, + _coin: &dyn CoinContext, + address: &str, + ) -> AddressResult { + Address::from_str(address) + } + + #[inline] + fn derive_address( + &self, + coin: &dyn CoinContext, + public_key: tw::PublicKey, + _derivation: Derivation, + prefix: Option, + ) -> AddressResult { + Address::with_public_key_coin_context(coin, &public_key, prefix) + } + + #[inline] + fn sign(&self, coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput { + TWSigner::::sign(coin, input) + } + + #[inline] + fn preimage_hashes( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + ) -> Self::PreSigningOutput { + TWTransactionCompiler::::preimage_hashes(coin, input) + } + + #[inline] + fn compile( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Self::SigningOutput { + TWTransactionCompiler::::compile( + coin, + input, + signatures, + public_keys, + ) + } +} diff --git a/rust/chains/tw_native_injective/src/injective_public_key.rs b/rust/chains/tw_native_injective/src/injective_public_key.rs new file mode 100644 index 00000000000..084d47e94f0 --- /dev/null +++ b/rust/chains/tw_native_injective/src/injective_public_key.rs @@ -0,0 +1,57 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::coin_context::CoinContext; +use tw_cosmos_sdk::proto::injective; +use tw_cosmos_sdk::public_key::secp256k1::prepare_secp256k1_public_key; +use tw_cosmos_sdk::public_key::{CosmosPublicKey, JsonPublicKey, ProtobufPublicKey}; +use tw_keypair::tw::PrivateKey; +use tw_keypair::KeyPairResult; +use tw_memory::Data; +use tw_proto::{google, to_any}; + +pub struct InjectiveEthSecp256PublicKey { + public_key: Data, +} + +impl CosmosPublicKey for InjectiveEthSecp256PublicKey { + fn from_private_key(coin: &dyn CoinContext, private_key: &PrivateKey) -> KeyPairResult + where + Self: Sized, + { + let public_key = private_key.get_public_key_by_type(coin.public_key_type())?; + Ok(InjectiveEthSecp256PublicKey { + public_key: public_key.to_bytes(), + }) + } + + fn from_bytes(coin: &dyn CoinContext, public_key_bytes: &[u8]) -> KeyPairResult { + let public_key = prepare_secp256k1_public_key(coin, public_key_bytes)?; + Ok(InjectiveEthSecp256PublicKey { public_key }) + } + + fn to_bytes(&self) -> Data { + self.public_key.clone() + } +} + +impl JsonPublicKey for InjectiveEthSecp256PublicKey { + fn public_key_type(&self) -> String { + /// https://github.com/cosmostation/cosmostation-chrome-extension/blob/e2fd27d71a17993f8eef07ce30f7a04a32e52788/src/constants/cosmos.ts#L4 + const INJECTIVE_SECP256K1_PUBLIC_KEY_TYPE: &str = "injective/PubKeyEthSecp256k1"; + + INJECTIVE_SECP256K1_PUBLIC_KEY_TYPE.to_string() + } +} + +impl ProtobufPublicKey for InjectiveEthSecp256PublicKey { + fn to_proto(&self) -> google::protobuf::Any { + let proto = injective::crypto::v1beta1::ethsecp256k1::PubKey { + key: self.public_key.clone(), + }; + to_any(&proto) + } +} diff --git a/rust/chains/tw_native_injective/src/lib.rs b/rust/chains/tw_native_injective/src/lib.rs new file mode 100644 index 00000000000..a61663bd72c --- /dev/null +++ b/rust/chains/tw_native_injective/src/lib.rs @@ -0,0 +1,9 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod context; +pub mod entry; +pub mod injective_public_key; diff --git a/rust/chains/tw_thorchain/Cargo.toml b/rust/chains/tw_thorchain/Cargo.toml new file mode 100644 index 00000000000..a39bc4d7c80 --- /dev/null +++ b/rust/chains/tw_thorchain/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "tw_thorchain" +version = "0.1.0" +edition = "2021" + +[dependencies] +tw_coin_entry = { path = "../../tw_coin_entry" } +tw_cosmos_sdk = { path = "../../tw_cosmos_sdk" } +tw_keypair = { path = "../../tw_keypair" } +tw_memory = { path = "../../tw_memory" } +tw_proto = { path = "../../tw_proto" } + +[dev-dependencies] +tw_cosmos_sdk = { path = "../../tw_cosmos_sdk", features = ["test-utils"] } diff --git a/rust/chains/tw_thorchain/src/compiler.rs b/rust/chains/tw_thorchain/src/compiler.rs new file mode 100644 index 00000000000..a6fd1b8bbc8 --- /dev/null +++ b/rust/chains/tw_thorchain/src/compiler.rs @@ -0,0 +1,40 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::signing_input::ThorchainSigningInput; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; +use tw_cosmos_sdk::context::StandardCosmosContext; +use tw_cosmos_sdk::modules::compiler::tw_compiler::TWTransactionCompiler; +use tw_proto::Cosmos::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct ThorchainCompiler; + +impl ThorchainCompiler { + pub fn preimage_hashes( + coin: &dyn CoinContext, + mut input: Proto::SigningInput<'_>, + ) -> CompilerProto::PreSigningOutput<'static> { + ThorchainSigningInput::prepare_signing_input(&mut input); + TWTransactionCompiler::::preimage_hashes(coin, input) + } + + pub fn compile( + coin: &dyn CoinContext, + mut input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Proto::SigningOutput<'static> { + ThorchainSigningInput::prepare_signing_input(&mut input); + TWTransactionCompiler::::compile( + coin, + input, + signatures, + public_keys, + ) + } +} diff --git a/rust/chains/tw_thorchain/src/entry.rs b/rust/chains/tw_thorchain/src/entry.rs new file mode 100644 index 00000000000..81f8bf562f1 --- /dev/null +++ b/rust/chains/tw_thorchain/src/entry.rs @@ -0,0 +1,88 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::compiler::ThorchainCompiler; +use crate::signer::ThorchainSigner; +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::derivation::Derivation; +use tw_coin_entry::error::AddressResult; +use tw_coin_entry::modules::json_signer::NoJsonSigner; +use tw_coin_entry::modules::message_signer::NoMessageSigner; +use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_cosmos_sdk::address::{Address, Bech32Prefix, CosmosAddress}; +use tw_keypair::tw; +use tw_proto::Cosmos::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct ThorchainEntry; + +impl CoinEntry for ThorchainEntry { + type AddressPrefix = Bech32Prefix; + type Address = Address; + type SigningInput<'a> = Proto::SigningInput<'a>; + type SigningOutput = Proto::SigningOutput<'static>; + type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + type JsonSigner = NoJsonSigner; + type PlanBuilder = NoPlanBuilder; + type MessageSigner = NoMessageSigner; + + #[inline] + fn parse_address( + &self, + coin: &dyn CoinContext, + address: &str, + _prefix: Option, + ) -> AddressResult { + Address::from_str_with_coin(coin, address) + } + + #[inline] + fn parse_address_unchecked( + &self, + _coin: &dyn CoinContext, + address: &str, + ) -> AddressResult { + Address::from_str(address) + } + + #[inline] + fn derive_address( + &self, + coin: &dyn CoinContext, + public_key: tw::PublicKey, + _derivation: Derivation, + prefix: Option, + ) -> AddressResult { + Address::with_public_key_coin_context(coin, &public_key, prefix) + } + + #[inline] + fn sign(&self, coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput { + ThorchainSigner::sign(coin, input) + } + + #[inline] + fn preimage_hashes( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + ) -> Self::PreSigningOutput { + ThorchainCompiler::preimage_hashes(coin, input) + } + + #[inline] + fn compile( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Self::SigningOutput { + ThorchainCompiler::compile(coin, input, signatures, public_keys) + } +} diff --git a/rust/chains/tw_thorchain/src/lib.rs b/rust/chains/tw_thorchain/src/lib.rs new file mode 100644 index 00000000000..be313341897 --- /dev/null +++ b/rust/chains/tw_thorchain/src/lib.rs @@ -0,0 +1,10 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod compiler; +pub mod entry; +pub mod signer; +pub mod signing_input; diff --git a/rust/chains/tw_thorchain/src/signer.rs b/rust/chains/tw_thorchain/src/signer.rs new file mode 100644 index 00000000000..d086eacd1c3 --- /dev/null +++ b/rust/chains/tw_thorchain/src/signer.rs @@ -0,0 +1,23 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::signing_input::ThorchainSigningInput; +use tw_coin_entry::coin_context::CoinContext; +use tw_cosmos_sdk::context::StandardCosmosContext; +use tw_cosmos_sdk::modules::signer::tw_signer::TWSigner; +use tw_proto::Cosmos::Proto; + +pub struct ThorchainSigner; + +impl ThorchainSigner { + pub fn sign( + coin: &dyn CoinContext, + mut input: Proto::SigningInput<'_>, + ) -> Proto::SigningOutput<'static> { + ThorchainSigningInput::prepare_signing_input(&mut input); + TWSigner::::sign(coin, input) + } +} diff --git a/rust/chains/tw_thorchain/src/signing_input.rs b/rust/chains/tw_thorchain/src/signing_input.rs new file mode 100644 index 00000000000..66863f7288f --- /dev/null +++ b/rust/chains/tw_thorchain/src/signing_input.rs @@ -0,0 +1,23 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::borrow::Cow; +use tw_proto::Cosmos::Proto; +use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum; + +const THORCHAIN_PREFIX_MSG_SEND: &str = "thorchain/MsgSend"; + +pub struct ThorchainSigningInput; + +impl ThorchainSigningInput { + pub fn prepare_signing_input(input: &mut Proto::SigningInput) { + for message in input.messages.iter_mut() { + if let MessageEnum::send_coins_message(ref mut msg_send) = message.message_oneof { + msg_send.type_prefix = Cow::from(THORCHAIN_PREFIX_MSG_SEND); + } + } + } +} diff --git a/rust/tw_any_coin/Cargo.toml b/rust/tw_any_coin/Cargo.toml index 17ba71621f1..bb8e9314f3d 100644 --- a/rust/tw_any_coin/Cargo.toml +++ b/rust/tw_any_coin/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] tw_coin_entry = { path = "../tw_coin_entry" } tw_coin_registry = { path = "../tw_coin_registry" } +tw_hash = { path = "../tw_hash" } tw_keypair = { path = "../tw_keypair" } tw_memory = { path = "../tw_memory" } tw_misc = { path = "../tw_misc" } @@ -17,6 +18,7 @@ test-utils = [] serde = { version = "1.0.163", features = ["derive"] } serde_json = { version = "1.0.96" } tw_any_coin = { path = "./", features = ["test-utils"] } +tw_cosmos_sdk = { path = "../tw_cosmos_sdk", features = ["test-utils"] } tw_encoding = { path = "../tw_encoding" } tw_keypair = { path = "../tw_keypair", features = ["test-utils"] } tw_memory = { path = "../tw_memory", features = ["test-utils"] } diff --git a/rust/tw_any_coin/src/any_address.rs b/rust/tw_any_coin/src/any_address.rs index 23fe3fe42e4..5d5098482a9 100644 --- a/rust/tw_any_coin/src/any_address.rs +++ b/rust/tw_any_coin/src/any_address.rs @@ -36,7 +36,19 @@ impl AnyAddress { prefix: Option, ) -> AddressResult { let (ctx, entry) = coin_dispatcher(coin).map_err(|_| AddressError::UnknownCoinType)?; - let address = entry.normalize_address(&ctx, address, prefix)?; + entry.validate_address(&ctx, address, prefix)?; + let address = entry.normalize_address(&ctx, address)?; + Ok(AnyAddress { coin, address }) + } + + /// Creates an address from a string representation and a coin type. + /// Please note that his function does not validate if the address belongs to the given chain. + pub(crate) fn with_string_unchecked( + coin: CoinType, + address: &str, + ) -> AddressResult { + let (ctx, entry) = coin_dispatcher(coin).map_err(|_| AddressError::UnknownCoinType)?; + let address = entry.normalize_address(&ctx, address)?; Ok(AnyAddress { coin, address }) } @@ -57,7 +69,7 @@ impl AnyAddress { #[inline] pub fn get_data(&self) -> AddressResult { let (ctx, entry) = coin_dispatcher(self.coin).map_err(|_| AddressError::UnknownCoinType)?; - entry.address_to_data(&ctx, &self.address, None) + entry.address_to_data(&ctx, &self.address) } /// Returns the address string representation. diff --git a/rust/tw_any_coin/src/ffi/tw_any_address.rs b/rust/tw_any_coin/src/ffi/tw_any_address.rs index 25aa2373a00..de69826f9f8 100644 --- a/rust/tw_any_coin/src/ffi/tw_any_address.rs +++ b/rust/tw_any_coin/src/ffi/tw_any_address.rs @@ -8,6 +8,7 @@ use crate::any_address::AnyAddress; use tw_coin_entry::derivation::Derivation; +use tw_coin_entry::prefix::AddressPrefix; use tw_keypair::ffi::pubkey::TWPublicKey; use tw_memory::ffi::tw_data::TWData; use tw_memory::ffi::tw_string::TWString; @@ -32,6 +33,28 @@ pub unsafe extern "C" fn tw_any_address_is_valid(string: *const TWString, coin: AnyAddress::is_valid(coin, string, None) } +/// Determines if the string is a valid Any address with the given hrp. +/// +/// \param string address to validate. +/// \param coin coin type of the address. +/// \param hrp explicit given hrp of the given address. +/// \return bool indicating if the address is valid. +#[no_mangle] +pub unsafe extern "C" fn tw_any_address_is_valid_bech32( + string: *const TWString, + coin: u32, + hrp: *const TWString, +) -> bool { + let string = try_or_false!(TWString::from_ptr_as_ref(string)); + let string = try_or_false!(string.as_str()); + + let hrp = try_or_false!(TWString::from_ptr_as_ref(hrp)); + let hrp = try_or_false!(hrp.as_str()); + + let prefix = AddressPrefix::Hrp(hrp.to_string()); + AnyAddress::is_valid(coin, string, Some(prefix)) +} + /// Creates an address from a string representation and a coin type. Must be deleted with `TWAnyAddressDelete` after use. /// /// \param string address to create. @@ -70,6 +93,34 @@ pub unsafe extern "C" fn tw_any_address_create_with_public_key_derivation( .unwrap_or_else(|_| std::ptr::null_mut()) } +/// Creates an bech32 address from a public key and a given hrp. +/// +/// \param public_key derivates the address from the public key. +/// \param coin coin type of the address. +/// \param hrp hrp of the address. +/// \return TWAnyAddress pointer or nullptr if public key is invalid. +#[no_mangle] +pub unsafe extern "C" fn tw_any_address_create_bech32_with_public_key( + public_key: *mut TWPublicKey, + coin: u32, + hrp: *const TWString, +) -> *mut TWAnyAddress { + let public_key = try_or_else!(TWPublicKey::from_ptr_as_ref(public_key), std::ptr::null_mut); + + let hrp = try_or_else!(TWString::from_ptr_as_ref(hrp), std::ptr::null_mut); + let hrp = try_or_else!(hrp.as_str(), std::ptr::null_mut); + + let prefix = AddressPrefix::Hrp(hrp.to_string()); + AnyAddress::with_public_key( + coin, + public_key.as_ref().clone(), + Derivation::default(), + Some(prefix), + ) + .map(|any_address| TWAnyAddress(any_address).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} + /// Deletes an address. /// /// \param address address to delete. @@ -102,3 +153,28 @@ pub unsafe extern "C" fn tw_any_address_data(address: *const TWAnyAddress) -> *m let data = try_or_else!(address.0.get_data(), std::ptr::null_mut); TWData::from(data).into_ptr() } + +/// Creates an address from a string representation and a coin type. Must be deleted with `TWAnyAddressDelete` after use. +/// This function does not check if the address belongs to the given chain. +/// +/// \param string address to create. +/// \param coin coin type of the address. +/// \return `TWAnyAddress` pointer or nullptr if address and coin are invalid. +/// +/// # Warning +/// +/// This function should only be used when address prefix is unavailable to be passed to this function. +/// Consider using `tw_any_address_create__with_string` if the prefix is known. +/// Please note that this function should be removed when all chains are migrated to Rust. +#[no_mangle] +pub unsafe extern "C" fn tw_any_address_create_with_string_unchecked( + string: *const TWString, + coin: u32, +) -> *mut TWAnyAddress { + let string = try_or_else!(TWString::from_ptr_as_ref(string), std::ptr::null_mut); + let string = try_or_else!(string.as_str(), std::ptr::null_mut); + + AnyAddress::with_string_unchecked(coin, string) + .map(|any_address| TWAnyAddress(any_address).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} diff --git a/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs b/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs index 9f21f68d317..114d7e9a46e 100644 --- a/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs +++ b/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs @@ -25,7 +25,7 @@ use tw_proto::TxCompiler::Proto as CompilerProto; use tw_proto::{deserialize, serialize}; #[test] -fn test_any_signer_sign_aptos() { +fn test_any_signer_compile_aptos() { let input = aptos_sign_transfer_input(); // Step 2: Obtain preimage hash diff --git a/rust/tw_any_coin/tests/chains/mod.rs b/rust/tw_any_coin/tests/chains/mod.rs index fa5b3a9718c..165ba051b71 100644 --- a/rust/tw_any_coin/tests/chains/mod.rs +++ b/rust/tw_any_coin/tests/chains/mod.rs @@ -5,3 +5,6 @@ // file LICENSE at the root of the source code distribution tree. mod aptos; +mod native_evmos; +mod native_injective; +mod thorchain; diff --git a/rust/tw_any_coin/tests/chains/native_evmos/mod.rs b/rust/tw_any_coin/tests/chains/native_evmos/mod.rs new file mode 100644 index 00000000000..4d62bbecabe --- /dev/null +++ b/rust/tw_any_coin/tests/chains/native_evmos/mod.rs @@ -0,0 +1,7 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +mod native_evmos_sign; diff --git a/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_sign.rs b/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_sign.rs new file mode 100644 index 00000000000..3615cfa3548 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_sign.rs @@ -0,0 +1,122 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::borrow::Cow; +use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; +use tw_coin_entry::error::SigningErrorType; +use tw_cosmos_sdk::test_utils::proto_utils::{make_amount, make_fee, make_message}; +use tw_encoding::hex::DecodeHex; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_proto::Cosmos::Proto; +use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum; +use tw_proto::{deserialize, serialize}; + +const NATIVE_EVMOS_COIN_TYPE: u32 = 20009001; + +fn account_1037_private_key() -> Cow<'static, [u8]> { + "80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005" + .decode_hex() + .unwrap() + .into() +} + +fn account_2139877_private_key() -> Cow<'static, [u8]> { + "79bcbded1a5678ab34e6d9db9ad78e4e480e7b22723cc5fbf59e843732e1a8e5" + .decode_hex() + .unwrap() + .into() +} + +#[test] +fn test_sign_native_evmos_tx_json() { + let send_msg = Proto::mod_Message::Send { + from_address: "evmos1hsk6jryyqjfhp5dhc55tc9jtckygx0ep4mur4z".into(), + to_address: "evmos1zt50azupanqlfam5afhv3hexwyutnuke45f6ye".into(), + amounts: vec![make_amount("muon", "1")], + ..Proto::mod_Message::Send::default() + }; + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::JSON, + account_number: 1037, + chain_id: "evmos_9001-2".into(), + sequence: 8, + fee: Some(make_fee(200000, make_amount("muon", "200"))), + private_key: account_1037_private_key(), + messages: vec![make_message(MessageEnum::send_coins_message(send_msg))], + ..Proto::SigningInput::default() + }; + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output = + TWDataHelper::wrap(unsafe { tw_any_signer_sign(input_data.ptr(), NATIVE_EVMOS_COIN_TYPE) }) + .to_vec() + .expect("!tw_any_signer_sign returned nullptr"); + + let output: Proto::SigningOutput = deserialize(&output).unwrap(); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + assert_eq!( + output.json, + r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"200","denom":"muon"}],"gas":"200000"},"memo":"","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"1","denom":"muon"}],"from_address":"evmos1hsk6jryyqjfhp5dhc55tc9jtckygx0ep4mur4z","to_address":"evmos1zt50azupanqlfam5afhv3hexwyutnuke45f6ye"}}],"signatures":[{"pub_key":{"type":"ethermint/PubKeyEthSecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"1hMFtRqKjB8tiuyHYVYZundPdomebIIvHLC1gj9uXtFc+iO3UAHBysBjFB4brd9AD5yriS3uUDTAqqfg6fNGNg=="}]}}"# + ); + assert_eq!( + output.signature_json, + r#"[{"pub_key":{"type":"ethermint/PubKeyEthSecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"1hMFtRqKjB8tiuyHYVYZundPdomebIIvHLC1gj9uXtFc+iO3UAHBysBjFB4brd9AD5yriS3uUDTAqqfg6fNGNg=="}]"# + ); +} + +/// CompoundingAuthz +#[test] +fn test_sign_native_evmos_tx_protobuf() { + use Proto::mod_Message::mod_AuthGrant::OneOfgrant_type as ProtoGrantType; + use Proto::mod_Message::mod_StakeAuthorization::OneOfvalidators as ProtoValidatorsType; + + let allow_list = Proto::mod_Message::mod_StakeAuthorization::Validators { + address: vec!["evmosvaloper1umk407eed7af6anvut6llg2zevnf0dn0feqqny".into()], + }; + let stake_authorization = Proto::mod_Message::StakeAuthorization { + authorization_type: Proto::mod_Message::AuthorizationType::DELEGATE, + validators: ProtoValidatorsType::allow_list(allow_list), + ..Proto::mod_Message::StakeAuthorization::default() + }; + let auth_grant = Proto::mod_Message::AuthGrant { + granter: "evmos12m9grgas60yk0kult076vxnsrqz8xpjy9rpf3e".into(), + grantee: "evmos18fzq4nac28gfma6gqfvkpwrgpm5ctar2z9mxf3".into(), + grant_type: ProtoGrantType::grant_stake(stake_authorization), + expiration: 1692309600, + }; + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::Protobuf, + account_number: 2139877, + chain_id: "evmos_9001-2".into(), + sequence: 3, + fee: Some(make_fee(180859, make_amount("aevmos", "4521475000000000"))), + private_key: account_2139877_private_key(), + messages: vec![make_message(MessageEnum::auth_grant(auth_grant))], + ..Proto::SigningInput::default() + }; + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output = + TWDataHelper::wrap(unsafe { tw_any_signer_sign(input_data.ptr(), NATIVE_EVMOS_COIN_TYPE) }) + .to_vec() + .expect("!tw_any_signer_sign returned nullptr"); + + let output: Proto::SigningOutput = deserialize(&output).unwrap(); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + // Original test: https://github.com/trustwallet/wallet-core/blob/a60033f797e33628e557af7c66be539c8d78bc61/tests/chains/Evmos/SignerTests.cpp#L91-L124 + // Please note the signature has been updated according to the serialization of the `StakeAuthorization` message. + // Previous: CvUBCvIBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQSzwEKLGV2bW9zMTJtOWdyZ2FzNjB5azBrdWx0MDc2dnhuc3Jxejh4cGp5OXJwZjNlEixldm1vczE4ZnpxNG5hYzI4Z2ZtYTZncWZ2a3B3cmdwbTVjdGFyMno5bXhmMxpxCmcKKi9jb3Ntb3Muc3Rha2luZy52MWJldGExLlN0YWtlQXV0aG9yaXphdGlvbhI5EjUKM2V2bW9zdmFsb3BlcjF1bWs0MDdlZWQ3YWY2YW52dXQ2bGxnMnpldm5mMGRuMGZlcXFueSABEgYI4LD6pgYSfQpZCk8KKC9ldGhlcm1pbnQuY3J5cHRvLnYxLmV0aHNlY3AyNTZrMS5QdWJLZXkSIwohA4B2WHbj6sH/GWE7z/YW5PRnXYFGaGRAov7gZZI2Fv2nEgQKAggBGAMSIAoaCgZhZXZtb3MSEDQ1MjE0NzUwMDAwMDAwMDAQ+4QLGkAm17CZgB7m+CPVlITnrHosklMTL9zrUeGRs8FL8N0GcRami9zdJ+e3xuXOtJmwP7G6QNh85CRYjFj8a8lpmmJM + assert_eq!( + output.serialized, + r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CvUBCvIBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQSzwEKLGV2bW9zMTJtOWdyZ2FzNjB5azBrdWx0MDc2dnhuc3Jxejh4cGp5OXJwZjNlEixldm1vczE4ZnpxNG5hYzI4Z2ZtYTZncWZ2a3B3cmdwbTVjdGFyMno5bXhmMxpxCmcKKi9jb3Ntb3Muc3Rha2luZy52MWJldGExLlN0YWtlQXV0aG9yaXphdGlvbhI5IAESNQozZXZtb3N2YWxvcGVyMXVtazQwN2VlZDdhZjZhbnZ1dDZsbGcyemV2bmYwZG4wZmVxcW55EgYI4LD6pgYSfQpZCk8KKC9ldGhlcm1pbnQuY3J5cHRvLnYxLmV0aHNlY3AyNTZrMS5QdWJLZXkSIwohA4B2WHbj6sH/GWE7z/YW5PRnXYFGaGRAov7gZZI2Fv2nEgQKAggBGAMSIAoaCgZhZXZtb3MSEDQ1MjE0NzUwMDAwMDAwMDAQ+4QLGkBXaTo3nk5EMFW9Euheez5ADx2bWo7XisNJ5vuGj1fKXh6CGNJGfJj/q1XUkBzaCvPNg+EcFHgtJdVSyF4cJZTg"}"# + ); +} diff --git a/rust/tw_any_coin/tests/chains/native_injective/mod.rs b/rust/tw_any_coin/tests/chains/native_injective/mod.rs new file mode 100644 index 00000000000..2781305c015 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/native_injective/mod.rs @@ -0,0 +1,8 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod native_injective_compile; +pub mod native_injective_sign; diff --git a/rust/tw_any_coin/tests/chains/native_injective/native_injective_compile.rs b/rust/tw_any_coin/tests/chains/native_injective/native_injective_compile.rs new file mode 100644 index 00000000000..fbffdddfb91 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/native_injective/native_injective_compile.rs @@ -0,0 +1,96 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_any_coin::ffi::tw_transaction_compiler::{ + tw_transaction_compiler_compile, tw_transaction_compiler_pre_image_hashes, +}; +use tw_coin_entry::error::SigningErrorType; +use tw_cosmos_sdk::test_utils::proto_utils::{make_amount, make_fee, make_message}; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_memory::test_utils::tw_data_vector_helper::TWDataVectorHelper; +use tw_proto::Cosmos::Proto; +use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum; +use tw_proto::TxCompiler::Proto as CompilerProto; +use tw_proto::{deserialize, serialize}; + +const NATIVE_INJECTIVE_COIN_TYPE: u32 = 10000060; +const ACCOUNT_88701_PUBLIC_KEY: &str = "04088ac2919987d927368cb2be2ade44cd0ed3616745a9699cae264b3fc5a7c3607d99f441b8340990ee990cb3eaf560f1f0bafe600c7e94a4be8392166984f728"; + +fn send_tx_input() -> Proto::SigningInput<'static> { + let send_msg = Proto::mod_Message::Send { + from_address: "inj1d0jkrsd09c7pule43y3ylrul43lwwcqaky8w8c".into(), + to_address: "inj1xmpkmxr4as00em23tc2zgmuyy2gr4h3wgcl6vd".into(), + amounts: vec![make_amount("inj", "10000000000")], + ..Proto::mod_Message::Send::default() + }; + Proto::SigningInput { + account_number: 88701, + chain_id: "injective-1".into(), + sequence: 0, + fee: Some(make_fee(110000, make_amount("inj", "100000000000000"))), + public_key: ACCOUNT_88701_PUBLIC_KEY.decode_hex().unwrap().into(), + messages: vec![make_message(MessageEnum::send_coins_message(send_msg))], + ..Proto::SigningInput::default() + } +} + +#[test] +fn test_compile_native_injective_tx_protobuf() { + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::Protobuf, + ..send_tx_input() + }; + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + // Step 1: Obtain preimage hash + + let preimage_output_data = TWDataHelper::wrap(unsafe { + tw_transaction_compiler_pre_image_hashes(NATIVE_INJECTIVE_COIN_TYPE, input_data.ptr()) + }) + .to_vec() + .expect("!tw_any_signer_sign returned nullptr"); + + let preimage_output: CompilerProto::PreSigningOutput = + deserialize(&preimage_output_data).unwrap(); + assert_eq!(preimage_output.error, SigningErrorType::OK); + assert!(preimage_output.error_message.is_empty()); + + let expected_preimage = "0a8f010a8c010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e64126c0a2a696e6a3164306a6b7273643039633770756c6534337933796c72756c34336c77776371616b7938773863122a696e6a31786d706b6d78723461733030656d32337463327a676d7579793267723468337767636c3676641a120a03696e6a120b3130303030303030303030129c010a7c0a740a2d2f696e6a6563746976652e63727970746f2e763162657461312e657468736563703235366b312e5075624b657912430a4104088ac2919987d927368cb2be2ade44cd0ed3616745a9699cae264b3fc5a7c3607d99f441b8340990ee990cb3eaf560f1f0bafe600c7e94a4be8392166984f72812040a020801121c0a160a03696e6a120f31303030303030303030303030303010b0db061a0b696e6a6563746976652d3120fdb405"; + assert_eq!(preimage_output.data.to_hex(), expected_preimage); + let expected_prehash = "57dc10c3d1893ff16e1f5a47fa4b2e96f37b9c57d98a42d88c971debb4947ec9"; + assert_eq!(preimage_output.data_hash.to_hex(), expected_prehash); + + // Step 2: Compile transaction info + + // Simulate signature, normally obtained from signature server + let signature = "f7a9ec0a521170bb5566ca973d3c73a1b69b162d99ce022059189991ec440637333394ff1c9e75fad84eb114393969f20989b036f1dfed28949e906dc0077421".decode_hex().unwrap(); + let public_key = ACCOUNT_88701_PUBLIC_KEY.decode_hex().unwrap(); + + let signatures = TWDataVectorHelper::create([signature.clone()]); + let public_keys = TWDataVectorHelper::create([public_key]); + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + let output_data = TWDataHelper::wrap(unsafe { + tw_transaction_compiler_compile( + NATIVE_INJECTIVE_COIN_TYPE, + input_data.ptr(), + signatures.ptr(), + public_keys.ptr(), + ) + }) + .to_vec() + .expect("!tw_transaction_compiler_compile returned nullptr"); + + let output: Proto::SigningOutput = + deserialize(&output_data).expect("Coin entry returned an invalid output"); + + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + let expected_encoded = r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"Co8BCowBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmwKKmluajFkMGprcnNkMDljN3B1bGU0M3kzeWxydWw0M2x3d2NxYWt5OHc4YxIqaW5qMXhtcGtteHI0YXMwMGVtMjN0YzJ6Z211eXkyZ3I0aDN3Z2NsNnZkGhIKA2luahILMTAwMDAwMDAwMDASnAEKfAp0Ci0vaW5qZWN0aXZlLmNyeXB0by52MWJldGExLmV0aHNlY3AyNTZrMS5QdWJLZXkSQwpBBAiKwpGZh9knNoyyvireRM0O02FnRalpnK4mSz/Fp8NgfZn0Qbg0CZDumQyz6vVg8fC6/mAMfpSkvoOSFmmE9ygSBAoCCAESHAoWCgNpbmoSDzEwMDAwMDAwMDAwMDAwMBCw2wYaQPep7ApSEXC7VWbKlz08c6G2mxYtmc4CIFkYmZHsRAY3MzOU/xyedfrYTrEUOTlp8gmJsDbx3+0olJ6QbcAHdCE="}"#; + assert_eq!(output.serialized, expected_encoded); + assert_eq!(output.signature.to_hex(), signature.to_hex()); +} diff --git a/rust/tw_any_coin/tests/chains/native_injective/native_injective_sign.rs b/rust/tw_any_coin/tests/chains/native_injective/native_injective_sign.rs new file mode 100644 index 00000000000..f5d7e448a27 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/native_injective/native_injective_sign.rs @@ -0,0 +1,97 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::borrow::Cow; +use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; +use tw_coin_entry::error::SigningErrorType; +use tw_cosmos_sdk::test_utils::proto_utils::{make_amount, make_fee, make_message}; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_proto::Cosmos::Proto; +use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum; +use tw_proto::{deserialize, serialize}; + +const NATIVE_INJECTIVE_COIN_TYPE: u32 = 10000060; + +fn account_17396_private_key() -> Cow<'static, [u8]> { + "9ee18daf8e463877aaf497282abc216852420101430482a28e246c179e2c5ef1" + .decode_hex() + .unwrap() + .into() +} + +fn send_tx_input() -> Proto::SigningInput<'static> { + let send_msg = Proto::mod_Message::Send { + from_address: "inj13u6g7vqgw074mgmf2ze2cadzvkz9snlwcrtq8a".into(), + to_address: "inj1xmpkmxr4as00em23tc2zgmuyy2gr4h3wgcl6vd".into(), + amounts: vec![make_amount("inj", "10000000000")], + ..Proto::mod_Message::Send::default() + }; + Proto::SigningInput { + account_number: 17396, + chain_id: "injective-1".into(), + sequence: 1, + fee: Some(make_fee(110000, make_amount("inj", "100000000000000"))), + private_key: account_17396_private_key(), + messages: vec![make_message(MessageEnum::send_coins_message(send_msg))], + ..Proto::SigningInput::default() + } +} + +#[test] +fn test_sign_native_injective_tx_protobuf() { + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::Protobuf, + ..send_tx_input() + }; + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output = TWDataHelper::wrap(unsafe { + tw_any_signer_sign(input_data.ptr(), NATIVE_INJECTIVE_COIN_TYPE) + }) + .to_vec() + .expect("!tw_any_signer_sign returned nullptr"); + + let output: Proto::SigningOutput = deserialize(&output).unwrap(); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + // https://www.mintscan.io/injective/txs/135DD2C4A1910E4334A9C0F15125DA992E724EBF23FEB9638FCB71218BB064A5 + assert_eq!( + output.serialized, + r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"Co8BCowBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmwKKmluajEzdTZnN3ZxZ3cwNzRtZ21mMnplMmNhZHp2a3o5c25sd2NydHE4YRIqaW5qMXhtcGtteHI0YXMwMGVtMjN0YzJ6Z211eXkyZ3I0aDN3Z2NsNnZkGhIKA2luahILMTAwMDAwMDAwMDASngEKfgp0Ci0vaW5qZWN0aXZlLmNyeXB0by52MWJldGExLmV0aHNlY3AyNTZrMS5QdWJLZXkSQwpBBFoMa4O4vZgn5QcnDK20mbfjqQlSRvaiITKB94PYd8mLJWdCdBsGOfMXdo/k9MJ2JmDCESKDp2hdgVUH3uMikXMSBAoCCAEYARIcChYKA2luahIPMTAwMDAwMDAwMDAwMDAwELDbBhpAx2vkplmzeK7n3puCFGPWhLd0l/ZC/CYkGl+stH+3S3hiCvIe7uwwMpUlNaSwvT8HwF1kNUp+Sx2m0Uo1x5xcFw=="}"# + ); + assert_eq!(output.signature.to_hex(), "c76be4a659b378aee7de9b821463d684b77497f642fc26241a5facb47fb74b78620af21eeeec3032952535a4b0bd3f07c05d64354a7e4b1da6d14a35c79c5c17"); +} + +#[test] +fn test_sign_native_injective_tx_json() { + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::JSON, + ..send_tx_input() + }; + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output = TWDataHelper::wrap(unsafe { + tw_any_signer_sign(input_data.ptr(), NATIVE_INJECTIVE_COIN_TYPE) + }) + .to_vec() + .expect("!tw_any_signer_sign returned nullptr"); + + let output: Proto::SigningOutput = deserialize(&output).unwrap(); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + // This transaction hasn't been broadcasted. + assert_eq!( + output.json, + r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"100000000000000","denom":"inj"}],"gas":"110000"},"memo":"","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"10000000000","denom":"inj"}],"from_address":"inj13u6g7vqgw074mgmf2ze2cadzvkz9snlwcrtq8a","to_address":"inj1xmpkmxr4as00em23tc2zgmuyy2gr4h3wgcl6vd"}}],"signatures":[{"pub_key":{"type":"injective/PubKeyEthSecp256k1","value":"BFoMa4O4vZgn5QcnDK20mbfjqQlSRvaiITKB94PYd8mLJWdCdBsGOfMXdo/k9MJ2JmDCESKDp2hdgVUH3uMikXM="},"signature":"7wwedceebL95DwbClz5AzEp2Z74itHC7raiV976DcacfjrJ58oDfjbAO5UOZQAlihiYBP7PpyISFQ72FPRhdZA=="}]}}"# + ); + assert_eq!( + output.signature_json, + r#"[{"pub_key":{"type":"injective/PubKeyEthSecp256k1","value":"BFoMa4O4vZgn5QcnDK20mbfjqQlSRvaiITKB94PYd8mLJWdCdBsGOfMXdo/k9MJ2JmDCESKDp2hdgVUH3uMikXM="},"signature":"7wwedceebL95DwbClz5AzEp2Z74itHC7raiV976DcacfjrJ58oDfjbAO5UOZQAlihiYBP7PpyISFQ72FPRhdZA=="}]"# + ); +} diff --git a/rust/tw_any_coin/tests/chains/thorchain/mod.rs b/rust/tw_any_coin/tests/chains/thorchain/mod.rs new file mode 100644 index 00000000000..3f38961fcbb --- /dev/null +++ b/rust/tw_any_coin/tests/chains/thorchain/mod.rs @@ -0,0 +1,11 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +mod test_cases; +mod thorchain_compile; +mod thorchain_sign; + +const THORCHAIN_COIN_TYPE: u32 = 931; diff --git a/rust/tw_any_coin/tests/chains/thorchain/test_cases.rs b/rust/tw_any_coin/tests/chains/thorchain/test_cases.rs new file mode 100644 index 00000000000..e626e3466c7 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/thorchain/test_cases.rs @@ -0,0 +1,41 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_cosmos_sdk::test_utils::proto_utils::{make_amount, make_fee, make_message}; +use tw_proto::Cosmos::Proto; +use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum; + +/// https://viewblock.io/thorchain/tx/FD0445AFFC4ED9ACCB7B5D3ADE361DAE4596EA096340F1360F1020381EA454AF +pub(super) mod send_fd0445af { + use super::*; + + pub const PRIVATE_KEY: &str = + "7105512f0c020a1dd759e14b865ec0125f59ac31e34d7a2807a228ed50cb343e"; + pub const JSON_TX_PREIMAGE: &str = r#"{"account_number":"593","chain_id":"thorchain","fee":{"amount":[{"amount":"2000000","denom":"rune"}],"gas":"200000"},"memo":"","msgs":[{"type":"thorchain/MsgSend","value":{"amount":[{"amount":"10000000","denom":"rune"}],"from_address":"thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r","to_address":"thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn"}}],"sequence":"3"}"#; + /// Expected `json` value. + pub const JSON_TX: &str = r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"2000000","denom":"rune"}],"gas":"200000"},"memo":"","msg":[{"type":"thorchain/MsgSend","value":{"amount":[{"amount":"10000000","denom":"rune"}],"from_address":"thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r","to_address":"thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A+2Zfjls9CkvX85aQrukFZnM1dluMTFUp8nqcEneMXx3"},"signature":"qgpMX3WNq4DsNBnYtdmBD4ejiailK4uI/m3/YVqCSNF8AtkUOTmP48ztqCbpkWTFvw1/9S8/ivsFxOcK6AI0jA=="}]}}"#; + /// Expected `signature` for JSON signing mode. + pub const JSON_SIGNING_SIGNATURE: &str = "aa0a4c5f758dab80ec3419d8b5d9810f87a389a8a52b8b88fe6dff615a8248d17c02d91439398fe3cceda826e99164c5bf0d7ff52f3f8afb05c4e70ae802348c"; + /// Expected `signature_json` for JSON signing mode. + pub const JSON_SIGNING_SIGNATURE_JSON: &str = r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A+2Zfjls9CkvX85aQrukFZnM1dluMTFUp8nqcEneMXx3"},"signature":"qgpMX3WNq4DsNBnYtdmBD4ejiailK4uI/m3/YVqCSNF8AtkUOTmP48ztqCbpkWTFvw1/9S8/ivsFxOcK6AI0jA=="}]"#; + + pub fn signing_input() -> Proto::SigningInput<'static> { + let send_msg = Proto::mod_Message::Send { + from_address: "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r".into(), + to_address: "thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn".into(), + amounts: vec![make_amount("rune", "10000000")], + ..Proto::mod_Message::Send::default() + }; + Proto::SigningInput { + account_number: 593, + chain_id: "thorchain".into(), + sequence: 3, + fee: Some(make_fee(200000, make_amount("rune", "2000000"))), + messages: vec![make_message(MessageEnum::send_coins_message(send_msg))], + ..Proto::SigningInput::default() + } + } +} diff --git a/rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs b/rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs new file mode 100644 index 00000000000..ed5a2eb354c --- /dev/null +++ b/rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs @@ -0,0 +1,97 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::chains::thorchain::test_cases::send_fd0445af::{ + signing_input, JSON_SIGNING_SIGNATURE, JSON_SIGNING_SIGNATURE_JSON, JSON_TX, JSON_TX_PREIMAGE, + PRIVATE_KEY, +}; +use crate::chains::thorchain::THORCHAIN_COIN_TYPE; +use tw_any_coin::ffi::tw_transaction_compiler::{ + tw_transaction_compiler_compile, tw_transaction_compiler_pre_image_hashes, +}; +use tw_coin_entry::error::SigningErrorType; +use tw_encoding::hex::ToHex; +use tw_hash::H256; +use tw_keypair::ecdsa::secp256k1; +use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_memory::test_utils::tw_data_vector_helper::TWDataVectorHelper; +use tw_misc::traits::ToBytesVec; +use tw_proto::Cosmos::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; +use tw_proto::{deserialize, serialize}; + +#[test] +fn test_any_signer_compile_thorchain() { + let private_key = secp256k1::KeyPair::try_from(PRIVATE_KEY).unwrap(); + let public_key = private_key.public().to_vec(); + + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::JSON, + public_key: public_key.clone().into(), + ..signing_input() + }; + + // Step 2: Obtain preimage hash + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + let preimage_data = TWDataHelper::wrap(unsafe { + tw_transaction_compiler_pre_image_hashes(THORCHAIN_COIN_TYPE, input_data.ptr()) + }) + .to_vec() + .expect("!tw_transaction_compiler_pre_image_hashes returned nullptr"); + + let preimage: CompilerProto::PreSigningOutput = + deserialize(&preimage_data).expect("Coin entry returned an invalid output"); + + assert_eq!(preimage.error, SigningErrorType::OK); + assert!(preimage.error_message.is_empty()); + let tx_preimage = String::from_utf8(preimage.data.to_vec()) + .expect("Invalid transaction preimage. Expected a JSON object"); + assert_eq!(tx_preimage, JSON_TX_PREIMAGE); + + // Step 3: Sign the data "externally" + + let tx_hash = H256::try_from(preimage.data_hash.as_ref()).expect("Invalid Transaction Hash"); + + let signature = private_key + .sign(tx_hash) + .expect("Error signing data") + .to_vec(); + assert!( + signature.to_hex().contains(JSON_SIGNING_SIGNATURE), + "{} must contain {}", + signature.to_hex(), + JSON_SIGNING_SIGNATURE + ); + + // Step 4: Compile transaction info + + let signatures = TWDataVectorHelper::create([signature]); + let public_keys = TWDataVectorHelper::create([public_key]); + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + let output_data = TWDataHelper::wrap(unsafe { + tw_transaction_compiler_compile( + THORCHAIN_COIN_TYPE, + input_data.ptr(), + signatures.ptr(), + public_keys.ptr(), + ) + }) + .to_vec() + .expect("!tw_transaction_compiler_compile returned nullptr"); + + let output: Proto::SigningOutput = + deserialize(&output_data).expect("Coin entry returned an invalid output"); + + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + // https://viewblock.io/thorchain/tx/FD0445AFFC4ED9ACCB7B5D3ADE361DAE4596EA096340F1360F1020381EA454AF + assert_eq!(output.json, JSON_TX); + assert_eq!(output.signature.to_hex(), JSON_SIGNING_SIGNATURE); + assert_eq!(output.signature_json, JSON_SIGNING_SIGNATURE_JSON); +} diff --git a/rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs b/rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs new file mode 100644 index 00000000000..585166daa76 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs @@ -0,0 +1,41 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::chains::thorchain::test_cases::send_fd0445af::{ + signing_input, JSON_SIGNING_SIGNATURE, JSON_SIGNING_SIGNATURE_JSON, JSON_TX, PRIVATE_KEY, +}; +use crate::chains::thorchain::THORCHAIN_COIN_TYPE; +use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; +use tw_coin_entry::error::SigningErrorType; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_proto::Cosmos::Proto; +use tw_proto::{deserialize, serialize}; + +#[test] +fn test_any_signer_sign_thorchain() { + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::JSON, + private_key: PRIVATE_KEY.decode_hex().unwrap().into(), + ..signing_input() + }; + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output = + TWDataHelper::wrap(unsafe { tw_any_signer_sign(input_data.ptr(), THORCHAIN_COIN_TYPE) }) + .to_vec() + .expect("!tw_any_signer_sign returned nullptr"); + + let output: Proto::SigningOutput = deserialize(&output).unwrap(); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + // https://viewblock.io/thorchain/tx/FD0445AFFC4ED9ACCB7B5D3ADE361DAE4596EA096340F1360F1020381EA454AF + assert_eq!(output.json, JSON_TX); + assert_eq!(output.signature.to_hex(), JSON_SIGNING_SIGNATURE); + assert_eq!(output.signature_json, JSON_SIGNING_SIGNATURE_JSON); +} diff --git a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs index 10013b4e6d3..0f4a6e9d928 100644 --- a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs +++ b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs @@ -5,8 +5,9 @@ // file LICENSE at the root of the source code distribution tree. use tw_any_coin::ffi::tw_any_address::{ - tw_any_address_create_with_public_key_derivation, tw_any_address_create_with_string, - tw_any_address_data, tw_any_address_description, tw_any_address_is_valid, + tw_any_address_create_bech32_with_public_key, tw_any_address_create_with_public_key_derivation, + tw_any_address_create_with_string, tw_any_address_data, tw_any_address_description, + tw_any_address_is_valid, tw_any_address_is_valid_bech32, }; use tw_any_coin::test_utils::TWAnyAddressHelper; use tw_coin_entry::derivation::Derivation; @@ -16,10 +17,12 @@ use tw_encoding::hex::DecodeHex; use tw_keypair::ffi::privkey::tw_private_key_get_public_key_by_type; use tw_keypair::test_utils::tw_private_key_helper::TWPrivateKeyHelper; use tw_keypair::test_utils::tw_public_key_helper::TWPublicKeyHelper; +use tw_keypair::tw::PublicKeyType; use tw_memory::test_utils::tw_data_helper::TWDataHelper; use tw_memory::test_utils::tw_string_helper::TWStringHelper; const ETHEREUM_COIN_TYPE: u32 = 60; +const OSMOSIS_COIN_TYPE: u32 = 10000118; #[test] fn test_any_address_derive() { @@ -39,11 +42,20 @@ fn test_any_address_derive() { }, // By default, Bitcoin will return a P2PKH address. BlockchainType::Bitcoin => "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", + BlockchainType::Cosmos if coin.id == "cosmos" => { + "cosmos1ten42eesehw0ktddcp0fws7d3ycsqez3lynlqx" + }, + // Skip other Cosmos chains as they have different addresses. + // TODO fix this when `CoinType` is generated by a codegen tool. + BlockchainType::Cosmos => continue, BlockchainType::Ethereum => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", - BlockchainType::Ronin => "ronin:Ac1ec44E4f0ca7D172B7803f6836De87Fb72b309", BlockchainType::InternetComputer => { "290cc7c359f44c8516fc169c5ed4f0f3ae2e24bf5de0d4c51f5e7545b5474faa" }, + BlockchainType::NativeEvmos => "evmos14s0vgnj0pjnazu4hsqlksdk7slah9vcfvt8ssm", + BlockchainType::NativeInjective => "inj14s0vgnj0pjnazu4hsqlksdk7slah9vcfyrp6ct", + BlockchainType::Ronin => "ronin:Ac1ec44E4f0ca7D172B7803f6836De87Fb72b309", + BlockchainType::Thorchain => "thor1ten42eesehw0ktddcp0fws7d3ycsqez3er2y4e", BlockchainType::Unsupported => unreachable!(), }; @@ -64,6 +76,7 @@ fn test_any_address_derive() { #[test] fn test_any_address_normalize_eth() { for coin in supported_coin_items() { + // TODO match `CoinType` when it's generated. let (denormalized, expected_normalized) = match coin.blockchain { BlockchainType::Aptos => ( "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", @@ -73,17 +86,35 @@ fn test_any_address_normalize_eth() { "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", ), + BlockchainType::Cosmos if coin.id == "cosmos" => ( + "cosmosvaloper1sxx9mszve0gaedz5ld7qdkjkfv8z992ax69k08", + "cosmosvaloper1sxx9mszve0gaedz5ld7qdkjkfv8z992ax69k08", + ), + // Skip other Cosmos chains until `CoinType` is not generated by a codegen tool. + BlockchainType::Cosmos => continue, BlockchainType::Ethereum => ( "0xb16db98b365b1f89191996942612b14f1da4bd5f", "0xb16Db98B365B1f89191996942612B14F1Da4Bd5f", ), + BlockchainType::InternetComputer => ( + "290CC7C359F44C8516FC169C5ED4F0F3AE2E24BF5DE0D4C51F5E7545B5474FAA", + "290cc7c359f44c8516fc169c5ed4f0f3ae2e24bf5de0d4c51f5e7545b5474faa", + ), + BlockchainType::NativeEvmos => ( + "evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw34", + "evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw34", + ), + BlockchainType::NativeInjective => ( + "inj14py36sx57ud82t9yrks9z6hdsrpn5x6k8tf7m3", + "inj14py36sx57ud82t9yrks9z6hdsrpn5x6k8tf7m3", + ), BlockchainType::Ronin => ( "0xb16db98b365b1f89191996942612b14f1da4bd5f", "ronin:b16Db98B365B1f89191996942612B14F1Da4Bd5f", ), - BlockchainType::InternetComputer => ( - "290CC7C359F44C8516FC169C5ED4F0F3AE2E24BF5DE0D4C51F5E7545B5474FAA", - "290cc7c359f44c8516fc169c5ed4f0f3ae2e24bf5de0d4c51f5e7545b5474faa", + BlockchainType::Thorchain => ( + "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r", + "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r", ), BlockchainType::Unsupported => unreachable!(), }; @@ -121,21 +152,40 @@ fn test_any_address_is_valid_coin() { "bc1qunq74p3h8425hr6wllevlvqqr6sezfxj262rff", "bc1pwse34zfpvt344rvlt7tw0ngjtfh9xasc4q03avf0lk74jzjpzjuqaz7ks5", ], + BlockchainType::Cosmos if coin.id == "cosmos" => vec![ + "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02", + "cosmosvaloper1sxx9mszve0gaedz5ld7qdkjkfv8z992ax69k08", + "cosmosvalconspub1zcjduepqjnnwe2jsywv0kfc97pz04zkm7tc9k2437cde2my3y5js9t7cw9mstfg3sa", + ], + // Skip other Cosmos chains until `CoinType` is not generated by a codegen tool. + BlockchainType::Cosmos => continue, BlockchainType::Ethereum => vec![ "0xb16db98b365b1f89191996942612b14f1da4bd5f", "0xb16Db98B365B1f89191996942612B14F1Da4Bd5f", ], + BlockchainType::InternetComputer => vec![ + "fb257577279ecac604d4780214af95aa6adc3a814f6f3d6d7ac844d1deca500a", + "e90c48d54847f4758f1d6b589a1db2500757a49a6722d4f775e050107b4b752d", + "a7c5baf393aed527ef6fb3869fbf84dd4e562edf9b04bd8f9bfbd6b8e6a22776", + "4cb2ca5cfcfa1d952f8cd7f0ec46c96e1023ab057b83a2c7ce236b9e71ccca0b", + ], + BlockchainType::NativeEvmos => vec![ + "evmos14py36sx57ud82t9yrks9z6hdsrpn5x6k0r05np", + "evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw34" + ], + BlockchainType::NativeInjective => vec![ + "inj13u6g7vqgw074mgmf2ze2cadzvkz9snlwcrtq8a", + "inj1xmpkmxr4as00em23tc2zgmuyy2gr4h3wgcl6vd" + ], BlockchainType::Ronin => vec![ "0xb16db98b365b1f89191996942612b14f1da4bd5f", "0xb16Db98B365B1f89191996942612B14F1Da4Bd5f", "ronin:b16db98b365b1f89191996942612b14f1da4bd5f", "ronin:b16Db98B365B1f89191996942612B14F1Da4Bd5f", ], - BlockchainType::InternetComputer => vec![ - "fb257577279ecac604d4780214af95aa6adc3a814f6f3d6d7ac844d1deca500a", - "e90c48d54847f4758f1d6b589a1db2500757a49a6722d4f775e050107b4b752d", - "a7c5baf393aed527ef6fb3869fbf84dd4e562edf9b04bd8f9bfbd6b8e6a22776", - "4cb2ca5cfcfa1d952f8cd7f0ec46c96e1023ab057b83a2c7ce236b9e71ccca0b", + BlockchainType::Thorchain => vec![ + "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r", + "thor1c8jd7ad9pcw4k3wkuqlkz4auv95mldr2kyhc65", ], _ => unreachable!(), }; @@ -162,6 +212,11 @@ fn test_any_address_is_valid_coin_invalid() { BlockchainType::Bitcoin => { vec!["0xb16db98b365b1f89191996942612b14f1da4bd5f"] }, + BlockchainType::Cosmos => vec![ + "cosmosvaloper1sxx9mszve0gaedz5ld7qdkjkfv8z992ax6", + "one1a50tun737ulcvwy0yvve0pvu5skq0kjargvhwe", + "bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2", + ], BlockchainType::Ethereum | BlockchainType::Ronin => { vec!["b16Db98B365B1f89191996942612B14F1Da4Bd5f"] }, @@ -170,6 +225,12 @@ fn test_any_address_is_valid_coin_invalid() { "3357cba483f268d044d4bbd4639f82c16028a76eebdf62c51bc11fc918d278bce", "553357cba483f268d044d4bbd4639f82c16028a76eebdf62c51bc11fc918d278", ], + BlockchainType::NativeEvmos => vec!["evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw"], + BlockchainType::NativeInjective => vec!["ini13u6g7vqgw074mgmf2ze2cadzvkz9snlwcrtq8a"], + BlockchainType::Thorchain => vec![ + "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02", + "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0e", + ], BlockchainType::Unsupported => unreachable!(), }; @@ -191,3 +252,36 @@ fn test_any_address_get_data_eth() { let data = TWDataHelper::wrap(unsafe { tw_any_address_data(any_address.ptr()) }); assert_eq!(data.to_vec(), Some(addr.decode_hex().unwrap())); } + +#[test] +fn test_any_address_is_valid_bech32() { + let addr = "juno1mry47pkga5tdswtluy0m8teslpalkdq0gnn4mf"; + + let address_str = TWStringHelper::create(addr); + let hrp = TWStringHelper::create("juno"); + // Should be valid even though Osmosis chain has `osmo` default hrp. + let result = + unsafe { tw_any_address_is_valid_bech32(address_str.ptr(), OSMOSIS_COIN_TYPE, hrp.ptr()) }; + assert!(result); +} + +#[test] +fn test_any_address_create_bech32_with_public_key() { + let private_key = TWPrivateKeyHelper::with_hex( + "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", + ); + let public_key = TWPublicKeyHelper::wrap(unsafe { + tw_private_key_get_public_key_by_type(private_key.ptr(), PublicKeyType::Secp256k1 as u32) + }); + let hrp = TWStringHelper::create("juno"); + + // Should be valid even though Osmosis chain has `osmo` default hrp. + let any_address = TWAnyAddressHelper::wrap(unsafe { + tw_any_address_create_bech32_with_public_key(public_key.ptr(), OSMOSIS_COIN_TYPE, hrp.ptr()) + }); + + let description = + TWStringHelper::wrap(unsafe { tw_any_address_description(any_address.ptr()) }); + let expected = "juno1ten42eesehw0ktddcp0fws7d3ycsqez3fksy86"; + assert_eq!(description.to_string(), Some(expected.to_string())); +} diff --git a/rust/tw_any_coin/tests/tw_any_signer_ffi_tests.rs b/rust/tw_any_coin/tests/tw_any_signer_ffi_tests.rs index 8af80dbc39f..7677f6c838b 100644 --- a/rust/tw_any_coin/tests/tw_any_signer_ffi_tests.rs +++ b/rust/tw_any_coin/tests/tw_any_signer_ffi_tests.rs @@ -12,6 +12,7 @@ use tw_memory::test_utils::tw_data_helper::TWDataHelper; use tw_number::U256; use tw_proto::{deserialize, serialize}; +const COSMOS_COIN_TYPE: u32 = 118; const ETHEREUM_COIN_TYPE: u32 = 60; #[test] @@ -55,6 +56,58 @@ fn test_any_signer_sign_eth() { assert_eq!(output.encoded.to_hex(), expected); } +#[test] +fn test_any_signer_sign_cosmos() { + use tw_proto::Cosmos::Proto; + use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum; + + let private_key = "8bbec3772ddb4df68f3186440380c301af116d1422001c1877d6f5e4dba8c8af" + .decode_hex() + .unwrap(); + + let send_msg = Proto::mod_Message::Send { + from_address: "cosmos1mky69cn8ektwy0845vec9upsdphktxt03gkwlx".into(), + to_address: "cosmos18s0hdnsllgcclweu9aymw4ngktr2k0rkygdzdp".into(), + amounts: vec![Proto::Amount { + denom: "uatom".into(), + amount: "400000".into(), + }], + ..Proto::mod_Message::Send::default() + }; + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::Protobuf, + account_number: 546179, + chain_id: "cosmoshub-4".into(), + sequence: 0, + fee: Some(Proto::Fee { + gas: 200000, + amounts: vec![Proto::Amount { + denom: "uatom".into(), + amount: "1000".into(), + }], + }), + private_key: private_key.into(), + messages: vec![Proto::Message { + message_oneof: MessageEnum::send_coins_message(send_msg), + }], + ..Proto::SigningInput::default() + }; + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output = + TWDataHelper::wrap(unsafe { tw_any_signer_sign(input_data.ptr(), COSMOS_COIN_TYPE) }) + .to_vec() + .expect("!tw_any_signer_sign returned nullptr"); + + let output: Proto::SigningOutput = deserialize(&output).unwrap(); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected = r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CpIBCo8BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm8KLWNvc21vczFta3k2OWNuOGVrdHd5MDg0NXZlYzl1cHNkcGhrdHh0MDNna3dseBItY29zbW9zMThzMGhkbnNsbGdjY2x3ZXU5YXltdzRuZ2t0cjJrMHJreWdkemRwGg8KBXVhdG9tEgY0MDAwMDASZQpOCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAuzvXOQ3owLGf5VGjeSzHzbpEfRn1+alK0HB4T4dVjZJEgQKAggBEhMKDQoFdWF0b20SBDEwMDAQwJoMGkCvvVE6d29P30cO9/lnXyGunWMPxNY12NuqDcCnFkNM0H4CUQdl1Gc9+ogIJbro5nyzZzlv9rl2/GsZox/JXoCX"}"#; + assert_eq!(output.serialized, expected); +} + #[test] fn test_any_signer_sign_unknown_coin() { let unsupported_coin = u32::MAX; diff --git a/rust/tw_aptos/fuzz/Cargo.toml b/rust/tw_aptos/fuzz/Cargo.toml index 2d3506a0ea6..721a84b3ab9 100644 --- a/rust/tw_aptos/fuzz/Cargo.toml +++ b/rust/tw_aptos/fuzz/Cargo.toml @@ -9,7 +9,6 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" -tw_number = { path = "../../tw_number" } tw_proto = { path = "../../tw_proto", features = ["fuzz"] } [dependencies.tw_aptos] diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs index 4438a15ae2b..e2a16cd0ac8 100644 --- a/rust/tw_aptos/src/entry.rs +++ b/rust/tw_aptos/src/entry.rs @@ -44,6 +44,15 @@ impl CoinEntry for AptosEntry { Address::from_str(address) } + #[inline] + fn parse_address_unchecked( + &self, + _coin: &dyn CoinContext, + address: &str, + ) -> AddressResult { + Address::from_str(address) + } + fn derive_address( &self, _coin: &dyn CoinContext, diff --git a/rust/tw_bech32_address/Cargo.toml b/rust/tw_bech32_address/Cargo.toml new file mode 100644 index 00000000000..1ca0cfa493f --- /dev/null +++ b/rust/tw_bech32_address/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "tw_bech32_address" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { version = "1.0.163", features = [ "derive" ] } +tw_coin_entry = { path = "../tw_coin_entry" } +tw_encoding = { path = "../tw_encoding" } +tw_hash = { path = "../tw_hash" } +tw_keypair = { path = "../tw_keypair" } +tw_memory = { path = "../tw_memory" } diff --git a/rust/tw_bech32_address/src/bech32_prefix.rs b/rust/tw_bech32_address/src/bech32_prefix.rs new file mode 100644 index 00000000000..8f6f418ed2c --- /dev/null +++ b/rust/tw_bech32_address/src/bech32_prefix.rs @@ -0,0 +1,22 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::error::AddressError; +use tw_coin_entry::prefix::AddressPrefix; + +pub struct Bech32Prefix { + pub hrp: String, +} + +impl TryFrom for Bech32Prefix { + type Error = AddressError; + + fn try_from(prefix: AddressPrefix) -> Result { + match prefix { + AddressPrefix::Hrp(hrp) => Ok(Bech32Prefix { hrp }), + } + } +} diff --git a/rust/tw_bech32_address/src/lib.rs b/rust/tw_bech32_address/src/lib.rs new file mode 100644 index 00000000000..67c361f90a7 --- /dev/null +++ b/rust/tw_bech32_address/src/lib.rs @@ -0,0 +1,441 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::bech32_prefix::Bech32Prefix; +use serde::{Serialize, Serializer}; +use std::fmt; +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::CoinAddress; +use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_encoding::bech32; +use tw_hash::hasher::Hasher; +use tw_hash::H160; +use tw_keypair::tw::{PrivateKey, PublicKey, PublicKeyType}; +use tw_memory::Data; + +pub mod bech32_prefix; + +pub struct Bech32Address { + hrp: String, + key_hash: Data, + /// An address string created from this `hrp` and `key_hash`. + address_str: String, +} + +impl Bech32Address { + pub fn new(hrp: String, key_hash: Data) -> AddressResult { + let address_str = bech32::encode(&hrp, &key_hash).map_err(|_| AddressError::InvalidHrp)?; + Ok(Bech32Address { + hrp, + key_hash, + address_str, + }) + } + + pub fn with_public_key_hasher( + hrp: String, + public_key: &PublicKey, + hasher: Hasher, + ) -> AddressResult { + let public_key_bytes = match public_key.public_key_type() { + // If the public key is extended, skips the first byte (Evmos specific). + // https://github.com/trustwallet/wallet-core/blob/d67078daa580b37063c97be66a625aaee9664882/src/Bech32Address.cpp#L61-L64 + PublicKeyType::Secp256k1Extended => public_key + .to_secp256k1() + .ok_or(AddressError::PublicKeyTypeMismatch)? + .uncompressed_without_prefix() + .to_vec(), + _ => public_key.to_bytes(), + }; + + let public_key_hash = hasher.hash(&public_key_bytes); + if public_key_hash.len() < H160::LEN { + return Err(AddressError::UnexpectedHasher); + } + + let (_skipped_bytes, key_bytes) = + public_key_hash.split_at(public_key_hash.len() - H160::LEN); + Bech32Address::new(hrp, key_bytes.to_vec()) + } + + pub fn with_public_key_coin_context( + coin: &dyn CoinContext, + public_key: &PublicKey, + prefix: Option, + ) -> AddressResult { + let hrp = match prefix { + Some(Bech32Prefix { hrp }) => hrp, + None => coin.hrp().ok_or(AddressError::InvalidHrp)?, + }; + let address_hasher = coin + .address_hasher() + .ok_or(AddressError::UnexpectedHasher)?; + Self::with_public_key_hasher(hrp, public_key, address_hasher) + } + + pub fn with_private_key_coin_context( + coin: &dyn CoinContext, + private_key: &PrivateKey, + ) -> AddressResult { + let hrp = coin.hrp().ok_or(AddressError::InvalidHrp)?; + let public_key_type = coin.public_key_type(); + let public_key = private_key + .get_public_key_by_type(public_key_type) + .map_err(|_| AddressError::PublicKeyTypeMismatch)?; + let address_hasher = coin + .address_hasher() + .ok_or(AddressError::UnexpectedHasher)?; + Bech32Address::with_public_key_hasher(hrp, &public_key, address_hasher) + } + + pub fn from_str_checked( + expected_hrp: &str, + address_str: String, + ) -> AddressResult { + let bech32::Decoded { hrp, bytes } = + bech32::decode(&address_str).map_err(|_| AddressError::InvalidInput)?; + // Copied from the legacy Bech32Address.cpp: + // https://github.com/trustwallet/wallet-core/blob/d67078daa580b37063c97be66a625aaee9664882/src/Bech32Address.cpp#L21 + if !hrp.starts_with(expected_hrp) { + return Err(AddressError::InvalidHrp); + } + Ok(Bech32Address { + hrp, + key_hash: bytes, + address_str, + }) + } + + pub fn from_str_with_coin_and_prefix( + coin: &dyn CoinContext, + address_str: String, + prefix: Option, + ) -> AddressResult + where + Self: Sized, + { + let hrp = match prefix { + Some(Bech32Prefix { hrp }) => hrp, + None => coin.hrp().ok_or(AddressError::InvalidHrp)?, + }; + Self::from_str_checked(&hrp, address_str) + } + + pub fn key_hash(&self) -> &[u8] { + &self.key_hash + } + + pub fn hrp(&self) -> &str { + &self.hrp + } +} + +impl CoinAddress for Bech32Address { + fn data(&self) -> Data { + self.key_hash.to_vec() + } +} + +impl FromStr for Bech32Address { + type Err = AddressError; + + fn from_str(s: &str) -> Result { + let bech32::Decoded { hrp, bytes } = + bech32::decode(s).map_err(|_| AddressError::InvalidInput)?; + Ok(Bech32Address { + hrp, + key_hash: bytes, + address_str: s.to_string(), + }) + } +} + +impl fmt::Display for Bech32Address { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.address_str) + } +} + +impl fmt::Debug for Bech32Address { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{self}") + } +} + +impl Serialize for Bech32Address { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tw_encoding::hex::{DecodeHex, ToHex}; + + struct FromPublicKeyTestInput<'a> { + hrp: &'a str, + private_key: &'a str, + public_key_type: PublicKeyType, + hasher: Hasher, + expected: &'a str, + } + + fn test_from_public_key(input: FromPublicKeyTestInput<'_>) { + let private_key = PrivateKey::new(input.private_key.decode_hex().unwrap()).unwrap(); + let public_key = private_key + .get_public_key_by_type(input.public_key_type) + .unwrap(); + let actual = + Bech32Address::with_public_key_hasher(input.hrp.to_string(), &public_key, input.hasher) + .unwrap(); + assert_eq!(actual.to_string(), input.expected); + } + + #[test] + fn test_address_from_str_checked_valid() { + fn test_impl(addr: &str, hrp: &str) { + Bech32Address::from_str_checked(hrp, addr.to_string()) + .unwrap_or_else(|e| panic!("ERROR={:?}: hrp={} addr={}", e, hrp, addr)); + } + + test_impl("bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2", "bnb"); + + test_impl("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02", "cosmos"); + test_impl( + "cosmospub1addwnpepqftjsmkr7d7nx4tmhw4qqze8w39vjq364xt8etn45xqarlu3l2wu2n7pgrq", + "cosmos", + ); + test_impl( + "cosmosvaloper1sxx9mszve0gaedz5ld7qdkjkfv8z992ax69k08", + "cosmos", + ); + test_impl( + "cosmosvalconspub1zcjduepqjnnwe2jsywv0kfc97pz04zkm7tc9k2437cde2my3y5js9t7cw9mstfg3sa", + "cosmos", + ); + + test_impl("one1a50tun737ulcvwy0yvve0pvu5skq0kjargvhwe", "one"); + test_impl("one1tp7xdd9ewwnmyvws96au0e7e7mz6f8hjqr3g3p", "one"); + + test_impl("io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j", "io"); + + test_impl("zil1fwh4ltdguhde9s7nysnp33d5wye6uqpugufkz7", "zil"); + + test_impl( + "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz", + "erd", + ); + test_impl( + "erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r", + "erd", + ); + test_impl( + "erd19nu5t7hszckwah5nlcadmk5rlchtugzplznskffpwecygcu0520s9tnyy0", + "erd", + ); + + // uppercase version + test_impl("BNB1GRPF0955H0YKZQ3AR5NMUM7Y6GDFL6LXFN46H2", "bnb"); + } + + #[test] + fn test_address_from_str_checked_invalid() { + fn test_impl(addr: &str, hrp: &str) { + Bech32Address::from_str_checked(hrp, addr.to_string()) + .expect_err(&format!("hrp={} addr={}", hrp, addr)); + } + + // 1-char diff + test_impl("bnb1grpf0955h0ykzq3ar6nmum7y6gdfl6lxfn46h2", "bnb"); + // mixed case + test_impl("bnb1grPF0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2", "bnb"); + + test_impl("cosmos1xsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02", "cosmos"); + test_impl( + "cosmospub1xddwnpepqftjsmkr7d7nx4tmhw4qqze8w39vjq364xt8etn45xqarlu3l2wu2n7pgrq", + "cosmos", + ); + test_impl( + "cosmosvaloper1xxx9mszve0gaedz5ld7qdkjkfv8z992ax69k08", + "cosmos", + ); + test_impl( + "cosmosvalconspub1xcjduepqjnnwe2jsywv0kfc97pz04zkm7tc9k2437cde2my3y5js9t7cw9mstfg3sa", + "cosmos", + ); + + test_impl("one1a50tun737ulcvwy0yvve0pe", "one"); + test_impl("oe1tp7xdd9ewwnmyvws96au0ee7e7mz6f8hjqr3g3p", "one"); + + test_impl("io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8", "io"); + test_impl("io187wzp08vnhjpkydnr97qlh8kh0dpkkytfam8j", "io"); + test_impl("it187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j", "io"); + + test_impl("", "erd"); + test_impl( + "erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35!", + "erd", + ); + test_impl( + "xerd19nu5t7hszckwah5nlcadmk5rlchtugzplznskffpwecygcu0520s9tnyy0", + "erd", + ); + } + + #[test] + fn test_decode() { + fn test_impl(addr: &str, hrp: &str, expected_hash: &str) { + let actual = Bech32Address::from_str_checked(hrp, addr.to_string()).unwrap(); + assert_eq!(actual.key_hash.to_hex(), expected_hash); + } + + test_impl( + "one1a50tun737ulcvwy0yvve0pvu5skq0kjargvhwe", + "one", + "ed1ebe4fd1f73f86388f231997859ca42c07da5d", + ); + + test_impl( + "io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j", + "io", + "3f9c20bcec9de520d88d98cbe07ee7b5ded0dac4", + ); + } + + #[test] + fn test_from_hrp_and_hash() { + fn test_impl(hrp: &str, key_hash: &str, expected_addr: &str) { + let actual = + Bech32Address::new(hrp.to_string(), key_hash.decode_hex().unwrap()).unwrap(); + assert_eq!(actual.to_string(), expected_addr); + } + + test_impl( + "bnb", + "b6561dcc104130059a7c08f48c64610c1f6f9064", + "bnb1ketpmnqsgycqtxnupr6gcerpps0klyryuudz05", + ); + test_impl( + "one", + "587c66b4b973a7b231d02ebbc7e7d9f6c5a49ef2", + "one1tp7xdd9ewwnmyvws96au0e7e7mz6f8hjqr3g3p", + ); + test_impl( + "zil", + "0x91cdDcEBE846ce4d47712287EEe53cF17c2cfB77", + "zil1j8xae6lggm8y63m3y2r7aefu797ze7mhzulnqg", + ); + test_impl( + "zil", + "1d19918a737306218b5cbb3241fcdcbd998c3a72", + "zil1r5verznnwvrzrz6uhveyrlxuhkvccwnju4aehf", + ); + } + + #[test] + fn test_from_hrp_and_public_key_hasher() { + test_from_public_key(FromPublicKeyTestInput { + hrp: "bnb", + private_key: "95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832", + public_key_type: PublicKeyType::Secp256k1, + hasher: Hasher::Sha256ripemd, + expected: "bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2", + }); + + test_from_public_key(FromPublicKeyTestInput { + hrp: "cosmos", + private_key: "80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005", + public_key_type: PublicKeyType::Secp256k1, + hasher: Hasher::Sha256ripemd, + expected: "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02", + }); + + test_from_public_key(FromPublicKeyTestInput { + hrp: "one", + private_key: "e2f88b4974ae763ca1c2db49218802c2e441293a09eaa9ab681779e05d1b7b94", + public_key_type: PublicKeyType::Secp256k1Extended, + hasher: Hasher::Keccak256, + expected: "one1a50tun737ulcvwy0yvve0pvu5skq0kjargvhwe", + }); + + test_from_public_key(FromPublicKeyTestInput { + hrp: "io", + private_key: "0806c458b262edd333a191e92f561aff338211ee3e18ab315a074a2d82aa343f", + public_key_type: PublicKeyType::Secp256k1Extended, + hasher: Hasher::Keccak256, + expected: "io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j", + }); + + test_from_public_key(FromPublicKeyTestInput { + hrp: "zil", + private_key: "3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6", + public_key_type: PublicKeyType::Secp256k1, + hasher: Hasher::Sha256, + expected: "zil1j8xae6lggm8y63m3y2r7aefu797ze7mhzulnqg", + }); + } + + /// From same public key, but different hashes: different results. + #[test] + fn test_different_hashes() { + test_from_public_key(FromPublicKeyTestInput { + hrp: "hrp", + private_key: "3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6", + public_key_type: PublicKeyType::Secp256k1, + hasher: Hasher::Sha256ripemd, + expected: "hrp186zwn9h0z9fyvwfqs4jl92cw3kexusm4xw6ptp", + }); + + test_from_public_key(FromPublicKeyTestInput { + hrp: "hrp", + private_key: "3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6", + public_key_type: PublicKeyType::Secp256k1, + hasher: Hasher::Sha256, + expected: "hrp1j8xae6lggm8y63m3y2r7aefu797ze7mhgfetvu", + }); + + test_from_public_key(FromPublicKeyTestInput { + hrp: "hrp", + private_key: "3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6", + public_key_type: PublicKeyType::Secp256k1Extended, + hasher: Hasher::Keccak256, + expected: "hrp17hff3s97m5uxpjcdq3nzqxxatt8cmumnsf03su", + }); + } + + /// From same public key, but different prefixes: different results (checksum). + #[test] + fn test_different_hrp() { + test_from_public_key(FromPublicKeyTestInput { + hrp: "hrpone", + private_key: "3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6", + public_key_type: PublicKeyType::Secp256k1, + hasher: Hasher::Sha256ripemd, + expected: "hrpone186zwn9h0z9fyvwfqs4jl92cw3kexusm47das6p", + }); + + test_from_public_key(FromPublicKeyTestInput { + hrp: "hrptwo", + private_key: "3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6", + public_key_type: PublicKeyType::Secp256k1, + hasher: Hasher::Sha256ripemd, + expected: "hrptwo186zwn9h0z9fyvwfqs4jl92cw3kexusm4qzr8p7", + }); + + test_from_public_key(FromPublicKeyTestInput { + hrp: "hrpthree", + private_key: "3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6", + public_key_type: PublicKeyType::Secp256k1, + hasher: Hasher::Sha256ripemd, + expected: "hrpthree186zwn9h0z9fyvwfqs4jl92cw3kexusm4wuqkvd", + }); + } +} diff --git a/rust/tw_bitcoin/Cargo.toml b/rust/tw_bitcoin/Cargo.toml index 24771b92ac7..451491eb345 100644 --- a/rust/tw_bitcoin/Cargo.toml +++ b/rust/tw_bitcoin/Cargo.toml @@ -3,8 +3,6 @@ name = "tw_bitcoin" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] bitcoin = "0.30.0" secp256k1 = { version = "0.27.0", features = [ "global-context", "rand-std" ] } diff --git a/rust/tw_bitcoin/src/entry.rs b/rust/tw_bitcoin/src/entry.rs index 0809b39f315..a847c4a0690 100644 --- a/rust/tw_bitcoin/src/entry.rs +++ b/rust/tw_bitcoin/src/entry.rs @@ -49,12 +49,23 @@ impl CoinEntry for BitcoinEntry { #[inline] fn parse_address( &self, - _coin: &dyn CoinContext, + coin: &dyn CoinContext, address: &str, _prefix: Option, + ) -> AddressResult { + self.parse_address_unchecked(coin, address) + } + + #[inline] + fn parse_address_unchecked( + &self, + _coin: &dyn CoinContext, + address: &str, ) -> AddressResult { let address = bitcoin::address::Address::from_str(address) .map_err(|_| AddressError::FromHexError)? + // At this moment, we support mainnet only. + // This check will be removed in coming PRs. .require_network(bitcoin::Network::Bitcoin) .map_err(|_| AddressError::InvalidInput)?; diff --git a/rust/tw_bitcoin/src/modules/legacy/build_and_sign.rs b/rust/tw_bitcoin/src/modules/legacy/build_and_sign.rs index 04cbe0d8d92..097f6b15772 100644 --- a/rust/tw_bitcoin/src/modules/legacy/build_and_sign.rs +++ b/rust/tw_bitcoin/src/modules/legacy/build_and_sign.rs @@ -5,7 +5,7 @@ use bitcoin::taproot::{LeafVersion, NodeInfo, TaprootSpendInfo}; use bitcoin::{Network, PrivateKey, PublicKey, ScriptBuf}; use secp256k1::XOnlyPublicKey; use tw_coin_entry::coin_entry::CoinEntry; -use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_encoding::hex; use tw_misc::traits::ToBytesVec; use tw_proto::Bitcoin::Proto as LegacyProto; @@ -19,6 +19,8 @@ use tw_proto::Utxo::Proto as UtxoProto; pub fn taproot_build_and_sign_transaction( legacy: LegacyProto::SigningInput, ) -> Result { + let coin = TestCoinContext::default(); + // Convert the appropriate lock time. let native_lock_time = LockTime::from_consensus(legacy.lock_time); let lock_time = match native_lock_time { @@ -91,7 +93,7 @@ pub fn taproot_build_and_sign_transaction( }; // Build and sign the Bitcoin transaction. - let signed = crate::entry::BitcoinEntry.sign(&EmptyCoinContext, signing_input); + let signed = crate::entry::BitcoinEntry.sign(&coin, signing_input); // Check for error. if signed.error != Proto::Error::OK { diff --git a/rust/tw_bitcoin/tests/brc20.rs b/rust/tw_bitcoin/tests/brc20.rs index 055452b5942..67c5a2678b8 100644 --- a/rust/tw_bitcoin/tests/brc20.rs +++ b/rust/tw_bitcoin/tests/brc20.rs @@ -4,13 +4,13 @@ use common::hex; use tw_bitcoin::aliases::*; use tw_bitcoin::BitcoinEntry; use tw_coin_entry::coin_entry::CoinEntry; -use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_proto::BitcoinV2::Proto; use tw_proto::Utxo::Proto as UtxoProto; #[test] fn coin_entry_sign_brc20_commit_reveal_transfer() { - let coin = EmptyCoinContext; + let coin = TestCoinContext::default(); let alice_private_key = hex("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129"); let alice_pubkey = hex("030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb"); diff --git a/rust/tw_bitcoin/tests/free_estimate.rs b/rust/tw_bitcoin/tests/free_estimate.rs index 3e86653ce6d..bc64fbfa429 100644 --- a/rust/tw_bitcoin/tests/free_estimate.rs +++ b/rust/tw_bitcoin/tests/free_estimate.rs @@ -4,7 +4,7 @@ use common::{hex, ONE_BTC}; use tw_bitcoin::aliases::*; use tw_bitcoin::entry::BitcoinEntry; use tw_coin_entry::coin_entry::CoinEntry; -use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_proto::BitcoinV2::Proto; use tw_proto::Utxo::Proto as UtxoProto; @@ -12,7 +12,7 @@ const SAT_VB: u64 = 20; #[test] fn p2pkh_fee_estimate() { - let coin = EmptyCoinContext; + let coin = TestCoinContext::default(); let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); @@ -66,7 +66,7 @@ fn p2pkh_fee_estimate() { #[test] fn p2wpkh_fee_estimate() { - let coin = EmptyCoinContext; + let coin = TestCoinContext::default(); let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); @@ -124,7 +124,7 @@ fn p2wpkh_fee_estimate() { #[test] fn p2tr_key_path_fee_estimate() { - let coin = EmptyCoinContext; + let coin = TestCoinContext::default(); let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); @@ -182,7 +182,7 @@ fn p2tr_key_path_fee_estimate() { #[test] fn brc20_inscribe_fee_estimate() { - let coin = EmptyCoinContext; + let coin = TestCoinContext::default(); let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); diff --git a/rust/tw_bitcoin/tests/ordinal_nft.rs b/rust/tw_bitcoin/tests/ordinal_nft.rs index 23ff1694a04..d72c8450372 100644 --- a/rust/tw_bitcoin/tests/ordinal_nft.rs +++ b/rust/tw_bitcoin/tests/ordinal_nft.rs @@ -4,13 +4,13 @@ use common::hex; use tw_bitcoin::aliases::*; use tw_bitcoin::entry::BitcoinEntry; use tw_coin_entry::coin_entry::CoinEntry; -use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_proto::BitcoinV2::Proto; use tw_proto::Utxo::Proto as UtxoProto; #[test] fn coin_entry_sign_ordinal_nft_commit_reveal_transfer() { - let coin = EmptyCoinContext; + let coin = TestCoinContext::default(); let alice_private_key = hex("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129"); let alice_pubkey = hex("030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb"); diff --git a/rust/tw_bitcoin/tests/p2pkh.rs b/rust/tw_bitcoin/tests/p2pkh.rs index 05f99c8d438..cb7a49829a4 100644 --- a/rust/tw_bitcoin/tests/p2pkh.rs +++ b/rust/tw_bitcoin/tests/p2pkh.rs @@ -4,13 +4,13 @@ use common::{hex, MINER_FEE, ONE_BTC}; use tw_bitcoin::aliases::*; use tw_bitcoin::entry::BitcoinEntry; use tw_coin_entry::coin_entry::CoinEntry; -use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_proto::BitcoinV2::Proto; use tw_proto::Utxo::Proto as UtxoProto; #[test] fn coin_entry_emtpy() { - let _coin = EmptyCoinContext; + let _coin = TestCoinContext::default(); let alice_private_key = hex("56429688a1a6b00b90ccd22a0de0a376b6569d8684022ae92229a28478bfb657"); let signing = Proto::SigningInput { @@ -25,7 +25,7 @@ fn coin_entry_emtpy() { #[test] fn coin_entry_sign_input_p2pkh_output_p2pkh() { - let coin = EmptyCoinContext; + let coin = TestCoinContext::default(); let alice_private_key = hex("56429688a1a6b00b90ccd22a0de0a376b6569d8684022ae92229a28478bfb657"); let alice_pubkey = hex("036666dd712e05a487916384bfcd5973eb53e8038eccbbf97f7eed775b87389536"); diff --git a/rust/tw_bitcoin/tests/p2sh.rs b/rust/tw_bitcoin/tests/p2sh.rs index d09a15165ad..9d8323e4e5b 100644 --- a/rust/tw_bitcoin/tests/p2sh.rs +++ b/rust/tw_bitcoin/tests/p2sh.rs @@ -7,13 +7,13 @@ use tw_bitcoin::aliases::*; use tw_bitcoin::entry::BitcoinEntry; use tw_bitcoin::modules::signer::Signer; use tw_coin_entry::coin_entry::CoinEntry; -use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_proto::BitcoinV2::Proto; use tw_proto::Utxo::Proto as UtxoProto; #[test] fn coin_entry_sign_input_p2pkh_output_p2sh() { - let coin = EmptyCoinContext; + let coin = TestCoinContext::default(); let alice_private_key = hex("56429688a1a6b00b90ccd22a0de0a376b6569d8684022ae92229a28478bfb657"); let alice_pubkey = hex("036666dd712e05a487916384bfcd5973eb53e8038eccbbf97f7eed775b87389536"); diff --git a/rust/tw_bitcoin/tests/p2tr_key_path.rs b/rust/tw_bitcoin/tests/p2tr_key_path.rs index c32c230c396..8a4c617f345 100644 --- a/rust/tw_bitcoin/tests/p2tr_key_path.rs +++ b/rust/tw_bitcoin/tests/p2tr_key_path.rs @@ -4,13 +4,13 @@ use common::{hex, MINER_FEE, ONE_BTC}; use tw_bitcoin::aliases::*; use tw_bitcoin::entry::BitcoinEntry; use tw_coin_entry::coin_entry::CoinEntry; -use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_proto::BitcoinV2::Proto; use tw_proto::Utxo::Proto as UtxoProto; #[test] fn coin_entry_sign_input_p2pkh_output_p2tr_key_path() { - let coin = EmptyCoinContext; + let coin = TestCoinContext::default(); let alice_private_key = hex("12ce558df23528f1aa86f1f51ac7e13a197a06bda27610fa89e13b04c40ee999"); let alice_pubkey = hex("0351e003fdc48e7f31c9bc94996c91f6c3273b7ef4208a1686021bedf7673bb058"); diff --git a/rust/tw_bitcoin/tests/p2tr_script_path.rs b/rust/tw_bitcoin/tests/p2tr_script_path.rs index b2f01b8f606..7671b1dd739 100644 --- a/rust/tw_bitcoin/tests/p2tr_script_path.rs +++ b/rust/tw_bitcoin/tests/p2tr_script_path.rs @@ -7,7 +7,7 @@ use tw_bitcoin::aliases::*; use tw_bitcoin::entry::BitcoinEntry; use tw_bitcoin::modules::transactions::{BRC20TransferInscription, Brc20Ticker}; use tw_coin_entry::coin_entry::CoinEntry; -use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_misc::traits::ToBytesVec; use tw_proto::BitcoinV2::Proto; use tw_proto::Utxo::Proto as UtxoProto; @@ -17,7 +17,7 @@ use tw_proto::Utxo::Proto as UtxoProto; /// reconstruct the BRC20 transfer tests, but without using the convenience /// builders. fn coin_entry_custom_script_path() { - let coin = EmptyCoinContext; + let coin = TestCoinContext::default(); let alice_private_key = hex("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129"); let alice_pubkey = hex("030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb"); diff --git a/rust/tw_bitcoin/tests/p2wpkh.rs b/rust/tw_bitcoin/tests/p2wpkh.rs index 5dfedc04959..ac97b68f2d9 100644 --- a/rust/tw_bitcoin/tests/p2wpkh.rs +++ b/rust/tw_bitcoin/tests/p2wpkh.rs @@ -4,13 +4,13 @@ use common::{hex, MINER_FEE, ONE_BTC}; use tw_bitcoin::aliases::*; use tw_bitcoin::entry::BitcoinEntry; use tw_coin_entry::coin_entry::CoinEntry; -use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_proto::BitcoinV2::Proto; use tw_proto::Utxo::Proto as UtxoProto; #[test] fn coin_entry_sign_input_p2pkh_output_p2wpkh() { - let coin = EmptyCoinContext; + let coin = TestCoinContext::default(); let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); diff --git a/rust/tw_bitcoin/tests/p2wsh.rs b/rust/tw_bitcoin/tests/p2wsh.rs index fd50fd831d5..15d844e0c7d 100644 --- a/rust/tw_bitcoin/tests/p2wsh.rs +++ b/rust/tw_bitcoin/tests/p2wsh.rs @@ -7,13 +7,13 @@ use tw_bitcoin::aliases::*; use tw_bitcoin::entry::BitcoinEntry; use tw_bitcoin::modules::signer::Signer; use tw_coin_entry::coin_entry::CoinEntry; -use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_proto::BitcoinV2::Proto; use tw_proto::Utxo::Proto as UtxoProto; #[test] fn coin_entry_sign_input_p2pkh_output_p2wsh() { - let coin = EmptyCoinContext; + let coin = TestCoinContext::default(); let alice_private_key = hex("56429688a1a6b00b90ccd22a0de0a376b6569d8684022ae92229a28478bfb657"); let alice_pubkey = hex("036666dd712e05a487916384bfcd5973eb53e8038eccbbf97f7eed775b87389536"); diff --git a/rust/tw_bitcoin/tests/plan_builder.rs b/rust/tw_bitcoin/tests/plan_builder.rs index 935c5ada511..3d1ea2025f1 100644 --- a/rust/tw_bitcoin/tests/plan_builder.rs +++ b/rust/tw_bitcoin/tests/plan_builder.rs @@ -5,13 +5,13 @@ use tw_bitcoin::aliases::*; use tw_bitcoin::BitcoinEntry; use tw_coin_entry::coin_entry::CoinEntry; use tw_coin_entry::modules::plan_builder::PlanBuilder; -use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_proto::BitcoinV2::Proto; use tw_proto::Utxo::Proto as UtxoProto; #[test] fn transaction_plan_compose_brc20() { - let _coin = EmptyCoinContext; + let _coin = TestCoinContext::default(); let alice_private_key = hex("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129"); let alice_pubkey = hex("030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb"); diff --git a/rust/tw_bitcoin/tests/send_to_address.rs b/rust/tw_bitcoin/tests/send_to_address.rs index 030148ea415..f8cddd60cb0 100644 --- a/rust/tw_bitcoin/tests/send_to_address.rs +++ b/rust/tw_bitcoin/tests/send_to_address.rs @@ -6,13 +6,13 @@ use secp256k1::XOnlyPublicKey; use tw_bitcoin::aliases::*; use tw_bitcoin::entry::BitcoinEntry; use tw_coin_entry::coin_entry::CoinEntry; -use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_proto::BitcoinV2::Proto; use tw_proto::Utxo::Proto as UtxoProto; #[test] fn send_to_p2sh_address() { - let coin = EmptyCoinContext; + let coin = TestCoinContext::default(); let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); @@ -73,7 +73,7 @@ fn send_to_p2sh_address() { #[test] fn send_to_p2pkh_address() { - let coin = EmptyCoinContext; + let coin = TestCoinContext::default(); let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); @@ -133,7 +133,7 @@ fn send_to_p2pkh_address() { #[test] fn send_to_p2wsh_address() { - let coin = EmptyCoinContext; + let coin = TestCoinContext::default(); let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); @@ -194,7 +194,7 @@ fn send_to_p2wsh_address() { #[test] fn send_to_p2wpkh_address() { - let coin = EmptyCoinContext; + let coin = TestCoinContext::default(); let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); @@ -254,7 +254,7 @@ fn send_to_p2wpkh_address() { #[test] fn send_to_p2tr_key_path_address() { - let coin = EmptyCoinContext; + let coin = TestCoinContext::default(); let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); diff --git a/rust/tw_coin_entry/Cargo.toml b/rust/tw_coin_entry/Cargo.toml index 41ab8b48576..d3df7dd56da 100644 --- a/rust/tw_coin_entry/Cargo.toml +++ b/rust/tw_coin_entry/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] serde_json = "1.0.95" tw_encoding = { path = "../tw_encoding" } +tw_hash = { path = "../tw_hash" } tw_keypair = { path = "../tw_keypair" } tw_memory = { path = "../tw_memory" } tw_misc = { path = "../tw_misc" } diff --git a/rust/tw_coin_entry/src/coin_context.rs b/rust/tw_coin_entry/src/coin_context.rs index 8d6ccc92e35..f6f4a9dbb83 100644 --- a/rust/tw_coin_entry/src/coin_context.rs +++ b/rust/tw_coin_entry/src/coin_context.rs @@ -4,12 +4,17 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use tw_hash::hasher::Hasher; use tw_keypair::tw::PublicKeyType; /// Extend the trait with methods required for blockchain additions. pub trait CoinContext { + /// Necessary chain property. fn public_key_type(&self) -> PublicKeyType; - // Example: - // fn ss58_prefix(&self) -> Option; + /// Optional chain property. + fn address_hasher(&self) -> Option; + + /// Optional chain property. + fn hrp(&self) -> Option; } diff --git a/rust/tw_coin_entry/src/coin_entry.rs b/rust/tw_coin_entry/src/coin_entry.rs index 195efe7263b..70c9d24077d 100644 --- a/rust/tw_coin_entry/src/coin_entry.rs +++ b/rust/tw_coin_entry/src/coin_entry.rs @@ -46,6 +46,14 @@ pub trait CoinEntry { prefix: Option, ) -> AddressResult; + /// Tries to parse `Self::Address` from the given `address` string by `coin` type. + /// Please note that this method does not check if the address belongs to the given chain. + fn parse_address_unchecked( + &self, + coin: &dyn CoinContext, + address: &str, + ) -> AddressResult; + /// Derives an address associated with the given `public_key` by `coin` context, `derivation` and address `prefix`. fn derive_address( &self, diff --git a/rust/tw_coin_entry/src/coin_entry_ext.rs b/rust/tw_coin_entry/src/coin_entry_ext.rs index 68caa9fe6e7..d3daf007c4a 100644 --- a/rust/tw_coin_entry/src/coin_entry_ext.rs +++ b/rust/tw_coin_entry/src/coin_entry_ext.rs @@ -29,12 +29,7 @@ pub trait CoinEntryExt { ) -> AddressResult<()>; /// Validates and normalizes the given `address`. - fn normalize_address( - &self, - coin: &dyn CoinContext, - address: &str, - prefix: Option, - ) -> AddressResult; + fn normalize_address(&self, coin: &dyn CoinContext, address: &str) -> AddressResult; /// Derives an address associated with the given `public_key` by `coin` context, `derivation` and address `prefix`. fn derive_address( @@ -46,12 +41,7 @@ pub trait CoinEntryExt { ) -> AddressResult; /// Returns underlying data (public key or key hash). - fn address_to_data( - &self, - coin: &dyn CoinContext, - address: &str, - prefix: Option, - ) -> AddressResult; + fn address_to_data(&self, coin: &dyn CoinContext, address: &str) -> AddressResult; /// Signs a transaction declared as the given `input`. fn sign(&self, coin: &dyn CoinContext, input: &[u8]) -> ProtoResult; @@ -110,16 +100,11 @@ where self.parse_address(coin, address, prefix).map(|_| ()) } - fn normalize_address( - &self, - coin: &dyn CoinContext, - address: &str, - prefix: Option, - ) -> AddressResult { - let prefix = prefix.map(T::AddressPrefix::try_from).transpose()?; + fn normalize_address(&self, coin: &dyn CoinContext, address: &str) -> AddressResult { // Parse the address and display it. // Please note that `Self::Address::to_string()` returns a normalize address. - ::parse_address(self, coin, address, prefix).map(|addr| addr.to_string()) + ::parse_address_unchecked(self, coin, address) + .map(|addr| addr.to_string()) } fn derive_address( @@ -136,15 +121,8 @@ where .map(|addr| addr.to_string()) } - fn address_to_data( - &self, - coin: &dyn CoinContext, - address: &str, - prefix: Option, - ) -> AddressResult { - let prefix = prefix.map(T::AddressPrefix::try_from).transpose()?; - - self.parse_address(coin, address, prefix) + fn address_to_data(&self, coin: &dyn CoinContext, address: &str) -> AddressResult { + self.parse_address_unchecked(coin, address) .map(|addr| addr.data()) } diff --git a/rust/tw_coin_entry/src/common/compile_input.rs b/rust/tw_coin_entry/src/common/compile_input.rs index 317c1f3b5f7..fc3dfa5f5fb 100644 --- a/rust/tw_coin_entry/src/common/compile_input.rs +++ b/rust/tw_coin_entry/src/common/compile_input.rs @@ -6,18 +6,13 @@ use crate::coin_entry::{PublicKeyBytes, SignatureBytes}; use crate::error::{SigningError, SigningErrorType, SigningResult}; -use tw_keypair::KeyPairError; -pub struct SingleSignaturePubkey { - pub signature: Signature, - pub public_key: PublicKey, +pub struct SingleSignaturePubkey { + pub signature: SignatureBytes, + pub public_key: PublicKeyBytes, } -impl SingleSignaturePubkey -where - Signature: for<'a> TryFrom<&'a [u8], Error = KeyPairError>, - PublicKey: for<'a> TryFrom<&'a [u8], Error = KeyPairError>, -{ +impl SingleSignaturePubkey { pub fn from_sign_pubkey_list( signatures: Vec, public_keys: Vec, @@ -26,18 +21,15 @@ where return Err(SigningError(SigningErrorType::Error_no_support_n2n)); } - let signature_data = signatures + let signature = signatures .into_iter() .next() .ok_or(SigningError(SigningErrorType::Error_signatures_count))?; - let public_key_data = public_keys + let public_key = public_keys .into_iter() .next() .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; - let signature = Signature::try_from(signature_data.as_slice())?; - let public_key = PublicKey::try_from(public_key_data.as_slice())?; - Ok(SingleSignaturePubkey { signature, public_key, diff --git a/rust/tw_coin_entry/src/derivation.rs b/rust/tw_coin_entry/src/derivation.rs index 3eb0f6c0b70..29378ffa176 100644 --- a/rust/tw_coin_entry/src/derivation.rs +++ b/rust/tw_coin_entry/src/derivation.rs @@ -5,9 +5,11 @@ // file LICENSE at the root of the source code distribution tree. /// Extend this enum. +#[derive(Default)] #[repr(u32)] pub enum Derivation { /// Default derivation. + #[default] Default = 0, } diff --git a/rust/tw_coin_entry/src/error.rs b/rust/tw_coin_entry/src/error.rs index 0e39825ce3b..179e8b73b3f 100644 --- a/rust/tw_coin_entry/src/error.rs +++ b/rust/tw_coin_entry/src/error.rs @@ -34,6 +34,8 @@ pub enum AddressError { FromHexError, PublicKeyTypeMismatch, UnexpectedAddressPrefix, + UnexpectedHasher, + InvalidHrp, InvalidInput, } diff --git a/rust/tw_coin_entry/src/test_utils/empty_context.rs b/rust/tw_coin_entry/src/test_utils/empty_context.rs deleted file mode 100644 index 423081ba7e1..00000000000 --- a/rust/tw_coin_entry/src/test_utils/empty_context.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -use crate::coin_context::CoinContext; -use tw_keypair::tw::PublicKeyType; - -/// Test coin context that panics on any `CoinContext` method call. -pub struct EmptyCoinContext; - -impl CoinContext for EmptyCoinContext { - fn public_key_type(&self) -> PublicKeyType { - panic!() - } -} diff --git a/rust/tw_coin_entry/src/test_utils/mod.rs b/rust/tw_coin_entry/src/test_utils/mod.rs index 805b39f4b3e..4cbcc23d463 100644 --- a/rust/tw_coin_entry/src/test_utils/mod.rs +++ b/rust/tw_coin_entry/src/test_utils/mod.rs @@ -4,4 +4,4 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -pub mod empty_context; +pub mod test_context; diff --git a/rust/tw_coin_entry/src/test_utils/test_context.rs b/rust/tw_coin_entry/src/test_utils/test_context.rs new file mode 100644 index 00000000000..50d31585a22 --- /dev/null +++ b/rust/tw_coin_entry/src/test_utils/test_context.rs @@ -0,0 +1,44 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::coin_context::CoinContext; +use tw_hash::hasher::Hasher; +use tw_keypair::tw::PublicKeyType; + +/// Test coin context that panics on any `CoinContext` method call. +#[derive(Default)] +pub struct TestCoinContext { + pub public_key_type: Option, + pub address_hasher: Option, + pub hrp: Option, +} + +impl TestCoinContext { + pub fn with_public_key_type(mut self, public_key_type: PublicKeyType) -> TestCoinContext { + self.public_key_type = Some(public_key_type); + self + } + + pub fn with_hrp(mut self, hrp: &str) -> TestCoinContext { + self.hrp = Some(hrp.to_string()); + self + } +} + +impl CoinContext for TestCoinContext { + fn public_key_type(&self) -> PublicKeyType { + self.public_key_type + .expect("EmptyCoinContext::public_key_type was not set") + } + + fn address_hasher(&self) -> Option { + self.address_hasher + } + + fn hrp(&self) -> Option { + self.hrp.clone() + } +} diff --git a/rust/tw_coin_registry/Cargo.toml b/rust/tw_coin_registry/Cargo.toml index b45a573c5a1..e8a4155f09b 100644 --- a/rust/tw_coin_registry/Cargo.toml +++ b/rust/tw_coin_registry/Cargo.toml @@ -10,10 +10,15 @@ serde_json = "1.0.96" tw_aptos = { path = "../tw_aptos" } tw_bitcoin = { path = "../tw_bitcoin" } tw_coin_entry = { path = "../tw_coin_entry" } +tw_cosmos = { path = "../chains/tw_cosmos" } tw_ethereum = { path = "../tw_ethereum" } tw_evm = { path = "../tw_evm" } +tw_hash = { path = "../tw_hash" } tw_internet_computer = { path = "../tw_internet_computer" } tw_keypair = { path = "../tw_keypair" } tw_memory = { path = "../tw_memory" } tw_misc = { path = "../tw_misc" } +tw_native_evmos = { path = "../chains/tw_native_evmos" } +tw_native_injective = { path = "../chains/tw_native_injective" } tw_ronin = { path = "../tw_ronin" } +tw_thorchain = { path = "../chains/tw_thorchain" } diff --git a/rust/tw_coin_registry/src/blockchain_type.rs b/rust/tw_coin_registry/src/blockchain_type.rs index 2139a373eb3..c344d6b16b5 100644 --- a/rust/tw_coin_registry/src/blockchain_type.rs +++ b/rust/tw_coin_registry/src/blockchain_type.rs @@ -15,9 +15,13 @@ use std::str::FromStr; pub enum BlockchainType { Aptos, Bitcoin, + Cosmos, Ethereum, InternetComputer, + NativeEvmos, + NativeInjective, Ronin, + Thorchain, Unsupported, } @@ -38,9 +42,13 @@ impl FromStr for BlockchainType { match s { "Aptos" => Ok(BlockchainType::Aptos), "Bitcoin" => Ok(BlockchainType::Bitcoin), + "Cosmos" => Ok(BlockchainType::Cosmos), "Ethereum" => Ok(BlockchainType::Ethereum), "InternetComputer" => Ok(BlockchainType::InternetComputer), + "NativeEvmos" => Ok(BlockchainType::NativeEvmos), + "NativeInjective" => Ok(BlockchainType::NativeInjective), "Ronin" => Ok(BlockchainType::Ronin), + "Thorchain" => Ok(BlockchainType::Thorchain), _ => Ok(BlockchainType::Unsupported), } } diff --git a/rust/tw_coin_registry/src/coin_context.rs b/rust/tw_coin_registry/src/coin_context.rs index 41c090a7194..2185e7615e6 100644 --- a/rust/tw_coin_registry/src/coin_context.rs +++ b/rust/tw_coin_registry/src/coin_context.rs @@ -6,6 +6,7 @@ use crate::registry::CoinItem; use tw_coin_entry::coin_context::CoinContext; +use tw_hash::hasher::Hasher; use tw_keypair::tw::PublicKeyType; pub struct CoinRegistryContext { @@ -24,4 +25,14 @@ impl CoinContext for CoinRegistryContext { fn public_key_type(&self) -> PublicKeyType { self.item.public_key_type } + + #[inline] + fn address_hasher(&self) -> Option { + self.item.address_hasher + } + + #[inline] + fn hrp(&self) -> Option { + self.item.hrp.clone() + } } diff --git a/rust/tw_coin_registry/src/dispatcher.rs b/rust/tw_coin_registry/src/dispatcher.rs index 68970653371..0f3c36e2b70 100644 --- a/rust/tw_coin_registry/src/dispatcher.rs +++ b/rust/tw_coin_registry/src/dispatcher.rs @@ -12,27 +12,39 @@ use crate::registry::get_coin_item; use tw_aptos::entry::AptosEntry; use tw_bitcoin::entry::BitcoinEntry; use tw_coin_entry::coin_entry_ext::CoinEntryExt; +use tw_cosmos::entry::CosmosEntry; use tw_ethereum::entry::EthereumEntry; use tw_evm::evm_entry::EvmEntryExt; use tw_internet_computer::entry::InternetComputerEntry; +use tw_native_evmos::entry::NativeEvmosEntry; +use tw_native_injective::entry::NativeInjectiveEntry; use tw_ronin::entry::RoninEntry; +use tw_thorchain::entry::ThorchainEntry; pub type CoinEntryExtStaticRef = &'static dyn CoinEntryExt; pub type EvmEntryExtStaticRef = &'static dyn EvmEntryExt; const APTOS: AptosEntry = AptosEntry; const BITCOIN: BitcoinEntry = BitcoinEntry; +const COSMOS: CosmosEntry = CosmosEntry; const ETHEREUM: EthereumEntry = EthereumEntry; const INTERNET_COMPUTER: InternetComputerEntry = InternetComputerEntry; +const NATIVE_EVMOS: NativeEvmosEntry = NativeEvmosEntry; +const NATIVE_INJECTIVE: NativeInjectiveEntry = NativeInjectiveEntry; const RONIN: RoninEntry = RoninEntry; +const THORCHAIN: ThorchainEntry = ThorchainEntry; pub fn blockchain_dispatcher(blockchain: BlockchainType) -> RegistryResult { match blockchain { BlockchainType::Aptos => Ok(&APTOS), BlockchainType::Bitcoin => Ok(&BITCOIN), + BlockchainType::Cosmos => Ok(&COSMOS), BlockchainType::Ethereum => Ok(ÐEREUM), BlockchainType::InternetComputer => Ok(&INTERNET_COMPUTER), + BlockchainType::NativeEvmos => Ok(&NATIVE_EVMOS), + BlockchainType::NativeInjective => Ok(&NATIVE_INJECTIVE), BlockchainType::Ronin => Ok(&RONIN), + BlockchainType::Thorchain => Ok(&THORCHAIN), BlockchainType::Unsupported => Err(RegistryError::Unsupported), } } @@ -51,9 +63,13 @@ pub fn evm_dispatcher(coin: CoinType) -> RegistryResult { match item.blockchain { BlockchainType::Aptos => Err(RegistryError::Unsupported), BlockchainType::Bitcoin => Err(RegistryError::Unsupported), + BlockchainType::Cosmos => Err(RegistryError::Unsupported), BlockchainType::Ethereum => Ok(ÐEREUM), BlockchainType::InternetComputer => Err(RegistryError::Unsupported), + BlockchainType::NativeEvmos => Err(RegistryError::Unsupported), + BlockchainType::NativeInjective => Err(RegistryError::Unsupported), BlockchainType::Ronin => Ok(&RONIN), + BlockchainType::Thorchain => Err(RegistryError::Unsupported), BlockchainType::Unsupported => Err(RegistryError::Unsupported), } } diff --git a/rust/tw_coin_registry/src/registry.rs b/rust/tw_coin_registry/src/registry.rs index 422dd38ae6f..e438210c278 100644 --- a/rust/tw_coin_registry/src/registry.rs +++ b/rust/tw_coin_registry/src/registry.rs @@ -10,6 +10,7 @@ use crate::error::{RegistryError, RegistryResult}; use lazy_static::lazy_static; use serde::Deserialize; use std::collections::HashMap; +use tw_hash::hasher::Hasher; use tw_keypair::tw::PublicKeyType; type RegistryMap = HashMap; @@ -26,9 +27,13 @@ lazy_static! { #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct CoinItem { + pub id: String, + pub name: String, pub coin_id: CoinType, pub blockchain: BlockchainType, pub public_key_type: PublicKeyType, + pub address_hasher: Option, + pub hrp: Option, } #[inline] diff --git a/rust/tw_cosmos_sdk/Cargo.toml b/rust/tw_cosmos_sdk/Cargo.toml new file mode 100644 index 00000000000..c2b1be4fd94 --- /dev/null +++ b/rust/tw_cosmos_sdk/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "tw_cosmos_sdk" +version = "0.1.0" +edition = "2021" + +[features] +test-utils = [] + +[dependencies] +quick-protobuf = "0.8.1" +serde = { version = "1.0.163", features = ["derive"] } +serde_json = "1.0.96" +tw_bech32_address = { path = "../tw_bech32_address" } +tw_coin_entry = { path = "../tw_coin_entry" } +tw_encoding = { path = "../tw_encoding" } +tw_hash = { path = "../tw_hash" } +tw_keypair = { path = "../tw_keypair" } +tw_memory = { path = "../tw_memory" } +tw_misc = { path = "../tw_misc" } +tw_number = { path = "../tw_number", features = ["serde"] } +tw_proto = { path = "../tw_proto" } + +[dev-dependencies] +tw_coin_entry = { path = "../tw_coin_entry", features = ["test-utils"] } +tw_cosmos_sdk = { path = "./", features = ["test-utils"] } +tw_misc = { path = "../tw_misc", features = ["test-utils"] } + +[build-dependencies] +pb-rs = "0.10.0" diff --git a/rust/tw_cosmos_sdk/build.rs b/rust/tw_cosmos_sdk/build.rs new file mode 100644 index 00000000000..a434de70fb2 --- /dev/null +++ b/rust/tw_cosmos_sdk/build.rs @@ -0,0 +1,71 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use pb_rs::types::FileDescriptor; +use pb_rs::ConfigBuilder; +use std::path::{Path, PathBuf}; +use std::{env, fs}; + +fn main() { + let cargo_manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let proto_ext = Some(Path::new("proto").as_os_str()); + + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()).join("proto"); + + let proto_dir = cargo_manifest_dir + .join("..") + .join("..") + .join("src") + .join("Cosmos") + .join("Protobuf"); + let proto_dir_str = proto_dir.to_str().expect("Invalid proto directory path"); + // Re-run this build.rs if the `proto` directory has been changed (i.e. a new file is added). + println!("cargo:rerun-if-changed={}", proto_dir_str); + + let protos: Vec<_> = fs::read_dir(&proto_dir) + .expect("Expected a valid directory with proto files") + .filter_map(|file| { + let file = file.ok()?; + if file.path().extension() != proto_ext { + return None; + } + + let path = file.path(); + let path_str = path.to_str().expect("Invalid Proto file name"); + println!("cargo:rerun-if-changed={}", path_str); + Some(path) + }) + .collect(); + + // Delete all old generated files before re-generating new ones + if out_dir.exists() { + fs::remove_dir_all(&out_dir).expect("Error removing out directory"); + } + fs::DirBuilder::new() + .create(&out_dir) + .expect("Error creating out directory"); + + // `tw_proto/common_proto` contains google.protobuf proto files that are used in Cosmos protocol. + let common_proto_dir = cargo_manifest_dir + .join("..") + .join("tw_proto") + .join("src") + .join("common") + .canonicalize() + .expect("Cannot find common proto directory"); + + let out_protos = ConfigBuilder::new( + &protos, + None, + Some(&out_dir), + &[common_proto_dir, proto_dir], + ) + .expect("Error configuring pb-rs builder") + .gen_info(true) + .dont_use_cow(true) + .build(); + FileDescriptor::run(&out_protos).expect("Error generating proto files"); +} diff --git a/rust/tw_cosmos_sdk/fuzz/.gitignore b/rust/tw_cosmos_sdk/fuzz/.gitignore new file mode 100644 index 00000000000..5c404b9583f --- /dev/null +++ b/rust/tw_cosmos_sdk/fuzz/.gitignore @@ -0,0 +1,5 @@ +target +corpus +artifacts +coverage +Cargo.lock diff --git a/rust/tw_cosmos_sdk/fuzz/Cargo.toml b/rust/tw_cosmos_sdk/fuzz/Cargo.toml new file mode 100644 index 00000000000..519510a0a49 --- /dev/null +++ b/rust/tw_cosmos_sdk/fuzz/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "tw_cosmos_sdk-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" +tw_coin_entry = { path = "../../tw_coin_entry", features = ["test-utils"] } +tw_keypair = { path = "../../tw_keypair" } +tw_proto = { path = "../../tw_proto", features = ["fuzz"] } + +[dependencies.tw_cosmos_sdk] +path = ".." + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[profile.release] +debug = 1 + +[[bin]] +name = "sign" +path = "fuzz_targets/sign.rs" +test = false +doc = false diff --git a/rust/tw_cosmos_sdk/fuzz/fuzz_targets/sign.rs b/rust/tw_cosmos_sdk/fuzz/fuzz_targets/sign.rs new file mode 100644 index 00000000000..af6f1bca403 --- /dev/null +++ b/rust/tw_cosmos_sdk/fuzz/fuzz_targets/sign.rs @@ -0,0 +1,16 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; +use tw_coin_entry::test_utils::test_context::TestCoinContext; +use tw_cosmos_sdk::context::StandardCosmosContext; +use tw_cosmos_sdk::modules::signer::tw_signer::TWSigner; +use tw_keypair::tw::PublicKeyType; +use tw_proto::Cosmos::Proto; + +fuzz_target!(|input: Proto::SigningInput<'_>| { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let _ = TWSigner::::sign(&coin, input); +}); diff --git a/rust/tw_cosmos_sdk/src/address.rs b/rust/tw_cosmos_sdk/src/address.rs new file mode 100644 index 00000000000..4f141b2a1dd --- /dev/null +++ b/rust/tw_cosmos_sdk/src/address.rs @@ -0,0 +1,29 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde::Serialize; +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::{AddressError, AddressResult}; + +pub type Address = tw_bech32_address::Bech32Address; +pub type Bech32Prefix = tw_bech32_address::bech32_prefix::Bech32Prefix; + +pub trait CosmosAddress: FromStr + Serialize + ToString { + fn from_str_with_coin(coin: &dyn CoinContext, addr: &str) -> AddressResult + where + Self: Sized; +} + +impl CosmosAddress for Address { + fn from_str_with_coin(coin: &dyn CoinContext, addr: &str) -> AddressResult + where + Self: Sized, + { + let prefix = None; + Address::from_str_with_coin_and_prefix(coin, addr.to_string(), prefix) + } +} diff --git a/rust/tw_cosmos_sdk/src/context.rs b/rust/tw_cosmos_sdk/src/context.rs new file mode 100644 index 00000000000..f7da7218f25 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/context.rs @@ -0,0 +1,34 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::{Address, CosmosAddress}; +use crate::hasher::sha256_hasher::Sha256Hasher; +use crate::hasher::CosmosHasher; +use crate::private_key::secp256k1::Secp256PrivateKey; +use crate::private_key::CosmosPrivateKey; +use crate::public_key::secp256k1::Secp256PublicKey; +use crate::public_key::CosmosPublicKey; +use crate::signature::secp256k1::Secp256k1Signature; +use crate::signature::CosmosSignature; + +pub trait CosmosContext { + type Address: CosmosAddress; + type TxHasher: CosmosHasher; + type PrivateKey: CosmosPrivateKey; + type PublicKey: CosmosPublicKey; + type Signature: CosmosSignature; +} + +#[derive(Default)] +pub struct StandardCosmosContext; + +impl CosmosContext for StandardCosmosContext { + type Address = Address; + type TxHasher = Sha256Hasher; + type PrivateKey = Secp256PrivateKey; + type PublicKey = Secp256PublicKey; + type Signature = Secp256k1Signature; +} diff --git a/rust/tw_cosmos_sdk/src/hasher/keccak256_hasher.rs b/rust/tw_cosmos_sdk/src/hasher/keccak256_hasher.rs new file mode 100644 index 00000000000..21ca81fb087 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/hasher/keccak256_hasher.rs @@ -0,0 +1,21 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::hasher::CosmosHasher; +use tw_hash::sha3::keccak256; +use tw_memory::Data; + +pub struct Keccak256Hasher; + +impl CosmosHasher for Keccak256Hasher { + fn hash_sign_doc(sign_doc: &[u8]) -> Data { + keccak256(sign_doc) + } + + fn hash_json_tx(json: &str) -> Data { + keccak256(json.as_bytes()) + } +} diff --git a/rust/tw_cosmos_sdk/src/hasher/mod.rs b/rust/tw_cosmos_sdk/src/hasher/mod.rs new file mode 100644 index 00000000000..7422d204961 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/hasher/mod.rs @@ -0,0 +1,16 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_memory::Data; + +pub mod keccak256_hasher; +pub mod sha256_hasher; + +pub trait CosmosHasher { + fn hash_sign_doc(sign_doc: &[u8]) -> Data; + + fn hash_json_tx(json: &str) -> Data; +} diff --git a/rust/tw_cosmos_sdk/src/hasher/sha256_hasher.rs b/rust/tw_cosmos_sdk/src/hasher/sha256_hasher.rs new file mode 100644 index 00000000000..3b2310cdbec --- /dev/null +++ b/rust/tw_cosmos_sdk/src/hasher/sha256_hasher.rs @@ -0,0 +1,21 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::hasher::CosmosHasher; +use tw_hash::sha2::sha256; +use tw_memory::Data; + +pub struct Sha256Hasher; + +impl CosmosHasher for Sha256Hasher { + fn hash_sign_doc(sign_doc: &[u8]) -> Data { + sha256(sign_doc) + } + + fn hash_json_tx(json: &str) -> Data { + sha256(json.as_bytes()) + } +} diff --git a/rust/tw_cosmos_sdk/src/lib.rs b/rust/tw_cosmos_sdk/src/lib.rs new file mode 100644 index 00000000000..52cba46ef65 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/lib.rs @@ -0,0 +1,25 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod address; +pub mod context; +pub mod hasher; +pub mod modules; +pub mod private_key; +pub mod public_key; +pub mod signature; +pub mod transaction; + +#[cfg(feature = "test-utils")] +pub mod test_utils; + +#[allow(non_snake_case)] +#[rustfmt::skip] +pub mod proto { + use tw_proto::google; + + include!(concat!(env!("OUT_DIR"), "/proto/mod.rs")); +} diff --git a/rust/tw_cosmos_sdk/src/modules/broadcast_msg.rs b/rust/tw_cosmos_sdk/src/modules/broadcast_msg.rs new file mode 100644 index 00000000000..3c2b636ea0a --- /dev/null +++ b/rust/tw_cosmos_sdk/src/modules/broadcast_msg.rs @@ -0,0 +1,60 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use quick_protobuf::MessageWrite; +use serde::Serialize; +use serde_json::Value as Json; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_encoding::base64::Base64Encoded; +use tw_proto::serialize; + +pub enum BroadcastMode { + Block, + Async, + Sync, +} + +#[derive(Serialize)] +#[serde(untagged)] +pub enum BroadcastMsg { + /// Binary representation of the transaction. + Raw { + mode: String, + tx_bytes: Base64Encoded, + }, + /// JSON encoded transaction. + Json { mode: String, tx: Json }, +} + +impl BroadcastMsg { + pub fn raw(mode: BroadcastMode, tx: &Tx) -> BroadcastMsg { + let mode = match mode { + BroadcastMode::Block => "BROADCAST_MODE_BLOCK", + BroadcastMode::Async => "BROADCAST_MODE_ASYNC", + BroadcastMode::Sync => "BROADCAST_MODE_SYNC", + } + .to_string(); + let tx_bytes = Base64Encoded(serialize(tx).expect("Error on serializing transaction")); + BroadcastMsg::Raw { mode, tx_bytes } + } + + pub fn json(mode: BroadcastMode, tx: Tx) -> SigningResult { + let mode = match mode { + BroadcastMode::Block => "block", + BroadcastMode::Async => "async", + BroadcastMode::Sync => "sync", + } + .to_string(); + let tx = + serde_json::to_value(tx).map_err(|_| SigningError(SigningErrorType::Error_internal))?; + Ok(BroadcastMsg::Json { mode, tx }) + } + + pub fn to_json_string(&self) -> String { + // It's safe to unwrap here because `BroadcastMsg` consists of checked fields only. + serde_json::to_string(self).expect("Unexpected error on serializing a BroadcastMsg") + } +} diff --git a/rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs b/rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs new file mode 100644 index 00000000000..188e4c3d377 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs @@ -0,0 +1,40 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::context::CosmosContext; +use crate::hasher::CosmosHasher; +use crate::modules::serializer::json_serializer::JsonSerializer; +use crate::public_key::JsonPublicKey; +use crate::transaction::UnsignedTransaction; +use std::marker::PhantomData; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_memory::Data; + +pub struct JsonTxPreimage { + pub encoded_tx: String, + pub tx_hash: Data, +} + +pub struct JsonPreimager { + _phantom: PhantomData, +} + +impl JsonPreimager +where + Context::PublicKey: JsonPublicKey, +{ + pub fn preimage_hash(unsigned: &UnsignedTransaction) -> SigningResult { + let tx_to_sign = JsonSerializer::build_unsigned_tx(unsigned)?; + let encoded_tx = serde_json::to_string(&tx_to_sign) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let tx_hash = Context::TxHasher::hash_json_tx(&encoded_tx); + + Ok(JsonTxPreimage { + encoded_tx, + tx_hash, + }) + } +} diff --git a/rust/tw_cosmos_sdk/src/modules/compiler/mod.rs b/rust/tw_cosmos_sdk/src/modules/compiler/mod.rs new file mode 100644 index 00000000000..a5d5ef61389 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/modules/compiler/mod.rs @@ -0,0 +1,9 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod json_preimager; +pub mod protobuf_preimager; +pub mod tw_compiler; diff --git a/rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs b/rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs new file mode 100644 index 00000000000..591d56a6aba --- /dev/null +++ b/rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs @@ -0,0 +1,49 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::context::CosmosContext; +use crate::hasher::CosmosHasher; +use crate::modules::serializer::protobuf_serializer::{ProtobufSerializer, SignDirectArgs}; +use crate::transaction::UnsignedTransaction; +use std::marker::PhantomData; +use tw_coin_entry::error::SigningResult; +use tw_memory::Data; +use tw_proto::serialize; + +pub struct ProtobufTxPreimage { + pub encoded_tx: Data, + pub tx_hash: Data, +} + +pub struct ProtobufPreimager { + _phantom: PhantomData, +} + +impl ProtobufPreimager { + pub fn preimage_hash( + unsigned: &UnsignedTransaction, + ) -> SigningResult { + let tx_to_sign = ProtobufSerializer::build_sign_doc(unsigned)?; + let encoded_tx = serialize(&tx_to_sign)?; + let tx_hash = Context::TxHasher::hash_sign_doc(&encoded_tx); + + Ok(ProtobufTxPreimage { + encoded_tx, + tx_hash, + }) + } + + pub fn preimage_hash_direct(args: &SignDirectArgs) -> SigningResult { + let tx_to_sign = ProtobufSerializer::::build_direct_sign_doc(args); + let encoded_tx = serialize(&tx_to_sign)?; + let tx_hash = Context::TxHasher::hash_sign_doc(&encoded_tx); + + Ok(ProtobufTxPreimage { + encoded_tx, + tx_hash, + }) + } +} diff --git a/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs b/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs new file mode 100644 index 00000000000..897276b8c90 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs @@ -0,0 +1,185 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::context::CosmosContext; +use crate::modules::broadcast_msg::{BroadcastMode, BroadcastMsg}; +use crate::modules::compiler::json_preimager::JsonPreimager; +use crate::modules::compiler::protobuf_preimager::ProtobufPreimager; +use crate::modules::serializer::json_serializer::JsonSerializer; +use crate::modules::serializer::protobuf_serializer::ProtobufSerializer; +use crate::modules::tx_builder::TxBuilder; +use crate::public_key::CosmosPublicKey; +use crate::signature::CosmosSignature; +use std::borrow::Cow; +use std::marker::PhantomData; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::common::compile_input::SingleSignaturePubkey; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::signing_output_error; +use tw_proto::Cosmos::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct TWTransactionCompiler { + _phantom: PhantomData, +} + +impl TWTransactionCompiler { + #[inline] + pub fn preimage_hashes( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> CompilerProto::PreSigningOutput<'static> { + match input.signing_mode { + Proto::SigningMode::JSON => Self::preimage_hashes_as_json(coin, input), + Proto::SigningMode::Protobuf => Self::preimage_hashes_as_protobuf(coin, input), + } + .unwrap_or_else(|e| signing_output_error!(CompilerProto::PreSigningOutput, e)) + } + + #[inline] + pub fn compile( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Proto::SigningOutput<'static> { + match input.signing_mode { + Proto::SigningMode::JSON => Self::compile_as_json(coin, input, signatures, public_keys), + Proto::SigningMode::Protobuf => { + Self::compile_as_protobuf(coin, input, signatures, public_keys) + }, + } + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + pub fn preimage_hashes_as_protobuf( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> SigningResult> { + let preimage = match TxBuilder::::try_sign_direct_args(&input) { + // If there was a `SignDirect` message in the signing input, generate the tx preimage directly. + Ok(Some(sign_direct_args)) => { + ProtobufPreimager::::preimage_hash_direct(&sign_direct_args)? + }, + // Otherwise, generate the tx preimage by using `TxBuilder`. + _ => { + // Please note the [`Proto::SigningInput::public_key`] should be set already. + let unsigned_tx = TxBuilder::::unsigned_tx_from_proto(coin, &input)?; + ProtobufPreimager::::preimage_hash(&unsigned_tx)? + }, + }; + + Ok(CompilerProto::PreSigningOutput { + data: Cow::from(preimage.encoded_tx), + data_hash: Cow::from(preimage.tx_hash), + ..CompilerProto::PreSigningOutput::default() + }) + } + + pub fn preimage_hashes_as_json( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> SigningResult> { + // Please note the [`Proto::SigningInput::public_key`] should be set already. + let unsigned_tx = TxBuilder::::unsigned_tx_from_proto(coin, &input)?; + let preimage = JsonPreimager::preimage_hash(&unsigned_tx)?; + + Ok(CompilerProto::PreSigningOutput { + data: Cow::from(preimage.encoded_tx.as_bytes().to_vec()), + data_hash: Cow::from(preimage.tx_hash), + ..CompilerProto::PreSigningOutput::default() + }) + } + + pub fn compile_as_protobuf( + coin: &dyn CoinContext, + mut input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> SigningResult> { + let SingleSignaturePubkey { + signature, + public_key, + } = SingleSignaturePubkey::from_sign_pubkey_list(signatures, public_keys)?; + let signature = Context::Signature::from_bytes(&signature)?; + let public_key = Context::PublicKey::from_bytes(coin, &public_key)?; + + let signed_tx_raw = match TxBuilder::::try_sign_direct_args(&input) { + // If there was a `SignDirect` message in the signing input, generate the `TxRaw` directly. + Ok(Some(sign_direct_args)) => ProtobufSerializer::::build_direct_signed_tx( + &sign_direct_args, + signature.to_bytes(), + ), + // Otherwise, generate the `TxRaw` by using `TxBuilder`. + _ => { + // Set the public key. It will be used to construct a signer info. + input.public_key = Cow::from(public_key.to_bytes()); + let unsigned_tx = TxBuilder::::unsigned_tx_from_proto(coin, &input)?; + let signed_tx = unsigned_tx.into_signed(signature.to_bytes()); + + ProtobufSerializer::build_signed_tx(&signed_tx)? + }, + }; + + let broadcast_mode = Self::broadcast_mode(input.mode); + let broadcast_tx = BroadcastMsg::raw(broadcast_mode, &signed_tx_raw).to_json_string(); + + let signature_json = + JsonSerializer::::serialize_signature(&public_key, signature.to_bytes()); + let signature_json = serde_json::to_string(&[signature_json]) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + + Ok(Proto::SigningOutput { + signature: Cow::from(signature.to_bytes()), + signature_json: Cow::from(signature_json), + serialized: Cow::from(broadcast_tx), + ..Proto::SigningOutput::default() + }) + } + + pub fn compile_as_json( + coin: &dyn CoinContext, + mut input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> SigningResult> { + let SingleSignaturePubkey { + signature, + public_key, + } = SingleSignaturePubkey::from_sign_pubkey_list(signatures, public_keys)?; + let signature = Context::Signature::from_bytes(&signature)?; + let public_key = Context::PublicKey::from_bytes(coin, &public_key)?; + + // Set the public key. It will be used to construct a signer info. + input.public_key = Cow::from(public_key.to_bytes()); + let unsigned_tx = TxBuilder::::unsigned_tx_from_proto(coin, &input)?; + let signed_tx = unsigned_tx.into_signed(signature.to_bytes()); + + let signed_tx_json = JsonSerializer::build_signed_tx(&signed_tx)?; + + let broadcast_mode = Self::broadcast_mode(input.mode); + let broadcast_tx = BroadcastMsg::json(broadcast_mode, &signed_tx_json)?.to_json_string(); + + let signature_json = serde_json::to_string(&signed_tx_json.signatures) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + + Ok(Proto::SigningOutput { + signature: Cow::from(signature.to_bytes()), + signature_json: Cow::from(signature_json), + json: Cow::from(broadcast_tx), + ..Proto::SigningOutput::default() + }) + } + + fn broadcast_mode(input: Proto::BroadcastMode) -> BroadcastMode { + match input { + Proto::BroadcastMode::BLOCK => BroadcastMode::Block, + Proto::BroadcastMode::SYNC => BroadcastMode::Sync, + Proto::BroadcastMode::ASYNC => BroadcastMode::Async, + } + } +} diff --git a/rust/tw_cosmos_sdk/src/modules/mod.rs b/rust/tw_cosmos_sdk/src/modules/mod.rs new file mode 100644 index 00000000000..2a667107a85 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/modules/mod.rs @@ -0,0 +1,11 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod broadcast_msg; +pub mod compiler; +pub mod serializer; +pub mod signer; +pub mod tx_builder; diff --git a/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs b/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs new file mode 100644 index 00000000000..073f7de68cb --- /dev/null +++ b/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs @@ -0,0 +1,125 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::context::CosmosContext; +use crate::private_key::SignatureData; +use crate::public_key::{CosmosPublicKey, JsonPublicKey}; +use crate::transaction::{Coin, Fee, SignedTransaction, UnsignedTransaction}; +use serde::Serialize; +use serde_json::Value as Json; +use std::marker::PhantomData; +use tw_coin_entry::error::SigningResult; +use tw_encoding::base64::Base64Encoded; + +#[derive(Serialize)] +pub struct SignedTxJson { + pub fee: FeeJson, + pub memo: String, + pub msg: Vec>, + pub signatures: Vec, +} + +#[derive(Serialize)] +pub struct UnsignedTxJson { + pub account_number: String, + pub chain_id: String, + pub fee: FeeJson, + pub memo: String, + pub msgs: Vec>, + pub sequence: String, +} + +#[derive(Serialize)] +pub struct FeeJson { + pub amount: Vec, + pub gas: String, +} + +#[derive(Clone, Serialize)] +pub struct AnyMsg { + #[serde(rename = "type")] + pub msg_type: String, + pub value: Value, +} + +#[derive(Clone, Serialize)] +pub struct SignatureJson { + pub pub_key: AnyMsg, + pub signature: Base64Encoded, +} + +/// `JsonSerializer` serializes transaction to JSON in Cosmos specific way. +pub struct JsonSerializer { + _phantom: PhantomData, +} + +impl JsonSerializer +where + Context: CosmosContext, + Context::PublicKey: JsonPublicKey, +{ + pub fn build_signed_tx(signed: &SignedTransaction) -> SigningResult { + let msg = signed + .tx_body + .messages + .iter() + .map(|msg| msg.to_json()) + .collect::>()?; + let signature = + Self::serialize_signature(&signed.signer.public_key, signed.signature.clone()); + + Ok(SignedTxJson { + fee: Self::build_fee(&signed.fee), + memo: signed.tx_body.memo.clone(), + msg, + signatures: vec![signature], + }) + } + + pub fn build_unsigned_tx( + unsigned: &UnsignedTransaction, + ) -> SigningResult { + let msgs = unsigned + .tx_body + .messages + .iter() + .map(|msg| msg.to_json()) + .collect::>()?; + + Ok(UnsignedTxJson { + account_number: unsigned.account_number.to_string(), + chain_id: unsigned.chain_id.clone(), + fee: Self::build_fee(&unsigned.fee), + memo: unsigned.tx_body.memo.clone(), + msgs, + sequence: unsigned.signer.sequence.to_string(), + }) + } + + pub fn serialize_signature( + public_key: &Context::PublicKey, + signature: SignatureData, + ) -> SignatureJson { + SignatureJson { + pub_key: Self::serialize_public_key(public_key), + signature: Base64Encoded(signature), + } + } + + pub fn serialize_public_key(public_key: &Context::PublicKey) -> AnyMsg { + AnyMsg { + msg_type: public_key.public_key_type(), + value: Base64Encoded(public_key.to_bytes()), + } + } + + pub fn build_fee(fee: &Fee) -> FeeJson { + FeeJson { + gas: fee.gas_limit.to_string(), + amount: fee.amounts.clone(), + } + } +} diff --git a/rust/tw_cosmos_sdk/src/modules/serializer/mod.rs b/rust/tw_cosmos_sdk/src/modules/serializer/mod.rs new file mode 100644 index 00000000000..7361a31450a --- /dev/null +++ b/rust/tw_cosmos_sdk/src/modules/serializer/mod.rs @@ -0,0 +1,8 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod json_serializer; +pub mod protobuf_serializer; diff --git a/rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs b/rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs new file mode 100644 index 00000000000..ee67300b747 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs @@ -0,0 +1,165 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::context::CosmosContext; +use crate::proto::cosmos::base::v1beta1 as base_proto; +use crate::proto::cosmos::signing::v1beta1 as signing_proto; +use crate::proto::cosmos::tx::v1beta1 as tx_proto; +use crate::public_key::ProtobufPublicKey; +use crate::transaction::{ + Coin, Fee, SignMode, SignedTransaction, SignerInfo, TxBody, UnsignedTransaction, +}; +use std::marker::PhantomData; +use tw_coin_entry::error::SigningResult; +use tw_memory::Data; +use tw_proto::serialize; + +pub fn build_coin(coin: &Coin) -> base_proto::Coin { + base_proto::Coin { + amount: coin.amount.to_string(), + denom: coin.denom.clone(), + } +} + +/// `ProtobufSerializer` serializes Cosmos specific Protobuf messages. +pub struct ProtobufSerializer { + _phantom: PhantomData, +} + +pub struct SignDirectArgs { + pub tx_body: Data, + pub auth_info: Data, + pub chain_id: String, + pub account_number: u64, +} + +impl ProtobufSerializer { + /// Serializes a signed transaction into the Cosmos [`tx_proto::TxRaw`] message. + /// [`tx_proto::TxRaw`] can be broadcasted to the network. + pub fn build_signed_tx(signed: &SignedTransaction) -> SigningResult { + let tx_body = Self::build_tx_body(&signed.tx_body)?; + let body_bytes = serialize(&tx_body).expect("Unexpected error on tx_body serialization"); + + let auth_info = Self::build_auth_info(&signed.signer, &signed.fee); + let auth_info_bytes = + serialize(&auth_info).expect("Unexpected error on auth_info serialization"); + + Ok(tx_proto::TxRaw { + body_bytes, + auth_info_bytes, + signatures: vec![signed.signature.clone()], + }) + } + + pub fn build_direct_signed_tx(args: &SignDirectArgs, signature: Data) -> tx_proto::TxRaw { + tx_proto::TxRaw { + body_bytes: args.tx_body.clone(), + auth_info_bytes: args.auth_info.clone(), + signatures: vec![signature], + } + } + + /// Serializes an unsigned transaction into the Cosmos [`tx_proto::SignDoc`] message. + /// [`tx_proto::SignDoc`] is used to generate a transaction prehash and sign it. + pub fn build_sign_doc( + unsigned: &UnsignedTransaction, + ) -> SigningResult { + let tx_body = Self::build_tx_body(&unsigned.tx_body)?; + let body_bytes = serialize(&tx_body).expect("Unexpected error on tx_body serialization"); + + let auth_info = Self::build_auth_info(&unsigned.signer, &unsigned.fee); + let auth_info_bytes = + serialize(&auth_info).expect("Unexpected error on auth_info serialization"); + + Ok(tx_proto::SignDoc { + body_bytes, + auth_info_bytes, + chain_id: unsigned.chain_id.clone(), + account_number: unsigned.account_number, + }) + } + + pub fn build_direct_sign_doc(args: &SignDirectArgs) -> tx_proto::SignDoc { + tx_proto::SignDoc { + body_bytes: args.tx_body.clone(), + auth_info_bytes: args.auth_info.clone(), + chain_id: args.chain_id.clone(), + account_number: args.account_number, + } + } + + pub fn build_auth_info( + signer: &SignerInfo, + fee: &Fee, + ) -> tx_proto::AuthInfo { + tx_proto::AuthInfo { + signer_infos: vec![Self::build_signer_info(signer)], + fee: Some(Self::build_fee(fee)), + // At this moment, we do not support transaction tip. + tip: None, + } + } + + pub fn build_tx_body(tx_body: &TxBody) -> SigningResult { + let messages: Vec<_> = tx_body + .messages + .iter() + .map(|msg| msg.to_proto()) + .collect::>()?; + + Ok(tx_proto::TxBody { + messages, + memo: tx_body.memo.clone(), + timeout_height: tx_body.timeout_height, + extension_options: Vec::default(), + non_critical_extension_options: Vec::default(), + }) + } + + pub fn build_signer_info(signer: &SignerInfo) -> tx_proto::SignerInfo { + use tx_proto::mod_ModeInfo::{self as mode_info, OneOfsum as SumEnum}; + + // Single is the mode info for a single signer. It is structured as a message + // to allow for additional fields such as locale for SIGN_MODE_TEXTUAL in the future. + let mode_info = tx_proto::ModeInfo { + sum: SumEnum::single(mode_info::Single { + mode: Self::build_sign_mode(signer.sign_mode), + }), + }; + + tx_proto::SignerInfo { + public_key: Some(signer.public_key.to_proto()), + mode_info: Some(mode_info), + sequence: signer.sequence, + } + } + + fn build_fee(fee: &Fee) -> tx_proto::Fee { + let payer = fee + .payer + .as_ref() + .map(Context::Address::to_string) + .unwrap_or_default(); + let granter = fee + .granter + .as_ref() + .map(Context::Address::to_string) + .unwrap_or_default(); + + tx_proto::Fee { + amount: fee.amounts.iter().map(build_coin).collect(), + gas_limit: fee.gas_limit, + payer, + granter, + } + } + + fn build_sign_mode(sign_mode: SignMode) -> signing_proto::SignMode { + match sign_mode { + SignMode::Direct => signing_proto::SignMode::SIGN_MODE_DIRECT, + } + } +} diff --git a/rust/tw_cosmos_sdk/src/modules/signer/mod.rs b/rust/tw_cosmos_sdk/src/modules/signer/mod.rs new file mode 100644 index 00000000000..c4071fb11ff --- /dev/null +++ b/rust/tw_cosmos_sdk/src/modules/signer/mod.rs @@ -0,0 +1,7 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod tw_signer; diff --git a/rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs b/rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs new file mode 100644 index 00000000000..8f63310be5a --- /dev/null +++ b/rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs @@ -0,0 +1,61 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::context::CosmosContext; +use crate::modules::compiler::tw_compiler::TWTransactionCompiler; +use crate::private_key::CosmosPrivateKey; +use crate::public_key::CosmosPublicKey; +use std::borrow::Cow; +use std::marker::PhantomData; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::signing_output_error; +use tw_proto::Cosmos::Proto; + +pub struct TWSigner { + _phantom: PhantomData, +} + +impl TWSigner { + #[inline] + pub fn sign( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> Proto::SigningOutput<'static> { + Self::sign_impl(coin, input) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn sign_impl( + coin: &dyn CoinContext, + mut input: Proto::SigningInput<'_>, + ) -> SigningResult> { + let private_key = Context::PrivateKey::try_from(&input.private_key)?; + let public_key = Context::PublicKey::from_private_key(coin, private_key.as_ref())?; + // Set the public key. It will be used to construct a signer info. + input.public_key = Cow::from(public_key.to_bytes()); + + let preimage_output = + TWTransactionCompiler::::preimage_hashes(coin, input.clone()); + if preimage_output.error != SigningErrorType::OK { + return Err(SigningError(preimage_output.error)); + } + + let signature_data = private_key.sign_tx_hash(&preimage_output.data_hash)?; + let compile_output = TWTransactionCompiler::::compile( + coin, + input, + vec![signature_data], + vec![public_key.to_bytes()], + ); + + if compile_output.error != SigningErrorType::OK { + return Err(SigningError(preimage_output.error)); + } + + Ok(compile_output) + } +} diff --git a/rust/tw_cosmos_sdk/src/modules/tx_builder.rs b/rust/tw_cosmos_sdk/src/modules/tx_builder.rs new file mode 100644 index 00000000000..da5e87a94d1 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/modules/tx_builder.rs @@ -0,0 +1,655 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::{Address, CosmosAddress}; +use crate::context::CosmosContext; +use crate::modules::serializer::protobuf_serializer::SignDirectArgs; +use crate::public_key::CosmosPublicKey; +use crate::transaction::message::cosmos_generic_message::JsonRawMessage; +use crate::transaction::message::{CosmosMessage, CosmosMessageBox}; +use crate::transaction::{Coin, Fee, SignMode, SignerInfo, TxBody, UnsignedTransaction}; +use std::marker::PhantomData; +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_misc::traits::ToBytesVec; +use tw_number::U256; +use tw_proto::Cosmos::Proto; +use tw_proto::{google, serialize}; + +const DEFAULT_TIMEOUT_HEIGHT: u64 = 0; + +pub struct TxBuilder { + _phantom: PhantomData, +} + +impl TxBuilder +where + Context: CosmosContext, +{ + /// Please note that [`Proto::SigningInput::public_key`] must be set. + /// If the public key should be derived from a private key, please do it before this method is called. + pub fn unsigned_tx_from_proto( + coin: &dyn CoinContext, + input: &Proto::SigningInput<'_>, + ) -> SigningResult> { + let fee = input + .fee + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_wrong_fee))?; + let signer = Self::signer_info_from_proto(coin, input)?; + + Ok(UnsignedTransaction { + signer, + fee: Self::fee_from_proto(fee)?, + chain_id: input.chain_id.to_string(), + account_number: input.account_number, + tx_body: Self::tx_body_from_proto(coin, input)?, + }) + } + + pub fn signer_info_from_proto( + coin: &dyn CoinContext, + input: &Proto::SigningInput, + ) -> SigningResult> { + let public_key = Context::PublicKey::from_bytes(coin, &input.public_key)?; + Ok(SignerInfo { + public_key, + sequence: input.sequence, + // At this moment, we support the Direct signing mode only. + sign_mode: SignMode::Direct, + }) + } + + fn fee_from_proto(input: &Proto::Fee) -> SigningResult> { + let amounts = input + .amounts + .iter() + .map(Self::coin_from_proto) + .collect::>()?; + Ok(Fee { + amounts, + gas_limit: input.gas, + payer: None, + granter: None, + }) + } + + fn coin_from_proto(input: &Proto::Amount<'_>) -> SigningResult { + let amount = U256::from_str(&input.amount)?; + Ok(Coin { + amount, + denom: input.denom.to_string(), + }) + } + + fn tx_body_from_proto( + coin: &dyn CoinContext, + input: &Proto::SigningInput<'_>, + ) -> SigningResult { + if input.messages.is_empty() { + return Err(SigningError(SigningErrorType::Error_invalid_params)); + } + + let messages = input + .messages + .iter() + .map(|msg| Self::tx_message(coin, msg)) + .collect::>()?; + + Ok(TxBody { + messages, + memo: input.memo.to_string(), + timeout_height: DEFAULT_TIMEOUT_HEIGHT, + }) + } + + pub fn try_sign_direct_args( + input: &Proto::SigningInput<'_>, + ) -> SigningResult> { + use Proto::mod_Message::OneOfmessage_oneof as MessageEnum; + + let Some(msg) = input.messages.first() else { + return Ok(None); + }; + + match msg.message_oneof { + MessageEnum::sign_direct_message(ref direct) => Ok(Some(SignDirectArgs { + tx_body: direct.body_bytes.to_vec(), + auth_info: direct.auth_info_bytes.to_vec(), + chain_id: input.chain_id.to_string(), + account_number: input.account_number, + })), + _ => Ok(None), + } + } + + pub fn tx_message( + coin: &dyn CoinContext, + input: &Proto::Message, + ) -> SigningResult { + use Proto::mod_Message::OneOfmessage_oneof as MessageEnum; + + match input.message_oneof { + MessageEnum::send_coins_message(ref send) => Self::send_msg_from_proto(coin, send), + MessageEnum::transfer_tokens_message(ref transfer) => { + Self::transfer_tokens_msg_from_proto(coin, transfer) + }, + MessageEnum::stake_message(ref delegate) => { + Self::delegate_msg_from_proto(coin, delegate) + }, + MessageEnum::unstake_message(ref undelegate) => { + Self::undelegate_msg_from_proto(coin, undelegate) + }, + MessageEnum::withdraw_stake_reward_message(ref withdraw) => { + Self::withdraw_reward_msg_from_proto(coin, withdraw) + }, + MessageEnum::set_withdraw_address_message(ref set) => { + Self::set_withdraw_address_msg_from_proto(coin, set) + }, + MessageEnum::restake_message(ref redelegate) => { + Self::redelegate_msg_from_proto(coin, redelegate) + }, + MessageEnum::raw_json_message(ref raw_json) => { + Self::wasm_raw_msg_from_proto(coin, raw_json) + }, + MessageEnum::wasm_terra_execute_contract_transfer_message(ref transfer) => { + Self::wasm_terra_execute_contract_transfer_msg_from_proto(coin, transfer) + }, + MessageEnum::wasm_terra_execute_contract_send_message(ref send) => { + Self::wasm_terra_execute_contract_send_msg_from_proto(coin, send) + }, + MessageEnum::thorchain_send_message(ref send) => { + Self::thorchain_send_msg_from_proto(coin, send) + }, + MessageEnum::wasm_terra_execute_contract_generic(ref generic) => { + Self::wasm_terra_execute_contract_generic_msg_from_proto(coin, generic) + }, + MessageEnum::wasm_execute_contract_transfer_message(ref transfer) => { + Self::wasm_execute_contract_transfer_msg_from_proto(coin, transfer) + }, + MessageEnum::wasm_execute_contract_send_message(ref send) => { + Self::wasm_execute_contract_send_msg_from_proto(coin, send) + }, + MessageEnum::wasm_execute_contract_generic(ref generic) => { + Self::wasm_execute_contract_generic_msg_from_proto(coin, generic) + }, + MessageEnum::sign_direct_message(ref _sign) => { + // `SignDirect` message must be handled before this function is called. + // Consider using `Self::try_sign_direct_args` instead. + Err(SigningError(SigningErrorType::Error_not_supported)) + }, + MessageEnum::auth_grant(ref grant) => Self::auth_grant_msg_from_proto(coin, grant), + MessageEnum::auth_revoke(ref revoke) => Self::auth_revoke_msg_from_proto(coin, revoke), + MessageEnum::msg_vote(ref vote) => Self::vote_msg_from_proto(coin, vote), + MessageEnum::msg_stride_liquid_staking_stake(ref stake) => { + Self::stride_stake_msg_from_proto(coin, stake) + }, + MessageEnum::msg_stride_liquid_staking_redeem(ref redeem) => { + Self::stride_redeem_msg_from_proto(coin, redeem) + }, + MessageEnum::thorchain_deposit_message(ref deposit) => { + Self::thorchain_deposit_msg_from_proto(coin, deposit) + }, + MessageEnum::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + } + } + + pub fn send_msg_from_proto( + coin: &dyn CoinContext, + send: &Proto::mod_Message::Send<'_>, + ) -> SigningResult { + use crate::transaction::message::cosmos_bank_message::SendMessage; + + let amounts = send + .amounts + .iter() + .map(Self::coin_from_proto) + .collect::>()?; + let msg = SendMessage { + custom_type_prefix: Self::custom_msg_type(&send.type_prefix), + from_address: Address::from_str_with_coin(coin, &send.from_address)?, + to_address: Address::from_str_with_coin(coin, &send.to_address)?, + amount: amounts, + }; + Ok(msg.into_boxed()) + } + + pub fn transfer_tokens_msg_from_proto( + coin: &dyn CoinContext, + transfer: &Proto::mod_Message::Transfer<'_>, + ) -> SigningResult { + use crate::transaction::message::ibc_message::{Height, TransferTokensMessage}; + + let token = transfer + .token + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + let token = Self::coin_from_proto(token)?; + let height = transfer + .timeout_height + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + + let msg = TransferTokensMessage { + source_port: transfer.source_port.to_string(), + source_channel: transfer.source_channel.to_string(), + token, + sender: Address::from_str_with_coin(coin, &transfer.sender)?, + // Don't use `Address::from_str_with_coin` as the recipient address can belong to another Cosmos chain. + receiver: Address::from_str(&transfer.receiver)?, + timeout_height: Height { + revision_number: height.revision_number, + revision_height: height.revision_height, + }, + timeout_timestamp: transfer.timeout_timestamp, + }; + Ok(msg.into_boxed()) + } + + pub fn delegate_msg_from_proto( + coin: &dyn CoinContext, + delegate: &Proto::mod_Message::Delegate<'_>, + ) -> SigningResult { + use crate::transaction::message::cosmos_staking_message::DelegateMessage; + + let amount = delegate + .amount + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + let amount = Self::coin_from_proto(amount)?; + let msg = DelegateMessage { + custom_type_prefix: Self::custom_msg_type(&delegate.type_prefix), + amount, + delegator_address: Address::from_str_with_coin(coin, &delegate.delegator_address)?, + validator_address: Address::from_str_with_coin(coin, &delegate.validator_address)?, + }; + Ok(msg.into_boxed()) + } + + pub fn undelegate_msg_from_proto( + coin: &dyn CoinContext, + undelegate: &Proto::mod_Message::Undelegate<'_>, + ) -> SigningResult { + use crate::transaction::message::cosmos_staking_message::UndelegateMessage; + + let amount = undelegate + .amount + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + let amount = Self::coin_from_proto(amount)?; + + let msg = UndelegateMessage { + custom_type_prefix: Self::custom_msg_type(&undelegate.type_prefix), + amount, + delegator_address: Address::from_str_with_coin(coin, &undelegate.delegator_address)?, + validator_address: Address::from_str_with_coin(coin, &undelegate.validator_address)?, + }; + Ok(msg.into_boxed()) + } + + pub fn withdraw_reward_msg_from_proto( + coin: &dyn CoinContext, + withdraw: &Proto::mod_Message::WithdrawDelegationReward<'_>, + ) -> SigningResult { + use crate::transaction::message::cosmos_staking_message::WithdrawDelegationRewardMessage; + + let msg = WithdrawDelegationRewardMessage { + custom_type_prefix: Self::custom_msg_type(&withdraw.type_prefix), + delegator_address: Address::from_str_with_coin(coin, &withdraw.delegator_address)?, + validator_address: Address::from_str_with_coin(coin, &withdraw.validator_address)?, + }; + Ok(msg.into_boxed()) + } + + pub fn set_withdraw_address_msg_from_proto( + coin: &dyn CoinContext, + set: &Proto::mod_Message::SetWithdrawAddress<'_>, + ) -> SigningResult { + use crate::transaction::message::cosmos_staking_message::SetWithdrawAddressMessage; + + let msg = SetWithdrawAddressMessage { + custom_type_prefix: Self::custom_msg_type(&set.type_prefix), + delegator_address: Address::from_str_with_coin(coin, &set.delegator_address)?, + withdraw_address: Address::from_str_with_coin(coin, &set.withdraw_address)?, + }; + Ok(msg.into_boxed()) + } + + pub fn redelegate_msg_from_proto( + coin: &dyn CoinContext, + redelegate: &Proto::mod_Message::BeginRedelegate<'_>, + ) -> SigningResult { + use crate::transaction::message::cosmos_staking_message::BeginRedelegateMessage; + + let amount = redelegate + .amount + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + let amount = Self::coin_from_proto(amount)?; + let validator_src_address = + Address::from_str_with_coin(coin, &redelegate.validator_src_address)?; + let validator_dst_address = + Address::from_str_with_coin(coin, &redelegate.validator_dst_address)?; + + let msg = BeginRedelegateMessage { + custom_type_prefix: Self::custom_msg_type(&redelegate.type_prefix), + amount, + delegator_address: Address::from_str_with_coin(coin, &redelegate.delegator_address)?, + validator_src_address, + validator_dst_address, + }; + Ok(msg.into_boxed()) + } + + pub fn wasm_raw_msg_from_proto( + _coin: &dyn CoinContext, + raw: &Proto::mod_Message::RawJSON<'_>, + ) -> SigningResult { + let value = serde_json::from_str(&raw.value) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + + let msg = JsonRawMessage { + msg_type: raw.type_pb.to_string(), + value, + }; + Ok(msg.into_boxed()) + } + + pub fn wasm_terra_execute_contract_transfer_msg_from_proto( + coin: &dyn CoinContext, + transfer: &Proto::mod_Message::WasmTerraExecuteContractTransfer<'_>, + ) -> SigningResult { + use crate::transaction::message::terra_wasm_message::TerraExecuteContractMessage; + use crate::transaction::message::wasm_message::{ExecuteMsg, WasmExecutePayload}; + + let execute_payload = WasmExecutePayload::Transfer { + amount: U256::from_big_endian_slice(&transfer.amount)?, + recipient: transfer.recipient_address.to_string(), + }; + + let msg = TerraExecuteContractMessage { + sender: Address::from_str_with_coin(coin, &transfer.sender_address)?, + contract: Address::from_str_with_coin(coin, &transfer.contract_address)?, + execute_msg: ExecuteMsg::json(execute_payload)?, + // Used in case you are sending native tokens along with this message. + coins: Vec::default(), + }; + Ok(msg.into_boxed()) + } + + pub fn wasm_terra_execute_contract_send_msg_from_proto( + coin: &dyn CoinContext, + send: &Proto::mod_Message::WasmTerraExecuteContractSend<'_>, + ) -> SigningResult { + use crate::transaction::message::terra_wasm_message::TerraExecuteContractMessage; + use crate::transaction::message::wasm_message::{ExecuteMsg, WasmExecutePayload}; + + let execute_payload = WasmExecutePayload::Send { + amount: U256::from_big_endian_slice(&send.amount)?, + contract: send.recipient_contract_address.to_string(), + msg: send.msg.to_string(), + }; + + let msg = TerraExecuteContractMessage { + sender: Address::from_str_with_coin(coin, &send.sender_address)?, + contract: Address::from_str_with_coin(coin, &send.contract_address)?, + execute_msg: ExecuteMsg::json(execute_payload)?, + // Used in case you are sending native tokens along with this message. + coins: Vec::default(), + }; + Ok(msg.into_boxed()) + } + + pub fn wasm_terra_execute_contract_generic_msg_from_proto( + coin: &dyn CoinContext, + generic: &Proto::mod_Message::WasmTerraExecuteContractGeneric<'_>, + ) -> SigningResult { + use crate::transaction::message::terra_wasm_message::TerraExecuteContractMessage; + use crate::transaction::message::wasm_message::ExecuteMsg; + + let coins = generic + .coins + .iter() + .map(Self::coin_from_proto) + .collect::>()?; + + let msg = TerraExecuteContractMessage { + sender: Address::from_str_with_coin(coin, &generic.sender_address)?, + contract: Address::from_str_with_coin(coin, &generic.contract_address)?, + execute_msg: ExecuteMsg::String(generic.execute_msg.to_string()), + coins, + }; + Ok(msg.into_boxed()) + } + + pub fn wasm_execute_contract_transfer_msg_from_proto( + coin: &dyn CoinContext, + transfer: &Proto::mod_Message::WasmExecuteContractTransfer<'_>, + ) -> SigningResult { + use crate::transaction::message::wasm_message::{ + ExecuteMsg, WasmExecuteContractMessage, WasmExecutePayload, + }; + + let transfer_payload = WasmExecutePayload::Transfer { + amount: U256::from_big_endian_slice(&transfer.amount)?, + recipient: transfer.recipient_address.to_string(), + }; + + let msg = WasmExecuteContractMessage { + sender: Address::from_str_with_coin(coin, &transfer.sender_address)?, + contract: Address::from_str_with_coin(coin, &transfer.contract_address)?, + msg: ExecuteMsg::json(transfer_payload)?, + // Used in case you are sending native tokens along with this message. + coins: Vec::default(), + }; + Ok(msg.into_boxed()) + } + + pub fn wasm_execute_contract_send_msg_from_proto( + coin: &dyn CoinContext, + send: &Proto::mod_Message::WasmExecuteContractSend<'_>, + ) -> SigningResult { + use crate::transaction::message::wasm_message::{ + ExecuteMsg, WasmExecuteContractMessage, WasmExecutePayload, + }; + + let execute_payload = WasmExecutePayload::Send { + amount: U256::from_big_endian_slice(&send.amount)?, + contract: send.recipient_contract_address.to_string(), + msg: send.msg.to_string(), + }; + + let msg = WasmExecuteContractMessage { + sender: Address::from_str_with_coin(coin, &send.sender_address)?, + contract: Address::from_str_with_coin(coin, &send.contract_address)?, + msg: ExecuteMsg::json(execute_payload)?, + // Used in case you are sending native tokens along with this message. + coins: Vec::default(), + }; + Ok(msg.into_boxed()) + } + + pub fn wasm_execute_contract_generic_msg_from_proto( + coin: &dyn CoinContext, + generic: &Proto::mod_Message::WasmExecuteContractGeneric<'_>, + ) -> SigningResult { + use crate::transaction::message::wasm_message::{ExecuteMsg, WasmExecuteContractMessage}; + + let coins = generic + .coins + .iter() + .map(Self::coin_from_proto) + .collect::>()?; + + let msg = WasmExecuteContractMessage { + sender: Address::from_str_with_coin(coin, &generic.sender_address)?, + contract: Address::from_str_with_coin(coin, &generic.contract_address)?, + msg: ExecuteMsg::String(generic.execute_msg.to_string()), + coins, + }; + Ok(msg.into_boxed()) + } + + pub fn thorchain_send_msg_from_proto( + _coin: &dyn CoinContext, + send: &Proto::mod_Message::THORChainSend<'_>, + ) -> SigningResult { + use crate::transaction::message::thorchain_message::ThorchainSendMessage; + + let amount = send + .amounts + .iter() + .map(Self::coin_from_proto) + .collect::>()?; + + let msg = ThorchainSendMessage { + from_address: send.from_address.to_vec(), + to_address: send.to_address.to_vec(), + amount, + }; + Ok(msg.into_boxed()) + } + + pub fn auth_grant_msg_from_proto( + coin: &dyn CoinContext, + auth: &Proto::mod_Message::AuthGrant<'_>, + ) -> SigningResult { + use crate::transaction::message::cosmos_auth_message::AuthGrantMessage; + use Proto::mod_Message::mod_AuthGrant::OneOfgrant_type as ProtoGrantType; + + const STAKE_AUTHORIZATION_MSG_TYPE: &str = "/cosmos.staking.v1beta1.StakeAuthorization"; + + let grant_msg = match auth.grant_type { + ProtoGrantType::grant_stake(ref stake) => google::protobuf::Any { + type_url: STAKE_AUTHORIZATION_MSG_TYPE.to_string(), + value: serialize(stake) + .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?, + }, + ProtoGrantType::None => { + return Err(SigningError(SigningErrorType::Error_invalid_params)) + }, + }; + + let msg = AuthGrantMessage { + granter: Address::from_str_with_coin(coin, &auth.granter)?, + grantee: Address::from_str_with_coin(coin, &auth.grantee)?, + grant_msg, + expiration_secs: auth.expiration, + }; + Ok(msg.into_boxed()) + } + + pub fn auth_revoke_msg_from_proto( + coin: &dyn CoinContext, + auth: &Proto::mod_Message::AuthRevoke<'_>, + ) -> SigningResult { + use crate::transaction::message::cosmos_auth_message::AuthRevokeMessage; + + let msg = AuthRevokeMessage { + granter: Address::from_str_with_coin(coin, &auth.granter)?, + grantee: Address::from_str_with_coin(coin, &auth.grantee)?, + msg_type_url: auth.msg_type_url.to_string(), + }; + Ok(msg.into_boxed()) + } + + pub fn vote_msg_from_proto( + coin: &dyn CoinContext, + vote: &Proto::mod_Message::MsgVote<'_>, + ) -> SigningResult { + use crate::transaction::message::cosmos_gov_message::{VoteMessage, VoteOption}; + use Proto::mod_Message::VoteOption as ProtoVoteOption; + + let option = match vote.option { + ProtoVoteOption::_UNSPECIFIED => VoteOption::Unspecified, + ProtoVoteOption::YES => VoteOption::Yes, + ProtoVoteOption::ABSTAIN => VoteOption::Abstain, + ProtoVoteOption::NO => VoteOption::No, + ProtoVoteOption::NO_WITH_VETO => VoteOption::NoWithVeto, + }; + + let msg = VoteMessage { + proposal_id: vote.proposal_id, + voter: Address::from_str_with_coin(coin, &vote.voter)?, + option, + }; + Ok(msg.into_boxed()) + } + + pub fn stride_stake_msg_from_proto( + coin: &dyn CoinContext, + stake: &Proto::mod_Message::MsgStrideLiquidStakingStake<'_>, + ) -> SigningResult { + use crate::transaction::message::stride_message::StrideLiquidStakeMessage; + + let msg = StrideLiquidStakeMessage { + creator: Address::from_str_with_coin(coin, &stake.creator)?, + amount: U256::from_str(&stake.amount)?, + host_denom: stake.host_denom.to_string(), + }; + Ok(msg.into_boxed()) + } + + pub fn stride_redeem_msg_from_proto( + _coin: &dyn CoinContext, + redeem: &Proto::mod_Message::MsgStrideLiquidStakingRedeem<'_>, + ) -> SigningResult { + use crate::transaction::message::stride_message::StrideLiquidRedeemMessage; + + let msg = StrideLiquidRedeemMessage { + creator: redeem.creator.to_string(), + amount: U256::from_str(&redeem.amount)?, + receiver: redeem.receiver.to_string(), + host_zone: redeem.host_zone.to_string(), + }; + Ok(msg.into_boxed()) + } + + pub fn thorchain_deposit_msg_from_proto( + _coin: &dyn CoinContext, + deposit: &Proto::mod_Message::THORChainDeposit<'_>, + ) -> SigningResult { + use crate::transaction::message::thorchain_message::{ + ThorchainAsset, ThorchainCoin, ThorchainDepositMessage, + }; + + let mut coins = Vec::with_capacity(deposit.coins.len()); + for coin_proto in deposit.coins.iter() { + let asset_proto = coin_proto + .asset + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + + let asset = ThorchainAsset { + chain: asset_proto.chain.to_string(), + symbol: asset_proto.symbol.to_string(), + ticker: asset_proto.ticker.to_string(), + synth: asset_proto.synth, + }; + coins.push(ThorchainCoin { + asset, + amount: U256::from_str(&coin_proto.amount)?, + decimals: coin_proto.decimals, + }); + } + + let msg = ThorchainDepositMessage { + coins, + memo: deposit.memo.to_string(), + signer: deposit.signer.to_vec(), + }; + Ok(msg.into_boxed()) + } + + fn custom_msg_type(type_prefix: &str) -> Option { + if type_prefix.is_empty() { + None + } else { + Some(type_prefix.to_string()) + } + } +} diff --git a/rust/tw_cosmos_sdk/src/private_key/mod.rs b/rust/tw_cosmos_sdk/src/private_key/mod.rs new file mode 100644 index 00000000000..30045cc6549 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/private_key/mod.rs @@ -0,0 +1,18 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::error::SigningResult; +use tw_keypair::{tw, KeyPairError}; +use tw_memory::Data; +use tw_misc::traits::FromSlice; + +pub mod secp256k1; + +pub type SignatureData = Data; + +pub trait CosmosPrivateKey: AsRef + FromSlice { + fn sign_tx_hash(&self, hash: &[u8]) -> SigningResult; +} diff --git a/rust/tw_cosmos_sdk/src/private_key/secp256k1.rs b/rust/tw_cosmos_sdk/src/private_key/secp256k1.rs new file mode 100644 index 00000000000..921409f4cd4 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/private_key/secp256k1.rs @@ -0,0 +1,36 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::private_key::CosmosPrivateKey; +use tw_coin_entry::error::{SigningError, SigningResult}; +use tw_keypair::tw; +use tw_keypair::tw::Curve; +use tw_keypair::KeyPairError; +use tw_memory::Data; + +pub struct Secp256PrivateKey(tw::PrivateKey); + +impl AsRef for Secp256PrivateKey { + fn as_ref(&self) -> &tw::PrivateKey { + &self.0 + } +} + +impl<'a> TryFrom<&'a [u8]> for Secp256PrivateKey { + type Error = KeyPairError; + + fn try_from(value: &'a [u8]) -> Result { + tw::PrivateKey::new(value.to_vec()).map(Secp256PrivateKey) + } +} + +impl CosmosPrivateKey for Secp256PrivateKey { + fn sign_tx_hash(&self, hash: &[u8]) -> SigningResult { + self.0 + .sign(hash, Curve::Secp256k1) + .map_err(SigningError::from) + } +} diff --git a/rust/tw_cosmos_sdk/src/public_key/mod.rs b/rust/tw_cosmos_sdk/src/public_key/mod.rs new file mode 100644 index 00000000000..d3a4e20f5e8 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/public_key/mod.rs @@ -0,0 +1,32 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::coin_context::CoinContext; +use tw_keypair::{tw, KeyPairResult}; +use tw_memory::Data; +use tw_proto::google; + +pub mod secp256k1; + +pub trait CosmosPublicKey: JsonPublicKey + ProtobufPublicKey + Sized { + fn from_private_key( + coin: &dyn CoinContext, + private_key: &tw::PrivateKey, + ) -> KeyPairResult; + + fn from_bytes(coin: &dyn CoinContext, public_key_bytes: &[u8]) -> KeyPairResult; + + fn to_bytes(&self) -> Data; +} + +pub trait ProtobufPublicKey { + fn to_proto(&self) -> google::protobuf::Any; +} + +pub trait JsonPublicKey { + /// In most cases, [`JsonPublicKey::public_key_type`] returns a corresponding protobuf message type. + fn public_key_type(&self) -> String; +} diff --git a/rust/tw_cosmos_sdk/src/public_key/secp256k1.rs b/rust/tw_cosmos_sdk/src/public_key/secp256k1.rs new file mode 100644 index 00000000000..3558aba8626 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/public_key/secp256k1.rs @@ -0,0 +1,67 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::proto::cosmos; +use crate::public_key::{CosmosPublicKey, JsonPublicKey, ProtobufPublicKey}; +use tw_coin_entry::coin_context::CoinContext; +use tw_keypair::ecdsa::secp256k1; +use tw_keypair::tw::{self, PublicKeyType}; +use tw_keypair::{KeyPairError, KeyPairResult}; +use tw_memory::Data; +use tw_misc::traits::ToBytesVec; +use tw_proto::{google, to_any}; + +pub struct Secp256PublicKey { + public_key: Data, +} + +impl CosmosPublicKey for Secp256PublicKey { + fn from_private_key(coin: &dyn CoinContext, private_key: &tw::PrivateKey) -> KeyPairResult + where + Self: Sized, + { + let public_key = private_key.get_public_key_by_type(coin.public_key_type())?; + Ok(Secp256PublicKey { + public_key: public_key.to_bytes(), + }) + } + + fn from_bytes(coin: &dyn CoinContext, public_key_bytes: &[u8]) -> KeyPairResult { + let public_key = prepare_secp256k1_public_key(coin, public_key_bytes)?; + Ok(Secp256PublicKey { public_key }) + } + + fn to_bytes(&self) -> Data { + self.public_key.clone() + } +} + +impl ProtobufPublicKey for Secp256PublicKey { + fn to_proto(&self) -> google::protobuf::Any { + let proto = cosmos::crypto::secp256k1::PubKey { + key: self.public_key.clone(), + }; + to_any(&proto) + } +} + +impl JsonPublicKey for Secp256PublicKey { + fn public_key_type(&self) -> String { + "tendermint/PubKeySecp256k1".to_string() + } +} + +pub fn prepare_secp256k1_public_key( + coin: &dyn CoinContext, + public_key_bytes: &[u8], +) -> KeyPairResult { + let public_key = secp256k1::PublicKey::try_from(public_key_bytes)?; + match coin.public_key_type() { + PublicKeyType::Secp256k1 => Ok(public_key.compressed().to_vec()), + PublicKeyType::Secp256k1Extended => Ok(public_key.uncompressed().to_vec()), + _ => Err(KeyPairError::InvalidPublicKey), + } +} diff --git a/rust/tw_cosmos_sdk/src/signature/mod.rs b/rust/tw_cosmos_sdk/src/signature/mod.rs new file mode 100644 index 00000000000..d2c5ba2169e --- /dev/null +++ b/rust/tw_cosmos_sdk/src/signature/mod.rs @@ -0,0 +1,16 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_keypair::KeyPairResult; +use tw_memory::Data; + +pub mod secp256k1; + +pub trait CosmosSignature: Sized { + fn from_bytes(signature_bytes: &[u8]) -> KeyPairResult; + + fn to_bytes(&self) -> Data; +} diff --git a/rust/tw_cosmos_sdk/src/signature/secp256k1.rs b/rust/tw_cosmos_sdk/src/signature/secp256k1.rs new file mode 100644 index 00000000000..f07ec5957ff --- /dev/null +++ b/rust/tw_cosmos_sdk/src/signature/secp256k1.rs @@ -0,0 +1,34 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::signature::CosmosSignature; +use tw_hash::{H512, H520}; +use tw_keypair::{KeyPairError, KeyPairResult}; +use tw_memory::Data; +use tw_misc::traits::ToBytesVec; + +pub struct Secp256k1Signature { + signature: H512, +} + +impl CosmosSignature for Secp256k1Signature { + fn from_bytes(signature_bytes: &[u8]) -> KeyPairResult { + let signature_slice = if signature_bytes.len() == H520::len() { + // Discard the last `v` recovery byte. + &signature_bytes[0..H512::len()] + } else { + signature_bytes + }; + + let signature = + H512::try_from(signature_slice).map_err(|_| KeyPairError::InvalidSignature)?; + Ok(Secp256k1Signature { signature }) + } + + fn to_bytes(&self) -> Data { + self.signature.to_vec() + } +} diff --git a/rust/tw_cosmos_sdk/src/test_utils/mod.rs b/rust/tw_cosmos_sdk/src/test_utils/mod.rs new file mode 100644 index 00000000000..334b4aa821d --- /dev/null +++ b/rust/tw_cosmos_sdk/src/test_utils/mod.rs @@ -0,0 +1,8 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod proto_utils; +pub mod sign_utils; diff --git a/rust/tw_cosmos_sdk/src/test_utils/proto_utils.rs b/rust/tw_cosmos_sdk/src/test_utils/proto_utils.rs new file mode 100644 index 00000000000..5bef94cc9eb --- /dev/null +++ b/rust/tw_cosmos_sdk/src/test_utils/proto_utils.rs @@ -0,0 +1,33 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_proto::Cosmos::Proto; +use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum; + +pub fn make_fee(gas: u64, amount: Proto::Amount<'_>) -> Proto::Fee<'_> { + Proto::Fee { + amounts: vec![amount], + gas, + } +} + +pub fn make_fee_none(gas: u64) -> Proto::Fee<'static> { + Proto::Fee { + amounts: Vec::default(), + gas, + } +} + +pub fn make_message(message_oneof: MessageEnum) -> Proto::Message { + Proto::Message { message_oneof } +} + +pub fn make_amount<'a>(denom: &'a str, amount: &'a str) -> Proto::Amount<'a> { + Proto::Amount { + denom: denom.into(), + amount: amount.into(), + } +} diff --git a/rust/tw_cosmos_sdk/src/test_utils/sign_utils.rs b/rust/tw_cosmos_sdk/src/test_utils/sign_utils.rs new file mode 100644 index 00000000000..3e439ad4dd1 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/test_utils/sign_utils.rs @@ -0,0 +1,167 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::context::CosmosContext; +use crate::modules::compiler::tw_compiler::TWTransactionCompiler; +use crate::modules::signer::tw_signer::TWSigner; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::SigningErrorType; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_proto::Common::Proto::SigningError; +use tw_proto::Cosmos::Proto; + +#[derive(Clone)] +pub struct TestInput<'a> { + pub coin: &'a dyn CoinContext, + pub input: Proto::SigningInput<'a>, + /// Stringified JSON object. + pub tx: &'a str, + /// Signature hex-encoded. + pub signature: &'a str, + /// Stringified signature JSON object. + pub signature_json: &'a str, +} + +#[derive(Clone)] +pub struct TestCompileInput<'a> { + pub coin: &'a dyn CoinContext, + pub input: Proto::SigningInput<'a>, + /// Either a stringified JSON object or a hex-encoded serialzied `SignDoc`. + pub tx_preimage: &'a str, + /// Expected transaction preimage hash. + pub tx_prehash: &'a str, + /// Stringified JSON object. + pub tx: &'a str, + /// Signature hex-encoded. + pub signature: &'a str, + /// Stringified signature JSON object. + pub signature_json: &'a str, +} + +#[derive(Clone)] +pub struct TestErrorInput<'a> { + pub coin: &'a dyn CoinContext, + pub input: Proto::SigningInput<'a>, + pub error: SigningErrorType, +} + +#[track_caller] +pub fn test_compile_protobuf(mut test_input: TestCompileInput<'_>) { + test_input.input.signing_mode = Proto::SigningMode::Protobuf; + test_compile_impl::(test_input); +} + +#[track_caller] +pub fn test_compile_json(mut test_input: TestCompileInput<'_>) { + test_input.input.signing_mode = Proto::SigningMode::JSON; + test_compile_impl::(test_input); +} + +#[track_caller] +pub fn test_sign_protobuf(mut test_input: TestInput<'_>) { + test_input.input.signing_mode = Proto::SigningMode::Protobuf; + test_sign_impl::(test_input); +} + +#[track_caller] +pub fn test_sign_protobuf_error(mut test_input: TestErrorInput<'_>) { + test_input.input.signing_mode = Proto::SigningMode::Protobuf; + let output = TWSigner::::sign(test_input.coin, test_input.input); + assert_eq!(output.error, test_input.error); +} + +#[track_caller] +pub fn test_sign_json(mut test_input: TestInput<'_>) { + test_input.input.signing_mode = Proto::SigningMode::JSON; + test_sign_impl::(test_input); +} + +#[track_caller] +pub fn test_sign_json_error(mut test_input: TestErrorInput<'_>) { + test_input.input.signing_mode = Proto::SigningMode::JSON; + let output = TWSigner::::sign(test_input.coin, test_input.input); + assert_eq!(output.error, test_input.error); +} + +#[track_caller] +fn test_sign_impl(test_input: TestInput<'_>) { + let output = TWSigner::::sign(test_input.coin, test_input.input); + assert_eq!(output.error, SigningError::OK); + assert!(output.error_message.is_empty()); + + let result = if output.serialized.is_empty() { + output.json + } else { + output.serialized + }; + assert_eq!(result, test_input.tx, "Unexpected result transaction"); + + assert_eq!( + output.signature.to_hex(), + test_input.signature, + "Unexpected signature" + ); + assert_eq!( + output.signature_json, test_input.signature_json, + "Unexpected signature JSON" + ); +} + +#[track_caller] +fn test_compile_impl(test_input: TestCompileInput<'_>) { + // First step - generate the preimage hashes. + let preimage_output = TWTransactionCompiler::::preimage_hashes( + test_input.coin, + test_input.input.clone(), + ); + assert_eq!(preimage_output.error, SigningError::OK); + assert!(preimage_output.error_message.is_empty()); + + let actual_str = match String::from_utf8(preimage_output.data.to_vec()) { + Ok(actual_tx) => actual_tx, + Err(_) => preimage_output.data.to_hex(), + }; + + assert_eq!( + actual_str, test_input.tx_preimage, + "Unexpected preimage transaction" + ); + assert_eq!( + preimage_output.data_hash.to_hex(), + test_input.tx_prehash, + "Unexpected preimage hash" + ); + + // Second step - Compile the transaction. + + let public_key = test_input.input.public_key.to_vec(); + let compile_output = TWTransactionCompiler::::compile( + test_input.coin, + test_input.input, + vec![test_input.signature.decode_hex().unwrap()], + vec![public_key], + ); + + assert_eq!(compile_output.error, SigningError::OK); + assert!(compile_output.error_message.is_empty()); + + let result_tx = if compile_output.serialized.is_empty() { + compile_output.json + } else { + compile_output.serialized + }; + assert_eq!(result_tx, test_input.tx, "Unexpected result transaction"); + + assert_eq!( + compile_output.signature.to_hex(), + test_input.signature, + "Unexpected signature" + ); + assert_eq!( + compile_output.signature_json, test_input.signature_json, + "Unexpected signature JSON" + ); +} diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_auth_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_auth_message.rs new file mode 100644 index 00000000000..7e892dd7f33 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_auth_message.rs @@ -0,0 +1,57 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::CosmosAddress; +use crate::proto::cosmos; +use crate::transaction::message::{CosmosMessage, ProtobufMessage}; +use tw_coin_entry::error::SigningResult; +use tw_proto::{google, to_any}; + +/// Supports Protobuf serialization only. +pub struct AuthGrantMessage { + pub granter: Address, + pub grantee: Address, + pub grant_msg: google::protobuf::Any, + pub expiration_secs: i64, +} + +impl CosmosMessage for AuthGrantMessage
{ + fn to_proto(&self) -> SigningResult { + let expiration = google::protobuf::Timestamp { + seconds: self.expiration_secs, + ..google::protobuf::Timestamp::default() + }; + let grant = cosmos::authz::v1beta1::Grant { + authorization: Some(self.grant_msg.clone()), + expiration: Some(expiration), + }; + + let proto_msg = cosmos::authz::v1beta1::MsgGrant { + granter: self.granter.to_string(), + grantee: self.grantee.to_string(), + grant: Some(grant), + }; + Ok(to_any(&proto_msg)) + } +} + +/// Supports Protobuf serialization only. +pub struct AuthRevokeMessage { + pub granter: Address, + pub grantee: Address, + pub msg_type_url: String, +} + +impl CosmosMessage for AuthRevokeMessage
{ + fn to_proto(&self) -> SigningResult { + let proto_msg = cosmos::authz::v1beta1::MsgRevoke { + granter: self.granter.to_string(), + grantee: self.grantee.to_string(), + msg_type_url: self.msg_type_url.clone(), + }; + Ok(to_any(&proto_msg)) + } +} diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs new file mode 100644 index 00000000000..dddfb065839 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs @@ -0,0 +1,45 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::CosmosAddress; +use crate::modules::serializer::protobuf_serializer::build_coin; +use crate::proto::cosmos; +use crate::transaction::message::{message_to_json, CosmosMessage, JsonMessage, ProtobufMessage}; +use crate::transaction::Coin; +use serde::Serialize; +use tw_coin_entry::error::SigningResult; +use tw_proto::to_any; + +const DEFAULT_JSON_SEND_TYPE: &str = "cosmos-sdk/MsgSend"; + +/// cosmos-sdk/MsgSend +#[derive(Serialize)] +pub struct SendMessage { + #[serde(skip)] + pub custom_type_prefix: Option, + pub from_address: Address, + pub to_address: Address, + pub amount: Vec, +} + +impl CosmosMessage for SendMessage
{ + fn to_proto(&self) -> SigningResult { + let proto_msg = cosmos::bank::v1beta1::MsgSend { + from_address: self.from_address.to_string(), + to_address: self.to_address.to_string(), + amount: self.amount.iter().map(build_coin).collect(), + }; + Ok(to_any(&proto_msg)) + } + + fn to_json(&self) -> SigningResult { + let msg_type = self + .custom_type_prefix + .as_deref() + .unwrap_or(DEFAULT_JSON_SEND_TYPE); + message_to_json(msg_type, self) + } +} diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_generic_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_generic_message.rs new file mode 100644 index 00000000000..6d64926930e --- /dev/null +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_generic_message.rs @@ -0,0 +1,25 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::transaction::message::{CosmosMessage, JsonMessage}; +use serde_json::Value as Json; +use tw_coin_entry::error::SigningResult; + +/// Any raw JSON message. +/// Supports JSON serialization only. +pub struct JsonRawMessage { + pub msg_type: String, + pub value: Json, +} + +impl CosmosMessage for JsonRawMessage { + fn to_json(&self) -> SigningResult { + Ok(JsonMessage { + msg_type: self.msg_type.clone(), + value: self.value.clone(), + }) + } +} diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_gov_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_gov_message.rs new file mode 100644 index 00000000000..2054761a6b5 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_gov_message.rs @@ -0,0 +1,46 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::CosmosAddress; +use crate::proto::cosmos; +use crate::transaction::message::{CosmosMessage, ProtobufMessage}; +use tw_coin_entry::error::SigningResult; +use tw_proto::to_any; + +pub enum VoteOption { + Unspecified, + Yes, + Abstain, + No, + NoWithVeto, +} + +pub struct VoteMessage { + pub proposal_id: u64, + pub voter: Address, + pub option: VoteOption, +} + +impl CosmosMessage for VoteMessage
{ + fn to_proto(&self) -> SigningResult { + use cosmos::gov::v1beta1::VoteOption as ProtoVoteOption; + + let option = match self.option { + VoteOption::Unspecified => ProtoVoteOption::VOTE_OPTION_UNSPECIFIED, + VoteOption::Yes => ProtoVoteOption::VOTE_OPTION_YES, + VoteOption::Abstain => ProtoVoteOption::VOTE_OPTION_ABSTAIN, + VoteOption::No => ProtoVoteOption::VOTE_OPTION_NO, + VoteOption::NoWithVeto => ProtoVoteOption::VOTE_OPTION_NO_WITH_VETO, + }; + + let proto_msg = cosmos::gov::v1beta1::MsgVote { + proposal_id: self.proposal_id, + voter: self.voter.to_string(), + option, + }; + Ok(to_any(&proto_msg)) + } +} diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_staking_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_staking_message.rs new file mode 100644 index 00000000000..353fd780547 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_staking_message.rs @@ -0,0 +1,163 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::CosmosAddress; +use crate::modules::serializer::protobuf_serializer::build_coin; +use crate::proto::cosmos; +use crate::transaction::message::{message_to_json, CosmosMessage, JsonMessage, ProtobufMessage}; +use crate::transaction::Coin; +use serde::Serialize; +use tw_coin_entry::error::SigningResult; +use tw_proto::to_any; + +const DEFAULT_JSON_SET_WITHDRAW_ADDRESS_TYPE: &str = "cosmos-sdk/MsgSetWithdrawAddress"; +const DEFAULT_JSON_WITHDRAW_REWARDS_TYPE: &str = "cosmos-sdk/MsgWithdrawDelegationReward"; +const DEFAULT_JSON_BEGIN_REDELEGATE_TYPE: &str = "cosmos-sdk/MsgBeginRedelegate"; +const DEFAULT_JSON_UNDELEGATE_TYPE: &str = "cosmos-sdk/MsgUndelegate"; +const DEFAULT_JSON_DELEGATE_TYPE: &str = "cosmos-sdk/MsgDelegate"; + +/// cosmos-sdk/MsgDelegate +#[derive(Serialize)] +pub struct DelegateMessage { + #[serde(skip)] + pub custom_type_prefix: Option, + pub amount: Coin, + pub delegator_address: Address, + pub validator_address: Address, +} + +impl CosmosMessage for DelegateMessage
{ + fn to_proto(&self) -> SigningResult { + let proto_msg = cosmos::staking::v1beta1::MsgDelegate { + amount: Some(build_coin(&self.amount)), + delegator_address: self.delegator_address.to_string(), + validator_address: self.validator_address.to_string(), + }; + Ok(to_any(&proto_msg)) + } + + fn to_json(&self) -> SigningResult { + let msg_type = self + .custom_type_prefix + .as_deref() + .unwrap_or(DEFAULT_JSON_DELEGATE_TYPE); + message_to_json(msg_type, self) + } +} + +/// cosmos-sdk/MsgUndelegate +#[derive(Serialize)] +pub struct UndelegateMessage { + #[serde(skip)] + pub custom_type_prefix: Option, + pub amount: Coin, + pub delegator_address: Address, + pub validator_address: Address, +} + +impl CosmosMessage for UndelegateMessage
{ + fn to_proto(&self) -> SigningResult { + let proto_msg = cosmos::staking::v1beta1::MsgUndelegate { + amount: Some(build_coin(&self.amount)), + delegator_address: self.delegator_address.to_string(), + validator_address: self.validator_address.to_string(), + }; + Ok(to_any(&proto_msg)) + } + + fn to_json(&self) -> SigningResult { + let msg_type = self + .custom_type_prefix + .as_deref() + .unwrap_or(DEFAULT_JSON_UNDELEGATE_TYPE); + message_to_json(msg_type, self) + } +} + +/// cosmos-sdk/MsgBeginRedelegate +#[derive(Serialize)] +pub struct BeginRedelegateMessage { + #[serde(skip)] + pub custom_type_prefix: Option, + pub amount: Coin, + pub delegator_address: Address, + pub validator_src_address: Address, + pub validator_dst_address: Address, +} + +impl CosmosMessage for BeginRedelegateMessage
{ + fn to_proto(&self) -> SigningResult { + let proto_msg = cosmos::staking::v1beta1::MsgBeginRedelegate { + amount: Some(build_coin(&self.amount)), + delegator_address: self.delegator_address.to_string(), + validator_src_address: self.validator_src_address.to_string(), + validator_dst_address: self.validator_dst_address.to_string(), + }; + Ok(to_any(&proto_msg)) + } + + fn to_json(&self) -> SigningResult { + let msg_type = self + .custom_type_prefix + .as_deref() + .unwrap_or(DEFAULT_JSON_BEGIN_REDELEGATE_TYPE); + message_to_json(msg_type, self) + } +} + +/// cosmos-sdk/MsgWithdrawDelegationReward +#[derive(Serialize)] +pub struct WithdrawDelegationRewardMessage { + #[serde(skip)] + pub custom_type_prefix: Option, + pub delegator_address: Address, + pub validator_address: Address, +} + +impl CosmosMessage for WithdrawDelegationRewardMessage
{ + fn to_proto(&self) -> SigningResult { + let proto_msg = cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward { + delegator_address: self.delegator_address.to_string(), + validator_address: self.validator_address.to_string(), + }; + Ok(to_any(&proto_msg)) + } + + fn to_json(&self) -> SigningResult { + let msg_type = self + .custom_type_prefix + .as_deref() + .unwrap_or(DEFAULT_JSON_WITHDRAW_REWARDS_TYPE); + message_to_json(msg_type, self) + } +} + +/// cosmos-sdk/MsgSetWithdrawAddress +#[derive(Serialize)] +pub struct SetWithdrawAddressMessage { + #[serde(skip)] + pub custom_type_prefix: Option, + pub delegator_address: Address, + pub withdraw_address: Address, +} + +impl CosmosMessage for SetWithdrawAddressMessage
{ + fn to_proto(&self) -> SigningResult { + let proto_msg = cosmos::distribution::v1beta1::MsgSetWithdrawAddress { + delegator_address: self.delegator_address.to_string(), + withdraw_address: self.withdraw_address.to_string(), + }; + Ok(to_any(&proto_msg)) + } + + fn to_json(&self) -> SigningResult { + let msg_type = self + .custom_type_prefix + .as_deref() + .unwrap_or(DEFAULT_JSON_SET_WITHDRAW_ADDRESS_TYPE); + message_to_json(msg_type, self) + } +} diff --git a/rust/tw_cosmos_sdk/src/transaction/message/ibc_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/ibc_message.rs new file mode 100644 index 00000000000..71d51e4b9fe --- /dev/null +++ b/rust/tw_cosmos_sdk/src/transaction/message/ibc_message.rs @@ -0,0 +1,53 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::CosmosAddress; +use crate::modules::serializer::protobuf_serializer::build_coin; +use crate::proto::ibc; +use crate::transaction::message::{CosmosMessage, ProtobufMessage}; +use crate::transaction::Coin; +use tw_coin_entry::error::SigningResult; +use tw_proto::to_any; + +pub struct Height { + pub revision_number: u64, + pub revision_height: u64, +} + +pub struct TransferTokensMessage { + /// IBC port, e.g. "transfer". + pub source_port: String, + /// IBC connection channel, e.g. "channel-141", see apis /ibc/applications/transfer/v1beta1/denom_traces (connections) or /node_info (own channel). + pub source_channel: String, + pub token: Coin, + pub sender: Address, + pub receiver: Address, + /// Timeout block height. Either timeout height or timestamp should be set. + /// Recommendation is to set height, to rev. 1 and block current + 1000 (see api /blocks/latest). + pub timeout_height: Height, + // Timeout timestamp (in nanoseconds) relative to the current block timestamp. Either timeout height or timestamp should be set. + pub timeout_timestamp: u64, +} + +impl CosmosMessage for TransferTokensMessage
{ + fn to_proto(&self) -> SigningResult { + let height = ibc::core::client::v1::Height { + revision_number: self.timeout_height.revision_number, + revision_height: self.timeout_height.revision_height, + }; + + let proto_msg = ibc::applications::transfer::v1::MsgTransfer { + source_port: self.source_port.clone(), + source_channel: self.source_channel.clone(), + token: Some(build_coin(&self.token)), + sender: self.sender.to_string(), + receiver: self.receiver.to_string(), + timeout_height: Some(height), + timeout_timestamp: self.timeout_timestamp, + }; + Ok(to_any(&proto_msg)) + } +} diff --git a/rust/tw_cosmos_sdk/src/transaction/message/mod.rs b/rust/tw_cosmos_sdk/src/transaction/message/mod.rs new file mode 100644 index 00000000000..bcc1d103bee --- /dev/null +++ b/rust/tw_cosmos_sdk/src/transaction/message/mod.rs @@ -0,0 +1,56 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::modules::serializer::json_serializer::AnyMsg; +use serde::Serialize; +use serde_json::Value as Json; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_proto::google; + +pub mod cosmos_auth_message; +pub mod cosmos_bank_message; +pub mod cosmos_generic_message; +pub mod cosmos_gov_message; +pub mod cosmos_staking_message; +pub mod ibc_message; +pub mod stride_message; +pub mod terra_wasm_message; +pub mod thorchain_message; +pub mod wasm_message; + +pub type ProtobufMessage = google::protobuf::Any; +pub type CosmosMessageBox = Box; +pub type JsonMessage = AnyMsg; + +pub trait CosmosMessage { + fn into_boxed(self) -> CosmosMessageBox + where + Self: 'static + Sized, + { + Box::new(self) + } + + /// Override the method if the message can be represented as a Protobuf message. + fn to_proto(&self) -> SigningResult { + Err(SigningError(SigningErrorType::Error_not_supported)) + } + + /// Override the method if the message can be represented as a JSON object. + fn to_json(&self) -> SigningResult { + Err(SigningError(SigningErrorType::Error_not_supported)) + } +} + +/// A standard implementation of the [`CosmosMessage::to_json`] method. +/// This suits any message type that implements the `serialize` trait. +pub fn message_to_json(msg_type: &str, msg: &T) -> SigningResult { + let value = + serde_json::to_value(msg).map_err(|_| SigningError(SigningErrorType::Error_internal))?; + Ok(JsonMessage { + msg_type: msg_type.to_string(), + value, + }) +} diff --git a/rust/tw_cosmos_sdk/src/transaction/message/stride_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/stride_message.rs new file mode 100644 index 00000000000..fe99e32c155 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/transaction/message/stride_message.rs @@ -0,0 +1,48 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::CosmosAddress; +use crate::proto::stride; +use crate::transaction::message::{CosmosMessage, ProtobufMessage}; +use tw_coin_entry::error::SigningResult; +use tw_number::U256; +use tw_proto::to_any; + +pub struct StrideLiquidStakeMessage { + pub creator: Address, + pub amount: U256, + pub host_denom: String, +} + +impl CosmosMessage for StrideLiquidStakeMessage
{ + fn to_proto(&self) -> SigningResult { + let proto_msg = stride::stakeibc::MsgLiquidStake { + creator: self.creator.to_string(), + amount: self.amount.to_string(), + host_denom: self.host_denom.clone(), + }; + Ok(to_any(&proto_msg)) + } +} + +pub struct StrideLiquidRedeemMessage { + pub creator: String, + pub amount: U256, + pub receiver: String, + pub host_zone: String, +} + +impl CosmosMessage for StrideLiquidRedeemMessage { + fn to_proto(&self) -> SigningResult { + let proto_msg = stride::stakeibc::MsgRedeemStake { + creator: self.creator.clone(), + amount: self.amount.to_string(), + receiver: self.receiver.clone(), + host_zone: self.host_zone.clone(), + }; + Ok(to_any(&proto_msg)) + } +} diff --git a/rust/tw_cosmos_sdk/src/transaction/message/terra_wasm_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/terra_wasm_message.rs new file mode 100644 index 00000000000..617f563d930 --- /dev/null +++ b/rust/tw_cosmos_sdk/src/transaction/message/terra_wasm_message.rs @@ -0,0 +1,54 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::CosmosAddress; +use crate::modules::serializer::protobuf_serializer::build_coin; +use crate::proto::terra; +use crate::transaction::message::wasm_message::ExecuteMsg; +use crate::transaction::message::{CosmosMessage, JsonMessage, ProtobufMessage}; +use crate::transaction::Coin; +use serde::Serialize; +use serde_json::json; +use tw_coin_entry::error::SigningResult; +use tw_proto::to_any; + +const DEFAULT_JSON_MSG_TYPE: &str = "wasm/MsgExecuteContract"; + +/// This method not only support token transfer, but also support all other types of contract call. +/// https://docs.terra.money/Tutorials/Smart-contracts/Manage-CW20-tokens.html#interacting-with-cw20-contract +#[derive(Serialize)] +pub struct TerraExecuteContractMessage { + pub sender: Address, + pub contract: Address, + pub execute_msg: ExecuteMsg, + pub coins: Vec, +} + +impl CosmosMessage for TerraExecuteContractMessage
{ + fn to_proto(&self) -> SigningResult { + let proto_msg = terra::wasm::v1beta1::MsgExecuteContract { + sender: self.sender.to_string(), + contract: self.contract.to_string(), + execute_msg: self.execute_msg.to_bytes(), + coins: self.coins.iter().map(build_coin).collect(), + }; + Ok(to_any(&proto_msg)) + } + + fn to_json(&self) -> SigningResult { + // Don't use `message_to_json` because we need to try to convert [`ExecuteMsg::String`] to [`ExecuteMsg::Json`] if possible. + let value = json!({ + "coins": self.coins, + "contract": self.contract, + "execute_msg": self.execute_msg.try_to_json(), + "sender": self.sender, + }); + Ok(JsonMessage { + msg_type: DEFAULT_JSON_MSG_TYPE.to_string(), + value, + }) + } +} diff --git a/rust/tw_cosmos_sdk/src/transaction/message/thorchain_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/thorchain_message.rs new file mode 100644 index 00000000000..2d33174bcee --- /dev/null +++ b/rust/tw_cosmos_sdk/src/transaction/message/thorchain_message.rs @@ -0,0 +1,82 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::modules::serializer::protobuf_serializer::build_coin; +use crate::proto::types; +use crate::transaction::message::{CosmosMessage, ProtobufMessage}; +use crate::transaction::Coin; +use tw_coin_entry::error::SigningResult; +use tw_memory::Data; +use tw_number::U256; +use tw_proto::to_any; + +pub struct ThorchainAsset { + pub chain: String, + pub symbol: String, + pub ticker: String, + pub synth: bool, +} + +impl ThorchainAsset { + pub fn to_proto(&self) -> types::Asset { + types::Asset { + chain: self.chain.clone(), + symbol: self.symbol.clone(), + ticker: self.ticker.clone(), + synth: self.synth, + } + } +} + +pub struct ThorchainCoin { + pub asset: ThorchainAsset, + pub amount: U256, + pub decimals: i64, +} + +impl ThorchainCoin { + pub fn to_proto(&self) -> types::Coin { + types::Coin { + asset: Some(self.asset.to_proto()), + amount: self.amount.to_string(), + decimals: self.decimals, + } + } +} + +pub struct ThorchainSendMessage { + pub from_address: Data, + pub to_address: Data, + pub amount: Vec, +} + +impl CosmosMessage for ThorchainSendMessage { + fn to_proto(&self) -> SigningResult { + let proto_msg = types::MsgSend { + from_address: self.from_address.clone(), + to_address: self.to_address.clone(), + amount: self.amount.iter().map(build_coin).collect(), + }; + Ok(to_any(&proto_msg)) + } +} + +pub struct ThorchainDepositMessage { + pub coins: Vec, + pub memo: String, + pub signer: Data, +} + +impl CosmosMessage for ThorchainDepositMessage { + fn to_proto(&self) -> SigningResult { + let proto_msg = types::MsgDeposit { + coins: self.coins.iter().map(ThorchainCoin::to_proto).collect(), + memo: self.memo.clone(), + signer: self.signer.clone(), + }; + Ok(to_any(&proto_msg)) + } +} diff --git a/rust/tw_cosmos_sdk/src/transaction/message/wasm_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/wasm_message.rs new file mode 100644 index 00000000000..fc24afc2b1b --- /dev/null +++ b/rust/tw_cosmos_sdk/src/transaction/message/wasm_message.rs @@ -0,0 +1,105 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::CosmosAddress; +use crate::modules::serializer::protobuf_serializer::build_coin; +use crate::proto::cosmwasm; +use crate::transaction::message::{CosmosMessage, JsonMessage, ProtobufMessage}; +use crate::transaction::Coin; +use serde::Serialize; +use serde_json::{json, Value as Json}; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_memory::Data; +use tw_number::U256; +use tw_proto::to_any; + +const DEFAULT_JSON_MSG_TYPE: &str = "wasm/MsgExecuteContract"; + +#[derive(Clone, Serialize)] +#[serde(untagged)] +pub enum ExecuteMsg { + /// Either a regular string or a stringified JSON object. + String(String), + /// JSON object with a type. + Json(Json), +} + +impl ExecuteMsg { + /// Tries to convert [`ExecuteMsg::String`] to [`ExecuteMsg::Json`], otherwise returns the same object. + pub fn try_to_json(&self) -> ExecuteMsg { + if let ExecuteMsg::String(s) = self { + if let Ok(json) = serde_json::from_str(s) { + return ExecuteMsg::Json(json); + } + } + self.clone() + } + + pub fn json(payload: Payload) -> SigningResult { + let payload = serde_json::to_value(payload) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + Ok(ExecuteMsg::Json(payload)) + } + + pub fn to_bytes(&self) -> Data { + match self { + ExecuteMsg::String(ref s) => s.as_bytes().to_vec(), + ExecuteMsg::Json(ref j) => j.to_string().as_bytes().to_vec(), + } + } +} + +/// This method not only support token transfer, but also support all other types of contract call. +#[derive(Serialize)] +pub struct WasmExecuteContractMessage { + pub sender: Address, + pub contract: Address, + pub msg: ExecuteMsg, + pub coins: Vec, +} + +impl CosmosMessage for WasmExecuteContractMessage
{ + fn to_proto(&self) -> SigningResult { + let proto_msg = cosmwasm::wasm::v1::MsgExecuteContract { + sender: self.sender.to_string(), + contract: self.contract.to_string(), + msg: self.msg.to_bytes(), + funds: self.coins.iter().map(build_coin).collect(), + }; + Ok(to_any(&proto_msg)) + } + + fn to_json(&self) -> SigningResult { + // Don't use `message_to_json` because we need to try to convert [`ExecuteMsg::String`] to [`ExecuteMsg::Json`] if possible. + let value = json!({ + "coins": self.coins, + "contract": self.contract, + "msg": self.msg.try_to_json(), + "sender": self.sender, + }); + Ok(JsonMessage { + msg_type: DEFAULT_JSON_MSG_TYPE.to_string(), + value, + }) + } +} + +#[derive(Serialize)] +pub enum WasmExecutePayload { + #[serde(rename = "transfer")] + Transfer { + #[serde(serialize_with = "U256::as_decimal_str")] + amount: U256, + recipient: String, + }, + #[serde(rename = "send")] + Send { + #[serde(serialize_with = "U256::as_decimal_str")] + amount: U256, + contract: String, + msg: String, + }, +} diff --git a/rust/tw_cosmos_sdk/src/transaction/mod.rs b/rust/tw_cosmos_sdk/src/transaction/mod.rs new file mode 100644 index 00000000000..0d3d02812de --- /dev/null +++ b/rust/tw_cosmos_sdk/src/transaction/mod.rs @@ -0,0 +1,72 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::context::CosmosContext; +use crate::private_key::SignatureData; +use serde::Serialize; +use tw_number::U256; + +pub mod message; + +use message::CosmosMessageBox; + +/// At this moment, TW only supports the Direct signing mode. +#[derive(Clone, Copy)] +pub enum SignMode { + Direct, +} + +pub struct Fee
{ + pub amounts: Vec, + pub gas_limit: u64, + pub payer: Option
, + pub granter: Option
, +} + +#[derive(Clone, Serialize)] +pub struct Coin { + #[serde(serialize_with = "U256::as_decimal_str")] + pub amount: U256, + pub denom: String, +} + +pub struct SignerInfo { + pub public_key: PublicKey, + pub sequence: u64, + pub sign_mode: SignMode, +} + +pub struct TxBody { + pub messages: Vec, + pub memo: String, + pub timeout_height: u64, +} + +pub struct UnsignedTransaction { + pub signer: SignerInfo, + pub fee: Fee, + pub chain_id: String, + pub account_number: u64, + pub tx_body: TxBody, +} + +impl UnsignedTransaction { + pub fn into_signed(self, signature: SignatureData) -> SignedTransaction { + SignedTransaction { + signer: self.signer, + fee: self.fee, + tx_body: self.tx_body, + signature, + } + } +} + +pub struct SignedTransaction { + pub signer: SignerInfo, + pub fee: Fee, + pub tx_body: TxBody, + pub signature: SignatureData, +} diff --git a/rust/tw_cosmos_sdk/tests/compile.rs b/rust/tw_cosmos_sdk/tests/compile.rs new file mode 100644 index 00000000000..78dd632c4a8 --- /dev/null +++ b/rust/tw_cosmos_sdk/tests/compile.rs @@ -0,0 +1,101 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::borrow::Cow; +use tw_coin_entry::test_utils::test_context::TestCoinContext; +use tw_cosmos_sdk::context::StandardCosmosContext; +use tw_cosmos_sdk::test_utils::proto_utils::{make_amount, make_fee, make_message}; +use tw_cosmos_sdk::test_utils::sign_utils::{ + test_compile_json, test_compile_protobuf, TestCompileInput, +}; +use tw_encoding::hex::DecodeHex; +use tw_keypair::tw::PublicKeyType; +use tw_proto::Cosmos::Proto; +use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum; + +#[test] +fn test_compile_with_signatures() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let send_msg = Proto::mod_Message::Send { + from_address: "cosmos1mky69cn8ektwy0845vec9upsdphktxt03gkwlx".into(), + to_address: "cosmos18s0hdnsllgcclweu9aymw4ngktr2k0rkygdzdp".into(), + amounts: vec![make_amount("uatom", "400000")], + ..Proto::mod_Message::Send::default() + }; + + let input = Proto::SigningInput { + account_number: 546179, + chain_id: "cosmoshub-4".into(), + sequence: 0, + fee: Some(make_fee(200000, make_amount("uatom", "1000"))), + public_key: "02ecef5ce437a302c67f95468de4b31f36e911f467d7e6a52b41c1e13e1d563649" + .decode_hex() + .unwrap() + .into(), + messages: vec![make_message(MessageEnum::send_coins_message(send_msg))], + ..Proto::SigningInput::default() + }; + + test_compile_protobuf::(TestCompileInput { + coin: &coin, + input: input.clone(), + tx_preimage: "0a92010a8f010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e64126f0a2d636f736d6f73316d6b793639636e38656b74777930383435766563397570736470686b7478743033676b776c78122d636f736d6f733138733068646e736c6c6763636c7765753961796d77346e676b7472326b30726b7967647a64701a0f0a057561746f6d120634303030303012650a4e0a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a2102ecef5ce437a302c67f95468de4b31f36e911f467d7e6a52b41c1e13e1d56364912040a02080112130a0d0a057561746f6d12043130303010c09a0c1a0b636f736d6f736875622d342083ab21", + tx_prehash: "fa7990e1814c900efaedf1bdbedba22c22336675befe0ae39974130fc204f3de", + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CpIBCo8BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm8KLWNvc21vczFta3k2OWNuOGVrdHd5MDg0NXZlYzl1cHNkcGhrdHh0MDNna3dseBItY29zbW9zMThzMGhkbnNsbGdjY2x3ZXU5YXltdzRuZ2t0cjJrMHJreWdkemRwGg8KBXVhdG9tEgY0MDAwMDASZQpOCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAuzvXOQ3owLGf5VGjeSzHzbpEfRn1+alK0HB4T4dVjZJEgQKAggBEhMKDQoFdWF0b20SBDEwMDAQwJoMGkCvvVE6d29P30cO9/lnXyGunWMPxNY12NuqDcCnFkNM0H4CUQdl1Gc9+ogIJbro5nyzZzlv9rl2/GsZox/JXoCX"}"#, + signature: "afbd513a776f4fdf470ef7f9675f21ae9d630fc4d635d8dbaa0dc0a716434cd07e02510765d4673dfa880825bae8e67cb367396ff6b976fc6b19a31fc95e8097", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AuzvXOQ3owLGf5VGjeSzHzbpEfRn1+alK0HB4T4dVjZJ"},"signature":"r71ROndvT99HDvf5Z18hrp1jD8TWNdjbqg3ApxZDTNB+AlEHZdRnPfqICCW66OZ8s2c5b/a5dvxrGaMfyV6Alw=="}]"#, + }); + + test_compile_json::(TestCompileInput { + coin: &coin, + input: input.clone(), + tx_preimage: r#"{"account_number":"546179","chain_id":"cosmoshub-4","fee":{"amount":[{"amount":"1000","denom":"uatom"}],"gas":"200000"},"memo":"","msgs":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"400000","denom":"uatom"}],"from_address":"cosmos1mky69cn8ektwy0845vec9upsdphktxt03gkwlx","to_address":"cosmos18s0hdnsllgcclweu9aymw4ngktr2k0rkygdzdp"}}],"sequence":"0"}"#, + tx_prehash: "0a31f6cd50f1a5c514929ba68a977e222a7df2dc11e8470e93118cc3545e6b37", + tx: r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"1000","denom":"uatom"}],"gas":"200000"},"memo":"","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"400000","denom":"uatom"}],"from_address":"cosmos1mky69cn8ektwy0845vec9upsdphktxt03gkwlx","to_address":"cosmos18s0hdnsllgcclweu9aymw4ngktr2k0rkygdzdp"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AuzvXOQ3owLGf5VGjeSzHzbpEfRn1+alK0HB4T4dVjZJ"},"signature":"tTyOrburrHEHa14qiw78e9StoZyyGmoku98IxYrWCmtN8Qo5mTeKa0BKKDfgG4LmmNdwYcrXtqQQ7F4dL3c26g=="}]}}"#, + signature: "b53c8eadbbabac71076b5e2a8b0efc7bd4ada19cb21a6a24bbdf08c58ad60a6b4df10a3999378a6b404a2837e01b82e698d77061cad7b6a410ec5e1d2f7736ea", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AuzvXOQ3owLGf5VGjeSzHzbpEfRn1+alK0HB4T4dVjZJ"},"signature":"tTyOrburrHEHa14qiw78e9StoZyyGmoku98IxYrWCmtN8Qo5mTeKa0BKKDfgG4LmmNdwYcrXtqQQ7F4dL3c26g=="}]"#, + }); +} + +#[test] +fn test_compile_with_signatures_direct() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let body_bytes = "0a89010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412690a2d636f736d6f733168736b366a727979716a6668703564686335357463396a74636b796778306570683664643032122d636f736d6f73317a743530617a7570616e716c66616d356166687633686578777975746e756b656834633537331a090a046d756f6e120131".decode_hex().unwrap(); + let auth_info_bytes = "0a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a210257286ec3f37d33557bbbaa000b27744ac9023aa9967cae75a181d1ff91fa9dc512040a020801180812110a0b0a046d756f6e120332303010c09a0c".decode_hex().unwrap(); + let sign_direct = Proto::mod_Message::SignDirect { + body_bytes: Cow::from(body_bytes), + auth_info_bytes: Cow::from(auth_info_bytes), + }; + let input = Proto::SigningInput { + account_number: 1037, + chain_id: "gaia-13003".into(), + public_key: "0257286ec3f37d33557bbbaa000b27744ac9023aa9967cae75a181d1ff91fa9dc5" + .decode_hex() + .unwrap() + .into(), + messages: vec![make_message(MessageEnum::sign_direct_message(sign_direct))], + ..Proto::SigningInput::default() + }; + + // real-world tx: https://www.mintscan.io/cosmos/txs/817101F3D96314AD028733248B28BAFAD535024D7D2C8875D3FE31DC159F096B + // curl -H 'Content-Type: application/json' --data-binary '{"tx_bytes": "Cr4BCr...1yKOU=", "mode": "BROADCAST_MODE_BLOCK"}' https://api.cosmos.network/cosmos/tx/v1beta1/txs + // also similar TX: BCDAC36B605576C8182C2829C808B30A69CAD4959D5ED1E6FF9984ABF280D603 + test_compile_protobuf::(TestCompileInput { + coin: &coin, + input: input.clone(), + tx_preimage: "0a8c010a89010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412690a2d636f736d6f733168736b366a727979716a6668703564686335357463396a74636b796778306570683664643032122d636f736d6f73317a743530617a7570616e716c66616d356166687633686578777975746e756b656834633537331a090a046d756f6e12013112650a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a210257286ec3f37d33557bbbaa000b27744ac9023aa9967cae75a181d1ff91fa9dc512040a020801180812110a0b0a046d756f6e120332303010c09a0c1a0a676169612d3133303033208d08", + tx_prehash: "8a6e6f74625fd39707843360120874853cc0c1d730b087f3939f4b187c75b907", + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CowBCokBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmkKLWNvc21vczFoc2s2anJ5eXFqZmhwNWRoYzU1dGM5anRja3lneDBlcGg2ZGQwMhItY29zbW9zMXp0NTBhenVwYW5xbGZhbTVhZmh2M2hleHd5dXRudWtlaDRjNTczGgkKBG11b24SATESZQpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3FEgQKAggBGAgSEQoLCgRtdW9uEgMyMDAQwJoMGkD54fQAFlekIAnE62hZYl0uQelh/HLv0oQpCciY5Dn8H1SZFuTsrGdu41PH1Uxa4woptCELi/8Ov9yzdeEFAC9H"}"#, + signature: "f9e1f4001657a42009c4eb6859625d2e41e961fc72efd2842909c898e439fc1f549916e4ecac676ee353c7d54c5ae30a29b4210b8bff0ebfdcb375e105002f47", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"+eH0ABZXpCAJxOtoWWJdLkHpYfxy79KEKQnImOQ5/B9UmRbk7KxnbuNTx9VMWuMKKbQhC4v/Dr/cs3XhBQAvRw=="}]"#, + }); +} diff --git a/rust/tw_cosmos_sdk/tests/sign.rs b/rust/tw_cosmos_sdk/tests/sign.rs new file mode 100644 index 00000000000..3be2015cc9c --- /dev/null +++ b/rust/tw_cosmos_sdk/tests/sign.rs @@ -0,0 +1,361 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::borrow::Cow; +use tw_coin_entry::test_utils::test_context::TestCoinContext; +use tw_cosmos_sdk::context::StandardCosmosContext; +use tw_cosmos_sdk::modules::tx_builder::TxBuilder; +use tw_cosmos_sdk::test_utils::sign_utils::{ + test_sign_json, test_sign_json_error, test_sign_protobuf, test_sign_protobuf_error, + TestErrorInput, TestInput, +}; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_keypair::tw::PublicKeyType; +use tw_proto::Common::Proto::SigningError; +use tw_proto::Cosmos::Proto; +use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum; + +fn account_1037_private_key() -> Cow<'static, [u8]> { + "80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005" + .decode_hex() + .unwrap() + .into() +} + +fn account_546179_private_key() -> Cow<'static, [u8]> { + "8bbec3772ddb4df68f3186440380c301af116d1422001c1877d6f5e4dba8c8af" + .decode_hex() + .unwrap() + .into() +} + +fn account_1366160_private_key() -> Cow<'static, [u8]> { + "a498a9ee41af9bab5ef2a8be63d5c970135c3c109e70efc8c56c534e6636b433" + .decode_hex() + .unwrap() + .into() +} + +fn make_fee(gas: u64, amount: Proto::Amount<'_>) -> Proto::Fee<'_> { + Proto::Fee { + amounts: vec![amount], + gas, + } +} + +fn make_message(message_oneof: MessageEnum) -> Proto::Message { + Proto::Message { message_oneof } +} + +fn make_amount<'a>(denom: &'a str, amount: &'a str) -> Proto::Amount<'a> { + Proto::Amount { + denom: denom.into(), + amount: amount.into(), + } +} + +fn make_height(revision_number: u64, revision_height: u64) -> Proto::Height { + Proto::Height { + revision_number, + revision_height, + } +} + +#[test] +fn test_sign_coin_send() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let send_msg = Proto::mod_Message::Send { + from_address: "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02".into(), + to_address: "cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573".into(), + amounts: vec![make_amount("muon", "1")], + ..Proto::mod_Message::Send::default() + }; + let input = Proto::SigningInput { + account_number: 1037, + chain_id: "gaia-13003".into(), + sequence: 8, + fee: Some(make_fee(200000, make_amount("muon", "200"))), + private_key: account_1037_private_key(), + messages: vec![make_message(MessageEnum::send_coins_message(send_msg))], + ..Proto::SigningInput::default() + }; + + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CowBCokBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmkKLWNvc21vczFoc2s2anJ5eXFqZmhwNWRoYzU1dGM5anRja3lneDBlcGg2ZGQwMhItY29zbW9zMXp0NTBhenVwYW5xbGZhbTVhZmh2M2hleHd5dXRudWtlaDRjNTczGgkKBG11b24SATESZQpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3FEgQKAggBGAgSEQoLCgRtdW9uEgMyMDAQwJoMGkD54fQAFlekIAnE62hZYl0uQelh/HLv0oQpCciY5Dn8H1SZFuTsrGdu41PH1Uxa4woptCELi/8Ov9yzdeEFAC9H"}"#, + signature: "f9e1f4001657a42009c4eb6859625d2e41e961fc72efd2842909c898e439fc1f549916e4ecac676ee353c7d54c5ae30a29b4210b8bff0ebfdcb375e105002f47", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"+eH0ABZXpCAJxOtoWWJdLkHpYfxy79KEKQnImOQ5/B9UmRbk7KxnbuNTx9VMWuMKKbQhC4v/Dr/cs3XhBQAvRw=="}]"#, + }); + test_sign_json::(TestInput { + coin: &coin, + input, + tx: r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"200","denom":"muon"}],"gas":"200000"},"memo":"","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"1","denom":"muon"}],"from_address":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","to_address":"cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"/D74mdIGyIB3/sQvIboLTfS9P9EV/fYGrgHZE2/vNj9X6eM6e57G3atljNB+PABnRw3pTk51uXmhCFop8O/ZJg=="}]}}"#, + signature: "fc3ef899d206c88077fec42f21ba0b4df4bd3fd115fdf606ae01d9136fef363f57e9e33a7b9ec6ddab658cd07e3c0067470de94e4e75b979a1085a29f0efd926", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"/D74mdIGyIB3/sQvIboLTfS9P9EV/fYGrgHZE2/vNj9X6eM6e57G3atljNB+PABnRw3pTk51uXmhCFop8O/ZJg=="}]"#, + }); +} + +#[test] +fn test_sign_raw_json() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let raw_json_msg = Proto::mod_Message::RawJSON { + type_pb: "test".into(), + value: r#"{"test":"hello"}"#.into(), + }; + let input = Proto::SigningInput { + account_number: 1037, + chain_id: "gaia-13003".into(), + sequence: 8, + fee: Some(make_fee(200000, make_amount("muon", "200"))), + private_key: account_1037_private_key(), + messages: vec![make_message(MessageEnum::raw_json_message(raw_json_msg))], + ..Proto::SigningInput::default() + }; + + // `RawJSON` doesn't support Protobuf serialization and signing. + test_sign_protobuf_error::(TestErrorInput { + coin: &coin, + input: input.clone(), + error: SigningError::Error_not_supported, + }); + test_sign_json::(TestInput { + coin: &coin, + input, + tx: r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"200","denom":"muon"}],"gas":"200000"},"memo":"","msg":[{"type":"test","value":{"test":"hello"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"qhxxCOMiVhP7e7Mx+98HUZI0t5DNOFXwzIqNQz+fT6hDKR/ebW0uocsYnE5CiBNEalmBcs5gSIJegNkHhgyEmA=="}]}}"#, + signature: "aa1c7108e3225613fb7bb331fbdf07519234b790cd3855f0cc8a8d433f9f4fa843291fde6d6d2ea1cb189c4e428813446a598172ce6048825e80d907860c8498", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"qhxxCOMiVhP7e7Mx+98HUZI0t5DNOFXwzIqNQz+fT6hDKR/ebW0uocsYnE5CiBNEalmBcs5gSIJegNkHhgyEmA=="}]"#, + }); +} + +#[test] +fn test_sign_ibc_transfer() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let transfer_tokens = Proto::mod_Message::Transfer { + source_port: "transfer".into(), + source_channel: "channel-141".into(), + token: Some(make_amount("uatom", "100000")), + sender: "cosmos1mky69cn8ektwy0845vec9upsdphktxt03gkwlx".into(), + receiver: "osmo18s0hdnsllgcclweu9aymw4ngktr2k0rkvn7jmn".into(), + timeout_height: Some(make_height(1, 8800000)), + ..Proto::mod_Message::Transfer::default() + }; + let input = Proto::SigningInput { + account_number: 546179, + chain_id: "cosmoshub-4".into(), + sequence: 2, + fee: Some(make_fee(500000, make_amount("uatom", "12500"))), + private_key: account_546179_private_key(), + messages: vec![make_message(MessageEnum::transfer_tokens_message( + transfer_tokens, + ))], + ..Proto::SigningInput::default() + }; + + // real-world tx: https://www.mintscan.io/cosmos/txs/817101F3D96314AD028733248B28BAFAD535024D7D2C8875D3FE31DC159F096B + // curl -H 'Content-Type: application/json' --data-binary '{"tx_bytes": "Cr4BCr...1yKOU=", "mode": "BROADCAST_MODE_BLOCK"}' https://api.cosmos.network/cosmos/tx/v1beta1/txs + // also similar TX: BCDAC36B605576C8182C2829C808B30A69CAD4959D5ED1E6FF9984ABF280D603 + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"Cr4BCrsBCikvaWJjLmFwcGxpY2F0aW9ucy50cmFuc2Zlci52MS5Nc2dUcmFuc2ZlchKNAQoIdHJhbnNmZXISC2NoYW5uZWwtMTQxGg8KBXVhdG9tEgYxMDAwMDAiLWNvc21vczFta3k2OWNuOGVrdHd5MDg0NXZlYzl1cHNkcGhrdHh0MDNna3dseCorb3NtbzE4czBoZG5zbGxnY2Nsd2V1OWF5bXc0bmdrdHIyazBya3ZuN2ptbjIHCAEQgI6ZBBJoClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEC7O9c5DejAsZ/lUaN5LMfNukR9GfX5qUrQcHhPh1WNkkSBAoCCAEYAhIUCg4KBXVhdG9tEgUxMjUwMBCgwh4aQK0HIWdFMk+C6Gi1KG/vELe1ffcc1aEWUIqz2t/ZhwqNNHxUUSp27wteiugHEMVTEIOBhs84t2gIcT/nD/1yKOU="}"#, + signature: "ad07216745324f82e868b5286fef10b7b57df71cd5a116508ab3dadfd9870a8d347c54512a76ef0b5e8ae80710c55310838186cf38b76808713fe70ffd7228e5", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AuzvXOQ3owLGf5VGjeSzHzbpEfRn1+alK0HB4T4dVjZJ"},"signature":"rQchZ0UyT4LoaLUob+8Qt7V99xzVoRZQirPa39mHCo00fFRRKnbvC16K6AcQxVMQg4GGzzi3aAhxP+cP/XIo5Q=="}]"#, + }); + // `Transfer` doesn't support JSON serialization and signing. + test_sign_json_error::(TestErrorInput { + coin: &coin, + input, + error: SigningError::Error_not_supported, + }); +} + +#[test] +fn test_sign_direct() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let body_bytes = "0a89010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412690a2d636f736d6f733168736b366a727979716a6668703564686335357463396a74636b796778306570683664643032122d636f736d6f73317a743530617a7570616e716c66616d356166687633686578777975746e756b656834633537331a090a046d756f6e120131".decode_hex().unwrap(); + let auth_info_bytes = "0a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a210257286ec3f37d33557bbbaa000b27744ac9023aa9967cae75a181d1ff91fa9dc512040a020801180812110a0b0a046d756f6e120332303010c09a0c".decode_hex().unwrap(); + let sign_direct = Proto::mod_Message::SignDirect { + body_bytes: Cow::from(body_bytes), + auth_info_bytes: Cow::from(auth_info_bytes), + }; + let mut input = Proto::SigningInput { + account_number: 1037, + chain_id: "gaia-13003".into(), + private_key: account_1037_private_key(), + messages: vec![make_message(MessageEnum::sign_direct_message(sign_direct))], + ..Proto::SigningInput::default() + }; + + // real-world tx: https://www.mintscan.io/cosmos/txs/817101F3D96314AD028733248B28BAFAD535024D7D2C8875D3FE31DC159F096B + // curl -H 'Content-Type: application/json' --data-binary '{"tx_bytes": "Cr4BCr...1yKOU=", "mode": "BROADCAST_MODE_BLOCK"}' https://api.cosmos.network/cosmos/tx/v1beta1/txs + // also similar TX: BCDAC36B605576C8182C2829C808B30A69CAD4959D5ED1E6FF9984ABF280D603 + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CowBCokBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmkKLWNvc21vczFoc2s2anJ5eXFqZmhwNWRoYzU1dGM5anRja3lneDBlcGg2ZGQwMhItY29zbW9zMXp0NTBhenVwYW5xbGZhbTVhZmh2M2hleHd5dXRudWtlaDRjNTczGgkKBG11b24SATESZQpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3FEgQKAggBGAgSEQoLCgRtdW9uEgMyMDAQwJoMGkD54fQAFlekIAnE62hZYl0uQelh/HLv0oQpCciY5Dn8H1SZFuTsrGdu41PH1Uxa4woptCELi/8Ov9yzdeEFAC9H"}"#, + signature: "f9e1f4001657a42009c4eb6859625d2e41e961fc72efd2842909c898e439fc1f549916e4ecac676ee353c7d54c5ae30a29b4210b8bff0ebfdcb375e105002f47", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"+eH0ABZXpCAJxOtoWWJdLkHpYfxy79KEKQnImOQ5/B9UmRbk7KxnbuNTx9VMWuMKKbQhC4v/Dr/cs3XhBQAvRw=="}]"#, + }); + + // `Transfer` doesn't support JSON serialization and signing. + // Set the default fee to get `SigningError::Error_not_supported`. + input.fee = Some(Proto::Fee::default()); + test_sign_json_error::(TestErrorInput { + coin: &coin, + input, + error: SigningError::Error_not_supported, + }); +} + +#[test] +fn test_sign_direct_0a90010a() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + // Transaction body has the following content: + // https://github.com/trustwallet/wallet-core/blob/1382e3c8ac6d8e956e25c0475039f6c3988f9355/tests/chains/Cosmos/SignerTests.cpp#L327-L340 + let body_bytes = "0a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d120731323334353637".decode_hex().unwrap(); + let auth_info_bytes = "0a0a0a0012040a020801180112130a0d0a0575636f736d12043230303010c09a0c" + .decode_hex() + .unwrap(); + let sign_direct = Proto::mod_Message::SignDirect { + body_bytes: Cow::from(body_bytes), + auth_info_bytes: Cow::from(auth_info_bytes), + }; + let mut input = Proto::SigningInput { + account_number: 1, + chain_id: "cosmoshub-4".into(), + private_key: account_1037_private_key(), + messages: vec![make_message(MessageEnum::sign_direct_message(sign_direct))], + ..Proto::SigningInput::default() + }; + + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CpMBCpABChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnAKLWNvc21vczFwa3B0cmU3ZmRrbDZnZnJ6bGVzamp2aHhobGMzcjRnbW1rOHJzNhItY29zbW9zMXF5cHF4cHE5cWNyc3N6ZzJwdnhxNnJzMHpxZzN5eWM1bHp2N3h1GhAKBXVjb3NtEgcxMjM0NTY3EiEKCgoAEgQKAggBGAESEwoNCgV1Y29zbRIEMjAwMBDAmgwaQEgXmSAlm4M5bz+OX1GtvvZ3fBV2wrZrp4A/Imd55KM7ASivB/siYJegmYiOKzQ82uwoEmFalNnG2BrHHDwDR2Y="}"#, + signature: "48179920259b83396f3f8e5f51adbef6777c1576c2b66ba7803f226779e4a33b0128af07fb226097a099888e2b343cdaec2812615a94d9c6d81ac71c3c034766", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"SBeZICWbgzlvP45fUa2+9nd8FXbCtmungD8iZ3nkozsBKK8H+yJgl6CZiI4rNDza7CgSYVqU2cbYGsccPANHZg=="}]"#, + }); + + // `Transfer` doesn't support JSON serialization and signing. + // Set the default fee to get `SigningError::Error_not_supported`. + input.fee = Some(Proto::Fee::default()); + test_sign_json_error::(TestErrorInput { + coin: &coin, + input, + error: SigningError::Error_not_supported, + }); +} + +#[test] +fn test_sign_vote() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let vote_msg = Proto::mod_Message::MsgVote { + proposal_id: 77, + voter: "cosmos1mry47pkga5tdswtluy0m8teslpalkdq07pswu4".into(), + option: Proto::mod_Message::VoteOption::YES, + }; + let input = Proto::SigningInput { + account_number: 1366160, + chain_id: "cosmoshub-4".into(), + sequence: 0, + fee: Some(make_fee(97681, make_amount("uatom", "2418"))), + private_key: account_1366160_private_key(), + messages: vec![make_message(MessageEnum::msg_vote(vote_msg))], + ..Proto::SigningInput::default() + }; + + // Successfully broadcasted https://www.mintscan.io/cosmos/txs/2EFA054B842B1641B131137B13360F95164C6C1D51BB4A4AC6DE8F75F504AA4C + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"ClQKUgobL2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3RlEjMITRItY29zbW9zMW1yeTQ3cGtnYTV0ZHN3dGx1eTBtOHRlc2xwYWxrZHEwN3Bzd3U0GAESZQpOCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAsv9teRyiTMiKU5gzwiD1D30MeEInSnstEep5tVQRarlEgQKAggBEhMKDQoFdWF0b20SBDI0MTgQkfsFGkA+Nb3NULc38quGC1x+8ZXry4w9mMX3IA7wUjFboTv7kVOwPlleIc8UqIsjVvKTUFnUuW8dlGQzNR1KkvbvZ1NA"}"#, + signature: "3e35bdcd50b737f2ab860b5c7ef195ebcb8c3d98c5f7200ef052315ba13bfb9153b03e595e21cf14a88b2356f2935059d4b96f1d946433351d4a92f6ef675340", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"Asv9teRyiTMiKU5gzwiD1D30MeEInSnstEep5tVQRarl"},"signature":"PjW9zVC3N/KrhgtcfvGV68uMPZjF9yAO8FIxW6E7+5FTsD5ZXiHPFKiLI1byk1BZ1LlvHZRkMzUdSpL272dTQA=="}]"#, + }); + + // `MsgVote` doesn't support JSON serialization and signing. + test_sign_json_error::(TestErrorInput { + coin: &coin, + input, + error: SigningError::Error_not_supported, + }); +} + +#[test] +fn test_vote_payload() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let mut vote_msg = Proto::mod_Message::MsgVote { + proposal_id: 123, + voter: "cosmos1mry47pkga5tdswtluy0m8teslpalkdq07pswu4".into(), + option: Proto::mod_Message::VoteOption::_UNSPECIFIED, + }; + + let tests = [ + (Proto::mod_Message::VoteOption::ABSTAIN, "087b122d636f736d6f73316d72793437706b67613574647377746c7579306d387465736c70616c6b6471303770737775341802"), + (Proto::mod_Message::VoteOption::NO, "087b122d636f736d6f73316d72793437706b67613574647377746c7579306d387465736c70616c6b6471303770737775341803"), + (Proto::mod_Message::VoteOption::NO_WITH_VETO, "087b122d636f736d6f73316d72793437706b67613574647377746c7579306d387465736c70616c6b6471303770737775341804"), + (Proto::mod_Message::VoteOption::_UNSPECIFIED, "087b122d636f736d6f73316d72793437706b67613574647377746c7579306d387465736c70616c6b647130377073777534"), + ]; + + for (option, expected) in tests { + vote_msg.option = option; + let payload = + TxBuilder::::vote_msg_from_proto(&coin, &vote_msg).unwrap(); + let actual = payload.to_proto().unwrap(); + assert_eq!(actual.value.to_hex(), expected); + } +} + +#[test] +fn test_error_missing_message() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let input = Proto::SigningInput { + account_number: 1037, + chain_id: "gaia-13003".into(), + sequence: 8, + fee: Some(make_fee(200000, make_amount("muon", "200"))), + private_key: account_1037_private_key(), + messages: Vec::default(), + ..Proto::SigningInput::default() + }; + + test_sign_protobuf_error::(TestErrorInput { + coin: &coin, + input: input.clone(), + error: SigningError::Error_invalid_params, + }); + + test_sign_json_error::(TestErrorInput { + coin: &coin, + input, + error: SigningError::Error_invalid_params, + }); +} diff --git a/rust/tw_cosmos_sdk/tests/sign_staking.rs b/rust/tw_cosmos_sdk/tests/sign_staking.rs new file mode 100644 index 00000000000..deaf265a136 --- /dev/null +++ b/rust/tw_cosmos_sdk/tests/sign_staking.rs @@ -0,0 +1,418 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::borrow::Cow; +use tw_coin_entry::test_utils::test_context::TestCoinContext; +use tw_cosmos_sdk::context::StandardCosmosContext; +use tw_cosmos_sdk::test_utils::proto_utils::{make_amount, make_fee, make_message}; +use tw_cosmos_sdk::test_utils::sign_utils::{ + test_sign_json, test_sign_json_error, test_sign_protobuf, TestErrorInput, TestInput, +}; +use tw_encoding::hex::DecodeHex; +use tw_keypair::tw::PublicKeyType; +use tw_proto::Common::Proto::SigningError; +use tw_proto::Cosmos::Proto; +use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum; + +fn account_1037_private_key() -> Cow<'static, [u8]> { + "80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005" + .decode_hex() + .unwrap() + .into() +} + +fn account_1290826_private_key() -> Cow<'static, [u8]> { + "c7764249cdf77f8f1d840fa8af431579e5e41cf1af937e1e23afa22f3f4f0ccc" + .decode_hex() + .unwrap() + .into() +} + +fn account_1932898_private_key() -> Cow<'static, [u8]> { + "d142e036ceebe70c4e61e3909d6c16bab518edfeac8bdf91000463ce0b4a6156" + .decode_hex() + .unwrap() + .into() +} + +#[test] +fn test_staking_compounding_authz() { + use Proto::mod_Message::mod_AuthGrant::OneOfgrant_type as ProtoGrantType; + use Proto::mod_Message::mod_StakeAuthorization::OneOfvalidators as ProtoValidatorsType; + + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let allow_list = Proto::mod_Message::mod_StakeAuthorization::Validators { + address: vec!["cosmosvaloper1gjtvly9lel6zskvwtvlg5vhwpu9c9waw7sxzwx".into()], + }; + let stake_authorization = Proto::mod_Message::StakeAuthorization { + authorization_type: Proto::mod_Message::AuthorizationType::DELEGATE, + validators: ProtoValidatorsType::allow_list(allow_list), + ..Proto::mod_Message::StakeAuthorization::default() + }; + let auth_grant = Proto::mod_Message::AuthGrant { + granter: "cosmos13k0q0l7lg2kr32kvt7ly236ppldy8v9dzwh3gd".into(), + grantee: "cosmos1fs7lu28hx5m9akm7rp0c2422cn8r2f7gurujhf".into(), + grant_type: ProtoGrantType::grant_stake(stake_authorization), + expiration: 1692309600, + }; + let input = Proto::SigningInput { + account_number: 1290826, + chain_id: "cosmoshub-4".into(), + sequence: 5, + fee: Some(make_fee(96681, make_amount("uatom", "2418"))), + private_key: account_1290826_private_key(), + messages: vec![make_message(MessageEnum::auth_grant(auth_grant))], + ..Proto::SigningInput::default() + }; + + // Original test: https://github.com/trustwallet/wallet-core/blob/a60033f797e33628e557af7c66be539c8d78bc61/tests/chains/Cosmos/StakingTests.cpp#L18-L52 + // Please note the signature has been updated according to the serialization of the `StakeAuthorization` message. + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + // Previous: CvgBCvUBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQS0gEKLWNvc21vczEzazBxMGw3bGcya3IzMmt2dDdseTIzNnBwbGR5OHY5ZHp3aDNnZBItY29zbW9zMWZzN2x1MjhoeDVtOWFrbTdycDBjMjQyMmNuOHIyZjdndXJ1amhmGnIKaAoqL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuU3Rha2VBdXRob3JpemF0aW9uEjoSNgo0Y29zbW9zdmFsb3BlcjFnanR2bHk5bGVsNnpza3Z3dHZsZzV2aHdwdTljOXdhdzdzeHp3eCABEgYI4LD6pgYSZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA/fcQw1hCVUx904t+kCXTiiziaLIY8lyssu1ENfzaN1KEgQKAggBGAUSEwoNCgV1YXRvbRIEMjQxOBCp8wUaQIFyfuijGKf87Hz61ZqxasfLI1PZnNge4RDq/tRyB/tZI6p80iGRqHecoV6+84EQkc9GTlNRQOSlApRCsivT9XI= + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CvgBCvUBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQS0gEKLWNvc21vczEzazBxMGw3bGcya3IzMmt2dDdseTIzNnBwbGR5OHY5ZHp3aDNnZBItY29zbW9zMWZzN2x1MjhoeDVtOWFrbTdycDBjMjQyMmNuOHIyZjdndXJ1amhmGnIKaAoqL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuU3Rha2VBdXRob3JpemF0aW9uEjogARI2CjRjb3Ntb3N2YWxvcGVyMWdqdHZseTlsZWw2enNrdnd0dmxnNXZod3B1OWM5d2F3N3N4end4EgYI4LD6pgYSZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA/fcQw1hCVUx904t+kCXTiiziaLIY8lyssu1ENfzaN1KEgQKAggBGAUSEwoNCgV1YXRvbRIEMjQxOBCp8wUaQEAN1nIfDawlHnep2bNEm14w+g7tYybJJT3htcGVS6s9D7va3ed1OUEIk9LZoc3G//VenJ+KLw26SRVBaRukgVI="}"#, + // Previous: 81727ee8a318a7fcec7cfad59ab16ac7cb2353d99cd81ee110eafed47207fb5923aa7cd22191a8779ca15ebef3811091cf464e535140e4a5029442b22bd3f572 + signature: "400dd6721f0dac251e77a9d9b3449b5e30fa0eed6326c9253de1b5c1954bab3d0fbbdadde77539410893d2d9a1cdc6fff55e9c9f8a2f0dba491541691ba48152", + // Previous: gXJ+6KMYp/zsfPrVmrFqx8sjU9mc2B7hEOr+1HIH+1kjqnzSIZGod5yhXr7zgRCRz0ZOU1FA5KUClEKyK9P1cg== + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A/fcQw1hCVUx904t+kCXTiiziaLIY8lyssu1ENfzaN1K"},"signature":"QA3Wch8NrCUed6nZs0SbXjD6Du1jJsklPeG1wZVLqz0Pu9rd53U5QQiT0tmhzcb/9V6cn4ovDbpJFUFpG6SBUg=="}]"#, + }); + + // `AuthGrant` doesn't support JSON serialization and signing. + test_sign_json_error::(TestErrorInput { + coin: &coin, + input, + error: SigningError::Error_not_supported, + }); +} + +#[test] +fn test_staking_compounding_authz_f355e659() { + use Proto::mod_Message::mod_AuthGrant::OneOfgrant_type as ProtoGrantType; + use Proto::mod_Message::mod_StakeAuthorization::OneOfvalidators as ProtoValidatorsType; + + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let allow_list = Proto::mod_Message::mod_StakeAuthorization::Validators { + address: vec!["cosmosvaloper1gjtvly9lel6zskvwtvlg5vhwpu9c9waw7sxzwx".into()], + }; + let stake_authorization = Proto::mod_Message::StakeAuthorization { + authorization_type: Proto::mod_Message::AuthorizationType::DELEGATE, + validators: ProtoValidatorsType::allow_list(allow_list), + ..Proto::mod_Message::StakeAuthorization::default() + }; + let auth_grant = Proto::mod_Message::AuthGrant { + granter: "cosmos1wd0hdkzq68nmwzpprcugx82msj3l2y3wh8g5vv".into(), + grantee: "cosmos1fs7lu28hx5m9akm7rp0c2422cn8r2f7gurujhf".into(), + grant_type: ProtoGrantType::grant_stake(stake_authorization), + expiration: 1733011200, + }; + let input = Proto::SigningInput { + account_number: 1932898, + chain_id: "cosmoshub-4".into(), + sequence: 0, + fee: Some(make_fee(96681, make_amount("uatom", "2418"))), + private_key: account_1932898_private_key(), + messages: vec![make_message(MessageEnum::auth_grant(auth_grant))], + ..Proto::SigningInput::default() + }; + + // Successfully broadcasted https://www.mintscan.io/cosmos/tx/F355E659CBB8C0191213415E8F3EC6FD0AD1541F96FF192855147F6C0872A98B?height=17879293 + test_sign_protobuf::(TestInput { + coin: &coin, + input: input, + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CvgBCvUBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQS0gEKLWNvc21vczF3ZDBoZGt6cTY4bm13enBwcmN1Z3g4Mm1zajNsMnkzd2g4ZzV2dhItY29zbW9zMWZzN2x1MjhoeDVtOWFrbTdycDBjMjQyMmNuOHIyZjdndXJ1amhmGnIKaAoqL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuU3Rha2VBdXRob3JpemF0aW9uEjogARI2CjRjb3Ntb3N2YWxvcGVyMWdqdHZseTlsZWw2enNrdnd0dmxnNXZod3B1OWM5d2F3N3N4end4EgYIgM6uugYSZQpOCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA1yVadHrd1uQDhwasOzMBbg6zarM8PjyhRmDVY97HiX5EgQKAggBEhMKDQoFdWF0b20SBDI0MTgQqfMFGkCcDzlVwE+RUkWhjH0PiBrKDzqGgIczmj9fuMI0umrOFTqmKm/IQGol0eo4XZOIcahSYlqJ+1MOptAZM8Csqoay"}"#, + signature: "9c0f3955c04f915245a18c7d0f881aca0f3a868087339a3f5fb8c234ba6ace153aa62a6fc8406a25d1ea385d938871a852625a89fb530ea6d01933c0acaa86b2", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A1yVadHrd1uQDhwasOzMBbg6zarM8PjyhRmDVY97HiX5"},"signature":"nA85VcBPkVJFoYx9D4gayg86hoCHM5o/X7jCNLpqzhU6pipvyEBqJdHqOF2TiHGoUmJaiftTDqbQGTPArKqGsg=="}]"#, + }); +} + +#[test] +fn test_staking_remove_compounding_authz() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let auth_revoke = Proto::mod_Message::AuthRevoke { + granter: "cosmos13k0q0l7lg2kr32kvt7ly236ppldy8v9dzwh3gd".into(), + grantee: "cosmos1fs7lu28hx5m9akm7rp0c2422cn8r2f7gurujhf".into(), + msg_type_url: "/cosmos.staking.v1beta1.MsgDelegate".into(), + }; + let input = Proto::SigningInput { + account_number: 1290826, + chain_id: "cosmoshub-4".into(), + sequence: 4, + fee: Some(make_fee(87735, make_amount("uatom", "2194"))), + private_key: account_1290826_private_key(), + messages: vec![make_message(MessageEnum::auth_revoke(auth_revoke))], + ..Proto::SigningInput::default() + }; + + // Successfully broadcasted: https://www.mintscan.io/cosmos/txs/E3218F634BB6A1BE256545EBE38275D5B02D41E88F504A43F97CD9CD2B624D44 + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CqoBCqcBCh8vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnUmV2b2tlEoMBCi1jb3Ntb3MxM2swcTBsN2xnMmtyMzJrdnQ3bHkyMzZwcGxkeTh2OWR6d2gzZ2QSLWNvc21vczFmczdsdTI4aHg1bTlha203cnAwYzI0MjJjbjhyMmY3Z3VydWpoZhojL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGUSZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA/fcQw1hCVUx904t+kCXTiiziaLIY8lyssu1ENfzaN1KEgQKAggBGAQSEwoNCgV1YXRvbRIEMjE5NBC3rQUaQI7K+W7MMBoD6FbFZxRBqs9VTjErztjWTy57+fvrLaTCIZ+eBs7CuaKqfUZdSN8otjubSHVTQID3k9DpPAX0yDo="}"#, + signature: "8ecaf96ecc301a03e856c5671441aacf554e312bced8d64f2e7bf9fbeb2da4c2219f9e06cec2b9a2aa7d465d48df28b63b9b4875534080f793d0e93c05f4c83a", + // Previous: gXJ+6KMYp/zsfPrVmrFqx8sjU9mc2B7hEOr+1HIH+1kjqnzSIZGod5yhXr7zgRCRz0ZOU1FA5KUClEKyK9P1cg== + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A/fcQw1hCVUx904t+kCXTiiziaLIY8lyssu1ENfzaN1K"},"signature":"jsr5bswwGgPoVsVnFEGqz1VOMSvO2NZPLnv5++stpMIhn54GzsK5oqp9Rl1I3yi2O5tIdVNAgPeT0Ok8BfTIOg=="}]"#, + }); + + // `AuthRevoke` doesn't support JSON serialization and signing. + test_sign_json_error::(TestErrorInput { + coin: &coin, + input, + error: SigningError::Error_not_supported, + }); +} + +#[test] +fn test_staking_delegate() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let delegate = Proto::mod_Message::Delegate { + delegator_address: "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02".into(), + validator_address: "cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp".into(), + amount: Some(make_amount("muon", "10")), + ..Proto::mod_Message::Delegate::default() + }; + let input = Proto::SigningInput { + account_number: 1037, + chain_id: "gaia-13003".into(), + sequence: 7, + fee: Some(make_fee(101721, make_amount("muon", "1018"))), + private_key: account_1037_private_key(), + messages: vec![make_message(MessageEnum::stake_message(delegate))], + mode: Proto::BroadcastMode::ASYNC, + ..Proto::SigningInput::default() + }; + + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_ASYNC","tx_bytes":"CpsBCpgBCiMvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dEZWxlZ2F0ZRJxCi1jb3Ntb3MxaHNrNmpyeXlxamZocDVkaGM1NXRjOWp0Y2t5Z3gwZXBoNmRkMDISNGNvc21vc3ZhbG9wZXIxemt1cHI4M2hyemtuM3VwNWVsa3R6Y3EzdHVmdDhueHNtd2RxZ3AaCgoEbXVvbhICMTASZgpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3FEgQKAggBGAcSEgoMCgRtdW9uEgQxMDE4ENmaBhpA8O9Jm/kL6Za2I3poDs5vpMowYJgNvYCJBRU/vxAjs0lNZYsq40qpTbwOTbORjJA5UjQ6auc40v6uCFT4q4z+uA=="}"#, + signature: "f0ef499bf90be996b6237a680ece6fa4ca3060980dbd808905153fbf1023b3494d658b2ae34aa94dbc0e4db3918c903952343a6ae738d2feae0854f8ab8cfeb8", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"8O9Jm/kL6Za2I3poDs5vpMowYJgNvYCJBRU/vxAjs0lNZYsq40qpTbwOTbORjJA5UjQ6auc40v6uCFT4q4z+uA=="}]"#, + }); + + test_sign_json::(TestInput { + coin: &coin, + input, + tx: r#"{"mode":"async","tx":{"fee":{"amount":[{"amount":"1018","denom":"muon"}],"gas":"101721"},"memo":"","msg":[{"type":"cosmos-sdk/MsgDelegate","value":{"amount":{"amount":"10","denom":"muon"},"delegator_address":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","validator_address":"cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"wIvfbCsLRCjzeXXoXTKfHLGXRbAAmUp0O134HVfVc6pfdVNJvvzISMHRUHgYcjsSiFlLyR32heia/yLgMDtIYQ=="}]}}"#, + signature: "c08bdf6c2b0b4428f37975e85d329f1cb19745b000994a743b5df81d57d573aa5f755349befcc848c1d1507818723b1288594bc91df685e89aff22e0303b4861", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"wIvfbCsLRCjzeXXoXTKfHLGXRbAAmUp0O134HVfVc6pfdVNJvvzISMHRUHgYcjsSiFlLyR32heia/yLgMDtIYQ=="}]"#, + }); +} + +#[test] +fn test_staking_delegate_custom_msg_type() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let delegate = Proto::mod_Message::Delegate { + delegator_address: "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02".into(), + validator_address: "cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp".into(), + amount: Some(make_amount("muon", "10")), + type_prefix: "unreal/MsgDelegate".into(), + }; + let input = Proto::SigningInput { + account_number: 1037, + chain_id: "gaia-13003".into(), + sequence: 7, + fee: Some(make_fee(101721, make_amount("muon", "1018"))), + private_key: account_1037_private_key(), + messages: vec![make_message(MessageEnum::stake_message(delegate))], + mode: Proto::BroadcastMode::ASYNC, + ..Proto::SigningInput::default() + }; + + // This transaction hasn't been broadcasted. + // Check if the custom type_prefix doesn't affect Protobuf serialization. + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_ASYNC","tx_bytes":"CpsBCpgBCiMvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dEZWxlZ2F0ZRJxCi1jb3Ntb3MxaHNrNmpyeXlxamZocDVkaGM1NXRjOWp0Y2t5Z3gwZXBoNmRkMDISNGNvc21vc3ZhbG9wZXIxemt1cHI4M2hyemtuM3VwNWVsa3R6Y3EzdHVmdDhueHNtd2RxZ3AaCgoEbXVvbhICMTASZgpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3FEgQKAggBGAcSEgoMCgRtdW9uEgQxMDE4ENmaBhpA8O9Jm/kL6Za2I3poDs5vpMowYJgNvYCJBRU/vxAjs0lNZYsq40qpTbwOTbORjJA5UjQ6auc40v6uCFT4q4z+uA=="}"#, + signature: "f0ef499bf90be996b6237a680ece6fa4ca3060980dbd808905153fbf1023b3494d658b2ae34aa94dbc0e4db3918c903952343a6ae738d2feae0854f8ab8cfeb8", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"8O9Jm/kL6Za2I3poDs5vpMowYJgNvYCJBRU/vxAjs0lNZYsq40qpTbwOTbORjJA5UjQ6auc40v6uCFT4q4z+uA=="}]"#, + }); + + // This transaction hasn't been broadcasted. + test_sign_json::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"async","tx":{"fee":{"amount":[{"amount":"1018","denom":"muon"}],"gas":"101721"},"memo":"","msg":[{"type":"unreal/MsgDelegate","value":{"amount":{"amount":"10","denom":"muon"},"delegator_address":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","validator_address":"cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"D9OufIm3RGkoGU/OO7eNHe17Yg4q0OBU0pHnqtULWXIm3J1eSUxsVI6OCbGAMYEqHUB9i5b1YGrueaDYFOH9xQ=="}]}}"#, + signature: "0fd3ae7c89b7446928194fce3bb78d1ded7b620e2ad0e054d291e7aad50b597226dc9d5e494c6c548e8e09b18031812a1d407d8b96f5606aee79a0d814e1fdc5", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"D9OufIm3RGkoGU/OO7eNHe17Yg4q0OBU0pHnqtULWXIm3J1eSUxsVI6OCbGAMYEqHUB9i5b1YGrueaDYFOH9xQ=="}]"#, + }); +} + +#[test] +fn test_staking_undelegate() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let undelegate = Proto::mod_Message::Undelegate { + delegator_address: "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02".into(), + validator_address: "cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp".into(), + amount: Some(make_amount("muon", "10")), + ..Proto::mod_Message::Undelegate::default() + }; + let input = Proto::SigningInput { + account_number: 1037, + chain_id: "gaia-13003".into(), + sequence: 7, + fee: Some(make_fee(101721, make_amount("muon", "1018"))), + private_key: account_1037_private_key(), + messages: vec![make_message(MessageEnum::unstake_message(undelegate))], + mode: Proto::BroadcastMode::SYNC, + ..Proto::SigningInput::default() + }; + + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_SYNC","tx_bytes":"Cp0BCpoBCiUvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dVbmRlbGVnYXRlEnEKLWNvc21vczFoc2s2anJ5eXFqZmhwNWRoYzU1dGM5anRja3lneDBlcGg2ZGQwMhI0Y29zbW9zdmFsb3BlcjF6a3VwcjgzaHJ6a24zdXA1ZWxrdHpjcTN0dWZ0OG54c213ZHFncBoKCgRtdW9uEgIxMBJmClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiECVyhuw/N9M1V7u6oACyd0SskCOqmWfK51oYHR/5H6ncUSBAoCCAEYBxISCgwKBG11b24SBDEwMTgQ2ZoGGkBhlxHFnjBERxLtjLbMCKXcrDctaSZ9djtWCa3ely1bpV6m+6aAFjpr8aEZH+q2AtjJSEdgpQRJxP+9/gQsRTnZ"}"#, + signature: "619711c59e30444712ed8cb6cc08a5dcac372d69267d763b5609adde972d5ba55ea6fba680163a6bf1a1191feab602d8c9484760a50449c4ffbdfe042c4539d9", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"YZcRxZ4wREcS7Yy2zAil3Kw3LWkmfXY7Vgmt3pctW6VepvumgBY6a/GhGR/qtgLYyUhHYKUEScT/vf4ELEU52Q=="}]"#, + }); + + test_sign_json::(TestInput { + coin: &coin, + input, + tx: r#"{"mode":"sync","tx":{"fee":{"amount":[{"amount":"1018","denom":"muon"}],"gas":"101721"},"memo":"","msg":[{"type":"cosmos-sdk/MsgUndelegate","value":{"amount":{"amount":"10","denom":"muon"},"delegator_address":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","validator_address":"cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"j4WpUVohGIHa6/s0bCvuyjq1wtQGqbOtQCz92qPQjisTN44Tz++Ozx1lAP6F0M4+eTA03XerqQ8hZCeAfL/3nw=="}]}}"#, + signature: "8f85a9515a211881daebfb346c2beeca3ab5c2d406a9b3ad402cfddaa3d08e2b13378e13cfef8ecf1d6500fe85d0ce3e793034dd77aba90f216427807cbff79f", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"j4WpUVohGIHa6/s0bCvuyjq1wtQGqbOtQCz92qPQjisTN44Tz++Ozx1lAP6F0M4+eTA03XerqQ8hZCeAfL/3nw=="}]"#, + }); +} + +#[test] +fn test_staking_restake() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let redelegate = Proto::mod_Message::BeginRedelegate { + delegator_address: "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02".into(), + validator_src_address: "cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp".into(), + validator_dst_address: "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02".into(), + amount: Some(make_amount("muon", "10")), + ..Proto::mod_Message::BeginRedelegate::default() + }; + let input = Proto::SigningInput { + account_number: 1037, + chain_id: "gaia-13003".into(), + sequence: 7, + fee: Some(make_fee(101721, make_amount("muon", "1018"))), + private_key: account_1037_private_key(), + messages: vec![make_message(MessageEnum::restake_message(redelegate))], + ..Proto::SigningInput::default() + }; + + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CtIBCs8BCiovY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dCZWdpblJlZGVsZWdhdGUSoAEKLWNvc21vczFoc2s2anJ5eXFqZmhwNWRoYzU1dGM5anRja3lneDBlcGg2ZGQwMhI0Y29zbW9zdmFsb3BlcjF6a3VwcjgzaHJ6a24zdXA1ZWxrdHpjcTN0dWZ0OG54c213ZHFncBotY29zbW9zMWhzazZqcnl5cWpmaHA1ZGhjNTV0YzlqdGNreWd4MGVwaDZkZDAyIgoKBG11b24SAjEwEmYKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQJXKG7D830zVXu7qgALJ3RKyQI6qZZ8rnWhgdH/kfqdxRIECgIIARgHEhIKDAoEbXVvbhIEMTAxOBDZmgYaQJ52qO5xdtBkNUeFeWrnqUXkngyHFKCXnOPPClyVI0HrULdp5jbwGra2RujEOn4BrbFCb3JFnpc2o1iuLXbKQxg="}"#, + signature: "9e76a8ee7176d064354785796ae7a945e49e0c8714a0979ce3cf0a5c952341eb50b769e636f01ab6b646e8c43a7e01adb1426f72459e9736a358ae2d76ca4318", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"nnao7nF20GQ1R4V5auepReSeDIcUoJec488KXJUjQetQt2nmNvAatrZG6MQ6fgGtsUJvckWelzajWK4tdspDGA=="}]"#, + }); + + test_sign_json::(TestInput { + coin: &coin, + input, + tx: r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"1018","denom":"muon"}],"gas":"101721"},"memo":"","msg":[{"type":"cosmos-sdk/MsgBeginRedelegate","value":{"amount":{"amount":"10","denom":"muon"},"delegator_address":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","validator_dst_address":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","validator_src_address":"cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"5k03Yb0loovvzagMCg4gjQJP2woriZVRcOZaXF1FSros6B1X4B8MEm3lpZwrWBJMEJVgyYA9ZaF6FLVI3WxQ2w=="}]}}"#, + signature: "e64d3761bd25a28befcda80c0a0e208d024fdb0a2b89955170e65a5c5d454aba2ce81d57e01f0c126de5a59c2b58124c109560c9803d65a17a14b548dd6c50db", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"5k03Yb0loovvzagMCg4gjQJP2woriZVRcOZaXF1FSros6B1X4B8MEm3lpZwrWBJMEJVgyYA9ZaF6FLVI3WxQ2w=="}]"#, + }); +} + +#[test] +fn test_staking_withdraw_rewards() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let withdraw = Proto::mod_Message::WithdrawDelegationReward { + delegator_address: "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02".into(), + validator_address: "cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp".into(), + ..Proto::mod_Message::WithdrawDelegationReward::default() + }; + let input = Proto::SigningInput { + account_number: 1037, + chain_id: "gaia-13003".into(), + sequence: 7, + fee: Some(make_fee(101721, make_amount("muon", "1018"))), + private_key: account_1037_private_key(), + messages: vec![make_message(MessageEnum::withdraw_stake_reward_message( + withdraw, + ))], + ..Proto::SigningInput::default() + }; + + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CqMBCqABCjcvY29zbW9zLmRpc3RyaWJ1dGlvbi52MWJldGExLk1zZ1dpdGhkcmF3RGVsZWdhdG9yUmV3YXJkEmUKLWNvc21vczFoc2s2anJ5eXFqZmhwNWRoYzU1dGM5anRja3lneDBlcGg2ZGQwMhI0Y29zbW9zdmFsb3BlcjF6a3VwcjgzaHJ6a24zdXA1ZWxrdHpjcTN0dWZ0OG54c213ZHFncBJmClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiECVyhuw/N9M1V7u6oACyd0SskCOqmWfK51oYHR/5H6ncUSBAoCCAEYBxISCgwKBG11b24SBDEwMTgQ2ZoGGkBW1Cd+0pNfMPEVXQtqG1VIijDjZP2UOiDlvUF478axnxlF8PaOAsY0S5OdUE3Wz7+nu8YVmrLZQS/8mlqLaK05"}"#, + signature: "56d4277ed2935f30f1155d0b6a1b55488a30e364fd943a20e5bd4178efc6b19f1945f0f68e02c6344b939d504dd6cfbfa7bbc6159ab2d9412ffc9a5a8b68ad39", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"VtQnftKTXzDxFV0LahtVSIow42T9lDog5b1BeO/GsZ8ZRfD2jgLGNEuTnVBN1s+/p7vGFZqy2UEv/Jpai2itOQ=="}]"#, + }); + + test_sign_json::(TestInput { + coin: &coin, + input, + tx: r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"1018","denom":"muon"}],"gas":"101721"},"memo":"","msg":[{"type":"cosmos-sdk/MsgWithdrawDelegationReward","value":{"delegator_address":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","validator_address":"cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"VG8NZzVvavlM+1qyK5dOSZwzEj8sLCkvTw5kh44Oco9GQxBf13FVC+s/I3HwiICqo4+o8jNMEDp3nx2C0tuY1g=="}]}}"#, + signature: "546f0d67356f6af94cfb5ab22b974e499c33123f2c2c292f4f0e64878e0e728f4643105fd771550beb3f2371f08880aaa38fa8f2334c103a779f1d82d2db98d6", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"VG8NZzVvavlM+1qyK5dOSZwzEj8sLCkvTw5kh44Oco9GQxBf13FVC+s/I3HwiICqo4+o8jNMEDp3nx2C0tuY1g=="}]"#, + }); +} + +#[test] +fn test_staking_set_withdraw_address() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let set_address = Proto::mod_Message::SetWithdrawAddress { + delegator_address: "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02".into(), + withdraw_address: "cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp".into(), + ..Proto::mod_Message::SetWithdrawAddress::default() + }; + let input = Proto::SigningInput { + account_number: 1037, + chain_id: "gaia-13003".into(), + sequence: 7, + fee: Some(make_fee(101721, make_amount("muon", "1018"))), + private_key: account_1037_private_key(), + messages: vec![make_message(MessageEnum::set_withdraw_address_message( + set_address, + ))], + ..Proto::SigningInput::default() + }; + + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"Cp4BCpsBCjIvY29zbW9zLmRpc3RyaWJ1dGlvbi52MWJldGExLk1zZ1NldFdpdGhkcmF3QWRkcmVzcxJlCi1jb3Ntb3MxaHNrNmpyeXlxamZocDVkaGM1NXRjOWp0Y2t5Z3gwZXBoNmRkMDISNGNvc21vc3ZhbG9wZXIxemt1cHI4M2hyemtuM3VwNWVsa3R6Y3EzdHVmdDhueHNtd2RxZ3ASZgpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3FEgQKAggBGAcSEgoMCgRtdW9uEgQxMDE4ENmaBhpAkm2TJLw4FcIwN5bkqVaGbmAgkTSHeYD8sUkIyJHLa89cPvThkFO/lKlxBMl2UAMs06hL6cYcl4Px+B6rpFdBpA=="}"#, + signature: "926d9324bc3815c2303796e4a956866e60209134877980fcb14908c891cb6bcf5c3ef4e19053bf94a97104c97650032cd3a84be9c61c9783f1f81eaba45741a4", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"km2TJLw4FcIwN5bkqVaGbmAgkTSHeYD8sUkIyJHLa89cPvThkFO/lKlxBMl2UAMs06hL6cYcl4Px+B6rpFdBpA=="}]"#, + }); + + test_sign_json::(TestInput { + coin: &coin, + input, + tx: r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"1018","denom":"muon"}],"gas":"101721"},"memo":"","msg":[{"type":"cosmos-sdk/MsgSetWithdrawAddress","value":{"delegator_address":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","withdraw_address":"cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"Is+87DPQbtQmIyZASdEdb7hlZhA9ViGiOxREAi6xqs46B5ChxGtIwCGGiWFtr5f5mucsNYmWYgXeRbVxlPutog=="}]}}"#, + signature: "22cfbcec33d06ed42623264049d11d6fb86566103d5621a23b1444022eb1aace3a0790a1c46b48c0218689616daf97f99ae72c3589966205de45b57194fbada2", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"Is+87DPQbtQmIyZASdEdb7hlZhA9ViGiOxREAi6xqs46B5ChxGtIwCGGiWFtr5f5mucsNYmWYgXeRbVxlPutog=="}]"#, + }); +} diff --git a/rust/tw_cosmos_sdk/tests/sign_stride.rs b/rust/tw_cosmos_sdk/tests/sign_stride.rs new file mode 100644 index 00000000000..a38706a35c4 --- /dev/null +++ b/rust/tw_cosmos_sdk/tests/sign_stride.rs @@ -0,0 +1,104 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::borrow::Cow; +use tw_coin_entry::test_utils::test_context::TestCoinContext; +use tw_cosmos_sdk::context::StandardCosmosContext; +use tw_cosmos_sdk::test_utils::proto_utils::{make_amount, make_fee, make_message}; +use tw_cosmos_sdk::test_utils::sign_utils::{ + test_sign_json_error, test_sign_protobuf, TestErrorInput, TestInput, +}; +use tw_encoding::hex::DecodeHex; +use tw_keypair::tw::PublicKeyType; +use tw_proto::Common::Proto::SigningError; +use tw_proto::Cosmos::Proto; +use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum; + +fn account_136412_private_key() -> Cow<'static, [u8]> { + "a498a9ee41af9bab5ef2a8be63d5c970135c3c109e70efc8c56c534e6636b433" + .decode_hex() + .unwrap() + .into() +} + +#[test] +fn test_stride_liquid_staking_stake() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("stride"); + + let stake = Proto::mod_Message::MsgStrideLiquidStakingStake { + creator: "stride1mry47pkga5tdswtluy0m8teslpalkdq0a2sjge".into(), + amount: "100000".into(), + host_denom: "uatom".into(), + }; + let input = Proto::SigningInput { + account_number: 136412, + chain_id: "stride-1".into(), + sequence: 0, + fee: Some(make_fee(500000, make_amount("ustrd", "0"))), + private_key: account_136412_private_key(), + messages: vec![make_message(MessageEnum::msg_stride_liquid_staking_stake( + stake, + ))], + ..Proto::SigningInput::default() + }; + + // Successfully broadcasted: https://www.mintscan.io/stride/txs/48E51A2571D99453C4581B30CECA2A1156C0D1EBACCD3619729B5A35AD67CC94?height=3485243 + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CmMKYQofL3N0cmlkZS5zdGFrZWliYy5Nc2dMaXF1aWRTdGFrZRI+Ci1zdHJpZGUxbXJ5NDdwa2dhNXRkc3d0bHV5MG04dGVzbHBhbGtkcTBhMnNqZ2USBjEwMDAwMBoFdWF0b20SYgpOCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAsv9teRyiTMiKU5gzwiD1D30MeEInSnstEep5tVQRarlEgQKAggBEhAKCgoFdXN0cmQSATAQoMIeGkCDaZHV5/Z3CAQC5DXkaHmF6OKUiS5XKDsl3ZnBaaVuJjlSWV2vA7MPwGbC17P6jbVJt58ZLcxIWFt76UO3y1ix"}"#, + signature: "836991d5e7f677080402e435e4687985e8e294892e57283b25dd99c169a56e263952595daf03b30fc066c2d7b3fa8db549b79f192dcc48585b7be943b7cb58b1", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"Asv9teRyiTMiKU5gzwiD1D30MeEInSnstEep5tVQRarl"},"signature":"g2mR1ef2dwgEAuQ15Gh5hejilIkuVyg7Jd2ZwWmlbiY5UlldrwOzD8Bmwtez+o21SbefGS3MSFhbe+lDt8tYsQ=="}]"#, + }); + + test_sign_json_error::(TestErrorInput { + coin: &coin, + input, + error: SigningError::Error_not_supported, + }); +} + +#[test] +fn test_stride_liquid_staking_redeem() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("stride"); + + let redeem = Proto::mod_Message::MsgStrideLiquidStakingRedeem { + creator: "stride1mry47pkga5tdswtluy0m8teslpalkdq0a2sjge".into(), + amount: "40000".into(), + receiver: "cosmos1mry47pkga5tdswtluy0m8teslpalkdq07pswu4".into(), + host_zone: "cosmoshub-4".into(), + }; + let input = Proto::SigningInput { + account_number: 136412, + chain_id: "stride-1".into(), + sequence: 1, + fee: Some(make_fee(1000000, make_amount("ustrd", "0"))), + private_key: account_136412_private_key(), + messages: vec![make_message(MessageEnum::msg_stride_liquid_staking_redeem( + redeem, + ))], + ..Proto::SigningInput::default() + }; + + // Successfully broadcasted: https://www.mintscan.io/stride/txs/B3D3A92A2FFB92A480A4B547A4303E6932204972A965D687DB4FB6B4E16B2C42?height=3485343 + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CpgBCpUBCh8vc3RyaWRlLnN0YWtlaWJjLk1zZ1JlZGVlbVN0YWtlEnIKLXN0cmlkZTFtcnk0N3BrZ2E1dGRzd3RsdXkwbTh0ZXNscGFsa2RxMGEyc2pnZRIFNDAwMDAaC2Nvc21vc2h1Yi00Ii1jb3Ntb3MxbXJ5NDdwa2dhNXRkc3d0bHV5MG04dGVzbHBhbGtkcTA3cHN3dTQSZApQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAsv9teRyiTMiKU5gzwiD1D30MeEInSnstEep5tVQRarlEgQKAggBGAESEAoKCgV1c3RyZBIBMBDAhD0aQKf84TYoPqwnXw22r0dok2fYplUFu003TlIfpoT+wqTZF1lHPC+RTAoJob6x50CnfvGlgJFBEQYPD+Ccv659VVA="}"#, + signature: "a7fce136283eac275f0db6af47689367d8a65505bb4d374e521fa684fec2a4d91759473c2f914c0a09a1beb1e740a77ef1a580914111060f0fe09cbfae7d5550", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"Asv9teRyiTMiKU5gzwiD1D30MeEInSnstEep5tVQRarl"},"signature":"p/zhNig+rCdfDbavR2iTZ9imVQW7TTdOUh+mhP7CpNkXWUc8L5FMCgmhvrHnQKd+8aWAkUERBg8P4Jy/rn1VUA=="}]"#, + }); + + test_sign_json_error::(TestErrorInput { + coin: &coin, + input, + error: SigningError::Error_not_supported, + }); +} diff --git a/rust/tw_cosmos_sdk/tests/sign_terra_wasm.rs b/rust/tw_cosmos_sdk/tests/sign_terra_wasm.rs new file mode 100644 index 00000000000..f3ac72bbf8f --- /dev/null +++ b/rust/tw_cosmos_sdk/tests/sign_terra_wasm.rs @@ -0,0 +1,272 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde_json::json; +use std::borrow::Cow; +use tw_coin_entry::test_utils::test_context::TestCoinContext; +use tw_cosmos_sdk::context::StandardCosmosContext; +use tw_cosmos_sdk::modules::tx_builder::TxBuilder; +use tw_cosmos_sdk::test_utils::proto_utils::{make_amount, make_fee, make_message}; +use tw_cosmos_sdk::test_utils::sign_utils::{test_sign_json, test_sign_protobuf, TestInput}; +use tw_encoding::base64; +use tw_encoding::hex::DecodeHex; +use tw_keypair::tw::PublicKeyType; +use tw_number::U256; +use tw_proto::Cosmos::Proto; +use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum; + +fn account_3407705_private_key() -> Cow<'static, [u8]> { + "cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616" + .decode_hex() + .unwrap() + .into() +} + +#[test] +fn test_terra_wasm_transfer_protobuf_9ff3f0() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("terra"); + + let transfer = Proto::mod_Message::WasmTerraExecuteContractTransfer { + sender_address: "terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf".into(), + // ANC + contract_address: "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76".into(), + amount: U256::encode_be_compact(250000), + recipient_address: "terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp".into(), + }; + + let input = Proto::SigningInput { + account_number: 3407705, + chain_id: "columbus-5".into(), + sequence: 3, + fee: Some(make_fee(200000, make_amount("uluna", "3000"))), + private_key: account_3407705_private_key(), + messages: vec![make_message( + MessageEnum::wasm_terra_execute_contract_transfer_message(transfer), + )], + ..Proto::SigningInput::default() + }; + + // https://finder.terra.money/mainnet/tx/9FF3F0A16879254C22EB90D8B4D6195467FE5014381FD36BD3C23CA6698FE94B + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CucBCuQBCiYvdGVycmEud2FzbS52MWJldGExLk1zZ0V4ZWN1dGVDb250cmFjdBK5AQosdGVycmExOHd1a3A4NGRxMjI3d3U0bWdoMGptNm45bmxuajZyczgycHA5d2YSLHRlcnJhMTR6NTZsMGZwMmxzZjg2enkzaHR5Mno0N2V6a2hudGh0cjl5cTc2Glt7InRyYW5zZmVyIjp7ImFtb3VudCI6IjI1MDAwMCIsInJlY2lwaWVudCI6InRlcnJhMWpsZ2FxeTludm4yaGY1dDJzcmE5eWN6OHM3N3duZjlsMGttZ2NwIn19EmcKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQNwZjrHsPmJKW/rXOWfukpQ1+lOHOJW3/IlFFnKLNmsABIECgIIARgDEhMKDQoFdWx1bmESBDMwMDAQwJoMGkAaprIEMLPH2HmFdwFGoaipb2GIyhXt6ombz+WMnG2mORBI6gFt0M+IymYgzZz6w1SW52R922yafDnn7yXfutRw"}"#, + signature: "1aa6b20430b3c7d87985770146a1a8a96f6188ca15edea899bcfe58c9c6da6391048ea016dd0cf88ca6620cd9cfac35496e7647ddb6c9a7c39e7ef25dfbad470", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"GqayBDCzx9h5hXcBRqGoqW9hiMoV7eqJm8/ljJxtpjkQSOoBbdDPiMpmIM2c+sNUludkfdtsmnw55+8l37rUcA=="}]"#, + }); +} + +#[test] +fn test_terra_wasm_transfer_json_078e90() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("terra"); + + let transfer = Proto::mod_Message::WasmTerraExecuteContractTransfer { + sender_address: "terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf".into(), + // ANC + contract_address: "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76".into(), + amount: U256::encode_be_compact(250000), + recipient_address: "terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp".into(), + }; + + let input = Proto::SigningInput { + account_number: 3407705, + chain_id: "columbus-5".into(), + sequence: 2, + fee: Some(make_fee(200000, make_amount("uluna", "3000"))), + private_key: account_3407705_private_key(), + messages: vec![make_message( + MessageEnum::wasm_terra_execute_contract_transfer_message(transfer), + )], + ..Proto::SigningInput::default() + }; + + // https://finder.terra.money/mainnet/tx/078E90458061611F6FD8B708882B55FF5C1FFB3FCE61322107A0A0DE39FC0F3E + test_sign_json::(TestInput { + coin: &coin, + input, + tx: r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"3000","denom":"uluna"}],"gas":"200000"},"memo":"","msg":[{"type":"wasm/MsgExecuteContract","value":{"coins":[],"contract":"terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76","execute_msg":{"transfer":{"amount":"250000","recipient":"terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp"}},"sender":"terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"BjETdtbA97Wv1zvcsCV1tM+bdYKC8O3uGTk4mMRv6pBJB2y/Ds7qoS7s/zrkhYak1YChklQetHsI30XRXzGIkg=="}]}}"#, + signature: "06311376d6c0f7b5afd73bdcb02575b4cf9b758282f0edee19393898c46fea9049076cbf0eceeaa12eecff3ae48586a4d580a192541eb47b08df45d15f318892", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"BjETdtbA97Wv1zvcsCV1tM+bdYKC8O3uGTk4mMRv6pBJB2y/Ds7qoS7s/zrkhYak1YChklQetHsI30XRXzGIkg=="}]"#, + }); +} + +#[test] +fn test_terra_wasm_generic() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("terra"); + + let generic = Proto::mod_Message::WasmTerraExecuteContractGeneric { + sender_address: "terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf".into(), + // ANC + contract_address: "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76".into(), + execute_msg: r#"{"transfer": { "amount": "250000", "recipient": "terra1d7048csap4wzcv5zm7z6tdqem2agyp9647vdyj" } }"#.into(), + coins: Vec::default(), + }; + + let input = Proto::SigningInput { + account_number: 3407705, + chain_id: "columbus-5".into(), + sequence: 7, + fee: Some(make_fee(200000, make_amount("uluna", "3000"))), + private_key: account_3407705_private_key(), + messages: vec![make_message( + MessageEnum::wasm_terra_execute_contract_generic(generic), + )], + ..Proto::SigningInput::default() + }; + + // https://finder.terra.money/mainnet/tx/EC4F8532847E4D6AF016E6F6D3F027AE7FB6FF0B533C5132B01382D83B214A6F + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"Cu4BCusBCiYvdGVycmEud2FzbS52MWJldGExLk1zZ0V4ZWN1dGVDb250cmFjdBLAAQosdGVycmExOHd1a3A4NGRxMjI3d3U0bWdoMGptNm45bmxuajZyczgycHA5d2YSLHRlcnJhMTR6NTZsMGZwMmxzZjg2enkzaHR5Mno0N2V6a2hudGh0cjl5cTc2GmJ7InRyYW5zZmVyIjogeyAiYW1vdW50IjogIjI1MDAwMCIsICJyZWNpcGllbnQiOiAidGVycmExZDcwNDhjc2FwNHd6Y3Y1em03ejZ0ZHFlbTJhZ3lwOTY0N3ZkeWoiIH0gfRJnClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDcGY6x7D5iSlv61zln7pKUNfpThziVt/yJRRZyizZrAASBAoCCAEYBxITCg0KBXVsdW5hEgQzMDAwEMCaDBpAkPsS7xlSng2LMc9KiD1soN5NLaDcUh8I9okPmsdJN3le1B7yxRGNB4aQfhaRl/8Z0r5vitRT0AWuxDasd8wcFw=="}"#, + signature: "90fb12ef19529e0d8b31cf4a883d6ca0de4d2da0dc521f08f6890f9ac74937795ed41ef2c5118d0786907e169197ff19d2be6f8ad453d005aec436ac77cc1c17", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"kPsS7xlSng2LMc9KiD1soN5NLaDcUh8I9okPmsdJN3le1B7yxRGNB4aQfhaRl/8Z0r5vitRT0AWuxDasd8wcFw=="}]"#, + }); + + // This transaction hasn't been broadcasted. + test_sign_json::(TestInput { + coin: &coin, + input, + tx: r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"3000","denom":"uluna"}],"gas":"200000"},"memo":"","msg":[{"type":"wasm/MsgExecuteContract","value":{"coins":[],"contract":"terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76","execute_msg":{"transfer":{"amount":"250000","recipient":"terra1d7048csap4wzcv5zm7z6tdqem2agyp9647vdyj"}},"sender":"terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"2Ph/0CdoT/lUn4fvQUp0mN+uhQ80W/9YWS6B8gmwWP5pgdFWDvkvWB4ytGRQ5M8XxJnLi32qZvsPW3Nbv9YElw=="}]}}"#, + signature: "d8f87fd027684ff9549f87ef414a7498dfae850f345bff58592e81f209b058fe6981d1560ef92f581e32b46450e4cf17c499cb8b7daa66fb0f5b735bbfd60497", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"2Ph/0CdoT/lUn4fvQUp0mN+uhQ80W/9YWS6B8gmwWP5pgdFWDvkvWB4ytGRQ5M8XxJnLi32qZvsPW3Nbv9YElw=="}]"#, + }); +} + +#[test] +fn test_terra_wasm_generic_with_coins() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("terra"); + + let generic = Proto::mod_Message::WasmTerraExecuteContractGeneric { + sender_address: "terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf".into(), + // ANC Market + contract_address: "terra1sepfj7s0aeg5967uxnfk4thzlerrsktkpelm5s".into(), + execute_msg: r#"{ "deposit_stable": {} }"#.into(), + coins: vec![make_amount("uusd", "1000")], + }; + + let input = Proto::SigningInput { + account_number: 3407705, + chain_id: "columbus-5".into(), + sequence: 9, + fee: Some(make_fee(600000, make_amount("uluna", "7000"))), + private_key: account_3407705_private_key(), + messages: vec![make_message( + MessageEnum::wasm_terra_execute_contract_generic(generic), + )], + ..Proto::SigningInput::default() + }; + + // https://finder.terra.money/mainnet/tx/6651FCE0EE5C6D6ACB655CC49A6FD5E939FB082862854616EA0642475BCDD0C9 + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CrIBCq8BCiYvdGVycmEud2FzbS52MWJldGExLk1zZ0V4ZWN1dGVDb250cmFjdBKEAQosdGVycmExOHd1a3A4NGRxMjI3d3U0bWdoMGptNm45bmxuajZyczgycHA5d2YSLHRlcnJhMXNlcGZqN3MwYWVnNTk2N3V4bmZrNHRoemxlcnJza3RrcGVsbTVzGhh7ICJkZXBvc2l0X3N0YWJsZSI6IHt9IH0qDAoEdXVzZBIEMTAwMBJnClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDcGY6x7D5iSlv61zln7pKUNfpThziVt/yJRRZyizZrAASBAoCCAEYCRITCg0KBXVsdW5hEgQ3MDAwEMDPJBpAGyi7f1ioY8XV6pjFq1s86Om4++CIUnd3rLHif2iopCcYvX0mLkTlQ6NUERg8nWTYgXcj6fOTO/ptgPuAtv0NWg=="}"#, + signature: "1b28bb7f58a863c5d5ea98c5ab5b3ce8e9b8fbe088527777acb1e27f68a8a42718bd7d262e44e543a35411183c9d64d8817723e9f3933bfa6d80fb80b6fd0d5a", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"Gyi7f1ioY8XV6pjFq1s86Om4++CIUnd3rLHif2iopCcYvX0mLkTlQ6NUERg8nWTYgXcj6fOTO/ptgPuAtv0NWg=="}]"#, + }); + + // This transaction hasn't been broadcasted. + test_sign_json::(TestInput { + coin: &coin, + input, + tx: r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"7000","denom":"uluna"}],"gas":"600000"},"memo":"","msg":[{"type":"wasm/MsgExecuteContract","value":{"coins":[{"amount":"1000","denom":"uusd"}],"contract":"terra1sepfj7s0aeg5967uxnfk4thzlerrsktkpelm5s","execute_msg":{"deposit_stable":{}},"sender":"terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"mmfWOFq/9WaQ3PfZZ3C2dgU6S7PH+p/WzVruTpx9UPEHA8dwlTm5rk47YwUbhwKWmpQoUq0fON+GR1VE+rFHqA=="}]}}"#, + signature: "9a67d6385abff56690dcf7d96770b676053a4bb3c7fa9fd6cd5aee4e9c7d50f10703c7709539b9ae4e3b63051b8702969a942852ad1f38df86475544fab147a8", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"mmfWOFq/9WaQ3PfZZ3C2dgU6S7PH+p/WzVruTpx9UPEHA8dwlTm5rk47YwUbhwKWmpQoUq0fON+GR1VE+rFHqA=="}]"#, + }); +} + +#[test] +fn test_terra_wasm_send() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("terra"); + + let encoded_msg = base64::encode(r#"{"some_message":{}}"#.as_bytes(), false); + assert_eq!(encoded_msg, "eyJzb21lX21lc3NhZ2UiOnt9fQ=="); + + let send = Proto::mod_Message::WasmTerraExecuteContractSend { + sender_address: "terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf".into(), + // ANC + contract_address: "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76".into(), + amount: U256::encode_be_compact(250000), + recipient_contract_address: "terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp".into(), + msg: encoded_msg.into(), + coin: Vec::default(), + }; + + let input = Proto::SigningInput { + account_number: 3407705, + chain_id: "columbus-5".into(), + sequence: 4, + fee: Some(make_fee(200000, make_amount("uluna", "3000"))), + private_key: account_3407705_private_key(), + messages: vec![make_message( + MessageEnum::wasm_terra_execute_contract_send_message(send), + )], + ..Proto::SigningInput::default() + }; + + // https://finder.terra.money/mainnet/tx/9FF3F0A16879254C22EB90D8B4D6195467FE5014381FD36BD3C23CA6698FE94B + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CocCCoQCCiYvdGVycmEud2FzbS52MWJldGExLk1zZ0V4ZWN1dGVDb250cmFjdBLZAQosdGVycmExOHd1a3A4NGRxMjI3d3U0bWdoMGptNm45bmxuajZyczgycHA5d2YSLHRlcnJhMTR6NTZsMGZwMmxzZjg2enkzaHR5Mno0N2V6a2hudGh0cjl5cTc2Gnt7InNlbmQiOnsiYW1vdW50IjoiMjUwMDAwIiwiY29udHJhY3QiOiJ0ZXJyYTFqbGdhcXk5bnZuMmhmNXQyc3JhOXljejhzNzd3bmY5bDBrbWdjcCIsIm1zZyI6ImV5SnpiMjFsWDIxbGMzTmhaMlVpT250OWZRPT0ifX0SZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awAEgQKAggBGAQSEwoNCgV1bHVuYRIEMzAwMBDAmgwaQL6NByKeRZsyq5g6CTMdmPqiM77nOe9uLO8FjpetFgkBFiG3Le7ieZZ+4vCMhD1bcFgMwSHibFI/uPil847U/+g="}"#, + signature: "be8d07229e459b32ab983a09331d98faa233bee739ef6e2cef058e97ad1609011621b72deee279967ee2f08c843d5b70580cc121e26c523fb8f8a5f38ed4ffe8", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"vo0HIp5FmzKrmDoJMx2Y+qIzvuc5724s7wWOl60WCQEWIbct7uJ5ln7i8IyEPVtwWAzBIeJsUj+4+KXzjtT/6A=="}]"#, + }); + + // This transaction hasn't been broadcasted. + test_sign_json::(TestInput { + coin: &coin, + input, + tx: r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"3000","denom":"uluna"}],"gas":"200000"},"memo":"","msg":[{"type":"wasm/MsgExecuteContract","value":{"coins":[],"contract":"terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76","execute_msg":{"send":{"amount":"250000","contract":"terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp","msg":"eyJzb21lX21lc3NhZ2UiOnt9fQ=="}},"sender":"terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"17wYg45V0rjwCxMdlvcKliFnPdE7LcBoN83lfZaHB3R4XxaBqUv3IGEl3wrIjGo2OBf5SD1HkQywloPvoI6ENA=="}]}}"#, + signature: "d7bc18838e55d2b8f00b131d96f70a9621673dd13b2dc06837cde57d96870774785f1681a94bf7206125df0ac88c6a363817f9483d47910cb09683efa08e8434", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"17wYg45V0rjwCxMdlvcKliFnPdE7LcBoN83lfZaHB3R4XxaBqUv3IGEl3wrIjGo2OBf5SD1HkQywloPvoI6ENA=="}]"#, + }); +} + +#[test] +fn test_terra_transfer_payload() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("terra"); + + let transfer = Proto::mod_Message::WasmTerraExecuteContractTransfer { + sender_address: "terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf".into(), + // ANC + contract_address: "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76".into(), + recipient_address: "recipient=address".into(), + amount: U256::encode_be_compact(250000), + }; + + let payload = + TxBuilder::::wasm_terra_execute_contract_transfer_msg_from_proto( + &coin, &transfer, + ) + .unwrap(); + let actual = payload.to_json().unwrap(); + let expected = json!({ + "coins": [], + "contract": "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76", + "execute_msg": { + "transfer": { + "amount": "250000", + "recipient": "recipient=address" + } + }, + "sender": "terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf" + }); + assert_eq!(actual.value, expected); +} diff --git a/rust/tw_cosmos_sdk/tests/sign_thorchain.rs b/rust/tw_cosmos_sdk/tests/sign_thorchain.rs new file mode 100644 index 00000000000..967ce2e48e9 --- /dev/null +++ b/rust/tw_cosmos_sdk/tests/sign_thorchain.rs @@ -0,0 +1,135 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::borrow::Cow; +use std::str::FromStr; +use tw_coin_entry::test_utils::test_context::TestCoinContext; +use tw_cosmos_sdk::address::Address; +use tw_cosmos_sdk::context::StandardCosmosContext; +use tw_cosmos_sdk::test_utils::proto_utils::{make_amount, make_fee, make_fee_none, make_message}; +use tw_cosmos_sdk::test_utils::sign_utils::{ + test_sign_json_error, test_sign_protobuf, TestErrorInput, TestInput, +}; +use tw_encoding::hex::DecodeHex; +use tw_keypair::tw::PublicKeyType; +use tw_proto::Common::Proto::SigningError; +use tw_proto::Cosmos::Proto; +use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum; + +fn account_593_private_key() -> Cow<'static, [u8]> { + "7105512f0c020a1dd759e14b865ec0125f59ac31e34d7a2807a228ed50cb343e" + .decode_hex() + .unwrap() + .into() +} + +fn account_75247_private_key() -> Cow<'static, [u8]> { + "2659e41d54ebd449d68b9d58510d8eeeb837ee00d6ecc760b7a731238d8c3113" + .decode_hex() + .unwrap() + .into() +} + +fn address_to_key_hash(addr: &str) -> Cow<'static, [u8]> { + Address::from_str(addr).unwrap().key_hash().to_vec().into() +} + +fn make_thorchain_coin<'a>( + amount: &str, + decimals: i64, + asset: Proto::THORChainAsset<'a>, +) -> Proto::THORChainCoin<'a> { + Proto::THORChainCoin { + amount: amount.to_string().into(), + decimals, + asset: Some(asset), + } +} + +#[test] +fn test_thorchain_send() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("thor"); + + let send = Proto::mod_Message::THORChainSend { + from_address: address_to_key_hash("thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r"), + to_address: address_to_key_hash("thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn"), + amounts: vec![make_amount("rune", "38000000")], + }; + let input = Proto::SigningInput { + account_number: 593, + chain_id: "thorchain-mainnet-v1".into(), + sequence: 21, + fee: Some(make_fee(2500000, make_amount("rune", "200"))), + private_key: account_593_private_key(), + messages: vec![make_message(MessageEnum::thorchain_send_message(send))], + ..Proto::SigningInput::default() + }; + + // https://viewblock.io/thorchain/tx/7E480FA163F6C6AFA17593F214C7BBC218F69AE3BC72366E39042AF381BFE105 + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"ClIKUAoOL3R5cGVzLk1zZ1NlbmQSPgoUFSLnZ9tusZcIsAOAKb+9YHvJvQ4SFMqGRZ+wBVHH30JUDF54aRksgzrbGhAKBHJ1bmUSCDM4MDAwMDAwEmYKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQPtmX45bPQpL1/OWkK7pBWZzNXZbjExVKfJ6nBJ3jF8dxIECgIIARgVEhIKCwoEcnVuZRIDMjAwEKDLmAEaQKZtS3ATa26OOGvqdKm14ZbHeNfkPtIajXi5MkZ5XaX2SWOeX+YnCPZ9TxF9Jj5cVIo71m55xq4hVL3yDbRe89g="}"#, + signature: "a66d4b70136b6e8e386bea74a9b5e196c778d7e43ed21a8d78b93246795da5f649639e5fe62708f67d4f117d263e5c548a3bd66e79c6ae2154bdf20db45ef3d8", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A+2Zfjls9CkvX85aQrukFZnM1dluMTFUp8nqcEneMXx3"},"signature":"pm1LcBNrbo44a+p0qbXhlsd41+Q+0hqNeLkyRnldpfZJY55f5icI9n1PEX0mPlxUijvWbnnGriFUvfINtF7z2A=="}]"#, + }); + + test_sign_json_error::(TestErrorInput { + coin: &coin, + input, + error: SigningError::Error_not_supported, + }); +} + +#[test] +fn test_thorchain_deposit() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("thor"); + + let deposit = Proto::mod_Message::THORChainDeposit { + memo: "=:DOGE.DOGE:DNhRF1h8J4ZnB1bxp9kaqhVLYetkx1nSJ5::tr:0".into(), + signer: address_to_key_hash("thor14j5lwl8ulexrqp5x39kmkctv2937694z3jn2dz"), + coins: vec![make_thorchain_coin( + "150000000", + 0, + Proto::THORChainAsset { + chain: "THOR".into(), + symbol: "RUNE".into(), + ticker: "RUNE".into(), + ..Proto::THORChainAsset::default() + }, + )], + }; + let input = Proto::SigningInput { + account_number: 75247, + chain_id: "thorchain-mainnet-v1".into(), + sequence: 7, + fee: Some(make_fee_none(50000000)), + private_key: account_75247_private_key(), + messages: vec![make_message(MessageEnum::thorchain_deposit_message( + deposit, + ))], + ..Proto::SigningInput::default() + }; + + // https://viewblock.io/thorchain/tx/0162213E7F9D85965B1C57FA3BF9603C655B542F358318303A7B00661AE42510 + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CoUBCoIBChEvdHlwZXMuTXNnRGVwb3NpdBJtCh8KEgoEVEhPUhIEUlVORRoEUlVORRIJMTUwMDAwMDAwEjQ9OkRPR0UuRE9HRTpETmhSRjFoOEo0Wm5CMWJ4cDlrYXFoVkxZZXRreDFuU0o1Ojp0cjowGhSsqfd8/P5MMAaGiW27YWxRY+0WohJZClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDuZVDlIFW3DtSEBa6aUBJ0DrQHlQ+2g7lIt5ekAM25SkSBAoCCAEYBxIFEIDh6xcaQAxKMZMKbM8gdLwn23GDXfbwyCkgqWzFMFlnrqFm0u54F8T32wmsoJQAdoLIyOskYmi7nb1rhryfabeeULwRhiw="}"#, + signature: "0c4a31930a6ccf2074bc27db71835df6f0c82920a96cc5305967aea166d2ee7817c4f7db09aca094007682c8c8eb246268bb9dbd6b86bc9f69b79e50bc11862c", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A7mVQ5SBVtw7UhAWumlASdA60B5UPtoO5SLeXpADNuUp"},"signature":"DEoxkwpszyB0vCfbcYNd9vDIKSCpbMUwWWeuoWbS7ngXxPfbCayglAB2gsjI6yRiaLudvWuGvJ9pt55QvBGGLA=="}]"#, + }); + + test_sign_json_error::(TestErrorInput { + coin: &coin, + input, + error: SigningError::Error_not_supported, + }); +} diff --git a/rust/tw_cosmos_sdk/tests/sign_wasm_contract.rs b/rust/tw_cosmos_sdk/tests/sign_wasm_contract.rs new file mode 100644 index 00000000000..7e0d375ea48 --- /dev/null +++ b/rust/tw_cosmos_sdk/tests/sign_wasm_contract.rs @@ -0,0 +1,253 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde_json::json; +use std::borrow::Cow; +use tw_coin_entry::test_utils::test_context::TestCoinContext; +use tw_cosmos_sdk::context::StandardCosmosContext; +use tw_cosmos_sdk::modules::tx_builder::TxBuilder; +use tw_cosmos_sdk::test_utils::proto_utils::{make_amount, make_fee, make_message}; +use tw_cosmos_sdk::test_utils::sign_utils::{test_sign_json, test_sign_protobuf, TestInput}; +use tw_encoding::base64; +use tw_encoding::hex::DecodeHex; +use tw_keypair::tw::PublicKeyType; +use tw_number::U256; +use tw_proto::Cosmos::Proto; +use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum; + +fn account_336_private_key() -> Cow<'static, [u8]> { + "37f0af5bc20adb6832d39368a15492cd1e9e0cc1556d4317a5f75f9ccdf525ee" + .decode_hex() + .unwrap() + .into() +} + +fn account_3407705_private_key() -> Cow<'static, [u8]> { + "cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616" + .decode_hex() + .unwrap() + .into() +} + +/// Airdrop Neutron +#[test] +fn test_wasm_execute_generic() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("neutron"); + + let execute_msg = r#"{"claim":{"address":"neutron19h42zjnls2tpmg6yylcg6nr56cjxcx35q6xt57", "proof":["404ae2093edcca979ccb6ae4a36689cebc9c2c6a2b00b106c5396b079bf6dcf5","282fee30a25a60904f54d4f74aee8fcf8dd2822799c43be733e18e15743d4ece","e10de4202fe6532329d0d463d9669f1b659920868b9ea87d6715bfd223a86a40","564b4122c6f98653153d8e09d5a5f659fa7ebea740aa6b689c94211f8a11cc4b"], "amount":"2000000"}}"#; + let contract = Proto::mod_Message::WasmExecuteContractGeneric { + sender_address: "neutron19h42zjnls2tpmg6yylcg6nr56cjxcx35q6xt57".into(), + // ANC + contract_address: "neutron1465d8udjudl6cd8kgdlh2s37p7q0cf9x7yveumqwqk6ng94qwnmq7n79qn" + .into(), + execute_msg: execute_msg.into(), + ..Proto::mod_Message::WasmExecuteContractGeneric::default() + }; + + let input = Proto::SigningInput { + account_number: 336, + chain_id: "pion-1".into(), + sequence: 0, + fee: Some(make_fee(666666, make_amount("untrn", "1000"))), + private_key: account_336_private_key(), + messages: vec![make_message(MessageEnum::wasm_execute_contract_generic( + contract, + ))], + ..Proto::SigningInput::default() + }; + + // Successfully broadcasted: https://explorer.rs-testnet.polypore.xyz/pion-1/tx/28F25164B1E2556844C227819B1D5437960B7E91181B37460EC6792588FF7E4E + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CpQECpEECiQvY29zbXdhc20ud2FzbS52MS5Nc2dFeGVjdXRlQ29udHJhY3QS6AMKLm5ldXRyb24xOWg0MnpqbmxzMnRwbWc2eXlsY2c2bnI1NmNqeGN4MzVxNnh0NTcSQm5ldXRyb24xNDY1ZDh1ZGp1ZGw2Y2Q4a2dkbGgyczM3cDdxMGNmOXg3eXZldW1xd3FrNm5nOTRxd25tcTduNzlxbhrxAnsiY2xhaW0iOnsiYWRkcmVzcyI6Im5ldXRyb24xOWg0MnpqbmxzMnRwbWc2eXlsY2c2bnI1NmNqeGN4MzVxNnh0NTciLCAicHJvb2YiOlsiNDA0YWUyMDkzZWRjY2E5NzljY2I2YWU0YTM2Njg5Y2ViYzljMmM2YTJiMDBiMTA2YzUzOTZiMDc5YmY2ZGNmNSIsIjI4MmZlZTMwYTI1YTYwOTA0ZjU0ZDRmNzRhZWU4ZmNmOGRkMjgyMjc5OWM0M2JlNzMzZTE4ZTE1NzQzZDRlY2UiLCJlMTBkZTQyMDJmZTY1MzIzMjlkMGQ0NjNkOTY2OWYxYjY1OTkyMDg2OGI5ZWE4N2Q2NzE1YmZkMjIzYTg2YTQwIiwiNTY0YjQxMjJjNmY5ODY1MzE1M2Q4ZTA5ZDVhNWY2NTlmYTdlYmVhNzQwYWE2YjY4OWM5NDIxMWY4YTExY2M0YiJdLCAiYW1vdW50IjoiMjAwMDAwMCJ9fRJlCk4KRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiECqPwhojhpWpB3vDr8R+qyUnDkcK3BPxS35F8OrHPq5WwSBAoCCAESEwoNCgV1bnRybhIEMTAwMBCq2CgaQMIEXC8zyuuXWuIeX7dZBBzxMjmheOP1ONitBrVZdwmuQUgClmwhOdW0JwRe8CJ5NUKqtDYZjKFAPKGEWQ2veDs="}"#, + signature: "c2045c2f33caeb975ae21e5fb759041cf13239a178e3f538d8ad06b5597709ae414802966c2139d5b427045ef022793542aab436198ca1403ca184590daf783b", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"Aqj8IaI4aVqQd7w6/EfqslJw5HCtwT8Ut+RfDqxz6uVs"},"signature":"wgRcLzPK65da4h5ft1kEHPEyOaF44/U42K0GtVl3Ca5BSAKWbCE51bQnBF7wInk1Qqq0NhmMoUA8oYRZDa94Ow=="}]"#, + }); + + // This transaction hasn't been broadcasted. + test_sign_json::(TestInput { + coin: &coin, + input, + tx: r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"1000","denom":"untrn"}],"gas":"666666"},"memo":"","msg":[{"type":"wasm/MsgExecuteContract","value":{"coins":[],"contract":"neutron1465d8udjudl6cd8kgdlh2s37p7q0cf9x7yveumqwqk6ng94qwnmq7n79qn","msg":{"claim":{"address":"neutron19h42zjnls2tpmg6yylcg6nr56cjxcx35q6xt57","amount":"2000000","proof":["404ae2093edcca979ccb6ae4a36689cebc9c2c6a2b00b106c5396b079bf6dcf5","282fee30a25a60904f54d4f74aee8fcf8dd2822799c43be733e18e15743d4ece","e10de4202fe6532329d0d463d9669f1b659920868b9ea87d6715bfd223a86a40","564b4122c6f98653153d8e09d5a5f659fa7ebea740aa6b689c94211f8a11cc4b"]}},"sender":"neutron19h42zjnls2tpmg6yylcg6nr56cjxcx35q6xt57"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"Aqj8IaI4aVqQd7w6/EfqslJw5HCtwT8Ut+RfDqxz6uVs"},"signature":"R0zmQ4RCZ+UL+dTxgCHjK3IRklnLDWIRn6ZYDT9CZzUThcJdxlwxog0zCAAWhzH6HDv1T6LvdATlm7p93o+jzA=="}]}}"#, + signature: "474ce643844267e50bf9d4f18021e32b72119259cb0d62119fa6580d3f4267351385c25dc65c31a20d330800168731fa1c3bf54fa2ef7404e59bba7dde8fa3cc", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"Aqj8IaI4aVqQd7w6/EfqslJw5HCtwT8Ut+RfDqxz6uVs"},"signature":"R0zmQ4RCZ+UL+dTxgCHjK3IRklnLDWIRn6ZYDT9CZzUThcJdxlwxog0zCAAWhzH6HDv1T6LvdATlm7p93o+jzA=="}]"#, + }); +} + +/// TerraV2 DepositStable +#[test] +fn test_wasm_execute_generic_with_coins() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("terra"); + + let execute_msg = r#"{ "deposit_stable": {} }"#; + let contract = Proto::mod_Message::WasmExecuteContractGeneric { + sender_address: "terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf".into(), + contract_address: "terra1sepfj7s0aeg5967uxnfk4thzlerrsktkpelm5s".into(), + execute_msg: execute_msg.into(), + coins: vec![make_amount("uusd", "1000")], + ..Proto::mod_Message::WasmExecuteContractGeneric::default() + }; + + let input = Proto::SigningInput { + account_number: 3407705, + chain_id: "phoenix-1".into(), + sequence: 9, + fee: Some(make_fee(600000, make_amount("uluna", "7000"))), + private_key: account_3407705_private_key(), + messages: vec![make_message(MessageEnum::wasm_execute_contract_generic( + contract, + ))], + ..Proto::SigningInput::default() + }; + + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CrABCq0BCiQvY29zbXdhc20ud2FzbS52MS5Nc2dFeGVjdXRlQ29udHJhY3QShAEKLHRlcnJhMTh3dWtwODRkcTIyN3d1NG1naDBqbTZuOW5sbmo2cnM4MnBwOXdmEix0ZXJyYTFzZXBmajdzMGFlZzU5Njd1eG5mazR0aHpsZXJyc2t0a3BlbG01cxoYeyAiZGVwb3NpdF9zdGFibGUiOiB7fSB9KgwKBHV1c2QSBDEwMDASZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awAEgQKAggBGAkSEwoNCgV1bHVuYRIENzAwMBDAzyQaQEDA2foXegF+rslj6o8bX2HPJfn+q/6Ezbq2iAd0SFOTQqS8aAyywQkdZJRToXcaby1HOYL1WvmsMPgrFzChiY4="}"#, + signature: "40c0d9fa177a017eaec963ea8f1b5f61cf25f9feabfe84cdbab688077448539342a4bc680cb2c1091d649453a1771a6f2d473982f55af9ac30f82b1730a1898e", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"QMDZ+hd6AX6uyWPqjxtfYc8l+f6r/oTNuraIB3RIU5NCpLxoDLLBCR1klFOhdxpvLUc5gvVa+aww+CsXMKGJjg=="}]"#, + }); + + // This transaction hasn't been broadcasted. + test_sign_json::(TestInput { + coin: &coin, + input, + tx: r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"7000","denom":"uluna"}],"gas":"600000"},"memo":"","msg":[{"type":"wasm/MsgExecuteContract","value":{"coins":[{"amount":"1000","denom":"uusd"}],"contract":"terra1sepfj7s0aeg5967uxnfk4thzlerrsktkpelm5s","msg":{"deposit_stable":{}},"sender":"terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"ohL3xBbHumPGwz7nCyocMmS9n08bq27bOlV3hRSduNZzwsaxq5IktzizeYTRmv5uLvAhKHsrsMwWvJWU0J0nvw=="}]}}"#, + signature: "a212f7c416c7ba63c6c33ee70b2a1c3264bd9f4f1bab6edb3a557785149db8d673c2c6b1ab9224b738b37984d19afe6e2ef021287b2bb0cc16bc9594d09d27bf", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"ohL3xBbHumPGwz7nCyocMmS9n08bq27bOlV3hRSduNZzwsaxq5IktzizeYTRmv5uLvAhKHsrsMwWvJWU0J0nvw=="}]"#, + }); +} + +/// TerraV2 Transfer +#[test] +fn test_wasm_execute_transfer() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("terra"); + + let transfer = Proto::mod_Message::WasmExecuteContractTransfer { + sender_address: "terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf".into(), + // ANC + contract_address: "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76".into(), + recipient_address: "terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp".into(), + amount: U256::encode_be_compact(250000), + }; + + let input = Proto::SigningInput { + account_number: 3407705, + chain_id: "phoenix-1".into(), + sequence: 3, + fee: Some(make_fee(200000, make_amount("uluna", "3000"))), + private_key: account_3407705_private_key(), + messages: vec![make_message( + MessageEnum::wasm_execute_contract_transfer_message(transfer), + )], + ..Proto::SigningInput::default() + }; + + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CuUBCuIBCiQvY29zbXdhc20ud2FzbS52MS5Nc2dFeGVjdXRlQ29udHJhY3QSuQEKLHRlcnJhMTh3dWtwODRkcTIyN3d1NG1naDBqbTZuOW5sbmo2cnM4MnBwOXdmEix0ZXJyYTE0ejU2bDBmcDJsc2Y4Nnp5M2h0eTJ6NDdlemtobnRodHI5eXE3NhpbeyJ0cmFuc2ZlciI6eyJhbW91bnQiOiIyNTAwMDAiLCJyZWNpcGllbnQiOiJ0ZXJyYTFqbGdhcXk5bnZuMmhmNXQyc3JhOXljejhzNzd3bmY5bDBrbWdjcCJ9fRJnClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDcGY6x7D5iSlv61zln7pKUNfpThziVt/yJRRZyizZrAASBAoCCAEYAxITCg0KBXVsdW5hEgQzMDAwEMCaDBpAiBGbQaj+jsXE6/FssD3fC77QOxpli9GqsPea+KoNyMIEgVj89Hii+oU1bAEQS4qV0SaE2V6RNy24uCcFTIRbcQ=="}"#, + signature: "88119b41a8fe8ec5c4ebf16cb03ddf0bbed03b1a658bd1aab0f79af8aa0dc8c2048158fcf478a2fa85356c01104b8a95d12684d95e91372db8b827054c845b71", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"iBGbQaj+jsXE6/FssD3fC77QOxpli9GqsPea+KoNyMIEgVj89Hii+oU1bAEQS4qV0SaE2V6RNy24uCcFTIRbcQ=="}]"#, + }); + + // This transaction hasn't been broadcasted. + test_sign_json::(TestInput { + coin: &coin, + input, + tx: r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"3000","denom":"uluna"}],"gas":"200000"},"memo":"","msg":[{"type":"wasm/MsgExecuteContract","value":{"coins":[],"contract":"terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76","msg":{"transfer":{"amount":"250000","recipient":"terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp"}},"sender":"terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"fuuH43BvZGyTHb7Eaw0QDSbnOm11qRWj7HljhEmvKbFJztaSpLJ0cDmENksmt4lmPtIE26EZ1SZ6XriGX6LS0A=="}]}}"#, + signature: "7eeb87e3706f646c931dbec46b0d100d26e73a6d75a915a3ec79638449af29b149ced692a4b274703984364b26b789663ed204dba119d5267a5eb8865fa2d2d0", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"fuuH43BvZGyTHb7Eaw0QDSbnOm11qRWj7HljhEmvKbFJztaSpLJ0cDmENksmt4lmPtIE26EZ1SZ6XriGX6LS0A=="}]"#, + }); +} + +/// TerraV2 Transfer +#[test] +fn test_wasm_execute_send() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("terra"); + + let encoded_msg = base64::encode(r#"{"some_message":{}}"#.as_bytes(), false); + let send = Proto::mod_Message::WasmExecuteContractSend { + sender_address: "terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf".into(), + contract_address: "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76".into(), + recipient_contract_address: "terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp".into(), + amount: U256::encode_be_compact(250000), + msg: encoded_msg.into(), + coin: Vec::default(), + }; + + let input = Proto::SigningInput { + account_number: 3407705, + chain_id: "phoenix-1".into(), + sequence: 4, + fee: Some(make_fee(200000, make_amount("uluna", "3000"))), + private_key: account_3407705_private_key(), + messages: vec![make_message( + MessageEnum::wasm_execute_contract_send_message(send), + )], + ..Proto::SigningInput::default() + }; + + test_sign_protobuf::(TestInput { + coin: &coin, + input: input.clone(), + tx: r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CoUCCoICCiQvY29zbXdhc20ud2FzbS52MS5Nc2dFeGVjdXRlQ29udHJhY3QS2QEKLHRlcnJhMTh3dWtwODRkcTIyN3d1NG1naDBqbTZuOW5sbmo2cnM4MnBwOXdmEix0ZXJyYTE0ejU2bDBmcDJsc2Y4Nnp5M2h0eTJ6NDdlemtobnRodHI5eXE3Nhp7eyJzZW5kIjp7ImFtb3VudCI6IjI1MDAwMCIsImNvbnRyYWN0IjoidGVycmExamxnYXF5OW52bjJoZjV0MnNyYTl5Y3o4czc3d25mOWwwa21nY3AiLCJtc2ciOiJleUp6YjIxbFgyMWxjM05oWjJVaU9udDlmUT09In19EmcKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQNwZjrHsPmJKW/rXOWfukpQ1+lOHOJW3/IlFFnKLNmsABIECgIIARgEEhMKDQoFdWx1bmESBDMwMDAQwJoMGkBKJbW1GDrv9j2FIckm7MtpDZzP2RjgDjU84oYmOHNHsxEBPLjtt3YAjsKWBCAsjbnbVoJ3s2XFG08nxQXS9xBK"}"#, + signature: "4a25b5b5183aeff63d8521c926eccb690d9ccfd918e00e353ce28626387347b311013cb8edb776008ec29604202c8db9db568277b365c51b4f27c505d2f7104a", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"SiW1tRg67/Y9hSHJJuzLaQ2cz9kY4A41POKGJjhzR7MRATy47bd2AI7ClgQgLI2521aCd7NlxRtPJ8UF0vcQSg=="}]"#, + }); + + // This transaction hasn't been broadcasted. + test_sign_json::(TestInput { + coin: &coin, + input, + tx: r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"3000","denom":"uluna"}],"gas":"200000"},"memo":"","msg":[{"type":"wasm/MsgExecuteContract","value":{"coins":[],"contract":"terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76","msg":{"send":{"amount":"250000","contract":"terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp","msg":"eyJzb21lX21lc3NhZ2UiOnt9fQ=="}},"sender":"terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"EXQcMfD4507CcIKeOgkPWY92EUpQ7NJ802bf1IoMfgk69iUs6iEYucTo0MJo/Igys0yfbIHiLt4oJMetwNpqmA=="}]}}"#, + signature: "11741c31f0f8e74ec270829e3a090f598f76114a50ecd27cd366dfd48a0c7e093af6252cea2118b9c4e8d0c268fc8832b34c9f6c81e22ede2824c7adc0da6a98", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA"},"signature":"EXQcMfD4507CcIKeOgkPWY92EUpQ7NJ802bf1IoMfgk69iUs6iEYucTo0MJo/Igys0yfbIHiLt4oJMetwNpqmA=="}]"#, + }); +} + +#[test] +fn test_terra_transfer_payload() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("terra"); + + let transfer = Proto::mod_Message::WasmExecuteContractTransfer { + sender_address: "terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf".into(), + // ANC + contract_address: "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76".into(), + recipient_address: "recipient=address".into(), + amount: U256::encode_be_compact(250000), + }; + + let payload = + TxBuilder::::wasm_execute_contract_transfer_msg_from_proto( + &coin, &transfer, + ) + .unwrap(); + let actual = payload.to_json().unwrap(); + let expected = json!({ + "coins": [], + "contract": "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76", + "msg": { + "transfer": { + "amount": "250000", + "recipient": "recipient=address" + } + }, + "sender": "terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf" + }); + assert_eq!(actual.value, expected); +} diff --git a/rust/tw_encoding/Cargo.toml b/rust/tw_encoding/Cargo.toml index 4a1b666a551..88008908278 100644 --- a/rust/tw_encoding/Cargo.toml +++ b/rust/tw_encoding/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] arbitrary = { version = "1", features = ["derive"], optional = true } bcs = "0.1.6" +bech32 = "0.9.1" bs58 = "0.4.0" ciborium = "0.2.1" data-encoding = "2.3.3" diff --git a/rust/tw_encoding/src/base64.rs b/rust/tw_encoding/src/base64.rs index dd202dec66d..5b83eda01a6 100644 --- a/rust/tw_encoding/src/base64.rs +++ b/rust/tw_encoding/src/base64.rs @@ -5,6 +5,8 @@ // file LICENSE at the root of the source code distribution tree. use crate::{EncodingError, EncodingResult}; +use serde::{Serialize, Serializer}; +use tw_memory::Data; pub fn encode(data: &[u8], is_url: bool) -> String { if is_url { @@ -22,3 +24,16 @@ pub fn decode(data: &str, is_url: bool) -> EncodingResult> { } .map_err(|_| EncodingError::InvalidInput) } + +#[derive(Clone, Debug)] +pub struct Base64Encoded(pub Data); + +impl Serialize for Base64Encoded { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let is_url = false; + serializer.serialize_str(&encode(&self.0, is_url)) + } +} diff --git a/rust/tw_encoding/src/bech32.rs b/rust/tw_encoding/src/bech32.rs new file mode 100644 index 00000000000..397f6e213ee --- /dev/null +++ b/rust/tw_encoding/src/bech32.rs @@ -0,0 +1,31 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use bech32::{FromBase32, ToBase32, Variant}; +use tw_memory::Data; + +pub use bech32::Error as Bech32Error; + +pub type Bech32Result = Result; + +pub struct Decoded { + pub hrp: String, + pub bytes: Data, +} + +pub fn encode(hrp: &str, data: &[u8]) -> Bech32Result { + bech32::encode(hrp, data.to_base32(), Variant::Bech32) +} + +pub fn decode(s: &str) -> Bech32Result { + let (hrp, base32_bytes, variant) = bech32::decode(s)?; + let bytes = Data::from_base32(&base32_bytes)?; + + if matches!(variant, Variant::Bech32) { + return Ok(Decoded { hrp, bytes }); + } + Err(Bech32Error::InvalidChecksum) +} diff --git a/rust/tw_encoding/src/lib.rs b/rust/tw_encoding/src/lib.rs index f658d9b019f..871a6a42887 100644 --- a/rust/tw_encoding/src/lib.rs +++ b/rust/tw_encoding/src/lib.rs @@ -8,6 +8,7 @@ pub mod base32; pub mod base58; pub mod base64; pub mod bcs; +pub mod bech32; pub mod cbor; pub mod ffi; pub mod hex; diff --git a/rust/tw_ethereum/src/entry.rs b/rust/tw_ethereum/src/entry.rs index cb9a1b4fb48..f69893fd63f 100644 --- a/rust/tw_ethereum/src/entry.rs +++ b/rust/tw_ethereum/src/entry.rs @@ -46,6 +46,16 @@ impl CoinEntry for EthereumEntry { Address::from_str(address) } + #[inline] + fn parse_address_unchecked( + &self, + _coin: &dyn CoinContext, + address: &str, + ) -> AddressResult { + Address::from_str(address) + } + + #[inline] fn derive_address( &self, _coin: &dyn CoinContext, diff --git a/rust/tw_ethereum/tests/compiler.rs b/rust/tw_ethereum/tests/compiler.rs index 48b0df2b256..27cce260850 100644 --- a/rust/tw_ethereum/tests/compiler.rs +++ b/rust/tw_ethereum/tests/compiler.rs @@ -7,7 +7,7 @@ use std::borrow::Cow; use tw_coin_entry::coin_entry_ext::CoinEntryExt; use tw_coin_entry::error::SigningErrorType; -use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_encoding::hex; use tw_ethereum::entry::EthereumEntry; use tw_keypair::ecdsa::secp256k1; @@ -19,6 +19,8 @@ use tw_proto::{deserialize, serialize}; #[test] fn test_external_signature_sign() { + let coin = TestCoinContext::default(); + let transfer = Proto::mod_Transaction::Transfer { amount: U256::encode_be_compact(1_000_000_000_000_000_000), data: Cow::default(), @@ -38,7 +40,7 @@ fn test_external_signature_sign() { // Step 1: Obtain preimage hash let input_data = serialize(&input).unwrap(); let preimage_data = EthereumEntry - .preimage_hashes(&EmptyCoinContext, &input_data) + .preimage_hashes(&coin, &input_data) .expect("!preimage_hashes"); let preimage: CompilerProto::PreSigningOutput = deserialize(&preimage_data).expect("Coin entry returned an invalid output"); @@ -62,7 +64,7 @@ fn test_external_signature_sign() { let input_data = serialize(&input).unwrap(); let output_data = EthereumEntry .compile( - &EmptyCoinContext, + &coin, &input_data, vec![signature], vec![public_key.to_bytes()], @@ -88,7 +90,7 @@ fn test_external_signature_sign() { let input_data = serialize(&input).unwrap(); let output_data = EthereumEntry - .sign(&EmptyCoinContext, &input_data) + .sign(&coin, &input_data) .expect("!output_data"); let output: Proto::SigningOutput = deserialize(&output_data).expect("Coin entry returned an invalid output"); diff --git a/rust/tw_ethereum/tests/signer.rs b/rust/tw_ethereum/tests/signer.rs index d880744d540..8e0acb0e2ae 100644 --- a/rust/tw_ethereum/tests/signer.rs +++ b/rust/tw_ethereum/tests/signer.rs @@ -5,19 +5,21 @@ // file LICENSE at the root of the source code distribution tree. use tw_coin_entry::coin_entry_ext::CoinEntryExt; -use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_encoding::hex::DecodeHex; use tw_ethereum::entry::EthereumEntry; #[test] fn test_sign_json() { + let coin = TestCoinContext::default(); + let input_json = r#"{"chainId":"AQ==","gasPrice":"1pOkAA==","gasLimit":"Ugg=","toAddress":"0x7d8bf18C7cE84b3E175b339c4Ca93aEd1dD166F1","transaction":{"transfer":{"amount":"A0i8paFgAA=="}}})"#; let private_key = "17209af590a86462395d5881e60d11c7fa7d482cfb02b5a01b93c2eeef243543" .decode_hex() .unwrap(); EthereumEntry - .sign_json(&EmptyCoinContext, input_json, private_key) + .sign_json(&coin, input_json, private_key) .expect_err("'EthEntry::sign_json' is not supported yet"); // Expected result - "f86a8084d693a400825208947d8bf18c7ce84b3e175b339c4ca93aed1dd166f1870348bca5a160008025a0fe5802b49e04c6b1705088310e133605ed8b549811a18968ad409ea02ad79f21a05bf845646fb1e1b9365f63a7fd5eb5e984094e3ed35c3bed7361aebbcbf41f10" diff --git a/rust/tw_evm/src/modules/compiler.rs b/rust/tw_evm/src/modules/compiler.rs index 4d5066874b4..23598eca7aa 100644 --- a/rust/tw_evm/src/modules/compiler.rs +++ b/rust/tw_evm/src/modules/compiler.rs @@ -64,7 +64,8 @@ impl Compiler { let SingleSignaturePubkey { signature, public_key: _, - } = SingleSignaturePubkey::::from_sign_pubkey_list(signatures, public_keys)?; + } = SingleSignaturePubkey::from_sign_pubkey_list(signatures, public_keys)?; + let signature = secp256k1::Signature::from_bytes(&signature)?; let chain_id = U256::from_big_endian_slice(&input.chain_id)?; diff --git a/rust/tw_evm/tests/message_signer.rs b/rust/tw_evm/tests/message_signer.rs index 7be25764c2a..2f3b8327d92 100644 --- a/rust/tw_evm/tests/message_signer.rs +++ b/rust/tw_evm/tests/message_signer.rs @@ -6,7 +6,7 @@ use tw_coin_entry::error::SigningErrorType; use tw_coin_entry::modules::message_signer::MessageSigner; -use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_evm::modules::message_signer::EthMessageSigner; use tw_keypair::ecdsa::secp256k1; @@ -30,6 +30,8 @@ struct SignVerifyTestInput { } fn test_message_signer_sign_verify(test_input: SignVerifyTestInput) { + let coin = TestCoinContext::default(); + let private_key = test_input.private_key.decode_hex().unwrap(); let chain_id = test_input .chain_id @@ -42,7 +44,7 @@ fn test_message_signer_sign_verify(test_input: SignVerifyTestInput) { ..Proto::MessageSigningInput::default() }; - let output = EthMessageSigner.sign_message(&EmptyCoinContext, signing_input); + let output = EthMessageSigner.sign_message(&coin, signing_input); assert_eq!(output.error, SigningErrorType::OK); assert!(output.error_message.is_empty()); assert_eq!(output.signature, test_input.signature); @@ -57,7 +59,7 @@ fn test_message_signer_sign_verify(test_input: SignVerifyTestInput) { signature: test_input.signature.into(), }; assert!( - EthMessageSigner.verify_message(&EmptyCoinContext, verifying_input), + EthMessageSigner.verify_message(&coin, verifying_input), "!verify_message: {}", test_input.signature ); @@ -72,6 +74,8 @@ struct SignErrorTestInput { } fn test_message_signer_sign_err(test_input: SignErrorTestInput) { + let coin = TestCoinContext::default(); + let private_key = test_input.private_key.decode_hex().unwrap(); let signing_input = Proto::MessageSigningInput { private_key: private_key.into(), @@ -83,7 +87,7 @@ fn test_message_signer_sign_err(test_input: SignErrorTestInput) { ..Proto::MessageSigningInput::default() }; - let output = EthMessageSigner.sign_message(&EmptyCoinContext, signing_input); + let output = EthMessageSigner.sign_message(&coin, signing_input); assert_eq!(output.error, test_input.error); } @@ -95,6 +99,8 @@ struct PreimageTestInput { } fn test_message_signer_preimage_hashes(test_input: PreimageTestInput) { + let coin = TestCoinContext::default(); + let signing_input = Proto::MessageSigningInput { message: test_input.msg.into(), message_type: test_input.msg_type, @@ -104,7 +110,7 @@ fn test_message_signer_preimage_hashes(test_input: PreimageTestInput) { ..Proto::MessageSigningInput::default() }; - let output = EthMessageSigner.message_preimage_hashes(&EmptyCoinContext, signing_input); + let output = EthMessageSigner.message_preimage_hashes(&coin, signing_input); assert_eq!(output.error, SigningErrorType::OK); assert!(output.error_message.is_empty()); assert_eq!(output.data_hash.to_hex(), test_input.data_hash); @@ -175,13 +181,15 @@ fn test_message_signer_hash_with_custom_array() { #[test] fn test_message_signer_hash_unequal_array_len() { + let coin = TestCoinContext::default(); + let signing_input = Proto::MessageSigningInput { message: EIP712_UNEQUAL_ARRAY_LEN.into(), message_type: Proto::MessageType::MessageType_typed_eip155, ..Proto::MessageSigningInput::default() }; - let output = EthMessageSigner.message_preimage_hashes(&EmptyCoinContext, signing_input); + let output = EthMessageSigner.message_preimage_hashes(&coin, signing_input); assert_eq!(output.error, SigningErrorType::Error_invalid_params); } diff --git a/rust/tw_hash/src/hasher.rs b/rust/tw_hash/src/hasher.rs new file mode 100644 index 00000000000..086c9915f0c --- /dev/null +++ b/rust/tw_hash/src/hasher.rs @@ -0,0 +1,40 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ripemd::ripemd_160; +use crate::sha2::sha256; +use crate::sha3::keccak256; +use serde::Deserialize; +use tw_memory::Data; + +// keccak256 + +/// Enum selector for the supported hash functions. +/// Add hash types if necessary. For example, when add a new hasher to `registry.json`. +#[derive(Clone, Copy, Debug, Deserialize, PartialEq)] +pub enum Hasher { + #[serde(rename = "sha256")] + Sha256, + #[serde(rename = "keccak256")] + Keccak256, + /// SHA256 hash of the SHA256 hash + #[serde(rename = "sha256d")] + Sha256d, + /// ripemd hash of the SHA256 hash + #[serde(rename = "sha256ripemd")] + Sha256ripemd, +} + +impl Hasher { + pub fn hash(&self, data: &[u8]) -> Data { + match self { + Hasher::Sha256 => sha256(data), + Hasher::Keccak256 => keccak256(data), + Hasher::Sha256d => sha256(&sha256(data)), + Hasher::Sha256ripemd => ripemd_160(&sha256(data)), + } + } +} diff --git a/rust/tw_hash/src/lib.rs b/rust/tw_hash/src/lib.rs index f7cfe16d911..8020e6cde19 100644 --- a/rust/tw_hash/src/lib.rs +++ b/rust/tw_hash/src/lib.rs @@ -9,6 +9,7 @@ pub mod blake2; pub mod crc32; pub mod ffi; pub mod groestl; +pub mod hasher; pub mod hmac; pub mod ripemd; pub mod sha1; diff --git a/rust/tw_internet_computer/Cargo.toml b/rust/tw_internet_computer/Cargo.toml index d774d4946ae..7fd375e1071 100644 --- a/rust/tw_internet_computer/Cargo.toml +++ b/rust/tw_internet_computer/Cargo.toml @@ -3,8 +3,6 @@ name = "tw_internet_computer" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] quick-protobuf = "0.8.1" serde = { version = "1.0.136", features = ["derive"] } diff --git a/rust/tw_internet_computer/src/entry.rs b/rust/tw_internet_computer/src/entry.rs index a9542fbb8ea..6fb47ba161c 100644 --- a/rust/tw_internet_computer/src/entry.rs +++ b/rust/tw_internet_computer/src/entry.rs @@ -53,6 +53,15 @@ impl CoinEntry for InternetComputerEntry { Self::Address::from_str(address) } + #[inline] + fn parse_address_unchecked( + &self, + _coin: &dyn CoinContext, + address: &str, + ) -> AddressResult { + Self::Address::from_str(address) + } + #[inline] fn derive_address( &self, diff --git a/rust/tw_keypair/src/traits.rs b/rust/tw_keypair/src/traits.rs index fa9216daa91..7b32076f7fe 100644 --- a/rust/tw_keypair/src/traits.rs +++ b/rust/tw_keypair/src/traits.rs @@ -5,7 +5,7 @@ // file LICENSE at the root of the source code distribution tree. use crate::KeyPairResult; -use tw_misc::traits::{ToBytesVec, ToBytesZeroizing}; +use tw_misc::traits::{FromSlice, ToBytesVec, ToBytesZeroizing}; pub trait KeyPairTrait: FromSlice + SigningKeyTrait + VerifyingKeyTrait { type Private: FromSlice + ToBytesZeroizing; @@ -33,7 +33,3 @@ pub trait VerifyingKeyTrait { /// Verifies if the given `hash` was signed using the private key. fn verify(&self, signature: Self::VerifySignature, message: Self::SigningMessage) -> bool; } - -pub trait FromSlice: for<'a> TryFrom<&'a [u8]> {} - -impl FromSlice for T where for<'a> T: TryFrom<&'a [u8]> {} diff --git a/rust/tw_keypair/src/tw/public.rs b/rust/tw_keypair/src/tw/public.rs index 8b527050944..754ed022870 100644 --- a/rust/tw_keypair/src/tw/public.rs +++ b/rust/tw_keypair/src/tw/public.rs @@ -142,4 +142,19 @@ impl PublicKey { _ => None, } } + + /// Returns a public key type. + pub fn public_key_type(&self) -> PublicKeyType { + match self { + PublicKey::Secp256k1(_) => PublicKeyType::Secp256k1, + PublicKey::Secp256k1Extended(_) => PublicKeyType::Secp256k1Extended, + PublicKey::Nist256p1(_) => PublicKeyType::Nist256p1, + PublicKey::Nist256p1Extended(_) => PublicKeyType::Nist256p1Extended, + PublicKey::Ed25519(_) => PublicKeyType::Ed25519, + PublicKey::Ed25519Blake2b(_) => PublicKeyType::Ed25519Blake2b, + PublicKey::Curve25519Waves(_) => PublicKeyType::Curve25519Waves, + PublicKey::Ed25519ExtendedCardano(_) => PublicKeyType::Ed25519ExtendedCardano, + PublicKey::Starkex(_) => PublicKeyType::Starkex, + } + } } diff --git a/rust/tw_misc/src/lib.rs b/rust/tw_misc/src/lib.rs index 314dedda6dc..a6017087f76 100644 --- a/rust/tw_misc/src/lib.rs +++ b/rust/tw_misc/src/lib.rs @@ -1,3 +1,9 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + pub mod macros; #[cfg(feature = "test-utils")] pub mod test_utils; diff --git a/rust/tw_misc/src/traits.rs b/rust/tw_misc/src/traits.rs index 1b8900eb019..36fb534853a 100644 --- a/rust/tw_misc/src/traits.rs +++ b/rust/tw_misc/src/traits.rs @@ -38,3 +38,7 @@ impl IntoOption for Option { self } } + +pub trait FromSlice: for<'a> TryFrom<&'a [u8]> {} + +impl FromSlice for T where for<'a> T: TryFrom<&'a [u8]> {} diff --git a/rust/tw_proto/src/common/google/mod.rs b/rust/tw_proto/src/common/google/mod.rs new file mode 100644 index 00000000000..a11a0995e85 --- /dev/null +++ b/rust/tw_proto/src/common/google/mod.rs @@ -0,0 +1,7 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod protobuf; diff --git a/rust/tw_proto/src/common/google/protobuf/any.proto b/rust/tw_proto/src/common/google/protobuf/any.proto new file mode 100644 index 00000000000..c7aa5a0baa1 --- /dev/null +++ b/rust/tw_proto/src/common/google/protobuf/any.proto @@ -0,0 +1,169 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Source (any): https://github.com/protocolbuffers/protobuf/blob/8bf4fe924a924e0ee290bf883977c108fc12f28d/src/google/protobuf/any.proto +// To recompile the file use the following command inside `wallet-core` directory: +// ``` +// cargo install pb-rs +// pb-rs --dont_use_cow --single-mod --output_directory rust/tw_proto/common_proto/google/protobuf/ rust/tw_proto/common_proto/google/protobuf/any.proto +// ``` + +syntax = "proto3"; + +package google.protobuf; + +option go_package = "google.golang.org/protobuf/types/known/anypb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "AnyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; + +// `Any` contains an arbitrary serialized protocol buffer message along with a +// URL that describes the type of the serialized message. +// +// Protobuf library provides support to pack/unpack Any values in the form +// of utility functions or additional generated methods of the Any type. +// +// Example 1: Pack and unpack a message in C++. +// +// Foo foo = ...; +// Any any; +// any.PackFrom(foo); +// ... +// if (any.UnpackTo(&foo)) { +// ... +// } +// +// Example 2: Pack and unpack a message in Java. +// +// Foo foo = ...; +// Any any = Any.pack(foo); +// ... +// if (any.is(Foo.class)) { +// foo = any.unpack(Foo.class); +// } +// // or ... +// if (any.isSameTypeAs(Foo.getDefaultInstance())) { +// foo = any.unpack(Foo.getDefaultInstance()); +// } +// +// Example 3: Pack and unpack a message in Python. +// +// foo = Foo(...) +// any = Any() +// any.Pack(foo) +// ... +// if any.Is(Foo.DESCRIPTOR): +// any.Unpack(foo) +// ... +// +// Example 4: Pack and unpack a message in Go +// +// foo := &pb.Foo{...} +// any, err := anypb.New(foo) +// if err != nil { +// ... +// } +// ... +// foo := &pb.Foo{} +// if err := any.UnmarshalTo(foo); err != nil { +// ... +// } +// +// The pack methods provided by protobuf library will by default use +// 'type.googleapis.com/full.type.name' as the type URL and the unpack +// methods only use the fully qualified type name after the last '/' +// in the type URL, for example "foo.bar.com/x/y.z" will yield type +// name "y.z". +// +// JSON +// ==== +// The JSON representation of an `Any` value uses the regular +// representation of the deserialized, embedded message, with an +// additional field `@type` which contains the type URL. Example: +// +// package google.profile; +// message Person { +// string first_name = 1; +// string last_name = 2; +// } +// +// { +// "@type": "type.googleapis.com/google.profile.Person", +// "firstName": , +// "lastName": +// } +// +// If the embedded message type is well-known and has a custom JSON +// representation, that representation will be embedded adding a field +// `value` which holds the custom JSON in addition to the `@type` +// field. Example (for message [google.protobuf.Duration][]): +// +// { +// "@type": "type.googleapis.com/google.protobuf.Duration", +// "value": "1.212s" +// } +// +message Any { + // A URL/resource name that uniquely identifies the type of the serialized + // protocol buffer message. This string must contain at least + // one "/" character. The last segment of the URL's path must represent + // the fully qualified name of the type (as in + // `path/google.protobuf.Duration`). The name should be in a canonical form + // (e.g., leading "." is not accepted). + // + // In practice, teams usually precompile into the binary all types that they + // expect it to use in the context of Any. However, for URLs which use the + // scheme `http`, `https`, or no scheme, one can optionally set up a type + // server that maps type URLs to message definitions as follows: + // + // * If no scheme is provided, `https` is assumed. + // * An HTTP GET on the URL must yield a [google.protobuf.Type][] + // value in binary format, or produce an error. + // * Applications are allowed to cache lookup results based on the + // URL, or have them precompiled into a binary to avoid any + // lookup. Therefore, binary compatibility needs to be preserved + // on changes to types. (Use versioned type names to manage + // breaking changes.) + // + // Note: this functionality is not currently available in the official + // protobuf release, and it is not used for type URLs beginning with + // type.googleapis.com. As of May 2023, there are no widely used type server + // implementations and no plans to implement one. + // + // Schemes other than `http`, `https` (or the empty scheme) might be + // used with implementation specific semantics. + // + string type_url = 1; + + // Must be a valid serialized protocol buffer of the above specified type. + bytes value = 2; +} diff --git a/rust/tw_proto/src/common/google/protobuf/any.rs b/rust/tw_proto/src/common/google/protobuf/any.rs new file mode 100644 index 00000000000..743b9b93b94 --- /dev/null +++ b/rust/tw_proto/src/common/google/protobuf/any.rs @@ -0,0 +1,51 @@ +// Automatically generated rust module for 'any.proto' file + +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(unused_imports)] +#![allow(unknown_lints)] +#![allow(clippy::all)] +#![cfg_attr(rustfmt, rustfmt_skip)] + + +use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; +use quick_protobuf::sizeofs::*; +use super::*; + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub struct Any { + pub type_url: String, + pub value: Vec, +} + +impl<'a> MessageRead<'a> for Any { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(10) => msg.type_url = r.read_string(bytes)?.to_owned(), + Ok(18) => msg.value = r.read_bytes(bytes)?.to_owned(), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for Any { + fn get_size(&self) -> usize { + 0 + + if self.type_url == String::default() { 0 } else { 1 + sizeof_len((&self.type_url).len()) } + + if self.value.is_empty() { 0 } else { 1 + sizeof_len((&self.value).len()) } + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if self.type_url != String::default() { w.write_with_tag(10, |w| w.write_string(&**&self.type_url))?; } + if !self.value.is_empty() { w.write_with_tag(18, |w| w.write_bytes(&**&self.value))?; } + Ok(()) + } +} + diff --git a/rust/tw_proto/src/common/google/protobuf/mod.rs b/rust/tw_proto/src/common/google/protobuf/mod.rs new file mode 100644 index 00000000000..3f4d90a7f08 --- /dev/null +++ b/rust/tw_proto/src/common/google/protobuf/mod.rs @@ -0,0 +1,11 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +mod any; +mod timestamp; + +pub use any::*; +pub use timestamp::*; diff --git a/rust/tw_proto/src/common/google/protobuf/timestamp.proto b/rust/tw_proto/src/common/google/protobuf/timestamp.proto new file mode 100644 index 00000000000..353b1632497 --- /dev/null +++ b/rust/tw_proto/src/common/google/protobuf/timestamp.proto @@ -0,0 +1,151 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Source: https://github.com/protocolbuffers/protobuf/blob/538a8e9a0d90b0bd8aea7b10f8e17ba76585b2e8/src/google/protobuf/timestamp.proto +// To recompile the file use the following command inside `wallet-core` directory: +// ``` +// cargo install pb-rs +// pb-rs --dont_use_cow --single-mod --output_directory rust/tw_proto/common_proto/google/protobuf/ rust/tw_proto/common_proto/google/protobuf/any.proto +// ``` + +syntax = "proto3"; + +package google.protobuf; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/protobuf/types/known/timestamppb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "TimestampProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; + +// A Timestamp represents a point in time independent of any time zone or local +// calendar, encoded as a count of seconds and fractions of seconds at +// nanosecond resolution. The count is relative to an epoch at UTC midnight on +// January 1, 1970, in the proleptic Gregorian calendar which extends the +// Gregorian calendar backwards to year one. +// +// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap +// second table is needed for interpretation, using a [24-hour linear +// smear](https://developers.google.com/time/smear). +// +// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By +// restricting to that range, we ensure that we can convert to and from [RFC +// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. +// +// # Examples +// +// Example 1: Compute Timestamp from POSIX `time()`. +// +// Timestamp timestamp; +// timestamp.set_seconds(time(NULL)); +// timestamp.set_nanos(0); +// +// Example 2: Compute Timestamp from POSIX `gettimeofday()`. +// +// struct timeval tv; +// gettimeofday(&tv, NULL); +// +// Timestamp timestamp; +// timestamp.set_seconds(tv.tv_sec); +// timestamp.set_nanos(tv.tv_usec * 1000); +// +// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. +// +// FILETIME ft; +// GetSystemTimeAsFileTime(&ft); +// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; +// +// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z +// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. +// Timestamp timestamp; +// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); +// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); +// +// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. +// +// long millis = System.currentTimeMillis(); +// +// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) +// .setNanos((int) ((millis % 1000) * 1000000)).build(); +// +// Example 5: Compute Timestamp from Java `Instant.now()`. +// +// Instant now = Instant.now(); +// +// Timestamp timestamp = +// Timestamp.newBuilder().setSeconds(now.getEpochSecond()) +// .setNanos(now.getNano()).build(); +// +// Example 6: Compute Timestamp from current time in Python. +// +// timestamp = Timestamp() +// timestamp.GetCurrentTime() +// +// # JSON Mapping +// +// In JSON format, the Timestamp type is encoded as a string in the +// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the +// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" +// where {year} is always expressed using four digits while {month}, {day}, +// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional +// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), +// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone +// is required. A proto3 JSON serializer should always use UTC (as indicated by +// "Z") when printing the Timestamp type and a proto3 JSON parser should be +// able to accept both UTC and other timezones (as indicated by an offset). +// +// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past +// 01:30 UTC on January 15, 2017. +// +// In JavaScript, one can convert a Date object to this format using the +// standard +// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) +// method. In Python, a standard `datetime.datetime` object can be converted +// to this format using +// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with +// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use +// the Joda Time's [`ISODateTimeFormat.dateTime()`]( +// http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime() +// ) to obtain a formatter capable of generating timestamps in this format. +// +message Timestamp { + // Represents seconds of UTC time since Unix epoch + // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + // 9999-12-31T23:59:59Z inclusive. + int64 seconds = 1; + + // Non-negative fractions of a second at nanosecond resolution. Negative + // second values with fractions must still have non-negative nanos values + // that count forward in time. Must be from 0 to 999,999,999 + // inclusive. + int32 nanos = 2; +} diff --git a/rust/tw_proto/src/common/google/protobuf/timestamp.rs b/rust/tw_proto/src/common/google/protobuf/timestamp.rs new file mode 100644 index 00000000000..2e073dbe982 --- /dev/null +++ b/rust/tw_proto/src/common/google/protobuf/timestamp.rs @@ -0,0 +1,51 @@ +// Automatically generated rust module for 'timestamp.proto' file + +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(unused_imports)] +#![allow(unknown_lints)] +#![allow(clippy::all)] +#![cfg_attr(rustfmt, rustfmt_skip)] + + +use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; +use quick_protobuf::sizeofs::*; +use super::*; + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Default, PartialEq, Clone)] +pub struct Timestamp { + pub seconds: i64, + pub nanos: i32, +} + +impl<'a> MessageRead<'a> for Timestamp { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(8) => msg.seconds = r.read_int64(bytes)?, + Ok(16) => msg.nanos = r.read_int32(bytes)?, + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for Timestamp { + fn get_size(&self) -> usize { + 0 + + if self.seconds == 0i64 { 0 } else { 1 + sizeof_varint(*(&self.seconds) as u64) } + + if self.nanos == 0i32 { 0 } else { 1 + sizeof_varint(*(&self.nanos) as u64) } + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if self.seconds != 0i64 { w.write_with_tag(8, |w| w.write_int64(*&self.seconds))?; } + if self.nanos != 0i32 { w.write_with_tag(16, |w| w.write_int32(*&self.nanos))?; } + Ok(()) + } +} + diff --git a/rust/tw_proto/src/common/mod.rs b/rust/tw_proto/src/common/mod.rs new file mode 100644 index 00000000000..972a22f676f --- /dev/null +++ b/rust/tw_proto/src/common/mod.rs @@ -0,0 +1,7 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod google; diff --git a/rust/tw_proto/src/lib.rs b/rust/tw_proto/src/lib.rs index cf592f95037..45eb41fac60 100644 --- a/rust/tw_proto/src/lib.rs +++ b/rust/tw_proto/src/lib.rs @@ -4,7 +4,11 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use quick_protobuf::{BytesReader, Writer}; +use quick_protobuf::{BytesReader, MessageInfo, Writer}; + +#[allow(non_snake_case)] +#[rustfmt::skip] +mod common; #[allow(non_snake_case)] #[rustfmt::skip] @@ -12,6 +16,7 @@ mod generated { include!(concat!(env!("OUT_DIR"), "/proto/mod.rs")); } +pub use common::google; pub use generated::TW::*; pub use quick_protobuf::{ deserialize_from_slice as deserialize_prefixed, serialize_into_vec as serialize_prefixed, @@ -38,6 +43,15 @@ pub fn deserialize<'a, T: MessageRead<'a>>(data: &'a [u8]) -> ProtoResult { T::from_reader(&mut reader, data) } +pub fn to_any(message: &T) -> google::protobuf::Any +where + T: MessageInfo + MessageWrite, +{ + let value = serialize(message).expect("Protobuf serialization should never fail"); + let type_url = format!("/{}", T::PATH); + google::protobuf::Any { type_url, value } +} + /// There is no way to create an instance of the `NoMessage` enum as it doesn't has variants. pub enum NoMessage {} diff --git a/rust/tw_ronin/src/entry.rs b/rust/tw_ronin/src/entry.rs index 976a08e0eca..36bb8fb96b2 100644 --- a/rust/tw_ronin/src/entry.rs +++ b/rust/tw_ronin/src/entry.rs @@ -46,6 +46,15 @@ impl CoinEntry for RoninEntry { Address::from_str(address) } + #[inline] + fn parse_address_unchecked( + &self, + _coin: &dyn CoinContext, + address: &str, + ) -> AddressResult { + Address::from_str(address) + } + fn derive_address( &self, _coin: &dyn CoinContext, diff --git a/rust/tw_ronin/tests/compiler.rs b/rust/tw_ronin/tests/compiler.rs index 53972303b5c..5c99e67b419 100644 --- a/rust/tw_ronin/tests/compiler.rs +++ b/rust/tw_ronin/tests/compiler.rs @@ -7,7 +7,7 @@ use std::borrow::Cow; use tw_coin_entry::coin_entry_ext::CoinEntryExt; use tw_coin_entry::error::SigningErrorType; -use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_keypair::ecdsa::secp256k1; use tw_keypair::tw; @@ -19,6 +19,8 @@ use tw_ronin::entry::RoninEntry; #[test] fn test_ronin_preimage_hashes_and_compile() { + let coin = TestCoinContext::default(); + let transfer = Proto::mod_Transaction::Transfer { amount: U256::encode_be_compact(1_000_000_000_000_000_000), data: Cow::default(), @@ -38,7 +40,7 @@ fn test_ronin_preimage_hashes_and_compile() { let input_data = serialize(&input).unwrap(); let res = RoninEntry - .preimage_hashes(&EmptyCoinContext, &input_data) + .preimage_hashes(&coin, &input_data) .expect("!preimage_hashes"); let preimage: CompilerProto::PreSigningOutput = deserialize(res.as_slice()).expect("Coin entry returned an invalid output"); @@ -61,7 +63,7 @@ fn test_ronin_preimage_hashes_and_compile() { // Step 3: Compile transaction info let output_data = RoninEntry .compile( - &EmptyCoinContext, + &coin, &input_data, vec![signature], vec![public_key.to_bytes()], diff --git a/rust/tw_ronin/tests/signer.rs b/rust/tw_ronin/tests/signer.rs index 32dd2cb5338..d68396a34b4 100644 --- a/rust/tw_ronin/tests/signer.rs +++ b/rust/tw_ronin/tests/signer.rs @@ -7,7 +7,7 @@ use std::borrow::Cow; use tw_coin_entry::coin_entry_ext::CoinEntryExt; use tw_coin_entry::error::SigningErrorType; -use tw_coin_entry::test_utils::empty_context::EmptyCoinContext; +use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_number::U256; use tw_proto::Ethereum::Proto; @@ -17,6 +17,8 @@ use tw_ronin::entry::RoninEntry; /// https://explorer.roninchain.com/tx/0xf13a2c4421700f8782ca73eaf16bb8baf82bcf093e23570a1ff062cdd8dbf6c3 #[test] fn test_ronin_signing() { + let coin = TestCoinContext::default(); + let private = "0x4646464646464646464646464646464646464646464646464646464646464646" .decode_hex() .unwrap(); @@ -40,9 +42,7 @@ fn test_ronin_signing() { }; let input_data = serialize(&input).unwrap(); - let output_data = RoninEntry - .sign(&EmptyCoinContext, &input_data) - .expect("!sign"); + let output_data = RoninEntry.sign(&coin, &input_data).expect("!sign"); let output: Proto::SigningOutput = deserialize(&output_data).expect("Coin entry returned an invalid output"); @@ -55,13 +55,15 @@ fn test_ronin_signing() { #[test] fn test_sign_json() { + let coin = TestCoinContext::default(); + let input_json = r#"{"chainId":"B+Q=","nonce":"AA==","gasPrice":"O5rKAA==","gasLimit":"Ugg=","toAddress":"ronin:c36edf48e21cf395b206352a1819de658fd7f988","privateKey":"RkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkY=","transaction":{"transfer":{"amount":"BDff"}}}"#; let private_key = "0x4646464646464646464646464646464646464646464646464646464646464646" .decode_hex() .unwrap(); RoninEntry - .sign_json(&EmptyCoinContext, input_json, private_key) + .sign_json(&coin, input_json, private_key) .expect_err("'EthEntry::sign_json' is not supported yet"); // Expected result - "f86880843b9aca0082520894c36edf48e21cf395b206352a1819de658fd7f988830437df80820feca0442aa06b0d0465bfecf84b28e2ce614a32a1ccc12735dc03a5799517d6659d7aa004e1bf2efa30743f1b6d49dbec2671e9fb5ead1e7da15e352ca1df6fb86a8ba7" diff --git a/rust/tw_utxo/Cargo.toml b/rust/tw_utxo/Cargo.toml index 5631960638f..738f2932771 100644 --- a/rust/tw_utxo/Cargo.toml +++ b/rust/tw_utxo/Cargo.toml @@ -3,8 +3,6 @@ name = "tw_utxo" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] tw_coin_entry = { path = "../tw_coin_entry" } tw_keypair = { path = "../tw_keypair" } diff --git a/src/Aptos/Entry.cpp b/src/Aptos/Entry.cpp deleted file mode 100644 index a0d362910ac..00000000000 --- a/src/Aptos/Entry.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Entry.h" - -namespace TW::Aptos { - -bool Entry::validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const { - return validateAddressRust(coin, address, addressPrefix); -} - -std::string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const { - return deriveAddressRust(coin, publicKey, derivation, addressPrefix); -} - -void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { - signRust(dataIn, coin, dataOut); -} - -Data Entry::preImageHashes([[maybe_unused]] TWCoinType coin, const Data& txInputData) const { - return preImageHashesRust(coin, txInputData); -} - -void Entry::compile([[maybe_unused]] TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const { - compileRust(coin, txInputData, signatures, publicKeys, dataOut); -} - -} // namespace TW::Aptos diff --git a/src/Aptos/Entry.h b/src/Aptos/Entry.h index 2219499f5ac..750c5dba2a9 100644 --- a/src/Aptos/Entry.h +++ b/src/Aptos/Entry.h @@ -6,19 +6,13 @@ #pragma once -#include "../CoinEntry.h" +#include "rust/RustCoinEntry.h" namespace TW::Aptos { /// Entry point for implementation of Aptos coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry final : public CoinEntry { -public: - bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const override; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const override; - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const override; - Data preImageHashes(TWCoinType coin, const Data& txInputData) const override; - void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const override; +class Entry final : public Rust::RustCoinEntry { }; } // namespace TW::Aptos diff --git a/src/Coin.cpp b/src/Coin.cpp index 29b91e2e9a7..943324470a7 100644 --- a/src/Coin.cpp +++ b/src/Coin.cpp @@ -66,6 +66,8 @@ #include "Sui/Entry.h" #include "Greenfield/Entry.h" #include "InternetComputer/Entry.h" +#include "NativeEvmos/Entry.h" +#include "NativeInjective/Entry.h" // end_of_coin_includes_marker_do_not_modify using namespace TW; @@ -123,6 +125,8 @@ TheOpenNetwork::Entry tonDP; Sui::Entry SuiDP; Greenfield::Entry GreenfieldDP; InternetComputer::Entry InternetComputerDP; +NativeEvmos::Entry NativeEvmosDP; +NativeInjective::Entry NativeInjectiveDP; // end_of_coin_dipatcher_declarations_marker_do_not_modify CoinEntry* coinDispatcher(TWCoinType coinType) { @@ -182,6 +186,8 @@ CoinEntry* coinDispatcher(TWCoinType coinType) { case TWBlockchainSui: entry = &SuiDP; break; case TWBlockchainGreenfield: entry = &GreenfieldDP; break; case TWBlockchainInternetComputer: entry = &InternetComputerDP; break; + case TWBlockchainNativeEvmos: entry = &NativeEvmosDP; break; + case TWBlockchainNativeInjective: entry = &NativeInjectiveDP; break; // end_of_coin_dipatcher_switch_marker_do_not_modify default: entry = nullptr; break; diff --git a/src/CoinEntry.cpp b/src/CoinEntry.cpp index 56ba53c3f95..f8ce744377b 100644 --- a/src/CoinEntry.cpp +++ b/src/CoinEntry.cpp @@ -32,101 +32,4 @@ byte getFromPrefixPkhOrDefault(const PrefixVariant &prefix, TWCoinType coin) { return TW::p2pkhPrefix(coin); } -bool validateAddressRust(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) { - if (!std::holds_alternative(addressPrefix)) { - throw std::invalid_argument("`Rust::tw_any_address_is_valid_bech32`, `Rust::tw_any_address_is_valid_ss58` are not supported yet"); - } - - Rust::TWStringWrapper addressStr = address; - return Rust::tw_any_address_is_valid(addressStr.get(), static_cast(coin)); -} - -std::string normalizeAddressRust(TWCoinType coin, const std::string& address) { - Rust::TWStringWrapper addressStr = address; - - auto anyAddress = Rust::wrapTWAnyAddress( - Rust::tw_any_address_create_with_string(addressStr.get(), static_cast(coin))); - if (!anyAddress) { - return {}; - } - - Rust::TWStringWrapper normalized = Rust::tw_any_address_description(anyAddress.get()); - return normalized.toStringOrDefault(); -} - -std::string deriveAddressRust(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) { - if (!std::holds_alternative(addressPrefix)) { - throw std::invalid_argument("`Rust::tw_any_address_create_bech32_with_public_key`, " - "`Rust::tw_any_address_create_ss58_with_public_key`, " - "`Rust::tw_any_address_create_with_public_key_filecoin_address_type` are not supported yet"); - } - - auto *twPublicKeyRaw = Rust::tw_public_key_create_with_data(publicKey.bytes.data(), - publicKey.bytes.size(), - static_cast(publicKey.type)); - auto twPublicKey = Rust::wrapTWPublicKey(twPublicKeyRaw); - if (!twPublicKey) { - return {}; - } - - auto *anyAddressRaw = Rust::tw_any_address_create_with_public_key_derivation(twPublicKey.get(), - static_cast(coin), - static_cast(derivation)); - auto anyAddress = Rust::wrapTWAnyAddress(anyAddressRaw); - if (!anyAddress) { - return {}; - } - - Rust::TWStringWrapper derivedAddress = Rust::tw_any_address_description(anyAddress.get()); - return derivedAddress.toStringOrDefault(); -} - -Data addressToDataRust(TWCoinType coin, const std::string& address) { - Rust::TWStringWrapper addressStr = address; - - auto anyAddress = Rust::wrapTWAnyAddress( - Rust::tw_any_address_create_with_string(addressStr.get(), static_cast(coin))); - if (!anyAddress) { - return {}; - } - - Rust::TWDataWrapper data = Rust::tw_any_address_data(anyAddress.get()); - return data.toDataOrDefault(); -} - -void signRust(const Data& dataIn, TWCoinType coin, Data& dataOut) { - Rust::TWDataWrapper input = Rust::tw_data_create_with_bytes(dataIn.data(), dataIn.size()); - Rust::TWDataWrapper output = Rust::tw_any_signer_sign(input.get(), static_cast(coin)); - - dataOut = output.toDataOrDefault(); -} - -Data preImageHashesRust(TWCoinType coin, const Data& dataIn) { - Rust::TWDataWrapper input = dataIn; - Rust::TWDataWrapper output = Rust::tw_transaction_compiler_pre_image_hashes(static_cast(coin), input.get()); - - return output.toDataOrDefault(); -} - -void compileRust( - TWCoinType coin, - const Data& dataIn, - const std::vector& signatures, - const std::vector& publicKeys, - Data& dataOut -) { - Rust::TWDataWrapper input = dataIn; - - std::vector publicKeysData; - for (const auto& publicKey : publicKeys) { - publicKeysData.push_back(publicKey.bytes); - } - - Rust::TWDataVectorWrapper signaturesVec = signatures; - Rust::TWDataVectorWrapper publicKeysVec = publicKeysData; - - Rust::TWDataWrapper output = Rust::tw_transaction_compiler_compile(static_cast(coin), input.get(), signaturesVec.get(), publicKeysVec.get()); - dataOut = output.toDataOrDefault(); -} - } // namespace TW diff --git a/src/CoinEntry.h b/src/CoinEntry.h index fec4bd8f265..c8b0b81bbaf 100644 --- a/src/CoinEntry.h +++ b/src/CoinEntry.h @@ -83,33 +83,6 @@ void signTemplate(const Data& dataIn, Data& dataOut) { dataOut.insert(dataOut.end(), serializedOut.begin(), serializedOut.end()); } -// In each coin's Entry.cpp that is implemented in Rust, this function calls `tw_any_address_is_valid*`. -bool validateAddressRust(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix); - -// In each coin's Entry.cpp that is implemented in Rust, this function calls `tw_any_address_create_with_string*`. -std::string normalizeAddressRust(TWCoinType coin, const std::string& address); - -// In each coin's Entry.cpp that is implemented in Rust, this function calls `tw_any_address_create_with_public_key*`. -std::string deriveAddressRust(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix); - -// In each coin's Entry.cpp that is implemented in Rust, this function calls `tw_any_address_create_with_string*`. -Data addressToDataRust(TWCoinType coin, const std::string& address); - -// In each coin's Entry.cpp that is implemented in Rust, this function calls `tw_any_signer_sign`. -// Note: use output parameter to avoid unneeded copies -void signRust(const Data& dataIn, TWCoinType coin, Data& dataOut); - -// In each coin's Entry.cpp that is implemented in Rust, this function calls `tw_transaction_compiler_pre_image_hashes`. -Data preImageHashesRust(TWCoinType coin, const Data& dataIn); - -// In each coin's Entry.cpp that is implemented in Rust, this function calls `tw_transaction_compiler_compile`. -// Note: use output parameter to avoid unneeded copies -void compileRust(TWCoinType coin, - const Data& dataIn, - const std::vector& signatures, - const std::vector& publicKeys, - Data& dataOut); - // Note: use output parameter to avoid unneeded copies template void planTemplate(const Data& dataIn, Data& dataOut) { diff --git a/src/Cosmos/Entry.cpp b/src/Cosmos/Entry.cpp index 01b06421f25..2886108b0d9 100644 --- a/src/Cosmos/Entry.cpp +++ b/src/Cosmos/Entry.cpp @@ -5,72 +5,35 @@ // file LICENSE at the root of the source code distribution tree. #include "Entry.h" -#include #include "Address.h" -#include "Signer.h" + +#include +#include +#include using namespace TW; using namespace std; namespace TW::Cosmos { -bool Entry::validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const { - if (auto* hrp = std::get_if(&addressPrefix); hrp) { - return Address::isValid(address, *hrp); - } - return Address::isValid(coin, address); -} +// TODO call `signRustJSON` when it's done. +string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { + auto input = Proto::SigningInput(); + google::protobuf::util::JsonStringToMessage(json, &input); + input.set_private_key(key.data(), key.size()); -std::string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, const PrefixVariant& addressPrefix) const { - if (std::holds_alternative(addressPrefix)) { - const std::string hrp = std::get(addressPrefix); - if (!hrp.empty()) { - return Address(hrp, publicKey, coin).string(); - } - } - return Address(coin, publicKey).string(); -} + auto inputData = data(input.SerializeAsString()); + Data dataOut; + sign(coin, inputData, dataOut); -Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { - Address addr; - if (!Address::decode(address, addr)) { - return Data(); + if (dataOut.empty()) { + return {}; } - return addr.getKeyHash(); -} - -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { - auto input = Proto::SigningInput(); - input.ParseFromArray(dataIn.data(), (int)dataIn.size()); - auto serializedOut = Signer::sign(input, coin).SerializeAsString(); - dataOut.insert(dataOut.end(), serializedOut.begin(), serializedOut.end()); -} -string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { - return Signer::signJSON(json, key, coin); -} - -Data Entry::preImageHashes(TWCoinType coin, const Data& txInputData) const { - return txCompilerTemplate( - txInputData, [&coin](const auto& input, auto& output) { - auto pkVec = Data(input.public_key().begin(), input.public_key().end()); - auto preimage = Signer().signaturePreimage(input, pkVec, coin); - auto isEvmCosmosChain = [coin]() { - return coin == TWCoinTypeNativeInjective || coin == TWCoinTypeNativeEvmos || coin == TWCoinTypeNativeCanto; - }; - auto imageHash = isEvmCosmosChain() ? Hash::keccak256(preimage) : Hash::sha256(preimage); - output.set_data(preimage.data(), preimage.size()); - output.set_data_hash(imageHash.data(), imageHash.size()); - }); -} + Proto::SigningOutput output; + output.ParseFromArray(dataOut.data(), static_cast(dataOut.size())); -void Entry::compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const { - dataOut = txCompilerSingleTemplate( - txInputData, signatures, publicKeys, - [coin](const auto& input, auto& output, const auto& signature, const auto& publicKey) { - auto signedTx = Signer().encodeTransaction(input, signature, publicKey, coin); - output.set_serialized(signedTx.data(), signedTx.size()); - }); + return output.json(); } } // namespace TW::Cosmos diff --git a/src/Cosmos/Entry.h b/src/Cosmos/Entry.h index 4bc3261f9c5..66b40cb1403 100644 --- a/src/Cosmos/Entry.h +++ b/src/Cosmos/Entry.h @@ -6,23 +6,17 @@ #pragma once -#include "../CoinEntry.h" +#include "rust/RustCoinEntry.h" namespace TW::Cosmos { /// Entry point for implementation of Cosmos coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry : public CoinEntry { +class Entry : public Rust::RustCoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const final; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const override; - Data addressToData(TWCoinType coin, const std::string& address) const final; - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const override; + ~Entry() override = default; bool supportsJSONSigning() const final { return true; } - std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const override; - - Data preImageHashes(TWCoinType coin, const Data& txInputData) const override; - void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const override; + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const final; }; } // namespace TW::Cosmos diff --git a/src/Cosmos/JsonSerialization.cpp b/src/Cosmos/JsonSerialization.cpp deleted file mode 100644 index d08c4fa362a..00000000000 --- a/src/Cosmos/JsonSerialization.cpp +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "JsonSerialization.h" -#include "ProtobufSerialization.h" -#include "../Cosmos/Address.h" -#include "../proto/Cosmos.pb.h" -#include "Base64.h" -#include "PrivateKey.h" - -using namespace TW; - -namespace TW::Cosmos::Json { - -using json = nlohmann::json; -using string = std::string; - -const string TYPE_PREFIX_MSG_SEND = "cosmos-sdk/MsgSend"; -const string TYPE_PREFIX_MSG_DELEGATE = "cosmos-sdk/MsgDelegate"; -const string TYPE_PREFIX_MSG_UNDELEGATE = "cosmos-sdk/MsgUndelegate"; -const string TYPE_PREFIX_MSG_REDELEGATE = "cosmos-sdk/MsgBeginRedelegate"; -const string TYPE_PREFIX_MSG_SET_WITHDRAW_ADDRESS = "cosmos-sdk/MsgSetWithdrawAddress"; -const string TYPE_PREFIX_MSG_WITHDRAW_REWARD = "cosmos-sdk/MsgWithdrawDelegationReward"; -const string TYPE_PREFIX_PUBLIC_KEY = "tendermint/PubKeySecp256k1"; -const string TYPE_EVMOS_PREFIX_PUBLIC_KEY = "ethermint/PubKeyEthSecp256k1"; -const string TYPE_PREFIX_WASM_MSG_EXECUTE = "wasm/MsgExecuteContract"; - -static inline std::string coinTypeToPrefixPublicKey(TWCoinType coin) noexcept { - if (coin == TWCoinTypeNativeEvmos) { - return TYPE_EVMOS_PREFIX_PUBLIC_KEY; - } - return TYPE_PREFIX_PUBLIC_KEY; -} - -static string broadcastMode(Proto::BroadcastMode mode) { - switch (mode) { - case Proto::BroadcastMode::BLOCK: - return "block"; - case Proto::BroadcastMode::ASYNC: - return "async"; - default: return "sync"; - } -} - -static json broadcastJSON(json& j, Proto::BroadcastMode mode) { - return { - {"tx", j}, - {"mode", broadcastMode(mode)} - }; -} - -static json amountJSON(const Proto::Amount& amount) { - return { - {"amount", amount.amount()}, - {"denom", amount.denom()} - }; -} - -static json amountsJSON(const ::google::protobuf::RepeatedPtrField& amounts) { - json j = json::array(); - for (auto& amount : amounts) { - j.push_back(amountJSON(amount)); - } - return j; -} - -static json feeJSON(const Proto::Fee& fee) { - json js = json::array(); - - for (auto& amount : fee.amounts()) { - js.push_back(amountJSON(amount)); - } - - return { - {"amount", js}, - {"gas", std::to_string(fee.gas())} - }; -} - -static json messageSend(const Proto::Message_Send& message) { - auto typePrefix = message.type_prefix().empty() ? TYPE_PREFIX_MSG_SEND : message.type_prefix(); - - return { - {"type", typePrefix}, - {"value", { - {"amount", amountsJSON(message.amounts())}, - {"from_address", message.from_address()}, - {"to_address", message.to_address()} - }} - }; -} - -static json messageDelegate(const Proto::Message_Delegate& message) { - auto typePrefix = message.type_prefix().empty() ? TYPE_PREFIX_MSG_DELEGATE : message.type_prefix(); - - return { - {"type", typePrefix}, - {"value", { - {"amount", amountJSON(message.amount())}, - {"delegator_address", message.delegator_address()}, - {"validator_address", message.validator_address()} - }} - }; -} - -static json messageUndelegate(const Proto::Message_Undelegate& message) { - auto typePrefix = message.type_prefix().empty() ? TYPE_PREFIX_MSG_UNDELEGATE : message.type_prefix(); - - return { - {"type", typePrefix}, - {"value", { - {"amount", amountJSON(message.amount())}, - {"delegator_address", message.delegator_address()}, - {"validator_address", message.validator_address()} - }} - }; -} - -static json messageRedelegate(const Proto::Message_BeginRedelegate& message) { - auto typePrefix = message.type_prefix().empty() ? TYPE_PREFIX_MSG_REDELEGATE : message.type_prefix(); - - return { - {"type", typePrefix}, - {"value", { - {"amount", amountJSON(message.amount())}, - {"delegator_address", message.delegator_address()}, - {"validator_src_address", message.validator_src_address()}, - {"validator_dst_address", message.validator_dst_address()}, - }} - }; -} - -static json messageWithdrawReward(const Proto::Message_WithdrawDelegationReward& message) { - auto typePrefix = message.type_prefix().empty() ? TYPE_PREFIX_MSG_WITHDRAW_REWARD : message.type_prefix(); - - return { - {"type", typePrefix}, - {"value", { - {"delegator_address", message.delegator_address()}, - {"validator_address", message.validator_address()} - }} - }; -} - -static json messageSetWithdrawAddress(const Proto::Message_SetWithdrawAddress& message) { - auto typePrefix = message.type_prefix().empty() ? TYPE_PREFIX_MSG_SET_WITHDRAW_ADDRESS : message.type_prefix(); - - return { - {"type", typePrefix}, - {"value", { - {"delegator_address", message.delegator_address()}, - {"withdraw_address", message.withdraw_address()} - }} - }; -} - - -// This method not only support token transfer, but also support all other types of contract call. -// https://docs.terra.money/Tutorials/Smart-contracts/Manage-CW20-tokens.html#interacting-with-cw20-contract -static json messageExecuteContract(const Proto::Message_ExecuteContract& message) { - auto typePrefix = message.type_prefix().empty() ? TYPE_PREFIX_WASM_MSG_EXECUTE : message.type_prefix(); - - return { - {"type", typePrefix}, - {"value", { - {"sender", message.sender()}, - {"contract", message.contract()}, - {"execute_msg", message.execute_msg()}, - {"coins", amountsJSON(message.coins())} - }} - }; -} - -json messageWasmTerraTransfer(const Proto::Message_WasmTerraExecuteContractTransfer& msg) { - return { - {"type", TYPE_PREFIX_WASM_MSG_EXECUTE}, - {"value", - { - {"sender", msg.sender_address()}, - {"contract", msg.contract_address()}, - {"execute_msg", Protobuf::wasmTerraExecuteTransferPayload(msg)}, - {"coins", json::array()} // used in case you are sending native tokens along with this message - } - } - }; -} - -static json messageRawJSON(const Proto::Message_RawJSON& message) { - return { - {"type", message.type()}, - {"value", json::parse(message.value())}, - }; -} - -static json messagesJSON(const Proto::SigningInput& input) { - json j = json::array(); - for (auto& msg : input.messages()) { - if (msg.has_send_coins_message()) { - j.push_back(messageSend(msg.send_coins_message())); - } else if (msg.has_stake_message()) { - j.push_back(messageDelegate(msg.stake_message())); - } else if (msg.has_unstake_message()) { - j.push_back(messageUndelegate(msg.unstake_message())); - } else if (msg.has_withdraw_stake_reward_message()) { - j.push_back(messageWithdrawReward(msg.withdraw_stake_reward_message())); - } else if (msg.has_set_withdraw_address_message()) { - j.push_back(messageSetWithdrawAddress(msg.set_withdraw_address_message())); - } else if (msg.has_restake_message()) { - j.push_back(messageRedelegate(msg.restake_message())); - } else if (msg.has_raw_json_message()) { - j.push_back(messageRawJSON(msg.raw_json_message())); - } else if (msg.has_execute_contract_message()) { - j.push_back(messageExecuteContract(msg.execute_contract_message())); - } else if (msg.has_transfer_tokens_message()) { - assert(false); // not suppored, use protobuf serialization - return json::array(); - } else if ((msg.has_wasm_terra_execute_contract_transfer_message())) { - j.push_back(messageWasmTerraTransfer(msg.wasm_terra_execute_contract_transfer_message())); - } else if (msg.has_transfer_tokens_message() || msg.has_wasm_terra_execute_contract_generic()) { - assert(false); // not supported, use protobuf serialization - return json::array(); - } - } - return j; -} - -json signatureJSON(const Data& signature, const Data& pubkey, TWCoinType coin) { - return { - {"pub_key", { - {"type", coinTypeToPrefixPublicKey(coin)}, - {"value", Base64::encode(pubkey)} - }}, - {"signature", Base64::encode(signature)} - }; -} - -json signaturePreimageJSON(const Proto::SigningInput& input) { - return { - {"account_number", std::to_string(input.account_number())}, - {"chain_id", input.chain_id()}, - {"fee", feeJSON(input.fee())}, - {"memo", input.memo()}, - {"msgs", messagesJSON(input)}, - {"sequence", std::to_string(input.sequence())} - }; -} - - -json transactionJSON(const Proto::SigningInput& input, const PublicKey& publicKey, const Data& signature, TWCoinType coin) { - json tx = { - {"fee", feeJSON(input.fee())}, - {"memo", input.memo()}, - {"msg", messagesJSON(input)}, - {"signatures", json::array({ - signatureJSON(signature, Data(publicKey.bytes), coin) - })} - }; - return broadcastJSON(tx, input.mode()); -} - -json transactionJSON(const Proto::SigningInput& input, const Data& signature, TWCoinType coin) { - auto privateKey = PrivateKey(input.private_key()); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - - return transactionJSON(input, publicKey, signature, coin); -} - -std::string buildJsonTxRaw(const Proto::SigningInput& input, const PublicKey& publicKey, const Data& signature, TWCoinType coin) { - return transactionJSON(input, publicKey, signature, coin).dump(); -} - -} // namespace TW::Cosmos diff --git a/src/Cosmos/JsonSerialization.h b/src/Cosmos/JsonSerialization.h deleted file mode 100644 index c8458bddf6b..00000000000 --- a/src/Cosmos/JsonSerialization.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "Data.h" -#include "PublicKey.h" -#include "../proto/Cosmos.pb.h" -#include -#include - -extern const std::string TYPE_PREFIX_MSG_SEND; -extern const std::string TYPE_PREFIX_MSG_TRANSFER; -extern const std::string TYPE_PREFIX_MSG_DELEGATE; -extern const std::string TYPE_PREFIX_MSG_UNDELEGATE; -extern const std::string TYPE_PREFIX_MSG_REDELEGATE; -extern const std::string TYPE_PREFIX_MSG_SET_WITHDRAW_ADDRESS; -extern const std::string TYPE_PREFIX_MSG_WITHDRAW_REWARD; -extern const std::string TYPE_PREFIX_PUBLIC_KEY; - -namespace TW::Cosmos::Json { - -using string = std::string; -using json = nlohmann::json; - -json signaturePreimageJSON(const Proto::SigningInput& input); -json transactionJSON(const Proto::SigningInput& input, const Data& signature, TWCoinType coin); -json transactionJSON(const Proto::SigningInput& input, const PublicKey& publicKey, const Data& signature, TWCoinType coin); -std::string buildJsonTxRaw(const Proto::SigningInput& input, const PublicKey& publicKey, const Data& signature, TWCoinType coin); -json signatureJSON(const Data& signature, const Data& pubkey, TWCoinType coin); - -} // namespace TW::Cosmos::json diff --git a/src/Cosmos/Protobuf/tx.proto b/src/Cosmos/Protobuf/tx.proto index b7973cda4bf..74e17ba25d7 100644 --- a/src/Cosmos/Protobuf/tx.proto +++ b/src/Cosmos/Protobuf/tx.proto @@ -1,5 +1,5 @@ syntax = "proto3"; -package cosmos; +package cosmos.tx.v1beta1; // Src: https://github.com/cosmos/cosmos-sdk/blob/master/proto/cosmos/tx/v1beta1/tx.proto diff --git a/src/Cosmos/ProtobufSerialization.cpp b/src/Cosmos/ProtobufSerialization.cpp deleted file mode 100644 index 657fe49cbca..00000000000 --- a/src/Cosmos/ProtobufSerialization.cpp +++ /dev/null @@ -1,577 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "ProtobufSerialization.h" -#include "JsonSerialization.h" -#include "../proto/Cosmos.pb.h" -#include "Protobuf/coin.pb.h" -#include "Protobuf/bank_tx.pb.h" -#include "Protobuf/cosmwasm_wasm_v1_tx.pb.h" -#include "Protobuf/distribution_tx.pb.h" -#include "Protobuf/staking_tx.pb.h" -#include "Protobuf/authz_tx.pb.h" -#include "Protobuf/tx.pb.h" -#include "Protobuf/stride_liquid_staking.pb.h" -#include "Protobuf/gov_tx.pb.h" -#include "Protobuf/crypto_secp256k1_keys.pb.h" -#include "Protobuf/terra_wasm_v1beta1_tx.pb.h" -#include "Protobuf/ibc_applications_transfer_tx.pb.h" -#include "Protobuf/thorchain_bank_tx.pb.h" -#include "Protobuf/ethermint_keys.pb.h" -#include "Protobuf/injective_keys.pb.h" - -#include "PrivateKey.h" -#include "Data.h" -#include "Hash.h" -#include "Base64.h" -#include "uint256.h" - -using namespace TW; - -namespace TW::Cosmos::Protobuf { - -namespace internal { - -// Some of the Cosmos blockchains use different public key types for address deriving and transaction signing. -// `registry.json` contains the public key required to derive an address, -// while this function prepares the given public key to use it for transaction signing/compiling. -inline PublicKey preparePublicKey(const PublicKey& publicKey, TWCoinType coin) { - return coin == TWCoinTypeNativeEvmos ? publicKey.compressed() : publicKey; -} - -} // namespace internal - -using json = nlohmann::json; -using string = std::string; -const auto ProtobufAnyNamespacePrefix = ""; // to override default 'type.googleapis.com' - -static string broadcastMode(Proto::BroadcastMode mode) { - switch (mode) { - case Proto::BroadcastMode::BLOCK: - return "BROADCAST_MODE_BLOCK"; - case Proto::BroadcastMode::ASYNC: - return "BROADCAST_MODE_ASYNC"; - default: return "BROADCAST_MODE_SYNC"; - } -} - -static json broadcastJSON(std::string data, Proto::BroadcastMode mode) { - return { - {"tx_bytes", data}, - {"mode", broadcastMode(mode)} - }; -} - -cosmos::base::v1beta1::Coin convertCoin(const Proto::Amount& amount) { - cosmos::base::v1beta1::Coin coin; - coin.set_denom(amount.denom()); - coin.set_amount(amount.amount()); - return coin; -} - -// Convert messages from external protobuf to internal protobuf -google::protobuf::Any convertMessage(const Proto::Message& msg) { - google::protobuf::Any any; - switch (msg.message_oneof_case()) { - case Proto::Message::kSendCoinsMessage: - { - assert(msg.has_send_coins_message()); - const auto& send = msg.send_coins_message(); - auto msgSend = cosmos::bank::v1beta1::MsgSend(); - msgSend.set_from_address(send.from_address()); - msgSend.set_to_address(send.to_address()); - for (auto i = 0; i < send.amounts_size(); ++i) { - *msgSend.add_amount() = convertCoin(send.amounts(i)); - } - any.PackFrom(msgSend, ProtobufAnyNamespacePrefix); - return any; - } - - case Proto::Message::kTransferTokensMessage: - { - assert(msg.has_transfer_tokens_message()); - const auto& transfer = msg.transfer_tokens_message(); - auto msgTransfer = ibc::applications::transfer::v1::MsgTransfer(); - msgTransfer.set_source_port(transfer.source_port()); - msgTransfer.set_source_channel(transfer.source_channel()); - *msgTransfer.mutable_token() = convertCoin(transfer.token()); - msgTransfer.set_sender(transfer.sender()); - msgTransfer.set_receiver(transfer.receiver()); - msgTransfer.mutable_timeout_height()->set_revision_number(transfer.timeout_height().revision_number()); - msgTransfer.mutable_timeout_height()->set_revision_height(transfer.timeout_height().revision_height()); - msgTransfer.set_timeout_timestamp(transfer.timeout_timestamp()); - any.PackFrom(msgTransfer, ProtobufAnyNamespacePrefix); - return any; - } - - case Proto::Message::kStakeMessage: - { - assert(msg.has_stake_message()); - const auto& stake = msg.stake_message(); - auto msgDelegate = cosmos::staking::v1beta1::MsgDelegate(); - msgDelegate.set_delegator_address(stake.delegator_address()); - msgDelegate.set_validator_address(stake.validator_address()); - *msgDelegate.mutable_amount() = convertCoin(stake.amount()); - any.PackFrom(msgDelegate, ProtobufAnyNamespacePrefix); - return any; - } - - case Proto::Message::kUnstakeMessage: - { - assert(msg.has_unstake_message()); - const auto& unstake = msg.unstake_message(); - auto msgUndelegate = cosmos::staking::v1beta1::MsgUndelegate(); - msgUndelegate.set_delegator_address(unstake.delegator_address()); - msgUndelegate.set_validator_address(unstake.validator_address()); - *msgUndelegate.mutable_amount() = convertCoin(unstake.amount()); - any.PackFrom(msgUndelegate, ProtobufAnyNamespacePrefix); - return any; - } - - case Proto::Message::kRestakeMessage: - { - assert(msg.has_restake_message()); - const auto& restake = msg.restake_message(); - auto msgRedelegate = cosmos::staking::v1beta1::MsgBeginRedelegate(); - msgRedelegate.set_delegator_address(restake.delegator_address()); - msgRedelegate.set_validator_src_address(restake.validator_src_address()); - msgRedelegate.set_validator_dst_address(restake.validator_dst_address()); - *msgRedelegate.mutable_amount() = convertCoin(restake.amount()); - any.PackFrom(msgRedelegate, ProtobufAnyNamespacePrefix); - return any; - } - - case Proto::Message::kWithdrawStakeRewardMessage: - { - assert(msg.has_withdraw_stake_reward_message()); - const auto& withdraw = msg.withdraw_stake_reward_message(); - auto msgWithdraw = cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward(); - msgWithdraw.set_delegator_address(withdraw.delegator_address()); - msgWithdraw.set_validator_address(withdraw.validator_address()); - any.PackFrom(msgWithdraw, ProtobufAnyNamespacePrefix); - return any; - } - - case Proto::Message::kSetWithdrawAddressMessage: - { - assert(msg.has_set_withdraw_address_message()); - const auto& withdraw = msg.set_withdraw_address_message(); - auto msgWithdraw = cosmos::distribution::v1beta1::MsgSetWithdrawAddress(); - msgWithdraw.set_delegator_address(withdraw.delegator_address()); - msgWithdraw.set_withdraw_address(withdraw.withdraw_address()); - any.PackFrom(msgWithdraw, ProtobufAnyNamespacePrefix); - return any; - } - - case Proto::Message::kExecuteContractMessage: - { - assert(msg.has_execute_contract_message()); - const auto& execContract = msg.execute_contract_message(); - auto executeContractMsg = terra::wasm::v1beta1::MsgExecuteContract(); - executeContractMsg.set_sender(execContract.sender()); - executeContractMsg.set_contract(execContract.contract()); - executeContractMsg.set_execute_msg(execContract.execute_msg()); - for (auto i = 0; i < execContract.coins_size(); ++i){ - *executeContractMsg.add_coins() = convertCoin(execContract.coins(i)); - } - any.PackFrom(executeContractMsg, ProtobufAnyNamespacePrefix); - return any; - } - - case Proto::Message::kWasmTerraExecuteContractTransferMessage: - { - assert(msg.has_wasm_terra_execute_contract_transfer_message()); - const auto& wasmExecute = msg.wasm_terra_execute_contract_transfer_message(); - auto msgExecute = terra::wasm::v1beta1::MsgExecuteContract(); - msgExecute.set_sender(wasmExecute.sender_address()); - msgExecute.set_contract(wasmExecute.contract_address()); - const std::string payload = wasmTerraExecuteTransferPayload(wasmExecute).dump(); - msgExecute.set_execute_msg(payload); - any.PackFrom(msgExecute, ProtobufAnyNamespacePrefix); - return any; - } - - case Proto::Message::kWasmTerraExecuteContractSendMessage: - { - assert(msg.has_wasm_terra_execute_contract_send_message()); - const auto& wasmExecute = msg.wasm_terra_execute_contract_send_message(); - auto msgExecute = terra::wasm::v1beta1::MsgExecuteContract(); - msgExecute.set_sender(wasmExecute.sender_address()); - msgExecute.set_contract(wasmExecute.contract_address()); - const std::string payload = wasmTerraExecuteSendPayload(wasmExecute).dump(); - msgExecute.set_execute_msg(payload); - any.PackFrom(msgExecute, ProtobufAnyNamespacePrefix); - return any; - } - - case Proto::Message::kThorchainSendMessage: - { - assert(msg.has_thorchain_send_message()); - const auto& send = msg.thorchain_send_message(); - auto msgSend =types::MsgSend(); - msgSend.set_from_address(send.from_address()); - msgSend.set_to_address(send.to_address()); - for (auto i = 0; i < send.amounts_size(); ++i) { - *msgSend.add_amount() = convertCoin(send.amounts(i)); - } - any.PackFrom(msgSend, ProtobufAnyNamespacePrefix); - return any; - } - - case Proto::Message::kWasmTerraExecuteContractGeneric: { - assert(msg.has_wasm_terra_execute_contract_generic()); - const auto& wasmExecute = msg.wasm_terra_execute_contract_generic(); - auto msgExecute = terra::wasm::v1beta1::MsgExecuteContract(); - msgExecute.set_sender(wasmExecute.sender_address()); - msgExecute.set_contract(wasmExecute.contract_address()); - msgExecute.set_execute_msg(wasmExecute.execute_msg()); - - for (auto i = 0; i < wasmExecute.coins_size(); ++i) { - *msgExecute.add_coins() = convertCoin(wasmExecute.coins(i)); - } - any.PackFrom(msgExecute, ProtobufAnyNamespacePrefix); - return any; - } - - case Proto::Message::kWasmExecuteContractTransferMessage: - { - assert(msg.has_wasm_execute_contract_transfer_message()); - const auto& wasmExecute = msg.wasm_execute_contract_transfer_message(); - auto msgExecute = cosmwasm::wasm::v1::MsgExecuteContract(); - msgExecute.set_sender(wasmExecute.sender_address()); - msgExecute.set_contract(wasmExecute.contract_address()); - const std::string payload = wasmExecuteTransferPayload(wasmExecute).dump(); - msgExecute.set_msg(payload); - any.PackFrom(msgExecute, ProtobufAnyNamespacePrefix); - return any; - } - - case Proto::Message::kWasmExecuteContractSendMessage: - { - assert(msg.has_wasm_execute_contract_send_message()); - const auto& wasmExecute = msg.wasm_execute_contract_send_message(); - auto msgExecute = cosmwasm::wasm::v1::MsgExecuteContract(); - msgExecute.set_sender(wasmExecute.sender_address()); - msgExecute.set_contract(wasmExecute.contract_address()); - const std::string payload = wasmExecuteSendPayload(wasmExecute).dump(); - msgExecute.set_msg(payload); - any.PackFrom(msgExecute, ProtobufAnyNamespacePrefix); - return any; - } - - case Proto::Message::kWasmExecuteContractGeneric: { - assert(msg.has_wasm_execute_contract_generic()); - const auto& wasmExecute = msg.wasm_execute_contract_generic(); - auto msgExecute = cosmwasm::wasm::v1::MsgExecuteContract(); - msgExecute.set_sender(wasmExecute.sender_address()); - msgExecute.set_contract(wasmExecute.contract_address()); - msgExecute.set_msg(wasmExecute.execute_msg()); - - for (auto i = 0; i < wasmExecute.coins_size(); ++i) { - *msgExecute.add_funds() = convertCoin(wasmExecute.coins(i)); - } - any.PackFrom(msgExecute, ProtobufAnyNamespacePrefix); - return any; - } - - case Proto::Message::kAuthGrant: { - assert(msg.has_auth_grant()); - const auto& authGrant = msg.auth_grant(); - auto msgAuthGrant = cosmos::authz::v1beta1::MsgGrant(); - msgAuthGrant.set_grantee(authGrant.grantee()); - msgAuthGrant.set_granter(authGrant.granter()); - auto* mtAuth = msgAuthGrant.mutable_grant()->mutable_authorization(); - // There is multiple grant possibilities, but we add support staking/compounding only for now. - switch (authGrant.grant_type_case()) { - case Proto::Message_AuthGrant::kGrantStake: - mtAuth->PackFrom(authGrant.grant_stake(), ProtobufAnyNamespacePrefix); - mtAuth->set_type_url("/cosmos.staking.v1beta1.StakeAuthorization"); - break; - case Proto::Message_AuthGrant::GRANT_TYPE_NOT_SET: - break; - } - auto* mtExp = msgAuthGrant.mutable_grant()->mutable_expiration(); - mtExp->set_seconds(authGrant.expiration()); - any.PackFrom(msgAuthGrant, ProtobufAnyNamespacePrefix); - return any; - } - - case Proto::Message::kAuthRevoke: { - assert(msg.has_auth_revoke()); - const auto& authRevoke = msg.auth_revoke(); - auto msgAuthRevoke = cosmos::authz::v1beta1::MsgRevoke(); - msgAuthRevoke.set_granter(authRevoke.granter()); - msgAuthRevoke.set_grantee(authRevoke.grantee()); - msgAuthRevoke.set_msg_type_url(authRevoke.msg_type_url()); - any.PackFrom(msgAuthRevoke, ProtobufAnyNamespacePrefix); - return any; - } - case Proto::Message::kMsgVote: { - assert(msg.has_msg_vote()); - const auto& vote = msg.msg_vote(); - auto msgVote = cosmos::gov::v1beta1::MsgVote(); - // LCOV_EXCL_START - switch (vote.option()) { - case Proto::Message_VoteOption__UNSPECIFIED: - msgVote.set_option(cosmos::gov::v1beta1::VOTE_OPTION_UNSPECIFIED); - break; - case Proto::Message_VoteOption_YES: - msgVote.set_option(cosmos::gov::v1beta1::VOTE_OPTION_YES); - break; - case Proto::Message_VoteOption_ABSTAIN: - msgVote.set_option(cosmos::gov::v1beta1::VOTE_OPTION_ABSTAIN); - break; - case Proto::Message_VoteOption_NO: - msgVote.set_option(cosmos::gov::v1beta1::VOTE_OPTION_NO); - break; - case Proto::Message_VoteOption_NO_WITH_VETO: - msgVote.set_option(cosmos::gov::v1beta1::VOTE_OPTION_NO_WITH_VETO); - break; - case Proto::Message_VoteOption_Message_VoteOption_INT_MIN_SENTINEL_DO_NOT_USE_: - msgVote.set_option(cosmos::gov::v1beta1::VoteOption_INT_MIN_SENTINEL_DO_NOT_USE_); - break; - case Proto::Message_VoteOption_Message_VoteOption_INT_MAX_SENTINEL_DO_NOT_USE_: - msgVote.set_option(cosmos::gov::v1beta1::VoteOption_INT_MAX_SENTINEL_DO_NOT_USE_); - break; - } - // LCOV_EXCL_STOP - msgVote.set_proposal_id(vote.proposal_id()); - msgVote.set_voter(vote.voter()); - any.PackFrom(msgVote, ProtobufAnyNamespacePrefix); - return any; - } - case Proto::Message::kMsgStrideLiquidStakingStake: { - const auto& stride_liquid_staking_stake = msg.msg_stride_liquid_staking_stake(); - auto liquid_staking_msg = stride::stakeibc::MsgLiquidStake(); - liquid_staking_msg.set_creator(stride_liquid_staking_stake.creator()); - liquid_staking_msg.set_amount(stride_liquid_staking_stake.amount()); - liquid_staking_msg.set_host_denom(stride_liquid_staking_stake.host_denom()); - any.PackFrom(liquid_staking_msg, ProtobufAnyNamespacePrefix); - return any; - } - case Proto::Message::kMsgStrideLiquidStakingRedeem: { - const auto& stride_liquid_staking_redeem = msg.msg_stride_liquid_staking_redeem(); - auto liquid_staking_msg = stride::stakeibc::MsgRedeemStake(); - liquid_staking_msg.set_creator(stride_liquid_staking_redeem.creator()); - liquid_staking_msg.set_amount(stride_liquid_staking_redeem.amount()); - liquid_staking_msg.set_receiver(stride_liquid_staking_redeem.receiver()); - liquid_staking_msg.set_host_zone(stride_liquid_staking_redeem.host_zone()); - any.PackFrom(liquid_staking_msg, ProtobufAnyNamespacePrefix); - return any; - } - case Proto::Message::kThorchainDepositMessage: { - assert(msg.has_thorchain_deposit_message()); - const auto& deposit = msg.thorchain_deposit_message(); - types::MsgDeposit msgDeposit; - msgDeposit.set_memo(deposit.memo()); - msgDeposit.set_signer(deposit.signer()); - for (auto i = 0; i < deposit.coins_size(); ++i) { - auto* coin = msgDeposit.add_coins(); - auto originalAsset = deposit.coins(i).asset(); - coin->mutable_asset()->set_chain(originalAsset.chain()); - coin->mutable_asset()->set_symbol(originalAsset.symbol()); - coin->mutable_asset()->set_ticker(originalAsset.ticker()); - coin->mutable_asset()->set_synth(originalAsset.synth()); - coin->set_amount(deposit.coins(i).amount()); - coin->set_decimals(deposit.coins(i).decimals()); - } - any.PackFrom(msgDeposit, ProtobufAnyNamespacePrefix); - - return any; - } - - default: - throw std::invalid_argument(std::string("Message not supported ") + std::to_string(msg.message_oneof_case())); - } -} - -std::string buildProtoTxBody(const Proto::SigningInput& input) { - if (input.messages_size() >= 1 && input.messages(0).has_sign_direct_message()) { - return input.messages(0).sign_direct_message().body_bytes(); - } - - if (input.messages_size() < 1) { - throw std::invalid_argument("No message found"); - } - assert(input.messages_size() >= 1); - auto txBody = cosmos::TxBody(); - for (auto i = 0; i < input.messages_size(); ++i) { - const auto msgAny = convertMessage(input.messages(i)); - *txBody.add_messages() = msgAny; - } - txBody.set_memo(input.memo()); - txBody.set_timeout_height(0); - - return txBody.SerializeAsString(); -} - -std::string buildAuthInfo(const Proto::SigningInput& input, TWCoinType coin) { - // AuthInfo - const auto privateKey = PrivateKey(input.private_key()); - const auto publicKey = privateKey.getPublicKey(TWCoinTypePublicKeyType(coin)); - return buildAuthInfo(input, publicKey, coin); -} - -std::string buildAuthInfo(const Proto::SigningInput& input, const PublicKey& publicKey, TWCoinType coin) { - const auto pbk = internal::preparePublicKey(publicKey, coin); - - if (input.messages_size() >= 1 && input.messages(0).has_sign_direct_message()) { - return input.messages(0).sign_direct_message().auth_info_bytes(); - } - // AuthInfo - auto authInfo = cosmos::AuthInfo(); - auto* signerInfo = authInfo.add_signer_infos(); - - signerInfo->mutable_mode_info()->mutable_single()->set_mode(cosmos::signing::v1beta1::SIGN_MODE_DIRECT); - signerInfo->set_sequence(input.sequence()); - switch(coin) { - case TWCoinTypeNativeEvmos: { - auto pubKey = ethermint::crypto::v1::ethsecp256k1::PubKey(); - pubKey.set_key(pbk.bytes.data(), pbk.bytes.size()); - signerInfo->mutable_public_key()->PackFrom(pubKey, ProtobufAnyNamespacePrefix); - break; - } - case TWCoinTypeNativeInjective: { - auto pubKey = injective::crypto::v1beta1::ethsecp256k1::PubKey(); - pubKey.set_key(pbk.bytes.data(), pbk.bytes.size()); - signerInfo->mutable_public_key()->PackFrom(pubKey, ProtobufAnyNamespacePrefix); - break; - } - default: { - auto pubKey = cosmos::crypto::secp256k1::PubKey(); - pubKey.set_key(pbk.bytes.data(), pbk.bytes.size()); - signerInfo->mutable_public_key()->PackFrom(pubKey, ProtobufAnyNamespacePrefix); - } - } - - auto* fee = authInfo.mutable_fee(); - for (auto i = 0; i < input.fee().amounts_size(); ++i) { - *fee->add_amount() = convertCoin(input.fee().amounts(i)); - } - - fee->set_gas_limit(input.fee().gas()); - fee->set_payer(""); - fee->set_granter(""); - // tip is omitted - return authInfo.SerializeAsString(); -} - -Data buildSignature(const Proto::SigningInput& input, const std::string& serializedTxBody, const std::string& serializedAuthInfo, TWCoinType coin) { - // SignDoc Preimage - auto signDoc = cosmos::SignDoc(); - signDoc.set_body_bytes(serializedTxBody); - signDoc.set_auth_info_bytes(serializedAuthInfo); - signDoc.set_chain_id(input.chain_id()); - signDoc.set_account_number(input.account_number()); - const auto serializedSignDoc = signDoc.SerializeAsString(); - - Data hashToSign; - switch(coin) { - case TWCoinTypeNativeInjective: - case TWCoinTypeNativeEvmos: { - hashToSign = Hash::keccak256(serializedSignDoc); - break; - } - default: { - hashToSign = Hash::sha256(serializedSignDoc); - } - } - - const auto privateKey = PrivateKey(input.private_key()); - auto signedHash = privateKey.sign(hashToSign, TWCurveSECP256k1); - auto signature = Data(signedHash.begin(), signedHash.end() - 1); - return signature; -} - -std::string buildProtoTxRaw(const std::string& serializedTxBody, const std::string& serializedAuthInfo, const Data& signature) { - auto txRaw = cosmos::TxRaw(); - txRaw.set_body_bytes(serializedTxBody); - txRaw.set_auth_info_bytes(serializedAuthInfo); - *txRaw.add_signatures() = std::string(signature.begin(), signature.end()); - return txRaw.SerializeAsString(); -} - -std::string signaturePreimageProto(const Proto::SigningInput& input, const PublicKey& publicKey, TWCoinType coin) { - // SignDoc Preimage - const auto serializedTxBody = buildProtoTxBody(input); - const auto serializedAuthInfo = buildAuthInfo(input, publicKey, coin); - - auto signDoc = cosmos::SignDoc(); - signDoc.set_body_bytes(serializedTxBody); - signDoc.set_auth_info_bytes(serializedAuthInfo); - signDoc.set_chain_id(input.chain_id()); - signDoc.set_account_number(input.account_number()); - return signDoc.SerializeAsString(); -} - -std::string buildProtoTxRaw(const Proto::SigningInput& input, const PublicKey& publicKey, const Data& signature, TWCoinType coin) { - const auto serializedTxBody = buildProtoTxBody(input); - const auto serializedAuthInfo = buildAuthInfo(input, publicKey, coin); - - auto txRaw = cosmos::TxRaw(); - txRaw.set_body_bytes(serializedTxBody); - txRaw.set_auth_info_bytes(serializedAuthInfo); - *txRaw.add_signatures() = std::string(signature.begin(), signature.end()); - auto data = txRaw.SerializeAsString(); - return broadcastJSON(Base64::encode(Data(data.begin(), data.end())), input.mode()).dump(); -} - -std::string buildProtoTxJson(const Proto::SigningInput& input, const std::string& serializedTx) { - const string serializedBase64 = Base64::encode(TW::data(serializedTx)); - const json jsonSerialized = { - {"tx_bytes", serializedBase64}, - {"mode", broadcastMode(input.mode())} - }; - return jsonSerialized.dump(); -} - -json wasmExecuteTransferPayload(const Proto::Message_WasmExecuteContractTransfer& msg) { - return { - {"transfer", - { - {"amount", toString(load(data(msg.amount())))}, - {"recipient", msg.recipient_address()} - } - } - }; -} - -json wasmExecuteSendPayload(const Proto::Message_WasmExecuteContractSend& msg) { - return { - {"send", - { - {"amount", toString(load(data(msg.amount())))}, - {"contract", msg.recipient_contract_address()}, - {"msg", msg.msg()} - } - } - }; -} - -json wasmTerraExecuteTransferPayload(const Proto::Message_WasmTerraExecuteContractTransfer& msg) { - return { - {"transfer", - { - {"amount", toString(load(data(msg.amount())))}, - {"recipient", msg.recipient_address()} - } - } - }; -} - -json wasmTerraExecuteSendPayload(const Proto::Message_WasmTerraExecuteContractSend& msg) { - return { - {"send", - { - {"amount", toString(load(data(msg.amount())))}, - {"contract", msg.recipient_contract_address()}, - {"msg", msg.msg()} - } - } - }; -} - -} // namespace diff --git a/src/Cosmos/ProtobufSerialization.h b/src/Cosmos/ProtobufSerialization.h deleted file mode 100644 index 6eae28e431f..00000000000 --- a/src/Cosmos/ProtobufSerialization.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "Data.h" -#include "PublicKey.h" -#include "../proto/Cosmos.pb.h" - -#include -#include - -#include - -namespace TW::Cosmos::Protobuf { - -std::string buildProtoTxBody(const Proto::SigningInput& input); - -std::string buildAuthInfo(const Proto::SigningInput& input, TWCoinType coin); -std::string buildAuthInfo(const Proto::SigningInput& input, const PublicKey& publicKey, TWCoinType coin); - -std::string signaturePreimageProto(const Proto::SigningInput& input, const PublicKey& publicKey, TWCoinType coin); - -std::string buildProtoTxRaw(const Proto::SigningInput& input, const PublicKey& publicKey, const Data& signature, TWCoinType coin); -std::string buildProtoTxRaw(const std::string& serializedTxBody, const std::string& serializedAuthInfo, const Data& signature); -Data buildSignature(const Proto::SigningInput& input, const std::string& serializedTxBody, const std::string& serializedAuthInfo, TWCoinType coin); - -std::string buildProtoTxJson(const Proto::SigningInput& input, const std::string& serializedTx); - -nlohmann::json wasmExecuteSendPayload(const Proto::Message_WasmExecuteContractSend& msg); - -nlohmann::json wasmExecuteTransferPayload(const Proto::Message_WasmExecuteContractTransfer& msg); - -nlohmann::json wasmExecuteSendPayload(const Proto::Message_WasmExecuteContractSend& msg); - -nlohmann::json wasmTerraExecuteTransferPayload(const Proto::Message_WasmTerraExecuteContractTransfer& msg); - -nlohmann::json wasmTerraExecuteSendPayload(const Proto::Message_WasmTerraExecuteContractSend& msg); - -} // namespace TW::Cosmos::protobuf diff --git a/src/Cosmos/Signer.cpp b/src/Cosmos/Signer.cpp deleted file mode 100644 index bceb5b1bd45..00000000000 --- a/src/Cosmos/Signer.cpp +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Signer.h" -#include "JsonSerialization.h" -#include "ProtobufSerialization.h" - -#include "PrivateKey.h" -#include "Data.h" -#include - -namespace TW::Cosmos { - -Proto::SigningOutput Signer::sign(const Proto::SigningInput& input, TWCoinType coin) noexcept { - switch (input.signing_mode()) { - case Proto::JSON: - return signJsonSerialized(input, coin); - - case Proto::Protobuf: - default: - return signProtobuf(input, coin); - } -} - -std::string Signer::signaturePreimage(const Proto::SigningInput& input, const Data& publicKey, TWCoinType coin) const { - switch (input.signing_mode()) { - case Proto::JSON: - return Json::signaturePreimageJSON(input).dump(); - - case Proto::Protobuf: - default: - auto pbk = PublicKey(publicKey, TWCoinTypePublicKeyType(coin)); - return Protobuf::signaturePreimageProto(input, pbk, coin); - } -} - -Proto::SigningOutput Signer::signJsonSerialized(const Proto::SigningInput& input, TWCoinType coin) noexcept { - auto key = PrivateKey(input.private_key()); - auto preimage = Json::signaturePreimageJSON(input).dump(); - auto hash = Hash::sha256(preimage); - auto signedHash = key.sign(hash, TWCurveSECP256k1); - - auto output = Proto::SigningOutput(); - auto signature = Data(signedHash.begin(), signedHash.end() - 1); - auto txJson = Json::transactionJSON(input, signature, coin); - output.set_json(txJson.dump()); - output.set_signature(signature.data(), signature.size()); - output.set_serialized(""); - output.set_error_message(""); - output.set_signature_json(txJson["tx"]["signatures"].dump()); - return output; -} - -Proto::SigningOutput Signer::signProtobuf(const Proto::SigningInput& input, TWCoinType coin) noexcept { - using namespace Protobuf; - using namespace Json; - try { - const auto serializedTxBody = buildProtoTxBody(input); - const auto serializedAuthInfo = buildAuthInfo(input, coin); - const auto signature = buildSignature(input, serializedTxBody, serializedAuthInfo, coin); - auto serializedTxRaw = buildProtoTxRaw(serializedTxBody, serializedAuthInfo, signature); - - auto output = Proto::SigningOutput(); - const std::string jsonSerialized = Protobuf::buildProtoTxJson(input, serializedTxRaw); - auto publicKey = PrivateKey(input.private_key()).getPublicKey(TWPublicKeyTypeSECP256k1); - auto signatures = nlohmann::json::array({signatureJSON(signature, publicKey.bytes, coin)}); - output.set_serialized(jsonSerialized); - output.set_signature(signature.data(), signature.size()); - output.set_json(""); - output.set_error_message(""); - output.set_signature_json(signatures.dump()); - return output; - } catch (const std::exception& ex) { - auto output = Proto::SigningOutput(); - output.set_error(Common::Proto::Error_internal); - output.set_error_message(std::string("Error: ") + ex.what()); - return output; - } -} - -std::string Signer::signJSON(const std::string& json, const Data& key, TWCoinType coin) { - auto input = Proto::SigningInput(); - google::protobuf::util::JsonStringToMessage(json, &input); - input.set_private_key(key.data(), key.size()); - auto output = Signer::sign(input, coin); - return output.json(); -} - -std::string Signer::encodeTransaction(const Proto::SigningInput& input, const Data& signature, const PublicKey& publicKey, TWCoinType coin) const { - switch (input.signing_mode()) { - case Proto::JSON: - return Json::buildJsonTxRaw(input, publicKey, signature, coin); - - case Proto::Protobuf: - default: - return Protobuf::buildProtoTxRaw(input, publicKey, signature, coin); - } -} -} // namespace TW::Cosmos diff --git a/src/Cosmos/Signer.h b/src/Cosmos/Signer.h deleted file mode 100644 index b63836fc2e7..00000000000 --- a/src/Cosmos/Signer.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "Data.h" -#include "PublicKey.h" -#include "../proto/Cosmos.pb.h" - -#include - -namespace TW::Cosmos { - -/// Helper class that performs Cosmos transaction signing. -class Signer { - public: - /// Signs a Proto::SigningInput transaction - static Proto::SigningOutput sign(const Proto::SigningInput& input, TWCoinType coin) noexcept; - - std::string encodeTransaction(const Proto::SigningInput& input, const Data& signature, const PublicKey& publicKey, TWCoinType coin) const; - std::string signaturePreimage(const Proto::SigningInput& input, const Data& publicKey, TWCoinType coin) const; - - /// Signs a Proto::SigningInput transaction, using Json serialization - static Proto::SigningOutput signJsonSerialized(const Proto::SigningInput& input, TWCoinType coin) noexcept; - - /// Signs a Proto::SigningInput transaction, using binary Protobuf serialization - static Proto::SigningOutput signProtobuf(const Proto::SigningInput& input, TWCoinType coin) noexcept; - - /// Signs a json Proto::SigningInput with private key - static std::string signJSON(const std::string& json, const Data& key, TWCoinType coin); -}; - -} // namespace TW::Cosmos diff --git a/src/Ethereum/Entry.cpp b/src/Ethereum/Entry.cpp index 85c679900c4..78834004477 100644 --- a/src/Ethereum/Entry.cpp +++ b/src/Ethereum/Entry.cpp @@ -14,30 +14,8 @@ namespace TW::Ethereum { -using namespace std; - -bool Entry::validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const { - return validateAddressRust(coin, address, addressPrefix); -} - -string Entry::normalizeAddress(TWCoinType coin, const string& address) const { - return normalizeAddressRust(coin, address); -} - -std::string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const { - return deriveAddressRust(coin, publicKey, derivation, addressPrefix); -} - -Data Entry::addressToData(TWCoinType coin, const std::string& address) const { - return addressToDataRust(coin, address); -} - -void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { - signRust(dataIn, coin, dataOut); -} - // TODO call `signRustJSON` when it's done. -string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { +std::string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { auto input = Proto::SigningInput(); google::protobuf::util::JsonStringToMessage(json, &input); input.set_private_key(key.data(), key.size()); @@ -56,12 +34,4 @@ string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json return hex(output.encoded()); } -Data Entry::preImageHashes(TWCoinType coin, const Data& txInputData) const { - return preImageHashesRust(coin, txInputData); -} - -void Entry::compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const { - compileRust(coin, txInputData, signatures, publicKeys, dataOut); -} - } // namespace TW::Ethereum diff --git a/src/Ethereum/Entry.h b/src/Ethereum/Entry.h index 4d3c909df02..f2a5203a0d2 100644 --- a/src/Ethereum/Entry.h +++ b/src/Ethereum/Entry.h @@ -6,24 +6,16 @@ #pragma once -#include "../CoinEntry.h" +#include "rust/RustCoinEntry.h" namespace TW::Ethereum { /// Entry point for Ethereum and Ethereum-fork coins. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry final : public CoinEntry { +class Entry : public Rust::RustCoinEntry { public: - bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const override; - std::string normalizeAddress(TWCoinType coin, const std::string& address) const override; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const override; - Data addressToData(TWCoinType coin, const std::string& address) const override; - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const override; - bool supportsJSONSigning() const override { return true; } - std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const override; - - Data preImageHashes(TWCoinType coin, const Data& txInputData) const override; - void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const override; + bool supportsJSONSigning() const final { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const final; }; } // namespace TW::Ethereum diff --git a/src/Greenfield/ProtobufSerialization.cpp b/src/Greenfield/ProtobufSerialization.cpp index 82da978ccdf..45fdd59647f 100644 --- a/src/Greenfield/ProtobufSerialization.cpp +++ b/src/Greenfield/ProtobufSerialization.cpp @@ -88,7 +88,7 @@ SigningResult ProtobufSerialization::encodeTxProtobuf(const Proto::Signing } const auto serializedTxBody = txBodyResult.payload(); - auto txRaw = cosmos::TxRaw(); + auto txRaw = cosmos::tx::v1beta1::TxRaw(); txRaw.set_body_bytes(serializedTxBody.data(), serializedTxBody.size()); txRaw.set_auth_info_bytes(serializedAuthInfo.data(), serializedAuthInfo.size()); *txRaw.add_signatures() = std::string(signature.begin(), signature.end()); @@ -100,7 +100,7 @@ SigningResult ProtobufSerialization::encodeTxProtobuf(const Proto::Signing } SigningResult ProtobufSerialization::encodeTxBody(const Proto::SigningInput& input) { - cosmos::TxBody txBody; + cosmos::tx::v1beta1::TxBody txBody; // At this moment, we support only one message. if (input.messages_size() != 1) { @@ -120,7 +120,7 @@ SigningResult ProtobufSerialization::encodeTxBody(const Proto::SigningInpu Data ProtobufSerialization::encodeAuthInfo(const Proto::SigningInput& input, const PublicKey& publicKey) { // AuthInfo - auto authInfo = cosmos::AuthInfo(); + auto authInfo = cosmos::tx::v1beta1::AuthInfo(); auto* signerInfo = authInfo.add_signer_infos(); // At this moment, we support Eip712 signing mode only. diff --git a/src/InternetComputer/Entry.cpp b/src/InternetComputer/Entry.cpp deleted file mode 100644 index a4d7e6bff38..00000000000 --- a/src/InternetComputer/Entry.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Entry.h" - -namespace TW::InternetComputer { - -// Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. - -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - return validateAddressRust(coin, address, addressPrefix); -} - -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - return deriveAddressRust(coin, publicKey, derivation, addressPrefix); -} - -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { - signRust(dataIn, coin, dataOut); -} - -TW::Data Entry::preImageHashes(TWCoinType coin, const Data& txInputData) const { - return preImageHashesRust(coin, txInputData); -} - -void Entry::compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const { - compileRust(coin, txInputData, signatures, publicKeys, dataOut); -} - -} // namespace TW::InternetComputer diff --git a/src/InternetComputer/Entry.h b/src/InternetComputer/Entry.h index e997bddb2d6..0d9081d60d1 100644 --- a/src/InternetComputer/Entry.h +++ b/src/InternetComputer/Entry.h @@ -6,22 +6,13 @@ #pragma once -#include "../CoinEntry.h" +#include "rust/RustCoinEntry.h" namespace TW::InternetComputer { /// Entry point for implementation of InternetComputer coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry final : public CoinEntry { -public: - bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - // normalizeAddress(): implement this if needed, e.g. Ethereum address is EIP55 checksummed - // plan(): implement this if the blockchain is UTXO based - - Data preImageHashes(TWCoinType coin, const Data& txInputData) const; - void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const; +class Entry final : public Rust::RustCoinEntry { }; } // namespace TW::InternetComputer diff --git a/src/NativeEvmos/Entry.h b/src/NativeEvmos/Entry.h new file mode 100644 index 00000000000..bc44a9620e2 --- /dev/null +++ b/src/NativeEvmos/Entry.h @@ -0,0 +1,18 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Cosmos/Entry.h" + +namespace TW::NativeEvmos { + +/// Entry point for implementation of NativeEvmos coin. +/// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file +class Entry final : public Cosmos::Entry { +}; + +} // namespace TW::NativeEvmos diff --git a/src/NativeInjective/Entry.h b/src/NativeInjective/Entry.h new file mode 100644 index 00000000000..1a55e88540b --- /dev/null +++ b/src/NativeInjective/Entry.h @@ -0,0 +1,18 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Cosmos/Entry.h" + +namespace TW::NativeInjective { + +/// Entry point for implementation of NativeEvmos coin. +/// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file +class Entry final : public Cosmos::Entry { +}; + +} // namespace TW::NativeInjective diff --git a/src/Ronin/Entry.cpp b/src/Ronin/Entry.cpp deleted file mode 100644 index 7889d28c786..00000000000 --- a/src/Ronin/Entry.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Entry.h" - -#include "Ethereum/Entry.h" -#include "HexCoding.h" - -namespace TW::Ronin { - -bool Entry::validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const { - return validateAddressRust(coin, address, addressPrefix); -} - -std::string Entry::normalizeAddress(TWCoinType coin, const std::string& address) const { - return normalizeAddressRust(coin, address); -} - -std::string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const { - return deriveAddressRust(coin, publicKey, derivation, addressPrefix); -} - -Data Entry::addressToData(TWCoinType coin, const std::string& address) const { - return addressToDataRust(coin, address); -} - -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { - signRust(dataIn, coin, dataOut); -} - -// TODO call `signRustJSON` when it's done. -std::string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { - return Ethereum::Entry().signJSON(coin, json, key); -} - -} // namespace TW::Ronin diff --git a/src/Ronin/Entry.h b/src/Ronin/Entry.h index 356c1940d7c..b9b5ab2c2d9 100644 --- a/src/Ronin/Entry.h +++ b/src/Ronin/Entry.h @@ -6,20 +6,12 @@ #pragma once -#include "../CoinEntry.h" +#include "Ethereum/Entry.h" namespace TW::Ronin { /// Entry point for Ronin (EVM side chain) -class Entry final : public CoinEntry { -public: - bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string normalizeAddress(TWCoinType coin, const std::string& address) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; - Data addressToData(TWCoinType coin, const std::string& address) const; - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - bool supportsJSONSigning() const { return true; } - std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; +class Entry final : public Ethereum::Entry { }; } // namespace TW::Ronin diff --git a/src/THORChain/Entry.cpp b/src/THORChain/Entry.cpp deleted file mode 100644 index ed96e9e3d9d..00000000000 --- a/src/THORChain/Entry.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Entry.h" - -#include "Signer.h" -#include "../proto/Cosmos.pb.h" - -using namespace std; - -namespace TW::THORChain { - -// Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. - -void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { - auto input = Cosmos::Proto::SigningInput(); - input.ParseFromArray(dataIn.data(), (int)dataIn.size()); - auto serializedOut = Signer::sign(input).SerializeAsString(); - dataOut.insert(dataOut.end(), serializedOut.begin(), serializedOut.end()); -} - -string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { - return Signer::signJSON(json, key); -} - -} // namespace TW::THORChain diff --git a/src/THORChain/Entry.h b/src/THORChain/Entry.h index aeb3707cd1f..551b26be00b 100644 --- a/src/THORChain/Entry.h +++ b/src/THORChain/Entry.h @@ -14,9 +14,6 @@ namespace TW::THORChain { /// Entry point for implementation of THORChain coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file class Entry final : public Cosmos::Entry { -public: - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; }; } // namespace TW::THORChain diff --git a/src/THORChain/Signer.cpp b/src/THORChain/Signer.cpp deleted file mode 100644 index 7554e242013..00000000000 --- a/src/THORChain/Signer.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Signer.h" -#include "../Cosmos/Signer.h" -#include "../proto/Cosmos.pb.h" - -#include -#include - -using namespace TW; - -namespace TW::THORChain { -const std::string TYPE_PREFIX_MSG_SEND = "thorchain/MsgSend"; - -Cosmos::Proto::SigningOutput Signer::sign(Cosmos::Proto::SigningInput& input) noexcept { - for (auto i = 0; i < input.messages_size(); ++i) { - if (input.messages(i).has_send_coins_message()) { - input.mutable_messages(i)->mutable_send_coins_message()->set_type_prefix(TYPE_PREFIX_MSG_SEND); - } - } - return Cosmos::Signer::sign(input, TWCoinTypeTHORChain); -} - -std::string Signer::signJSON(const std::string& json, const Data& key) { - auto input = Cosmos::Proto::SigningInput(); - google::protobuf::util::JsonStringToMessage(json, &input); - input.set_private_key(key.data(), key.size()); - auto output = Signer::sign(input); - return output.json(); -} - -} // namespace TW::THORChain diff --git a/src/THORChain/Signer.h b/src/THORChain/Signer.h deleted file mode 100644 index 10a5214c784..00000000000 --- a/src/THORChain/Signer.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "Data.h" -#include "../proto/Cosmos.pb.h" -#include - -namespace TW::THORChain { - -/// Helper class that performs THORChain transaction signing. -class Signer { - public: - /// Signs a Proto::SigningInput transaction - static Cosmos::Proto::SigningOutput sign(Cosmos::Proto::SigningInput& input) noexcept; - /// Signs a json Proto::SigningInput with private key - static std::string signJSON(const std::string& json, const Data& key); -}; - -} // namespace TW::THORChain diff --git a/src/proto/Cosmos.proto b/src/proto/Cosmos.proto index 264e811a8d9..323ac93bb08 100644 --- a/src/proto/Cosmos.proto +++ b/src/proto/Cosmos.proto @@ -117,14 +117,6 @@ message Message { string type_prefix = 3; } - message ExecuteContract { - string sender = 1; - string contract = 2; - string execute_msg = 3; - repeated Amount coins = 4; - string type_prefix = 5; - } - // transfer within wasm/MsgExecuteContract, used by Terra Classic message WasmTerraExecuteContractTransfer { // sender address @@ -224,6 +216,7 @@ message Message { } // execute within wasm/MsgExecuteContract + // TODO replaces `ExecuteContract`. message WasmExecuteContractGeneric { // sender address string sender_address = 1; @@ -353,7 +346,6 @@ message Message { WasmTerraExecuteContractTransfer wasm_terra_execute_contract_transfer_message = 8; WasmTerraExecuteContractSend wasm_terra_execute_contract_send_message = 9; THORChainSend thorchain_send_message = 10; - ExecuteContract execute_contract_message = 11; WasmTerraExecuteContractGeneric wasm_terra_execute_contract_generic = 12; WasmExecuteContractTransfer wasm_execute_contract_transfer_message = 13; WasmExecuteContractSend wasm_execute_contract_send_message = 14; diff --git a/src/rust/RustCoinEntry.cpp b/src/rust/RustCoinEntry.cpp new file mode 100644 index 00000000000..497edc57644 --- /dev/null +++ b/src/rust/RustCoinEntry.cpp @@ -0,0 +1,114 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "RustCoinEntry.h" +#include "Wrapper.h" + +namespace TW::Rust { + +bool RustCoinEntry::validateAddress(TWCoinType coin, const std::string &address, const PrefixVariant &addressPrefix) const { + Rust::TWStringWrapper addressStr = address; + + if (std::holds_alternative(addressPrefix)) { + return Rust::tw_any_address_is_valid(addressStr.get(), static_cast(coin)); + } else if (const auto* hrpPrefix = std::get_if(&addressPrefix); hrpPrefix) { + Rust::TWStringWrapper hrpStr = std::string(*hrpPrefix); + return Rust::tw_any_address_is_valid_bech32(addressStr.get(), static_cast(coin), hrpStr.get()); + } else { + throw std::invalid_argument("`Rust::tw_any_address_is_valid_ss58`, `Rust::tw_any_address_create_with_public_key_filecoin_address_type` are not supported yet"); + } +} + +std::string RustCoinEntry::normalizeAddress(TWCoinType coin, const std::string& address) const { + Rust::TWStringWrapper addressStr = address; + + // `CoinEntry::normalizeAddress` is used when a `TWAnyAddress` has been created already, therefore validated. + auto anyAddress = Rust::wrapTWAnyAddress( + Rust::tw_any_address_create_with_string_unchecked(addressStr.get(), static_cast(coin))); + if (!anyAddress) { + return {}; + } + + Rust::TWStringWrapper normalized = Rust::tw_any_address_description(anyAddress.get()); + return normalized.toStringOrDefault(); +} + +std::string RustCoinEntry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const { + auto *twPublicKeyRaw = Rust::tw_public_key_create_with_data(publicKey.bytes.data(), + publicKey.bytes.size(), + static_cast(publicKey.type)); + auto twPublicKey = Rust::wrapTWPublicKey(twPublicKeyRaw); + if (!twPublicKey) { + return {}; + } + + Rust::TWAnyAddress* anyAddressRaw = nullptr; + if (std::holds_alternative(addressPrefix)) { + anyAddressRaw = Rust::tw_any_address_create_with_public_key_derivation(twPublicKey.get(), + static_cast(coin), + static_cast(derivation)); + } else if (const auto* hrpPrefix = std::get_if(&addressPrefix); hrpPrefix) { + Rust::TWStringWrapper hrpStr = std::string(*hrpPrefix); + anyAddressRaw = Rust::tw_any_address_create_bech32_with_public_key(twPublicKey.get(), + static_cast(coin), + hrpStr.get()); + } else { + throw std::invalid_argument("`Rust::tw_any_address_is_valid_ss58`, `Rust::tw_any_address_create_with_public_key_filecoin_address_type` are not supported yet"); + } + + auto anyAddress = Rust::wrapTWAnyAddress(anyAddressRaw); + if (!anyAddress) { + return {}; + } + + Rust::TWStringWrapper derivedAddress = Rust::tw_any_address_description(anyAddress.get()); + return derivedAddress.toStringOrDefault(); +} + +Data RustCoinEntry::addressToData(TWCoinType coin, const std::string& address) const { + Rust::TWStringWrapper addressStr = address; + + // `CoinEntry::normalizeAddress` is used when a `TWAnyAddress` has been created already, therefore validated. + auto anyAddress = Rust::wrapTWAnyAddress( + Rust::tw_any_address_create_with_string_unchecked(addressStr.get(), static_cast(coin))); + if (!anyAddress) { + return {}; + } + + Rust::TWDataWrapper data = Rust::tw_any_address_data(anyAddress.get()); + return data.toDataOrDefault(); +} + +void RustCoinEntry::sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const { + Rust::TWDataWrapper input = Rust::tw_data_create_with_bytes(dataIn.data(), dataIn.size()); + Rust::TWDataWrapper output = Rust::tw_any_signer_sign(input.get(), static_cast(coin)); + + dataOut = output.toDataOrDefault(); +} + +Data RustCoinEntry::preImageHashes(TWCoinType coin, const Data& txInputData) const { + Rust::TWDataWrapper input = txInputData; + Rust::TWDataWrapper output = Rust::tw_transaction_compiler_pre_image_hashes(static_cast(coin), input.get()); + + return output.toDataOrDefault(); +} + +void RustCoinEntry::compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const { + Rust::TWDataWrapper input = txInputData; + + std::vector publicKeysData; + for (const auto& publicKey : publicKeys) { + publicKeysData.push_back(publicKey.bytes); + } + + Rust::TWDataVectorWrapper signaturesVec = signatures; + Rust::TWDataVectorWrapper publicKeysVec = publicKeysData; + + Rust::TWDataWrapper output = Rust::tw_transaction_compiler_compile(static_cast(coin), input.get(), signaturesVec.get(), publicKeysVec.get()); + dataOut = output.toDataOrDefault(); +} + +} // namespace TW::Rust diff --git a/src/rust/RustCoinEntry.h b/src/rust/RustCoinEntry.h new file mode 100644 index 00000000000..edcf926d9e2 --- /dev/null +++ b/src/rust/RustCoinEntry.h @@ -0,0 +1,26 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "CoinEntry.h" + +namespace TW::Rust { + +class RustCoinEntry : public CoinEntry { +public: + ~RustCoinEntry() noexcept override = default; + bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const override; + std::string normalizeAddress(TWCoinType coin, const std::string& address) const override; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const override; + Data addressToData(TWCoinType coin, const std::string& address) const override; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const override; + + Data preImageHashes(TWCoinType coin, const Data& txInputData) const override; + void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const override; +}; + +} // namespace TW::Rust diff --git a/swift/Tests/Blockchains/CosmosTests.swift b/swift/Tests/Blockchains/CosmosTests.swift index 6903c9487ee..7a7c906d5d0 100644 --- a/swift/Tests/Blockchains/CosmosTests.swift +++ b/swift/Tests/Blockchains/CosmosTests.swift @@ -102,7 +102,7 @@ class CosmosSignerTests: XCTestCase { let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .cosmos) - XCTAssertJSONEqual(output.serialized, "{\"tx_bytes\": \"CvgBCvUBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQS0gEKLWNvc21vczEzazBxMGw3bGcya3IzMmt2dDdseTIzNnBwbGR5OHY5ZHp3aDNnZBItY29zbW9zMWZzN2x1MjhoeDVtOWFrbTdycDBjMjQyMmNuOHIyZjdndXJ1amhmGnIKaAoqL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuU3Rha2VBdXRob3JpemF0aW9uEjoSNgo0Y29zbW9zdmFsb3BlcjFnanR2bHk5bGVsNnpza3Z3dHZsZzV2aHdwdTljOXdhdzdzeHp3eCABEgYI4LD6pgYSZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA/fcQw1hCVUx904t+kCXTiiziaLIY8lyssu1ENfzaN1KEgQKAggBGAUSEwoNCgV1YXRvbRIEMjQxOBCp8wUaQIFyfuijGKf87Hz61ZqxasfLI1PZnNge4RDq/tRyB/tZI6p80iGRqHecoV6+84EQkc9GTlNRQOSlApRCsivT9XI=\", \"mode\": \"BROADCAST_MODE_BLOCK\"}") + XCTAssertJSONEqual(output.serialized, "{\"tx_bytes\": \"CvgBCvUBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQS0gEKLWNvc21vczEzazBxMGw3bGcya3IzMmt2dDdseTIzNnBwbGR5OHY5ZHp3aDNnZBItY29zbW9zMWZzN2x1MjhoeDVtOWFrbTdycDBjMjQyMmNuOHIyZjdndXJ1amhmGnIKaAoqL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuU3Rha2VBdXRob3JpemF0aW9uEjogARI2CjRjb3Ntb3N2YWxvcGVyMWdqdHZseTlsZWw2enNrdnd0dmxnNXZod3B1OWM5d2F3N3N4end4EgYI4LD6pgYSZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA/fcQw1hCVUx904t+kCXTiiziaLIY8lyssu1ENfzaN1KEgQKAggBGAUSEwoNCgV1YXRvbRIEMjQxOBCp8wUaQEAN1nIfDawlHnep2bNEm14w+g7tYybJJT3htcGVS6s9D7va3ed1OUEIk9LZoc3G//VenJ+KLw26SRVBaRukgVI=\", \"mode\": \"BROADCAST_MODE_BLOCK\"}") XCTAssertEqual(output.errorMessage, "") } diff --git a/tests/chains/Cosmos/CryptoOrg/SignerTests.cpp b/tests/chains/Cosmos/CryptoOrg/SignerTests.cpp index fa6793727c9..f71f90c5cff 100644 --- a/tests/chains/Cosmos/CryptoOrg/SignerTests.cpp +++ b/tests/chains/Cosmos/CryptoOrg/SignerTests.cpp @@ -5,11 +5,11 @@ // file LICENSE at the root of the source code distribution tree. #include "proto/Cosmos.pb.h" -#include "Cosmos/Signer.h" #include "Cosmos/Address.h" #include "HexCoding.h" #include "PublicKey.h" #include "TestUtilities.h" +#include "TrustWalletCore/TWAnySigner.h" #include #include @@ -72,7 +72,8 @@ TEST(CryptoorgSigner, SignTx_DDCCE4) { auto privateKey = parse_hex("200e439e39cf1aad465ee3de6166247f914cbc0f823fc2dd48bf16dcd556f39d"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Cosmos::Signer::sign(input, TWCoinTypeCryptoOrg); + auto output = Cosmos::Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeCryptoOrg); assertJSONEqual(output.json(), R"( { @@ -125,7 +126,7 @@ TEST(CryptoorgSigner, SignJson) { auto inputJson = R"({"accountNumber":"125798","chainId":"crypto-org-chain-mainnet-1","fee":{"amounts":[{"denom":"basecro","amount":"5000"}],"gas":"200000"},"messages":[{"sendCoinsMessage":{"fromAddress":"cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0","toAddress":"cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus","amounts":[{"denom":"basecro","amount":"100000000"}]}}]})"; auto privateKey = parse_hex("200e439e39cf1aad465ee3de6166247f914cbc0f823fc2dd48bf16dcd556f39d"); - auto outputJson = Cosmos::Signer::signJSON(inputJson, privateKey, TWCoinTypeCryptoOrg); + auto outputJson = TW::anySignJSON(TWCoinTypeCryptoOrg, inputJson, privateKey); assertJSONEqual(outputJson, R"( { diff --git a/tests/chains/Cosmos/Juno/TWAnySignerTests.cpp b/tests/chains/Cosmos/Juno/TWAnySignerTests.cpp index 13444945f39..dafa5aee10a 100644 --- a/tests/chains/Cosmos/Juno/TWAnySignerTests.cpp +++ b/tests/chains/Cosmos/Juno/TWAnySignerTests.cpp @@ -39,7 +39,9 @@ TEST(TWAnySignerJuno, Sign) { amountOfFee->set_amount("1000"); Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeCosmos); + ANY_SIGN(input, TWCoinTypeJuno); + + EXPECT_EQ(output.error(), Common::Proto::OK); // https://www.mintscan.io/juno/txs/3DCE6AAF19657BCF11D44FD6BE124D57B44E04CA34851DE0ECCE619F70ECC46F auto expectedJson = R"( diff --git a/tests/chains/Cosmos/NativeEvmos/TWCoinTypeTests.cpp b/tests/chains/Cosmos/NativeEvmos/TWCoinTypeTests.cpp index 822897296e8..2713fcbba7e 100644 --- a/tests/chains/Cosmos/NativeEvmos/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/NativeEvmos/TWCoinTypeTests.cpp @@ -20,7 +20,7 @@ TEST(TWEvmosCoinType, TWCoinTypeNativeEvmos) { auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeNativeEvmos)); ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeNativeEvmos), 18); - ASSERT_EQ(TWBlockchainCosmos, TWCoinTypeBlockchain(TWCoinTypeNativeEvmos)); + ASSERT_EQ(TWBlockchainNativeEvmos, TWCoinTypeBlockchain(TWCoinTypeNativeEvmos)); assertStringsEqual(symbol, "EVMOS"); assertStringsEqual(txUrl, "https://mintscan.io/evmos/txs/A16C211C83AD1E684DE46F694FAAC17D8465C864BD7385A81EC062CDE0638811"); diff --git a/tests/chains/Cosmos/NativeInjective/SignerTests.cpp b/tests/chains/Cosmos/NativeInjective/SignerTests.cpp index 46ed18809ba..a103ff8d09a 100644 --- a/tests/chains/Cosmos/NativeInjective/SignerTests.cpp +++ b/tests/chains/Cosmos/NativeInjective/SignerTests.cpp @@ -8,12 +8,10 @@ #include "Base64.h" #include "proto/Cosmos.pb.h" #include "Cosmos/Address.h" -#include "Cosmos/Signer.h" #include "TestUtilities.h" -#include +#include "TrustWalletCore/TWAnySigner.h" #include -#include namespace TW::Cosmos::nativeInjective::tests { @@ -46,7 +44,8 @@ TEST(NativeInjectiveSigner, Sign) { auto privateKey = parse_hex("9ee18daf8e463877aaf497282abc216852420101430482a28e246c179e2c5ef1"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeNativeInjective); + auto output = Cosmos::Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeNativeInjective); // https://www.mintscan.io/injective/txs/135DD2C4A1910E4334A9C0F15125DA992E724EBF23FEB9638FCB71218BB064A5 assertJSONEqual(output.serialized(), "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"Co8BCowBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmwKKmluajEzdTZnN3ZxZ3cwNzRtZ21mMnplMmNhZHp2a3o5c25sd2NydHE4YRIqaW5qMXhtcGtteHI0YXMwMGVtMjN0YzJ6Z211eXkyZ3I0aDN3Z2NsNnZkGhIKA2luahILMTAwMDAwMDAwMDASngEKfgp0Ci0vaW5qZWN0aXZlLmNyeXB0by52MWJldGExLmV0aHNlY3AyNTZrMS5QdWJLZXkSQwpBBFoMa4O4vZgn5QcnDK20mbfjqQlSRvaiITKB94PYd8mLJWdCdBsGOfMXdo/k9MJ2JmDCESKDp2hdgVUH3uMikXMSBAoCCAEYARIcChYKA2luahIPMTAwMDAwMDAwMDAwMDAwELDbBhpAx2vkplmzeK7n3puCFGPWhLd0l/ZC/CYkGl+stH+3S3hiCvIe7uwwMpUlNaSwvT8HwF1kNUp+Sx2m0Uo1x5xcFw==\"}"); diff --git a/tests/chains/Cosmos/NativeInjective/TWCoinTypeTests.cpp b/tests/chains/Cosmos/NativeInjective/TWCoinTypeTests.cpp index 0b152ca68be..f92a2f9ca7e 100644 --- a/tests/chains/Cosmos/NativeInjective/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/NativeInjective/TWCoinTypeTests.cpp @@ -28,7 +28,7 @@ TEST(TWNativeInjectiveCoinType, TWCoinType) { assertStringsEqual(name, "Native Injective"); assertStringsEqual(symbol, "INJ"); ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); - ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainCosmos); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainNativeInjective); ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); assertStringsEqual(chainId, "injective-1"); diff --git a/tests/chains/Cosmos/NativeInjective/TransactionCompilerTests.cpp b/tests/chains/Cosmos/NativeInjective/TransactionCompilerTests.cpp index a7591f1b786..163b19dfaf1 100644 --- a/tests/chains/Cosmos/NativeInjective/TransactionCompilerTests.cpp +++ b/tests/chains/Cosmos/NativeInjective/TransactionCompilerTests.cpp @@ -5,7 +5,6 @@ // file LICENSE at the root of the source code distribution tree. #include "Base64.h" -#include "Cosmos/Signer.h" #include "HexCoding.h" #include "proto/Cosmos.pb.h" #include "proto/TransactionCompiler.pb.h" @@ -95,8 +94,7 @@ TEST(NativeInjectiveCompiler, CompileWithSignatures) { EXPECT_EQ(output.error(), Common::Proto::OK); EXPECT_EQ(output.serialized(), expectedTx); - EXPECT_EQ(output.signature(), ""); - EXPECT_EQ(hex(output.signature()), ""); + EXPECT_EQ(hex(output.signature()), "f7a9ec0a521170bb5566ca973d3c73a1b69b162d99ce022059189991ec440637333394ff1c9e75fad84eb114393969f20989b036f1dfed28949e906dc0077421"); } } diff --git a/tests/chains/Cosmos/Osmosis/SignerTests.cpp b/tests/chains/Cosmos/Osmosis/SignerTests.cpp index d7935f190de..b0ae1832083 100644 --- a/tests/chains/Cosmos/Osmosis/SignerTests.cpp +++ b/tests/chains/Cosmos/Osmosis/SignerTests.cpp @@ -5,10 +5,10 @@ // file LICENSE at the root of the source code distribution tree. #include "Cosmos/Address.h" -#include "Cosmos/Signer.h" #include "HexCoding.h" #include "PublicKey.h" #include "proto/Cosmos.pb.h" +#include "TrustWalletCore/TWAnySigner.h" #include "TestUtilities.h" #include @@ -45,7 +45,8 @@ TEST(OsmosisSigner, SignTransfer_81B4) { auto privateKey = parse_hex("8bbec3772ddb4df68f3186440380c301af116d1422001c1877d6f5e4dba8c8af"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeOsmosis); + auto output = Cosmos::Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeOsmosis); // https://www.mintscan.io/osmosis/txs/81B4F01BDE72AF7FF4536E5D7E66EB218E9FC9ACAA7C5EB5DB237DD0595D5F5F // curl -H 'Content-Type: application/json' --data-binary '{"tx_bytes": "Co0B...rYVj", "mode": "BROADCAST_MODE_BLOCK"}' https://lcd-osmosis.keplr.app/cosmos/tx/v1beta1/txs diff --git a/tests/chains/Cosmos/ProtobufTests.cpp b/tests/chains/Cosmos/ProtobufTests.cpp deleted file mode 100644 index 009bf227afd..00000000000 --- a/tests/chains/Cosmos/ProtobufTests.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Base64.h" -#include "Cosmos/Address.h" -#include "Cosmos/Protobuf/authz_tx.pb.h" -#include "Cosmos/Protobuf/bank_tx.pb.h" -#include "Cosmos/Protobuf/tx.pb.h" -#include "Cosmos/ProtobufSerialization.h" -#include "Data.h" -#include "HexCoding.h" - -#include "Protobuf/Article.pb.h" -#include "TestUtilities.h" - -#include -#include - -#include - -namespace TW::Cosmos::tests { - -using json = nlohmann::json; - -TEST(CosmosProtobuf, SendMsg) { - auto msgSend = cosmos::bank::v1beta1::MsgSend(); - msgSend.set_from_address("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02"); - msgSend.set_to_address("cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573"); - auto coin = msgSend.add_amount(); - coin->set_denom("muon"); - coin->set_amount("1"); - - auto txBody = cosmos::TxBody(); - txBody.add_messages()->PackFrom(msgSend); - txBody.set_memo(""); - txBody.set_timeout_height(0); - - const auto serialized = data(txBody.SerializeAsString()); - EXPECT_EQ(hex(serialized), "0a9c010a2f747970652e676f6f676c65617069732e636f6d2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412690a2d636f736d6f733168736b366a727979716a6668703564686335357463396a74636b796778306570683664643032122d636f736d6f73317a743530617a7570616e716c66616d356166687633686578777975746e756b656834633537331a090a046d756f6e120131"); - EXPECT_EQ(Base64::encode(serialized), "CpwBCi90eXBlLmdvb2dsZWFwaXMuY29tL2Nvc21vcy5iYW5rLnYxYmV0YTEuTXNnU2VuZBJpCi1jb3Ntb3MxaHNrNmpyeXlxamZocDVkaGM1NXRjOWp0Y2t5Z3gwZXBoNmRkMDISLWNvc21vczF6dDUwYXp1cGFucWxmYW01YWZodjNoZXh3eXV0bnVrZWg0YzU3MxoJCgRtdW9uEgEx"); - - std::string json; - google::protobuf::util::MessageToJsonString(txBody, &json); - assertJSONEqual(json, R"({"messages":[{"@type":"type.googleapis.com/cosmos.bank.v1beta1.MsgSend","amount":[{"amount":"1","denom":"muon"}],"fromAddress":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","toAddress":"cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573"}]})"); -} - -TEST(CosmosProtobuf, ExecuteContractMsg) { - auto input = Proto::SigningInput(); - input.set_signing_mode(Proto::JSON); // obsolete - input.set_account_number(1037); - input.set_chain_id("gaia-13003"); - input.set_memo(""); - input.set_sequence(8); - - auto fromAddress = Address("cosmos", parse_hex("BC2DA90C84049370D1B7C528BC164BC588833F21")); - auto toAddress = Address("cosmos", parse_hex("12E8FE8B81ECC1F4F774EA6EC8DF267138B9F2D9")); - - auto msg = input.add_messages(); - auto& message = *msg->mutable_execute_contract_message(); - message.set_sender(fromAddress.string()); - message.set_contract(toAddress.string()); - message.set_execute_msg("transfer"); - auto* coin = message.add_coins(); - coin->set_denom("muon"); - coin->set_amount("1"); - - auto body = Protobuf::buildProtoTxBody(input); - - EXPECT_EQ(hex(body), "0a9d010a262f74657272612e7761736d2e763162657461312e4d736745786563757465436f6e747261637412730a2d636f736d6f733168736b366a727979716a6668703564686335357463396a74636b796778306570683664643032122d636f736d6f73317a743530617a7570616e716c66616d356166687633686578777975746e756b656834633537331a087472616e736665722a090a046d756f6e120131"); -} - -TEST(CosmosProtobuf, DeterministicSerialization_Article) { - // https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-027-deterministic-protobuf-serialization.md - auto article = blog::Article(); - article.set_title("The world needs change 🌳"); - article.set_description(""); - article.set_created(1596806111080); - article.set_updated(0); - article.set_public_(true); - article.set_promoted(false); - article.set_type(blog::NEWS); - article.set_review(blog::REVIEW_UNSPECIFIED); - *article.add_comments() = "Nice one"; - *article.add_comments() = "Thank you"; - - const auto serialized = data(article.SerializeAsString()); - EXPECT_EQ(hex(serialized), "0a1b54686520776f726c64206e65656473206368616e676520f09f8cb318e8bebec8bc2e280138024a084e696365206f6e654a095468616e6b20796f75"); -} - -} // namespace TW::Cosmos::tests diff --git a/tests/chains/Cosmos/Secret/SignerTests.cpp b/tests/chains/Cosmos/Secret/SignerTests.cpp index 997ab2d3886..8ccb283c938 100644 --- a/tests/chains/Cosmos/Secret/SignerTests.cpp +++ b/tests/chains/Cosmos/Secret/SignerTests.cpp @@ -5,9 +5,10 @@ // file LICENSE at the root of the source code distribution tree. #include "Cosmos/Address.h" -#include "Cosmos/Signer.h" +#include "TrustWalletCore/TWAnySigner.h" #include "HexCoding.h" #include "PrivateKey.h" +#include "proto/Cosmos.pb.h" #include "PublicKey.h" #include "TestUtilities.h" @@ -45,7 +46,8 @@ TEST(SecretSigner, Sign) { auto privateKey = parse_hex("87201512d132ef7a1e57f9e24905fbc24300bd73f676b5716182be5f3e39dada"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeSecret); + auto output = Cosmos::Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeSecret); // https://www.mintscan.io/secret/txs/01F4BD2458BF966F287533775C8D67BBC7CA7214CAEB1752D270A90223E9E82F // curl -H 'Content-Type: application/json' --data-binary "{\"tx_bytes\":\"CpIB...c4o=\",\"mode\":\"BROADCAST_MODE_BLOCK\"}" https://scrt-lcd.blockpane.com/cosmos/tx/v1beta1/txs diff --git a/tests/chains/Cosmos/SignerTests.cpp b/tests/chains/Cosmos/SignerTests.cpp index dc45bc5cd1c..20ec6a027a7 100644 --- a/tests/chains/Cosmos/SignerTests.cpp +++ b/tests/chains/Cosmos/SignerTests.cpp @@ -9,7 +9,7 @@ #include "Base64.h" #include "proto/Cosmos.pb.h" #include "Cosmos/Address.h" -#include "Cosmos/Signer.h" +#include "TrustWalletCore/TWAnySigner.h" #include "TestUtilities.h" #include "Cosmos/Protobuf/bank_tx.pb.h" #include "Cosmos/Protobuf/coin.pb.h" @@ -52,7 +52,8 @@ TEST(CosmosSigner, SignTxProtobuf) { auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeCosmos); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeCosmos); assertJSONEqual(output.serialized(), "{\"tx_bytes\": \"CowBCokBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmkKLWNvc21vczFoc2s2anJ5eXFqZmhwNWRoYzU1dGM5anRja3lneDBlcGg2ZGQwMhItY29zbW9zMXp0NTBhenVwYW5xbGZhbTVhZmh2M2hleHd5dXRudWtlaDRjNTczGgkKBG11b24SATESZQpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3FEgQKAggBGAgSEQoLCgRtdW9uEgMyMDAQwJoMGkD54fQAFlekIAnE62hZYl0uQelh/HLv0oQpCciY5Dn8H1SZFuTsrGdu41PH1Uxa4woptCELi/8Ov9yzdeEFAC9H\", \"mode\": \"BROADCAST_MODE_BLOCK\"}"); EXPECT_EQ(hex(output.signature()), "f9e1f4001657a42009c4eb6859625d2e41e961fc72efd2842909c898e439fc1f549916e4ecac676ee353c7d54c5ae30a29b4210b8bff0ebfdcb375e105002f47"); @@ -78,9 +79,11 @@ TEST(CosmosSigner, SignProtobuf_ErrorMissingMessage) { auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeCosmos); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeCosmos); - EXPECT_EQ(output.error_message(), "Error: No message found"); + // TODO +// EXPECT_EQ(output.error_message(), "Error: No message found"); EXPECT_EQ(output.serialized(), ""); EXPECT_EQ(output.json(), ""); EXPECT_EQ(hex(output.signature()), ""); @@ -119,7 +122,8 @@ TEST(CosmosSigner, SignTxJson) { auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeCosmos); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeCosmos); // the sample tx on testnet // https://hubble.figment.network/chains/gaia-13003/blocks/142933/transactions/3A9206598C3D2E75A5EC074FD33EA53EB18EC729357F0965971C1C51F812AEA3?format=json @@ -128,47 +132,6 @@ TEST(CosmosSigner, SignTxJson) { EXPECT_EQ(hex(output.signature()), "fc3ef899d206c88077fec42f21ba0b4df4bd3fd115fdf606ae01d9136fef363f57e9e33a7b9ec6ddab658cd07e3c0067470de94e4e75b979a1085a29f0efd926"); } -TEST(CosmosSigner, SignTxJsonWithExecuteContractMsg) { - auto input = Proto::SigningInput(); - input.set_signing_mode(Proto::JSON); // obsolete - input.set_account_number(1037); - input.set_chain_id("gaia-13003"); - input.set_memo(""); - input.set_sequence(8); - - auto fromAddress = Address("cosmos", parse_hex("BC2DA90C84049370D1B7C528BC164BC588833F21")); - auto toAddress = Address("cosmos", parse_hex("12E8FE8B81ECC1F4F774EA6EC8DF267138B9F2D9")); - - auto msg = input.add_messages(); - auto& message = *msg->mutable_execute_contract_message(); - message.set_sender(fromAddress.string()); - message.set_contract(toAddress.string()); - message.set_execute_msg("transfer"); - auto* coin = message.add_coins(); - coin->set_denom("muon"); - coin->set_amount("1"); - - auto& fee = *input.mutable_fee(); - fee.set_gas(200000); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("muon"); - amountOfFee->set_amount("200"); - - std::string json; - google::protobuf::util::MessageToJsonString(input, &json); - - EXPECT_EQ("{\"accountNumber\":\"1037\",\"chainId\":\"gaia-13003\",\"fee\":{\"amounts\":[{\"denom\":\"muon\",\"amount\":\"200\"}],\"gas\":\"200000\"},\"sequence\":\"8\",\"messages\":[{\"executeContractMessage\":{\"sender\":\"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02\",\"contract\":\"cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573\",\"executeMsg\":\"transfer\",\"coins\":[{\"denom\":\"muon\",\"amount\":\"1\"}]}}]}", json); - - auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); - input.set_private_key(privateKey.data(), privateKey.size()); - - auto output = Signer::sign(input, TWCoinTypeCosmos); - - EXPECT_EQ("{\"mode\":\"block\",\"tx\":{\"fee\":{\"amount\":[{\"amount\":\"200\",\"denom\":\"muon\"}],\"gas\":\"200000\"},\"memo\":\"\",\"msg\":[{\"type\":\"wasm/MsgExecuteContract\",\"value\":{\"coins\":[{\"amount\":\"1\",\"denom\":\"muon\"}],\"contract\":\"cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573\",\"execute_msg\":\"transfer\",\"sender\":\"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02\"}}],\"signatures\":[{\"pub_key\":{\"type\":\"tendermint/PubKeySecp256k1\",\"value\":\"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F\"},\"signature\":\"9iQTB1Jjw8FuYPwgzVLbs1cABGYFlk3JRKGQyojQcwY/ni+9D/ViNQMb+4UuokYi74GnpPZpH5RqbJ2ju6VL2g==\"}]}}", output.json()); - - EXPECT_EQ(hex(output.signature()), "f62413075263c3c16e60fc20cd52dbb35700046605964dc944a190ca88d073063f9e2fbd0ff56235031bfb852ea24622ef81a7a4f6691f946a6c9da3bba54bda"); -} - TEST(CosmosSigner, SignTxJsonWithRawJSONMsg) { auto input = Proto::SigningInput(); input.set_signing_mode(Proto::JSON); // obsolete @@ -199,7 +162,8 @@ TEST(CosmosSigner, SignTxJsonWithRawJSONMsg) { auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeCosmos); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeCosmos); EXPECT_EQ(output.json(), "{\"mode\":\"block\",\"tx\":{\"fee\":{\"amount\":[{\"amount\":\"200\",\"denom\":\"muon\"}],\"gas\":\"200000\"},\"memo\":\"\",\"msg\":[{\"type\":\"test\",\"value\":{\"test\":\"hello\"}}],\"signatures\":[{\"pub_key\":{\"type\":\"tendermint/PubKeySecp256k1\",\"value\":\"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F\"},\"signature\":\"qhxxCOMiVhP7e7Mx+98HUZI0t5DNOFXwzIqNQz+fT6hDKR/ebW0uocsYnE5CiBNEalmBcs5gSIJegNkHhgyEmA==\"}]}}"); @@ -236,13 +200,15 @@ TEST(CosmosSigner, SignTxJson_WithMode) { input.set_private_key(privateKey.data(), privateKey.size()); { - auto output = Signer::sign(input, TWCoinTypeCosmos); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeCosmos); EXPECT_EQ(R"({"mode":"async","tx":{"fee":{"amount":[{"amount":"200","denom":"muon"}],"gas":"200000"},"memo":"","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"1","denom":"muon"}],"from_address":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","to_address":"cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"/D74mdIGyIB3/sQvIboLTfS9P9EV/fYGrgHZE2/vNj9X6eM6e57G3atljNB+PABnRw3pTk51uXmhCFop8O/ZJg=="}]}})", output.json()); EXPECT_EQ(output.error_message(), ""); } input.set_mode(Proto::BroadcastMode::SYNC); { - auto output = Signer::sign(input, TWCoinTypeCosmos); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeCosmos); EXPECT_EQ(R"({"mode":"sync","tx":{"fee":{"amount":[{"amount":"200","denom":"muon"}],"gas":"200000"},"memo":"","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"1","denom":"muon"}],"from_address":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","to_address":"cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"/D74mdIGyIB3/sQvIboLTfS9P9EV/fYGrgHZE2/vNj9X6eM6e57G3atljNB+PABnRw3pTk51uXmhCFop8O/ZJg=="}]}})", output.json()); EXPECT_EQ(output.error_message(), ""); } @@ -281,7 +247,8 @@ TEST(CosmosSigner, SignIbcTransferProtobuf_817101) { EXPECT_EQ(Cosmos::Address(TWCoinTypeCosmos, PrivateKey(privateKey).getPublicKey(TWPublicKeyTypeSECP256k1)).string(), "cosmos1mky69cn8ektwy0845vec9upsdphktxt03gkwlx"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeCosmos); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeCosmos); // real-world tx: https://www.mintscan.io/cosmos/txs/817101F3D96314AD028733248B28BAFAD535024D7D2C8875D3FE31DC159F096B // curl -H 'Content-Type: application/json' --data-binary '{"tx_bytes": "Cr4BCr...1yKOU=", "mode": "BROADCAST_MODE_BLOCK"}' https://api.cosmos.network/cosmos/tx/v1beta1/txs @@ -314,7 +281,8 @@ TEST(CosmosSigner, SignDirect1) { auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeCosmos); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeCosmos); assertJSONEqual(output.serialized(), "{\"tx_bytes\": \"CowBCokBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmkKLWNvc21vczFoc2s2anJ5eXFqZmhwNWRoYzU1dGM5anRja3lneDBlcGg2ZGQwMhItY29zbW9zMXp0NTBhenVwYW5xbGZhbTVhZmh2M2hleHd5dXRudWtlaDRjNTczGgkKBG11b24SATESZQpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3FEgQKAggBGAgSEQoLCgRtdW9uEgMyMDAQwJoMGkD54fQAFlekIAnE62hZYl0uQelh/HLv0oQpCciY5Dn8H1SZFuTsrGdu41PH1Uxa4woptCELi/8Ov9yzdeEFAC9H\", \"mode\": \"BROADCAST_MODE_BLOCK\"}"); EXPECT_EQ(hex(output.signature()), "f9e1f4001657a42009c4eb6859625d2e41e961fc72efd2842909c898e439fc1f549916e4ecac676ee353c7d54c5ae30a29b4210b8bff0ebfdcb375e105002f47"); @@ -359,7 +327,8 @@ TEST(CosmosSigner, SignDirect_0a90010a) { auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeCosmos); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeCosmos); assertJSONEqual(output.serialized(), "{\"tx_bytes\": \"CpMBCpABChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnAKLWNvc21vczFwa3B0cmU3ZmRrbDZnZnJ6bGVzamp2aHhobGMzcjRnbW1rOHJzNhItY29zbW9zMXF5cHF4cHE5cWNyc3N6ZzJwdnhxNnJzMHpxZzN5eWM1bHp2N3h1GhAKBXVjb3NtEgcxMjM0NTY3EiEKCgoAEgQKAggBGAESEwoNCgV1Y29zbRIEMjAwMBDAmgwaQEgXmSAlm4M5bz+OX1GtvvZ3fBV2wrZrp4A/Imd55KM7ASivB/siYJegmYiOKzQ82uwoEmFalNnG2BrHHDwDR2Y=\", \"mode\": \"BROADCAST_MODE_BLOCK\"}"); EXPECT_EQ(hex(output.signature()), "48179920259b83396f3f8e5f51adbef6777c1576c2b66ba7803f226779e4a33b0128af07fb226097a099888e2b343cdaec2812615a94d9c6d81ac71c3c034766"); @@ -391,7 +360,9 @@ TEST(CosmosSigner, MsgVote) { auto privateKey = parse_hex("a498a9ee41af9bab5ef2a8be63d5c970135c3c109e70efc8c56c534e6636b433"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeCosmos); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeCosmos); + auto expected = R"( {"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"ClQKUgobL2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3RlEjMITRItY29zbW9zMW1yeTQ3cGtnYTV0ZHN3dGx1eTBtOHRlc2xwYWxrZHEwN3Bzd3U0GAESZQpOCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAsv9teRyiTMiKU5gzwiD1D30MeEInSnstEep5tVQRarlEgQKAggBEhMKDQoFdWF0b20SBDI0MTgQkfsFGkA+Nb3NULc38quGC1x+8ZXry4w9mMX3IA7wUjFboTv7kVOwPlleIc8UqIsjVvKTUFnUuW8dlGQzNR1KkvbvZ1NA"})"; assertJSONEqual(output.serialized(), expected); diff --git a/tests/chains/Cosmos/StakingTests.cpp b/tests/chains/Cosmos/StakingTests.cpp index 91e097e0342..ad800b54184 100644 --- a/tests/chains/Cosmos/StakingTests.cpp +++ b/tests/chains/Cosmos/StakingTests.cpp @@ -7,10 +7,10 @@ #include "Base64.h" #include "Coin.h" #include "Cosmos/Address.h" -#include "Cosmos/Signer.h" #include "HexCoding.h" #include "proto/Cosmos.pb.h" #include "TestUtilities.h" +#include #include namespace TW::Cosmos::tests { @@ -42,11 +42,14 @@ TEST(CosmosStaking, CompoundingAuthz) { auto privateKey = parse_hex("c7764249cdf77f8f1d840fa8af431579e5e41cf1af937e1e23afa22f3f4f0ccc"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeCosmos); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeCosmos); + // Please note the signature has been updated according to the serialization of the `StakeAuthorization` message. + // Previous: CvgBCvUBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQS0gEKLWNvc21vczEzazBxMGw3bGcya3IzMmt2dDdseTIzNnBwbGR5OHY5ZHp3aDNnZBItY29zbW9zMWZzN2x1MjhoeDVtOWFrbTdycDBjMjQyMmNuOHIyZjdndXJ1amhmGnIKaAoqL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuU3Rha2VBdXRob3JpemF0aW9uEjoSNgo0Y29zbW9zdmFsb3BlcjFnanR2bHk5bGVsNnpza3Z3dHZsZzV2aHdwdTljOXdhdzdzeHp3eCABEgYI4LD6pgYSZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA/fcQw1hCVUx904t+kCXTiiziaLIY8lyssu1ENfzaN1KEgQKAggBGAUSEwoNCgV1YXRvbRIEMjQxOBCp8wUaQIFyfuijGKf87Hz61ZqxasfLI1PZnNge4RDq/tRyB/tZI6p80iGRqHecoV6+84EQkc9GTlNRQOSlApRCsivT9XI= auto expected = R"( { "mode":"BROADCAST_MODE_BLOCK", - "tx_bytes":"CvgBCvUBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQS0gEKLWNvc21vczEzazBxMGw3bGcya3IzMmt2dDdseTIzNnBwbGR5OHY5ZHp3aDNnZBItY29zbW9zMWZzN2x1MjhoeDVtOWFrbTdycDBjMjQyMmNuOHIyZjdndXJ1amhmGnIKaAoqL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuU3Rha2VBdXRob3JpemF0aW9uEjoSNgo0Y29zbW9zdmFsb3BlcjFnanR2bHk5bGVsNnpza3Z3dHZsZzV2aHdwdTljOXdhdzdzeHp3eCABEgYI4LD6pgYSZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA/fcQw1hCVUx904t+kCXTiiziaLIY8lyssu1ENfzaN1KEgQKAggBGAUSEwoNCgV1YXRvbRIEMjQxOBCp8wUaQIFyfuijGKf87Hz61ZqxasfLI1PZnNge4RDq/tRyB/tZI6p80iGRqHecoV6+84EQkc9GTlNRQOSlApRCsivT9XI=" + "tx_bytes":"CvgBCvUBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQS0gEKLWNvc21vczEzazBxMGw3bGcya3IzMmt2dDdseTIzNnBwbGR5OHY5ZHp3aDNnZBItY29zbW9zMWZzN2x1MjhoeDVtOWFrbTdycDBjMjQyMmNuOHIyZjdndXJ1amhmGnIKaAoqL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuU3Rha2VBdXRob3JpemF0aW9uEjogARI2CjRjb3Ntb3N2YWxvcGVyMWdqdHZseTlsZWw2enNrdnd0dmxnNXZod3B1OWM5d2F3N3N4end4EgYI4LD6pgYSZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA/fcQw1hCVUx904t+kCXTiiziaLIY8lyssu1ENfzaN1KEgQKAggBGAUSEwoNCgV1YXRvbRIEMjQxOBCp8wUaQEAN1nIfDawlHnep2bNEm14w+g7tYybJJT3htcGVS6s9D7va3ed1OUEIk9LZoc3G//VenJ+KLw26SRVBaRukgVI=" })"; assertJSONEqual(output.serialized(), expected); } @@ -75,7 +78,8 @@ TEST(CosmosStaking, RevokeCompoundingAuthz) { auto privateKey = parse_hex("c7764249cdf77f8f1d840fa8af431579e5e41cf1af937e1e23afa22f3f4f0ccc"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeCosmos); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeCosmos); auto expected = R"( { "mode":"BROADCAST_MODE_BLOCK", @@ -109,7 +113,8 @@ TEST(CosmosStaking, Staking) { auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeCosmos); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeCosmos); assertJSONEqual(output.serialized(), "{\"tx_bytes\":\"CpsBCpgBCiMvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dEZWxlZ2F0ZRJxCi1jb3Ntb3MxaHNrNmpyeXlxamZocDVkaGM1NXRjOWp0Y2t5Z3gwZXBoNmRkMDISNGNvc21vc3ZhbG9wZXIxemt1cHI4M2hyemtuM3VwNWVsa3R6Y3EzdHVmdDhueHNtd2RxZ3AaCgoEbXVvbhICMTASZgpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3FEgQKAggBGAcSEgoMCgRtdW9uEgQxMDE4ENmaBhpA8O9Jm/kL6Za2I3poDs5vpMowYJgNvYCJBRU/vxAjs0lNZYsq40qpTbwOTbORjJA5UjQ6auc40v6uCFT4q4z+uA==\",\"mode\":\"BROADCAST_MODE_BLOCK\"}"); EXPECT_EQ(hex(output.signature()), "f0ef499bf90be996b6237a680ece6fa4ca3060980dbd808905153fbf1023b3494d658b2ae34aa94dbc0e4db3918c903952343a6ae738d2feae0854f8ab8cfeb8"); @@ -117,7 +122,8 @@ TEST(CosmosStaking, Staking) { { // Json-serialization, for coverage (to be removed later) input.set_signing_mode(Proto::JSON); - output = Signer::sign(input, TWCoinTypeCosmos); + ANY_SIGN(input, TWCoinTypeCosmos); + ASSERT_EQ(hex(output.signature()), "c08bdf6c2b0b4428f37975e85d329f1cb19745b000994a743b5df81d57d573aa5f755349befcc848c1d1507818723b1288594bc91df685e89aff22e0303b4861"); EXPECT_EQ(output.error_message(), ""); EXPECT_EQ(hex(output.serialized()), ""); @@ -149,7 +155,8 @@ TEST(CosmosStaking, Unstaking) { auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeCosmos); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeCosmos); assertJSONEqual(output.serialized(), "{\"tx_bytes\":\"Cp0BCpoBCiUvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dVbmRlbGVnYXRlEnEKLWNvc21vczFoc2s2anJ5eXFqZmhwNWRoYzU1dGM5anRja3lneDBlcGg2ZGQwMhI0Y29zbW9zdmFsb3BlcjF6a3VwcjgzaHJ6a24zdXA1ZWxrdHpjcTN0dWZ0OG54c213ZHFncBoKCgRtdW9uEgIxMBJmClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiECVyhuw/N9M1V7u6oACyd0SskCOqmWfK51oYHR/5H6ncUSBAoCCAEYBxISCgwKBG11b24SBDEwMTgQ2ZoGGkBhlxHFnjBERxLtjLbMCKXcrDctaSZ9djtWCa3ely1bpV6m+6aAFjpr8aEZH+q2AtjJSEdgpQRJxP+9/gQsRTnZ\",\"mode\":\"BROADCAST_MODE_BLOCK\"}"); EXPECT_EQ(hex(output.signature()), "619711c59e30444712ed8cb6cc08a5dcac372d69267d763b5609adde972d5ba55ea6fba680163a6bf1a1191feab602d8c9484760a50449c4ffbdfe042c4539d9"); @@ -157,7 +164,7 @@ TEST(CosmosStaking, Unstaking) { { // Json-serialization, for coverage (to be removed later) input.set_signing_mode(Proto::JSON); - output = Signer::sign(input, TWCoinTypeCosmos); + ANY_SIGN(input, TWCoinTypeCosmos); ASSERT_EQ(hex(output.signature()), "8f85a9515a211881daebfb346c2beeca3ab5c2d406a9b3ad402cfddaa3d08e2b13378e13cfef8ecf1d6500fe85d0ce3e793034dd77aba90f216427807cbff79f"); EXPECT_EQ(output.error_message(), ""); EXPECT_EQ(hex(output.serialized()), ""); @@ -191,7 +198,8 @@ TEST(CosmosStaking, Restaking) { auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeCosmos); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeCosmos); assertJSONEqual(output.serialized(), "{\"tx_bytes\":\"CtIBCs8BCiovY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dCZWdpblJlZGVsZWdhdGUSoAEKLWNvc21vczFoc2s2anJ5eXFqZmhwNWRoYzU1dGM5anRja3lneDBlcGg2ZGQwMhI0Y29zbW9zdmFsb3BlcjF6a3VwcjgzaHJ6a24zdXA1ZWxrdHpjcTN0dWZ0OG54c213ZHFncBotY29zbW9zMWhzazZqcnl5cWpmaHA1ZGhjNTV0YzlqdGNreWd4MGVwaDZkZDAyIgoKBG11b24SAjEwEmYKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQJXKG7D830zVXu7qgALJ3RKyQI6qZZ8rnWhgdH/kfqdxRIECgIIARgHEhIKDAoEbXVvbhIEMTAxOBDZmgYaQJ52qO5xdtBkNUeFeWrnqUXkngyHFKCXnOPPClyVI0HrULdp5jbwGra2RujEOn4BrbFCb3JFnpc2o1iuLXbKQxg=\",\"mode\":\"BROADCAST_MODE_BLOCK\"}"); EXPECT_EQ(hex(output.signature()), "9e76a8ee7176d064354785796ae7a945e49e0c8714a0979ce3cf0a5c952341eb50b769e636f01ab6b646e8c43a7e01adb1426f72459e9736a358ae2d76ca4318"); @@ -199,7 +207,7 @@ TEST(CosmosStaking, Restaking) { { // Json-serialization, for coverage (to be removed later) input.set_signing_mode(Proto::JSON); - output = Signer::sign(input, TWCoinTypeCosmos); + ANY_SIGN(input, TWCoinTypeCosmos); ASSERT_EQ(hex(output.signature()), "e64d3761bd25a28befcda80c0a0e208d024fdb0a2b89955170e65a5c5d454aba2ce81d57e01f0c126de5a59c2b58124c109560c9803d65a17a14b548dd6c50db"); EXPECT_EQ(output.error_message(), ""); EXPECT_EQ(hex(output.serialized()), ""); @@ -228,7 +236,8 @@ TEST(CosmosStaking, Withdraw) { auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeCosmos); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeCosmos); assertJSONEqual(output.serialized(), "{\"tx_bytes\":\"CqMBCqABCjcvY29zbW9zLmRpc3RyaWJ1dGlvbi52MWJldGExLk1zZ1dpdGhkcmF3RGVsZWdhdG9yUmV3YXJkEmUKLWNvc21vczFoc2s2anJ5eXFqZmhwNWRoYzU1dGM5anRja3lneDBlcGg2ZGQwMhI0Y29zbW9zdmFsb3BlcjF6a3VwcjgzaHJ6a24zdXA1ZWxrdHpjcTN0dWZ0OG54c213ZHFncBJmClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiECVyhuw/N9M1V7u6oACyd0SskCOqmWfK51oYHR/5H6ncUSBAoCCAEYBxISCgwKBG11b24SBDEwMTgQ2ZoGGkBW1Cd+0pNfMPEVXQtqG1VIijDjZP2UOiDlvUF478axnxlF8PaOAsY0S5OdUE3Wz7+nu8YVmrLZQS/8mlqLaK05\",\"mode\":\"BROADCAST_MODE_BLOCK\"}"); EXPECT_EQ(hex(output.signature()), "56d4277ed2935f30f1155d0b6a1b55488a30e364fd943a20e5bd4178efc6b19f1945f0f68e02c6344b939d504dd6cfbfa7bbc6159ab2d9412ffc9a5a8b68ad39"); @@ -236,7 +245,7 @@ TEST(CosmosStaking, Withdraw) { { // Json-serialization, for coverage (to be removed later) input.set_signing_mode(Proto::JSON); - output = Signer::sign(input, TWCoinTypeCosmos); + ANY_SIGN(input, TWCoinTypeCosmos); ASSERT_EQ(hex(output.signature()), "546f0d67356f6af94cfb5ab22b974e499c33123f2c2c292f4f0e64878e0e728f4643105fd771550beb3f2371f08880aaa38fa8f2334c103a779f1d82d2db98d6"); EXPECT_EQ(output.error_message(), ""); EXPECT_EQ(hex(output.serialized()), ""); @@ -265,7 +274,8 @@ TEST(CosmosStaking, SetWithdrawAddress) { auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeCosmos); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeCosmos); assertJSONEqual(output.serialized(), R"({"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"Cp4BCpsBCjIvY29zbW9zLmRpc3RyaWJ1dGlvbi52MWJldGExLk1zZ1NldFdpdGhkcmF3QWRkcmVzcxJlCi1jb3Ntb3MxaHNrNmpyeXlxamZocDVkaGM1NXRjOWp0Y2t5Z3gwZXBoNmRkMDISNGNvc21vc3ZhbG9wZXIxemt1cHI4M2hyemtuM3VwNWVsa3R6Y3EzdHVmdDhueHNtd2RxZ3ASZgpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3FEgQKAggBGAcSEgoMCgRtdW9uEgQxMDE4ENmaBhpAkm2TJLw4FcIwN5bkqVaGbmAgkTSHeYD8sUkIyJHLa89cPvThkFO/lKlxBMl2UAMs06hL6cYcl4Px+B6rpFdBpA=="})"); EXPECT_EQ(hex(output.signature()), "926d9324bc3815c2303796e4a956866e60209134877980fcb14908c891cb6bcf5c3ef4e19053bf94a97104c97650032cd3a84be9c61c9783f1f81eaba45741a4"); @@ -273,7 +283,7 @@ TEST(CosmosStaking, SetWithdrawAddress) { { // Json-serialization, for coverage (to be removed later) input.set_signing_mode(Proto::JSON); - output = Signer::sign(input, TWCoinTypeCosmos); + ANY_SIGN(input, TWCoinTypeCosmos); ASSERT_EQ(hex(output.signature()), "22cfbcec33d06ed42623264049d11d6fb86566103d5621a23b1444022eb1aace3a0790a1c46b48c0218689616daf97f99ae72c3589966205de45b57194fbada2"); EXPECT_EQ(output.error_message(), ""); EXPECT_EQ(hex(output.serialized()), ""); diff --git a/tests/chains/Cosmos/THORChain/SignerTests.cpp b/tests/chains/Cosmos/THORChain/SignerTests.cpp index 75f86efda13..fcb07cae3a9 100644 --- a/tests/chains/Cosmos/THORChain/SignerTests.cpp +++ b/tests/chains/Cosmos/THORChain/SignerTests.cpp @@ -5,10 +5,12 @@ // file LICENSE at the root of the source code distribution tree. #include "proto/Cosmos.pb.h" -#include "THORChain/Signer.h" +#include "Coin.h" #include "HexCoding.h" #include "Bech32Address.h" #include "TestUtilities.h" +#include "TrustWalletCore/TWAnySigner.h" +#include "TrustWalletCore/TWCoinType.h" #include #include @@ -80,7 +82,8 @@ TEST(THORChainSigner, SignTx_Protobuf_7E480F) { auto privateKey = parse_hex("7105512f0c020a1dd759e14b865ec0125f59ac31e34d7a2807a228ed50cb343e"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = THORChain::Signer::sign(input); + auto output = Cosmos::Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeTHORChain); // https://viewblock.io/thorchain/tx/7E480FA163F6C6AFA17593F214C7BBC218F69AE3BC72366E39042AF381BFE105 // curl -H 'Content-Type: application/json' --data-binary '{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"ClIKUAoO..89g="}' https:///cosmos/tx/v1beta1/txs @@ -158,7 +161,8 @@ TEST(THORChainSigner, SignTx_MsgDeposit) { auto privateKey = parse_hex("2659e41d54ebd449d68b9d58510d8eeeb837ee00d6ecc760b7a731238d8c3113"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = THORChain::Signer::sign(input); + auto output = Cosmos::Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeTHORChain); // https://viewblock.io/thorchain/tx/0162213E7F9D85965B1C57FA3BF9603C655B542F358318303A7B00661AE42510 // curl -H 'Content-Type: application/json' --data-binary '{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CoUBCoIB..hiw="}' https:///cosmos/tx/v1beta1/txs @@ -225,7 +229,8 @@ TEST(THORChainSigner, SignTx_Json_Deprecated) { auto privateKey = parse_hex("7105512f0c020a1dd759e14b865ec0125f59ac31e34d7a2807a228ed50cb343e"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = THORChain::Signer::sign(input); + auto output = Cosmos::Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeTHORChain); assertJSONEqual(output.json(), R"( { @@ -275,7 +280,7 @@ TEST(THORChainSigner, SignJson) { auto inputJson = R"({"fee":{"amounts":[{"denom":"rune","amount":"200"}],"gas":"2000000"},"memo":"memo1234","messages":[{"sendCoinsMessage":{"fromAddress":"thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r","toAddress":"thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn","amounts":[{"denom":"rune","amount":"50000000"}]}}]})"; auto privateKey = parse_hex("7105512f0c020a1dd759e14b865ec0125f59ac31e34d7a2807a228ed50cb343e"); - auto outputJson = THORChain::Signer::signJSON(inputJson, privateKey); + auto outputJson = TW::anySignJSON(TWCoinTypeTHORChain, inputJson, privateKey); EXPECT_EQ(R"({"mode":"block","tx":{"fee":{"amount":[{"amount":"200","denom":"rune"}],"gas":"2000000"},"memo":"memo1234","msg":[{"type":"thorchain/MsgSend","value":{"amount":[{"amount":"50000000","denom":"rune"}],"from_address":"thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r","to_address":"thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A+2Zfjls9CkvX85aQrukFZnM1dluMTFUp8nqcEneMXx3"},"signature":"12AaNC0v51Rhz8rBf7V7rpI6oksREWrjzba3RK1v1NNlqZq62sG0aXWvStp9zZXe07Pp2FviFBAx+uqWsO30NQ=="}]}})", outputJson); } diff --git a/tests/chains/Cosmos/Terra/SignerTests.cpp b/tests/chains/Cosmos/Terra/SignerTests.cpp index 2d6373de942..aa6004567da 100644 --- a/tests/chains/Cosmos/Terra/SignerTests.cpp +++ b/tests/chains/Cosmos/Terra/SignerTests.cpp @@ -8,10 +8,9 @@ #include "Base64.h" #include "proto/Cosmos.pb.h" #include "Cosmos/Address.h" -#include "Cosmos/Signer.h" -#include "Cosmos/ProtobufSerialization.h" #include "uint256.h" #include "TestUtilities.h" +#include "TrustWalletCore/TWAnySigner.h" #include #include @@ -52,7 +51,8 @@ TEST(TerraClassicSigner, SignSendTx) { auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeTerra); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeTerra); assertJSONEqual(output.json(), R"( { @@ -161,7 +161,8 @@ TEST(TerraClassicSigner, SignWasmTransferTxProtobuf_9FF3F0) { auto privateKey = parse_hex("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeTerra); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeTerra); // https://finder.terra.money/mainnet/tx/9FF3F0A16879254C22EB90D8B4D6195467FE5014381FD36BD3C23CA6698FE94B // curl -H 'Content-Type: application/json' --data-binary '{"mode": "BROADCAST_MODE_BLOCK","tx_bytes": "CogCCo..wld8"})' https:///cosmos/tx/v1beta1/txs @@ -206,7 +207,8 @@ TEST(TerraClassicSigner, SignWasmTransferTxJson_078E90) { auto privateKey = parse_hex("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeTerra); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeTerra); // https://finder.terra.money/mainnet/tx/078E90458061611F6FD8B708882B55FF5C1FFB3FCE61322107A0A0DE39FC0F3E // curl -H 'Content-Type: application/json' --data-binary '{"mode": "block","tx":{...}}' https:///txs @@ -284,7 +286,8 @@ TEST(TerraClassicSigner, SignWasmGeneric_EC4F85) { auto privateKey = parse_hex("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeTerra); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeTerra); // https://finder.terra.money/mainnet/tx/EC4F8532847E4D6AF016E6F6D3F027AE7FB6FF0B533C5132B01382D83B214A6F // curl -H 'Content-Type: application/json' --data-binary '{"mode": "BROADCAST_MODE_BLOCK","tx_bytes": "Cu4BC...iVt"})' https:///cosmos/tx/v1beta1/txs @@ -333,7 +336,8 @@ TEST(TerraClassicSigner, SignWasmGenericWithCoins_6651FC) { auto privateKey = parse_hex("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeTerra); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeTerra); // https://finder.terra.money/mainnet/tx/6651FCE0EE5C6D6ACB655CC49A6FD5E939FB082862854616EA0642475BCDD0C9 // curl -H 'Content-Type: application/json' --data-binary '{"mode": "BROADCAST_MODE_BLOCK","tx_bytes": "CrIBCq8B.....0NWg=="})' https:///cosmos/tx/v1beta1/txs @@ -413,7 +417,8 @@ TEST(TerraClassicSigner, SignWasmSendTxProtobuf) { auto privateKey = parse_hex("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeTerra); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeTerra); // https://finder.terra.money/mainnet/tx/9FF3F0A16879254C22EB90D8B4D6195467FE5014381FD36BD3C23CA6698FE94B // curl -H 'Content-Type: application/json' --data-binary '{"mode": "BROADCAST_MODE_BLOCK","tx_bytes": "CogCCo..wld8"})' https:///cosmos/tx/v1beta1/txs @@ -427,23 +432,4 @@ TEST(TerraClassicSigner, SignWasmSendTxProtobuf) { EXPECT_EQ(output.json(), ""); } -TEST(TerraClassicSigner, SignWasmTerraTransferPayload) { - auto proto = Proto::Message_WasmTerraExecuteContractTransfer(); - proto.set_recipient_address("recipient=address"); - const auto amount = store(uint256_t(250000), 0); - proto.set_amount(amount.data(), amount.size()); - - const auto payload = Protobuf::wasmTerraExecuteTransferPayload(proto); - - assertJSONEqual(payload.dump(), R"( - { - "transfer": - { - "amount": "250000", - "recipient": "recipient=address" - } - } - )"); -} - } // namespace TW::Cosmos::tests diff --git a/tests/chains/Cosmos/TerraV2/SignerTests.cpp b/tests/chains/Cosmos/TerraV2/SignerTests.cpp index cfd8dd8dde4..b6f94de7972 100644 --- a/tests/chains/Cosmos/TerraV2/SignerTests.cpp +++ b/tests/chains/Cosmos/TerraV2/SignerTests.cpp @@ -8,9 +8,8 @@ #include "Base64.h" #include "proto/Cosmos.pb.h" #include "Cosmos/Address.h" -#include "Cosmos/Signer.h" -#include "Cosmos/ProtobufSerialization.h" #include "uint256.h" +#include "TrustWalletCore/TWAnySigner.h" #include "TestUtilities.h" #include @@ -84,7 +83,8 @@ TEST(TerraSigner, SignSendTx) { auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeTerraV2); + auto output = Cosmos::Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeTerraV2); // similar tx: https://finder.terra.money/mainnet/tx/fbbe73ad2f0db3a13911dc424f8a34370dc4b7e8b66687f536797e68ee200ece assertJSONEqual(output.serialized(), R"( @@ -161,7 +161,8 @@ TEST(TerraSigner, SignWasmTransferTx) { auto privateKey = parse_hex("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeTerraV2); + auto output = Cosmos::Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeTerraV2); assertJSONEqual(output.serialized(), R"( { @@ -202,7 +203,8 @@ TEST(TerraSigner, SignWasmGeneric) { auto privateKey = parse_hex("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeTerraV2); + auto output = Cosmos::Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeTerraV2); assertJSONEqual(output.serialized(), R"( { @@ -248,7 +250,8 @@ TEST(TerraSigner, SignWasmGenericWithCoins) { auto privateKey = parse_hex("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeTerraV2); + auto output = Cosmos::Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeTerraV2); assertJSONEqual(output.serialized(), R"( { @@ -327,7 +330,8 @@ TEST(TerraSigner, SignWasmSendTx) { auto privateKey = parse_hex("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeTerraV2); + auto output = Cosmos::Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeTerraV2); assertJSONEqual(output.serialized(), R"( { @@ -340,23 +344,4 @@ TEST(TerraSigner, SignWasmSendTx) { EXPECT_EQ(output.json(), ""); } -TEST(TerraSigner, SignWasmTransferPayload) { - auto proto = Proto::Message_WasmExecuteContractTransfer(); - proto.set_recipient_address("recipient=address"); - const auto amount = store(uint256_t(250000), 0); - proto.set_amount(amount.data(), amount.size()); - - const auto payload = Protobuf::wasmExecuteTransferPayload(proto); - - assertJSONEqual(payload.dump(), R"( - { - "transfer": - { - "amount": "250000", - "recipient": "recipient=address" - } - } - )"); -} - } // namespace TW::Cosmos::tests diff --git a/tests/chains/Cosmos/TransactionCompilerTests.cpp b/tests/chains/Cosmos/TransactionCompilerTests.cpp index 4500533ab7a..49284252185 100644 --- a/tests/chains/Cosmos/TransactionCompilerTests.cpp +++ b/tests/chains/Cosmos/TransactionCompilerTests.cpp @@ -139,7 +139,7 @@ TEST(CosmosCompiler, CompileWithSignatures) { Cosmos::Proto::SigningOutput output; ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); EXPECT_EQ(output.serialized().size(), 0ul); - EXPECT_EQ(output.error(), Common::Proto::Error_invalid_params); + EXPECT_EQ(output.error(), Common::Proto::Error_signatures_count); } /// Step 3: Obtain json preimage hash @@ -168,10 +168,6 @@ TEST(CosmosCompiler, CompileWithSignatures) { "0a31f6cd50f1a5c514929ba68a977e222a7df2dc11e8470e93118cc3545e6b37"); signature = Base64::decode("tTyOrburrHEHa14qiw78e9StoZyyGmoku98IxYrWCmtN8Qo5mTeKa0BKKDfgG4LmmNdwYcrXtqQQ7F4dL3c26g=="); - { - auto result = TW::anySignJSON(coin, jsonPreImage, privateKey.bytes); - EXPECT_EQ(result, "{\"mode\":\"block\",\"tx\":{\"fee\":{\"amount\":[],\"gas\":\"0\"},\"memo\":\"\",\"msg\":[],\"signatures\":[{\"pub_key\":{\"type\":\"tendermint/PubKeySecp256k1\",\"value\":\"AuzvXOQ3owLGf5VGjeSzHzbpEfRn1+alK0HB4T4dVjZJ\"},\"signature\":\"tTyOrburrHEHa14qiw78e9StoZyyGmoku98IxYrWCmtN8Qo5mTeKa0BKKDfgG4LmmNdwYcrXtqQQ7F4dL3c26g==\"}]}}"); - } { // JSON const Data outputData = TransactionCompiler::compileWithSignatures( @@ -180,6 +176,6 @@ TEST(CosmosCompiler, CompileWithSignatures) { ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); EXPECT_EQ(output.error(), Common::Proto::OK); - EXPECT_EQ(hex(output.serialized()), "7b226d6f6465223a22626c6f636b222c227478223a7b22666565223a7b22616d6f756e74223a5b7b22616d6f756e74223a2231303030222c2264656e6f6d223a227561746f6d227d5d2c22676173223a22323030303030227d2c226d656d6f223a22222c226d7367223a5b7b2274797065223a22636f736d6f732d73646b2f4d736753656e64222c2276616c7565223a7b22616d6f756e74223a5b7b22616d6f756e74223a22343030303030222c2264656e6f6d223a227561746f6d227d5d2c2266726f6d5f61646472657373223a22636f736d6f73316d6b793639636e38656b74777930383435766563397570736470686b7478743033676b776c78222c22746f5f61646472657373223a22636f736d6f733138733068646e736c6c6763636c7765753961796d77346e676b7472326b30726b7967647a6470227d7d5d2c227369676e617475726573223a5b7b227075625f6b6579223a7b2274797065223a2274656e6465726d696e742f5075624b6579536563703235366b31222c2276616c7565223a2241757a76584f51336f774c47663556476a65537a487a62704566526e312b616c4b30484234543464566a5a4a227d2c227369676e6174757265223a227454794f72627572724845486131347169773738653953746f5a7979476d6f6b7539384978597257436d744e38516f356d54654b6130424b4b44666747344c6d6d4e64775963725874715151374634644c33633236673d3d227d5d7d7d"); + EXPECT_EQ(hex(output.json()), "7b226d6f6465223a22626c6f636b222c227478223a7b22666565223a7b22616d6f756e74223a5b7b22616d6f756e74223a2231303030222c2264656e6f6d223a227561746f6d227d5d2c22676173223a22323030303030227d2c226d656d6f223a22222c226d7367223a5b7b2274797065223a22636f736d6f732d73646b2f4d736753656e64222c2276616c7565223a7b22616d6f756e74223a5b7b22616d6f756e74223a22343030303030222c2264656e6f6d223a227561746f6d227d5d2c2266726f6d5f61646472657373223a22636f736d6f73316d6b793639636e38656b74777930383435766563397570736470686b7478743033676b776c78222c22746f5f61646472657373223a22636f736d6f733138733068646e736c6c6763636c7765753961796d77346e676b7472326b30726b7967647a6470227d7d5d2c227369676e617475726573223a5b7b227075625f6b6579223a7b2274797065223a2274656e6465726d696e742f5075624b6579536563703235366b31222c2276616c7565223a2241757a76584f51336f774c47663556476a65537a487a62704566526e312b616c4b30484234543464566a5a4a227d2c227369676e6174757265223a227454794f72627572724845486131347169773738653953746f5a7979476d6f6b7539384978597257436d744e38516f356d54654b6130424b4b44666747344c6d6d4e64775963725874715151374634644c33633236673d3d227d5d7d7d"); } } diff --git a/tests/chains/Evmos/SignerTests.cpp b/tests/chains/Evmos/SignerTests.cpp index 02ae7c9b45e..981bd727b27 100644 --- a/tests/chains/Evmos/SignerTests.cpp +++ b/tests/chains/Evmos/SignerTests.cpp @@ -8,9 +8,9 @@ #include "Base64.h" #include "proto/Cosmos.pb.h" #include "Cosmos/Address.h" -#include "Cosmos/Signer.h" #include "TestUtilities.h" +#include #include #include @@ -46,7 +46,8 @@ TEST(EvmosSigner, SignTxJsonEthermintKeyType) { auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeNativeEvmos); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeNativeEvmos); auto anotherExpectedJson =R"( { "mode":"block", @@ -64,7 +65,7 @@ TEST(EvmosSigner, SignTxJsonEthermintKeyType) { "type":"ethermint/PubKeyEthSecp256k1", "value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F" }, - "signature":"RWt8aaBxdMAeEjym8toWskJ6WaJpEF9Ciucz2lAHkvNnTicGpzxwTUzJbJXRirSnGkejhISaYtDw2RBiq0vg5w==" + "signature":"1hMFtRqKjB8tiuyHYVYZundPdomebIIvHLC1gj9uXtFc+iO3UAHBysBjFB4brd9AD5yriS3uUDTAqqfg6fNGNg==" } ]} })"_json; @@ -82,7 +83,7 @@ TEST(EvmosSigner, SignTxJsonEthermintKeyType) { "type":"ethermint/PubKeyEthSecp256k1", "value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F" }, - "signature":"RWt8aaBxdMAeEjym8toWskJ6WaJpEF9Ciucz2lAHkvNnTicGpzxwTUzJbJXRirSnGkejhISaYtDw2RBiq0vg5w==" + "signature":"1hMFtRqKjB8tiuyHYVYZundPdomebIIvHLC1gj9uXtFc+iO3UAHBysBjFB4brd9AD5yriS3uUDTAqqfg6fNGNg==" } ])"_json; EXPECT_EQ(signatures, expectedSignatures); @@ -115,10 +116,13 @@ TEST(EvmosSigner, CompoundingAuthz) { auto privateKey = parse_hex("79bcbded1a5678ab34e6d9db9ad78e4e480e7b22723cc5fbf59e843732e1a8e5"); input.set_private_key(privateKey.data(), privateKey.size()); - auto output = Signer::sign(input, TWCoinTypeNativeEvmos); + auto output = Proto::SigningOutput(); + ANY_SIGN(input, TWCoinTypeNativeEvmos); + // Please note the signature has been updated according to the serialization of the `StakeAuthorization` message. + // Previous: CvUBCvIBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQSzwEKLGV2bW9zMTJtOWdyZ2FzNjB5azBrdWx0MDc2dnhuc3Jxejh4cGp5OXJwZjNlEixldm1vczE4ZnpxNG5hYzI4Z2ZtYTZncWZ2a3B3cmdwbTVjdGFyMno5bXhmMxpxCmcKKi9jb3Ntb3Muc3Rha2luZy52MWJldGExLlN0YWtlQXV0aG9yaXphdGlvbhI5EjUKM2V2bW9zdmFsb3BlcjF1bWs0MDdlZWQ3YWY2YW52dXQ2bGxnMnpldm5mMGRuMGZlcXFueSABEgYI4LD6pgYSfQpZCk8KKC9ldGhlcm1pbnQuY3J5cHRvLnYxLmV0aHNlY3AyNTZrMS5QdWJLZXkSIwohA4B2WHbj6sH/GWE7z/YW5PRnXYFGaGRAov7gZZI2Fv2nEgQKAggBGAMSIAoaCgZhZXZtb3MSEDQ1MjE0NzUwMDAwMDAwMDAQ+4QLGkAm17CZgB7m+CPVlITnrHosklMTL9zrUeGRs8FL8N0GcRami9zdJ+e3xuXOtJmwP7G6QNh85CRYjFj8a8lpmmJM auto expected = R"( { - "mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CvUBCvIBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQSzwEKLGV2bW9zMTJtOWdyZ2FzNjB5azBrdWx0MDc2dnhuc3Jxejh4cGp5OXJwZjNlEixldm1vczE4ZnpxNG5hYzI4Z2ZtYTZncWZ2a3B3cmdwbTVjdGFyMno5bXhmMxpxCmcKKi9jb3Ntb3Muc3Rha2luZy52MWJldGExLlN0YWtlQXV0aG9yaXphdGlvbhI5EjUKM2V2bW9zdmFsb3BlcjF1bWs0MDdlZWQ3YWY2YW52dXQ2bGxnMnpldm5mMGRuMGZlcXFueSABEgYI4LD6pgYSfQpZCk8KKC9ldGhlcm1pbnQuY3J5cHRvLnYxLmV0aHNlY3AyNTZrMS5QdWJLZXkSIwohA4B2WHbj6sH/GWE7z/YW5PRnXYFGaGRAov7gZZI2Fv2nEgQKAggBGAMSIAoaCgZhZXZtb3MSEDQ1MjE0NzUwMDAwMDAwMDAQ+4QLGkAm17CZgB7m+CPVlITnrHosklMTL9zrUeGRs8FL8N0GcRami9zdJ+e3xuXOtJmwP7G6QNh85CRYjFj8a8lpmmJM" + "mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CvUBCvIBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQSzwEKLGV2bW9zMTJtOWdyZ2FzNjB5azBrdWx0MDc2dnhuc3Jxejh4cGp5OXJwZjNlEixldm1vczE4ZnpxNG5hYzI4Z2ZtYTZncWZ2a3B3cmdwbTVjdGFyMno5bXhmMxpxCmcKKi9jb3Ntb3Muc3Rha2luZy52MWJldGExLlN0YWtlQXV0aG9yaXphdGlvbhI5IAESNQozZXZtb3N2YWxvcGVyMXVtazQwN2VlZDdhZjZhbnZ1dDZsbGcyemV2bmYwZG4wZmVxcW55EgYI4LD6pgYSfQpZCk8KKC9ldGhlcm1pbnQuY3J5cHRvLnYxLmV0aHNlY3AyNTZrMS5QdWJLZXkSIwohA4B2WHbj6sH/GWE7z/YW5PRnXYFGaGRAov7gZZI2Fv2nEgQKAggBGAMSIAoaCgZhZXZtb3MSEDQ1MjE0NzUwMDAwMDAwMDAQ+4QLGkBXaTo3nk5EMFW9Euheez5ADx2bWo7XisNJ5vuGj1fKXh6CGNJGfJj/q1XUkBzaCvPNg+EcFHgtJdVSyF4cJZTg" })"; assertJSONEqual(output.serialized(), expected); } diff --git a/tests/chains/Evmos/TransactionCompilerTests.cpp b/tests/chains/Evmos/TransactionCompilerTests.cpp index 72d8ab82606..c58e374fb1d 100644 --- a/tests/chains/Evmos/TransactionCompilerTests.cpp +++ b/tests/chains/Evmos/TransactionCompilerTests.cpp @@ -5,7 +5,6 @@ // file LICENSE at the root of the source code distribution tree. #include "Base64.h" -#include "Cosmos/Signer.h" #include "HexCoding.h" #include "proto/Cosmos.pb.h" #include "proto/TransactionCompiler.pb.h" @@ -95,8 +94,7 @@ TEST(EvmosCompiler, CompileWithSignatures) { EXPECT_EQ(output.error(), Common::Proto::OK); EXPECT_EQ(output.serialized(), expectedTx); - EXPECT_EQ(output.signature(), ""); - EXPECT_EQ(hex(output.signature()), ""); + EXPECT_EQ(hex(output.signature()), hex(signature)); } } From ea4e1c181925625e7bf63afcc7b26c3a051153de Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Fri, 17 Nov 2023 16:33:43 +0100 Subject: [PATCH 016/128] [Rust]: Install rustup in a correct way (#3562) --- tools/install-sys-dependencies-mac | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/install-sys-dependencies-mac b/tools/install-sys-dependencies-mac index 2e7fc69a00e..9ff7f1a41fd 100755 --- a/tools/install-sys-dependencies-mac +++ b/tools/install-sys-dependencies-mac @@ -3,3 +3,12 @@ set -e brew install boost ninja xcodegen xcbeautify + +if command -v rustup &> /dev/null +then + echo "Rustup is already installed." +else + echo "Rustup is not installed. Installing it now." + brew install rustup + rustup-init -y --default-toolchain none --no-update-default-toolchain +fi From b8beaec5566741b1bf0c41594552c3dc48297d43 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 22 Nov 2023 16:54:19 +0100 Subject: [PATCH 017/128] [Rust]: Codegen tools (#3566) --- codegen-v2/Cargo.lock | 82 ++++- codegen-v2/Cargo.toml | 3 + codegen-v2/src/codegen/mod.rs | 1 + .../src/codegen/rust/blockchain_type.rs | 133 ++++++++ codegen-v2/src/codegen/rust/coin_crate.rs | 88 ++++++ codegen-v2/src/codegen/rust/coin_id.rs | 46 +++ .../codegen/rust/coin_integration_tests.rs | 133 ++++++++ codegen-v2/src/codegen/rust/mod.rs | 96 ++++++ codegen-v2/src/codegen/rust/new_blockchain.rs | 30 ++ codegen-v2/src/codegen/rust/toml_editor.rs | 112 +++++++ codegen-v2/src/lib.rs | 17 ++ codegen-v2/src/main.rs | 13 + codegen-v2/src/utils.rs | 123 ++++++++ rust/Cargo.lock | 34 +++ rust/tw_any_coin/Cargo.toml | 2 +- rust/tw_any_coin/src/ffi/tw_any_address.rs | 8 + rust/tw_any_coin/src/ffi/tw_any_signer.rs | 3 + rust/tw_any_coin/src/ffi/tw_message_signer.rs | 5 + .../src/ffi/tw_transaction_compiler.rs | 3 + .../src/test_utils/address_utils.rs | 105 +++++++ rust/tw_any_coin/src/test_utils/mod.rs | 11 +- .../tests/chains/aptos/aptos_address.rs | 73 +++++ rust/tw_any_coin/tests/chains/aptos/mod.rs | 1 + .../tests/chains/bitcoin/bitcoin_address.rs | 49 +++ rust/tw_any_coin/tests/chains/bitcoin/mod.rs | 7 + .../tests/chains/cosmos/cosmos_address.rs | 83 +++++ .../tests/chains/cosmos/cosmos_sign.rs | 65 ++++ rust/tw_any_coin/tests/chains/cosmos/mod.rs | 8 + .../tests/chains/ethereum/ethereum_address.rs | 48 +++ .../ethereum/ethereum_compile.rs} | 16 +- .../ethereum/ethereum_message_sign.rs} | 18 +- .../tests/chains/ethereum/ethereum_sign.rs | 56 ++++ rust/tw_any_coin/tests/chains/ethereum/mod.rs | 10 + .../internet_computer_address.rs | 64 ++++ .../tests/chains/internet_computer/mod.rs | 7 + rust/tw_any_coin/tests/chains/mod.rs | 4 + .../tests/chains/native_evmos/mod.rs | 1 + .../native_evmos/native_evmos_address.rs | 71 +++++ .../tests/chains/native_injective/mod.rs | 5 +- .../native_injective_address.rs | 71 +++++ .../tw_any_coin/tests/chains/thorchain/mod.rs | 3 +- .../chains/thorchain/thorchain_address.rs | 75 +++++ .../chains/thorchain/thorchain_compile.rs | 6 +- .../tests/chains/thorchain/thorchain_sign.rs | 11 +- .../tests/coin_address_derivation_tests.rs | 167 ++++++++++ .../tests/tw_any_address_ffi_tests.rs | 287 ------------------ .../tests/tw_any_signer_ffi_tests.rs | 101 ------ rust/tw_coin_registry/Cargo.toml | 7 + rust/tw_coin_registry/build.rs | 105 +++++++ rust/tw_coin_registry/src/blockchain_type.rs | 39 +-- rust/tw_coin_registry/src/dispatcher.rs | 15 +- rust/tw_coin_registry/src/lib.rs | 5 +- rust/tw_coin_registry/src/registry.rs | 7 + rust/wallet_core_rs/src/ffi/ethereum/abi.rs | 15 +- rust/wallet_core_rs/src/ffi/ethereum/rlp.rs | 6 +- rust/wallet_core_rs/tests/ethereum_abi.rs | 13 +- rust/wallet_core_rs/tests/ethereum_rlp.rs | 12 +- 57 files changed, 2086 insertions(+), 493 deletions(-) create mode 100644 codegen-v2/src/codegen/rust/blockchain_type.rs create mode 100644 codegen-v2/src/codegen/rust/coin_crate.rs create mode 100644 codegen-v2/src/codegen/rust/coin_id.rs create mode 100644 codegen-v2/src/codegen/rust/coin_integration_tests.rs create mode 100644 codegen-v2/src/codegen/rust/mod.rs create mode 100644 codegen-v2/src/codegen/rust/new_blockchain.rs create mode 100644 codegen-v2/src/codegen/rust/toml_editor.rs create mode 100644 codegen-v2/src/utils.rs create mode 100644 rust/tw_any_coin/src/test_utils/address_utils.rs create mode 100644 rust/tw_any_coin/tests/chains/aptos/aptos_address.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_address.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/mod.rs create mode 100644 rust/tw_any_coin/tests/chains/cosmos/cosmos_address.rs create mode 100644 rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs create mode 100644 rust/tw_any_coin/tests/chains/cosmos/mod.rs create mode 100644 rust/tw_any_coin/tests/chains/ethereum/ethereum_address.rs rename rust/tw_any_coin/tests/{tw_transaction_compiler_ffi_tests.rs => chains/ethereum/ethereum_compile.rs} (92%) rename rust/tw_any_coin/tests/{tw_message_signer_ffi_tests.rs => chains/ethereum/ethereum_message_sign.rs} (89%) create mode 100644 rust/tw_any_coin/tests/chains/ethereum/ethereum_sign.rs create mode 100644 rust/tw_any_coin/tests/chains/ethereum/mod.rs create mode 100644 rust/tw_any_coin/tests/chains/internet_computer/internet_computer_address.rs create mode 100644 rust/tw_any_coin/tests/chains/internet_computer/mod.rs create mode 100644 rust/tw_any_coin/tests/chains/native_evmos/native_evmos_address.rs create mode 100644 rust/tw_any_coin/tests/chains/native_injective/native_injective_address.rs create mode 100644 rust/tw_any_coin/tests/chains/thorchain/thorchain_address.rs create mode 100644 rust/tw_any_coin/tests/coin_address_derivation_tests.rs delete mode 100644 rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs create mode 100644 rust/tw_coin_registry/build.rs diff --git a/codegen-v2/Cargo.lock b/codegen-v2/Cargo.lock index 32a0596b5ae..254400df25c 100644 --- a/codegen-v2/Cargo.lock +++ b/codegen-v2/Cargo.lock @@ -27,11 +27,23 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" name = "codegen-v2" version = "0.1.0" dependencies = [ + "convert_case", "handlebars", "heck", + "pathdiff", "serde", "serde_json", "serde_yaml", + "toml_edit", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", ] [[package]] @@ -63,6 +75,12 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "generic-array" version = "0.14.7" @@ -93,6 +111,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" + [[package]] name = "heck" version = "0.4.1" @@ -106,7 +130,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.2", ] [[package]] @@ -130,12 +164,24 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + [[package]] name = "once_cell" version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "pest" version = "2.5.7" @@ -241,7 +287,7 @@ version = "0.9.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c" dependencies = [ - "indexmap", + "indexmap 1.9.3", "itoa", "ryu", "serde", @@ -290,6 +336,23 @@ dependencies = [ "syn", ] +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + [[package]] name = "typenum" version = "1.16.0" @@ -308,6 +371,12 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[package]] name = "unsafe-libyaml" version = "0.2.8" @@ -319,3 +388,12 @@ name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "winnow" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +dependencies = [ + "memchr", +] diff --git a/codegen-v2/Cargo.toml b/codegen-v2/Cargo.toml index 7598a3ad756..e024e4024ff 100644 --- a/codegen-v2/Cargo.toml +++ b/codegen-v2/Cargo.toml @@ -12,8 +12,11 @@ name = "parser" path = "src/main.rs" [dependencies] +convert_case = "0.6.0" +pathdiff = "0.2.1" serde = { version = "1.0.159", features = ["derive"] } serde_json = "1.0.95" serde_yaml = "0.9.21" +toml_edit = "0.21.0" handlebars = "4.3.6" heck = "0.4.1" diff --git a/codegen-v2/src/codegen/mod.rs b/codegen-v2/src/codegen/mod.rs index 52487305973..b0d31c99e15 100644 --- a/codegen-v2/src/codegen/mod.rs +++ b/codegen-v2/src/codegen/mod.rs @@ -4,4 +4,5 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +pub mod rust; pub mod swift; diff --git a/codegen-v2/src/codegen/rust/blockchain_type.rs b/codegen-v2/src/codegen/rust/blockchain_type.rs new file mode 100644 index 00000000000..debce4802d6 --- /dev/null +++ b/codegen-v2/src/codegen/rust/blockchain_type.rs @@ -0,0 +1,133 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::rust::toml_editor::Dependencies; +use crate::codegen::rust::{rust_source_directory, CoinItem}; +use crate::utils::FileContent; +use crate::Result; +use std::path::{Path, PathBuf}; + +const BLOCKCHAIN_TYPE_START: &str = "start_of_blockchain_type"; +const BLOCKCHAIN_TYPE_END: &str = "end_of_blockchain_type"; +const BLOCKCHAIN_ENTRIES_START: &str = "start_of_blockchain_entries"; +const BLOCKCHAIN_ENTRIES_END: &str = "end_of_blockchain_entries"; +const BLOCKCHAIN_DISPATCHER_START: &str = "start_of_blockchain_dispatcher"; +const BLOCKCHAIN_DISPATCHER_END: &str = "end_of_blockchain_dispatcher"; + +pub fn coin_registry_directory() -> PathBuf { + rust_source_directory().join("tw_coin_registry") +} + +pub fn blockchain_type_path() -> PathBuf { + coin_registry_directory() + .join("src") + .join("blockchain_type.rs") +} + +pub fn dispatcher_path() -> PathBuf { + coin_registry_directory().join("src").join("dispatcher.rs") +} + +pub struct CoinRegistry { + coin: CoinItem, +} + +impl CoinRegistry { + pub fn new(coin: CoinItem) -> CoinRegistry { + CoinRegistry { coin } + } + + pub fn add(self, path_to_new_blockchain_crate: &Path) -> Result<()> { + self.add_blockchain_crate_to_manifest_file(path_to_new_blockchain_crate)?; + self.add_blockchain_variant()?; + self.add_use_of_blockchain_entry()?; + self.add_blockchain_entry()?; + self.add_blockchain_dispatcher() + } + + fn add_blockchain_crate_to_manifest_file( + &self, + path_to_new_blockchain_crate: &Path, + ) -> Result<()> { + let path_to_cargo_manifest = coin_registry_directory().join("Cargo.toml"); + Dependencies::new(path_to_cargo_manifest).insert_dependency( + &self.coin.id.to_tw_crate_name(), + path_to_new_blockchain_crate, + ) + } + + fn add_blockchain_variant(&self) -> Result<()> { + let blockchain_type_rs_path = blockchain_type_path(); + let blockchain_type = self.coin.blockchain_type(); + + let mut blockchain_type_rs = FileContent::read(blockchain_type_rs_path)?; + + { + let mut enum_region = blockchain_type_rs + .find_region_with_comments(BLOCKCHAIN_TYPE_START, BLOCKCHAIN_TYPE_END)?; + enum_region.push_line(format!(" {blockchain_type},")); + enum_region.sort(); + } + + blockchain_type_rs.write() + } + + fn add_use_of_blockchain_entry(&self) -> Result<()> { + let dispatcher_rs_path = dispatcher_path(); + let blockchain_entry = self.coin.blockchain_entry(); + let tw_crate_name = self.coin.id.to_tw_crate_name(); + + let mut dispatcher_rs = FileContent::read(dispatcher_rs_path)?; + + { + let import_pattern = "use "; + let mut last_entry = dispatcher_rs.rfind_line(|line| line.contains(import_pattern))?; + last_entry.push_line_after(format!("use {tw_crate_name}::entry::{blockchain_entry};")); + } + + dispatcher_rs.write() + } + + fn add_blockchain_entry(&self) -> Result<()> { + let dispatcher_rs_path = dispatcher_path(); + let blockchain_entry = self.coin.blockchain_entry(); + let blockchain_entry_const = self.coin.blockchain_entry_upper_snake(); + + let mut dispatcher_rs = FileContent::read(dispatcher_rs_path)?; + + { + let mut entries_region = dispatcher_rs + .find_region_with_comments(BLOCKCHAIN_ENTRIES_START, BLOCKCHAIN_ENTRIES_END)?; + entries_region.push_line(format!( + "const {blockchain_entry_const}: {blockchain_entry} = {blockchain_entry};" + )); + entries_region.sort(); + } + + dispatcher_rs.write() + } + + fn add_blockchain_dispatcher(&self) -> Result<()> { + let dispatcher_rs_path = dispatcher_path(); + let blockchain_type = self.coin.blockchain_type(); + let blockchain_entry_const = self.coin.blockchain_entry_upper_snake(); + + let mut dispatcher_rs = FileContent::read(dispatcher_rs_path)?; + + { + let mut dispatcher_region = dispatcher_rs.find_region_with_comments( + BLOCKCHAIN_DISPATCHER_START, + BLOCKCHAIN_DISPATCHER_END, + )?; + dispatcher_region.push_line(format!( + " BlockchainType::{blockchain_type} => Ok(&{blockchain_entry_const})," + )); + dispatcher_region.sort(); + } + + dispatcher_rs.write() + } +} diff --git a/codegen-v2/src/codegen/rust/coin_crate.rs b/codegen-v2/src/codegen/rust/coin_crate.rs new file mode 100644 index 00000000000..19a957d9978 --- /dev/null +++ b/codegen-v2/src/codegen/rust/coin_crate.rs @@ -0,0 +1,88 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::rust::coin_id::CoinId; +use crate::codegen::rust::{chains_directory, rs_header, CoinItem}; +use crate::{Error, Result}; +use std::path::PathBuf; +use std::{fs, io}; + +pub fn coin_source_directory(id: &CoinId) -> PathBuf { + chains_directory().join(id.to_tw_crate_name()) +} + +pub struct CoinCrate { + coin: CoinItem, +} + +impl CoinCrate { + pub fn new(coin: CoinItem) -> CoinCrate { + CoinCrate { coin } + } + + /// Creates a Cargo crate with `entry.rs` file. + /// Returns the path to the create crate. + pub fn create(self) -> Result { + let header = rs_header(); + + let blockchain_path = coin_source_directory(&self.coin.id); + let blockchain_toml_path = blockchain_path.join("Cargo.toml"); + + let blockchain_src_path = blockchain_path.join("src"); + let blockchain_lib_rs_path = blockchain_src_path.join("lib.rs"); + let blockchain_entry_path = blockchain_src_path.join("entry.rs"); + + let tw_crate_name = self.coin.id.to_tw_crate_name(); + let blockchain_name = self.coin.blockchain_type(); + + if blockchain_path.exists() { + return Err(Error::IoError(io::Error::new( + io::ErrorKind::AlreadyExists, + "blockchain already exists", + ))); + } + + fs::create_dir(&blockchain_path)?; + fs::create_dir(&blockchain_src_path)?; + + let blockchain_toml = format!( + r#"[package] +name = "{tw_crate_name}" +version = "0.1.0" +edition = "2021" + +[dependencies] +tw_coin_entry = {{ path = "../../tw_coin_entry" }} +tw_proto = {{ path = "../../tw_proto" }} +"# + ); + fs::write(blockchain_toml_path, blockchain_toml)?; + + let blockchain_lib_rs = format!( + r#"{header} + +pub mod entry; +"# + ); + fs::write(blockchain_lib_rs_path, blockchain_lib_rs)?; + + let blockchain_entry = format!( + r#"{header} + +use tw_coin_entry::coin_entry::CoinEntry; + +pub struct {blockchain_name}Entry; + +impl CoinEntry for {blockchain_name}Entry {{ + // TODO declare associated types and implement methods from the 'CoinEntry' trait. +}} +"# + ); + fs::write(blockchain_entry_path, blockchain_entry)?; + + Ok(blockchain_path) + } +} diff --git a/codegen-v2/src/codegen/rust/coin_id.rs b/codegen-v2/src/codegen/rust/coin_id.rs new file mode 100644 index 00000000000..6f0eaed7c36 --- /dev/null +++ b/codegen-v2/src/codegen/rust/coin_id.rs @@ -0,0 +1,46 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::{Error, Result}; +use serde::de::Error as SerdeError; +use serde::{Deserialize, Deserializer}; + +#[derive(Clone, Eq, PartialEq)] +pub struct CoinId(String); + +impl CoinId { + /// Returns `Ok` if only the given `id` is a valid Rust identifier. + pub fn new(id: String) -> Result { + let first_letter = id + .chars() + .next() + .ok_or(Error::RegistryError("Invalid 'id'".to_string()))?; + let valid_chars = id.chars().all(|ch| ch.is_ascii_alphanumeric() || ch == '_'); + + if first_letter.is_numeric() || !valid_chars { + return Err(Error::RegistryError("Invalid 'id'".to_string())); + } + Ok(CoinId(id)) + } + + pub fn to_tw_crate_name(&self) -> String { + format!("tw_{}", self.0) + } + + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl<'de> Deserialize<'de> for CoinId { + fn deserialize(deserializer: D) -> std::result::Result + where + D: Deserializer<'de>, + { + let id = String::deserialize(deserializer)?; + CoinId::new(id).map_err(|e| SerdeError::custom(format!("{e:?}"))) + } +} diff --git a/codegen-v2/src/codegen/rust/coin_integration_tests.rs b/codegen-v2/src/codegen/rust/coin_integration_tests.rs new file mode 100644 index 00000000000..3fde3ff1795 --- /dev/null +++ b/codegen-v2/src/codegen/rust/coin_integration_tests.rs @@ -0,0 +1,133 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::rust::coin_id::CoinId; +use crate::codegen::rust::{rs_header, tw_any_coin_directory, CoinItem}; +use crate::utils::FileContent; +use crate::{Error, Result}; +use std::fs; +use std::path::PathBuf; + +pub fn chains_integration_tests_directory() -> PathBuf { + tw_any_coin_directory().join("tests").join("chains") +} + +pub fn coin_integration_tests_directory(id: &CoinId) -> PathBuf { + chains_integration_tests_directory().join(id.as_str()) +} + +pub struct CoinIntegrationTests { + coin: CoinItem, +} + +impl CoinIntegrationTests { + pub fn new(coin: CoinItem) -> CoinIntegrationTests { + CoinIntegrationTests { coin } + } + + pub fn create(self) -> Result { + let blockchain_tests_path = self.coin_tests_directory(); + if blockchain_tests_path.exists() { + return Ok(blockchain_tests_path); + } + + fs::create_dir(&blockchain_tests_path)?; + + self.list_blockchain_in_chains_mod()?; + self.create_address_tests()?; + self.create_sign_tests()?; + self.create_chain_tests_mod_rs()?; + + Ok(blockchain_tests_path) + } + + fn coin_tests_directory(&self) -> PathBuf { + coin_integration_tests_directory(&self.coin.id) + } + + fn create_address_tests(&self) -> Result<()> { + let header = rs_header(); + let chain_id = self.coin.id.as_str(); + let address_tests_path = self + .coin_tests_directory() + .join(format!("{chain_id}_address.rs")); + + let address_tests_rs = format!( + r#"{header} + +#[test] +fn test_{chain_id}_address_normalization() {{ + todo!() +}} + +#[test] +fn test_{chain_id}_address_is_valid() {{ + todo!() +}} + +#[test] +fn test_{chain_id}_address_invalid() {{ + todo!() +}} + +#[test] +fn test_{chain_id}_address_get_data() {{ + todo!() +}} +"# + ); + fs::write(address_tests_path, address_tests_rs).map_err(Error::from) + } + + fn create_sign_tests(&self) -> Result<()> { + let header = rs_header(); + let chain_id = self.coin.id.as_str(); + let sign_tests_path = self + .coin_tests_directory() + .join(format!("{chain_id}_sign.rs")); + + let sign_tests_rs = format!( + r#"{header} + +#[test] +fn test_{chain_id}_sign() {{ + todo!() +}} +"# + ); + fs::write(sign_tests_path, sign_tests_rs).map_err(Error::from) + } + + fn create_chain_tests_mod_rs(&self) -> Result<()> { + let header = rs_header(); + let chain_id = self.coin.id.as_str(); + let blockchain_tests_mod_path = self.coin_tests_directory().join("mod.rs"); + + let blockchain_mod_rs = format!( + r#"{header} + +mod {chain_id}_address; +mod {chain_id}_sign; +"# + ); + fs::write(blockchain_tests_mod_path, blockchain_mod_rs).map_err(Error::from) + } + + fn list_blockchain_in_chains_mod(&self) -> Result<()> { + let chains_mod_path = chains_integration_tests_directory().join("mod.rs"); + let chain_id = self.coin.id.as_str(); + + let mut chains_mod_rs = FileContent::read(chains_mod_path)?; + + { + let mod_pattern = "mod "; + let mut last_mod = chains_mod_rs.rfind_line(|line| line.starts_with(mod_pattern))?; + last_mod.push_line_after(format!("mod {chain_id};")); + } + + chains_mod_rs.write() + } +} diff --git a/codegen-v2/src/codegen/rust/mod.rs b/codegen-v2/src/codegen/rust/mod.rs new file mode 100644 index 00000000000..b0d1a55910b --- /dev/null +++ b/codegen-v2/src/codegen/rust/mod.rs @@ -0,0 +1,96 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::rust::coin_id::CoinId; +use crate::{current_year, Error, Result}; +use convert_case::{Case, Casing}; +use std::path::PathBuf; +use std::{env, fs}; + +pub mod blockchain_type; +pub mod coin_crate; +pub mod coin_id; +pub mod coin_integration_tests; +pub mod new_blockchain; +pub mod toml_editor; + +pub fn rust_source_directory() -> PathBuf { + PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()) + .join("..") + .join("rust") +} + +pub fn chains_directory() -> PathBuf { + rust_source_directory().join("chains") +} + +pub fn tw_any_coin_directory() -> PathBuf { + rust_source_directory().join("tw_any_coin") +} + +pub fn workspace_toml_path() -> PathBuf { + rust_source_directory().join("Cargo.toml") +} + +pub fn registry_json_path() -> PathBuf { + PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()) + .join("..") + .join("registry.json") +} + +pub fn rs_header() -> String { + let current_year = current_year(); + format!( + r#"// Copyright © 2017-{current_year} Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree."# + ) +} + +#[derive(Clone, Deserialize)] +pub struct CoinItem { + pub id: CoinId, + pub name: String, + pub blockchain: String, +} + +impl CoinItem { + /// Transforms a coin name to a Rust name. + /// https://github.com/trustwallet/wallet-core/blob/3769f31b7d0c75126b2f426bb065364429aaa379/codegen/lib/coin_skeleton_gen.rb#L15-L22 + pub fn coin_type(&self) -> String { + self.name.replace([' ', '.', '-'], "") + } + + /// Returns the blockchain type in `UpperCamel` case. + pub fn blockchain_type(&self) -> String { + self.blockchain.to_case(Case::UpperCamel) + } + + /// Returns the blockchain type in `UPPER_SNAKE` case. + pub fn blockchain_entry_upper_snake(&self) -> String { + self.blockchain.to_case(Case::UpperSnake) + } + + /// Returns a Rust blockchain entry of the blockchain. + pub fn blockchain_entry(&self) -> String { + format!("{}Entry", self.blockchain_type()) + } +} + +pub(crate) fn read_coin_from_registry(coin: &CoinId) -> Result { + let registry_path = registry_json_path(); + + let registry_bytes = fs::read(registry_path)?; + let coins: Vec = + serde_json::from_slice(®istry_bytes).map_err(|e| Error::RegistryError(e.to_string()))?; + + coins + .into_iter() + .find(|item| item.id == *coin) + .ok_or(Error::InvalidCommand) +} diff --git a/codegen-v2/src/codegen/rust/new_blockchain.rs b/codegen-v2/src/codegen/rust/new_blockchain.rs new file mode 100644 index 00000000000..252bbcc92da --- /dev/null +++ b/codegen-v2/src/codegen/rust/new_blockchain.rs @@ -0,0 +1,30 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::rust::blockchain_type::CoinRegistry; +use crate::codegen::rust::coin_crate::CoinCrate; +use crate::codegen::rust::coin_id::CoinId; +use crate::codegen::rust::coin_integration_tests::CoinIntegrationTests; +use crate::codegen::rust::toml_editor::Workspace; +use crate::codegen::rust::{read_coin_from_registry, workspace_toml_path}; +use crate::Result; + +pub fn new_blockchain(coin: &str) -> Result<()> { + let coin_id = CoinId::new(coin.to_string())?; + let coin_item = read_coin_from_registry(&coin_id)?; + + // Create blockchain's crate. + let blockchain_crate_path = CoinCrate::new(coin_item.clone()).create()?; + + // Insert the created crate to the workspace. + Workspace::new(workspace_toml_path()).insert_crate(&blockchain_crate_path)?; + // Create integration tests. + CoinIntegrationTests::new(coin_item.clone()).create()?; + // Add the new blockchain to the `tw_coin_registry`. + CoinRegistry::new(coin_item).add(&blockchain_crate_path)?; + + Ok(()) +} diff --git a/codegen-v2/src/codegen/rust/toml_editor.rs b/codegen-v2/src/codegen/rust/toml_editor.rs new file mode 100644 index 00000000000..da33d5fe270 --- /dev/null +++ b/codegen-v2/src/codegen/rust/toml_editor.rs @@ -0,0 +1,112 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::{Error, Result}; +use std::fs; +use std::path::{Path, PathBuf}; +use std::str::FromStr; +use toml_edit::{Document, InlineTable, Item, Value}; + +const NEW_LINE_TAB_DECORATOR: &str = "\n "; +const NO_DECORATOR: &str = ""; + +pub struct Workspace { + path_to_toml: PathBuf, +} + +impl Workspace { + pub fn new(path_to_toml: PathBuf) -> Workspace { + Workspace { path_to_toml } + } + + pub fn insert_crate(self, path_to_crate: &Path) -> Result<()> { + let manifest = fs::read_to_string(&self.path_to_toml)?; + let mut manifest = Document::from_str(&manifest)?; + + let members = manifest["workspace"]["members"] + .as_array_mut() + .ok_or(Error::TomlFormat( + "Invalid 'workspace' TOML format".to_string(), + ))?; + + // Try to get a path to the crate relative to the `Cargo.toml`. + let relative_path_to_crate = relative_path_to_crate(&self.path_to_toml, path_to_crate)?; + + // Push the new member, sort and save the manifest. + + let relative_path_to_crate_decorated = Value::from(relative_path_to_crate.to_string()) + .decorated(NEW_LINE_TAB_DECORATOR, NO_DECORATOR); + + members.push_formatted(relative_path_to_crate_decorated); + members.sort_by(|x, y| x.as_str().cmp(&y.as_str())); + + fs::write(self.path_to_toml, manifest.to_string())?; + Ok(()) + } +} + +pub struct Dependencies { + path_to_toml: PathBuf, +} + +impl Dependencies { + pub fn new(path_to_toml: PathBuf) -> Dependencies { + Dependencies { path_to_toml } + } + + pub fn insert_dependency(self, dep_name: &str, path_to_dep_crate: &Path) -> Result<()> { + let manifest = fs::read_to_string(&self.path_to_toml)?; + let mut manifest = Document::from_str(&manifest)?; + + let dependencies = manifest["dependencies"] + .as_table_like_mut() + .ok_or(Error::TomlFormat("Invalid 'Cargo.toml' format".to_string()))?; + + // Try to get a path to the crate relative to the `Cargo.toml`. + let relative_path_to_crate = relative_path_to_crate(&self.path_to_toml, path_to_dep_crate)?; + + // Create the new dependency member (aka a TOML inline table with `path` key-value). + let mut new_member = InlineTable::new(); + new_member.insert("path", relative_path_to_crate.into()); + + // Push the new member, sort and save the manifest. + dependencies.insert(dep_name, Item::Value(Value::InlineTable(new_member).into())); + dependencies.sort_values(); + + fs::write(self.path_to_toml, manifest.to_string())?; + + Ok(()) + } +} + +/// Returns a path to the dependency accordingly to the Cargo manifest file. +/// The result string can be put to `Cargo.toml` as: +/// ```toml +/// tw_foo = { path = "" } +/// ``` +fn relative_path_to_crate( + path_to_cargo_manifest: &Path, + path_to_dependency: &Path, +) -> Result { + let absolute_path_to_crate_directory = path_to_cargo_manifest + .parent() + .ok_or_else(|| Error::io_error_other("Cannot get a parent directory".to_string()))? + .canonicalize()?; + let absolute_path_to_dependency = path_to_dependency.canonicalize()?; + + let relative_path_to_dependency = pathdiff::diff_paths( + absolute_path_to_dependency, + absolute_path_to_crate_directory, + ) + .ok_or_else(|| { + Error::io_error_other("Cannot get a relative path to the dependency".to_string()) + })? + .to_str() + .ok_or_else(|| Error::io_error_other("Invalid path to the crate".to_string()))? + .to_string(); + + Ok(relative_path_to_dependency) +} diff --git a/codegen-v2/src/lib.rs b/codegen-v2/src/lib.rs index ca6556bb44d..788f82cda8a 100644 --- a/codegen-v2/src/lib.rs +++ b/codegen-v2/src/lib.rs @@ -9,12 +9,15 @@ extern crate serde; use handlebars::{RenderError, TemplateError}; use serde_yaml::Error as YamlError; +use std::io; use std::io::Error as IoError; +use toml_edit::TomlError; pub mod codegen; pub mod manifest; #[cfg(test)] mod tests; +pub mod utils; pub type Result = std::result::Result; @@ -25,9 +28,17 @@ pub enum Error { RenderError(RenderError), TemplateError(TemplateError), BadFormat(String), + RegistryError(String), + TomlFormat(String), InvalidCommand, } +impl Error { + pub fn io_error_other(err: String) -> Error { + Error::IoError(IoError::new(io::ErrorKind::Other, err)) + } +} + impl From for Error { fn from(err: IoError) -> Self { Error::IoError(err) @@ -52,6 +63,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: TomlError) -> Self { + Error::TomlFormat(err.to_string()) + } +} + fn current_year() -> u64 { use std::time::{SystemTime, UNIX_EPOCH}; diff --git a/codegen-v2/src/main.rs b/codegen-v2/src/main.rs index 1be255b2f20..39e5da606cf 100644 --- a/codegen-v2/src/main.rs +++ b/codegen-v2/src/main.rs @@ -4,6 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use libparser::codegen::rust::new_blockchain::new_blockchain; use libparser::codegen::swift::RenderIntput; use libparser::manifest::parse_dir; use libparser::{Error, Result}; @@ -18,6 +19,18 @@ fn main() -> Result<()> { match args[1].as_str() { "swift" => generate_swift_bindings(), + "rust" => generate_rust(&args[2..]), + _ => Err(Error::InvalidCommand), + } +} + +fn generate_rust(args: &[String]) -> Result<()> { + if args.len() < 2 { + return Err(Error::InvalidCommand); + } + + match args[0].as_str() { + "new-blockchain" => new_blockchain(&args[1]), _ => Err(Error::InvalidCommand), } } diff --git a/codegen-v2/src/utils.rs b/codegen-v2/src/utils.rs new file mode 100644 index 00000000000..bb4b01d2902 --- /dev/null +++ b/codegen-v2/src/utils.rs @@ -0,0 +1,123 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::{Error, Result}; +use std::fs; +use std::path::{Path, PathBuf}; + +pub fn read_lines>(path: P) -> Result> { + let lines = fs::read_to_string(path)? + .split('\n') + .map(|line| line.to_string()) + .collect(); + Ok(lines) +} + +pub fn write_lines>(path: P, lines: Vec) -> Result<()> { + let content = lines.join("\n"); + fs::write(path, content).map_err(Error::from) +} + +pub struct FileContent { + path: PathBuf, + lines: Vec, +} + +impl FileContent { + pub fn read(path: PathBuf) -> Result { + read_lines(&path).map(|lines| FileContent { path, lines }) + } + + pub fn find_region_with_comments( + &mut self, + start_comment: &str, + end_comment: &str, + ) -> Result> { + // Find the position of the `start_comment`. + let start_comment_at = self + .lines + .iter() + .position(|line| line.contains(start_comment)) + .ok_or_else(|| { + Error::io_error_other(format!("Cannot find the `{start_comment}` line")) + })?; + let end_comment_at = self + .lines + .iter() + .skip(start_comment_at) + .position(|line| line.contains(end_comment)) + .ok_or_else(|| { + Error::io_error_other(format!("Cannot find the `{end_comment}` line")) + })? + + start_comment_at; + + let region_starts_at = start_comment_at + 1; + let region_ends_at = end_comment_at - 1; + + if region_starts_at > region_ends_at { + return Err(Error::io_error_other(format!( + "There must be the content between {start_comment} and {end_comment}" + ))); + } + + Ok(FileRegion { + lines: &mut self.lines, + region_starts_at, + region_ends_at, + }) + } + + pub fn rfind_line(&mut self, f: F) -> Result> + where + F: Fn(&str) -> bool, + { + let line_idx = self + .lines + .iter() + .rposition(|line| f(&line)) + .ok_or_else(|| { + Error::io_error_other(format!( + "{:?} file does not contain a required pattern", + self.path + )) + })?; + Ok(LinePointer { + lines: &mut self.lines, + line_idx, + }) + } + + pub fn write(self) -> Result<()> { + write_lines(self.path, self.lines) + } +} + +pub struct FileRegion<'a> { + lines: &'a mut Vec, + region_starts_at: usize, + region_ends_at: usize, +} + +impl<'a> FileRegion<'a> { + pub fn push_line(&mut self, line: String) { + self.lines.insert(self.region_ends_at + 1, line); + } + + pub fn sort(&mut self) { + self.lines[self.region_starts_at..self.region_ends_at].sort() + } +} + +pub struct LinePointer<'a> { + lines: &'a mut Vec, + line_idx: usize, +} + +impl<'a> LinePointer<'a> { + pub fn push_line_after(&mut self, line: String) { + self.lines.insert(self.line_idx + 1, line); + } +} diff --git a/rust/Cargo.lock b/rust/Cargo.lock index d58c01c250c..24a18ba843f 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -653,6 +653,12 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -1266,6 +1272,12 @@ dependencies = [ "semver", ] +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + [[package]] name = "ryu" version = "1.0.13" @@ -1489,6 +1501,25 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.37", +] + [[package]] name = "subtle" version = "2.4.1" @@ -1674,9 +1705,12 @@ dependencies = [ name = "tw_coin_registry" version = "0.1.0" dependencies = [ + "itertools", "lazy_static", "serde", "serde_json", + "strum", + "strum_macros", "tw_aptos", "tw_bitcoin", "tw_coin_entry", diff --git a/rust/tw_any_coin/Cargo.toml b/rust/tw_any_coin/Cargo.toml index bb8e9314f3d..e2aeea6d021 100644 --- a/rust/tw_any_coin/Cargo.toml +++ b/rust/tw_any_coin/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] tw_coin_entry = { path = "../tw_coin_entry" } tw_coin_registry = { path = "../tw_coin_registry" } +tw_encoding = { path = "../tw_encoding" } tw_hash = { path = "../tw_hash" } tw_keypair = { path = "../tw_keypair" } tw_memory = { path = "../tw_memory" } @@ -19,7 +20,6 @@ serde = { version = "1.0.163", features = ["derive"] } serde_json = { version = "1.0.96" } tw_any_coin = { path = "./", features = ["test-utils"] } tw_cosmos_sdk = { path = "../tw_cosmos_sdk", features = ["test-utils"] } -tw_encoding = { path = "../tw_encoding" } tw_keypair = { path = "../tw_keypair", features = ["test-utils"] } tw_memory = { path = "../tw_memory", features = ["test-utils"] } tw_misc = { path = "../tw_misc", features = ["test-utils"] } diff --git a/rust/tw_any_coin/src/ffi/tw_any_address.rs b/rust/tw_any_coin/src/ffi/tw_any_address.rs index de69826f9f8..ba8d39163c5 100644 --- a/rust/tw_any_coin/src/ffi/tw_any_address.rs +++ b/rust/tw_any_coin/src/ffi/tw_any_address.rs @@ -9,6 +9,7 @@ use crate::any_address::AnyAddress; use tw_coin_entry::derivation::Derivation; use tw_coin_entry::prefix::AddressPrefix; +use tw_coin_registry::coin_type::CoinType; use tw_keypair::ffi::pubkey::TWPublicKey; use tw_memory::ffi::tw_data::TWData; use tw_memory::ffi::tw_string::TWString; @@ -29,6 +30,7 @@ impl RawPtrTrait for TWAnyAddress {} pub unsafe extern "C" fn tw_any_address_is_valid(string: *const TWString, coin: u32) -> bool { let string = try_or_false!(TWString::from_ptr_as_ref(string)); let string = try_or_false!(string.as_str()); + let coin = try_or_false!(CoinType::try_from(coin)); AnyAddress::is_valid(coin, string, None) } @@ -51,6 +53,8 @@ pub unsafe extern "C" fn tw_any_address_is_valid_bech32( let hrp = try_or_false!(TWString::from_ptr_as_ref(hrp)); let hrp = try_or_false!(hrp.as_str()); + let coin = try_or_false!(CoinType::try_from(coin)); + let prefix = AddressPrefix::Hrp(hrp.to_string()); AnyAddress::is_valid(coin, string, Some(prefix)) } @@ -67,6 +71,7 @@ pub unsafe extern "C" fn tw_any_address_create_with_string( ) -> *mut TWAnyAddress { let string = try_or_else!(TWString::from_ptr_as_ref(string), std::ptr::null_mut); let string = try_or_else!(string.as_str(), std::ptr::null_mut); + let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut); AnyAddress::with_string(coin, string, None) .map(|any_address| TWAnyAddress(any_address).into_ptr()) @@ -87,6 +92,7 @@ pub unsafe extern "C" fn tw_any_address_create_with_public_key_derivation( ) -> *mut TWAnyAddress { let public_key = try_or_else!(TWPublicKey::from_ptr_as_ref(public_key), std::ptr::null_mut); let derivation = try_or_else!(Derivation::from_raw(derivation), std::ptr::null_mut); + let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut); AnyAddress::with_public_key(coin, public_key.as_ref().clone(), derivation, None) .map(|any_address| TWAnyAddress(any_address).into_ptr()) @@ -106,6 +112,7 @@ pub unsafe extern "C" fn tw_any_address_create_bech32_with_public_key( hrp: *const TWString, ) -> *mut TWAnyAddress { let public_key = try_or_else!(TWPublicKey::from_ptr_as_ref(public_key), std::ptr::null_mut); + let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut); let hrp = try_or_else!(TWString::from_ptr_as_ref(hrp), std::ptr::null_mut); let hrp = try_or_else!(hrp.as_str(), std::ptr::null_mut); @@ -173,6 +180,7 @@ pub unsafe extern "C" fn tw_any_address_create_with_string_unchecked( ) -> *mut TWAnyAddress { let string = try_or_else!(TWString::from_ptr_as_ref(string), std::ptr::null_mut); let string = try_or_else!(string.as_str(), std::ptr::null_mut); + let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut); AnyAddress::with_string_unchecked(coin, string) .map(|any_address| TWAnyAddress(any_address).into_ptr()) diff --git a/rust/tw_any_coin/src/ffi/tw_any_signer.rs b/rust/tw_any_coin/src/ffi/tw_any_signer.rs index 76e3965d5e9..c0bde8e921c 100644 --- a/rust/tw_any_coin/src/ffi/tw_any_signer.rs +++ b/rust/tw_any_coin/src/ffi/tw_any_signer.rs @@ -7,6 +7,7 @@ #![allow(clippy::missing_safety_doc)] use crate::any_signer::AnySigner; +use tw_coin_registry::coin_type::CoinType; use tw_memory::ffi::tw_data::TWData; use tw_memory::ffi::RawPtrTrait; use tw_misc::try_or_else; @@ -19,6 +20,7 @@ use tw_misc::try_or_else; #[no_mangle] pub unsafe extern "C" fn tw_any_signer_sign(input: *const TWData, coin: u32) -> *mut TWData { let input = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); + let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut); AnySigner::sign(input.as_slice(), coin) .map(|output| TWData::from(output).into_ptr()) @@ -33,6 +35,7 @@ pub unsafe extern "C" fn tw_any_signer_sign(input: *const TWData, coin: u32) -> #[no_mangle] pub unsafe extern "C" fn tw_any_signer_plan(input: *const TWData, coin: u32) -> *mut TWData { let input = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); + let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut); AnySigner::plan(input.as_slice(), coin) .map(|output| TWData::from(output).into_ptr()) diff --git a/rust/tw_any_coin/src/ffi/tw_message_signer.rs b/rust/tw_any_coin/src/ffi/tw_message_signer.rs index 32144ecc204..e1188890bf1 100644 --- a/rust/tw_any_coin/src/ffi/tw_message_signer.rs +++ b/rust/tw_any_coin/src/ffi/tw_message_signer.rs @@ -7,6 +7,7 @@ #![allow(clippy::missing_safety_doc)] use crate::message_signer::MessageSigner; +use tw_coin_registry::coin_type::CoinType; use tw_memory::ffi::tw_data::TWData; use tw_memory::ffi::RawPtrTrait; use tw_misc::{try_or_else, try_or_false}; @@ -19,6 +20,7 @@ use tw_misc::{try_or_else, try_or_false}; #[no_mangle] pub unsafe extern "C" fn tw_message_signer_sign(input: *const TWData, coin: u32) -> *mut TWData { let input = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); + let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut); MessageSigner::sign_message(input.as_slice(), coin) .map(|output| TWData::from(output).into_ptr()) @@ -33,6 +35,7 @@ pub unsafe extern "C" fn tw_message_signer_sign(input: *const TWData, coin: u32) #[no_mangle] pub unsafe extern "C" fn tw_message_signer_verify(input: *const TWData, coin: u32) -> bool { let input = try_or_false!(TWData::from_ptr_as_ref(input)); + let coin = try_or_false!(CoinType::try_from(coin)); MessageSigner::verify_message(input.as_slice(), coin).unwrap_or_default() } @@ -47,6 +50,8 @@ pub unsafe extern "C" fn tw_message_signer_pre_image_hashes( coin: u32, ) -> *mut TWData { let input = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); + let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut); + MessageSigner::message_preimage_hashes(input.as_slice(), coin) .map(|output| TWData::from(output).into_ptr()) .unwrap_or_else(|_| std::ptr::null_mut()) diff --git a/rust/tw_any_coin/src/ffi/tw_transaction_compiler.rs b/rust/tw_any_coin/src/ffi/tw_transaction_compiler.rs index a6f76e19681..bd227f4426f 100644 --- a/rust/tw_any_coin/src/ffi/tw_transaction_compiler.rs +++ b/rust/tw_any_coin/src/ffi/tw_transaction_compiler.rs @@ -7,6 +7,7 @@ #![allow(clippy::missing_safety_doc)] use crate::transaction_compiler::TransactionCompiler; +use tw_coin_registry::coin_type::CoinType; use tw_memory::ffi::tw_data::TWData; use tw_memory::ffi::tw_data_vector::TWDataVector; use tw_memory::ffi::RawPtrTrait; @@ -25,6 +26,7 @@ pub unsafe extern "C" fn tw_transaction_compiler_pre_image_hashes( input: *const TWData, ) -> *mut TWData { let input = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); + let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut); TransactionCompiler::preimage_hashes(coin, input.as_slice()) .map(|output| TWData::from(output).into_ptr()) @@ -57,6 +59,7 @@ pub unsafe extern "C" fn tw_transaction_compiler_compile( TWDataVector::from_ptr_as_ref(public_keys), std::ptr::null_mut ); + let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut); TransactionCompiler::compile( coin, diff --git a/rust/tw_any_coin/src/test_utils/address_utils.rs b/rust/tw_any_coin/src/test_utils/address_utils.rs new file mode 100644 index 00000000000..8efd7de120e --- /dev/null +++ b/rust/tw_any_coin/src/test_utils/address_utils.rs @@ -0,0 +1,105 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ffi::tw_any_address::{ + tw_any_address_create_bech32_with_public_key, tw_any_address_create_with_string, + tw_any_address_data, tw_any_address_delete, tw_any_address_description, + tw_any_address_is_valid, tw_any_address_is_valid_bech32, TWAnyAddress, +}; +use tw_coin_registry::coin_type::CoinType; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_keypair::ffi::privkey::tw_private_key_get_public_key_by_type; +use tw_keypair::test_utils::tw_private_key_helper::TWPrivateKeyHelper; +use tw_keypair::test_utils::tw_public_key_helper::TWPublicKeyHelper; +use tw_keypair::tw::PublicKeyType; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_memory::test_utils::tw_string_helper::TWStringHelper; +use tw_memory::test_utils::tw_wrapper::{TWWrapper, WithDestructor}; + +pub type TWAnyAddressHelper = TWWrapper; + +impl WithDestructor for TWAnyAddress { + fn destructor() -> unsafe extern "C" fn(*mut Self) { + tw_any_address_delete + } +} + +pub fn test_address_normalization(coin: CoinType, denormalized: &str, normalized: &str) { + let expected = normalized; + let denormalized = TWStringHelper::create(denormalized); + + let any_address = TWAnyAddressHelper::wrap(unsafe { + tw_any_address_create_with_string(denormalized.ptr(), coin as u32) + }); + + let normalized = TWStringHelper::wrap(unsafe { tw_any_address_description(any_address.ptr()) }); + + assert_eq!(normalized.to_string(), Some(expected.to_string())); +} + +pub fn test_address_valid(coin: CoinType, address: &str) { + let address = TWStringHelper::create(address); + assert!(unsafe { tw_any_address_is_valid(address.ptr(), coin as u32) }); +} + +pub fn test_address_invalid(coin: CoinType, address: &str) { + let address = TWStringHelper::create(address); + assert!(!unsafe { tw_any_address_is_valid(address.ptr(), coin as u32) }); +} + +pub fn test_address_get_data(coin: CoinType, address: &str, data_hex: &str) { + let address_str = TWStringHelper::create(address); + let any_address = TWAnyAddressHelper::wrap(unsafe { + tw_any_address_create_with_string(address_str.ptr(), coin as u32) + }); + + let actual_data = TWDataHelper::wrap(unsafe { tw_any_address_data(any_address.ptr()) }) + .to_vec() + .unwrap_or_else(|| panic!("!tw_any_address_data")); + assert_eq!( + actual_data.to_hex(), + // Decode and encode again to allow to use `0x` and non-prefixed data hexes. + data_hex.decode_hex().unwrap().to_hex() + ); +} + +pub struct AddressCreateBech32WithPublicKey<'a> { + pub coin: CoinType, + pub private_key: &'a str, + pub public_key_type: PublicKeyType, + pub hrp: &'a str, + pub expected: &'a str, +} + +pub fn test_address_create_bech32_with_public_key(input: AddressCreateBech32WithPublicKey<'_>) { + let private_key = TWPrivateKeyHelper::with_hex(input.private_key); + let public_key = TWPublicKeyHelper::wrap(unsafe { + tw_private_key_get_public_key_by_type(private_key.ptr(), input.public_key_type as u32) + }); + let hrp = TWStringHelper::create(input.hrp); + + let any_address = TWAnyAddressHelper::wrap(unsafe { + tw_any_address_create_bech32_with_public_key(public_key.ptr(), input.coin as u32, hrp.ptr()) + }); + + let actual = TWStringHelper::wrap(unsafe { tw_any_address_description(any_address.ptr()) }); + assert_eq!(actual.to_string(), Some(input.expected.to_string())); +} + +pub struct AddressBech32IsValid<'a> { + pub coin: CoinType, + pub address: &'a str, + pub hrp: &'a str, +} + +pub fn test_address_bech32_is_valid(input: AddressBech32IsValid<'_>) { + let address_str = TWStringHelper::create(input.address); + let hrp = TWStringHelper::create(input.hrp); + // Should be valid even though Osmosis chain has `osmo` default hrp. + let result = + unsafe { tw_any_address_is_valid_bech32(address_str.ptr(), input.coin as u32, hrp.ptr()) }; + assert!(result); +} diff --git a/rust/tw_any_coin/src/test_utils/mod.rs b/rust/tw_any_coin/src/test_utils/mod.rs index 671dbb1a436..7bdd5ac4e99 100644 --- a/rust/tw_any_coin/src/test_utils/mod.rs +++ b/rust/tw_any_coin/src/test_utils/mod.rs @@ -4,13 +4,4 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use crate::ffi::tw_any_address::{tw_any_address_delete, TWAnyAddress}; -use tw_memory::test_utils::tw_wrapper::{TWWrapper, WithDestructor}; - -pub type TWAnyAddressHelper = TWWrapper; - -impl WithDestructor for TWAnyAddress { - fn destructor() -> unsafe extern "C" fn(*mut Self) { - tw_any_address_delete - } -} +pub mod address_utils; diff --git a/rust/tw_any_coin/tests/chains/aptos/aptos_address.rs b/rust/tw_any_coin/tests/chains/aptos/aptos_address.rs new file mode 100644 index 00000000000..788cdc5aeec --- /dev/null +++ b/rust/tw_any_coin/tests/chains/aptos/aptos_address.rs @@ -0,0 +1,73 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_any_coin::test_utils::address_utils::{ + test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, +}; +use tw_coin_registry::coin_type::CoinType; + +#[test] +fn test_aptos_address_normalization() { + test_address_normalization( + CoinType::Aptos, + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", + ); +} + +#[test] +fn test_aptos_address_is_valid() { + test_address_valid(CoinType::Aptos, "0x1"); + test_address_valid( + CoinType::Aptos, + "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", + ); + test_address_valid( + CoinType::Aptos, + "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", + ); + test_address_valid( + CoinType::Aptos, + "19aadeca9388e009d136245b9a67423f3eee242b03142849eb4f81a4a409e59c", + ); + test_address_valid( + CoinType::Aptos, + "0x777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb", + ); + test_address_valid( + CoinType::Aptos, + "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175", + ); +} + +#[test] +fn test_aptos_address_invalid() { + // Empty + test_address_invalid(CoinType::Aptos, ""); + // Invalid Hex + test_address_invalid( + CoinType::Aptos, + "Seff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", + ); + // Too long + test_address_invalid( + CoinType::Aptos, + "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175bb", + ); + test_address_invalid( + CoinType::Aptos, + "0xSeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", + ); +} + +#[test] +fn test_aptos_address_get_data() { + test_address_get_data( + CoinType::Aptos, + "0x1", + "0000000000000000000000000000000000000000000000000000000000000001", + ); +} diff --git a/rust/tw_any_coin/tests/chains/aptos/mod.rs b/rust/tw_any_coin/tests/chains/aptos/mod.rs index 0e046226232..ecea2c75bfd 100644 --- a/rust/tw_any_coin/tests/chains/aptos/mod.rs +++ b/rust/tw_any_coin/tests/chains/aptos/mod.rs @@ -4,6 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +mod aptos_address; mod aptos_compile; mod aptos_sign; mod test_cases; diff --git a/rust/tw_any_coin/tests/chains/bitcoin/bitcoin_address.rs b/rust/tw_any_coin/tests/chains/bitcoin/bitcoin_address.rs new file mode 100644 index 00000000000..0397c6c7768 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/bitcoin/bitcoin_address.rs @@ -0,0 +1,49 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_any_coin::test_utils::address_utils::{ + test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, +}; +use tw_coin_registry::coin_type::CoinType; + +#[test] +fn test_bitcoin_address_normalization() { + test_address_normalization( + CoinType::Bitcoin, + "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", + "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", + ); +} + +#[test] +fn test_bitcoin_address_is_valid() { + test_address_valid(CoinType::Bitcoin, "1MrZNGN7mfWZiZNQttrzHjfw72jnJC2JNx"); + test_address_valid( + CoinType::Bitcoin, + "bc1qunq74p3h8425hr6wllevlvqqr6sezfxj262rff", + ); + test_address_valid( + CoinType::Bitcoin, + "bc1pwse34zfpvt344rvlt7tw0ngjtfh9xasc4q03avf0lk74jzjpzjuqaz7ks5", + ); +} + +#[test] +fn test_bitcoin_address_invalid() { + test_address_invalid( + CoinType::Bitcoin, + "0xb16db98b365b1f89191996942612b14f1da4bd5f", + ); +} + +#[test] +fn test_bitcoin_address_get_data() { + test_address_get_data( + CoinType::Bitcoin, + "1MrZNGN7mfWZiZNQttrzHjfw72jnJC2JNx", + "314d725a4e474e376d66575a695a4e517474727a486a667737326a6e4a43324a4e78", + ); +} diff --git a/rust/tw_any_coin/tests/chains/bitcoin/mod.rs b/rust/tw_any_coin/tests/chains/bitcoin/mod.rs new file mode 100644 index 00000000000..00682e69022 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/bitcoin/mod.rs @@ -0,0 +1,7 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +mod bitcoin_address; diff --git a/rust/tw_any_coin/tests/chains/cosmos/cosmos_address.rs b/rust/tw_any_coin/tests/chains/cosmos/cosmos_address.rs new file mode 100644 index 00000000000..964414dff44 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/cosmos/cosmos_address.rs @@ -0,0 +1,83 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_any_coin::test_utils::address_utils::{ + test_address_bech32_is_valid, test_address_create_bech32_with_public_key, + test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, + AddressBech32IsValid, AddressCreateBech32WithPublicKey, +}; +use tw_coin_registry::coin_type::CoinType; +use tw_keypair::tw::PublicKeyType; + +#[test] +fn test_cosmos_address_normalization() { + test_address_normalization( + CoinType::Cosmos, + "cosmosvaloper1sxx9mszve0gaedz5ld7qdkjkfv8z992ax69k08", + "cosmosvaloper1sxx9mszve0gaedz5ld7qdkjkfv8z992ax69k08", + ); +} + +#[test] +fn test_cosmos_address_is_valid() { + test_address_valid( + CoinType::Cosmos, + "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02", + ); + test_address_valid( + CoinType::Cosmos, + "cosmosvaloper1sxx9mszve0gaedz5ld7qdkjkfv8z992ax69k08", + ); + test_address_valid( + CoinType::Cosmos, + "cosmosvalconspub1zcjduepqjnnwe2jsywv0kfc97pz04zkm7tc9k2437cde2my3y5js9t7cw9mstfg3sa", + ); +} + +#[test] +fn test_cosmos_address_invalid() { + test_address_invalid( + CoinType::Cosmos, + "cosmosvaloper1sxx9mszve0gaedz5ld7qdkjkfv8z992ax6", + ); + test_address_invalid( + CoinType::Cosmos, + "one1a50tun737ulcvwy0yvve0pvu5skq0kjargvhwe", + ); + test_address_invalid( + CoinType::Cosmos, + "bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2", + ); +} + +#[test] +fn test_cosmos_address_get_data() { + test_address_get_data( + CoinType::Cosmos, + "cosmosvaloper1sxx9mszve0gaedz5ld7qdkjkfv8z992ax69k08", + "818c5dc04ccbd1dcb454fb7c06da564b0e22955d", + ); +} + +#[test] +fn test_any_address_is_valid_bech32() { + test_address_bech32_is_valid(AddressBech32IsValid { + coin: CoinType::Cosmos, + address: "juno1mry47pkga5tdswtluy0m8teslpalkdq0gnn4mf", + hrp: "juno", + }); +} + +#[test] +fn test_any_address_create_bech32_with_public_key() { + test_address_create_bech32_with_public_key(AddressCreateBech32WithPublicKey { + coin: CoinType::Cosmos, + private_key: "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", + public_key_type: PublicKeyType::Secp256k1, + hrp: "juno", + expected: "juno1ten42eesehw0ktddcp0fws7d3ycsqez3fksy86", + }); +} diff --git a/rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs b/rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs new file mode 100644 index 00000000000..b9140fb9731 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs @@ -0,0 +1,65 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; +use tw_coin_entry::error::SigningErrorType; +use tw_coin_registry::coin_type::CoinType; +use tw_encoding::hex::DecodeHex; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_proto::{deserialize, serialize}; + +#[test] +fn test_any_signer_sign_cosmos() { + use tw_proto::Cosmos::Proto; + use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum; + + let private_key = "8bbec3772ddb4df68f3186440380c301af116d1422001c1877d6f5e4dba8c8af" + .decode_hex() + .unwrap(); + + let send_msg = Proto::mod_Message::Send { + from_address: "cosmos1mky69cn8ektwy0845vec9upsdphktxt03gkwlx".into(), + to_address: "cosmos18s0hdnsllgcclweu9aymw4ngktr2k0rkygdzdp".into(), + amounts: vec![Proto::Amount { + denom: "uatom".into(), + amount: "400000".into(), + }], + ..Proto::mod_Message::Send::default() + }; + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::Protobuf, + account_number: 546179, + chain_id: "cosmoshub-4".into(), + sequence: 0, + fee: Some(Proto::Fee { + gas: 200000, + amounts: vec![Proto::Amount { + denom: "uatom".into(), + amount: "1000".into(), + }], + }), + private_key: private_key.into(), + messages: vec![Proto::Message { + message_oneof: MessageEnum::send_coins_message(send_msg), + }], + ..Proto::SigningInput::default() + }; + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output = TWDataHelper::wrap(unsafe { + tw_any_signer_sign(input_data.ptr(), CoinType::Cosmos as u32) + }) + .to_vec() + .expect("!tw_any_signer_sign returned nullptr"); + + let output: Proto::SigningOutput = deserialize(&output).unwrap(); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected = r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CpIBCo8BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm8KLWNvc21vczFta3k2OWNuOGVrdHd5MDg0NXZlYzl1cHNkcGhrdHh0MDNna3dseBItY29zbW9zMThzMGhkbnNsbGdjY2x3ZXU5YXltdzRuZ2t0cjJrMHJreWdkemRwGg8KBXVhdG9tEgY0MDAwMDASZQpOCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAuzvXOQ3owLGf5VGjeSzHzbpEfRn1+alK0HB4T4dVjZJEgQKAggBEhMKDQoFdWF0b20SBDEwMDAQwJoMGkCvvVE6d29P30cO9/lnXyGunWMPxNY12NuqDcCnFkNM0H4CUQdl1Gc9+ogIJbro5nyzZzlv9rl2/GsZox/JXoCX"}"#; + assert_eq!(output.serialized, expected); +} diff --git a/rust/tw_any_coin/tests/chains/cosmos/mod.rs b/rust/tw_any_coin/tests/chains/cosmos/mod.rs new file mode 100644 index 00000000000..47b109ad2c9 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/cosmos/mod.rs @@ -0,0 +1,8 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +mod cosmos_address; +mod cosmos_sign; diff --git a/rust/tw_any_coin/tests/chains/ethereum/ethereum_address.rs b/rust/tw_any_coin/tests/chains/ethereum/ethereum_address.rs new file mode 100644 index 00000000000..f0a03ec2cde --- /dev/null +++ b/rust/tw_any_coin/tests/chains/ethereum/ethereum_address.rs @@ -0,0 +1,48 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_any_coin::test_utils::address_utils::{ + test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, +}; +use tw_coin_registry::blockchain_type::BlockchainType; +use tw_coin_registry::registry::coin_items_by_blockchain; + +#[test] +fn test_ethereum_address_normalization() { + for coin in coin_items_by_blockchain(BlockchainType::Ethereum) { + test_address_normalization( + coin.coin_id, + "0xb16db98b365b1f89191996942612b14f1da4bd5f", + "0xb16Db98B365B1f89191996942612B14F1Da4Bd5f", + ); + } +} + +#[test] +fn test_ethereum_address_is_valid() { + for coin in coin_items_by_blockchain(BlockchainType::Ethereum) { + test_address_valid(coin.coin_id, "0xb16db98b365b1f89191996942612b14f1da4bd5f"); + test_address_valid(coin.coin_id, "0xb16Db98B365B1f89191996942612B14F1Da4Bd5f"); + } +} + +#[test] +fn test_ethereum_address_invalid() { + for coin in coin_items_by_blockchain(BlockchainType::Ethereum) { + test_address_invalid(coin.coin_id, "b16Db98B365B1f89191996942612B14F1Da4Bd5f"); + } +} + +#[test] +fn test_ethereum_address_get_data() { + for coin in coin_items_by_blockchain(BlockchainType::Ethereum) { + test_address_get_data( + coin.coin_id, + "0xb16Db98B365B1f89191996942612B14F1Da4Bd5f", + "b16Db98B365B1f89191996942612B14F1Da4Bd5f", + ); + } +} diff --git a/rust/tw_any_coin/tests/tw_transaction_compiler_ffi_tests.rs b/rust/tw_any_coin/tests/chains/ethereum/ethereum_compile.rs similarity index 92% rename from rust/tw_any_coin/tests/tw_transaction_compiler_ffi_tests.rs rename to rust/tw_any_coin/tests/chains/ethereum/ethereum_compile.rs index ea875557d4c..f31a380d456 100644 --- a/rust/tw_any_coin/tests/tw_transaction_compiler_ffi_tests.rs +++ b/rust/tw_any_coin/tests/chains/ethereum/ethereum_compile.rs @@ -10,6 +10,7 @@ use tw_any_coin::ffi::tw_transaction_compiler::{ tw_transaction_compiler_compile, tw_transaction_compiler_pre_image_hashes, }; use tw_coin_entry::error::SigningErrorType; +use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; use tw_memory::test_utils::tw_data_vector_helper::TWDataVectorHelper; @@ -18,8 +19,6 @@ use tw_proto::Ethereum::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; use tw_proto::{deserialize, serialize}; -const ETHEREUM_COIN_TYPE: u32 = 60; - #[test] fn test_transaction_compiler_eth() { let transfer = Proto::mod_Transaction::Transfer { @@ -41,7 +40,7 @@ fn test_transaction_compiler_eth() { // Step 2: Obtain preimage hash let input_data = TWDataHelper::create(serialize(&input).unwrap()); let preimage_data = TWDataHelper::wrap(unsafe { - tw_transaction_compiler_pre_image_hashes(ETHEREUM_COIN_TYPE, input_data.ptr()) + tw_transaction_compiler_pre_image_hashes(CoinType::Ethereum as u32, input_data.ptr()) }) .to_vec() .expect("!tw_transaction_compiler_pre_image_hashes returned nullptr"); @@ -68,7 +67,7 @@ fn test_transaction_compiler_eth() { let input_data = TWDataHelper::create(serialize(&input).unwrap()); let output_data = TWDataHelper::wrap(unsafe { tw_transaction_compiler_compile( - ETHEREUM_COIN_TYPE, + CoinType::Ethereum as u32, input_data.ptr(), signatures.ptr(), public_keys.ptr(), @@ -100,11 +99,12 @@ fn test_transaction_compiler_plan_not_supported() { ..Proto::SigningInput::default() }; let input_data = TWDataHelper::create(serialize(&input).unwrap()); - let plan = - TWDataHelper::wrap(unsafe { tw_any_signer_plan(input_data.ptr(), ETHEREUM_COIN_TYPE) }); + let plan = TWDataHelper::wrap(unsafe { + tw_any_signer_plan(input_data.ptr(), CoinType::Ethereum as u32) + }); assert!( plan.is_null(), - "Transaction plan is expected to be not supported by the {} coin", - ETHEREUM_COIN_TYPE + "Transaction plan is expected to be not supported by the {:?} coin", + CoinType::Ethereum ); } diff --git a/rust/tw_any_coin/tests/tw_message_signer_ffi_tests.rs b/rust/tw_any_coin/tests/chains/ethereum/ethereum_message_sign.rs similarity index 89% rename from rust/tw_any_coin/tests/tw_message_signer_ffi_tests.rs rename to rust/tw_any_coin/tests/chains/ethereum/ethereum_message_sign.rs index 0a58ddfbec4..1b348169f49 100644 --- a/rust/tw_any_coin/tests/tw_message_signer_ffi_tests.rs +++ b/rust/tw_any_coin/tests/chains/ethereum/ethereum_message_sign.rs @@ -8,12 +8,11 @@ use tw_any_coin::ffi::tw_message_signer::{ tw_message_signer_pre_image_hashes, tw_message_signer_sign, tw_message_signer_verify, }; use tw_coin_entry::error::SigningErrorType; +use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; use tw_proto::{deserialize, serialize, Ethereum, TxCompiler}; -const ETHEREUM_COIN_TYPE: u32 = 60; - #[test] fn test_tw_message_signer_sign() { let input = Ethereum::Proto::MessageSigningInput { @@ -27,10 +26,11 @@ fn test_tw_message_signer_sign() { }; let input_data = TWDataHelper::create(serialize(&input).unwrap()); - let output = - TWDataHelper::wrap(unsafe { tw_message_signer_sign(input_data.ptr(), ETHEREUM_COIN_TYPE) }) - .to_vec() - .expect("!tw_message_signer_sign returned nullptr"); + let output = TWDataHelper::wrap(unsafe { + tw_message_signer_sign(input_data.ptr(), CoinType::Ethereum as u32) + }) + .to_vec() + .expect("!tw_message_signer_sign returned nullptr"); let output: Ethereum::Proto::MessageSigningOutput = deserialize(&output).unwrap(); assert_eq!(output.error, SigningErrorType::OK); @@ -47,7 +47,7 @@ fn test_tw_message_signer_verify() { }; let input_data = TWDataHelper::create(serialize(&input).unwrap()); - let verified = unsafe { tw_message_signer_verify(input_data.ptr(), ETHEREUM_COIN_TYPE) }; + let verified = unsafe { tw_message_signer_verify(input_data.ptr(), CoinType::Ethereum as u32) }; assert!(verified); } @@ -60,7 +60,7 @@ fn test_tw_message_signer_verify_invalid() { }; let input_data = TWDataHelper::create(serialize(&input).unwrap()); - let verified = unsafe { tw_message_signer_verify(input_data.ptr(), ETHEREUM_COIN_TYPE) }; + let verified = unsafe { tw_message_signer_verify(input_data.ptr(), CoinType::Ethereum as u32) }; assert!(!verified); } @@ -78,7 +78,7 @@ fn test_tw_message_signer_pre_image_hashes() { let input_data = TWDataHelper::create(serialize(&input).unwrap()); let output = TWDataHelper::wrap(unsafe { - tw_message_signer_pre_image_hashes(input_data.ptr(), ETHEREUM_COIN_TYPE) + tw_message_signer_pre_image_hashes(input_data.ptr(), CoinType::Ethereum as u32) }) .to_vec() .expect("!tw_message_signer_sign returned nullptr"); diff --git a/rust/tw_any_coin/tests/chains/ethereum/ethereum_sign.rs b/rust/tw_any_coin/tests/chains/ethereum/ethereum_sign.rs new file mode 100644 index 00000000000..47a78da82e7 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/ethereum/ethereum_sign.rs @@ -0,0 +1,56 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::borrow::Cow; +use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; +use tw_coin_entry::error::SigningErrorType; +use tw_coin_registry::coin_type::CoinType; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_number::U256; +use tw_proto::{deserialize, serialize}; + +#[test] +fn test_any_signer_sign_eth() { + use tw_proto::Ethereum::Proto; + + let private = "0x4646464646464646464646464646464646464646464646464646464646464646" + .decode_hex() + .unwrap(); + + let transfer = Proto::mod_Transaction::Transfer { + amount: U256::encode_be_compact(1_000_000_000_000_000_000), + data: Cow::default(), + }; + + let input = Proto::SigningInput { + chain_id: U256::encode_be_compact(1), + nonce: U256::encode_be_compact(9), + gas_price: U256::encode_be_compact(20_000_000_000), + gas_limit: U256::encode_be_compact(21_000), + to_address: "0x3535353535353535353535353535353535353535".into(), + transaction: Some(Proto::Transaction { + transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::transfer(transfer), + }), + private_key: private.into(), + ..Proto::SigningInput::default() + }; + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output = TWDataHelper::wrap(unsafe { + tw_any_signer_sign(input_data.ptr(), CoinType::Ethereum as u32) + }) + .to_vec() + .expect("!tw_any_signer_sign returned nullptr"); + + let output: Proto::SigningOutput = deserialize(&output).unwrap(); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + let expected = "f86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83"; + assert_eq!(output.encoded.to_hex(), expected); +} diff --git a/rust/tw_any_coin/tests/chains/ethereum/mod.rs b/rust/tw_any_coin/tests/chains/ethereum/mod.rs new file mode 100644 index 00000000000..153b12547b5 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/ethereum/mod.rs @@ -0,0 +1,10 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +mod ethereum_address; +mod ethereum_compile; +mod ethereum_message_sign; +mod ethereum_sign; diff --git a/rust/tw_any_coin/tests/chains/internet_computer/internet_computer_address.rs b/rust/tw_any_coin/tests/chains/internet_computer/internet_computer_address.rs new file mode 100644 index 00000000000..ef44d0e022f --- /dev/null +++ b/rust/tw_any_coin/tests/chains/internet_computer/internet_computer_address.rs @@ -0,0 +1,64 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_any_coin::test_utils::address_utils::{ + test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, +}; +use tw_coin_registry::coin_type::CoinType; + +#[test] +fn test_thorchain_address_normalization() { + test_address_normalization( + CoinType::InternetComputer, + "290CC7C359F44C8516FC169C5ED4F0F3AE2E24BF5DE0D4C51F5E7545B5474FAA", + "290cc7c359f44c8516fc169c5ed4f0f3ae2e24bf5de0d4c51f5e7545b5474faa", + ); +} + +#[test] +fn test_thorchain_address_is_valid() { + test_address_valid( + CoinType::InternetComputer, + "fb257577279ecac604d4780214af95aa6adc3a814f6f3d6d7ac844d1deca500a", + ); + test_address_valid( + CoinType::InternetComputer, + "e90c48d54847f4758f1d6b589a1db2500757a49a6722d4f775e050107b4b752d", + ); + test_address_valid( + CoinType::InternetComputer, + "a7c5baf393aed527ef6fb3869fbf84dd4e562edf9b04bd8f9bfbd6b8e6a22776", + ); + test_address_valid( + CoinType::InternetComputer, + "4cb2ca5cfcfa1d952f8cd7f0ec46c96e1023ab057b83a2c7ce236b9e71ccca0b", + ); +} + +#[test] +fn test_thorchain_address_invalid() { + test_address_invalid( + CoinType::InternetComputer, + "3357cba483f268d044d4bbd4639f82c16028a76eebdf62c51bc11fc918d278b", + ); + test_address_invalid( + CoinType::InternetComputer, + "3357cba483f268d044d4bbd4639f82c16028a76eebdf62c51bc11fc918d278bce", + ); + test_address_invalid( + CoinType::InternetComputer, + "553357cba483f268d044d4bbd4639f82c16028a76eebdf62c51bc11fc918d278", + ); +} + +#[test] +fn test_thorchain_address_get_data() { + test_address_get_data( + CoinType::InternetComputer, + "4cb2ca5cfcfa1d952f8cd7f0ec46c96e1023ab057b83a2c7ce236b9e71ccca0b", + "4cb2ca5cfcfa1d952f8cd7f0ec46c96e1023ab057b83a2c7ce236b9e71ccca0b", + ); +} diff --git a/rust/tw_any_coin/tests/chains/internet_computer/mod.rs b/rust/tw_any_coin/tests/chains/internet_computer/mod.rs new file mode 100644 index 00000000000..e305a45c6be --- /dev/null +++ b/rust/tw_any_coin/tests/chains/internet_computer/mod.rs @@ -0,0 +1,7 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +mod internet_computer_address; diff --git a/rust/tw_any_coin/tests/chains/mod.rs b/rust/tw_any_coin/tests/chains/mod.rs index 165ba051b71..ddd04b53632 100644 --- a/rust/tw_any_coin/tests/chains/mod.rs +++ b/rust/tw_any_coin/tests/chains/mod.rs @@ -5,6 +5,10 @@ // file LICENSE at the root of the source code distribution tree. mod aptos; +mod bitcoin; +mod cosmos; +mod ethereum; +mod internet_computer; mod native_evmos; mod native_injective; mod thorchain; diff --git a/rust/tw_any_coin/tests/chains/native_evmos/mod.rs b/rust/tw_any_coin/tests/chains/native_evmos/mod.rs index 4d62bbecabe..2a92924fc00 100644 --- a/rust/tw_any_coin/tests/chains/native_evmos/mod.rs +++ b/rust/tw_any_coin/tests/chains/native_evmos/mod.rs @@ -4,4 +4,5 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +mod native_evmos_address; mod native_evmos_sign; diff --git a/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_address.rs b/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_address.rs new file mode 100644 index 00000000000..d779aabc230 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_address.rs @@ -0,0 +1,71 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_any_coin::test_utils::address_utils::{ + test_address_bech32_is_valid, test_address_create_bech32_with_public_key, + test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, + AddressBech32IsValid, AddressCreateBech32WithPublicKey, +}; +use tw_coin_registry::coin_type::CoinType; +use tw_keypair::tw::PublicKeyType; + +#[test] +fn test_native_evmos_address_normalization() { + test_address_normalization( + CoinType::NativeEvmos, + "evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw34", + "evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw34", + ); +} + +#[test] +fn test_native_evmos_address_is_valid() { + test_address_valid( + CoinType::NativeEvmos, + "evmos14py36sx57ud82t9yrks9z6hdsrpn5x6k0r05np", + ); + test_address_valid( + CoinType::NativeEvmos, + "evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw34", + ); +} + +#[test] +fn test_native_evmos_address_invalid() { + test_address_invalid( + CoinType::NativeEvmos, + "evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw", + ); +} + +#[test] +fn test_native_evmos_address_get_data() { + test_address_get_data( + CoinType::NativeEvmos, + "evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw34", + "f1829676db577682e944fc3493d451b67ff3e29f", + ); +} + +#[test] +fn test_any_address_is_valid_bech32() { + test_address_bech32_is_valid(AddressBech32IsValid { + coin: CoinType::Cosmos, + address: "evmos14py36sx57ud82t9yrks9z6hdsrpn5x6k0r05np", + hrp: "evmos", + }); +} + +#[test] +fn test_any_address_create_bech32_with_public_key() { + test_address_create_bech32_with_public_key(AddressCreateBech32WithPublicKey { + coin: CoinType::NativeEvmos, + private_key: "8d2a3bd62d300a148c89dc8635f87b7a24a951bd1c4e78675fe40e1a640d46ed", + public_key_type: PublicKeyType::Secp256k1Extended, + hrp: "evmos", + expected: "evmos14py36sx57ud82t9yrks9z6hdsrpn5x6k0r05np", + }); +} diff --git a/rust/tw_any_coin/tests/chains/native_injective/mod.rs b/rust/tw_any_coin/tests/chains/native_injective/mod.rs index 2781305c015..a421b328188 100644 --- a/rust/tw_any_coin/tests/chains/native_injective/mod.rs +++ b/rust/tw_any_coin/tests/chains/native_injective/mod.rs @@ -4,5 +4,6 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -pub mod native_injective_compile; -pub mod native_injective_sign; +mod native_injective_address; +mod native_injective_compile; +mod native_injective_sign; diff --git a/rust/tw_any_coin/tests/chains/native_injective/native_injective_address.rs b/rust/tw_any_coin/tests/chains/native_injective/native_injective_address.rs new file mode 100644 index 00000000000..1bd9c4a8f8e --- /dev/null +++ b/rust/tw_any_coin/tests/chains/native_injective/native_injective_address.rs @@ -0,0 +1,71 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_any_coin::test_utils::address_utils::{ + test_address_bech32_is_valid, test_address_create_bech32_with_public_key, + test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, + AddressBech32IsValid, AddressCreateBech32WithPublicKey, +}; +use tw_coin_registry::coin_type::CoinType; +use tw_keypair::tw::PublicKeyType; + +#[test] +fn test_native_injective_address_normalization() { + test_address_normalization( + CoinType::NativeInjective, + "inj14py36sx57ud82t9yrks9z6hdsrpn5x6k8tf7m3", + "inj14py36sx57ud82t9yrks9z6hdsrpn5x6k8tf7m3", + ); +} + +#[test] +fn test_native_injective_address_is_valid() { + test_address_valid( + CoinType::NativeInjective, + "inj13u6g7vqgw074mgmf2ze2cadzvkz9snlwcrtq8a", + ); + test_address_valid( + CoinType::NativeInjective, + "inj1xmpkmxr4as00em23tc2zgmuyy2gr4h3wgcl6vd", + ); +} + +#[test] +fn test_native_injective_address_invalid() { + test_address_invalid( + CoinType::NativeInjective, + "ini13u6g7vqgw074mgmf2ze2cadzvkz9snlwcrtq8a", + ); +} + +#[test] +fn test_native_injective_address_get_data() { + test_address_get_data( + CoinType::NativeInjective, + "inj1xmpkmxr4as00em23tc2zgmuyy2gr4h3wgcl6vd", + "36c36d9875ec1efced515e14246f8422903ade2e", + ); +} + +#[test] +fn test_any_address_is_valid_bech32() { + test_address_bech32_is_valid(AddressBech32IsValid { + coin: CoinType::Cosmos, + address: "evmos14py36sx57ud82t9yrks9z6hdsrpn5x6k0r05np", + hrp: "evmos", + }); +} + +#[test] +fn test_any_address_create_bech32_with_public_key() { + test_address_create_bech32_with_public_key(AddressCreateBech32WithPublicKey { + coin: CoinType::NativeInjective, + private_key: "8d2a3bd62d300a148c89dc8635f87b7a24a951bd1c4e78675fe40e1a640d46ed", + public_key_type: PublicKeyType::Secp256k1Extended, + hrp: "inj", + expected: "inj14py36sx57ud82t9yrks9z6hdsrpn5x6k8tf7m3", + }); +} diff --git a/rust/tw_any_coin/tests/chains/thorchain/mod.rs b/rust/tw_any_coin/tests/chains/thorchain/mod.rs index 3f38961fcbb..20d258d5105 100644 --- a/rust/tw_any_coin/tests/chains/thorchain/mod.rs +++ b/rust/tw_any_coin/tests/chains/thorchain/mod.rs @@ -5,7 +5,6 @@ // file LICENSE at the root of the source code distribution tree. mod test_cases; +mod thorchain_address; mod thorchain_compile; mod thorchain_sign; - -const THORCHAIN_COIN_TYPE: u32 = 931; diff --git a/rust/tw_any_coin/tests/chains/thorchain/thorchain_address.rs b/rust/tw_any_coin/tests/chains/thorchain/thorchain_address.rs new file mode 100644 index 00000000000..03ea3d0c188 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/thorchain/thorchain_address.rs @@ -0,0 +1,75 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_any_coin::test_utils::address_utils::{ + test_address_bech32_is_valid, test_address_create_bech32_with_public_key, + test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, + AddressBech32IsValid, AddressCreateBech32WithPublicKey, +}; +use tw_coin_registry::coin_type::CoinType; +use tw_keypair::tw::PublicKeyType; + +#[test] +fn test_thorchain_address_normalization() { + test_address_normalization( + CoinType::THORChain, + "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r", + "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r", + ); +} + +#[test] +fn test_thorchain_address_is_valid() { + test_address_valid( + CoinType::THORChain, + "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r", + ); + test_address_valid( + CoinType::THORChain, + "thor1c8jd7ad9pcw4k3wkuqlkz4auv95mldr2kyhc65", + ); +} + +#[test] +fn test_thorchain_address_invalid() { + test_address_invalid( + CoinType::THORChain, + "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02", + ); + test_address_invalid( + CoinType::THORChain, + "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0e", + ); +} + +#[test] +fn test_thorchain_address_get_data() { + test_address_get_data( + CoinType::THORChain, + "thor1c8jd7ad9pcw4k3wkuqlkz4auv95mldr2kyhc65", + "c1e4df75a50e1d5b45d6e03f6157bc6169bfb46a", + ); +} + +#[test] +fn test_any_address_is_valid_bech32() { + test_address_bech32_is_valid(AddressBech32IsValid { + coin: CoinType::Cosmos, + address: "thor1c8jd7ad9pcw4k3wkuqlkz4auv95mldr2kyhc65", + hrp: "thor", + }); +} + +#[test] +fn test_any_address_create_bech32_with_public_key() { + test_address_create_bech32_with_public_key(AddressCreateBech32WithPublicKey { + coin: CoinType::Cosmos, + private_key: "8d2a3bd62d300a148c89dc8635f87b7a24a951bd1c4e78675fe40e1a640d46ed", + public_key_type: PublicKeyType::Secp256k1, + hrp: "thor", + expected: "thor1p05ufmhfpkjzqmc2u8humvgcqatq0esjqzrcut", + }); +} diff --git a/rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs b/rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs index ed5a2eb354c..ca2fda25eab 100644 --- a/rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs +++ b/rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs @@ -8,11 +8,11 @@ use crate::chains::thorchain::test_cases::send_fd0445af::{ signing_input, JSON_SIGNING_SIGNATURE, JSON_SIGNING_SIGNATURE_JSON, JSON_TX, JSON_TX_PREIMAGE, PRIVATE_KEY, }; -use crate::chains::thorchain::THORCHAIN_COIN_TYPE; use tw_any_coin::ffi::tw_transaction_compiler::{ tw_transaction_compiler_compile, tw_transaction_compiler_pre_image_hashes, }; use tw_coin_entry::error::SigningErrorType; +use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::ToHex; use tw_hash::H256; use tw_keypair::ecdsa::secp256k1; @@ -38,7 +38,7 @@ fn test_any_signer_compile_thorchain() { // Step 2: Obtain preimage hash let input_data = TWDataHelper::create(serialize(&input).unwrap()); let preimage_data = TWDataHelper::wrap(unsafe { - tw_transaction_compiler_pre_image_hashes(THORCHAIN_COIN_TYPE, input_data.ptr()) + tw_transaction_compiler_pre_image_hashes(CoinType::THORChain as u32, input_data.ptr()) }) .to_vec() .expect("!tw_transaction_compiler_pre_image_hashes returned nullptr"); @@ -75,7 +75,7 @@ fn test_any_signer_compile_thorchain() { let input_data = TWDataHelper::create(serialize(&input).unwrap()); let output_data = TWDataHelper::wrap(unsafe { tw_transaction_compiler_compile( - THORCHAIN_COIN_TYPE, + CoinType::THORChain as u32, input_data.ptr(), signatures.ptr(), public_keys.ptr(), diff --git a/rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs b/rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs index 585166daa76..c45c196b026 100644 --- a/rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs +++ b/rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs @@ -7,9 +7,9 @@ use crate::chains::thorchain::test_cases::send_fd0445af::{ signing_input, JSON_SIGNING_SIGNATURE, JSON_SIGNING_SIGNATURE_JSON, JSON_TX, PRIVATE_KEY, }; -use crate::chains::thorchain::THORCHAIN_COIN_TYPE; use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; use tw_coin_entry::error::SigningErrorType; +use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; use tw_proto::Cosmos::Proto; @@ -25,10 +25,11 @@ fn test_any_signer_sign_thorchain() { let input_data = TWDataHelper::create(serialize(&input).unwrap()); - let output = - TWDataHelper::wrap(unsafe { tw_any_signer_sign(input_data.ptr(), THORCHAIN_COIN_TYPE) }) - .to_vec() - .expect("!tw_any_signer_sign returned nullptr"); + let output = TWDataHelper::wrap(unsafe { + tw_any_signer_sign(input_data.ptr(), CoinType::THORChain as u32) + }) + .to_vec() + .expect("!tw_any_signer_sign returned nullptr"); let output: Proto::SigningOutput = deserialize(&output).unwrap(); assert_eq!(output.error, SigningErrorType::OK); diff --git a/rust/tw_any_coin/tests/coin_address_derivation_tests.rs b/rust/tw_any_coin/tests/coin_address_derivation_tests.rs new file mode 100644 index 00000000000..7ce749d3883 --- /dev/null +++ b/rust/tw_any_coin/tests/coin_address_derivation_tests.rs @@ -0,0 +1,167 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_any_coin::ffi::tw_any_address::{ + tw_any_address_create_with_public_key_derivation, tw_any_address_description, +}; +use tw_any_coin::test_utils::address_utils::TWAnyAddressHelper; +use tw_coin_entry::derivation::Derivation; +use tw_coin_registry::coin_type::CoinType; +use tw_coin_registry::registry::get_coin_item; +use tw_keypair::ffi::privkey::tw_private_key_get_public_key_by_type; +use tw_keypair::test_utils::tw_private_key_helper::TWPrivateKeyHelper; +use tw_keypair::test_utils::tw_public_key_helper::TWPublicKeyHelper; +use tw_memory::test_utils::tw_string_helper::TWStringHelper; + +#[test] +fn test_coin_address_derivation() { + let private_key = TWPrivateKeyHelper::with_hex( + "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", + ); + + for coin in CoinType::iter() { + let coin_item = get_coin_item(coin).unwrap(); + + // Skip unsupported blockchains. + if !coin_item.blockchain.is_supported() { + continue; + } + + let public_key = TWPublicKeyHelper::wrap(unsafe { + tw_private_key_get_public_key_by_type( + private_key.ptr(), + coin_item.public_key_type as u32, + ) + }); + + let expected_address = match coin { + CoinType::Ethereum + | CoinType::AcalaEVM + | CoinType::Arbitrum + | CoinType::ArbitrumNova + | CoinType::Aurora + | CoinType::AvalancheCChain + | CoinType::Boba + | CoinType::Callisto + | CoinType::Celo + | CoinType::ConfluxeSpace + | CoinType::CronosChain + | CoinType::ECOChain + | CoinType::EthereumClassic + | CoinType::Evmos + | CoinType::Fantom + | CoinType::GoChain + | CoinType::KavaEvm + | CoinType::Klaytn + | CoinType::KuCoinCommunityChain + | CoinType::Meter + | CoinType::Metis + | CoinType::Moonbeam + | CoinType::Moonriver + | CoinType::Optimism + | CoinType::Zksync + | CoinType::PolygonzkEVM + | CoinType::OKXChain + | CoinType::POANetwork + | CoinType::Polygon + | CoinType::SmartBitcoinCash + | CoinType::SmartChain + | CoinType::SmartChainLegacy + | CoinType::Theta + | CoinType::ThetaFuel + | CoinType::ThunderCore + | CoinType::TomoChain + | CoinType::VeChain + | CoinType::Wanchain + | CoinType::xDai + | CoinType::IoTeXEVM + | CoinType::Scroll + | CoinType::OpBNB + | CoinType::Neon + | CoinType::Base + | CoinType::Linea + | CoinType::Greenfield + | CoinType::Mantle + | CoinType::ZenEON => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", + // end_of_ethereum_coin_address_derivation - DO NOT REMOVE + CoinType::Bitcoin + // TODO all Bitcoin-based blockchains should have different addresses. + // It should be fixed when Bitcoin is finalized. + | CoinType::Litecoin + | CoinType::Dogecoin + | CoinType::Dash + | CoinType::Viacoin + | CoinType::DigiByte + | CoinType::Monacoin + | CoinType::Syscoin + | CoinType::Pivx + | CoinType::Firo + | CoinType::BitcoinCash + | CoinType::BitcoinGold + | CoinType::Ravencoin + | CoinType::Qtum + | CoinType::eCash + | CoinType::Stratis + => "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", + CoinType::Aptos => "0x9006fa46f038224e8004bdda97f2e7a60c2c3d135bce7cb15541e5c0aae907a4", + CoinType::Cosmos => "cosmos1ten42eesehw0ktddcp0fws7d3ycsqez3lynlqx", + CoinType::Stargaze => "stars1ten42eesehw0ktddcp0fws7d3ycsqez3tcyzth", + CoinType::Juno => "juno1ten42eesehw0ktddcp0fws7d3ycsqez3fksy86", + CoinType::Stride => "stride1ten42eesehw0ktddcp0fws7d3ycsqez3u0nr52", + CoinType::Axelar => "axelar1ten42eesehw0ktddcp0fws7d3ycsqez3m29ht8", + CoinType::Crescent => "cre1ten42eesehw0ktddcp0fws7d3ycsqez3mvq64t", + CoinType::Kujira => "kujira1ten42eesehw0ktddcp0fws7d3ycsqez3wv38dv", + CoinType::Comdex => "comdex1ten42eesehw0ktddcp0fws7d3ycsqez3ct3ae3", + CoinType::Neutron => "neutron1ten42eesehw0ktddcp0fws7d3ycsqez3mm6a6p", + CoinType::Sommelier => "somm1ten42eesehw0ktddcp0fws7d3ycsqez3ncun3v", + CoinType::FetchAI => "fetch1ten42eesehw0ktddcp0fws7d3ycsqez3ve6mz3", + CoinType::Mars => "mars1ten42eesehw0ktddcp0fws7d3ycsqez3ze2x4a", + CoinType::Umee => "umee1ten42eesehw0ktddcp0fws7d3ycsqez3djwqy5", + CoinType::Noble => "noble1ten42eesehw0ktddcp0fws7d3ycsqez3h8xhcg", + CoinType::Sei => "sei1ten42eesehw0ktddcp0fws7d3ycsqez3jgzfx8", + CoinType::Tia => "celestia1ten42eesehw0ktddcp0fws7d3ycsqez3wwz06t", + CoinType::Coreum => "core1ten42eesehw0ktddcp0fws7d3ycsqez3v2ty8a", + CoinType::Quasar => "quasar1ten42eesehw0ktddcp0fws7d3ycsqez338fzdr", + CoinType::Persistence => "persistence1ten42eesehw0ktddcp0fws7d3ycsqez33g4vwz", + CoinType::Akash => "akash1ten42eesehw0ktddcp0fws7d3ycsqez3jl7ceu", + CoinType::Terra => "terra1ten42eesehw0ktddcp0fws7d3ycsqez3eqflzx", + CoinType::TerraV2 => "terra1ten42eesehw0ktddcp0fws7d3ycsqez3eqflzx", + CoinType::Kava => "kava1ten42eesehw0ktddcp0fws7d3ycsqez3r38zkp", + CoinType::Bluzelle => "bluzelle1ten42eesehw0ktddcp0fws7d3ycsqez32usaxh", + CoinType::BandChain => "band1ten42eesehw0ktddcp0fws7d3ycsqez3xtnacw", + CoinType::Rootstock => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", + CoinType::THORChain => "thor1ten42eesehw0ktddcp0fws7d3ycsqez3er2y4e", + CoinType::CryptoOrg => "cro1ten42eesehw0ktddcp0fws7d3ycsqez38lmxuh", + CoinType::Ronin => "ronin:Ac1ec44E4f0ca7D172B7803f6836De87Fb72b309", + CoinType::Secret => "secret1ten42eesehw0ktddcp0fws7d3ycsqez3ap8ka6", + CoinType::Osmosis => "osmo1ten42eesehw0ktddcp0fws7d3ycsqez3hlq0k5", + CoinType::NativeEvmos => "evmos14s0vgnj0pjnazu4hsqlksdk7slah9vcfvt8ssm", + CoinType::Agoric => "agoric1ten42eesehw0ktddcp0fws7d3ycsqez3de3qss", + CoinType::NativeInjective => "inj14s0vgnj0pjnazu4hsqlksdk7slah9vcfyrp6ct", + CoinType::NativeCanto => "canto14s0vgnj0pjnazu4hsqlksdk7slah9vcfuuhw7m", + CoinType::InternetComputer => "290cc7c359f44c8516fc169c5ed4f0f3ae2e24bf5de0d4c51f5e7545b5474faa", + // end_of_coin_address_derivation - DO NOT REMOVE + _ => panic!("{:?} must be covered", coin), + }; + + let any_address = TWAnyAddressHelper::wrap(unsafe { + tw_any_address_create_with_public_key_derivation( + public_key.ptr(), + coin as u32, + Derivation::Default as u32, + ) + }); + + let description = + TWStringHelper::wrap(unsafe { tw_any_address_description(any_address.ptr()) }); + assert_eq!( + description.to_string(), + Some(expected_address.to_string()), + "Invalid {:?} address", + coin + ); + } +} diff --git a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs deleted file mode 100644 index 0f4a6e9d928..00000000000 --- a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs +++ /dev/null @@ -1,287 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -use tw_any_coin::ffi::tw_any_address::{ - tw_any_address_create_bech32_with_public_key, tw_any_address_create_with_public_key_derivation, - tw_any_address_create_with_string, tw_any_address_data, tw_any_address_description, - tw_any_address_is_valid, tw_any_address_is_valid_bech32, -}; -use tw_any_coin::test_utils::TWAnyAddressHelper; -use tw_coin_entry::derivation::Derivation; -use tw_coin_registry::blockchain_type::BlockchainType; -use tw_coin_registry::registry::supported_coin_items; -use tw_encoding::hex::DecodeHex; -use tw_keypair::ffi::privkey::tw_private_key_get_public_key_by_type; -use tw_keypair::test_utils::tw_private_key_helper::TWPrivateKeyHelper; -use tw_keypair::test_utils::tw_public_key_helper::TWPublicKeyHelper; -use tw_keypair::tw::PublicKeyType; -use tw_memory::test_utils::tw_data_helper::TWDataHelper; -use tw_memory::test_utils::tw_string_helper::TWStringHelper; - -const ETHEREUM_COIN_TYPE: u32 = 60; -const OSMOSIS_COIN_TYPE: u32 = 10000118; - -#[test] -fn test_any_address_derive() { - let private_key = TWPrivateKeyHelper::with_hex( - "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", - ); - - for coin in supported_coin_items() { - let public_key = TWPublicKeyHelper::wrap(unsafe { - tw_private_key_get_public_key_by_type(private_key.ptr(), coin.public_key_type as u32) - }); - - // TODO match `CoinType` when it's generated. - let expected_address = match coin.blockchain { - BlockchainType::Aptos => { - "0x9006fa46f038224e8004bdda97f2e7a60c2c3d135bce7cb15541e5c0aae907a4" - }, - // By default, Bitcoin will return a P2PKH address. - BlockchainType::Bitcoin => "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", - BlockchainType::Cosmos if coin.id == "cosmos" => { - "cosmos1ten42eesehw0ktddcp0fws7d3ycsqez3lynlqx" - }, - // Skip other Cosmos chains as they have different addresses. - // TODO fix this when `CoinType` is generated by a codegen tool. - BlockchainType::Cosmos => continue, - BlockchainType::Ethereum => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", - BlockchainType::InternetComputer => { - "290cc7c359f44c8516fc169c5ed4f0f3ae2e24bf5de0d4c51f5e7545b5474faa" - }, - BlockchainType::NativeEvmos => "evmos14s0vgnj0pjnazu4hsqlksdk7slah9vcfvt8ssm", - BlockchainType::NativeInjective => "inj14s0vgnj0pjnazu4hsqlksdk7slah9vcfyrp6ct", - BlockchainType::Ronin => "ronin:Ac1ec44E4f0ca7D172B7803f6836De87Fb72b309", - BlockchainType::Thorchain => "thor1ten42eesehw0ktddcp0fws7d3ycsqez3er2y4e", - BlockchainType::Unsupported => unreachable!(), - }; - - let any_address = TWAnyAddressHelper::wrap(unsafe { - tw_any_address_create_with_public_key_derivation( - public_key.ptr(), - coin.coin_id, - Derivation::Default as u32, - ) - }); - - let description = - TWStringHelper::wrap(unsafe { tw_any_address_description(any_address.ptr()) }); - assert_eq!(description.to_string(), Some(expected_address.to_string())); - } -} - -#[test] -fn test_any_address_normalize_eth() { - for coin in supported_coin_items() { - // TODO match `CoinType` when it's generated. - let (denormalized, expected_normalized) = match coin.blockchain { - BlockchainType::Aptos => ( - "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", - "0xf3d7f364dd7705824a5ebda9c7aab6cb3fc7bb5b58718249f12defec240b36cc", - ), - BlockchainType::Bitcoin => ( - "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", - "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", - ), - BlockchainType::Cosmos if coin.id == "cosmos" => ( - "cosmosvaloper1sxx9mszve0gaedz5ld7qdkjkfv8z992ax69k08", - "cosmosvaloper1sxx9mszve0gaedz5ld7qdkjkfv8z992ax69k08", - ), - // Skip other Cosmos chains until `CoinType` is not generated by a codegen tool. - BlockchainType::Cosmos => continue, - BlockchainType::Ethereum => ( - "0xb16db98b365b1f89191996942612b14f1da4bd5f", - "0xb16Db98B365B1f89191996942612B14F1Da4Bd5f", - ), - BlockchainType::InternetComputer => ( - "290CC7C359F44C8516FC169C5ED4F0F3AE2E24BF5DE0D4C51F5E7545B5474FAA", - "290cc7c359f44c8516fc169c5ed4f0f3ae2e24bf5de0d4c51f5e7545b5474faa", - ), - BlockchainType::NativeEvmos => ( - "evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw34", - "evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw34", - ), - BlockchainType::NativeInjective => ( - "inj14py36sx57ud82t9yrks9z6hdsrpn5x6k8tf7m3", - "inj14py36sx57ud82t9yrks9z6hdsrpn5x6k8tf7m3", - ), - BlockchainType::Ronin => ( - "0xb16db98b365b1f89191996942612b14f1da4bd5f", - "ronin:b16Db98B365B1f89191996942612B14F1Da4Bd5f", - ), - BlockchainType::Thorchain => ( - "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r", - "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r", - ), - BlockchainType::Unsupported => unreachable!(), - }; - - let denormalized = TWStringHelper::create(denormalized); - - let any_address = TWAnyAddressHelper::wrap(unsafe { - tw_any_address_create_with_string(denormalized.ptr(), coin.coin_id) - }); - - let normalized = - TWStringHelper::wrap(unsafe { tw_any_address_description(any_address.ptr()) }); - - assert_eq!( - normalized.to_string(), - Some(expected_normalized.to_string()) - ); - } -} - -#[test] -fn test_any_address_is_valid_coin() { - for coin in supported_coin_items() { - let valid = match coin.blockchain { - BlockchainType::Aptos => vec![ - "0x1", - "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", - "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", - "19aadeca9388e009d136245b9a67423f3eee242b03142849eb4f81a4a409e59c", - "0x777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb", - "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175", - ], - BlockchainType::Bitcoin => vec![ - "1MrZNGN7mfWZiZNQttrzHjfw72jnJC2JNx", - "bc1qunq74p3h8425hr6wllevlvqqr6sezfxj262rff", - "bc1pwse34zfpvt344rvlt7tw0ngjtfh9xasc4q03avf0lk74jzjpzjuqaz7ks5", - ], - BlockchainType::Cosmos if coin.id == "cosmos" => vec![ - "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02", - "cosmosvaloper1sxx9mszve0gaedz5ld7qdkjkfv8z992ax69k08", - "cosmosvalconspub1zcjduepqjnnwe2jsywv0kfc97pz04zkm7tc9k2437cde2my3y5js9t7cw9mstfg3sa", - ], - // Skip other Cosmos chains until `CoinType` is not generated by a codegen tool. - BlockchainType::Cosmos => continue, - BlockchainType::Ethereum => vec![ - "0xb16db98b365b1f89191996942612b14f1da4bd5f", - "0xb16Db98B365B1f89191996942612B14F1Da4Bd5f", - ], - BlockchainType::InternetComputer => vec![ - "fb257577279ecac604d4780214af95aa6adc3a814f6f3d6d7ac844d1deca500a", - "e90c48d54847f4758f1d6b589a1db2500757a49a6722d4f775e050107b4b752d", - "a7c5baf393aed527ef6fb3869fbf84dd4e562edf9b04bd8f9bfbd6b8e6a22776", - "4cb2ca5cfcfa1d952f8cd7f0ec46c96e1023ab057b83a2c7ce236b9e71ccca0b", - ], - BlockchainType::NativeEvmos => vec![ - "evmos14py36sx57ud82t9yrks9z6hdsrpn5x6k0r05np", - "evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw34" - ], - BlockchainType::NativeInjective => vec![ - "inj13u6g7vqgw074mgmf2ze2cadzvkz9snlwcrtq8a", - "inj1xmpkmxr4as00em23tc2zgmuyy2gr4h3wgcl6vd" - ], - BlockchainType::Ronin => vec![ - "0xb16db98b365b1f89191996942612b14f1da4bd5f", - "0xb16Db98B365B1f89191996942612B14F1Da4Bd5f", - "ronin:b16db98b365b1f89191996942612b14f1da4bd5f", - "ronin:b16Db98B365B1f89191996942612B14F1Da4Bd5f", - ], - BlockchainType::Thorchain => vec![ - "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r", - "thor1c8jd7ad9pcw4k3wkuqlkz4auv95mldr2kyhc65", - ], - _ => unreachable!(), - }; - - for valid_addr in valid { - let valid = TWStringHelper::create(valid_addr); - assert!(unsafe { tw_any_address_is_valid(valid.ptr(), coin.coin_id) }); - } - } -} - -#[test] -fn test_any_address_is_valid_coin_invalid() { - for coin in supported_coin_items() { - let invalid = match coin.blockchain { - BlockchainType::Aptos => { - vec![ - "", // Empty - "Seff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", // Invalid Hex - "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175bb", // Too long - "0xSeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b", - ] - }, - BlockchainType::Bitcoin => { - vec!["0xb16db98b365b1f89191996942612b14f1da4bd5f"] - }, - BlockchainType::Cosmos => vec![ - "cosmosvaloper1sxx9mszve0gaedz5ld7qdkjkfv8z992ax6", - "one1a50tun737ulcvwy0yvve0pvu5skq0kjargvhwe", - "bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2", - ], - BlockchainType::Ethereum | BlockchainType::Ronin => { - vec!["b16Db98B365B1f89191996942612B14F1Da4Bd5f"] - }, - BlockchainType::InternetComputer => vec![ - "3357cba483f268d044d4bbd4639f82c16028a76eebdf62c51bc11fc918d278b", - "3357cba483f268d044d4bbd4639f82c16028a76eebdf62c51bc11fc918d278bce", - "553357cba483f268d044d4bbd4639f82c16028a76eebdf62c51bc11fc918d278", - ], - BlockchainType::NativeEvmos => vec!["evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw"], - BlockchainType::NativeInjective => vec!["ini13u6g7vqgw074mgmf2ze2cadzvkz9snlwcrtq8a"], - BlockchainType::Thorchain => vec![ - "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02", - "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0e", - ], - BlockchainType::Unsupported => unreachable!(), - }; - - for invalid_addr in invalid { - let valid = TWStringHelper::create(invalid_addr); - assert!(!unsafe { tw_any_address_is_valid(valid.ptr(), coin.coin_id) }); - } - } -} - -#[test] -fn test_any_address_get_data_eth() { - let addr = "0xb16Db98B365B1f89191996942612B14F1Da4Bd5f"; - - let address_str = TWStringHelper::create(addr); - let any_address = TWAnyAddressHelper::wrap(unsafe { - tw_any_address_create_with_string(address_str.ptr(), ETHEREUM_COIN_TYPE) - }); - let data = TWDataHelper::wrap(unsafe { tw_any_address_data(any_address.ptr()) }); - assert_eq!(data.to_vec(), Some(addr.decode_hex().unwrap())); -} - -#[test] -fn test_any_address_is_valid_bech32() { - let addr = "juno1mry47pkga5tdswtluy0m8teslpalkdq0gnn4mf"; - - let address_str = TWStringHelper::create(addr); - let hrp = TWStringHelper::create("juno"); - // Should be valid even though Osmosis chain has `osmo` default hrp. - let result = - unsafe { tw_any_address_is_valid_bech32(address_str.ptr(), OSMOSIS_COIN_TYPE, hrp.ptr()) }; - assert!(result); -} - -#[test] -fn test_any_address_create_bech32_with_public_key() { - let private_key = TWPrivateKeyHelper::with_hex( - "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", - ); - let public_key = TWPublicKeyHelper::wrap(unsafe { - tw_private_key_get_public_key_by_type(private_key.ptr(), PublicKeyType::Secp256k1 as u32) - }); - let hrp = TWStringHelper::create("juno"); - - // Should be valid even though Osmosis chain has `osmo` default hrp. - let any_address = TWAnyAddressHelper::wrap(unsafe { - tw_any_address_create_bech32_with_public_key(public_key.ptr(), OSMOSIS_COIN_TYPE, hrp.ptr()) - }); - - let description = - TWStringHelper::wrap(unsafe { tw_any_address_description(any_address.ptr()) }); - let expected = "juno1ten42eesehw0ktddcp0fws7d3ycsqez3fksy86"; - assert_eq!(description.to_string(), Some(expected.to_string())); -} diff --git a/rust/tw_any_coin/tests/tw_any_signer_ffi_tests.rs b/rust/tw_any_coin/tests/tw_any_signer_ffi_tests.rs index 7677f6c838b..74cd4e04d4b 100644 --- a/rust/tw_any_coin/tests/tw_any_signer_ffi_tests.rs +++ b/rust/tw_any_coin/tests/tw_any_signer_ffi_tests.rs @@ -4,109 +4,8 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use std::borrow::Cow; use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; -use tw_coin_entry::error::SigningErrorType; -use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; -use tw_number::U256; -use tw_proto::{deserialize, serialize}; - -const COSMOS_COIN_TYPE: u32 = 118; -const ETHEREUM_COIN_TYPE: u32 = 60; - -#[test] -fn test_any_signer_sign_eth() { - use tw_proto::Ethereum::Proto; - - let private = "0x4646464646464646464646464646464646464646464646464646464646464646" - .decode_hex() - .unwrap(); - - let transfer = Proto::mod_Transaction::Transfer { - amount: U256::encode_be_compact(1_000_000_000_000_000_000), - data: Cow::default(), - }; - - let input = Proto::SigningInput { - chain_id: U256::encode_be_compact(1), - nonce: U256::encode_be_compact(9), - gas_price: U256::encode_be_compact(20_000_000_000), - gas_limit: U256::encode_be_compact(21_000), - to_address: "0x3535353535353535353535353535353535353535".into(), - transaction: Some(Proto::Transaction { - transaction_oneof: Proto::mod_Transaction::OneOftransaction_oneof::transfer(transfer), - }), - private_key: private.into(), - ..Proto::SigningInput::default() - }; - - let input_data = TWDataHelper::create(serialize(&input).unwrap()); - - let output = - TWDataHelper::wrap(unsafe { tw_any_signer_sign(input_data.ptr(), ETHEREUM_COIN_TYPE) }) - .to_vec() - .expect("!tw_any_signer_sign returned nullptr"); - - let output: Proto::SigningOutput = deserialize(&output).unwrap(); - assert_eq!(output.error, SigningErrorType::OK); - assert!(output.error_message.is_empty()); - - let expected = "f86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83"; - assert_eq!(output.encoded.to_hex(), expected); -} - -#[test] -fn test_any_signer_sign_cosmos() { - use tw_proto::Cosmos::Proto; - use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum; - - let private_key = "8bbec3772ddb4df68f3186440380c301af116d1422001c1877d6f5e4dba8c8af" - .decode_hex() - .unwrap(); - - let send_msg = Proto::mod_Message::Send { - from_address: "cosmos1mky69cn8ektwy0845vec9upsdphktxt03gkwlx".into(), - to_address: "cosmos18s0hdnsllgcclweu9aymw4ngktr2k0rkygdzdp".into(), - amounts: vec![Proto::Amount { - denom: "uatom".into(), - amount: "400000".into(), - }], - ..Proto::mod_Message::Send::default() - }; - let input = Proto::SigningInput { - signing_mode: Proto::SigningMode::Protobuf, - account_number: 546179, - chain_id: "cosmoshub-4".into(), - sequence: 0, - fee: Some(Proto::Fee { - gas: 200000, - amounts: vec![Proto::Amount { - denom: "uatom".into(), - amount: "1000".into(), - }], - }), - private_key: private_key.into(), - messages: vec![Proto::Message { - message_oneof: MessageEnum::send_coins_message(send_msg), - }], - ..Proto::SigningInput::default() - }; - - let input_data = TWDataHelper::create(serialize(&input).unwrap()); - - let output = - TWDataHelper::wrap(unsafe { tw_any_signer_sign(input_data.ptr(), COSMOS_COIN_TYPE) }) - .to_vec() - .expect("!tw_any_signer_sign returned nullptr"); - - let output: Proto::SigningOutput = deserialize(&output).unwrap(); - assert_eq!(output.error, SigningErrorType::OK); - assert!(output.error_message.is_empty()); - - let expected = r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CpIBCo8BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm8KLWNvc21vczFta3k2OWNuOGVrdHd5MDg0NXZlYzl1cHNkcGhrdHh0MDNna3dseBItY29zbW9zMThzMGhkbnNsbGdjY2x3ZXU5YXltdzRuZ2t0cjJrMHJreWdkemRwGg8KBXVhdG9tEgY0MDAwMDASZQpOCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAuzvXOQ3owLGf5VGjeSzHzbpEfRn1+alK0HB4T4dVjZJEgQKAggBEhMKDQoFdWF0b20SBDEwMDAQwJoMGkCvvVE6d29P30cO9/lnXyGunWMPxNY12NuqDcCnFkNM0H4CUQdl1Gc9+ogIJbro5nyzZzlv9rl2/GsZox/JXoCX"}"#; - assert_eq!(output.serialized, expected); -} #[test] fn test_any_signer_sign_unknown_coin() { diff --git a/rust/tw_coin_registry/Cargo.toml b/rust/tw_coin_registry/Cargo.toml index e8a4155f09b..a63ba08b267 100644 --- a/rust/tw_coin_registry/Cargo.toml +++ b/rust/tw_coin_registry/Cargo.toml @@ -7,6 +7,8 @@ edition = "2021" lazy_static = "1.4.0" serde = { version = "1.0.163", features = ["derive"] } serde_json = "1.0.96" +strum = "0.25" +strum_macros = "0.25" tw_aptos = { path = "../tw_aptos" } tw_bitcoin = { path = "../tw_bitcoin" } tw_coin_entry = { path = "../tw_coin_entry" } @@ -22,3 +24,8 @@ tw_native_evmos = { path = "../chains/tw_native_evmos" } tw_native_injective = { path = "../chains/tw_native_injective" } tw_ronin = { path = "../tw_ronin" } tw_thorchain = { path = "../chains/tw_thorchain" } + +[build-dependencies] +itertools = "0.10.5" +serde = { version = "1.0.163", features = ["derive"] } +serde_json = "1.0.96" diff --git a/rust/tw_coin_registry/build.rs b/rust/tw_coin_registry/build.rs new file mode 100644 index 00000000000..4c79c1d01f4 --- /dev/null +++ b/rust/tw_coin_registry/build.rs @@ -0,0 +1,105 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use itertools::Itertools; +use serde::Deserialize; +use std::io::Write; +use std::path::PathBuf; +use std::{env, fs}; + +/// We're only interested in `id` and `name` of the coin to generate `CoinType` enum. +#[derive(Deserialize)] +struct CoinItem { + #[serde(rename = "coinId")] + coin_id: u64, + name: String, +} + +/// Transforms a coin name to a Rust name. +/// https://github.com/trustwallet/wallet-core/blob/3769f31b7d0c75126b2f426bb065364429aaa379/codegen/lib/coin_skeleton_gen.rb#L15-L22 +fn format_name(name: &str) -> String { + name.replace([' ', '.', '-'], "") +} + +fn generate_coin_type(coins: &[CoinItem]) -> String { + const RAW_TYPE: &str = "u32"; + const ENUM_NAME: &str = "CoinType"; + + let coin_types_variants = coins + .iter() + .map(|coin| format!("\t{} = {},\n", format_name(&coin.name), coin.coin_id)) + .join(""); + + format!( + r#"#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(strum_macros::EnumIter, strum_macros::FromRepr)] +#[repr({RAW_TYPE})] +pub enum CoinType {{ +{coin_types_variants} +}} + +impl {ENUM_NAME} {{ + pub fn iter() -> impl IntoIterator {{ + use strum::IntoEnumIterator; + + ::iter() + }} +}} + +impl TryFrom<{RAW_TYPE}> for {ENUM_NAME} {{ + type Error = (); + + fn try_from(num: {RAW_TYPE}) -> Result<{ENUM_NAME}, ()> {{ + {ENUM_NAME}::from_repr(num).ok_or(()) + }} +}} + +impl<'de> serde::Deserialize<'de> for {ENUM_NAME} {{ + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + {{ + let num_value: {RAW_TYPE} = {RAW_TYPE}::deserialize(deserializer)?; + {ENUM_NAME}::try_from(num_value).map_err(|_| serde::de::Error::custom("Invalid `CoinType` value")) + }} +}} +"# + ) +} + +fn generate_and_write_coin_type(coins: &[CoinItem]) { + let coin_type_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("coin_type.rs"); + + let coin_type_content = generate_coin_type(coins); + + let mut file = fs::OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(coin_type_path) + .expect("Error creating/opening coin_type.rs"); + file.write_all(coin_type_content.as_bytes()) + .expect("Error writing coin_type.rs"); +} + +fn main() { + let registry_path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()) + .join("..") + .join("..") + .join("registry.json"); + let registry_path_str = registry_path.to_str().unwrap(); + + // Re-run this build.rs if the `proto` directory has been changed (i.e. a new file is added). + println!("cargo:rerun-if-changed={registry_path_str}"); + + let registry_bytes = fs::read(registry_path).expect("Error reading registry.json"); + + let coins: Vec = + serde_json::from_slice(®istry_bytes).expect("registry.json expected to be valid"); + + generate_and_write_coin_type(&coins); +} diff --git a/rust/tw_coin_registry/src/blockchain_type.rs b/rust/tw_coin_registry/src/blockchain_type.rs index c344d6b16b5..357697cd83c 100644 --- a/rust/tw_coin_registry/src/blockchain_type.rs +++ b/rust/tw_coin_registry/src/blockchain_type.rs @@ -4,15 +4,13 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use crate::error::RegistryError; -use serde::de::Error; -use serde::{Deserialize, Deserializer}; -use std::str::FromStr; +use serde::Deserialize; /// Blockchain implementation type. /// Extend this enum when adding new blockchains. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Deserialize, PartialEq)] pub enum BlockchainType { + // start_of_blockchain_type - USED TO GENERATE CODE Aptos, Bitcoin, Cosmos, @@ -22,34 +20,13 @@ pub enum BlockchainType { NativeInjective, Ronin, Thorchain, + // end_of_blockchain_type - USED TO GENERATE CODE + #[serde(other)] Unsupported, } -impl<'de> Deserialize<'de> for BlockchainType { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - BlockchainType::from_str(&s).map_err(|e| Error::custom(format!("{e:?}"))) - } -} - -impl FromStr for BlockchainType { - type Err = RegistryError; - - fn from_str(s: &str) -> Result { - match s { - "Aptos" => Ok(BlockchainType::Aptos), - "Bitcoin" => Ok(BlockchainType::Bitcoin), - "Cosmos" => Ok(BlockchainType::Cosmos), - "Ethereum" => Ok(BlockchainType::Ethereum), - "InternetComputer" => Ok(BlockchainType::InternetComputer), - "NativeEvmos" => Ok(BlockchainType::NativeEvmos), - "NativeInjective" => Ok(BlockchainType::NativeInjective), - "Ronin" => Ok(BlockchainType::Ronin), - "Thorchain" => Ok(BlockchainType::Thorchain), - _ => Ok(BlockchainType::Unsupported), - } +impl BlockchainType { + pub fn is_supported(&self) -> bool { + !matches!(self, BlockchainType::Unsupported) } } diff --git a/rust/tw_coin_registry/src/dispatcher.rs b/rust/tw_coin_registry/src/dispatcher.rs index 0f3c36e2b70..02850a513d0 100644 --- a/rust/tw_coin_registry/src/dispatcher.rs +++ b/rust/tw_coin_registry/src/dispatcher.rs @@ -24,6 +24,7 @@ use tw_thorchain::entry::ThorchainEntry; pub type CoinEntryExtStaticRef = &'static dyn CoinEntryExt; pub type EvmEntryExtStaticRef = &'static dyn EvmEntryExt; +// start_of_blockchain_entries - USED TO GENERATE CODE const APTOS: AptosEntry = AptosEntry; const BITCOIN: BitcoinEntry = BitcoinEntry; const COSMOS: CosmosEntry = CosmosEntry; @@ -33,9 +34,11 @@ const NATIVE_EVMOS: NativeEvmosEntry = NativeEvmosEntry; const NATIVE_INJECTIVE: NativeInjectiveEntry = NativeInjectiveEntry; const RONIN: RoninEntry = RoninEntry; const THORCHAIN: ThorchainEntry = ThorchainEntry; +// end_of_blockchain_entries - USED TO GENERATE CODE pub fn blockchain_dispatcher(blockchain: BlockchainType) -> RegistryResult { match blockchain { + // start_of_blockchain_dispatcher - USED TO GENERATE CODE BlockchainType::Aptos => Ok(&APTOS), BlockchainType::Bitcoin => Ok(&BITCOIN), BlockchainType::Cosmos => Ok(&COSMOS), @@ -44,8 +47,9 @@ pub fn blockchain_dispatcher(blockchain: BlockchainType) -> RegistryResult Ok(&NATIVE_EVMOS), BlockchainType::NativeInjective => Ok(&NATIVE_INJECTIVE), BlockchainType::Ronin => Ok(&RONIN), - BlockchainType::Thorchain => Ok(&THORCHAIN), BlockchainType::Unsupported => Err(RegistryError::Unsupported), + BlockchainType::Thorchain => Ok(&THORCHAIN), + // end_of_blockchain_dispatcher - USED TO GENERATE CODE } } @@ -61,15 +65,8 @@ pub fn coin_dispatcher( pub fn evm_dispatcher(coin: CoinType) -> RegistryResult { let item = get_coin_item(coin)?; match item.blockchain { - BlockchainType::Aptos => Err(RegistryError::Unsupported), - BlockchainType::Bitcoin => Err(RegistryError::Unsupported), - BlockchainType::Cosmos => Err(RegistryError::Unsupported), BlockchainType::Ethereum => Ok(ÐEREUM), - BlockchainType::InternetComputer => Err(RegistryError::Unsupported), - BlockchainType::NativeEvmos => Err(RegistryError::Unsupported), - BlockchainType::NativeInjective => Err(RegistryError::Unsupported), BlockchainType::Ronin => Ok(&RONIN), - BlockchainType::Thorchain => Err(RegistryError::Unsupported), - BlockchainType::Unsupported => Err(RegistryError::Unsupported), + _ => Err(RegistryError::Unsupported), } } diff --git a/rust/tw_coin_registry/src/lib.rs b/rust/tw_coin_registry/src/lib.rs index cbad9faaa44..d8e50a8f1c6 100644 --- a/rust/tw_coin_registry/src/lib.rs +++ b/rust/tw_coin_registry/src/lib.rs @@ -6,7 +6,10 @@ pub mod blockchain_type; pub mod coin_context; -pub mod coin_type; pub mod dispatcher; pub mod error; pub mod registry; + +pub mod coin_type { + include!(concat!(env!("OUT_DIR"), "/coin_type.rs")); +} diff --git a/rust/tw_coin_registry/src/registry.rs b/rust/tw_coin_registry/src/registry.rs index e438210c278..58b6fef8cc7 100644 --- a/rust/tw_coin_registry/src/registry.rs +++ b/rust/tw_coin_registry/src/registry.rs @@ -51,6 +51,13 @@ pub fn supported_coin_items() -> impl Iterator { registry_iter().filter(|item| !matches!(item.blockchain, BlockchainType::Unsupported)) } +#[inline] +pub fn coin_items_by_blockchain( + blockchain: BlockchainType, +) -> impl Iterator { + registry_iter().filter(move |item| item.blockchain == blockchain) +} + fn parse_registry_json() -> RegistryMap { let items: Vec = serde_json::from_str(REGISTRY_JSON).expect("registry.json expected to be valid"); diff --git a/rust/wallet_core_rs/src/ffi/ethereum/abi.rs b/rust/wallet_core_rs/src/ffi/ethereum/abi.rs index 76b9906d0bc..84f64877781 100644 --- a/rust/wallet_core_rs/src/ffi/ethereum/abi.rs +++ b/rust/wallet_core_rs/src/ffi/ethereum/abi.rs @@ -20,9 +20,10 @@ use tw_misc::try_or_else; /// \return serialized `EthereumAbi::Proto::ContractCallDecodingOutput`. #[no_mangle] pub unsafe extern "C" fn tw_ethereum_abi_decode_contract_call( - coin: CoinType, + coin: u32, input: *const TWData, ) -> *mut TWData { + let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut); let input_data = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); let evm_dispatcher = try_or_else!(evm_dispatcher(coin), std::ptr::null_mut); @@ -39,9 +40,10 @@ pub unsafe extern "C" fn tw_ethereum_abi_decode_contract_call( /// \return The serialized data of a `TW.EthereumAbi.Proto.ParamsDecodingOutput` proto object. #[no_mangle] pub unsafe extern "C" fn tw_ethereum_abi_decode_params( - coin: CoinType, + coin: u32, input: *const TWData, ) -> *mut TWData { + let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut); let input_data = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); let evm_dispatcher = try_or_else!(evm_dispatcher(coin), std::ptr::null_mut); @@ -58,9 +60,10 @@ pub unsafe extern "C" fn tw_ethereum_abi_decode_params( /// \return function type signature as a Non-null string. #[no_mangle] pub unsafe extern "C" fn tw_ethereum_abi_function_get_signature( - coin: CoinType, + coin: u32, input: *const TWData, ) -> *mut TWString { + let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut); let input_data = try_or_else!(TWData::from_ptr_as_ref(input), || TWString::new() .into_ptr()); let evm_dispatcher = try_or_else!(evm_dispatcher(coin), || TWString::new().into_ptr()); @@ -78,9 +81,10 @@ pub unsafe extern "C" fn tw_ethereum_abi_function_get_signature( /// \return The serialized data of a `TW.EthereumAbi.Proto.FunctionEncodingOutput` proto object. #[no_mangle] pub unsafe extern "C" fn tw_ethereum_abi_encode_function( - coin: CoinType, + coin: u32, input: *const TWData, ) -> *mut TWData { + let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut); let input_data = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); let evm_dispatcher = try_or_else!(evm_dispatcher(coin), std::ptr::null_mut); @@ -97,9 +101,10 @@ pub unsafe extern "C" fn tw_ethereum_abi_encode_function( /// \return The serialized data of a `TW.EthereumAbi.Proto.ValueDecodingOutput` proto object. #[no_mangle] pub unsafe extern "C" fn tw_ethereum_abi_decode_value( - coin: CoinType, + coin: u32, input: *const TWData, ) -> *mut TWData { + let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut); let input_data = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); let evm_dispatcher = try_or_else!(evm_dispatcher(coin), std::ptr::null_mut); diff --git a/rust/wallet_core_rs/src/ffi/ethereum/rlp.rs b/rust/wallet_core_rs/src/ffi/ethereum/rlp.rs index 308228bd09b..f13470891c6 100644 --- a/rust/wallet_core_rs/src/ffi/ethereum/rlp.rs +++ b/rust/wallet_core_rs/src/ffi/ethereum/rlp.rs @@ -18,10 +18,8 @@ use tw_misc::try_or_else; /// \param input Non-null serialized `EthereumRlp::Proto::EncodingInput`. /// \return serialized `EthereumRlp::Proto::EncodingOutput`. #[no_mangle] -pub unsafe extern "C" fn tw_ethereum_rlp_encode( - coin: CoinType, - input: *const TWData, -) -> *mut TWData { +pub unsafe extern "C" fn tw_ethereum_rlp_encode(coin: u32, input: *const TWData) -> *mut TWData { + let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut); let input_data = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); let evm_dispatcher = try_or_else!(evm_dispatcher(coin), std::ptr::null_mut); evm_dispatcher diff --git a/rust/wallet_core_rs/tests/ethereum_abi.rs b/rust/wallet_core_rs/tests/ethereum_abi.rs index 85b98a043dc..50f1dd09d7e 100644 --- a/rust/wallet_core_rs/tests/ethereum_abi.rs +++ b/rust/wallet_core_rs/tests/ethereum_abi.rs @@ -17,12 +17,11 @@ use wallet_core_rs::ffi::ethereum::abi::{ tw_ethereum_abi_function_get_signature, }; +use tw_coin_registry::coin_type::CoinType; use Proto::mod_ParamType::OneOfparam as ParamTypeEnum; use Proto::mod_Token::OneOftoken as TokenEnum; use Proto::AbiError as AbiErrorKind; -const ETHEREUM_COIN_TYPE: u32 = 60; - fn param(name: &str, kind: ParamTypeEnum<'static>) -> Proto::Param<'static> { Proto::Param { name: name.to_string().into(), @@ -58,7 +57,7 @@ fn test_ethereum_abi_decode_contract_call() { let input_data = TWDataHelper::create(serialize(&input).unwrap()); let output_data = TWDataHelper::wrap(unsafe { - tw_ethereum_abi_decode_contract_call(ETHEREUM_COIN_TYPE, input_data.ptr()) + tw_ethereum_abi_decode_contract_call(CoinType::Ethereum as u32, input_data.ptr()) }) .to_vec() .expect("!tw_ethereum_abi_decode_contract_call returned nullptr"); @@ -98,7 +97,7 @@ fn test_ethereum_abi_decode_params() { let input_data = TWDataHelper::create(serialize(&input).unwrap()); let output_data = TWDataHelper::wrap(unsafe { - tw_ethereum_abi_decode_params(ETHEREUM_COIN_TYPE, input_data.ptr()) + tw_ethereum_abi_decode_params(CoinType::Ethereum as u32, input_data.ptr()) }) .to_vec() .expect("!tw_ethereum_abi_decode_params returned nullptr"); @@ -135,7 +134,7 @@ fn test_ethereum_abi_function_get_signature() { let input_data = TWDataHelper::create(serialize(&input).unwrap()); let actual = TWStringHelper::wrap(unsafe { - tw_ethereum_abi_function_get_signature(ETHEREUM_COIN_TYPE, input_data.ptr()) + tw_ethereum_abi_function_get_signature(CoinType::Ethereum as u32, input_data.ptr()) }) .to_string() .expect("!tw_ethereum_abi_function_get_signature returned nullptr"); @@ -156,7 +155,7 @@ fn test_ethereum_abi_encode_function() { let input_data = TWDataHelper::create(serialize(&input).unwrap()); let output_data = TWDataHelper::wrap(unsafe { - tw_ethereum_abi_encode_function(ETHEREUM_COIN_TYPE, input_data.ptr()) + tw_ethereum_abi_encode_function(CoinType::Ethereum as u32, input_data.ptr()) }) .to_vec() .expect("!tw_ethereum_abi_encode_function returned nullptr"); @@ -182,7 +181,7 @@ fn test_ethereum_abi_decode_value() { let input_data = TWDataHelper::create(serialize(&input).unwrap()); let output_data = TWDataHelper::wrap(unsafe { - tw_ethereum_abi_decode_value(ETHEREUM_COIN_TYPE, input_data.ptr()) + tw_ethereum_abi_decode_value(CoinType::Ethereum as u32, input_data.ptr()) }) .to_vec() .expect("!tw_ethereum_abi_decode_value returned nullptr"); diff --git a/rust/wallet_core_rs/tests/ethereum_rlp.rs b/rust/wallet_core_rs/tests/ethereum_rlp.rs index 29811ac0def..2326c482042 100644 --- a/rust/wallet_core_rs/tests/ethereum_rlp.rs +++ b/rust/wallet_core_rs/tests/ethereum_rlp.rs @@ -5,6 +5,7 @@ // file LICENSE at the root of the source code distribution tree. use tw_coin_entry::error::SigningErrorType; +use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::ToHex; use tw_memory::test_utils::tw_data_helper::TWDataHelper; use tw_proto::EthereumRlp::Proto as RlpProto; @@ -12,8 +13,6 @@ use tw_proto::{deserialize, serialize}; use wallet_core_rs::ffi::ethereum::rlp::tw_ethereum_rlp_encode; use RlpProto::mod_RlpItem::OneOfitem as Item; -const ETHEREUM_COIN_TYPE: u32 = 60; - #[test] fn test_ethereum_rlp() { let item = RlpProto::RlpItem { @@ -22,10 +21,11 @@ fn test_ethereum_rlp() { let input = RlpProto::EncodingInput { item: Some(item) }; let input_data = TWDataHelper::create(serialize(&input).unwrap()); - let output_data = - TWDataHelper::wrap(unsafe { tw_ethereum_rlp_encode(ETHEREUM_COIN_TYPE, input_data.ptr()) }) - .to_vec() - .expect("!tw_ethereum_rlp_encode returned nullptr"); + let output_data = TWDataHelper::wrap(unsafe { + tw_ethereum_rlp_encode(CoinType::Ethereum as u32, input_data.ptr()) + }) + .to_vec() + .expect("!tw_ethereum_rlp_encode returned nullptr"); let output: RlpProto::EncodingOutput = deserialize(&output_data).expect("!tw_ethereum_rlp_encode returned an invalid output"); From 6ef3eb511ef566145251f9551d4e2987ffe5c552 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Fri, 24 Nov 2023 14:22:29 +0100 Subject: [PATCH 018/128] [Rust]: Extend codegen-v2 capabilities (#3570) * [Rust]: Small refactoring * [Rust]: Add `TemplateGenerator` * Add new blockchain crate templates * [Rust]: Add new blockchain integration tests templates * [Rust]: Add new blockchain to test_coin_address_derivation * [C++]: Add cpp::new_blockchain * Add C++ Entry.h template * [C++]: Fix cpp::new_blockchain * [C++]: Add TWCoinType, TWBlockchain enum generating * [C++]: Add Blockchain entry to the `Coin.cpp` dispatcher * [Codegen]: Some improvements * [Codegen]: Revert coin_skeleton_gen.rb * [Codegen]: Generate Blockchain.proto file * [Codegen]: Generate TWCoinTypeTests.cppp file * [Codegen]: Generate TWAnySignerTests.cpp, TWAnyAddressTests.cpp files * [Codegen]: Fix Protobuf * [Codegen]: Refactor codegen-v1 to allow to generate mobile tests only * [Codegen]: Generate CoinAddressDerivationTests.cpp cases * [Codegen]: Add new-evmchain cmd * [Codegen]: Refactor C++ code generators * [Codegen]: Refactor Rust generators, fix sorting error * [Codegen]: Run codegen-v2 new-blockchain-rust iotex on Rust CI pipeline * [CI] Trigger CI --- .github/workflows/linux-ci-rust.yml | 15 ++ codegen-v2/Cargo.lock | 10 ++ codegen-v2/Cargo.toml | 1 + .../cpp/blockchain_dispatcher_generator.rs | 75 ++++++++++ codegen-v2/src/codegen/cpp/entry_generator.rs | 43 ++++++ codegen-v2/src/codegen/cpp/mod.rs | 45 ++++++ codegen-v2/src/codegen/cpp/new_blockchain.rs | 36 +++++ codegen-v2/src/codegen/cpp/new_evmchain.rs | 22 +++ codegen-v2/src/codegen/cpp/templates/Entry.h | 19 +++ .../cpp/templates/TWAnyAddressTests.cpp | 26 ++++ .../cpp/templates/TWAnySignerTests.cpp | 19 +++ .../codegen/cpp/templates/TWCoinTypeTests.cpp | 31 ++++ .../cpp/tw_any_address_tests_generator.rs | 39 +++++ .../cpp/tw_any_signer_tests_generator.rs | 39 +++++ codegen-v2/src/codegen/cpp/tw_blockchain.rs | 41 ++++++ ...coin_address_derivation_tests_generator.rs | 65 +++++++++ .../src/codegen/cpp/tw_coin_type_generator.rs | 37 +++++ .../cpp/tw_coin_type_tests_generator.rs | 39 +++++ codegen-v2/src/codegen/mod.rs | 3 + codegen-v2/src/codegen/proto/mod.rs | 18 +++ .../src/codegen/proto/new_blockchain.rs | 13 ++ .../src/codegen/proto/proto_generator.rs | 37 +++++ .../codegen/proto/templates/Blockchain.proto | 39 +++++ .../rust/blockchain_dispatcher_generator.rs | 80 +++++++++++ .../src/codegen/rust/blockchain_type.rs | 133 ------------------ .../codegen/rust/blockchain_type_generator.rs | 41 ++++++ .../coin_address_derivation_test_generator.rs | 56 ++++++++ codegen-v2/src/codegen/rust/coin_crate.rs | 84 +++++------ .../codegen/rust/coin_integration_tests.rs | 108 +++++++------- .../rust/coin_registry_manifest_generator.rs | 21 +++ codegen-v2/src/codegen/rust/mod.rs | 72 ++-------- codegen-v2/src/codegen/rust/new_blockchain.rs | 25 ++-- codegen-v2/src/codegen/rust/new_evmchain.rs | 14 ++ .../templates/blockchain_crate/Cargo.toml | 10 ++ .../templates/blockchain_crate/address.rs | 36 +++++ .../templates/blockchain_crate/compiler.rs | 52 +++++++ .../rust/templates/blockchain_crate/entry.rs | 91 ++++++++++++ .../rust/templates/blockchain_crate/lib.rs | 10 ++ .../rust/templates/blockchain_crate/signer.rs | 29 ++++ .../integration_tests/address_tests.rs | 30 ++++ .../integration_tests/compile_tests.rs | 10 ++ .../rust/templates/integration_tests/mod.rs | 9 ++ .../templates/integration_tests/sign_tests.rs | 10 ++ codegen-v2/src/codegen/rust/toml_editor.rs | 6 +- codegen-v2/src/codegen/template_generator.rs | 80 +++++++++++ codegen-v2/src/{codegen/rust => }/coin_id.rs | 0 codegen-v2/src/lib.rs | 2 + codegen-v2/src/main.rs | 46 ++++-- codegen-v2/src/registry.rs | 89 ++++++++++++ codegen-v2/src/utils.rs | 58 ++++++-- codegen/bin/newcoin | 2 +- codegen/bin/newcoin-mobile-tests | 22 +++ codegen/lib/coin_skeleton_gen.rb | 63 ++++++--- include/TrustWalletCore/TWBlockchain.h | 4 +- include/TrustWalletCore/TWCoinType.h | 1 + ...sts.rs => coin_address_derivation_test.rs} | 7 +- rust/tw_coin_entry/src/coin_entry.rs | 7 + rust/tw_coin_registry/src/dispatcher.rs | 2 +- tests/common/CoinAddressDerivationTests.cpp | 2 + 59 files changed, 1664 insertions(+), 360 deletions(-) create mode 100644 codegen-v2/src/codegen/cpp/blockchain_dispatcher_generator.rs create mode 100644 codegen-v2/src/codegen/cpp/entry_generator.rs create mode 100644 codegen-v2/src/codegen/cpp/mod.rs create mode 100644 codegen-v2/src/codegen/cpp/new_blockchain.rs create mode 100644 codegen-v2/src/codegen/cpp/new_evmchain.rs create mode 100644 codegen-v2/src/codegen/cpp/templates/Entry.h create mode 100644 codegen-v2/src/codegen/cpp/templates/TWAnyAddressTests.cpp create mode 100644 codegen-v2/src/codegen/cpp/templates/TWAnySignerTests.cpp create mode 100644 codegen-v2/src/codegen/cpp/templates/TWCoinTypeTests.cpp create mode 100644 codegen-v2/src/codegen/cpp/tw_any_address_tests_generator.rs create mode 100644 codegen-v2/src/codegen/cpp/tw_any_signer_tests_generator.rs create mode 100644 codegen-v2/src/codegen/cpp/tw_blockchain.rs create mode 100644 codegen-v2/src/codegen/cpp/tw_coin_address_derivation_tests_generator.rs create mode 100644 codegen-v2/src/codegen/cpp/tw_coin_type_generator.rs create mode 100644 codegen-v2/src/codegen/cpp/tw_coin_type_tests_generator.rs create mode 100644 codegen-v2/src/codegen/proto/mod.rs create mode 100644 codegen-v2/src/codegen/proto/new_blockchain.rs create mode 100644 codegen-v2/src/codegen/proto/proto_generator.rs create mode 100644 codegen-v2/src/codegen/proto/templates/Blockchain.proto create mode 100644 codegen-v2/src/codegen/rust/blockchain_dispatcher_generator.rs delete mode 100644 codegen-v2/src/codegen/rust/blockchain_type.rs create mode 100644 codegen-v2/src/codegen/rust/blockchain_type_generator.rs create mode 100644 codegen-v2/src/codegen/rust/coin_address_derivation_test_generator.rs create mode 100644 codegen-v2/src/codegen/rust/coin_registry_manifest_generator.rs create mode 100644 codegen-v2/src/codegen/rust/new_evmchain.rs create mode 100644 codegen-v2/src/codegen/rust/templates/blockchain_crate/Cargo.toml create mode 100644 codegen-v2/src/codegen/rust/templates/blockchain_crate/address.rs create mode 100644 codegen-v2/src/codegen/rust/templates/blockchain_crate/compiler.rs create mode 100644 codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs create mode 100644 codegen-v2/src/codegen/rust/templates/blockchain_crate/lib.rs create mode 100644 codegen-v2/src/codegen/rust/templates/blockchain_crate/signer.rs create mode 100644 codegen-v2/src/codegen/rust/templates/integration_tests/address_tests.rs create mode 100644 codegen-v2/src/codegen/rust/templates/integration_tests/compile_tests.rs create mode 100644 codegen-v2/src/codegen/rust/templates/integration_tests/mod.rs create mode 100644 codegen-v2/src/codegen/rust/templates/integration_tests/sign_tests.rs create mode 100644 codegen-v2/src/codegen/template_generator.rs rename codegen-v2/src/{codegen/rust => }/coin_id.rs (100%) create mode 100644 codegen-v2/src/registry.rs create mode 100755 codegen/bin/newcoin-mobile-tests rename rust/tw_any_coin/tests/{coin_address_derivation_tests.rs => coin_address_derivation_test.rs} (96%) diff --git a/.github/workflows/linux-ci-rust.yml b/.github/workflows/linux-ci-rust.yml index 9a27424cea2..e0dcee23cfd 100644 --- a/.github/workflows/linux-ci-rust.yml +++ b/.github/workflows/linux-ci-rust.yml @@ -74,3 +74,18 @@ jobs: - name: Gather and check Rust code coverage run: | tools/check-coverage rust/coverage.stats rust/coverage.info + + # Generate files for a blockchain in the end of the pipeline. + # Please note the blockchain should not be implemented in Rust at the moment of running this step, + # otherwise consider either generate files for another blockchain or remove this step at all. + - name: Test codegen-v2 new-blockchain-rust + run: | + cargo run -- new-blockchain-rust iotex + working-directory: codegen-v2 + + # Check if `new-blockchain-rust` command has generated files that do not break project compilation. + - name: Check Rust compiles + run: | + cargo check --tests + working-directory: rust + diff --git a/codegen-v2/Cargo.lock b/codegen-v2/Cargo.lock index 254400df25c..6caac47e7df 100644 --- a/codegen-v2/Cargo.lock +++ b/codegen-v2/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -27,6 +36,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" name = "codegen-v2" version = "0.1.0" dependencies = [ + "aho-corasick", "convert_case", "handlebars", "heck", diff --git a/codegen-v2/Cargo.toml b/codegen-v2/Cargo.toml index e024e4024ff..52ad244f84a 100644 --- a/codegen-v2/Cargo.toml +++ b/codegen-v2/Cargo.toml @@ -12,6 +12,7 @@ name = "parser" path = "src/main.rs" [dependencies] +aho-corasick = "1.1.2" convert_case = "0.6.0" pathdiff = "0.2.1" serde = { version = "1.0.159", features = ["derive"] } diff --git a/codegen-v2/src/codegen/cpp/blockchain_dispatcher_generator.rs b/codegen-v2/src/codegen/cpp/blockchain_dispatcher_generator.rs new file mode 100644 index 00000000000..58b13ac452c --- /dev/null +++ b/codegen-v2/src/codegen/cpp/blockchain_dispatcher_generator.rs @@ -0,0 +1,75 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::cpp::cpp_source_directory; +use crate::registry::CoinItem; +use crate::utils::FileContent; +use crate::Result; +use std::path::PathBuf; + +const COIN_INCLUDES_END: &str = "end_of_coin_includes_marker_do_not_modify"; +const COIN_DISPATCHER_DECLARATIONS_END: &str = + "end_of_coin_dipatcher_declarations_marker_do_not_modify"; +const COIN_DISPATCHER_SWITCH_END: &str = "end_of_coin_dipatcher_switch_marker_do_not_modify"; + +fn dispatcher_coin_cpp_path() -> PathBuf { + cpp_source_directory().join("Coin.cpp") +} + +/// Represents `Coin.cpp`. +pub struct BlockchainDispatcherGenerator; + +impl BlockchainDispatcherGenerator { + pub fn generate_new_blockchain_type_dispatching(coin: &CoinItem) -> Result<()> { + let mut file_content = FileContent::read(dispatcher_coin_cpp_path())?; + + Self::generate_include_of_blockchain_entry(coin, &mut file_content)?; + Self::generate_blockchain_entry_constant(coin, &mut file_content)?; + Self::generate_blockchain_dispatcher_case(coin, &mut file_content)?; + + file_content.write() + } + + fn generate_include_of_blockchain_entry( + coin: &CoinItem, + file_content: &mut FileContent, + ) -> Result<()> { + let blockchain_type = coin.blockchain_type(); + + let mut line_marker = file_content.rfind_line(|line| line.contains(COIN_INCLUDES_END))?; + line_marker.push_line_before(format!(r#"#include "{blockchain_type}/Entry.h""#)); + + Ok(()) + } + + fn generate_blockchain_entry_constant( + coin: &CoinItem, + file_content: &mut FileContent, + ) -> Result<()> { + let blockchain_type = coin.blockchain_type(); + + let mut entries_region = + file_content.rfind_line(|line| line.contains(COIN_DISPATCHER_DECLARATIONS_END))?; + entries_region.push_line_before(format!("{blockchain_type}::Entry {blockchain_type}DP;")); + + Ok(()) + } + + fn generate_blockchain_dispatcher_case( + coin: &CoinItem, + file_content: &mut FileContent, + ) -> Result<()> { + let blockchain_type = coin.blockchain_type(); + + let mut entries_region = + file_content.rfind_line(|line| line.contains(COIN_DISPATCHER_SWITCH_END))?; + entries_region.push_line_before(format!( + " case TWBlockchain{blockchain_type}: entry = &{blockchain_type}DP; break;" + )); + + Ok(()) + } +} diff --git a/codegen-v2/src/codegen/cpp/entry_generator.rs b/codegen-v2/src/codegen/cpp/entry_generator.rs new file mode 100644 index 00000000000..033c94d6856 --- /dev/null +++ b/codegen-v2/src/codegen/cpp/entry_generator.rs @@ -0,0 +1,43 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::cpp::cpp_source_directory; +use crate::codegen::template_generator::TemplateGenerator; +use crate::registry::CoinItem; +use crate::{Error, Result}; +use std::path::PathBuf; +use std::{fs, io}; + +const ENTRY_HEADER_TEMPLATE: &str = include_str!("templates/Entry.h"); + +pub fn coin_source_directory(coin: &CoinItem) -> PathBuf { + cpp_source_directory().join(coin.blockchain_type()) +} + +pub struct EntryGenerator; + +impl EntryGenerator { + pub fn generate(coin: &CoinItem) -> Result { + let blockchain_dir = coin_source_directory(coin); + let entry_header_path = blockchain_dir.join("Entry.h"); + + if blockchain_dir.exists() { + return Err(Error::IoError(io::Error::new( + io::ErrorKind::AlreadyExists, + "blockchain already exists", + ))); + } + + fs::create_dir(&blockchain_dir)?; + + TemplateGenerator::new(ENTRY_HEADER_TEMPLATE) + .write_to(entry_header_path.clone()) + .with_default_patterns(coin) + .write()?; + + Ok(entry_header_path) + } +} diff --git a/codegen-v2/src/codegen/cpp/mod.rs b/codegen-v2/src/codegen/cpp/mod.rs new file mode 100644 index 00000000000..87443e4ad6d --- /dev/null +++ b/codegen-v2/src/codegen/cpp/mod.rs @@ -0,0 +1,45 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::registry::CoinItem; +use std::env; +use std::path::PathBuf; + +pub mod blockchain_dispatcher_generator; +pub mod entry_generator; +pub mod new_blockchain; +pub mod new_evmchain; +pub mod tw_any_address_tests_generator; +pub mod tw_any_signer_tests_generator; +pub mod tw_blockchain; +pub mod tw_coin_address_derivation_tests_generator; +pub mod tw_coin_type_generator; +pub mod tw_coin_type_tests_generator; + +pub fn cpp_source_directory() -> PathBuf { + PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()) + .join("..") + .join("src") +} + +pub fn cpp_include_directory() -> PathBuf { + PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()) + .join("..") + .join("include") + .join("TrustWalletCore") +} + +pub fn integration_tests_directory() -> PathBuf { + PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()) + .join("..") + .join("tests") +} + +pub fn coin_integration_tests_directory(coin: &CoinItem) -> PathBuf { + integration_tests_directory() + .join("chains") + .join(coin.coin_type()) +} diff --git a/codegen-v2/src/codegen/cpp/new_blockchain.rs b/codegen-v2/src/codegen/cpp/new_blockchain.rs new file mode 100644 index 00000000000..75634f481de --- /dev/null +++ b/codegen-v2/src/codegen/cpp/new_blockchain.rs @@ -0,0 +1,36 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::cpp::blockchain_dispatcher_generator::BlockchainDispatcherGenerator; +use crate::codegen::cpp::entry_generator::EntryGenerator; +use crate::codegen::cpp::tw_any_address_tests_generator::TWAnyAddressTestsGenerator; +use crate::codegen::cpp::tw_any_signer_tests_generator::TWAnySignerTestsGenerator; +use crate::codegen::cpp::tw_blockchain::TWBlockchainGenerator; +use crate::codegen::cpp::tw_coin_address_derivation_tests_generator::CoinAddressDerivationTestsGenerator; +use crate::codegen::cpp::tw_coin_type_generator::TWCoinTypeGenerator; +use crate::codegen::cpp::tw_coin_type_tests_generator::TWCoinTypeTestsGenerator; +use crate::registry::CoinItem; +use crate::Result; + +pub fn new_blockchain(coin: &CoinItem) -> Result<()> { + // Generate C++ files. + EntryGenerator::generate(coin)?; + + // Add the new coin type to the `TWCoinType` enum. + TWCoinTypeGenerator::generate_coin_type_variant(coin)?; + // Add the new blockchain type to the `TWBlockchain` enum. + TWBlockchainGenerator::generate_blockchain_type_variant(coin)?; + // Add the blockchain entry to the dispatcher `Coin.cpp`. + BlockchainDispatcherGenerator::generate_new_blockchain_type_dispatching(coin)?; + + // Add integration tests. + TWCoinTypeTestsGenerator::generate(coin)?; + TWAnyAddressTestsGenerator::generate(coin)?; + TWAnySignerTestsGenerator::generate(coin)?; + CoinAddressDerivationTestsGenerator::generate_new_coin_type_case(coin)?; + + Ok(()) +} diff --git a/codegen-v2/src/codegen/cpp/new_evmchain.rs b/codegen-v2/src/codegen/cpp/new_evmchain.rs new file mode 100644 index 00000000000..36c29e127e2 --- /dev/null +++ b/codegen-v2/src/codegen/cpp/new_evmchain.rs @@ -0,0 +1,22 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::cpp::tw_coin_address_derivation_tests_generator::CoinAddressDerivationTestsGenerator; +use crate::codegen::cpp::tw_coin_type_generator::TWCoinTypeGenerator; +use crate::codegen::cpp::tw_coin_type_tests_generator::TWCoinTypeTestsGenerator; +use crate::registry::CoinItem; +use crate::Result; + +pub fn new_evmchain(coin: &CoinItem) -> Result<()> { + // Add the new coin type to the `TWCoinType` enum. + TWCoinTypeGenerator::generate_coin_type_variant(coin)?; + + // Add integration tests. + TWCoinTypeTestsGenerator::generate(coin)?; + CoinAddressDerivationTestsGenerator::generate_new_evm_coin_type_case(coin)?; + + Ok(()) +} diff --git a/codegen-v2/src/codegen/cpp/templates/Entry.h b/codegen-v2/src/codegen/cpp/templates/Entry.h new file mode 100644 index 00000000000..a07c98e471d --- /dev/null +++ b/codegen-v2/src/codegen/cpp/templates/Entry.h @@ -0,0 +1,19 @@ +// Copyright © 2017-{YEAR} Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "rust/RustCoinEntry.h" + +namespace TW::{BLOCKCHAIN} { + +/// Entry point for {BLOCKCHAIN} coin. +/// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file +class Entry : public Rust::RustCoinEntry { +}; + +} // namespace TW::{BLOCKCHAIN} + diff --git a/codegen-v2/src/codegen/cpp/templates/TWAnyAddressTests.cpp b/codegen-v2/src/codegen/cpp/templates/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..35516cf2597 --- /dev/null +++ b/codegen-v2/src/codegen/cpp/templates/TWAnyAddressTests.cpp @@ -0,0 +1,26 @@ +// Copyright © 2017-{YEAR} Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include "HexCoding.h" + +#include "TestUtilities.h" +#include + +using namespace TW; + +// TODO: Finalize tests + +TEST(TW{COIN_TYPE}, Address) { + // TODO: Finalize test implementation + + auto string = STRING("__ADD_VALID_ADDRESS_HERE__"); + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinType{COIN_TYPE})); + auto string2 = WRAPS(TWAnyAddressDescription(addr.get())); + EXPECT_TRUE(TWStringEqual(string.get(), string2.get())); + auto keyHash = WRAPD(TWAnyAddressData(addr.get())); + assertHexEqual(keyHash, "__CORRESPONDING_ADDRESS_DATA__"); +} diff --git a/codegen-v2/src/codegen/cpp/templates/TWAnySignerTests.cpp b/codegen-v2/src/codegen/cpp/templates/TWAnySignerTests.cpp new file mode 100644 index 00000000000..38f1493b916 --- /dev/null +++ b/codegen-v2/src/codegen/cpp/templates/TWAnySignerTests.cpp @@ -0,0 +1,19 @@ +// Copyright © 2017-{YEAR} Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include "HexCoding.h" + +#include "TestUtilities.h" +#include + +using namespace TW; + +// TODO: Finalize tests + +TEST(TWAnySigner{COIN_TYPE}, Sign) { + // TODO: Finalize test implementation +} diff --git a/codegen-v2/src/codegen/cpp/templates/TWCoinTypeTests.cpp b/codegen-v2/src/codegen/cpp/templates/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..83124519c83 --- /dev/null +++ b/codegen-v2/src/codegen/cpp/templates/TWCoinTypeTests.cpp @@ -0,0 +1,31 @@ +// Copyright © 2017-{YEAR} Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" +#include +#include + +TEST(TW{COIN_TYPE}CoinType, TWCoinType) { + const auto coin = TWCoinType{COIN_TYPE}; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("{EXPLORER_SAMPLE_TX}")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("{EXPLORER_SAMPLE_ACCOUNT}")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "{COIN_ID}"); + assertStringsEqual(name, "{COIN_TYPE}"); + assertStringsEqual(symbol, "{SYMBOL}"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), {DECIMALS}); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchain{BLOCKCHAIN}); + ASSERT_EQ(TWCoinTypeP2pkhPrefix(coin), {P2PKH_PREFIX}); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), {P2SH_PREFIX}); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), {STATIC_PREFIX}); + assertStringsEqual(txUrl, "{EXPLORER_URL}{EXPLORER_TX_PATH}{EXPLORER_SAMPLE_TX}"); + assertStringsEqual(accUrl, "{EXPLORER_URL}{EXPLORER_ACCOUNT_PATH}{EXPLORER_SAMPLE_ACCOUNT}"); +} diff --git a/codegen-v2/src/codegen/cpp/tw_any_address_tests_generator.rs b/codegen-v2/src/codegen/cpp/tw_any_address_tests_generator.rs new file mode 100644 index 00000000000..f7d7384a0d8 --- /dev/null +++ b/codegen-v2/src/codegen/cpp/tw_any_address_tests_generator.rs @@ -0,0 +1,39 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::cpp::coin_integration_tests_directory; +use crate::codegen::template_generator::TemplateGenerator; +use crate::registry::CoinItem; +use crate::Result; +use std::fs; +use std::path::PathBuf; + +const TW_ANY_ADDRESS_TESTS_TEMPLATE: &str = include_str!("templates/TWAnyAddressTests.cpp"); + +pub fn tw_any_address_tests_path(coin: &CoinItem) -> PathBuf { + coin_integration_tests_directory(coin).join("TWAnyAddressTests.cpp") +} + +pub struct TWAnyAddressTestsGenerator; + +impl TWAnyAddressTestsGenerator { + pub fn generate(coin: &CoinItem) -> Result<()> { + let coin_tests_dir = coin_integration_tests_directory(coin); + let tw_any_address_tests_path = coin_tests_dir.join("TWAnyAddressTests.cpp"); + + fs::create_dir_all(coin_tests_dir)?; + if tw_any_address_tests_path.exists() { + return Ok(()); + } + + TemplateGenerator::new(TW_ANY_ADDRESS_TESTS_TEMPLATE) + .write_to(tw_any_address_tests_path) + .with_default_patterns(coin) + .write()?; + + Ok(()) + } +} diff --git a/codegen-v2/src/codegen/cpp/tw_any_signer_tests_generator.rs b/codegen-v2/src/codegen/cpp/tw_any_signer_tests_generator.rs new file mode 100644 index 00000000000..32391ffe618 --- /dev/null +++ b/codegen-v2/src/codegen/cpp/tw_any_signer_tests_generator.rs @@ -0,0 +1,39 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::cpp::coin_integration_tests_directory; +use crate::codegen::template_generator::TemplateGenerator; +use crate::registry::CoinItem; +use crate::Result; +use std::fs; +use std::path::PathBuf; + +const TW_ANY_SIGNER_TESTS_TEMPLATE: &str = include_str!("templates/TWAnySignerTests.cpp"); + +pub fn tw_any_signer_tests_path(coin: &CoinItem) -> PathBuf { + coin_integration_tests_directory(coin).join("TWAnySignerTests.cpp") +} + +pub struct TWAnySignerTestsGenerator; + +impl TWAnySignerTestsGenerator { + pub fn generate(coin: &CoinItem) -> Result<()> { + let coin_tests_dir = coin_integration_tests_directory(coin); + let tw_any_signer_tests_path = coin_tests_dir.join("TWAnySignerTests.cpp"); + + fs::create_dir_all(coin_tests_dir)?; + if tw_any_signer_tests_path.exists() { + return Ok(()); + } + + TemplateGenerator::new(TW_ANY_SIGNER_TESTS_TEMPLATE) + .write_to(tw_any_signer_tests_path) + .with_default_patterns(coin) + .write()?; + + Ok(()) + } +} diff --git a/codegen-v2/src/codegen/cpp/tw_blockchain.rs b/codegen-v2/src/codegen/cpp/tw_blockchain.rs new file mode 100644 index 00000000000..dee2986d328 --- /dev/null +++ b/codegen-v2/src/codegen/cpp/tw_blockchain.rs @@ -0,0 +1,41 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::cpp::cpp_include_directory; +use crate::registry::CoinItem; +use crate::utils::FileContent; +use crate::Result; +use std::path::PathBuf; + +/// An offset because of removed blockchain enum types. +const BLOCKCHAIN_TYPE_OFFSET: usize = 2; + +pub fn tw_blockchain_path() -> PathBuf { + cpp_include_directory().join("TWBlockchain.h") +} + +/// Represents `TWBlockchain.h`. +pub struct TWBlockchainGenerator; + +impl TWBlockchainGenerator { + pub fn generate_blockchain_type_variant(coin: &CoinItem) -> Result<()> { + let coin_type = coin.blockchain_type(); + + let mut tw_blockchain_type_rs = FileContent::read(tw_blockchain_path())?; + + { + let mut enum_region = + tw_blockchain_type_rs.find_region_with_prefix(" TWBlockchain")?; + // Add an offset because of removed blockchain enum types. + let new_blockchain_id = enum_region.count_lines() + BLOCKCHAIN_TYPE_OFFSET; + enum_region.push_line(format!( + " TWBlockchain{coin_type} = {new_blockchain_id}," + )); + } + + tw_blockchain_type_rs.write() + } +} diff --git a/codegen-v2/src/codegen/cpp/tw_coin_address_derivation_tests_generator.rs b/codegen-v2/src/codegen/cpp/tw_coin_address_derivation_tests_generator.rs new file mode 100644 index 00000000000..772fea23676 --- /dev/null +++ b/codegen-v2/src/codegen/cpp/tw_coin_address_derivation_tests_generator.rs @@ -0,0 +1,65 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::cpp::integration_tests_directory; +use crate::registry::CoinItem; +use crate::utils::FileContent; +use crate::Result; +use std::path::PathBuf; + +const COIN_ADDRESS_DERIVATION_TESTS_END: &str = + "end_of_coin_address_derivation_tests_marker_do_not_modify"; +const EVM_ADDRESS_DERIVATION_TESTS_END: &str = + "end_of_evm_address_derivation_tests_marker_do_not_modify"; + +pub fn coin_address_derivation_tests_path() -> PathBuf { + integration_tests_directory() + .join("common") + .join("CoinAddressDerivationTests.cpp") +} + +/// Represents `CoinAddressDerivationTests.cpp`. +pub struct CoinAddressDerivationTestsGenerator; + +impl CoinAddressDerivationTestsGenerator { + pub fn generate_new_coin_type_case(coin: &CoinItem) -> Result<()> { + let coin_type = coin.coin_type(); + + let mut coin_address_derivation_test_rs = + FileContent::read(coin_address_derivation_tests_path())?; + + { + let mut switch_case_region = coin_address_derivation_test_rs + .rfind_line(|line| line.contains(COIN_ADDRESS_DERIVATION_TESTS_END))?; + + #[rustfmt::skip] + let test_case = format!( +r#" case TWCoinType{coin_type}: + EXPECT_EQ(address, "__TODO__"); + break;"# +); + + switch_case_region.push_paragraph_before(test_case); + } + + coin_address_derivation_test_rs.write() + } + + pub fn generate_new_evm_coin_type_case(coin: &CoinItem) -> Result<()> { + let coin_type = coin.coin_type(); + + let mut evm_address_derivation_test_rs = + FileContent::read(coin_address_derivation_tests_path())?; + + { + let mut switch_case_region = evm_address_derivation_test_rs + .rfind_line(|line| line.contains(EVM_ADDRESS_DERIVATION_TESTS_END))?; + switch_case_region.push_line_before(format!(" case TWCoinType{coin_type}:")); + } + + evm_address_derivation_test_rs.write() + } +} diff --git a/codegen-v2/src/codegen/cpp/tw_coin_type_generator.rs b/codegen-v2/src/codegen/cpp/tw_coin_type_generator.rs new file mode 100644 index 00000000000..385f69076cd --- /dev/null +++ b/codegen-v2/src/codegen/cpp/tw_coin_type_generator.rs @@ -0,0 +1,37 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::cpp::cpp_include_directory; +use crate::registry::CoinItem; +use crate::utils::FileContent; +use crate::Result; +use std::path::PathBuf; + +const TW_COIN_TYPE_END: &str = "end_of_tw_coin_type_marker_do_not_modify"; + +pub fn tw_coin_type_path() -> PathBuf { + cpp_include_directory().join("TWCoinType.h") +} + +/// Represents `TWCoinType.h`. +pub struct TWCoinTypeGenerator; + +impl TWCoinTypeGenerator { + pub fn generate_coin_type_variant(coin: &CoinItem) -> Result<()> { + let coin_type = coin.coin_type(); + let coin_id_number = coin.coin_id_number; + + let mut tw_coin_type_rs = FileContent::read(tw_coin_type_path())?; + + { + let mut enum_region = + tw_coin_type_rs.rfind_line(|line| line.contains(TW_COIN_TYPE_END))?; + enum_region.push_line_before(format!(" TWCoinType{coin_type} = {coin_id_number},")); + } + + tw_coin_type_rs.write() + } +} diff --git a/codegen-v2/src/codegen/cpp/tw_coin_type_tests_generator.rs b/codegen-v2/src/codegen/cpp/tw_coin_type_tests_generator.rs new file mode 100644 index 00000000000..2790c302138 --- /dev/null +++ b/codegen-v2/src/codegen/cpp/tw_coin_type_tests_generator.rs @@ -0,0 +1,39 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::cpp::coin_integration_tests_directory; +use crate::codegen::template_generator::TemplateGenerator; +use crate::registry::CoinItem; +use crate::Result; +use std::fs; +use std::path::PathBuf; + +const TW_COIN_TYPE_TESTS_TEMPLATE: &str = include_str!("templates/TWCoinTypeTests.cpp"); + +pub fn tw_coin_type_tests_path(coin: &CoinItem) -> PathBuf { + coin_integration_tests_directory(coin).join("TWCoinTypeTests.cpp") +} + +pub struct TWCoinTypeTestsGenerator; + +impl TWCoinTypeTestsGenerator { + pub fn generate(coin: &CoinItem) -> Result<()> { + let coin_tests_dir = coin_integration_tests_directory(coin); + let tw_coin_type_tests_path = coin_tests_dir.join("TWCoinTypeTests.cpp"); + + fs::create_dir(coin_tests_dir)?; + if tw_coin_type_tests_path.exists() { + return Ok(()); + } + + TemplateGenerator::new(TW_COIN_TYPE_TESTS_TEMPLATE) + .write_to(tw_coin_type_tests_path) + .with_default_patterns(coin) + .write()?; + + Ok(()) + } +} diff --git a/codegen-v2/src/codegen/mod.rs b/codegen-v2/src/codegen/mod.rs index b0d31c99e15..2f1b1a70839 100644 --- a/codegen-v2/src/codegen/mod.rs +++ b/codegen-v2/src/codegen/mod.rs @@ -4,5 +4,8 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +pub mod cpp; +pub mod proto; pub mod rust; pub mod swift; +pub mod template_generator; diff --git a/codegen-v2/src/codegen/proto/mod.rs b/codegen-v2/src/codegen/proto/mod.rs new file mode 100644 index 00000000000..e760e33c8ea --- /dev/null +++ b/codegen-v2/src/codegen/proto/mod.rs @@ -0,0 +1,18 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::env; +use std::path::PathBuf; + +pub mod new_blockchain; +pub mod proto_generator; + +pub fn proto_source_directory() -> PathBuf { + PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()) + .join("..") + .join("src") + .join("proto") +} diff --git a/codegen-v2/src/codegen/proto/new_blockchain.rs b/codegen-v2/src/codegen/proto/new_blockchain.rs new file mode 100644 index 00000000000..805e342f124 --- /dev/null +++ b/codegen-v2/src/codegen/proto/new_blockchain.rs @@ -0,0 +1,13 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::proto::proto_generator::ProtoGenerator; +use crate::registry::CoinItem; +use crate::Result; + +pub fn new_blockchain(coin: &CoinItem) -> Result<()> { + ProtoGenerator::generate(coin) +} diff --git a/codegen-v2/src/codegen/proto/proto_generator.rs b/codegen-v2/src/codegen/proto/proto_generator.rs new file mode 100644 index 00000000000..3b08ef8b16c --- /dev/null +++ b/codegen-v2/src/codegen/proto/proto_generator.rs @@ -0,0 +1,37 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::proto::proto_source_directory; +use crate::codegen::template_generator::TemplateGenerator; +use crate::registry::CoinItem; +use crate::Result; +use std::path::PathBuf; + +const PROTO_TEMPLATE: &str = include_str!("templates/Blockchain.proto"); + +pub fn blockchain_proto_path(coin: &CoinItem) -> PathBuf { + let blockchain_type = coin.blockchain_type(); + proto_source_directory().join(format!("{blockchain_type}.proto")) +} + +pub struct ProtoGenerator; + +impl ProtoGenerator { + pub fn generate(coin: &CoinItem) -> Result<()> { + let proto_path = blockchain_proto_path(coin); + + if proto_path.exists() { + return Ok(()); + } + + TemplateGenerator::new(PROTO_TEMPLATE) + .write_to(proto_path) + .with_default_patterns(coin) + .write()?; + + Ok(()) + } +} diff --git a/codegen-v2/src/codegen/proto/templates/Blockchain.proto b/codegen-v2/src/codegen/proto/templates/Blockchain.proto new file mode 100644 index 00000000000..c78772ce0a5 --- /dev/null +++ b/codegen-v2/src/codegen/proto/templates/Blockchain.proto @@ -0,0 +1,39 @@ +// Copyright © 2017-{YEAR} Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +syntax = "proto3"; + +package TW.{BLOCKCHAIN}.Proto; +option java_package = "wallet.core.jni.proto"; + +import "Common.proto"; + +// TODO: typical balance transfer, add more fields needed to sign +message TransferMessage { + int64 amount = 1; + int64 fee = 2; + string to = 3; +} + +// TODO: Input data necessary to create a signed transaction. +message SigningInput { + bytes private_key = 1; + + oneof message_oneof { + TransferMessage transfer = 2; + } +} + +// Transaction signing output. +message SigningOutput { + // Signed and encoded transaction bytes. + bytes encoded = 1; + + // A possible error, `OK` if none. + Common.Proto.SigningError error = 2; + + string error_message = 3; +} diff --git a/codegen-v2/src/codegen/rust/blockchain_dispatcher_generator.rs b/codegen-v2/src/codegen/rust/blockchain_dispatcher_generator.rs new file mode 100644 index 00000000000..4c00268a7ca --- /dev/null +++ b/codegen-v2/src/codegen/rust/blockchain_dispatcher_generator.rs @@ -0,0 +1,80 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::rust::coin_registry_directory; +use crate::registry::CoinItem; +use crate::utils::FileContent; +use crate::Result; +use std::path::PathBuf; + +const BLOCKCHAIN_ENTRIES_START: &str = "start_of_blockchain_entries"; +const BLOCKCHAIN_ENTRIES_END: &str = "end_of_blockchain_entries"; +const BLOCKCHAIN_DISPATCHER_START: &str = "start_of_blockchain_dispatcher"; +const BLOCKCHAIN_DISPATCHER_END: &str = "end_of_blockchain_dispatcher"; + +pub fn dispatcher_path() -> PathBuf { + coin_registry_directory().join("src").join("dispatcher.rs") +} + +pub struct BlockchainDispatcherGenerator; + +impl BlockchainDispatcherGenerator { + pub fn generate_new_blockchain_type_dispatching(coin: &CoinItem) -> Result<()> { + let dispatcher_rs_path = dispatcher_path(); + let mut dispatcher_rs = FileContent::read(dispatcher_rs_path)?; + + Self::generate_use_of_blockchain_entry(coin, &mut dispatcher_rs)?; + Self::generate_blockchain_entry_constant(coin, &mut dispatcher_rs)?; + Self::generate_blockchain_dispatch(coin, &mut dispatcher_rs)?; + + dispatcher_rs.write() + } + + fn generate_use_of_blockchain_entry( + coin: &CoinItem, + file_content: &mut FileContent, + ) -> Result<()> { + let import_pattern = "use "; + let blockchain_entry = coin.blockchain_entry(); + let tw_crate_name = coin.id.to_tw_crate_name(); + + let mut last_entry = file_content.rfind_line(|line| line.contains(import_pattern))?; + last_entry.push_line_after(format!("use {tw_crate_name}::entry::{blockchain_entry};")); + + Ok(()) + } + + fn generate_blockchain_entry_constant( + coin: &CoinItem, + file_content: &mut FileContent, + ) -> Result<()> { + let blockchain_entry = coin.blockchain_entry(); + let blockchain_entry_const = coin.blockchain_entry_upper_snake(); + + let mut entries_region = file_content + .find_region_with_comments(BLOCKCHAIN_ENTRIES_START, BLOCKCHAIN_ENTRIES_END)?; + entries_region.push_line(format!( + "const {blockchain_entry_const}: {blockchain_entry} = {blockchain_entry};" + )); + entries_region.sort(); + + Ok(()) + } + + fn generate_blockchain_dispatch(coin: &CoinItem, file_content: &mut FileContent) -> Result<()> { + let blockchain_type = coin.blockchain_type(); + let blockchain_entry_const = coin.blockchain_entry_upper_snake(); + + let mut dispatcher_region = file_content + .find_region_with_comments(BLOCKCHAIN_DISPATCHER_START, BLOCKCHAIN_DISPATCHER_END)?; + dispatcher_region.push_line(format!( + " BlockchainType::{blockchain_type} => Ok(&{blockchain_entry_const})," + )); + dispatcher_region.sort(); + + Ok(()) + } +} diff --git a/codegen-v2/src/codegen/rust/blockchain_type.rs b/codegen-v2/src/codegen/rust/blockchain_type.rs deleted file mode 100644 index debce4802d6..00000000000 --- a/codegen-v2/src/codegen/rust/blockchain_type.rs +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -use crate::codegen::rust::toml_editor::Dependencies; -use crate::codegen::rust::{rust_source_directory, CoinItem}; -use crate::utils::FileContent; -use crate::Result; -use std::path::{Path, PathBuf}; - -const BLOCKCHAIN_TYPE_START: &str = "start_of_blockchain_type"; -const BLOCKCHAIN_TYPE_END: &str = "end_of_blockchain_type"; -const BLOCKCHAIN_ENTRIES_START: &str = "start_of_blockchain_entries"; -const BLOCKCHAIN_ENTRIES_END: &str = "end_of_blockchain_entries"; -const BLOCKCHAIN_DISPATCHER_START: &str = "start_of_blockchain_dispatcher"; -const BLOCKCHAIN_DISPATCHER_END: &str = "end_of_blockchain_dispatcher"; - -pub fn coin_registry_directory() -> PathBuf { - rust_source_directory().join("tw_coin_registry") -} - -pub fn blockchain_type_path() -> PathBuf { - coin_registry_directory() - .join("src") - .join("blockchain_type.rs") -} - -pub fn dispatcher_path() -> PathBuf { - coin_registry_directory().join("src").join("dispatcher.rs") -} - -pub struct CoinRegistry { - coin: CoinItem, -} - -impl CoinRegistry { - pub fn new(coin: CoinItem) -> CoinRegistry { - CoinRegistry { coin } - } - - pub fn add(self, path_to_new_blockchain_crate: &Path) -> Result<()> { - self.add_blockchain_crate_to_manifest_file(path_to_new_blockchain_crate)?; - self.add_blockchain_variant()?; - self.add_use_of_blockchain_entry()?; - self.add_blockchain_entry()?; - self.add_blockchain_dispatcher() - } - - fn add_blockchain_crate_to_manifest_file( - &self, - path_to_new_blockchain_crate: &Path, - ) -> Result<()> { - let path_to_cargo_manifest = coin_registry_directory().join("Cargo.toml"); - Dependencies::new(path_to_cargo_manifest).insert_dependency( - &self.coin.id.to_tw_crate_name(), - path_to_new_blockchain_crate, - ) - } - - fn add_blockchain_variant(&self) -> Result<()> { - let blockchain_type_rs_path = blockchain_type_path(); - let blockchain_type = self.coin.blockchain_type(); - - let mut blockchain_type_rs = FileContent::read(blockchain_type_rs_path)?; - - { - let mut enum_region = blockchain_type_rs - .find_region_with_comments(BLOCKCHAIN_TYPE_START, BLOCKCHAIN_TYPE_END)?; - enum_region.push_line(format!(" {blockchain_type},")); - enum_region.sort(); - } - - blockchain_type_rs.write() - } - - fn add_use_of_blockchain_entry(&self) -> Result<()> { - let dispatcher_rs_path = dispatcher_path(); - let blockchain_entry = self.coin.blockchain_entry(); - let tw_crate_name = self.coin.id.to_tw_crate_name(); - - let mut dispatcher_rs = FileContent::read(dispatcher_rs_path)?; - - { - let import_pattern = "use "; - let mut last_entry = dispatcher_rs.rfind_line(|line| line.contains(import_pattern))?; - last_entry.push_line_after(format!("use {tw_crate_name}::entry::{blockchain_entry};")); - } - - dispatcher_rs.write() - } - - fn add_blockchain_entry(&self) -> Result<()> { - let dispatcher_rs_path = dispatcher_path(); - let blockchain_entry = self.coin.blockchain_entry(); - let blockchain_entry_const = self.coin.blockchain_entry_upper_snake(); - - let mut dispatcher_rs = FileContent::read(dispatcher_rs_path)?; - - { - let mut entries_region = dispatcher_rs - .find_region_with_comments(BLOCKCHAIN_ENTRIES_START, BLOCKCHAIN_ENTRIES_END)?; - entries_region.push_line(format!( - "const {blockchain_entry_const}: {blockchain_entry} = {blockchain_entry};" - )); - entries_region.sort(); - } - - dispatcher_rs.write() - } - - fn add_blockchain_dispatcher(&self) -> Result<()> { - let dispatcher_rs_path = dispatcher_path(); - let blockchain_type = self.coin.blockchain_type(); - let blockchain_entry_const = self.coin.blockchain_entry_upper_snake(); - - let mut dispatcher_rs = FileContent::read(dispatcher_rs_path)?; - - { - let mut dispatcher_region = dispatcher_rs.find_region_with_comments( - BLOCKCHAIN_DISPATCHER_START, - BLOCKCHAIN_DISPATCHER_END, - )?; - dispatcher_region.push_line(format!( - " BlockchainType::{blockchain_type} => Ok(&{blockchain_entry_const})," - )); - dispatcher_region.sort(); - } - - dispatcher_rs.write() - } -} diff --git a/codegen-v2/src/codegen/rust/blockchain_type_generator.rs b/codegen-v2/src/codegen/rust/blockchain_type_generator.rs new file mode 100644 index 00000000000..f82ce5144fa --- /dev/null +++ b/codegen-v2/src/codegen/rust/blockchain_type_generator.rs @@ -0,0 +1,41 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::rust::coin_registry_directory; +use crate::registry::CoinItem; +use crate::utils::FileContent; +use crate::Result; +use std::path::PathBuf; + +const BLOCKCHAIN_TYPE_START: &str = "start_of_blockchain_type"; +const BLOCKCHAIN_TYPE_END: &str = "end_of_blockchain_type"; + +pub fn blockchain_type_path() -> PathBuf { + coin_registry_directory() + .join("src") + .join("blockchain_type.rs") +} + +/// Represents `BlockchainType` enum generator. +pub struct BlockchainTypeGenerator; + +impl BlockchainTypeGenerator { + pub fn add_new_blockchain_type(coin: &CoinItem) -> Result<()> { + let blockchain_type_rs_path = blockchain_type_path(); + let blockchain_type = coin.blockchain_type(); + + let mut blockchain_type_rs = FileContent::read(blockchain_type_rs_path)?; + + { + let mut enum_region = blockchain_type_rs + .find_region_with_comments(BLOCKCHAIN_TYPE_START, BLOCKCHAIN_TYPE_END)?; + enum_region.push_line(format!(" {blockchain_type},")); + enum_region.sort(); + } + + blockchain_type_rs.write() + } +} diff --git a/codegen-v2/src/codegen/rust/coin_address_derivation_test_generator.rs b/codegen-v2/src/codegen/rust/coin_address_derivation_test_generator.rs new file mode 100644 index 00000000000..25b2b9933b4 --- /dev/null +++ b/codegen-v2/src/codegen/rust/coin_address_derivation_test_generator.rs @@ -0,0 +1,56 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::rust::tw_any_coin_directory; +use crate::registry::CoinItem; +use crate::utils::FileContent; +use crate::Result; +use std::path::PathBuf; + +const COIN_ADDRESS_DERIVATION_TEST_END: &str = + "end_of_coin_address_derivation_tests_marker_do_not_modify"; +const EVM_ADDRESS_DERIVATION_TEST_END: &str = + "end_of_evm_address_derivation_tests_marker_do_not_modify"; + +pub fn coin_address_derivation_test_path() -> PathBuf { + tw_any_coin_directory() + .join("tests") + .join("coin_address_derivation_test.rs") +} + +pub struct CoinAddressDerivationTestGenerator; + +impl CoinAddressDerivationTestGenerator { + pub fn generate_new_coin_type_case(coin: &CoinItem) -> Result<()> { + let coin_type = coin.coin_type(); + + let mut coin_address_derivation_test_rs = + FileContent::read(coin_address_derivation_test_path())?; + + { + let mut end_of_test = coin_address_derivation_test_rs + .rfind_line(|line| line.contains(COIN_ADDRESS_DERIVATION_TEST_END))?; + end_of_test.push_line_before(format!(" CoinType::{coin_type} => todo!(),")); + } + + coin_address_derivation_test_rs.write() + } + + pub fn generate_new_evm_coin_type_case(coin: &CoinItem) -> Result<()> { + let coin_type = coin.coin_type(); + + let mut coin_address_derivation_test_rs = + FileContent::read(coin_address_derivation_test_path())?; + + { + let mut end_of_test = coin_address_derivation_test_rs + .rfind_line(|line| line.contains(EVM_ADDRESS_DERIVATION_TEST_END))?; + end_of_test.push_line_before(format!(" | CoinType::{coin_type}")); + } + + coin_address_derivation_test_rs.write() + } +} diff --git a/codegen-v2/src/codegen/rust/coin_crate.rs b/codegen-v2/src/codegen/rust/coin_crate.rs index 19a957d9978..8b6ef18b3c1 100644 --- a/codegen-v2/src/codegen/rust/coin_crate.rs +++ b/codegen-v2/src/codegen/rust/coin_crate.rs @@ -4,12 +4,21 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use crate::codegen::rust::coin_id::CoinId; -use crate::codegen::rust::{chains_directory, rs_header, CoinItem}; +use crate::codegen::rust::chains_directory; +use crate::codegen::template_generator::TemplateGenerator; +use crate::coin_id::CoinId; +use crate::registry::CoinItem; use crate::{Error, Result}; use std::path::PathBuf; use std::{fs, io}; +const BLOCKCHAIN_ADDRESS_TEMPLATE: &str = include_str!("templates/blockchain_crate/address.rs"); +const BLOCKCHAIN_COMPILER_TEMPLATE: &str = include_str!("templates/blockchain_crate/compiler.rs"); +const BLOCKCHAIN_ENTRY_TEMPLATE: &str = include_str!("templates/blockchain_crate/entry.rs"); +const BLOCKCHAIN_MANIFEST_TEMPLATE: &str = include_str!("templates/blockchain_crate/Cargo.toml"); +const BLOCKCHAIN_LIB_TEMPLATE: &str = include_str!("templates/blockchain_crate/lib.rs"); +const BLOCKCHAIN_SIGNER_TEMPLATE: &str = include_str!("templates/blockchain_crate/signer.rs"); + pub fn coin_source_directory(id: &CoinId) -> PathBuf { chains_directory().join(id.to_tw_crate_name()) } @@ -26,17 +35,15 @@ impl CoinCrate { /// Creates a Cargo crate with `entry.rs` file. /// Returns the path to the create crate. pub fn create(self) -> Result { - let header = rs_header(); - let blockchain_path = coin_source_directory(&self.coin.id); let blockchain_toml_path = blockchain_path.join("Cargo.toml"); let blockchain_src_path = blockchain_path.join("src"); let blockchain_lib_rs_path = blockchain_src_path.join("lib.rs"); let blockchain_entry_path = blockchain_src_path.join("entry.rs"); - - let tw_crate_name = self.coin.id.to_tw_crate_name(); - let blockchain_name = self.coin.blockchain_type(); + let blockchain_compiler_path = blockchain_src_path.join("compiler.rs"); + let blockchain_address_rs_path = blockchain_src_path.join("address.rs"); + let blockchain_signer_rs_path = blockchain_src_path.join("signer.rs"); if blockchain_path.exists() { return Err(Error::IoError(io::Error::new( @@ -48,40 +55,35 @@ impl CoinCrate { fs::create_dir(&blockchain_path)?; fs::create_dir(&blockchain_src_path)?; - let blockchain_toml = format!( - r#"[package] -name = "{tw_crate_name}" -version = "0.1.0" -edition = "2021" - -[dependencies] -tw_coin_entry = {{ path = "../../tw_coin_entry" }} -tw_proto = {{ path = "../../tw_proto" }} -"# - ); - fs::write(blockchain_toml_path, blockchain_toml)?; - - let blockchain_lib_rs = format!( - r#"{header} - -pub mod entry; -"# - ); - fs::write(blockchain_lib_rs_path, blockchain_lib_rs)?; - - let blockchain_entry = format!( - r#"{header} - -use tw_coin_entry::coin_entry::CoinEntry; - -pub struct {blockchain_name}Entry; - -impl CoinEntry for {blockchain_name}Entry {{ - // TODO declare associated types and implement methods from the 'CoinEntry' trait. -}} -"# - ); - fs::write(blockchain_entry_path, blockchain_entry)?; + TemplateGenerator::new(BLOCKCHAIN_MANIFEST_TEMPLATE) + .write_to(blockchain_toml_path) + .with_default_patterns(&self.coin) + .write()?; + + TemplateGenerator::new(BLOCKCHAIN_LIB_TEMPLATE) + .write_to(blockchain_lib_rs_path) + .with_default_patterns(&self.coin) + .write()?; + + TemplateGenerator::new(BLOCKCHAIN_ENTRY_TEMPLATE) + .write_to(blockchain_entry_path) + .with_default_patterns(&self.coin) + .write()?; + + TemplateGenerator::new(BLOCKCHAIN_COMPILER_TEMPLATE) + .write_to(blockchain_compiler_path) + .with_default_patterns(&self.coin) + .write()?; + + TemplateGenerator::new(BLOCKCHAIN_ADDRESS_TEMPLATE) + .write_to(blockchain_address_rs_path) + .with_default_patterns(&self.coin) + .write()?; + + TemplateGenerator::new(BLOCKCHAIN_SIGNER_TEMPLATE) + .write_to(blockchain_signer_rs_path) + .with_default_patterns(&self.coin) + .write()?; Ok(blockchain_path) } diff --git a/codegen-v2/src/codegen/rust/coin_integration_tests.rs b/codegen-v2/src/codegen/rust/coin_integration_tests.rs index 3fde3ff1795..18c5356f064 100644 --- a/codegen-v2/src/codegen/rust/coin_integration_tests.rs +++ b/codegen-v2/src/codegen/rust/coin_integration_tests.rs @@ -4,13 +4,20 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use crate::codegen::rust::coin_id::CoinId; -use crate::codegen::rust::{rs_header, tw_any_coin_directory, CoinItem}; +use crate::codegen::rust::tw_any_coin_directory; +use crate::codegen::template_generator::TemplateGenerator; +use crate::coin_id::CoinId; +use crate::registry::CoinItem; use crate::utils::FileContent; -use crate::{Error, Result}; +use crate::Result; use std::fs; use std::path::PathBuf; +const ADDRESS_TESTS_TEMPLATE: &str = include_str!("templates/integration_tests/address_tests.rs"); +const COMPILE_TESTS_TEMPLATE: &str = include_str!("templates/integration_tests/compile_tests.rs"); +const MOD_TESTS_TEMPLATE: &str = include_str!("templates/integration_tests/mod.rs"); +const SIGN_TESTS_TEMPLATE: &str = include_str!("templates/integration_tests/sign_tests.rs"); + pub fn chains_integration_tests_directory() -> PathBuf { tw_any_coin_directory().join("tests").join("chains") } @@ -19,6 +26,12 @@ pub fn coin_integration_tests_directory(id: &CoinId) -> PathBuf { chains_integration_tests_directory().join(id.as_str()) } +pub fn coin_address_derivation_test_path() -> PathBuf { + tw_any_coin_directory() + .join("tests") + .join("coin_address_derivation_test.rs") +} + pub struct CoinIntegrationTests { coin: CoinItem, } @@ -38,6 +51,7 @@ impl CoinIntegrationTests { self.list_blockchain_in_chains_mod()?; self.create_address_tests()?; + self.create_compile_tests()?; self.create_sign_tests()?; self.create_chain_tests_mod_rs()?; @@ -49,71 +63,48 @@ impl CoinIntegrationTests { } fn create_address_tests(&self) -> Result<()> { - let header = rs_header(); - let chain_id = self.coin.id.as_str(); + let coin_id = self.coin.id.as_str(); let address_tests_path = self .coin_tests_directory() - .join(format!("{chain_id}_address.rs")); - - let address_tests_rs = format!( - r#"{header} - -#[test] -fn test_{chain_id}_address_normalization() {{ - todo!() -}} - -#[test] -fn test_{chain_id}_address_is_valid() {{ - todo!() -}} - -#[test] -fn test_{chain_id}_address_invalid() {{ - todo!() -}} - -#[test] -fn test_{chain_id}_address_get_data() {{ - todo!() -}} -"# - ); - fs::write(address_tests_path, address_tests_rs).map_err(Error::from) + .join(format!("{coin_id}_address.rs")); + + TemplateGenerator::new(ADDRESS_TESTS_TEMPLATE) + .write_to(address_tests_path) + .with_default_patterns(&self.coin) + .write() + } + + fn create_compile_tests(&self) -> Result<()> { + let coin_id = self.coin.id.as_str(); + let compile_tests_path = self + .coin_tests_directory() + .join(format!("{coin_id}_compile.rs")); + + TemplateGenerator::new(COMPILE_TESTS_TEMPLATE) + .write_to(compile_tests_path) + .with_default_patterns(&self.coin) + .write() } fn create_sign_tests(&self) -> Result<()> { - let header = rs_header(); - let chain_id = self.coin.id.as_str(); + let coin_id = self.coin.id.as_str(); let sign_tests_path = self .coin_tests_directory() - .join(format!("{chain_id}_sign.rs")); - - let sign_tests_rs = format!( - r#"{header} - -#[test] -fn test_{chain_id}_sign() {{ - todo!() -}} -"# - ); - fs::write(sign_tests_path, sign_tests_rs).map_err(Error::from) + .join(format!("{coin_id}_sign.rs")); + + TemplateGenerator::new(SIGN_TESTS_TEMPLATE) + .write_to(sign_tests_path) + .with_default_patterns(&self.coin) + .write() } fn create_chain_tests_mod_rs(&self) -> Result<()> { - let header = rs_header(); - let chain_id = self.coin.id.as_str(); let blockchain_tests_mod_path = self.coin_tests_directory().join("mod.rs"); - let blockchain_mod_rs = format!( - r#"{header} - -mod {chain_id}_address; -mod {chain_id}_sign; -"# - ); - fs::write(blockchain_tests_mod_path, blockchain_mod_rs).map_err(Error::from) + TemplateGenerator::new(MOD_TESTS_TEMPLATE) + .write_to(blockchain_tests_mod_path) + .with_default_patterns(&self.coin) + .write() } fn list_blockchain_in_chains_mod(&self) -> Result<()> { @@ -124,8 +115,9 @@ mod {chain_id}_sign; { let mod_pattern = "mod "; - let mut last_mod = chains_mod_rs.rfind_line(|line| line.starts_with(mod_pattern))?; - last_mod.push_line_after(format!("mod {chain_id};")); + let mut mod_region = chains_mod_rs.find_region_with_prefix(mod_pattern)?; + mod_region.push_line(format!("mod {chain_id};")); + mod_region.sort(); } chains_mod_rs.write() diff --git a/codegen-v2/src/codegen/rust/coin_registry_manifest_generator.rs b/codegen-v2/src/codegen/rust/coin_registry_manifest_generator.rs new file mode 100644 index 00000000000..6b0ff03b2f2 --- /dev/null +++ b/codegen-v2/src/codegen/rust/coin_registry_manifest_generator.rs @@ -0,0 +1,21 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::rust::coin_registry_directory; +use crate::codegen::rust::toml_editor::Dependencies; +use crate::registry::CoinItem; +use crate::Result; +use std::path::Path; + +pub struct CoinRegistryManifestGenerator; + +impl CoinRegistryManifestGenerator { + pub fn add_dependency(coin: &CoinItem, path_to_new_blockchain_crate: &Path) -> Result<()> { + let path_to_cargo_manifest = coin_registry_directory().join("Cargo.toml"); + Dependencies::new(path_to_cargo_manifest) + .insert_dependency(&coin.id.to_tw_crate_name(), path_to_new_blockchain_crate) + } +} diff --git a/codegen-v2/src/codegen/rust/mod.rs b/codegen-v2/src/codegen/rust/mod.rs index b0d1a55910b..7edc77d5062 100644 --- a/codegen-v2/src/codegen/rust/mod.rs +++ b/codegen-v2/src/codegen/rust/mod.rs @@ -4,17 +4,17 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use crate::codegen::rust::coin_id::CoinId; -use crate::{current_year, Error, Result}; -use convert_case::{Case, Casing}; +use std::env; use std::path::PathBuf; -use std::{env, fs}; -pub mod blockchain_type; +pub mod blockchain_dispatcher_generator; +pub mod blockchain_type_generator; +pub mod coin_address_derivation_test_generator; pub mod coin_crate; -pub mod coin_id; pub mod coin_integration_tests; +pub mod coin_registry_manifest_generator; pub mod new_blockchain; +pub mod new_evmchain; pub mod toml_editor; pub fn rust_source_directory() -> PathBuf { @@ -35,62 +35,6 @@ pub fn workspace_toml_path() -> PathBuf { rust_source_directory().join("Cargo.toml") } -pub fn registry_json_path() -> PathBuf { - PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()) - .join("..") - .join("registry.json") -} - -pub fn rs_header() -> String { - let current_year = current_year(); - format!( - r#"// Copyright © 2017-{current_year} Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree."# - ) -} - -#[derive(Clone, Deserialize)] -pub struct CoinItem { - pub id: CoinId, - pub name: String, - pub blockchain: String, -} - -impl CoinItem { - /// Transforms a coin name to a Rust name. - /// https://github.com/trustwallet/wallet-core/blob/3769f31b7d0c75126b2f426bb065364429aaa379/codegen/lib/coin_skeleton_gen.rb#L15-L22 - pub fn coin_type(&self) -> String { - self.name.replace([' ', '.', '-'], "") - } - - /// Returns the blockchain type in `UpperCamel` case. - pub fn blockchain_type(&self) -> String { - self.blockchain.to_case(Case::UpperCamel) - } - - /// Returns the blockchain type in `UPPER_SNAKE` case. - pub fn blockchain_entry_upper_snake(&self) -> String { - self.blockchain.to_case(Case::UpperSnake) - } - - /// Returns a Rust blockchain entry of the blockchain. - pub fn blockchain_entry(&self) -> String { - format!("{}Entry", self.blockchain_type()) - } -} - -pub(crate) fn read_coin_from_registry(coin: &CoinId) -> Result { - let registry_path = registry_json_path(); - - let registry_bytes = fs::read(registry_path)?; - let coins: Vec = - serde_json::from_slice(®istry_bytes).map_err(|e| Error::RegistryError(e.to_string()))?; - - coins - .into_iter() - .find(|item| item.id == *coin) - .ok_or(Error::InvalidCommand) +pub fn coin_registry_directory() -> PathBuf { + rust_source_directory().join("tw_coin_registry") } diff --git a/codegen-v2/src/codegen/rust/new_blockchain.rs b/codegen-v2/src/codegen/rust/new_blockchain.rs index 252bbcc92da..2f3a4996dd2 100644 --- a/codegen-v2/src/codegen/rust/new_blockchain.rs +++ b/codegen-v2/src/codegen/rust/new_blockchain.rs @@ -4,27 +4,32 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use crate::codegen::rust::blockchain_type::CoinRegistry; +use crate::codegen::rust::blockchain_dispatcher_generator::BlockchainDispatcherGenerator; +use crate::codegen::rust::blockchain_type_generator::BlockchainTypeGenerator; +use crate::codegen::rust::coin_address_derivation_test_generator::CoinAddressDerivationTestGenerator; use crate::codegen::rust::coin_crate::CoinCrate; -use crate::codegen::rust::coin_id::CoinId; use crate::codegen::rust::coin_integration_tests::CoinIntegrationTests; +use crate::codegen::rust::coin_registry_manifest_generator::CoinRegistryManifestGenerator; use crate::codegen::rust::toml_editor::Workspace; -use crate::codegen::rust::{read_coin_from_registry, workspace_toml_path}; +use crate::codegen::rust::workspace_toml_path; +use crate::registry::CoinItem; use crate::Result; -pub fn new_blockchain(coin: &str) -> Result<()> { - let coin_id = CoinId::new(coin.to_string())?; - let coin_item = read_coin_from_registry(&coin_id)?; - +pub fn new_blockchain(coin: &CoinItem) -> Result<()> { // Create blockchain's crate. - let blockchain_crate_path = CoinCrate::new(coin_item.clone()).create()?; + let blockchain_crate_path = CoinCrate::new(coin.clone()).create()?; // Insert the created crate to the workspace. Workspace::new(workspace_toml_path()).insert_crate(&blockchain_crate_path)?; + // Create integration tests. - CoinIntegrationTests::new(coin_item.clone()).create()?; + CoinIntegrationTests::new(coin.clone()).create()?; + CoinAddressDerivationTestGenerator::generate_new_coin_type_case(coin)?; + // Add the new blockchain to the `tw_coin_registry`. - CoinRegistry::new(coin_item).add(&blockchain_crate_path)?; + BlockchainTypeGenerator::add_new_blockchain_type(coin)?; + CoinRegistryManifestGenerator::add_dependency(coin, &blockchain_crate_path)?; + BlockchainDispatcherGenerator::generate_new_blockchain_type_dispatching(coin)?; Ok(()) } diff --git a/codegen-v2/src/codegen/rust/new_evmchain.rs b/codegen-v2/src/codegen/rust/new_evmchain.rs new file mode 100644 index 00000000000..2222306a4de --- /dev/null +++ b/codegen-v2/src/codegen/rust/new_evmchain.rs @@ -0,0 +1,14 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::codegen::rust::coin_address_derivation_test_generator::CoinAddressDerivationTestGenerator; +use crate::registry::CoinItem; +use crate::Result; + +pub fn new_evmchain(coin: &CoinItem) -> Result<()> { + // Modify integration tests. + CoinAddressDerivationTestGenerator::generate_new_evm_coin_type_case(coin) +} diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/Cargo.toml b/codegen-v2/src/codegen/rust/templates/blockchain_crate/Cargo.toml new file mode 100644 index 00000000000..9d5a7a7d04a --- /dev/null +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "{TW_CRATE_NAME}" +version = "0.1.0" +edition = "2021" + +[dependencies] +tw_coin_entry = { path = "../../tw_coin_entry" } +tw_keypair = { path = "../../tw_keypair" } +tw_memory = { path = "../../tw_memory" } +tw_proto = { path = "../../tw_proto" } diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/address.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/address.rs new file mode 100644 index 00000000000..96d862a63cd --- /dev/null +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/address.rs @@ -0,0 +1,36 @@ +// Copyright © 2017-{YEAR} Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use std::fmt; +use std::str::FromStr; +use tw_coin_entry::coin_entry::CoinAddress; +use tw_coin_entry::error::AddressError; +use tw_memory::Data; + +pub struct {BLOCKCHAIN}Address { + // TODO add necessary fields. +} + +impl CoinAddress for {BLOCKCHAIN}Address { + #[inline] + fn data(&self) -> Data { + todo!() + } +} + +impl FromStr for {BLOCKCHAIN}Address { + type Err = AddressError; + + fn from_str(_s: &str) -> Result { + todo!() + } +} + +impl fmt::Display for {BLOCKCHAIN}Address { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + todo!() + } +} diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/compiler.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/compiler.rs new file mode 100644 index 00000000000..2e1831cf5b6 --- /dev/null +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/compiler.rs @@ -0,0 +1,52 @@ +// Copyright © 2017-{YEAR} Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::error::SigningResult; +use tw_coin_entry::signing_output_error; +use tw_proto::{BLOCKCHAIN}::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct {BLOCKCHAIN}Compiler; + +impl {BLOCKCHAIN}Compiler { + #[inline] + pub fn preimage_hashes( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> CompilerProto::PreSigningOutput<'static> { + Self::preimage_hashes_impl(coin, input) + .unwrap_or_else(|e| signing_output_error!(CompilerProto::PreSigningOutput, e)) + } + + fn preimage_hashes_impl( + _coin: &dyn CoinContext, + _input: Proto::SigningInput<'_>, + ) -> SigningResult> { + todo!() + } + + #[inline] + pub fn compile( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Proto::SigningOutput<'static> { + Self::compile_impl(coin, input, signatures, public_keys) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn compile_impl( + _coin: &dyn CoinContext, + _input: Proto::SigningInput<'_>, + _signatures: Vec, + _public_keys: Vec, + ) -> SigningResult> { + todo!() + } +} diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs new file mode 100644 index 00000000000..afbada1d43e --- /dev/null +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs @@ -0,0 +1,91 @@ +// Copyright © 2017-{YEAR} Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::{BLOCKCHAIN}Address; +use crate::compiler::{BLOCKCHAIN}Compiler; +use crate::signer::{BLOCKCHAIN}Signer; +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::derivation::Derivation; +use tw_coin_entry::error::AddressResult; +use tw_coin_entry::modules::json_signer::NoJsonSigner; +use tw_coin_entry::modules::message_signer::NoMessageSigner; +use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::prefix::NoPrefix; +use tw_keypair::tw::PublicKey; +use tw_proto::{BLOCKCHAIN}::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct {BLOCKCHAIN}Entry; + +impl CoinEntry for {BLOCKCHAIN}Entry { + type AddressPrefix = NoPrefix; + type Address = {BLOCKCHAIN}Address; + type SigningInput<'a> = Proto::SigningInput<'a>; + type SigningOutput = Proto::SigningOutput<'static>; + type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + + // Optional modules: + type JsonSigner = NoJsonSigner; + type PlanBuilder = NoPlanBuilder; + type MessageSigner = NoMessageSigner; + + #[inline] + fn parse_address( + &self, + _coin: &dyn CoinContext, + _address: &str, + _prefix: Option, + ) -> AddressResult { + todo!() + } + + #[inline] + fn parse_address_unchecked( + &self, + _coin: &dyn CoinContext, + address: &str, + ) -> AddressResult { + {BLOCKCHAIN}Address::from_str(address) + } + + #[inline] + fn derive_address( + &self, + _coin: &dyn CoinContext, + _public_key: PublicKey, + _derivation: Derivation, + _prefix: Option, + ) -> AddressResult { + todo!() + } + + #[inline] + fn sign(&self, coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput { + {BLOCKCHAIN}Signer::sign(coin, input) + } + + #[inline] + fn preimage_hashes( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + ) -> Self::PreSigningOutput { + {BLOCKCHAIN}Compiler::preimage_hashes(coin, input) + } + + #[inline] + fn compile( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Self::SigningOutput { + {BLOCKCHAIN}Compiler::compile(coin, input, signatures, public_keys) + } +} diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/lib.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/lib.rs new file mode 100644 index 00000000000..c5ac4e098e5 --- /dev/null +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/lib.rs @@ -0,0 +1,10 @@ +// Copyright © 2017-{YEAR} Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod address; +pub mod compiler; +pub mod entry; +pub mod signer; diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/signer.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/signer.rs new file mode 100644 index 00000000000..17cb69f66e9 --- /dev/null +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/signer.rs @@ -0,0 +1,29 @@ +// Copyright © 2017-{YEAR} Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::SigningResult; +use tw_coin_entry::signing_output_error; +use tw_proto::{BLOCKCHAIN}::Proto; + +pub struct {BLOCKCHAIN}Signer; + +impl {BLOCKCHAIN}Signer { + pub fn sign( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> Proto::SigningOutput<'static> { + Self::sign_impl(coin, input) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn sign_impl( + _coin: &dyn CoinContext, + _input: Proto::SigningInput<'_>, + ) -> SigningResult> { + todo!() + } +} diff --git a/codegen-v2/src/codegen/rust/templates/integration_tests/address_tests.rs b/codegen-v2/src/codegen/rust/templates/integration_tests/address_tests.rs new file mode 100644 index 00000000000..6a3a7e6a330 --- /dev/null +++ b/codegen-v2/src/codegen/rust/templates/integration_tests/address_tests.rs @@ -0,0 +1,30 @@ +// Copyright © 2017-{YEAR} Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_any_coin::test_utils::address_utils::{ + test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, +}; +use tw_coin_registry::coin_type::CoinType; + +#[test] +fn test_{COIN_ID}_address_normalization() { + test_address_normalization(CoinType::{COIN_TYPE}, "DENORMALIZED", "EXPECTED"); +} + +#[test] +fn test_{COIN_ID}_address_is_valid() { + test_address_valid(CoinType::{COIN_TYPE}, "VALID ADDRESS"); +} + +#[test] +fn test_{COIN_ID}_address_invalid() { + test_address_invalid(CoinType::{COIN_TYPE}, "INVALID ADDRESS"); +} + +#[test] +fn test_{COIN_ID}_address_get_data() { + test_address_get_data(CoinType::{COIN_TYPE}, "ADDRESS", "HEX(DATA)"); +} diff --git a/codegen-v2/src/codegen/rust/templates/integration_tests/compile_tests.rs b/codegen-v2/src/codegen/rust/templates/integration_tests/compile_tests.rs new file mode 100644 index 00000000000..d403c5e154b --- /dev/null +++ b/codegen-v2/src/codegen/rust/templates/integration_tests/compile_tests.rs @@ -0,0 +1,10 @@ +// Copyright © 2017-{YEAR} Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#[test] +fn test_{COIN_ID}_compile() { + todo!() +} diff --git a/codegen-v2/src/codegen/rust/templates/integration_tests/mod.rs b/codegen-v2/src/codegen/rust/templates/integration_tests/mod.rs new file mode 100644 index 00000000000..96f7f590af1 --- /dev/null +++ b/codegen-v2/src/codegen/rust/templates/integration_tests/mod.rs @@ -0,0 +1,9 @@ +// Copyright © 2017-{YEAR} Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +mod {COIN_ID}_address; +mod {COIN_ID}_compile; +mod {COIN_ID}_sign; diff --git a/codegen-v2/src/codegen/rust/templates/integration_tests/sign_tests.rs b/codegen-v2/src/codegen/rust/templates/integration_tests/sign_tests.rs new file mode 100644 index 00000000000..9a24b45d74b --- /dev/null +++ b/codegen-v2/src/codegen/rust/templates/integration_tests/sign_tests.rs @@ -0,0 +1,10 @@ +// Copyright © 2017-{YEAR} Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#[test] +fn test_{COIN_ID}_sign() { + todo!() +} diff --git a/codegen-v2/src/codegen/rust/toml_editor.rs b/codegen-v2/src/codegen/rust/toml_editor.rs index da33d5fe270..0ac35b3c53d 100644 --- a/codegen-v2/src/codegen/rust/toml_editor.rs +++ b/codegen-v2/src/codegen/rust/toml_editor.rs @@ -37,8 +37,8 @@ impl Workspace { // Push the new member, sort and save the manifest. - let relative_path_to_crate_decorated = Value::from(relative_path_to_crate.to_string()) - .decorated(NEW_LINE_TAB_DECORATOR, NO_DECORATOR); + let relative_path_to_crate_decorated = + Value::from(relative_path_to_crate).decorated(NEW_LINE_TAB_DECORATOR, NO_DECORATOR); members.push_formatted(relative_path_to_crate_decorated); members.sort_by(|x, y| x.as_str().cmp(&y.as_str())); @@ -73,7 +73,7 @@ impl Dependencies { new_member.insert("path", relative_path_to_crate.into()); // Push the new member, sort and save the manifest. - dependencies.insert(dep_name, Item::Value(Value::InlineTable(new_member).into())); + dependencies.insert(dep_name, Item::Value(Value::InlineTable(new_member))); dependencies.sort_values(); fs::write(self.path_to_toml, manifest.to_string())?; diff --git a/codegen-v2/src/codegen/template_generator.rs b/codegen-v2/src/codegen/template_generator.rs new file mode 100644 index 00000000000..39f1780f1f1 --- /dev/null +++ b/codegen-v2/src/codegen/template_generator.rs @@ -0,0 +1,80 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::registry::CoinItem; +use crate::{current_year, Error, Result}; +use std::fs; +use std::path::PathBuf; + +const PATTERNS_CAPACITY: usize = 20; + +pub struct TemplateGenerator { + template_content: &'static str, + write_to: Option, + to_replace: Vec, + replace_with: Vec, +} + +impl TemplateGenerator { + pub fn new(template_content: &'static str) -> TemplateGenerator { + TemplateGenerator { + template_content, + write_to: None, + to_replace: Vec::with_capacity(PATTERNS_CAPACITY), + replace_with: Vec::with_capacity(PATTERNS_CAPACITY), + } + } + + pub fn write_to(mut self, write_to: PathBuf) -> TemplateGenerator { + self.write_to = Some(write_to); + self + } + + /// Use default patterns. + pub fn with_default_patterns(self, coin: &CoinItem) -> TemplateGenerator { + self.add_pattern("{YEAR}", current_year()) + .add_pattern("{BLOCKCHAIN}", coin.blockchain_type()) + .add_pattern("{TW_CRATE_NAME}", coin.id.to_tw_crate_name()) + .add_pattern("{COIN_ID}", coin.id.as_str()) + .add_pattern("{COIN_TYPE}", coin.coin_type()) + .add_pattern("{SYMBOL}", &coin.symbol) + .add_pattern("{DECIMALS}", coin.decimals) + .add_pattern("{P2PKH_PREFIX}", coin.p2pkh_prefix) + .add_pattern("{P2SH_PREFIX}", coin.p2sh_prefix) + .add_pattern("{STATIC_PREFIX}", coin.static_prefix) + .add_pattern("{EXPLORER_URL}", &coin.explorer.url) + .add_pattern("{EXPLORER_TX_PATH}", &coin.explorer.tx_path) + .add_pattern("{EXPLORER_ACCOUNT_PATH}", &coin.explorer.account_path) + .add_pattern("{EXPLORER_SAMPLE_TX}", &coin.explorer.sample_tx) + .add_pattern("{EXPLORER_SAMPLE_ACCOUNT}", &coin.explorer.sample_account) + } + + pub fn add_pattern( + mut self, + to_replace: K, + replace_with: V, + ) -> TemplateGenerator { + self.to_replace.push(to_replace.to_string()); + self.replace_with.push(replace_with.to_string()); + self + } + + pub fn write(self) -> Result<()> { + let write_to_path = self.write_to.ok_or_else(|| { + Error::io_error_other("Incorrect use of 'TemplateGenerator'".to_string()) + })?; + let file_to_write = fs::File::create(write_to_path)?; + + aho_corasick::AhoCorasick::new(self.to_replace) + .map_err(|e| Error::io_error_other(format!("Invalid patterns: {e}")))? + .try_stream_replace_all( + self.template_content.as_bytes(), + file_to_write, + &self.replace_with, + ) + .map_err(Error::from) + } +} diff --git a/codegen-v2/src/codegen/rust/coin_id.rs b/codegen-v2/src/coin_id.rs similarity index 100% rename from codegen-v2/src/codegen/rust/coin_id.rs rename to codegen-v2/src/coin_id.rs diff --git a/codegen-v2/src/lib.rs b/codegen-v2/src/lib.rs index 788f82cda8a..5227f533701 100644 --- a/codegen-v2/src/lib.rs +++ b/codegen-v2/src/lib.rs @@ -14,7 +14,9 @@ use std::io::Error as IoError; use toml_edit::TomlError; pub mod codegen; +pub mod coin_id; pub mod manifest; +pub mod registry; #[cfg(test)] mod tests; pub mod utils; diff --git a/codegen-v2/src/main.rs b/codegen-v2/src/main.rs index 39e5da606cf..a45bb39d733 100644 --- a/codegen-v2/src/main.rs +++ b/codegen-v2/src/main.rs @@ -4,9 +4,11 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use libparser::codegen::rust::new_blockchain::new_blockchain; use libparser::codegen::swift::RenderIntput; +use libparser::codegen::{cpp, proto, rust}; +use libparser::coin_id::CoinId; use libparser::manifest::parse_dir; +use libparser::registry::read_coin_from_registry; use libparser::{Error, Result}; use std::fs::read_to_string; @@ -18,21 +20,45 @@ fn main() -> Result<()> { } match args[1].as_str() { + "new-blockchain-rust" => new_blockchain_rust(&args[2..]), + "new-blockchain" => new_blockchain(&args[2..]), + "new-evmchain" => new_evmchain(&args[2..]), "swift" => generate_swift_bindings(), - "rust" => generate_rust(&args[2..]), _ => Err(Error::InvalidCommand), } } -fn generate_rust(args: &[String]) -> Result<()> { - if args.len() < 2 { - return Err(Error::InvalidCommand); - } +fn new_blockchain_rust(args: &[String]) -> Result<()> { + let coin_str = args.iter().next().ok_or_else(|| Error::InvalidCommand)?; + let coin_id = CoinId::new(coin_str.clone())?; + let coin_item = read_coin_from_registry(&coin_id)?; - match args[0].as_str() { - "new-blockchain" => new_blockchain(&args[1]), - _ => Err(Error::InvalidCommand), - } + rust::new_blockchain::new_blockchain(&coin_item)?; + + Ok(()) +} + +fn new_blockchain(args: &[String]) -> Result<()> { + let coin_str = args.iter().next().ok_or_else(|| Error::InvalidCommand)?; + let coin_id = CoinId::new(coin_str.clone())?; + let coin_item = read_coin_from_registry(&coin_id)?; + + proto::new_blockchain::new_blockchain(&coin_item)?; + rust::new_blockchain::new_blockchain(&coin_item)?; + cpp::new_blockchain::new_blockchain(&coin_item)?; + + Ok(()) +} + +fn new_evmchain(args: &[String]) -> Result<()> { + let coin_str = args.iter().next().ok_or_else(|| Error::InvalidCommand)?; + let coin_id = CoinId::new(coin_str.clone())?; + let coin_item = read_coin_from_registry(&coin_id)?; + + rust::new_evmchain::new_evmchain(&coin_item)?; + cpp::new_evmchain::new_evmchain(&coin_item)?; + + Ok(()) } fn generate_swift_bindings() -> Result<()> { diff --git a/codegen-v2/src/registry.rs b/codegen-v2/src/registry.rs new file mode 100644 index 00000000000..aaad3ff0330 --- /dev/null +++ b/codegen-v2/src/registry.rs @@ -0,0 +1,89 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::coin_id::CoinId; +use crate::{Error, Result}; +use convert_case::{Case, Casing}; +use std::path::PathBuf; +use std::{env, fs}; + +pub fn registry_json_path() -> PathBuf { + PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()) + .join("..") + .join("registry.json") +} + +#[derive(Clone, Deserialize)] +pub struct CoinExplorer { + pub url: String, + #[serde(rename = "txPath")] + pub tx_path: String, + #[serde(rename = "accountPath")] + pub account_path: String, + #[serde(rename = "sampleTx")] + #[serde(default)] + pub sample_tx: String, + #[serde(rename = "sampleAccount")] + #[serde(default)] + pub sample_account: String, +} + +#[derive(Clone, Deserialize)] +pub struct CoinItem { + pub id: CoinId, + pub name: String, + #[serde(rename = "coinId")] + pub coin_id_number: u32, + pub symbol: String, + pub decimals: u8, + pub blockchain: String, + #[serde(rename = "p2pkhPrefix")] + #[serde(default)] + pub p2pkh_prefix: u8, + #[serde(rename = "p2shPrefix")] + #[serde(default)] + pub p2sh_prefix: u8, + #[serde(rename = "staticPrefix")] + #[serde(default)] + pub static_prefix: u8, + pub explorer: CoinExplorer, +} + +impl CoinItem { + /// Transforms a coin name to a Rust name. + /// https://github.com/trustwallet/wallet-core/blob/3769f31b7d0c75126b2f426bb065364429aaa379/codegen/lib/coin_skeleton_gen.rb#L15-L22 + pub fn coin_type(&self) -> String { + self.name.replace([' ', '.', '-'], "") + } + + /// Returns the blockchain type in `UpperCamel` case. + pub fn blockchain_type(&self) -> String { + self.blockchain.to_case(Case::UpperCamel) + } + + /// Returns the blockchain type in `UPPER_SNAKE` case. + pub fn blockchain_entry_upper_snake(&self) -> String { + self.blockchain.to_case(Case::UpperSnake) + } + + /// Returns a Rust blockchain entry of the blockchain. + pub fn blockchain_entry(&self) -> String { + format!("{}Entry", self.blockchain_type()) + } +} + +pub fn read_coin_from_registry(coin: &CoinId) -> Result { + let registry_path = registry_json_path(); + + let registry_bytes = fs::read(registry_path)?; + let coins: Vec = + serde_json::from_slice(®istry_bytes).map_err(|e| Error::RegistryError(e.to_string()))?; + + coins + .into_iter() + .find(|item| item.id == *coin) + .ok_or(Error::InvalidCommand) +} diff --git a/codegen-v2/src/utils.rs b/codegen-v2/src/utils.rs index bb4b01d2902..43e1bd84f81 100644 --- a/codegen-v2/src/utils.rs +++ b/codegen-v2/src/utils.rs @@ -31,6 +31,28 @@ impl FileContent { read_lines(&path).map(|lines| FileContent { path, lines }) } + pub fn find_region_with_prefix(&mut self, prefix: &str) -> Result> { + // Find the first line that starts with the `prefix`. + let region_starts_at = self + .lines + .iter() + .position(|line| line.starts_with(prefix)) + .ok_or_else(|| Error::io_error_other(format!("Cannot find the `{prefix}` region")))?; + + // Find the last line that starts with the `prefix`. + let region_ends_at = self + .lines + .iter() + .rposition(|line| line.starts_with(prefix)) + .ok_or_else(|| Error::io_error_other(format!("Cannot find the `{prefix}` region")))?; + + Ok(FileRegion { + lines: &mut self.lines, + region_starts_at, + region_ends_at, + }) + } + pub fn find_region_with_comments( &mut self, start_comment: &str, @@ -74,16 +96,12 @@ impl FileContent { where F: Fn(&str) -> bool, { - let line_idx = self - .lines - .iter() - .rposition(|line| f(&line)) - .ok_or_else(|| { - Error::io_error_other(format!( - "{:?} file does not contain a required pattern", - self.path - )) - })?; + let line_idx = self.lines.iter().rposition(|line| f(line)).ok_or_else(|| { + Error::io_error_other(format!( + "{:?} file does not contain a required pattern", + self.path + )) + })?; Ok(LinePointer { lines: &mut self.lines, line_idx, @@ -104,10 +122,15 @@ pub struct FileRegion<'a> { impl<'a> FileRegion<'a> { pub fn push_line(&mut self, line: String) { self.lines.insert(self.region_ends_at + 1, line); + self.region_ends_at += 1; } pub fn sort(&mut self) { - self.lines[self.region_starts_at..self.region_ends_at].sort() + self.lines[self.region_starts_at..=self.region_ends_at].sort() + } + + pub fn count_lines(&self) -> usize { + self.region_ends_at - self.region_starts_at } } @@ -117,6 +140,19 @@ pub struct LinePointer<'a> { } impl<'a> LinePointer<'a> { + /// Please note that the line pointer will be shifted to the same line on which it pointed before. + pub fn push_line_before(&mut self, line: String) { + self.lines.insert(self.line_idx, line); + self.line_idx += 1; + } + + pub fn push_paragraph_before(&mut self, paragraph: String) { + for line in paragraph.split("\n") { + self.push_line_before(line.to_string()); + } + } + + /// Please note that the line pointer will not be shifted to the pushed element. pub fn push_line_after(&mut self, line: String) { self.lines.insert(self.line_idx + 1, line); } diff --git a/codegen/bin/newcoin b/codegen/bin/newcoin index aeeec609d73..ee14b241f7a 100755 --- a/codegen/bin/newcoin +++ b/codegen/bin/newcoin @@ -18,4 +18,4 @@ end coin_id = command_line_args[0] -generate_skeleton(coin_id, "") +generate_skeleton(coin_id, "full") diff --git a/codegen/bin/newcoin-mobile-tests b/codegen/bin/newcoin-mobile-tests new file mode 100755 index 00000000000..d6ea29a84c9 --- /dev/null +++ b/codegen/bin/newcoin-mobile-tests @@ -0,0 +1,22 @@ +#!/usr/bin/env ruby + +# Sript for creating new skeleton files for new coin mobile tests. See also `newcoin` or `newevmchain`. +# It is considered to be used by codegen-v2 tool until Swift and Android tests generating supported. +# 1. Add relevant entry to registry.json (in order to minimize merge conflict, don't add at the very end) +# 2. Invoke this script with the id of the coin, e.g.: codegen/bin/newcoin-mobile-tests ethereum + +require 'fileutils' + +CurrentDir = File.dirname(__FILE__) +$LOAD_PATH.unshift(File.join(CurrentDir, '..', 'lib')) +require 'coin_skeleton_gen' + +command_line_args = ARGV +if command_line_args.length < 1 + puts "Usage: newcoin-mobile-tests " + return +end + +coin_id = command_line_args[0] + +generate_skeleton(coin_id, "mobile-tests") diff --git a/codegen/lib/coin_skeleton_gen.rb b/codegen/lib/coin_skeleton_gen.rb index 29335e4fdb0..9a12fca1e9e 100755 --- a/codegen/lib/coin_skeleton_gen.rb +++ b/codegen/lib/coin_skeleton_gen.rb @@ -90,6 +90,36 @@ def self.insert_target_line(target_file, target_line, original_line) return true end +def generate_blockchain_files(coin) + name = format_name(coin) + + generate_file("newcoin/Address.h.erb", "src/#{name}", "Address.h", coin) + generate_file("newcoin/Address.cpp.erb", "src/#{name}", "Address.cpp", coin) + generate_file("newcoin/Entry.h.erb", "src/#{name}", "Entry.h", coin) + generate_file("newcoin/Entry.cpp.erb", "src/#{name}", "Entry.cpp", coin) + generate_file("newcoin/Proto.erb", "src/proto", "#{name}.proto", coin) + generate_file("newcoin/Signer.h.erb", "src/#{name}", "Signer.h", coin) + generate_file("newcoin/Signer.cpp.erb", "src/#{name}", "Signer.cpp", coin) + + generate_file("newcoin/AddressTests.cpp.erb", "tests/chains/#{name}", "AddressTests.cpp", coin) + generate_file("newcoin/SignerTests.cpp.erb", "tests/chains/#{name}", "SignerTests.cpp", coin) + generate_file("newcoin/TransactionCompilerTests.cpp.erb", "tests/chains/#{name}", "TransactionCompilerTests.cpp", coin) + generate_file("newcoin/TWAddressTests.cpp.erb", "tests/chains/#{name}", "TWAnyAddressTests.cpp", coin) + generate_file("newcoin/TWSignerTests.cpp.erb", "tests/chains/#{name}", "TWAnySignerTests.cpp", coin) +end + +def generate_mobile_tests(coin) + name = format_name(coin) + + generate_file("newcoin/AddressTests.kt.erb", "android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/#{format_name_lowercase(coin)}", "Test#{name}Address.kt", coin) + generate_file("newcoin/SignerTests.kt.erb", "android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/#{format_name_lowercase(coin)}", "Test#{name}Signer.kt", coin) + generate_file("newcoin/Tests.swift.erb", "swift/Tests/Blockchains", "#{name}Tests.swift", coin) +end + +def generate_coin_type_tests(coin) + coin_test_gen.generate_coin_test_file(coin, 'TWCoinTypeTests.cpp.erb', true) +end + def generate_skeleton(coin_id, mode) puts "New coin template for coin '#{coin_id}' #{mode} requested" @@ -112,32 +142,19 @@ def generate_skeleton(coin_id, mode) return end coin = coinSelect.first - name = format_name(coin) - - insert_coin_type(coin, mode) - if (mode != "evm") + if (mode == "full") + insert_coin_type(coin, mode) insert_coin_entry(coin) - - generate_file("newcoin/Address.h.erb", "src/#{name}", "Address.h", coin) - generate_file("newcoin/Address.cpp.erb", "src/#{name}", "Address.cpp", coin) - generate_file("newcoin/Entry.h.erb", "src/#{name}", "Entry.h", coin) - generate_file("newcoin/Entry.cpp.erb", "src/#{name}", "Entry.cpp", coin) - generate_file("newcoin/Proto.erb", "src/proto", "#{name}.proto", coin) - generate_file("newcoin/Signer.h.erb", "src/#{name}", "Signer.h", coin) - generate_file("newcoin/Signer.cpp.erb", "src/#{name}", "Signer.cpp", coin) - - generate_file("newcoin/AddressTests.cpp.erb", "tests/chains/#{name}", "AddressTests.cpp", coin) - generate_file("newcoin/SignerTests.cpp.erb", "tests/chains/#{name}", "SignerTests.cpp", coin) - generate_file("newcoin/TransactionCompilerTests.cpp.erb", "tests/chains/#{name}", "TransactionCompilerTests.cpp", coin) - generate_file("newcoin/TWAddressTests.cpp.erb", "tests/chains/#{name}", "TWAnyAddressTests.cpp", coin) - generate_file("newcoin/TWSignerTests.cpp.erb", "tests/chains/#{name}", "TWAnySignerTests.cpp", coin) - generate_file("newcoin/AddressTests.kt.erb", "android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/#{format_name_lowercase(coin)}", "Test#{name}Address.kt", coin) - generate_file("newcoin/SignerTests.kt.erb", "android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/#{format_name_lowercase(coin)}", "Test#{name}Signer.kt", coin) - generate_file("newcoin/Tests.swift.erb", "swift/Tests/Blockchains", "#{name}Tests.swift", coin) + generate_blockchain_files(coin) + generate_mobile_tests(coin) + generate_coin_type_tests(coin) + elsif (mode == "evm") + insert_coin_type(coin, mode) + generate_coin_type_tests(coin) + elsif (mode == "mobile-tests") + generate_mobile_tests(coin) end - coin_test_gen.generate_coin_test_file(coin, 'TWCoinTypeTests.cpp.erb', true) - puts "please tools/generate-files to generate Swift/Java/Protobuf files" end diff --git a/include/TrustWalletCore/TWBlockchain.h b/include/TrustWalletCore/TWBlockchain.h index 75da789d240..94187ca6899 100644 --- a/include/TrustWalletCore/TWBlockchain.h +++ b/include/TrustWalletCore/TWBlockchain.h @@ -65,8 +65,8 @@ enum TWBlockchain { TWBlockchainSui = 50, TWBlockchainGreenfield = 51, TWBlockchainInternetComputer = 52, - TWBlockchainNativeEvmos = 55, // Cosmos - TWBlockchainNativeInjective = 56, // Cosmos + TWBlockchainNativeEvmos = 53, // Cosmos + TWBlockchainNativeInjective = 54, // Cosmos }; TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index cd2780d8a55..56df414d348 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -179,6 +179,7 @@ enum TWCoinType { TWCoinTypeZenEON = 7332, TWCoinTypeInternetComputer = 223, TWCoinTypeTia = 21000118, + // end_of_tw_coin_type_marker_do_not_modify }; /// Returns the blockchain for a coin type. diff --git a/rust/tw_any_coin/tests/coin_address_derivation_tests.rs b/rust/tw_any_coin/tests/coin_address_derivation_test.rs similarity index 96% rename from rust/tw_any_coin/tests/coin_address_derivation_tests.rs rename to rust/tw_any_coin/tests/coin_address_derivation_test.rs index 7ce749d3883..8320fd77d57 100644 --- a/rust/tw_any_coin/tests/coin_address_derivation_tests.rs +++ b/rust/tw_any_coin/tests/coin_address_derivation_test.rs @@ -85,8 +85,9 @@ fn test_coin_address_derivation() { | CoinType::Linea | CoinType::Greenfield | CoinType::Mantle - | CoinType::ZenEON => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", - // end_of_ethereum_coin_address_derivation - DO NOT REMOVE + | CoinType::ZenEON + // end_of_evm_address_derivation_tests_marker_do_not_modify + => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", CoinType::Bitcoin // TODO all Bitcoin-based blockchains should have different addresses. // It should be fixed when Bitcoin is finalized. @@ -143,7 +144,7 @@ fn test_coin_address_derivation() { CoinType::NativeInjective => "inj14s0vgnj0pjnazu4hsqlksdk7slah9vcfyrp6ct", CoinType::NativeCanto => "canto14s0vgnj0pjnazu4hsqlksdk7slah9vcfuuhw7m", CoinType::InternetComputer => "290cc7c359f44c8516fc169c5ed4f0f3ae2e24bf5de0d4c51f5e7545b5474faa", - // end_of_coin_address_derivation - DO NOT REMOVE + // end_of_coin_address_derivation_tests_marker_do_not_modify _ => panic!("{:?} must be covered", coin), }; diff --git a/rust/tw_coin_entry/src/coin_entry.rs b/rust/tw_coin_entry/src/coin_entry.rs index 70c9d24077d..785021d409b 100644 --- a/rust/tw_coin_entry/src/coin_entry.rs +++ b/rust/tw_coin_entry/src/coin_entry.rs @@ -26,6 +26,13 @@ pub trait CoinAddress: fmt::Display { fn data(&self) -> Data; } +/// The main coin entry trait. It is responsible for address management and the transaction signing. +/// +/// # Maintaining +/// +/// Please sync them with the code generator template if there is need to make any changes in this trait +/// (e.g adding/deleting a method or an associated type): +/// https://github.com/trustwallet/wallet-core/blob/master/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs pub trait CoinEntry { type AddressPrefix: Prefix; type Address: CoinAddress; diff --git a/rust/tw_coin_registry/src/dispatcher.rs b/rust/tw_coin_registry/src/dispatcher.rs index 02850a513d0..61e6f0d5998 100644 --- a/rust/tw_coin_registry/src/dispatcher.rs +++ b/rust/tw_coin_registry/src/dispatcher.rs @@ -47,9 +47,9 @@ pub fn blockchain_dispatcher(blockchain: BlockchainType) -> RegistryResult Ok(&NATIVE_EVMOS), BlockchainType::NativeInjective => Ok(&NATIVE_INJECTIVE), BlockchainType::Ronin => Ok(&RONIN), - BlockchainType::Unsupported => Err(RegistryError::Unsupported), BlockchainType::Thorchain => Ok(&THORCHAIN), // end_of_blockchain_dispatcher - USED TO GENERATE CODE + BlockchainType::Unsupported => Err(RegistryError::Unsupported), } } diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index d9a8736b834..f9b6c28e46b 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -84,6 +84,7 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeGreenfield: case TWCoinTypeMantle: case TWCoinTypeZenEON: + // end_of_evm_address_derivation_tests_marker_do_not_modify EXPECT_EQ(address, "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); break; @@ -384,6 +385,7 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeTia: EXPECT_EQ(address, "celestia1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0g3wnkv"); break; + // end_of_coin_address_derivation_tests_marker_do_not_modify // no default branch here, intentionally, to better notice any missing coins } } From a8c4ee77e4e84945e8da06f040b78fc8b06b497f Mon Sep 17 00:00:00 2001 From: Satoshi Otomakan Date: Fri, 24 Nov 2023 18:25:27 +0100 Subject: [PATCH 019/128] [Codegen]: Add stdout logs, fix codegen-v1 --- .../cpp/blockchain_dispatcher_generator.rs | 4 +++- codegen-v2/src/codegen/cpp/entry_generator.rs | 9 ++++----- .../cpp/tw_any_address_tests_generator.rs | 2 ++ .../cpp/tw_any_signer_tests_generator.rs | 2 ++ codegen-v2/src/codegen/cpp/tw_blockchain.rs | 4 +++- ...coin_address_derivation_tests_generator.rs | 10 ++++++---- .../src/codegen/cpp/tw_coin_type_generator.rs | 4 +++- .../cpp/tw_coin_type_tests_generator.rs | 2 ++ .../src/codegen/proto/proto_generator.rs | 2 ++ .../rust/blockchain_dispatcher_generator.rs | 1 + .../codegen/rust/blockchain_type_generator.rs | 1 + .../coin_address_derivation_test_generator.rs | 10 ++++++---- codegen-v2/src/codegen/rust/coin_crate.rs | 19 +++++++++++++------ .../codegen/rust/coin_integration_tests.rs | 8 +++++++- .../rust/coin_registry_manifest_generator.rs | 1 + codegen-v2/src/main.rs | 5 +++++ codegen/lib/coin_skeleton_gen.rb | 3 +-- 17 files changed, 62 insertions(+), 25 deletions(-) diff --git a/codegen-v2/src/codegen/cpp/blockchain_dispatcher_generator.rs b/codegen-v2/src/codegen/cpp/blockchain_dispatcher_generator.rs index 58b13ac452c..d0b4fcd552a 100644 --- a/codegen-v2/src/codegen/cpp/blockchain_dispatcher_generator.rs +++ b/codegen-v2/src/codegen/cpp/blockchain_dispatcher_generator.rs @@ -24,7 +24,9 @@ pub struct BlockchainDispatcherGenerator; impl BlockchainDispatcherGenerator { pub fn generate_new_blockchain_type_dispatching(coin: &CoinItem) -> Result<()> { - let mut file_content = FileContent::read(dispatcher_coin_cpp_path())?; + let dispatcher_path = dispatcher_coin_cpp_path(); + println!("[EDIT] {dispatcher_path:?}"); + let mut file_content = FileContent::read(dispatcher_path)?; Self::generate_include_of_blockchain_entry(coin, &mut file_content)?; Self::generate_blockchain_entry_constant(coin, &mut file_content)?; diff --git a/codegen-v2/src/codegen/cpp/entry_generator.rs b/codegen-v2/src/codegen/cpp/entry_generator.rs index 033c94d6856..f7c453b719d 100644 --- a/codegen-v2/src/codegen/cpp/entry_generator.rs +++ b/codegen-v2/src/codegen/cpp/entry_generator.rs @@ -25,14 +25,13 @@ impl EntryGenerator { let entry_header_path = blockchain_dir.join("Entry.h"); if blockchain_dir.exists() { - return Err(Error::IoError(io::Error::new( - io::ErrorKind::AlreadyExists, - "blockchain already exists", - ))); + println!("[SKIP] Entry file already exists: {blockchain_dir:?}"); + return Ok(blockchain_dir); } - fs::create_dir(&blockchain_dir)?; + fs::create_dir_all(&blockchain_dir)?; + println!("[ADD] {entry_header_path:?}"); TemplateGenerator::new(ENTRY_HEADER_TEMPLATE) .write_to(entry_header_path.clone()) .with_default_patterns(coin) diff --git a/codegen-v2/src/codegen/cpp/tw_any_address_tests_generator.rs b/codegen-v2/src/codegen/cpp/tw_any_address_tests_generator.rs index f7d7384a0d8..f6fa62ac324 100644 --- a/codegen-v2/src/codegen/cpp/tw_any_address_tests_generator.rs +++ b/codegen-v2/src/codegen/cpp/tw_any_address_tests_generator.rs @@ -26,9 +26,11 @@ impl TWAnyAddressTestsGenerator { fs::create_dir_all(coin_tests_dir)?; if tw_any_address_tests_path.exists() { + println!("[SKIP] {tw_any_address_tests_path:?} already exists"); return Ok(()); } + println!("[ADD] {tw_any_address_tests_path:?}"); TemplateGenerator::new(TW_ANY_ADDRESS_TESTS_TEMPLATE) .write_to(tw_any_address_tests_path) .with_default_patterns(coin) diff --git a/codegen-v2/src/codegen/cpp/tw_any_signer_tests_generator.rs b/codegen-v2/src/codegen/cpp/tw_any_signer_tests_generator.rs index 32391ffe618..677162aee84 100644 --- a/codegen-v2/src/codegen/cpp/tw_any_signer_tests_generator.rs +++ b/codegen-v2/src/codegen/cpp/tw_any_signer_tests_generator.rs @@ -26,9 +26,11 @@ impl TWAnySignerTestsGenerator { fs::create_dir_all(coin_tests_dir)?; if tw_any_signer_tests_path.exists() { + println!("[SKIP] {tw_any_signer_tests_path:?} already exists"); return Ok(()); } + println!("[ADD] {tw_any_signer_tests_path:?}"); TemplateGenerator::new(TW_ANY_SIGNER_TESTS_TEMPLATE) .write_to(tw_any_signer_tests_path) .with_default_patterns(coin) diff --git a/codegen-v2/src/codegen/cpp/tw_blockchain.rs b/codegen-v2/src/codegen/cpp/tw_blockchain.rs index dee2986d328..f74c1e20ea9 100644 --- a/codegen-v2/src/codegen/cpp/tw_blockchain.rs +++ b/codegen-v2/src/codegen/cpp/tw_blockchain.rs @@ -23,8 +23,10 @@ pub struct TWBlockchainGenerator; impl TWBlockchainGenerator { pub fn generate_blockchain_type_variant(coin: &CoinItem) -> Result<()> { let coin_type = coin.blockchain_type(); + let tw_blockchain_type_path = tw_blockchain_path(); - let mut tw_blockchain_type_rs = FileContent::read(tw_blockchain_path())?; + println!("[EDIT] {tw_blockchain_type_path:?}"); + let mut tw_blockchain_type_rs = FileContent::read(tw_blockchain_type_path)?; { let mut enum_region = diff --git a/codegen-v2/src/codegen/cpp/tw_coin_address_derivation_tests_generator.rs b/codegen-v2/src/codegen/cpp/tw_coin_address_derivation_tests_generator.rs index 772fea23676..1cdeaf295ac 100644 --- a/codegen-v2/src/codegen/cpp/tw_coin_address_derivation_tests_generator.rs +++ b/codegen-v2/src/codegen/cpp/tw_coin_address_derivation_tests_generator.rs @@ -27,9 +27,10 @@ pub struct CoinAddressDerivationTestsGenerator; impl CoinAddressDerivationTestsGenerator { pub fn generate_new_coin_type_case(coin: &CoinItem) -> Result<()> { let coin_type = coin.coin_type(); + let test_path = coin_address_derivation_tests_path(); + println!("[EDIT] {test_path:?}"); - let mut coin_address_derivation_test_rs = - FileContent::read(coin_address_derivation_tests_path())?; + let mut coin_address_derivation_test_rs = FileContent::read(test_path)?; { let mut switch_case_region = coin_address_derivation_test_rs @@ -50,9 +51,10 @@ r#" case TWCoinType{coin_type}: pub fn generate_new_evm_coin_type_case(coin: &CoinItem) -> Result<()> { let coin_type = coin.coin_type(); + let test_path = coin_address_derivation_tests_path(); + println!("[EDIT] {test_path:?}"); - let mut evm_address_derivation_test_rs = - FileContent::read(coin_address_derivation_tests_path())?; + let mut evm_address_derivation_test_rs = FileContent::read(test_path)?; { let mut switch_case_region = evm_address_derivation_test_rs diff --git a/codegen-v2/src/codegen/cpp/tw_coin_type_generator.rs b/codegen-v2/src/codegen/cpp/tw_coin_type_generator.rs index 385f69076cd..d238922ae4d 100644 --- a/codegen-v2/src/codegen/cpp/tw_coin_type_generator.rs +++ b/codegen-v2/src/codegen/cpp/tw_coin_type_generator.rs @@ -23,8 +23,10 @@ impl TWCoinTypeGenerator { pub fn generate_coin_type_variant(coin: &CoinItem) -> Result<()> { let coin_type = coin.coin_type(); let coin_id_number = coin.coin_id_number; + let tw_coin_type_file_path = tw_coin_type_path(); - let mut tw_coin_type_rs = FileContent::read(tw_coin_type_path())?; + println!("[EDIT] {tw_coin_type_file_path:?}"); + let mut tw_coin_type_rs = FileContent::read(tw_coin_type_file_path)?; { let mut enum_region = diff --git a/codegen-v2/src/codegen/cpp/tw_coin_type_tests_generator.rs b/codegen-v2/src/codegen/cpp/tw_coin_type_tests_generator.rs index 2790c302138..8b85c4e79f4 100644 --- a/codegen-v2/src/codegen/cpp/tw_coin_type_tests_generator.rs +++ b/codegen-v2/src/codegen/cpp/tw_coin_type_tests_generator.rs @@ -26,9 +26,11 @@ impl TWCoinTypeTestsGenerator { fs::create_dir(coin_tests_dir)?; if tw_coin_type_tests_path.exists() { + println!("[SKIP] {tw_coin_type_tests_path:?} already exists"); return Ok(()); } + println!("[ADD] {tw_coin_type_tests_path:?}"); TemplateGenerator::new(TW_COIN_TYPE_TESTS_TEMPLATE) .write_to(tw_coin_type_tests_path) .with_default_patterns(coin) diff --git a/codegen-v2/src/codegen/proto/proto_generator.rs b/codegen-v2/src/codegen/proto/proto_generator.rs index 3b08ef8b16c..f3bd016984d 100644 --- a/codegen-v2/src/codegen/proto/proto_generator.rs +++ b/codegen-v2/src/codegen/proto/proto_generator.rs @@ -24,9 +24,11 @@ impl ProtoGenerator { let proto_path = blockchain_proto_path(coin); if proto_path.exists() { + println!("[SKIP] Protobuf interface already exists: {proto_path:?}"); return Ok(()); } + println!("[ADD] {proto_path:?}"); TemplateGenerator::new(PROTO_TEMPLATE) .write_to(proto_path) .with_default_patterns(coin) diff --git a/codegen-v2/src/codegen/rust/blockchain_dispatcher_generator.rs b/codegen-v2/src/codegen/rust/blockchain_dispatcher_generator.rs index 4c00268a7ca..19c83519e15 100644 --- a/codegen-v2/src/codegen/rust/blockchain_dispatcher_generator.rs +++ b/codegen-v2/src/codegen/rust/blockchain_dispatcher_generator.rs @@ -24,6 +24,7 @@ pub struct BlockchainDispatcherGenerator; impl BlockchainDispatcherGenerator { pub fn generate_new_blockchain_type_dispatching(coin: &CoinItem) -> Result<()> { let dispatcher_rs_path = dispatcher_path(); + println!("[EDIT] {dispatcher_rs_path:?}"); let mut dispatcher_rs = FileContent::read(dispatcher_rs_path)?; Self::generate_use_of_blockchain_entry(coin, &mut dispatcher_rs)?; diff --git a/codegen-v2/src/codegen/rust/blockchain_type_generator.rs b/codegen-v2/src/codegen/rust/blockchain_type_generator.rs index f82ce5144fa..b902e88f739 100644 --- a/codegen-v2/src/codegen/rust/blockchain_type_generator.rs +++ b/codegen-v2/src/codegen/rust/blockchain_type_generator.rs @@ -27,6 +27,7 @@ impl BlockchainTypeGenerator { let blockchain_type_rs_path = blockchain_type_path(); let blockchain_type = coin.blockchain_type(); + println!("[EDIT] {blockchain_type_rs_path:?}"); let mut blockchain_type_rs = FileContent::read(blockchain_type_rs_path)?; { diff --git a/codegen-v2/src/codegen/rust/coin_address_derivation_test_generator.rs b/codegen-v2/src/codegen/rust/coin_address_derivation_test_generator.rs index 25b2b9933b4..4a5d3acdcc6 100644 --- a/codegen-v2/src/codegen/rust/coin_address_derivation_test_generator.rs +++ b/codegen-v2/src/codegen/rust/coin_address_derivation_test_generator.rs @@ -25,10 +25,11 @@ pub struct CoinAddressDerivationTestGenerator; impl CoinAddressDerivationTestGenerator { pub fn generate_new_coin_type_case(coin: &CoinItem) -> Result<()> { + let test_path = coin_address_derivation_test_path(); let coin_type = coin.coin_type(); - let mut coin_address_derivation_test_rs = - FileContent::read(coin_address_derivation_test_path())?; + println!("[EDIT] {test_path:?}"); + let mut coin_address_derivation_test_rs = FileContent::read(test_path)?; { let mut end_of_test = coin_address_derivation_test_rs @@ -40,10 +41,11 @@ impl CoinAddressDerivationTestGenerator { } pub fn generate_new_evm_coin_type_case(coin: &CoinItem) -> Result<()> { + let test_path = coin_address_derivation_test_path(); let coin_type = coin.coin_type(); - let mut coin_address_derivation_test_rs = - FileContent::read(coin_address_derivation_test_path())?; + println!("[EDIT] {test_path:?}"); + let mut coin_address_derivation_test_rs = FileContent::read(test_path)?; { let mut end_of_test = coin_address_derivation_test_rs diff --git a/codegen-v2/src/codegen/rust/coin_crate.rs b/codegen-v2/src/codegen/rust/coin_crate.rs index 8b6ef18b3c1..330d84e26b2 100644 --- a/codegen-v2/src/codegen/rust/coin_crate.rs +++ b/codegen-v2/src/codegen/rust/coin_crate.rs @@ -46,40 +46,47 @@ impl CoinCrate { let blockchain_signer_rs_path = blockchain_src_path.join("signer.rs"); if blockchain_path.exists() { - return Err(Error::IoError(io::Error::new( - io::ErrorKind::AlreadyExists, - "blockchain already exists", - ))); + let tw_crate_name = self.coin.id.to_tw_crate_name(); + println!( + "[SKIP] '{tw_crate_name}' blockchain crate already exists: {blockchain_path:?}" + ); + return Ok(blockchain_path); } - fs::create_dir(&blockchain_path)?; - fs::create_dir(&blockchain_src_path)?; + fs::create_dir_all(&blockchain_path)?; + fs::create_dir_all(&blockchain_src_path)?; + println!("[ADD] {blockchain_toml_path:?}"); TemplateGenerator::new(BLOCKCHAIN_MANIFEST_TEMPLATE) .write_to(blockchain_toml_path) .with_default_patterns(&self.coin) .write()?; + println!("[ADD] {blockchain_lib_rs_path:?}"); TemplateGenerator::new(BLOCKCHAIN_LIB_TEMPLATE) .write_to(blockchain_lib_rs_path) .with_default_patterns(&self.coin) .write()?; + println!("[ADD] {blockchain_entry_path:?}"); TemplateGenerator::new(BLOCKCHAIN_ENTRY_TEMPLATE) .write_to(blockchain_entry_path) .with_default_patterns(&self.coin) .write()?; + println!("[ADD] {blockchain_compiler_path:?}"); TemplateGenerator::new(BLOCKCHAIN_COMPILER_TEMPLATE) .write_to(blockchain_compiler_path) .with_default_patterns(&self.coin) .write()?; + println!("[ADD] {blockchain_address_rs_path:?}"); TemplateGenerator::new(BLOCKCHAIN_ADDRESS_TEMPLATE) .write_to(blockchain_address_rs_path) .with_default_patterns(&self.coin) .write()?; + println!("[ADD] {blockchain_signer_rs_path:?}"); TemplateGenerator::new(BLOCKCHAIN_SIGNER_TEMPLATE) .write_to(blockchain_signer_rs_path) .with_default_patterns(&self.coin) diff --git a/codegen-v2/src/codegen/rust/coin_integration_tests.rs b/codegen-v2/src/codegen/rust/coin_integration_tests.rs index 18c5356f064..541e1b2ae3c 100644 --- a/codegen-v2/src/codegen/rust/coin_integration_tests.rs +++ b/codegen-v2/src/codegen/rust/coin_integration_tests.rs @@ -44,10 +44,11 @@ impl CoinIntegrationTests { pub fn create(self) -> Result { let blockchain_tests_path = self.coin_tests_directory(); if blockchain_tests_path.exists() { + println!("[SKIP] integration tests already exists: {blockchain_tests_path:?}"); return Ok(blockchain_tests_path); } - fs::create_dir(&blockchain_tests_path)?; + fs::create_dir_all(&blockchain_tests_path)?; self.list_blockchain_in_chains_mod()?; self.create_address_tests()?; @@ -68,6 +69,7 @@ impl CoinIntegrationTests { .coin_tests_directory() .join(format!("{coin_id}_address.rs")); + println!("[ADD] {address_tests_path:?}"); TemplateGenerator::new(ADDRESS_TESTS_TEMPLATE) .write_to(address_tests_path) .with_default_patterns(&self.coin) @@ -80,6 +82,7 @@ impl CoinIntegrationTests { .coin_tests_directory() .join(format!("{coin_id}_compile.rs")); + println!("[ADD] {compile_tests_path:?}"); TemplateGenerator::new(COMPILE_TESTS_TEMPLATE) .write_to(compile_tests_path) .with_default_patterns(&self.coin) @@ -92,6 +95,7 @@ impl CoinIntegrationTests { .coin_tests_directory() .join(format!("{coin_id}_sign.rs")); + println!("[ADD] {sign_tests_path:?}"); TemplateGenerator::new(SIGN_TESTS_TEMPLATE) .write_to(sign_tests_path) .with_default_patterns(&self.coin) @@ -101,6 +105,7 @@ impl CoinIntegrationTests { fn create_chain_tests_mod_rs(&self) -> Result<()> { let blockchain_tests_mod_path = self.coin_tests_directory().join("mod.rs"); + println!("[ADD] {blockchain_tests_mod_path:?}"); TemplateGenerator::new(MOD_TESTS_TEMPLATE) .write_to(blockchain_tests_mod_path) .with_default_patterns(&self.coin) @@ -111,6 +116,7 @@ impl CoinIntegrationTests { let chains_mod_path = chains_integration_tests_directory().join("mod.rs"); let chain_id = self.coin.id.as_str(); + println!("[EDIT] {chains_mod_path:?}"); let mut chains_mod_rs = FileContent::read(chains_mod_path)?; { diff --git a/codegen-v2/src/codegen/rust/coin_registry_manifest_generator.rs b/codegen-v2/src/codegen/rust/coin_registry_manifest_generator.rs index 6b0ff03b2f2..f76fed045db 100644 --- a/codegen-v2/src/codegen/rust/coin_registry_manifest_generator.rs +++ b/codegen-v2/src/codegen/rust/coin_registry_manifest_generator.rs @@ -15,6 +15,7 @@ pub struct CoinRegistryManifestGenerator; impl CoinRegistryManifestGenerator { pub fn add_dependency(coin: &CoinItem, path_to_new_blockchain_crate: &Path) -> Result<()> { let path_to_cargo_manifest = coin_registry_directory().join("Cargo.toml"); + println!("[EDIT] {path_to_cargo_manifest:?}"); Dependencies::new(path_to_cargo_manifest) .insert_dependency(&coin.id.to_tw_crate_name(), path_to_new_blockchain_crate) } diff --git a/codegen-v2/src/main.rs b/codegen-v2/src/main.rs index a45bb39d733..d708659a4c0 100644 --- a/codegen-v2/src/main.rs +++ b/codegen-v2/src/main.rs @@ -33,6 +33,7 @@ fn new_blockchain_rust(args: &[String]) -> Result<()> { let coin_id = CoinId::new(coin_str.clone())?; let coin_item = read_coin_from_registry(&coin_id)?; + println!("New Rust blockchain template for coin '{coin_str}' requested"); rust::new_blockchain::new_blockchain(&coin_item)?; Ok(()) @@ -43,6 +44,8 @@ fn new_blockchain(args: &[String]) -> Result<()> { let coin_id = CoinId::new(coin_str.clone())?; let coin_item = read_coin_from_registry(&coin_id)?; + println!("New '{coin_str}' blockchain template requested"); + proto::new_blockchain::new_blockchain(&coin_item)?; rust::new_blockchain::new_blockchain(&coin_item)?; cpp::new_blockchain::new_blockchain(&coin_item)?; @@ -55,6 +58,8 @@ fn new_evmchain(args: &[String]) -> Result<()> { let coin_id = CoinId::new(coin_str.clone())?; let coin_item = read_coin_from_registry(&coin_id)?; + println!("New '{coin_str}' EVM chain template requested"); + rust::new_evmchain::new_evmchain(&coin_item)?; cpp::new_evmchain::new_evmchain(&coin_item)?; diff --git a/codegen/lib/coin_skeleton_gen.rb b/codegen/lib/coin_skeleton_gen.rb index 9a12fca1e9e..b0bcbe85e80 100755 --- a/codegen/lib/coin_skeleton_gen.rb +++ b/codegen/lib/coin_skeleton_gen.rb @@ -117,6 +117,7 @@ def generate_mobile_tests(coin) end def generate_coin_type_tests(coin) + coin_test_gen = CoinTestGen.new() coin_test_gen.generate_coin_test_file(coin, 'TWCoinTypeTests.cpp.erb', true) end @@ -133,8 +134,6 @@ def generate_skeleton(coin_id, mode) @coins = coins - coin_test_gen = CoinTestGen.new() - # Find coin in list of coins, by Id coinSelect = coins.select {|c| c['id'] == coin_id} if coinSelect.length() == 0 From c94d86028abfb8ca2c22746983b493f0fa78f024 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Fri, 24 Nov 2023 20:39:42 +0100 Subject: [PATCH 020/128] [KMP]: Bump to 4.0.9 (#3574) * [KMP]: Bump to 4.0.9 * [Codegen]: Fix warnings --- codegen-v2/src/codegen/cpp/entry_generator.rs | 4 ++-- codegen-v2/src/codegen/rust/coin_crate.rs | 4 ++-- samples/kmp/shared/build.gradle.kts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/codegen-v2/src/codegen/cpp/entry_generator.rs b/codegen-v2/src/codegen/cpp/entry_generator.rs index f7c453b719d..49f341ab35f 100644 --- a/codegen-v2/src/codegen/cpp/entry_generator.rs +++ b/codegen-v2/src/codegen/cpp/entry_generator.rs @@ -7,9 +7,9 @@ use crate::codegen::cpp::cpp_source_directory; use crate::codegen::template_generator::TemplateGenerator; use crate::registry::CoinItem; -use crate::{Error, Result}; +use crate::Result; +use std::fs; use std::path::PathBuf; -use std::{fs, io}; const ENTRY_HEADER_TEMPLATE: &str = include_str!("templates/Entry.h"); diff --git a/codegen-v2/src/codegen/rust/coin_crate.rs b/codegen-v2/src/codegen/rust/coin_crate.rs index 330d84e26b2..4586f66ae3f 100644 --- a/codegen-v2/src/codegen/rust/coin_crate.rs +++ b/codegen-v2/src/codegen/rust/coin_crate.rs @@ -8,9 +8,9 @@ use crate::codegen::rust::chains_directory; use crate::codegen::template_generator::TemplateGenerator; use crate::coin_id::CoinId; use crate::registry::CoinItem; -use crate::{Error, Result}; +use crate::Result; +use std::fs; use std::path::PathBuf; -use std::{fs, io}; const BLOCKCHAIN_ADDRESS_TEMPLATE: &str = include_str!("templates/blockchain_crate/address.rs"); const BLOCKCHAIN_COMPILER_TEMPLATE: &str = include_str!("templates/blockchain_crate/compiler.rs"); diff --git a/samples/kmp/shared/build.gradle.kts b/samples/kmp/shared/build.gradle.kts index dc4b6fbb8b1..a56cc448990 100644 --- a/samples/kmp/shared/build.gradle.kts +++ b/samples/kmp/shared/build.gradle.kts @@ -35,7 +35,7 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation("com.trustwallet:wallet-core-kotlin:4.0.5") + implementation("com.trustwallet:wallet-core-kotlin:4.0.9") } } val commonTest by getting { From 296f9f37d2ad920119de27074ef421ca88f81135 Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Wed, 29 Nov 2023 17:14:29 -0500 Subject: [PATCH 021/128] feat(tomochain): rebrand to viction (#3578) --- .../blockchains/CoinAddressDerivationTests.kt | 2 +- .../core/app/blockchains/TestCoinType.kt | 2 +- docs/registry.md | 2 +- include/TrustWalletCore/TWCoinType.h | 2 +- .../core/test/CoinAddressDerivationTests.kt | 2 +- registry.json | 12 ++++---- .../tests/coin_address_derivation_test.rs | 2 +- swift/Tests/CoinAddressDerivationTests.swift | 2 +- swift/Tests/CoinTypeTests.swift | 2 +- .../TWCoinTypeTests.cpp | 30 +++++++++---------- tests/common/CoinAddressDerivationTests.cpp | 2 +- tests/interface/TWCoinTypeTests.cpp | 4 +-- tests/interface/TWHRPTests.cpp | 2 +- 13 files changed, 33 insertions(+), 33 deletions(-) rename tests/chains/{TomoChain => Viction}/TWCoinTypeTests.cpp (53%) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index 0b0b62d0834..7e44af34e2e 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -57,7 +57,7 @@ class CoinAddressDerivationTests { XRP -> assertEquals("rPwE3gChNKtZ1mhH3Ko8YFGqKmGRWLWXV3", address) TEZOS -> assertEquals("tz1acnY9VbMagps26Kj3RfoGRWD9nYG5qaRX", address) THUNDERCORE -> assertEquals("0x4b92b3ED6d8b24575Bf5ce4C6a86ED261DA0C8d7", address) - TOMOCHAIN -> assertEquals("0xC74b6D8897cBa9A4b659d43fEF73C9cA852cE424", address) + VICTION -> assertEquals("0xC74b6D8897cBa9A4b659d43fEF73C9cA852cE424", address) TRON -> assertEquals("TQ5NMqJjhpQGK7YJbESKtNCo86PJ89ujio", address) VECHAIN -> assertEquals("0x1a553275dF34195eAf23942CB7328AcF9d48c160", address) WANCHAIN -> assertEquals("0xD5ca90b928279FE5D06144136a25DeD90127aC15", address) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/TestCoinType.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/TestCoinType.kt index 5b92dd34849..d565a1a08be 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/TestCoinType.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/TestCoinType.kt @@ -32,7 +32,7 @@ class TestCoinType { assertEquals(CoinType.POANETWORK.value(), 178) assertEquals(CoinType.VECHAIN.value(), 818) assertEquals(CoinType.ICON.value(), 74) - assertEquals(CoinType.TOMOCHAIN.value(), 889) + assertEquals(CoinType.VICTION.value(), 889) assertEquals(CoinType.TEZOS.value(), 1729) assertEquals(CoinType.QTUM.value(), 2301) assertEquals(CoinType.NEBULAS.value(), 2718) diff --git a/docs/registry.md b/docs/registry.md index 74a8d9b829e..b49763ff960 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -71,7 +71,7 @@ This list is generated from [./registry.json](../registry.json) | 818 | VeChain | VET | | | | 820 | Callisto | CLO | | | | 888 | NEO | NEO | | | -| 889 | TomoChain | TOMO | | | +| 889 | Viction | VIC | | | | 899 | eCash | XEC | | | | 931 | THORChain | RUNE | | | | 966 | Polygon | MATIC | | | diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 56df414d348..6a09ac2f74f 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -72,7 +72,7 @@ enum TWCoinType { TWCoinTypeTheta = 500, TWCoinTypeThunderCore = 1001, TWCoinTypeNEO = 888, - TWCoinTypeTomoChain = 889, + TWCoinTypeViction = 889, TWCoinTypeTron = 195, TWCoinTypeVeChain = 818, TWCoinTypeViacoin = 14, diff --git a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt index 1cbfd41590e..cec9314a3be 100644 --- a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt +++ b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt @@ -53,7 +53,7 @@ class CoinAddressDerivationTests { XRP -> "rPwE3gChNKtZ1mhH3Ko8YFGqKmGRWLWXV3" Tezos -> "tz1acnY9VbMagps26Kj3RfoGRWD9nYG5qaRX" ThunderCore -> "0x4b92b3ED6d8b24575Bf5ce4C6a86ED261DA0C8d7" - TomoChain -> "0xC74b6D8897cBa9A4b659d43fEF73C9cA852cE424" + Viction -> "0xC74b6D8897cBa9A4b659d43fEF73C9cA852cE424" Tron -> "TQ5NMqJjhpQGK7YJbESKtNCo86PJ89ujio" VeChain -> "0x1a553275dF34195eAf23942CB7328AcF9d48c160" Wanchain -> "0xD5ca90b928279FE5D06144136a25DeD90127aC15" diff --git a/registry.json b/registry.json index 65e637c59b4..28031ea8d0e 100644 --- a/registry.json +++ b/registry.json @@ -2573,10 +2573,10 @@ } }, { - "id": "tomochain", - "name": "TomoChain", + "id": "viction", + "name": "Viction", "coinId": 889, - "symbol": "TOMO", + "symbol": "VIC", "decimals": 18, "blockchain": "Ethereum", "derivation": [ @@ -2589,15 +2589,15 @@ "chainId": "88", "addressHasher": "keccak256", "explorer": { - "url": "https://tomoscan.io", + "url": "https://www.vicscan.xyz", "txPath": "/tx/", "accountPath": "/address/", "sampleTx": "0x35a8d3ab06c94d5b7d27221b7c9a24ba3f1710dd0fcfd75c5d59b3a885fd709b", "sampleAccount": "0x86cCbD9bfb371c355202086882bC644A7D0b024B" }, "info": { - "url": "https://tomochain.com", - "source": "https://github.com/tomochain/tomochain", + "url": "https://www.viction.xyz/", + "source": "https://github.com/BuildOnViction/tomochain", "rpc": "https://rpc.tomochain.com", "documentation": "https://eth.wiki/json-rpc/API" } diff --git a/rust/tw_any_coin/tests/coin_address_derivation_test.rs b/rust/tw_any_coin/tests/coin_address_derivation_test.rs index 8320fd77d57..ef3eabd910b 100644 --- a/rust/tw_any_coin/tests/coin_address_derivation_test.rs +++ b/rust/tw_any_coin/tests/coin_address_derivation_test.rs @@ -73,7 +73,7 @@ fn test_coin_address_derivation() { | CoinType::Theta | CoinType::ThetaFuel | CoinType::ThunderCore - | CoinType::TomoChain + | CoinType::Viction | CoinType::VeChain | CoinType::Wanchain | CoinType::xDai diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index df0a6c09048..fdc68d10b43 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -224,7 +224,7 @@ class CoinAddressDerivationTests: XCTestCase { case .thunderCore: let expectedResult = "0x4b92b3ED6d8b24575Bf5ce4C6a86ED261DA0C8d7" assertCoinDerivation(coin, expectedResult, derivedAddress, address) - case .tomoChain: + case .viction: let expectedResult = "0xC74b6D8897cBa9A4b659d43fEF73C9cA852cE424" assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .tron: diff --git a/swift/Tests/CoinTypeTests.swift b/swift/Tests/CoinTypeTests.swift index 7ef5b5e2260..5021dcf0f32 100644 --- a/swift/Tests/CoinTypeTests.swift +++ b/swift/Tests/CoinTypeTests.swift @@ -23,7 +23,7 @@ class CoinTypeTests: XCTestCase { XCTAssertEqual(CoinType.poanetwork.rawValue, 178) XCTAssertEqual(CoinType.veChain.rawValue, 818) XCTAssertEqual(CoinType.icon.rawValue, 74) - XCTAssertEqual(CoinType.tomoChain.rawValue, 889) + XCTAssertEqual(CoinType.viction.rawValue, 889) XCTAssertEqual(CoinType.tezos.rawValue, 1729) XCTAssertEqual(CoinType.qtum.rawValue, 2301) XCTAssertEqual(CoinType.nebulas.rawValue, 2718) diff --git a/tests/chains/TomoChain/TWCoinTypeTests.cpp b/tests/chains/Viction/TWCoinTypeTests.cpp similarity index 53% rename from tests/chains/TomoChain/TWCoinTypeTests.cpp rename to tests/chains/Viction/TWCoinTypeTests.cpp index ad7780223cc..f138c29f6c9 100644 --- a/tests/chains/TomoChain/TWCoinTypeTests.cpp +++ b/tests/chains/Viction/TWCoinTypeTests.cpp @@ -13,22 +13,22 @@ #include -TEST(TWTomoChainCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeTomoChain)); +TEST(TWVictionType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeViction)); auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x35a8d3ab06c94d5b7d27221b7c9a24ba3f1710dd0fcfd75c5d59b3a885fd709b")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeTomoChain, txId.get())); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeViction, txId.get())); auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x86cCbD9bfb371c355202086882bC644A7D0b024B")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeTomoChain, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeTomoChain)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeTomoChain)); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeViction, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeViction)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeViction)); - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeTomoChain), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeTomoChain)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeTomoChain)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeTomoChain)); - assertStringsEqual(symbol, "TOMO"); - assertStringsEqual(txUrl, "https://tomoscan.io/tx/0x35a8d3ab06c94d5b7d27221b7c9a24ba3f1710dd0fcfd75c5d59b3a885fd709b"); - assertStringsEqual(accUrl, "https://tomoscan.io/address/0x86cCbD9bfb371c355202086882bC644A7D0b024B"); - assertStringsEqual(id, "tomochain"); - assertStringsEqual(name, "TomoChain"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeViction), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeViction)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeViction)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeViction)); + assertStringsEqual(symbol, "VIC"); + assertStringsEqual(txUrl, "https://www.vicscan.xyz/tx/0x35a8d3ab06c94d5b7d27221b7c9a24ba3f1710dd0fcfd75c5d59b3a885fd709b"); + assertStringsEqual(accUrl, "https://www.vicscan.xyz/address/0x86cCbD9bfb371c355202086882bC644A7D0b024B"); + assertStringsEqual(id, "viction"); + assertStringsEqual(name, "Viction"); } diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index f9b6c28e46b..da1bd05e983 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -71,7 +71,7 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeTheta: case TWCoinTypeThetaFuel: case TWCoinTypeThunderCore: - case TWCoinTypeTomoChain: + case TWCoinTypeViction: case TWCoinTypeVeChain: case TWCoinTypeWanchain: case TWCoinTypeXDai: diff --git a/tests/interface/TWCoinTypeTests.cpp b/tests/interface/TWCoinTypeTests.cpp index fa80b967903..83cecb15c9a 100644 --- a/tests/interface/TWCoinTypeTests.cpp +++ b/tests/interface/TWCoinTypeTests.cpp @@ -57,7 +57,7 @@ TEST(TWCoinType, TWPurpose) { ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeTezos)); ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeTheta)); ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeThunderCore)); - ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeTomoChain)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeViction)); ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeTron)); ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeVeChain)); ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeWanchain)); @@ -130,7 +130,7 @@ TEST(TWCoinType, TWPublicKeyType) { ASSERT_EQ(TWPublicKeyTypeED25519, TWCoinTypePublicKeyType(TWCoinTypeTezos)); ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeTheta)); ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeThunderCore)); - ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeTomoChain)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeViction)); ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeTron)); ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeVeChain)); ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeWanchain)); diff --git a/tests/interface/TWHRPTests.cpp b/tests/interface/TWHRPTests.cpp index 279ccd82b15..8c6a38ba969 100644 --- a/tests/interface/TWHRPTests.cpp +++ b/tests/interface/TWHRPTests.cpp @@ -113,7 +113,7 @@ TEST(TWHPR, HPRByCoinType) { ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeTezos)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeTheta)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeThunderCore)); - ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeTomoChain)); + ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeViction)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeTron)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeVeChain)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeWanchain)); From 230e7ab2348c5652e78f2f942faaec7a5d0a8506 Mon Sep 17 00:00:00 2001 From: Riku Date: Fri, 8 Dec 2023 12:53:37 +0100 Subject: [PATCH 022/128] [XRP] Add support for Escrow transactions (#3572) * add XRP escrow proto defs * XRP escrow transaction encoding * update XRP signer * XRP escrow transaction tests * add XRP mainnet escrow transaction tests * [KMP]: Bump to 4.0.10 --------- Co-authored-by: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> --- samples/kmp/shared/build.gradle.kts | 2 +- src/XRP/Signer.cpp | 72 +++++++ src/XRP/Transaction.cpp | 53 ++++- src/XRP/Transaction.h | 42 ++++ src/proto/Ripple.proto | 52 +++++ tests/chains/XRP/TWAnySignerTests.cpp | 274 ++++++++++++++++++++++++++ 6 files changed, 491 insertions(+), 4 deletions(-) diff --git a/samples/kmp/shared/build.gradle.kts b/samples/kmp/shared/build.gradle.kts index a56cc448990..d7ba445c535 100644 --- a/samples/kmp/shared/build.gradle.kts +++ b/samples/kmp/shared/build.gradle.kts @@ -35,7 +35,7 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation("com.trustwallet:wallet-core-kotlin:4.0.9") + implementation("com.trustwallet:wallet-core-kotlin:4.0.10") } } val commonTest by getting { diff --git a/src/XRP/Signer.cpp b/src/XRP/Signer.cpp index c8b3e018585..c60afac81eb 100644 --- a/src/XRP/Signer.cpp +++ b/src/XRP/Signer.cpp @@ -26,6 +26,30 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { signPayment(input, output, transaction); break; + case Proto::SigningInput::kOpEscrowCreate: + transaction.createEscrowCreate( + input.op_escrow_create().amount(), + input.op_escrow_create().destination(), + input.op_escrow_create().destination_tag(), + input.op_escrow_create().cancel_after(), + input.op_escrow_create().finish_after(), + input.op_escrow_create().condition()); + break; + + case Proto::SigningInput::kOpEscrowCancel: + transaction.createEscrowCancel( + input.op_escrow_cancel().owner(), + input.op_escrow_cancel().offer_sequence()); + break; + + case Proto::SigningInput::kOpEscrowFinish: + transaction.createEscrowFinish( + input.op_escrow_finish().owner(), + input.op_escrow_finish().offer_sequence(), + input.op_escrow_finish().condition(), + input.op_escrow_finish().fulfillment()); + break; + case Proto::SigningInput::kOpNftokenBurn: transaction.createNFTokenBurn(input.op_nftoken_burn().nftoken_id()); break; @@ -94,6 +118,30 @@ TW::Data Signer::preImage() const { signPayment(input, output, transaction); break; + case Proto::SigningInput::kOpEscrowCreate: + transaction.createEscrowCreate( + input.op_escrow_create().amount(), + input.op_escrow_create().destination(), + input.op_escrow_create().destination_tag(), + input.op_escrow_create().cancel_after(), + input.op_escrow_create().finish_after(), + input.op_escrow_create().condition()); + break; + + case Proto::SigningInput::kOpEscrowCancel: + transaction.createEscrowCancel( + input.op_escrow_cancel().owner(), + input.op_escrow_cancel().offer_sequence()); + break; + + case Proto::SigningInput::kOpEscrowFinish: + transaction.createEscrowFinish( + input.op_escrow_finish().owner(), + input.op_escrow_finish().offer_sequence(), + input.op_escrow_finish().condition(), + input.op_escrow_finish().fulfillment()); + break; + case Proto::SigningInput::kOpNftokenBurn: transaction.createNFTokenBurn(input.op_nftoken_burn().nftoken_id()); break; @@ -149,6 +197,30 @@ Proto::SigningOutput Signer::compile(const Data& signature, const PublicKey& pub signPayment(input, output, transaction); break; + case Proto::SigningInput::kOpEscrowCreate: + transaction.createEscrowCreate( + input.op_escrow_create().amount(), + input.op_escrow_create().destination(), + input.op_escrow_create().destination_tag(), + input.op_escrow_create().cancel_after(), + input.op_escrow_create().finish_after(), + input.op_escrow_create().condition()); + break; + + case Proto::SigningInput::kOpEscrowCancel: + transaction.createEscrowCancel( + input.op_escrow_cancel().owner(), + input.op_escrow_cancel().offer_sequence()); + break; + + case Proto::SigningInput::kOpEscrowFinish: + transaction.createEscrowFinish( + input.op_escrow_finish().owner(), + input.op_escrow_finish().offer_sequence(), + input.op_escrow_finish().condition(), + input.op_escrow_finish().fulfillment()); + break; + case Proto::SigningInput::kOpNftokenBurn: transaction.createNFTokenBurn(input.op_nftoken_burn().nftoken_id()); break; diff --git a/src/XRP/Transaction.cpp b/src/XRP/Transaction.cpp index f608fa962e0..52dbc3b0cff 100644 --- a/src/XRP/Transaction.cpp +++ b/src/XRP/Transaction.cpp @@ -22,7 +22,9 @@ Data Transaction::serialize() const { auto data = Data(); - /// field must be sorted by field type then by field name + /// fields must be sorted by field type code then by field code (key) + // https://xrpl.org/serialization.html#canonical-field-order + /// "type" encodeType(FieldType::int16, 2, data); encode16BE(uint16_t(transaction_type), data); @@ -36,17 +38,37 @@ Data Transaction::serialize() const { encode32BE(sequence, data); /// "destinationTag" - if ((transaction_type == TransactionType::payment) && encode_tag) { + if (((transaction_type == TransactionType::payment) || + (transaction_type == TransactionType::EscrowCreate)) && encode_tag) { encodeType(FieldType::int32, 14, data); encode32BE(static_cast(destination_tag), data); } + /// "OfferSequence" + if ((transaction_type == TransactionType::EscrowCancel) || + (transaction_type == TransactionType::EscrowFinish)) { + encodeType(FieldType::int32, 25, data); + encode32BE(offer_sequence, data); + } + /// "lastLedgerSequence" if (last_ledger_sequence > 0) { encodeType(FieldType::int32, 27, data); encode32BE(last_ledger_sequence, data); } + /// "CancelAfter" + if ((transaction_type == TransactionType::EscrowCreate) && cancel_after > 0) { + encodeType(FieldType::int32, 36, data); + encode32BE(static_cast(cancel_after), data); + } + + /// "FinishAfter" + if ((transaction_type == TransactionType::EscrowCreate) && finish_after > 0) { + encodeType(FieldType::int32, 37, data); + encode32BE(static_cast(finish_after), data); + } + /// "NFTokenId" if ((transaction_type == TransactionType::NFTokenCreateOffer) || (transaction_type == TransactionType::NFTokenBurn)) { @@ -71,6 +93,9 @@ Data Transaction::serialize() const { } else if (transaction_type == TransactionType::TrustSet) { encodeType(FieldType::amount, 3, data); append(data, serializeCurrencyAmount(limit_amount)); + } else if (transaction_type == TransactionType::EscrowCreate) { + encodeType(FieldType::amount, 1, data); + append(data, serializeAmount(amount)); } /// "fee" @@ -82,23 +107,45 @@ Data Transaction::serialize() const { encodeType(FieldType::vl, 3, data); encodeBytes(pub_key, data); } + /// "txnSignature" if (!signature.empty()) { encodeType(FieldType::vl, 4, data); encodeBytes(signature, data); } + /// "Fulfillment" + if ((transaction_type == TransactionType::EscrowFinish) && !fulfillment.empty()) { + encodeType(FieldType::vl, 16, data); + encodeBytes(fulfillment, data); + } + + /// "Condition" + if (((transaction_type == TransactionType::EscrowCreate) || + (transaction_type == TransactionType::EscrowFinish)) && !condition.empty()) { + encodeType(FieldType::vl, 17, data); + encodeBytes(condition, data); + } + /// "account" encodeType(FieldType::account, 1, data); encodeBytes(serializeAddress(account), data); /// "destination" if ((transaction_type == TransactionType::payment) || - (transaction_type == TransactionType::NFTokenCreateOffer)) { + (transaction_type == TransactionType::NFTokenCreateOffer) || + (transaction_type == TransactionType::EscrowCreate)) { encodeType(FieldType::account, 3, data); encodeBytes(destination, data); } + /// "Owner" + if ((transaction_type == TransactionType::EscrowCancel) || + (transaction_type == TransactionType::EscrowFinish)) { + encodeType(FieldType::account, 2, data); + encodeBytes(owner, data); + } + /// "NFTokenOffers" if (transaction_type == TransactionType::NFTokenCancelOffer) { // only support one offer diff --git a/src/XRP/Transaction.h b/src/XRP/Transaction.h index 563fc9fd78d..3058a8c856d 100644 --- a/src/XRP/Transaction.h +++ b/src/XRP/Transaction.h @@ -29,6 +29,9 @@ enum class FieldType: int { enum class TransactionType { no_type = -1, payment = 0, + EscrowCreate = 1, + EscrowFinish = 2, + EscrowCancel = 4, TrustSet = 20, NFTokenBurn = 26, NFTokenCreateOffer = 27, @@ -64,6 +67,12 @@ class Transaction { int64_t destination_tag; Data pub_key; Data signature; + int64_t cancel_after; + int64_t finish_after; + Data owner; + int32_t offer_sequence; + Data condition; + Data fulfillment; Data nftoken_id; Data sell_offer; Data token_offers; @@ -78,6 +87,9 @@ class Transaction { , account(p_account) , encode_tag(false) , destination_tag(0) + , cancel_after(0) + , finish_after(0) + , offer_sequence(0) , nftoken_id(0) , sell_offer(0) , token_offers(0) @@ -108,6 +120,36 @@ class Transaction { setCurrencyAmount(currency_amount, currency, value, issuer); } + void createEscrowCreate(int64_t amount, const std::string& destination, int64_t destination_tag, + int64_t cancel_after, int64_t finish_after, const std::string& condition) { + transaction_type = TransactionType::EscrowCreate; + if (cancel_after == 0 && finish_after == 0) { + throw std::invalid_argument("Either CancelAfter or FinishAfter must be specified"); + } else if (finish_after == 0 && condition.length() == 0) { + throw std::invalid_argument("Either Condition or FinishAfter must be specified"); + } + this->amount = amount; + setDestination(destination, destination_tag); + this->cancel_after = cancel_after; + this->finish_after = finish_after; + this->condition = parse_hex(condition); + } + + void createEscrowCancel(const std::string& owner, int32_t offer_sequence) { + transaction_type = TransactionType::EscrowCancel; + setAccount(owner, this->owner); + this->offer_sequence = offer_sequence; + } + + void createEscrowFinish(const std::string& owner, int32_t offer_sequence, + const std::string& condition, const std::string& fulfillment) { + transaction_type = TransactionType::EscrowFinish; + setAccount(owner, this->owner); + this->offer_sequence = offer_sequence; + this->condition = parse_hex(condition); + this->fulfillment = parse_hex(fulfillment); + } + void createNFTokenBurn(const std::string& p_nftoken_id) { transaction_type = TransactionType::NFTokenBurn; nftoken_id = parse_hex(p_nftoken_id); diff --git a/src/proto/Ripple.proto b/src/proto/Ripple.proto index 8423e56fc97..d32b58e25c9 100644 --- a/src/proto/Ripple.proto +++ b/src/proto/Ripple.proto @@ -40,6 +40,52 @@ message OperationPayment { int64 destination_tag = 4; } +// https://xrpl.org/escrowcreate.html +message OperationEscrowCreate { + // Escrow amount + int64 amount = 1; + + // Beneficiary account + string destination = 2; + + // Destination Tag + int64 destination_tag = 3; + + // Escrow expire time + int64 cancel_after = 4; + + // Escrow release time + int64 finish_after = 5; + + // Crypto condition + // https://datatracker.ietf.org/doc/html/draft-thomas-crypto-conditions-02#section-8.1 + string condition = 6; +} + +// https://xrpl.org/escrowcancel.html +message OperationEscrowCancel { + // Funding account + string owner = 1; + + // Escrow transaction sequence + int32 offer_sequence = 2; +} + +// https://xrpl.org/escrowfinish.html +message OperationEscrowFinish { + // Funding account + string owner = 1; + + // Escrow transaction sequence + int32 offer_sequence = 2; + + // Crypto condition + string condition = 3; + + // Fulfillment matching condition + string fulfillment = 4; +} + // https://xrpl.org/nftokenburn.html message OperationNFTokenBurn { // Hash256 NFTokenId @@ -99,6 +145,12 @@ message SigningInput { OperationNFTokenAcceptOffer op_nftoken_accept_offer = 11; OperationNFTokenCancelOffer op_nftoken_cancel_offer = 12; + + OperationEscrowCreate op_escrow_create = 16; + + OperationEscrowCancel op_escrow_cancel = 17; + + OperationEscrowFinish op_escrow_finish = 18; } // Only used by tss chain-integration. diff --git a/tests/chains/XRP/TWAnySignerTests.cpp b/tests/chains/XRP/TWAnySignerTests.cpp index 6b55b2976b3..a6753435329 100644 --- a/tests/chains/XRP/TWAnySignerTests.cpp +++ b/tests/chains/XRP/TWAnySignerTests.cpp @@ -121,6 +121,280 @@ TEST(TWAnySignerRipple, SignTokenPayment1) { EXPECT_EQ(hex(output.encoded()), "12000022000000002401ec61e0201b01ec61f561d48a68d1c931200000000000000000000000000055534400000000004b4e9c06f24296074f7bc48f92a97916c6dc5ea968400000000000000a73210348c331ab218ba964150490c83875b06ccad2100b1f5707f296764712738cf1ca74473045022100a938783258d33e2e3e6099d1ab68fd85c3fd21adfa00e136a67bed8fddec6c9a02206cc6784c1f212f19dc939207643d361ceaa8334eb366722cf33b24dc7669dd7a81143a2f2f189d05abb8519cc9dee0e2dbc6fa53924183148132e4e20aecf29090ac428a9c43f230a829220d"); } +TEST(TWAnySignerRipple, SignEscrowCreateMain) { + // https://xrpscan.com/tx/3576E5D413CBDC228D13F281BB66304C1EE9DDEAA5563F1783EDB1848266D739 + auto key = parse_hex("a3cf20a85b25be4c955f0814718cc7a02eae9195159bd72ede5dd5c4e60d22c4"); + Proto::SigningInput input; + + // with finish after and dest tag + input.mutable_op_escrow_create()->set_amount(21300); + input.mutable_op_escrow_create()->set_destination("rEeSXUWEYyEADhDHvi3mtahkFVn7dYNH2G"); + input.mutable_op_escrow_create()->set_destination_tag(67); + input.mutable_op_escrow_create()->set_cancel_after(755015907); + input.mutable_op_escrow_create()->set_finish_after(755015897); + input.mutable_op_escrow_create()->set_condition(""); + + input.set_fee(12); + input.set_sequence(84363229); + input.set_last_ledger_sequence(84363920); + input.set_account("rnXwGtLDXXcV63CnRoNaesSsJCZZkJwo9w"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "120001220000000024050747dd2e00000043201b05074a9020242d00a0e320252d00a0d961400000000000533468400000000000000c7321029b557f4db390d68d39d3457204c225d4a68ed86854567a1da99d3e2cd640717374473045022100e62d5005401f1d2b1d9eaa42e0fdbb8b8a433d0cfe71455e782882aa6ab0656f02207b589489b4f344e87a956382e5ede6a55fbfc7e38701364c1fe7d056e9a3253a81143194b932f389b95922fba31662f3c8a606fedfd68314a0a67483ad4d51b2524eb304c0fcef6b2025b865"); +} + +TEST(TWAnySignerRipple, SignEscrowCreate) { + // https://testnet.xrpl.org/transactions/3F581927C742D5FAE65FB0759D0F04EF3B64B4A087911B07975816ECCB59915B + auto key = parse_hex("f157cf7951908b9a2b28d6c5817a3212c3971d8c05a1e964bbafaa5ad7529cb0"); + Proto::SigningInput input; + + // with finish after and dest tag + input.mutable_op_escrow_create()->set_amount(345941506); + input.mutable_op_escrow_create()->set_destination("rNS1tYfynXoKC3eX52gvVnSyU9mqWXvCgh"); + input.mutable_op_escrow_create()->set_destination_tag(2467); + input.mutable_op_escrow_create()->set_cancel_after(0); + input.mutable_op_escrow_create()->set_finish_after(750095491); + input.mutable_op_escrow_create()->set_condition(""); + + input.set_fee(12); + input.set_sequence(41874843); + input.set_last_ledger_sequence(41874865); + input.set_account("rL6iE1bbAHekMavpGot6gRxqkQKm6yfoQ6"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "120001220000000024027ef59b2e000009a3201b027ef5b120252cb58c836140000000149ea60268400000000000000c7321021846a49ea81238d03dff5a89a9da82eb06b23a276af9a06b45d4aba39713311f744630440220176318f29d2b815f599072230690397f91262c1f801bafada9820d89c719359c0220756eb74d815e20e86f6748c6821d3204f93221a95b4481a572a10530f5776c698114d8242542e6108fccf75a7f5bb0059cfae6d155378314937e838cb1033342c72acfae58fe2e3875ce7693"); +} + +TEST(TWAnySignerRipple, SignEscrowCreate2) { + // https://testnet.xrpl.org/transactions/3F581927C742D5FAE65FB0759D0F04EF3B64B4A087911B07975816ECCB59915B + auto key = parse_hex("8b488ed9b9875174140a97cad53cd8c652789889612f94a9006b7ced18a1c6ef"); + Proto::SigningInput input; + + // with cancel after > 0x7fffffff + input.mutable_op_escrow_create()->set_amount(88941506); + input.mutable_op_escrow_create()->set_destination("rfC73DuBhDqF3Zw1K3uxaQNCkwT8pPKyf5"); + input.mutable_op_escrow_create()->set_destination_tag(0); + input.mutable_op_escrow_create()->set_cancel_after(2147483648); + input.mutable_op_escrow_create()->set_finish_after(750097108); + input.mutable_op_escrow_create()->set_condition(""); + + input.set_fee(12); + input.set_sequence(41875372); + input.set_last_ledger_sequence(41875394); + input.set_account("rEE4PdEYhEikJ1bvQjdE9HdjBV8yp8FsGC"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "120001220000000024027ef7ac201b027ef7c220248000000020252cb592d46140000000054d23c268400000000000000c73210211cfeb81bc410e694e98c6a0f17c9c89d85e2b89bc17d2699063c0920217ab0574463044022038d27cd842422d8ee72d5cab11734ce128aef21d7cec17654d21c27d0556d23e0220059f913178a4c65a5d3289896876989e0fcaf3add9769459fb232ab94398368a81149c4970a2b763b9484e3b65d67f3d9b7b1698cb7f83144917342345fbe5cef1e22d3f1353fc468bf696ac"); +} + +TEST(TWAnySignerRipple, SignEscrowCreateWithConditionMain) { + // https://xrpscan.com/tx/77E01FD30A788BFC96F28960F099D4076255252F33FCD31EEBBCBB61E3318544 + auto key = parse_hex("a3cf20a85b25be4c955f0814718cc7a02eae9195159bd72ede5dd5c4e60d22c4"); + Proto::SigningInput input; + + // with cancel after and crypto condition + input.mutable_op_escrow_create()->set_amount(37000); + input.mutable_op_escrow_create()->set_destination("rEeSXUWEYyEADhDHvi3mtahkFVn7dYNH2G"); + input.mutable_op_escrow_create()->set_destination_tag(0); + input.mutable_op_escrow_create()->set_cancel_after(755014300); + input.mutable_op_escrow_create()->set_finish_after(0); + input.mutable_op_escrow_create()->set_condition("a0258020c26add2db64dd6d5700a5e2721c1e908d599901627b8dc82f25b3e035ec4004b810120"); // PREIMAGE-SHA-256 crypto-condition of secret 5d729ac237c4c7976403817b6409be7190efbfad49af2cf974b9582a854e8794 + + input.set_fee(12); + input.set_sequence(84363226); + input.set_last_ledger_sequence(84363509); + input.set_account("rnXwGtLDXXcV63CnRoNaesSsJCZZkJwo9w"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "120001220000000024050747da201b050748f520242d009a9c61400000000000908868400000000000000c7321029b557f4db390d68d39d3457204c225d4a68ed86854567a1da99d3e2cd6407173744630440220307f4c91e91166db1428eb1ab8f65a84bd9b89542ed844045ffd040f5e13d12b022061120350b9685381e9941c7ec54ce154ca0ef0d01f630aeb3e78dd9fd087ff80701127a0258020c26add2db64dd6d5700a5e2721c1e908d599901627b8dc82f25b3e035ec4004b81012081143194b932f389b95922fba31662f3c8a606fedfd68314a0a67483ad4d51b2524eb304c0fcef6b2025b865"); +} + +TEST(TWAnySignerRipple, SignEscrowCreateWithCondition) { + // https://testnet.xrpl.org/transactions/A8EE35E26CD09E3D6A415DDEFEA6723CA5AFEB1838C5FE06835937FA49DEF3A0 + auto key = parse_hex("a3cf20a85b25be4c955f0814718cc7a02eae9195159bd72ede5dd5c4e60d22c4"); + Proto::SigningInput input; + + // with cancel after and crypto condition + input.mutable_op_escrow_create()->set_amount(30941506); + input.mutable_op_escrow_create()->set_destination("rEeSXUWEYyEADhDHvi3mtahkFVn7dYNH2G"); + input.mutable_op_escrow_create()->set_destination_tag(0); + input.mutable_op_escrow_create()->set_cancel_after(750090371); + input.mutable_op_escrow_create()->set_finish_after(0); + input.mutable_op_escrow_create()->set_condition("a0258020b3dda5c580919ce0fd6acdf013c337461951946e54b41446467961568cdd9e7b810120"); // PREIMAGE-SHA-256 crypto-condition of secret b3dda5c580919ce0fd6acdf013c337461951946e54b41446467961568cdd9e7b + + input.set_fee(12); + input.set_sequence(41872968); + input.set_last_ledger_sequence(41873012); + input.set_account("rnXwGtLDXXcV63CnRoNaesSsJCZZkJwo9w"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "120001220000000024027eee48201b027eee7420242cb57883614000000001d8214268400000000000000c7321029b557f4db390d68d39d3457204c225d4a68ed86854567a1da99d3e2cd640717374473045022100931b3a6634471fa22f709417d7280b76564a8f3a700cf51a50a2c1b1e0162d570220217c0f2e3922e9bc5b2175712c0e244f2f05bf42ccd1e632b06476f66704203f701127a0258020b3dda5c580919ce0fd6acdf013c337461951946e54b41446467961568cdd9e7b81012081143194b932f389b95922fba31662f3c8a606fedfd68314a0a67483ad4d51b2524eb304c0fcef6b2025b865"); +} + +TEST(TWAnySignerRipple, SignEscrowCreateWithCondition2) { + // https://testnet.xrpl.org/transactions/25AE9F7CBC9944B140A4BE338A47DD8C2C29313B44694533D9D47CD758A60A8F + auto key = parse_hex("be60f33cbeb2b5ee688dcb1e93986f2522d8ad76b3c48398bf2be02a6699e781"); + Proto::SigningInput input; + + // with cancel after, crypto condition and dest tag + input.mutable_op_escrow_create()->set_amount(28941506); + input.mutable_op_escrow_create()->set_destination("r9YD31TAtbS8EPwEt2gzGDjsaMDyV1s5QE"); + input.mutable_op_escrow_create()->set_destination_tag(2467); + input.mutable_op_escrow_create()->set_cancel_after(750094604); + input.mutable_op_escrow_create()->set_finish_after(0); + input.mutable_op_escrow_create()->set_condition("a0258020ffecf1ae6182f10efebe0c0896cd6b044df7b27d33b05030033ef63d47e2b250810120"); // PREIMAGE-SHA-256 crypto-condition of secret ffecf1ae6182f10efebe0c0896cd6b044df7b27d33b05030033ef63d47e2b250 + + input.set_fee(12); + input.set_sequence(41874370); + input.set_last_ledger_sequence(41874392); + input.set_account("rpLGh11T9B6b4UjAU1WRCJowLw8uk7vS44"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "120001220000000024027ef3c22e000009a3201b027ef3d820242cb5890c614000000001b99cc268400000000000000c7321035e6cd73289f9b1a796fba572f7a2732aae23b2a9ea6b0ec239d5b9feb388774074473045022100c4bb3b65acd5d30aa8f85ea2a0d2c0e18d2025a005a827722059a9a636eb1bca02207d73b4a64d679e605a6cb31881d7ea3642c1e54e3bf38d13d0dd4219c27d1420701127a0258020ffecf1ae6182f10efebe0c0896cd6b044df7b27d33b05030033ef63d47e2b25081012081140e9c9b31b826671aaa387555cdeccab82a78402083145da8080d21fecf98f24ea2223482e5d24f107799"); +} + +TEST(TWAnySignerRipple, SignEscrowCancelMain) { + // https://xrpscan.com/tx/949B3C3D8B4528C95D07654BBA10B08ABA65FFD339E31706BC93CB0824427F97 + auto key = parse_hex("a3cf20a85b25be4c955f0814718cc7a02eae9195159bd72ede5dd5c4e60d22c4"); + Proto::SigningInput input; + + input.mutable_op_escrow_cancel()->set_owner("rnXwGtLDXXcV63CnRoNaesSsJCZZkJwo9w"); + input.mutable_op_escrow_cancel()->set_offer_sequence(84363227); + + input.set_fee(12); + input.set_sequence(84363228); + input.set_last_ledger_sequence(84363740); + input.set_account("rnXwGtLDXXcV63CnRoNaesSsJCZZkJwo9w"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "120004220000000024050747dc2019050747db201b050749dc68400000000000000c7321029b557f4db390d68d39d3457204c225d4a68ed86854567a1da99d3e2cd64071737446304402202c0416934dbf3a0c42d0b0da9e893cec69e42c81f41424f4a388c3ba8862e65a02201781e22ef85b251902e918f6d923769993757a79b865a62ecdebc1a015368f1f81143194b932f389b95922fba31662f3c8a606fedfd682143194b932f389b95922fba31662f3c8a606fedfd6"); +} + +TEST(TWAnySignerRipple, SignEscrowCancel) { + // https://testnet.xrpl.org/transactions/5B0F8766FFBDE7D3A9ACAA63361BF00FE0739DC8718507776EB2C1AD980BC965 + auto key = parse_hex("bf9810cc4f7cc5e6dea8a0c29f3389d9d511e795d467b402a870e71d93243705"); + Proto::SigningInput input; + + input.mutable_op_escrow_cancel()->set_owner("rE16pf2ZQUZBDLKAyTFF9Q1b3YY1nc7v2J"); + input.mutable_op_escrow_cancel()->set_offer_sequence(41875229); + + input.set_fee(12); + input.set_sequence(41875230); + input.set_last_ledger_sequence(41875263); + input.set_account("rE16pf2ZQUZBDLKAyTFF9Q1b3YY1nc7v2J"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "120004220000000024027ef71e2019027ef71d201b027ef73f68400000000000000c73210277314966f72e9520199faa3941bd45b89e444f7eabf203e805527f880de80b8674473045022100ec04d05db5725ce154a511f93056fde0b825b7e0bb4a59b4d4264a008eafdcfe0220676f30f916c6ea0644c11c0bcafcfa8209a083041742d672a726a5c8d99230ea8114a327f724d30f2732f78a4ec6744db298e827ba2b8214a327f724d30f2732f78a4ec6744db298e827ba2b"); +} + +TEST(TWAnySignerRipple, SignEscrowFinishMain) { + // https://xrpscan.com/tx/F015FB9E893877289E3058F14DD2FAA93D7F1E44AC2C7F71E684BD65B94EEB59 + auto key = parse_hex("f60edca8e4bb25f9916017c9c7fe93e633b800550f86cf305a2e99271d7cede5"); + Proto::SigningInput input; + + input.mutable_op_escrow_finish()->set_owner("rnXwGtLDXXcV63CnRoNaesSsJCZZkJwo9w"); + input.mutable_op_escrow_finish()->set_offer_sequence(84363235); + + input.set_fee(12); + input.set_sequence(84363475); + input.set_last_ledger_sequence(84364395); + input.set_account("rEeSXUWEYyEADhDHvi3mtahkFVn7dYNH2G"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "120002220000000024050748d32019050747e3201b05074c6b68400000000000000c7321025a4c754a3f836ebe18520e7d3861c6e38a4adfe466465d5db6cbb2d745d27ee574473045022100df5a22c475fa039d8fd7f1dd9a3b248e2f11232bf23ae0206b79b6ac3014a80e02202df2da6d98ed9ced91b9790a3d84f28c2aeb368e3cde84806926086d74d406c18114a0a67483ad4d51b2524eb304c0fcef6b2025b86582143194b932f389b95922fba31662f3c8a606fedfd6"); +} + +TEST(TWAnySignerRipple, SignEscrowFinish) { + // https://testnet.xrpl.org/transactions/690E04E97761E3E5F33A9FF3DA42C16E8E234043850DA294BB3FE38CAE551E71 + auto key = parse_hex("a6e306206d400dcc4a2d00e70b4a3925d511b2dabc1a85f4ffbf174a334e28e6"); + Proto::SigningInput input; + + input.mutable_op_escrow_finish()->set_owner("rL6iE1bbAHekMavpGot6gRxqkQKm6yfoQ6"); + input.mutable_op_escrow_finish()->set_offer_sequence(41874843); + + input.set_fee(12); + input.set_sequence(41874845); + input.set_last_ledger_sequence(41874877); + input.set_account("rNS1tYfynXoKC3eX52gvVnSyU9mqWXvCgh"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "120002220000000024027ef59d2019027ef59b201b027ef5bd68400000000000000c7321026f8adad2b4071daa02916f8759ff148fad37c1562e48e71bb608d896d1c833cb74473045022100a7f06325574a9c4300725cb069029645b94d67217e5ae15a2e20bc0387e32aaf02206f9f1a7ae4aaccf2f4c2ab8d90f868e786a285aae677e0b01507eca5dd6823818114937e838cb1033342c72acfae58fe2e3875ce76938214d8242542e6108fccf75a7f5bb0059cfae6d15537"); +} + +TEST(TWAnySignerRipple, SignEscrowFinishWithConditionMain) { + // https://xrpscan.com/tx/7E9AC2C8286E3EC0410784920A0F8048C79257EDF19B392F98A31F62E3CF4FAD + auto key = parse_hex("f60edca8e4bb25f9916017c9c7fe93e633b800550f86cf305a2e99271d7cede5"); + Proto::SigningInput input; + + input.mutable_op_escrow_finish()->set_owner("rnXwGtLDXXcV63CnRoNaesSsJCZZkJwo9w"); + input.mutable_op_escrow_finish()->set_offer_sequence(84363226); + input.mutable_op_escrow_finish()->set_condition("a0258020c26add2db64dd6d5700a5e2721c1e908d599901627b8dc82f25b3e035ec4004b810120"); + input.mutable_op_escrow_finish()->set_fulfillment("a02280205d729ac237c4c7976403817b6409be7190efbfad49af2cf974b9582a854e8794"); + + input.set_fee(423); + input.set_sequence(84363473); + input.set_last_ledger_sequence(84363511); + input.set_account("rEeSXUWEYyEADhDHvi3mtahkFVn7dYNH2G"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "120002220000000024050748d12019050747da201b050748f76840000000000001a77321025a4c754a3f836ebe18520e7d3861c6e38a4adfe466465d5db6cbb2d745d27ee574473045022100f06a6ac18efc1280f9d26cbb47c31f7ecd72ed200f9d05c6c762ffcf18b53534022049c6bb4ac8e79c478939a55dd1cb571d56ec929e5200332e410fd69c0fe1ef48701024a02280205d729ac237c4c7976403817b6409be7190efbfad49af2cf974b9582a854e8794701127a0258020c26add2db64dd6d5700a5e2721c1e908d599901627b8dc82f25b3e035ec4004b8101208114a0a67483ad4d51b2524eb304c0fcef6b2025b86582143194b932f389b95922fba31662f3c8a606fedfd6"); +} + +TEST(TWAnySignerRipple, SignEscrowFinishWithCondition) { + // https://testnet.xrpl.org/transactions/4A49D4AD05FBDC4A354E31C7453829509F59DD2B51CDE560C4350155F5DBFD86 + auto key = parse_hex("4bae9219cf9c58e5db0c395900085f07fc06709e1b2223ccac40191fbcbdab2a"); + Proto::SigningInput input; + + input.mutable_op_escrow_finish()->set_owner("rpLGh11T9B6b4UjAU1WRCJowLw8uk7vS44"); + input.mutable_op_escrow_finish()->set_offer_sequence(41874370); + input.mutable_op_escrow_finish()->set_condition("a0258020ffecf1ae6182f10efebe0c0896cd6b044df7b27d33b05030033ef63d47e2b250810120"); + input.mutable_op_escrow_finish()->set_fulfillment("a022802049b9ab20ca85b55d0c12b948ec7c524f843c77be1ef1561a42b7167dce174b7a"); + + input.set_fee(423); + input.set_sequence(41874372); + input.set_last_ledger_sequence(41874394); + input.set_account("r9YD31TAtbS8EPwEt2gzGDjsaMDyV1s5QE"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "120002220000000024027ef3c42019027ef3c2201b027ef3da6840000000000001a773210277c5d02c3c774c96017234a532dae12023ac8fb499c5d90a56488900ecc746d07446304402206698c1d296bf1493c97beb64945558724c6c88474cd3e0b90e9dc9e7313ac1970220175fef60c48646be934be28a964af0cc55843fb6e6ef17c886716a03af849f74701024a022802049b9ab20ca85b55d0c12b948ec7c524f843c77be1ef1561a42b7167dce174b7a701127a0258020ffecf1ae6182f10efebe0c0896cd6b044df7b27d33b05030033ef63d47e2b25081012081145da8080d21fecf98f24ea2223482e5d24f10779982140e9c9b31b826671aaa387555cdeccab82a784020"); +} + TEST(TWAnySignerRipple, SignNfTokenBurn) { // https://devnet.xrpl.org/transactions/37DA90BE3C30016B3A2C3D47D9677278A3F6D4141B318793CE6AA467A6530E2D auto key = parse_hex("7c2ea5c7b1fd7dfc62d879918b7fc779cdff6bf6391d02ec99854297e916318e"); From 2ef7d1fd169a1cfc839d2753080ed3ebfe858808 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Dec 2023 07:35:02 -0500 Subject: [PATCH 023/128] Bump protobufjs from 6.11.3 to 7.2.5 in /samples/node (#3413) Bumps [protobufjs](https://github.com/protobufjs/protobuf.js) from 6.11.3 to 7.2.5. - [Release notes](https://github.com/protobufjs/protobuf.js/releases) - [Changelog](https://github.com/protobufjs/protobuf.js/blob/master/CHANGELOG.md) - [Commits](https://github.com/protobufjs/protobuf.js/compare/v6.11.3...protobufjs-v7.2.5) --- updated-dependencies: - dependency-name: protobufjs dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sztergbaum Roman --- samples/node/package-lock.json | 45 ++++++++++++---------------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/samples/node/package-lock.json b/samples/node/package-lock.json index e0a6b4d8d20..2e8f7ed848c 100644 --- a/samples/node/package-lock.json +++ b/samples/node/package-lock.json @@ -140,11 +140,6 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" - }, "node_modules/@types/node": { "version": "10.17.60", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", @@ -194,9 +189,9 @@ } }, "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" }, "node_modules/make-error": { "version": "1.3.6", @@ -205,9 +200,9 @@ "dev": true }, "node_modules/protobufjs": { - "version": "6.11.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", - "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", + "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -220,13 +215,11 @@ "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", "@types/node": ">=13.7.0", - "long": "^4.0.0" + "long": "^5.0.0" }, - "bin": { - "pbjs": "bin/pbjs", - "pbts": "bin/pbts" + "engines": { + "node": ">=12.0.0" } }, "node_modules/protobufjs/node_modules/@types/node": { @@ -424,11 +417,6 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, - "@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" - }, "@types/node": { "version": "10.17.60", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", @@ -466,9 +454,9 @@ "dev": true }, "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" }, "make-error": { "version": "1.3.6", @@ -477,9 +465,9 @@ "dev": true }, "protobufjs": { - "version": "6.11.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", - "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", + "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -491,9 +479,8 @@ "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", "@types/node": ">=13.7.0", - "long": "^4.0.0" + "long": "^5.0.0" }, "dependencies": { "@types/node": { From 769289c90dc0bda9b2d7810fdb1bc6556866780c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 08:20:25 -0500 Subject: [PATCH 024/128] Bump rustix from 0.36.11 to 0.36.17 in /samples/rust (#3590) Bumps [rustix](https://github.com/bytecodealliance/rustix) from 0.36.11 to 0.36.17. - [Release notes](https://github.com/bytecodealliance/rustix/releases) - [Commits](https://github.com/bytecodealliance/rustix/compare/v0.36.11...v0.36.17) --- updated-dependencies: - dependency-name: rustix dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- samples/rust/Cargo.lock | 137 +++++++++++++++++++++++++--------------- 1 file changed, 85 insertions(+), 52 deletions(-) diff --git a/samples/rust/Cargo.lock b/samples/rust/Cargo.lock index 038f6999084..b77fefe671f 100644 --- a/samples/rust/Cargo.lock +++ b/samples/rust/Cargo.lock @@ -52,23 +52,12 @@ checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "errno" -version = "0.2.8" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", "libc", + "windows-sys 0.52.0", ] [[package]] @@ -273,9 +262,9 @@ checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "rustix" -version = "0.36.11" +version = "0.36.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" +checksum = "305efbd14fde4139eb501df5f136994bb520b033fa9fbdce287507dc23b8c7ed" dependencies = [ "bitflags", "errno", @@ -359,49 +348,36 @@ dependencies = [ ] [[package]] -name = "winapi" -version = "0.3.9" +name = "windows-sys" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-sys" -version = "0.42.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows-targets 0.42.2", ] [[package]] name = "windows-sys" -version = "0.45.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.0", ] [[package]] @@ -410,13 +386,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -425,38 +416,80 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" From 2c36c857070e81fe4cb16abadd63199e11e62c96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 08:46:27 -0500 Subject: [PATCH 025/128] Bump protobufjs from 7.1.0 to 7.2.5 in /samples/typescript/devconsole.ts (#3412) Bumps [protobufjs](https://github.com/protobufjs/protobuf.js) from 7.1.0 to 7.2.5. - [Release notes](https://github.com/protobufjs/protobuf.js/releases) - [Changelog](https://github.com/protobufjs/protobuf.js/blob/master/CHANGELOG.md) - [Commits](https://github.com/protobufjs/protobuf.js/compare/protobufjs-v7.1.0...protobufjs-v7.2.5) --- updated-dependencies: - dependency-name: protobufjs dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sztergbaum Roman --- samples/typescript/devconsole.ts/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/samples/typescript/devconsole.ts/package-lock.json b/samples/typescript/devconsole.ts/package-lock.json index ce8a4ecf0ee..aafb3d70d37 100644 --- a/samples/typescript/devconsole.ts/package-lock.json +++ b/samples/typescript/devconsole.ts/package-lock.json @@ -609,9 +609,9 @@ } }, "node_modules/protobufjs": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.1.0.tgz", - "integrity": "sha512-rCuxKlh0UQKSMjrpIcTLbR5TtGQ52cgs1a5nUoPBAKOccdPblN67BJtjrbtudUJK6HmBvUdsmymyYOzO7lxZEA==", + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", + "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -1303,9 +1303,9 @@ "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==" }, "protobufjs": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.1.0.tgz", - "integrity": "sha512-rCuxKlh0UQKSMjrpIcTLbR5TtGQ52cgs1a5nUoPBAKOccdPblN67BJtjrbtudUJK6HmBvUdsmymyYOzO7lxZEA==", + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", + "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", From f7bdfbe6541934cc1430d6dc249c4b099c9b25f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 09:12:48 -0500 Subject: [PATCH 026/128] Bump protobufjs from 6.11.3 to 6.11.4 in /wasm (#3414) Bumps [protobufjs](https://github.com/protobufjs/protobuf.js) from 6.11.3 to 6.11.4. - [Release notes](https://github.com/protobufjs/protobuf.js/releases) - [Changelog](https://github.com/protobufjs/protobuf.js/blob/master/CHANGELOG.md) - [Commits](https://github.com/protobufjs/protobuf.js/commits) --- updated-dependencies: - dependency-name: protobufjs dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sztergbaum Roman --- wasm/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/wasm/package-lock.json b/wasm/package-lock.json index 7647bbb08c3..d9115d32d77 100644 --- a/wasm/package-lock.json +++ b/wasm/package-lock.json @@ -1027,9 +1027,9 @@ } }, "node_modules/protobufjs": { - "version": "6.11.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", - "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", + "version": "6.11.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", + "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -2083,9 +2083,9 @@ "dev": true }, "protobufjs": { - "version": "6.11.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", - "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", + "version": "6.11.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", + "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==", "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", From 2a63cdae4818d46136ec837a6069515247887b2a Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 13 Dec 2023 17:22:34 +0100 Subject: [PATCH 027/128] [Rust/BNB]: Move BNB Beacon chain to Rust (#3589) * [BNB]: Generate BNB Beacon chain skeleton files in Rust * [BNB]: Implement `BinanceAddress`, add Address tests * [BNB]: Add TBinance address tests * [BNB]: Add Transaction and its encoding, prehashing * Add Amino encoding * [BNB]: Sign a transaction * TODO Add tests, handle all message types * [BNB]: Add a signing test * [BNB]: Add `SendOrder`, `CancelOrder` * [BNB]: Add Token orders * [BNB]: Add `TokenBurnOrder`, `HTLTOrder` * [BNB]: Add `DepositHTLT` * [BNB]: Add `ClaimHTLTOrder`, `RefundHTLTOrder` * [BNB]: Add `TransferOutOrder` * [BNB]: Add `SideChainDelegate`, `SideChainRedelegate`, `SideChainUndelegate` orders * [BNB]: Add `TimeLock`, `TimeRelock`, `TimeUnlock` orders * [BNB]: Implement Transaction Compiler * [BNB]: Increase code coverage * [BNB]: Replace C++ implementation * [BNB]: Move TX preimage implementation to `JsonPreimager` * Remove C++ Signing tests * [BNB]: Extend `CoinEntry` documentation * [CI] Trigger CI * [BNB]: Remove `TransactionCompilerBuildInput` * [BNB]: Avoid duplicating code * [BNB]: Add fuzz tests --- codegen-v2/Cargo.toml | 4 +- .../TrustWalletCore/TWTransactionCompiler.h | 17 - registry.json | 2 + rust/Cargo.lock | 33 + rust/Cargo.toml | 1 + rust/chains/tw_binance/Cargo.toml | 21 + rust/chains/tw_binance/fuzz/.gitignore | 5 + rust/chains/tw_binance/fuzz/Cargo.toml | 30 + .../tw_binance/fuzz/fuzz_targets/sign.rs | 11 + rust/chains/tw_binance/src/address.rs | 91 +++ rust/chains/tw_binance/src/amino.rs | 127 ++++ rust/chains/tw_binance/src/compiler.rs | 87 +++ rust/chains/tw_binance/src/entry.rs | 91 +++ rust/chains/tw_binance/src/lib.rs | 14 + .../tw_binance/src/modules/mod.rs} | 5 +- .../tw_binance/src/modules/preimager.rs | 29 + .../tw_binance/src/modules/serializer.rs | 65 ++ .../tw_binance/src/modules/tx_builder.rs | 501 +++++++++++++ rust/chains/tw_binance/src/signature.rs | 9 + rust/chains/tw_binance/src/signer.rs | 53 ++ .../src/transaction/message/htlt_order.rs | 153 ++++ .../tw_binance/src/transaction/message/mod.rs | 84 +++ .../src/transaction/message/send_order.rs | 69 ++ .../message/side_chain_delegate.rs | 138 ++++ .../transaction/message/time_lock_order.rs | 115 +++ .../src/transaction/message/token_order.rs | 169 +++++ .../src/transaction/message/trade_order.rs | 103 +++ .../transaction/message/tranfer_out_order.rs | 48 ++ rust/chains/tw_binance/src/transaction/mod.rs | 47 ++ rust/coverage.stats | 2 +- rust/tw_any_coin/Cargo.toml | 13 +- rust/tw_any_coin/src/test_utils/mod.rs | 1 + rust/tw_any_coin/src/test_utils/sign_utils.rs | 96 +++ .../tests/chains/binance/binance_address.rs | 65 ++ .../tests/chains/binance/binance_compile.rs | 77 ++ .../tests/chains/binance/binance_sign.rs | 687 +++++++++++++++++ rust/tw_any_coin/tests/chains/binance/mod.rs | 18 + rust/tw_any_coin/tests/chains/mod.rs | 2 + .../tw_any_coin/tests/chains/tbinance/mod.rs | 3 +- .../tests/chains/tbinance/tbinance_address.rs | 49 ++ .../tests/coin_address_derivation_test.rs | 2 + rust/tw_aptos/Cargo.toml | 4 +- rust/tw_aptos/fuzz/.gitignore | 1 + rust/tw_bech32_address/Cargo.toml | 2 +- rust/tw_bech32_address/src/lib.rs | 37 +- rust/tw_bitcoin/Cargo.toml | 4 +- rust/tw_coin_entry/Cargo.toml | 2 +- rust/tw_coin_entry/src/coin_entry.rs | 18 +- rust/tw_coin_entry/src/error.rs | 2 +- rust/tw_coin_entry/src/prefix.rs | 2 + rust/tw_coin_registry/Cargo.toml | 9 +- rust/tw_coin_registry/src/blockchain_type.rs | 1 + rust/tw_coin_registry/src/dispatcher.rs | 3 + rust/tw_cosmos_sdk/Cargo.toml | 4 +- .../src/modules/compiler/tw_compiler.rs | 18 +- rust/tw_cosmos_sdk/src/signature/mod.rs | 10 +- rust/tw_cosmos_sdk/src/signature/secp256k1.rs | 28 +- rust/tw_encoding/Cargo.toml | 2 +- rust/tw_encoding/fuzz/Cargo.toml | 2 +- rust/tw_encoding/src/ffi.rs | 2 + rust/tw_encoding/src/hex.rs | 10 + rust/tw_encoding/src/lib.rs | 1 + rust/tw_evm/Cargo.toml | 4 +- rust/tw_evm/fuzz/Cargo.toml | 2 +- rust/tw_hash/Cargo.toml | 4 +- rust/tw_internet_computer/Cargo.toml | 2 +- rust/tw_keypair/Cargo.toml | 4 +- rust/tw_keypair/src/ecdsa/signature.rs | 23 +- rust/tw_misc/Cargo.toml | 4 +- rust/tw_misc/src/lib.rs | 1 + rust/tw_misc/src/serde.rs | 16 + rust/tw_number/Cargo.toml | 2 +- rust/wallet_core_rs/Cargo.toml | 2 +- samples/go/core/transactionHelper.go | 19 - samples/go/sample/external_signing.go | 39 +- src/Binance/Entry.cpp | 117 +-- src/Binance/Entry.h | 16 +- src/Binance/Serialization.cpp | 198 ----- src/Binance/Serialization.h | 21 - src/Binance/Signer.cpp | 227 ------ src/Binance/Signer.h | 64 -- src/Coin.cpp | 6 - src/Coin.h | 2 - src/CoinEntry.h | 3 - src/Cosmos/Entry.cpp | 23 +- src/Cosmos/Entry.h | 2 +- src/Ethereum/Entry.cpp | 27 +- src/Ethereum/Entry.h | 2 +- src/TransactionCompiler.cpp | 7 - src/TransactionCompiler.h | 3 - src/interface/TWTransactionCompiler.cpp | 17 - src/rust/RustCoinEntry.h | 39 + tests/chains/Binance/SignerTests.cpp | 702 ------------------ .../Binance/TransactionCompilerTests.cpp | 44 +- .../Ethereum/TransactionCompilerTests.cpp | 15 - tests/chains/TBinance/AddressTests.cpp | 42 -- .../interface/TWTransactionCompilerTests.cpp | 46 +- tools/new-blockchain | 11 + tools/new-evmchain | 7 + 99 files changed, 3419 insertions(+), 1643 deletions(-) create mode 100644 rust/chains/tw_binance/Cargo.toml create mode 100644 rust/chains/tw_binance/fuzz/.gitignore create mode 100644 rust/chains/tw_binance/fuzz/Cargo.toml create mode 100644 rust/chains/tw_binance/fuzz/fuzz_targets/sign.rs create mode 100644 rust/chains/tw_binance/src/address.rs create mode 100644 rust/chains/tw_binance/src/amino.rs create mode 100644 rust/chains/tw_binance/src/compiler.rs create mode 100644 rust/chains/tw_binance/src/entry.rs create mode 100644 rust/chains/tw_binance/src/lib.rs rename rust/{tw_coin_registry/src/coin_type.rs => chains/tw_binance/src/modules/mod.rs} (76%) create mode 100644 rust/chains/tw_binance/src/modules/preimager.rs create mode 100644 rust/chains/tw_binance/src/modules/serializer.rs create mode 100644 rust/chains/tw_binance/src/modules/tx_builder.rs create mode 100644 rust/chains/tw_binance/src/signature.rs create mode 100644 rust/chains/tw_binance/src/signer.rs create mode 100644 rust/chains/tw_binance/src/transaction/message/htlt_order.rs create mode 100644 rust/chains/tw_binance/src/transaction/message/mod.rs create mode 100644 rust/chains/tw_binance/src/transaction/message/send_order.rs create mode 100644 rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs create mode 100644 rust/chains/tw_binance/src/transaction/message/time_lock_order.rs create mode 100644 rust/chains/tw_binance/src/transaction/message/token_order.rs create mode 100644 rust/chains/tw_binance/src/transaction/message/trade_order.rs create mode 100644 rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs create mode 100644 rust/chains/tw_binance/src/transaction/mod.rs create mode 100644 rust/tw_any_coin/src/test_utils/sign_utils.rs create mode 100644 rust/tw_any_coin/tests/chains/binance/binance_address.rs create mode 100644 rust/tw_any_coin/tests/chains/binance/binance_compile.rs create mode 100644 rust/tw_any_coin/tests/chains/binance/binance_sign.rs create mode 100644 rust/tw_any_coin/tests/chains/binance/mod.rs rename src/Cosmos/Address.cpp => rust/tw_any_coin/tests/chains/tbinance/mod.rs (82%) create mode 100644 rust/tw_any_coin/tests/chains/tbinance/tbinance_address.rs create mode 100644 rust/tw_misc/src/serde.rs delete mode 100644 src/Binance/Serialization.cpp delete mode 100644 src/Binance/Serialization.h delete mode 100644 src/Binance/Signer.cpp delete mode 100644 src/Binance/Signer.h delete mode 100644 tests/chains/Binance/SignerTests.cpp delete mode 100644 tests/chains/TBinance/AddressTests.cpp create mode 100755 tools/new-blockchain create mode 100755 tools/new-evmchain diff --git a/codegen-v2/Cargo.toml b/codegen-v2/Cargo.toml index 52ad244f84a..43c2afea1c6 100644 --- a/codegen-v2/Cargo.toml +++ b/codegen-v2/Cargo.toml @@ -15,8 +15,8 @@ path = "src/main.rs" aho-corasick = "1.1.2" convert_case = "0.6.0" pathdiff = "0.2.1" -serde = { version = "1.0.159", features = ["derive"] } -serde_json = "1.0.95" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" serde_yaml = "0.9.21" toml_edit = "0.21.0" handlebars = "4.3.6" diff --git a/include/TrustWalletCore/TWTransactionCompiler.h b/include/TrustWalletCore/TWTransactionCompiler.h index 5ec06883c40..f9ecf2d6c86 100644 --- a/include/TrustWalletCore/TWTransactionCompiler.h +++ b/include/TrustWalletCore/TWTransactionCompiler.h @@ -18,23 +18,6 @@ TW_EXTERN_C_BEGIN TW_EXPORT_STRUCT struct TWTransactionCompiler; -/// Builds a coin-specific SigningInput (proto object) from a simple transaction. -/// -/// \deprecated `TWTransactionCompilerBuildInput` will be removed soon. -/// \param coin coin type. -/// \param from sender of the transaction. -/// \param to receiver of the transaction. -/// \param amount transaction amount in string -/// \param asset optional asset name, like "BNB" -/// \param memo optional memo -/// \param chainId optional chainId to override default -/// \return serialized data of the SigningInput proto object. -TW_EXPORT_STATIC_METHOD -TWData* _Nonnull TWTransactionCompilerBuildInput(enum TWCoinType coinType, TWString* _Nonnull from, - TWString* _Nonnull to, TWString* _Nonnull amount, - TWString* _Nonnull asset, TWString* _Nonnull memo, - TWString* _Nonnull chainId); - /// Obtains pre-signing hashes of a transaction. /// /// We provide a default `PreSigningOutput` in TransactionCompiler.proto. diff --git a/registry.json b/registry.json index 28031ea8d0e..d78c60aae73 100644 --- a/registry.json +++ b/registry.json @@ -2440,6 +2440,7 @@ ], "curve": "secp256k1", "publicKeyType": "secp256k1", + "addressHasher": "sha256ripemd", "hrp": "bnb", "chainId": "Binance-Chain-Tigris", "explorer": { @@ -2472,6 +2473,7 @@ ], "curve": "secp256k1", "publicKeyType": "secp256k1", + "addressHasher": "sha256ripemd", "hrp": "tbnb", "explorer": { "url": "https://testnet-explorer.binance.org", diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 24a18ba843f..8954202beeb 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1364,6 +1364,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + [[package]] name = "sha1" version = "0.10.5" @@ -1669,6 +1680,27 @@ dependencies = [ "tw_memory", ] +[[package]] +name = "tw_binance" +version = "0.1.0" +dependencies = [ + "quick-protobuf", + "serde", + "serde_json", + "serde_repr", + "strum_macros", + "tw_bech32_address", + "tw_coin_entry", + "tw_cosmos_sdk", + "tw_encoding", + "tw_evm", + "tw_hash", + "tw_keypair", + "tw_memory", + "tw_misc", + "tw_proto", +] + [[package]] name = "tw_bitcoin" version = "0.1.0" @@ -1712,6 +1744,7 @@ dependencies = [ "strum", "strum_macros", "tw_aptos", + "tw_binance", "tw_bitcoin", "tw_coin_entry", "tw_cosmos", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 87d437eff4c..6b80ffb89e6 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "chains/tw_binance", "chains/tw_cosmos", "chains/tw_native_evmos", "chains/tw_native_injective", diff --git a/rust/chains/tw_binance/Cargo.toml b/rust/chains/tw_binance/Cargo.toml new file mode 100644 index 00000000000..9a1d5003651 --- /dev/null +++ b/rust/chains/tw_binance/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "tw_binance" +version = "0.1.0" +edition = "2021" + +[dependencies] +quick-protobuf = "0.8.1" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +serde_repr = "0.1" +strum_macros = "0.25" +tw_bech32_address = { path = "../../tw_bech32_address" } +tw_coin_entry = { path = "../../tw_coin_entry" } +tw_cosmos_sdk = { path = "../../tw_cosmos_sdk" } +tw_encoding = { path = "../../tw_encoding" } +tw_evm = { path = "../../tw_evm" } +tw_hash = { path = "../../tw_hash" } +tw_keypair = { path = "../../tw_keypair" } +tw_memory = { path = "../../tw_memory" } +tw_misc = { path = "../../tw_misc" } +tw_proto = { path = "../../tw_proto" } diff --git a/rust/chains/tw_binance/fuzz/.gitignore b/rust/chains/tw_binance/fuzz/.gitignore new file mode 100644 index 00000000000..5c404b9583f --- /dev/null +++ b/rust/chains/tw_binance/fuzz/.gitignore @@ -0,0 +1,5 @@ +target +corpus +artifacts +coverage +Cargo.lock diff --git a/rust/chains/tw_binance/fuzz/Cargo.toml b/rust/chains/tw_binance/fuzz/Cargo.toml new file mode 100644 index 00000000000..179f9d77239 --- /dev/null +++ b/rust/chains/tw_binance/fuzz/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "tw_binance-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" +tw_any_coin = { path = "../../../tw_any_coin", features = ["test-utils"] } +tw_coin_registry = { path = "../../../tw_coin_registry" } +tw_proto = { path = "../../../tw_proto", features = ["fuzz"] } + +[dependencies.tw_binance] +path = ".." + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[profile.release] +debug = 1 + +[[bin]] +name = "sign" +path = "fuzz_targets/sign.rs" +test = false +doc = false diff --git a/rust/chains/tw_binance/fuzz/fuzz_targets/sign.rs b/rust/chains/tw_binance/fuzz/fuzz_targets/sign.rs new file mode 100644 index 00000000000..482fd1f5844 --- /dev/null +++ b/rust/chains/tw_binance/fuzz/fuzz_targets/sign.rs @@ -0,0 +1,11 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; +use tw_any_coin::test_utils::sign_utils::AnySignerHelper; +use tw_coin_registry::coin_type::CoinType; +use tw_proto::Binance::Proto; + +fuzz_target!(|input: Proto::SigningInput<'_>| { + let mut signer = AnySignerHelper::::default(); + let _ = signer.sign(CoinType::Binance, input); +}); diff --git a/rust/chains/tw_binance/src/address.rs b/rust/chains/tw_binance/src/address.rs new file mode 100644 index 00000000000..9bc164e0395 --- /dev/null +++ b/rust/chains/tw_binance/src/address.rs @@ -0,0 +1,91 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde::Serialize; +use std::fmt; +use std::str::FromStr; +use tw_bech32_address::bech32_prefix::Bech32Prefix; +use tw_bech32_address::Bech32Address; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::CoinAddress; +use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_keypair::tw::PublicKey; +use tw_memory::Data; + +/// The list of known BNB hrps. +const BNB_KNOWN_HRPS: [&str; 2] = [ + BinanceAddress::VALIDATOR_HRP, // BNB Validator HRP. + "bca", +]; + +#[derive(Serialize)] +pub struct BinanceAddress(Bech32Address); + +impl CoinAddress for BinanceAddress { + #[inline] + fn data(&self) -> Data { + self.0.data() + } +} + +impl BinanceAddress { + pub const VALIDATOR_HRP: &'static str = "bva"; + + pub fn new_validator_addr(key_hash: Data) -> AddressResult { + Bech32Address::new(Self::VALIDATOR_HRP.to_string(), key_hash).map(BinanceAddress) + } + + /// Creates a Binance address with the only `prefix` + pub fn from_str_with_coin_and_prefix( + coin: &dyn CoinContext, + address_str: String, + prefix: Option, + ) -> AddressResult + where + Self: Sized, + { + let possible_hrps = match prefix { + Some(Bech32Prefix { hrp }) => vec![hrp], + None => { + let coin_hrp = coin.hrp().ok_or(AddressError::InvalidHrp)?; + let other_hrps = BNB_KNOWN_HRPS + .iter() + .map(|another_hrp| another_hrp.to_string()); + std::iter::once(coin_hrp).chain(other_hrps).collect() + }, + }; + Bech32Address::from_str_checked(possible_hrps, address_str).map(BinanceAddress) + } + + pub fn with_public_key_coin_context( + coin: &dyn CoinContext, + public_key: &PublicKey, + prefix: Option, + ) -> AddressResult { + Bech32Address::with_public_key_coin_context(coin, public_key, prefix).map(BinanceAddress) + } + + pub fn from_key_hash_with_coin( + coin: &dyn CoinContext, + key_hash: Data, + ) -> AddressResult { + Bech32Address::from_key_hash_with_coin(coin, key_hash).map(BinanceAddress) + } +} + +impl FromStr for BinanceAddress { + type Err = AddressError; + + fn from_str(s: &str) -> Result { + Bech32Address::from_str(s).map(BinanceAddress) + } +} + +impl fmt::Display for BinanceAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} diff --git a/rust/chains/tw_binance/src/amino.rs b/rust/chains/tw_binance/src/amino.rs new file mode 100644 index 00000000000..0c891586b9e --- /dev/null +++ b/rust/chains/tw_binance/src/amino.rs @@ -0,0 +1,127 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use quick_protobuf::MessageWrite; +use tw_encoding::{EncodingError, EncodingResult}; +use tw_memory::Data; +use tw_proto::serialize; + +pub struct AminoEncoder { + /// The Amino content starts with a prefix. + content: Data, +} + +impl AminoEncoder { + pub fn new(prefix: &[u8]) -> AminoEncoder { + AminoEncoder { + content: prefix.to_vec(), + } + } + + pub fn extend_content(mut self, content: &[u8]) -> AminoEncoder { + self.content.extend_from_slice(content); + self + } + + pub fn extend_with_msg(mut self, msg: &M) -> EncodingResult { + let msg_data = serialize(msg).map_err(|_| EncodingError::Internal)?; + self.content.extend_from_slice(&msg_data); + Ok(self) + } + + pub fn encode(self) -> Data { + self.content + } + + pub fn encode_size_prefixed(self) -> EncodingResult { + const CONTENT_SIZE_CAPACITY: usize = 10; + + let content_len = self.content.len(); + let capacity = content_len + CONTENT_SIZE_CAPACITY; + + let mut buffer = Vec::with_capacity(capacity); + + Self::write_varint(&mut buffer, content_len as u64)?; + buffer.extend_from_slice(&self.content); + + Ok(buffer) + } + + /// This method takes `&mut Data` instead of `&mut [u8]` because the given `buffer` can be extended (become longer). + fn write_varint(buffer: &mut Data, num: u64) -> EncodingResult<()> { + let mut writer = quick_protobuf::Writer::new(buffer); + writer + .write_varint(num) + .map_err(|_| EncodingError::Internal) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tw_encoding::hex::DecodeHex; + + struct TestInput { + prefix: &'static str, + content: &'static str, + content_size_prefixed: bool, + expected: &'static str, + } + + fn amino_encode_impl(input: TestInput) { + let prefix = input.prefix.decode_hex().unwrap(); + let content = input.content.decode_hex().unwrap(); + + let encoder = AminoEncoder::new(&prefix).extend_content(&content); + + let actual = if input.content_size_prefixed { + encoder + .encode_size_prefixed() + .expect("Error on Amino encoding with content size prefix") + } else { + encoder.encode() + }; + + let expected = input.expected.decode_hex().unwrap(); + assert_eq!(actual, expected); + } + + #[test] + fn test_amino_encode() { + let content_size_prefixed = false; + + amino_encode_impl(TestInput { + prefix: "0b0c0d0e", + content: "0102030405060708", + content_size_prefixed, + expected: "0b0c0d0e0102030405060708", + }); + amino_encode_impl(TestInput { + prefix: "0b0c0d0e", + content: "01020304050607080102030405060708010203040506070801020304050607080102030405060708", + content_size_prefixed, + expected: "0b0c0d0e01020304050607080102030405060708010203040506070801020304050607080102030405060708", + }); + } + + #[test] + fn test_amino_encode_with_content_size_prefix() { + let content_size_prefixed = true; + + amino_encode_impl(TestInput { + prefix: "0b0c0d0e", + content: "0102030405060708", + content_size_prefixed, + expected: "0c0b0c0d0e0102030405060708", + }); + amino_encode_impl(TestInput { + prefix: "0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e", + content: "0102030405060708", + content_size_prefixed, + expected: "dc020b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0b0c0d0e0f101112131415161718191a1b1c1d1e0102030405060708", + }); + } +} diff --git a/rust/chains/tw_binance/src/compiler.rs b/rust/chains/tw_binance/src/compiler.rs new file mode 100644 index 00000000000..1ee1e23dd15 --- /dev/null +++ b/rust/chains/tw_binance/src/compiler.rs @@ -0,0 +1,87 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::modules::preimager::{JsonPreimager, JsonTxPreimage}; +use crate::modules::serializer::BinanceAminoSerializer; +use crate::modules::tx_builder::TxBuilder; +use crate::signature::BinanceSignature; +use crate::transaction::SignerInfo; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::common::compile_input::SingleSignaturePubkey; +use tw_coin_entry::error::SigningResult; +use tw_coin_entry::signing_output_error; +use tw_keypair::ecdsa::secp256k1; +use tw_proto::Binance::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct BinanceCompiler; + +impl BinanceCompiler { + #[inline] + pub fn preimage_hashes( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> CompilerProto::PreSigningOutput<'static> { + Self::preimage_hashes_impl(coin, input) + .unwrap_or_else(|e| signing_output_error!(CompilerProto::PreSigningOutput, e)) + } + + fn preimage_hashes_impl( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> SigningResult> { + let unsigned_tx = TxBuilder::unsigned_tx_from_proto(coin, &input)?; + let JsonTxPreimage { + tx_hash, + encoded_tx, + } = JsonPreimager::preimage_hash(&unsigned_tx)?; + + Ok(CompilerProto::PreSigningOutput { + data_hash: tx_hash.to_vec().into(), + data: encoded_tx.as_bytes().to_vec().into(), + ..CompilerProto::PreSigningOutput::default() + }) + } + + #[inline] + pub fn compile( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Proto::SigningOutput<'static> { + Self::compile_impl(coin, input, signatures, public_keys) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn compile_impl( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> SigningResult> { + let SingleSignaturePubkey { + signature, + public_key, + } = SingleSignaturePubkey::from_sign_pubkey_list(signatures, public_keys)?; + let signature = BinanceSignature::try_from(signature.as_slice())?; + let public_key = secp256k1::PublicKey::try_from(public_key.as_slice())?; + + let unsigned_tx = TxBuilder::unsigned_tx_from_proto(coin, &input)?; + let signed_tx = unsigned_tx.into_signed(SignerInfo { + public_key, + signature, + }); + + let encoded_tx = BinanceAminoSerializer::serialize_signed_tx(&signed_tx)?; + + Ok(Proto::SigningOutput { + encoded: encoded_tx.into(), + ..Proto::SigningOutput::default() + }) + } +} diff --git a/rust/chains/tw_binance/src/entry.rs b/rust/chains/tw_binance/src/entry.rs new file mode 100644 index 00000000000..bda948c9900 --- /dev/null +++ b/rust/chains/tw_binance/src/entry.rs @@ -0,0 +1,91 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::BinanceAddress; +use crate::compiler::BinanceCompiler; +use crate::signer::BinanceSigner; +use std::str::FromStr; +use tw_bech32_address::bech32_prefix::Bech32Prefix; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::derivation::Derivation; +use tw_coin_entry::error::AddressResult; +use tw_coin_entry::modules::json_signer::NoJsonSigner; +use tw_coin_entry::modules::message_signer::NoMessageSigner; +use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_keypair::tw::PublicKey; +use tw_proto::Binance::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct BinanceEntry; + +impl CoinEntry for BinanceEntry { + type AddressPrefix = Bech32Prefix; + type Address = BinanceAddress; + type SigningInput<'a> = Proto::SigningInput<'a>; + type SigningOutput = Proto::SigningOutput<'static>; + type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + + // Optional modules: + type JsonSigner = NoJsonSigner; + type PlanBuilder = NoPlanBuilder; + type MessageSigner = NoMessageSigner; + + #[inline] + fn parse_address( + &self, + coin: &dyn CoinContext, + address: &str, + prefix: Option, + ) -> AddressResult { + BinanceAddress::from_str_with_coin_and_prefix(coin, address.to_string(), prefix) + } + + #[inline] + fn parse_address_unchecked( + &self, + _coin: &dyn CoinContext, + address: &str, + ) -> AddressResult { + BinanceAddress::from_str(address) + } + + #[inline] + fn derive_address( + &self, + coin: &dyn CoinContext, + public_key: PublicKey, + _derivation: Derivation, + prefix: Option, + ) -> AddressResult { + BinanceAddress::with_public_key_coin_context(coin, &public_key, prefix) + } + + #[inline] + fn sign(&self, coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput { + BinanceSigner::sign(coin, input) + } + + #[inline] + fn preimage_hashes( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + ) -> Self::PreSigningOutput { + BinanceCompiler::preimage_hashes(coin, input) + } + + #[inline] + fn compile( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Self::SigningOutput { + BinanceCompiler::compile(coin, input, signatures, public_keys) + } +} diff --git a/rust/chains/tw_binance/src/lib.rs b/rust/chains/tw_binance/src/lib.rs new file mode 100644 index 00000000000..0bd07a91bbf --- /dev/null +++ b/rust/chains/tw_binance/src/lib.rs @@ -0,0 +1,14 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod address; +pub mod amino; +pub mod compiler; +pub mod entry; +pub mod modules; +pub mod signature; +pub mod signer; +pub mod transaction; diff --git a/rust/tw_coin_registry/src/coin_type.rs b/rust/chains/tw_binance/src/modules/mod.rs similarity index 76% rename from rust/tw_coin_registry/src/coin_type.rs rename to rust/chains/tw_binance/src/modules/mod.rs index d03c00f87ca..f27e84a2506 100644 --- a/rust/tw_coin_registry/src/coin_type.rs +++ b/rust/chains/tw_binance/src/modules/mod.rs @@ -4,5 +4,6 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -/// TODO make this enum generated from `registry.json`. -pub type CoinType = u32; +pub mod preimager; +pub mod serializer; +pub mod tx_builder; diff --git a/rust/chains/tw_binance/src/modules/preimager.rs b/rust/chains/tw_binance/src/modules/preimager.rs new file mode 100644 index 00000000000..18e2d47b5b5 --- /dev/null +++ b/rust/chains/tw_binance/src/modules/preimager.rs @@ -0,0 +1,29 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::transaction::UnsignedTransaction; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_hash::{sha2, H256}; + +pub struct JsonTxPreimage { + pub encoded_tx: String, + pub tx_hash: H256, +} + +pub struct JsonPreimager; + +impl JsonPreimager { + pub fn preimage_hash(unsigned: &UnsignedTransaction) -> SigningResult { + let encoded_tx = serde_json::to_string(unsigned) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let tx_hash = sha2::sha256(encoded_tx.as_bytes()); + let tx_hash = H256::try_from(tx_hash.as_slice()).expect("sha256 must return 32 bytes"); + Ok(JsonTxPreimage { + encoded_tx, + tx_hash, + }) + } +} diff --git a/rust/chains/tw_binance/src/modules/serializer.rs b/rust/chains/tw_binance/src/modules/serializer.rs new file mode 100644 index 00000000000..4716ffd9ab9 --- /dev/null +++ b/rust/chains/tw_binance/src/modules/serializer.rs @@ -0,0 +1,65 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::amino::AminoEncoder; +use crate::transaction::SignedTransaction; +use std::borrow::Cow; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_hash::H264; +use tw_memory::Data; +use tw_misc::traits::ToBytesVec; +use tw_proto::serialize; +use tw_proto::Binance::Proto; + +/// cbindgen:ignore +pub const TRANSACTION_AMINO_PREFIX: [u8; 4] = [0xF0, 0x62, 0x5D, 0xEE]; +/// cbindgen:ignore +pub const PUBLIC_KEY_PREFIX: [u8; 4] = [0xEB, 0x5A, 0xE9, 0x87]; + +pub struct BinanceAminoSerializer; + +impl BinanceAminoSerializer { + pub fn serialize_signed_tx(tx: &SignedTransaction) -> SigningResult { + let msgs = tx + .unsigned + .msgs + .iter() + .map(|msg| msg.to_amino_protobuf().map(Cow::from)) + .collect::>>()?; + + let signature = Self::serialize_signature(tx)?; + let tx = Proto::Transaction { + msgs, + signatures: vec![signature.into()], + memo: tx.unsigned.memo.clone().into(), + source: tx.unsigned.source, + data: tx.unsigned.data.clone().unwrap_or_default().into(), + }; + Ok(AminoEncoder::new(&TRANSACTION_AMINO_PREFIX) + .extend_with_msg(&tx)? + .encode_size_prefixed()?) + } + + pub fn serialize_public_key(public_key: H264) -> Data { + let public_key_len = public_key.len() as u8; + AminoEncoder::new(&PUBLIC_KEY_PREFIX) + // Push the length of the public key. + .extend_content(&[public_key_len]) + .extend_content(public_key.as_slice()) + .encode() + } + + pub fn serialize_signature(signed: &SignedTransaction) -> SigningResult { + let sign_msg = Proto::Signature { + pub_key: Self::serialize_public_key(signed.signer.public_key.compressed()).into(), + signature: signed.signer.signature.to_vec().into(), + account_number: signed.unsigned.account_number, + sequence: signed.unsigned.sequence, + }; + // There is no need to use Amino encoding here as the prefix is empty. + serialize(&sign_msg).map_err(|_| SigningError(SigningErrorType::Error_internal)) + } +} diff --git a/rust/chains/tw_binance/src/modules/tx_builder.rs b/rust/chains/tw_binance/src/modules/tx_builder.rs new file mode 100644 index 00000000000..a355df60dc4 --- /dev/null +++ b/rust/chains/tw_binance/src/modules/tx_builder.rs @@ -0,0 +1,501 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::BinanceAddress; +use crate::transaction::message::Token; +use crate::transaction::message::{BinanceMessage, BinanceMessageBox}; +use crate::transaction::UnsignedTransaction; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_evm::address::Address as EthereumAddress; +use tw_hash::H160; +use tw_proto::Binance::Proto; + +pub struct TxBuilder; + +impl TxBuilder { + pub fn unsigned_tx_from_proto( + coin: &dyn CoinContext, + input: &Proto::SigningInput<'_>, + ) -> SigningResult { + let msg = Self::msg_from_proto(coin, input)?; + + Ok(UnsignedTransaction { + account_number: input.account_number, + chain_id: input.chain_id.to_string(), + data: None, + memo: input.memo.to_string(), + msgs: vec![msg], + sequence: input.sequence, + source: input.source, + }) + } + + pub fn msg_from_proto( + coin: &dyn CoinContext, + input: &Proto::SigningInput<'_>, + ) -> SigningResult { + use Proto::mod_SigningInput::OneOforder_oneof as OrderEnum; + + match input.order_oneof { + OrderEnum::trade_order(ref new_order) => Self::trade_order_from_proto(coin, new_order), + OrderEnum::cancel_trade_order(ref cancel_order) => { + Self::cancel_order_from_proto(coin, cancel_order) + }, + OrderEnum::send_order(ref send_order) => Self::send_order_from_proto(coin, send_order), + OrderEnum::freeze_order(ref freeze_order) => { + Self::freeze_order_from_proto(coin, freeze_order) + }, + OrderEnum::unfreeze_order(ref unfreeze_order) => { + Self::unfreeze_order_from_proto(coin, unfreeze_order) + }, + OrderEnum::htlt_order(ref htlt_order) => Self::htlt_order_from_proto(coin, htlt_order), + OrderEnum::depositHTLT_order(ref deposit_htlt) => { + Self::deposit_htlt_order_from_proto(coin, deposit_htlt) + }, + OrderEnum::claimHTLT_order(ref claim_htlt) => { + Self::claim_htlt_order_from_proto(coin, claim_htlt) + }, + OrderEnum::refundHTLT_order(ref refund_htlt) => { + Self::refund_htlt_order_from_proto(coin, refund_htlt) + }, + OrderEnum::issue_order(ref issue_order) => { + Self::issue_order_from_proto(coin, issue_order) + }, + OrderEnum::mint_order(ref mint_order) => Self::mint_order_from_proto(coin, mint_order), + OrderEnum::burn_order(ref burn_order) => Self::burn_order_from_proto(coin, burn_order), + OrderEnum::transfer_out_order(ref transfer_out) => { + Self::transfer_out_order_from_proto(coin, transfer_out) + }, + OrderEnum::side_delegate_order(ref side_delegate) => { + Self::side_delegate_order_from_proto(coin, side_delegate) + }, + OrderEnum::side_redelegate_order(ref side_redelegate) => { + Self::side_redelegate_order_from_proto(coin, side_redelegate) + }, + OrderEnum::side_undelegate_order(ref side_undelegate) => { + Self::side_undelegate_order_from_proto(coin, side_undelegate) + }, + OrderEnum::time_lock_order(ref time_lock) => { + Self::time_lock_order_from_proto(coin, time_lock) + }, + OrderEnum::time_relock_order(ref time_relock) => { + Self::time_relock_order_from_proto(coin, time_relock) + }, + OrderEnum::time_unlock_order(ref time_unlock) => { + Self::time_unlock_order_from_proto(coin, time_unlock) + }, + OrderEnum::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + } + } + + pub fn trade_order_from_proto( + coin: &dyn CoinContext, + new_order: &Proto::TradeOrder<'_>, + ) -> SigningResult { + use crate::transaction::message::trade_order::NewTradeOrder; + use crate::transaction::message::trade_order::OrderType; + + let order_type = OrderType::from_repr(new_order.ordertype) + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + let sender = BinanceAddress::from_key_hash_with_coin(coin, new_order.sender.to_vec())?; + + Ok(NewTradeOrder { + id: new_order.id.to_string(), + order_type, + price: new_order.price, + quantity: new_order.quantity, + sender, + side: new_order.side, + symbol: new_order.symbol.to_string(), + time_in_force: new_order.timeinforce, + } + .into_boxed()) + } + + pub fn cancel_order_from_proto( + coin: &dyn CoinContext, + cancel_order: &Proto::CancelTradeOrder<'_>, + ) -> SigningResult { + use crate::transaction::message::trade_order::CancelTradeOrder; + + let sender = BinanceAddress::from_key_hash_with_coin(coin, cancel_order.sender.to_vec())?; + Ok(CancelTradeOrder { + sender, + symbol: cancel_order.symbol.to_string(), + refid: cancel_order.refid.to_string(), + } + .into_boxed()) + } + + pub fn send_order_from_proto( + coin: &dyn CoinContext, + send_order: &Proto::SendOrder<'_>, + ) -> SigningResult { + use crate::transaction::message::send_order::{InOut, SendOrder}; + + fn in_out_from_proto( + coin: &dyn CoinContext, + address_key_hash: &[u8], + coins: &[Proto::mod_SendOrder::Token], + ) -> SigningResult { + let address = BinanceAddress::from_key_hash_with_coin(coin, address_key_hash.to_vec())?; + let coins = coins.iter().map(TxBuilder::token_from_proto).collect(); + + Ok(InOut { address, coins }) + } + + let inputs = send_order + .inputs + .iter() + .map(|input| in_out_from_proto(coin, &input.address, &input.coins)) + .collect::>>()?; + + let outputs = send_order + .outputs + .iter() + .map(|output| in_out_from_proto(coin, &output.address, &output.coins)) + .collect::>>()?; + + Ok(SendOrder { inputs, outputs }.into_boxed()) + } + + pub fn freeze_order_from_proto( + coin: &dyn CoinContext, + freeze_order: &Proto::TokenFreezeOrder<'_>, + ) -> SigningResult { + use crate::transaction::message::token_order::TokenFreezeOrder; + + let from = BinanceAddress::from_key_hash_with_coin(coin, freeze_order.from.to_vec())?; + Ok(TokenFreezeOrder { + from, + symbol: freeze_order.symbol.to_string(), + amount: freeze_order.amount, + } + .into_boxed()) + } + + pub fn unfreeze_order_from_proto( + coin: &dyn CoinContext, + unfreeze_order: &Proto::TokenUnfreezeOrder<'_>, + ) -> SigningResult { + use crate::transaction::message::token_order::TokenUnfreezeOrder; + + let from = BinanceAddress::from_key_hash_with_coin(coin, unfreeze_order.from.to_vec())?; + Ok(TokenUnfreezeOrder { + from, + symbol: unfreeze_order.symbol.to_string(), + amount: unfreeze_order.amount, + } + .into_boxed()) + } + + pub fn htlt_order_from_proto( + coin: &dyn CoinContext, + htlt_order: &Proto::HTLTOrder<'_>, + ) -> SigningResult { + use crate::transaction::message::htlt_order::HTLTOrder; + + let from = BinanceAddress::from_key_hash_with_coin(coin, htlt_order.from.to_vec())?; + let to = BinanceAddress::from_key_hash_with_coin(coin, htlt_order.to.to_vec())?; + + let amount = htlt_order + .amount + .iter() + .map(Self::token_from_proto) + .collect(); + + Ok(HTLTOrder { + from, + to, + recipient_other_chain: htlt_order.recipient_other_chain.to_string(), + sender_other_chain: htlt_order.sender_other_chain.to_string(), + random_number_hash: htlt_order.random_number_hash.to_vec(), + timestamp: htlt_order.timestamp, + amount, + expected_income: htlt_order.expected_income.to_string(), + height_span: htlt_order.height_span, + cross_chain: htlt_order.cross_chain, + } + .into_boxed()) + } + + pub fn deposit_htlt_order_from_proto( + coin: &dyn CoinContext, + deposit_htlt: &Proto::DepositHTLTOrder<'_>, + ) -> SigningResult { + use crate::transaction::message::htlt_order::DepositHTLTOrder; + + let from = BinanceAddress::from_key_hash_with_coin(coin, deposit_htlt.from.to_vec())?; + + let amount = deposit_htlt + .amount + .iter() + .map(Self::token_from_proto) + .collect(); + + Ok(DepositHTLTOrder { + from, + amount, + swap_id: deposit_htlt.swap_id.to_vec(), + } + .into_boxed()) + } + + pub fn claim_htlt_order_from_proto( + coin: &dyn CoinContext, + claim_htlt: &Proto::ClaimHTLOrder<'_>, + ) -> SigningResult { + use crate::transaction::message::htlt_order::ClaimHTLTOrder; + + let from = BinanceAddress::from_key_hash_with_coin(coin, claim_htlt.from.to_vec())?; + + Ok(ClaimHTLTOrder { + from, + swap_id: claim_htlt.swap_id.to_vec(), + random_number: claim_htlt.random_number.to_vec(), + } + .into_boxed()) + } + + pub fn refund_htlt_order_from_proto( + coin: &dyn CoinContext, + refund_htlt: &Proto::RefundHTLTOrder<'_>, + ) -> SigningResult { + use crate::transaction::message::htlt_order::RefundHTLTOrder; + + let from = BinanceAddress::from_key_hash_with_coin(coin, refund_htlt.from.to_vec())?; + let swap_id = refund_htlt.swap_id.to_vec(); + + Ok(RefundHTLTOrder { from, swap_id }.into_boxed()) + } + + pub fn issue_order_from_proto( + coin: &dyn CoinContext, + issue_order: &Proto::TokenIssueOrder<'_>, + ) -> SigningResult { + use crate::transaction::message::token_order::TokenIssueOrder; + + let from = BinanceAddress::from_key_hash_with_coin(coin, issue_order.from.to_vec())?; + Ok(TokenIssueOrder { + from, + name: issue_order.name.to_string(), + symbol: issue_order.symbol.to_string(), + total_supply: issue_order.total_supply, + mintable: issue_order.mintable, + } + .into_boxed()) + } + + pub fn mint_order_from_proto( + coin: &dyn CoinContext, + mint_order: &Proto::TokenMintOrder<'_>, + ) -> SigningResult { + use crate::transaction::message::token_order::TokenMintOrder; + + let from = BinanceAddress::from_key_hash_with_coin(coin, mint_order.from.to_vec())?; + Ok(TokenMintOrder { + from, + symbol: mint_order.symbol.to_string(), + amount: mint_order.amount, + } + .into_boxed()) + } + + pub fn burn_order_from_proto( + coin: &dyn CoinContext, + burn_order: &Proto::TokenBurnOrder<'_>, + ) -> SigningResult { + use crate::transaction::message::token_order::TokenBurnOrder; + + let from = BinanceAddress::from_key_hash_with_coin(coin, burn_order.from.to_vec())?; + Ok(TokenBurnOrder { + from, + symbol: burn_order.symbol.to_string(), + amount: burn_order.amount, + } + .into_boxed()) + } + + pub fn transfer_out_order_from_proto( + coin: &dyn CoinContext, + transfer_out: &Proto::TransferOut<'_>, + ) -> SigningResult { + use crate::transaction::message::tranfer_out_order::TransferOutOrder; + + let from = BinanceAddress::from_key_hash_with_coin(coin, transfer_out.from.to_vec())?; + + let to_bytes = H160::try_from(transfer_out.to.as_ref()) + .map_err(|_| SigningError(SigningErrorType::Error_invalid_address))?; + let to = EthereumAddress::from_bytes(to_bytes); + + let amount_proto = transfer_out + .amount + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + + Ok(TransferOutOrder { + from, + to, + amount: Self::token_from_proto(amount_proto), + expire_time: transfer_out.expire_time, + } + .into_boxed()) + } + + pub fn side_delegate_order_from_proto( + coin: &dyn CoinContext, + side_delegate: &Proto::SideChainDelegate<'_>, + ) -> SigningResult { + use crate::transaction::message::side_chain_delegate::SideDelegateOrder; + + let delegator_addr = + BinanceAddress::from_key_hash_with_coin(coin, side_delegate.delegator_addr.to_vec())?; + let validator_addr = + BinanceAddress::new_validator_addr(side_delegate.validator_addr.to_vec())?; + + let delegation = side_delegate + .delegation + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + + Ok(SideDelegateOrder { + delegator_addr, + validator_addr, + delegation: Self::token_from_proto(delegation), + side_chain_id: side_delegate.chain_id.to_string(), + } + .into_boxed()) + } + + pub fn side_redelegate_order_from_proto( + coin: &dyn CoinContext, + side_redelegate: &Proto::SideChainRedelegate<'_>, + ) -> SigningResult { + use crate::transaction::message::side_chain_delegate::SideRedelegateOrder; + + let delegator_addr = + BinanceAddress::from_key_hash_with_coin(coin, side_redelegate.delegator_addr.to_vec())?; + let validator_src_addr = + BinanceAddress::new_validator_addr(side_redelegate.validator_src_addr.to_vec())?; + let validator_dst_addr = + BinanceAddress::new_validator_addr(side_redelegate.validator_dst_addr.to_vec())?; + + let amount = side_redelegate + .amount + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + + Ok(SideRedelegateOrder { + delegator_addr, + validator_src_addr, + validator_dst_addr, + amount: Self::token_from_proto(amount), + side_chain_id: side_redelegate.chain_id.to_string(), + } + .into_boxed()) + } + + pub fn side_undelegate_order_from_proto( + coin: &dyn CoinContext, + side_undelegate: &Proto::SideChainUndelegate<'_>, + ) -> SigningResult { + use crate::transaction::message::side_chain_delegate::SideUndelegateOrder; + + let delegator_addr = + BinanceAddress::from_key_hash_with_coin(coin, side_undelegate.delegator_addr.to_vec())?; + let validator_addr = + BinanceAddress::new_validator_addr(side_undelegate.validator_addr.to_vec())?; + + let amount = side_undelegate + .amount + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + + Ok(SideUndelegateOrder { + delegator_addr, + validator_addr, + amount: Self::token_from_proto(amount), + side_chain_id: side_undelegate.chain_id.to_string(), + } + .into_boxed()) + } + + pub fn time_lock_order_from_proto( + coin: &dyn CoinContext, + time_lock: &Proto::TimeLockOrder<'_>, + ) -> SigningResult { + use crate::transaction::message::time_lock_order::TimeLockOrder; + + let from = BinanceAddress::from_key_hash_with_coin(coin, time_lock.from_address.to_vec())?; + let amount = time_lock + .amount + .iter() + .map(Self::token_from_proto) + .collect(); + + Ok(TimeLockOrder { + from, + description: time_lock.description.to_string(), + amount, + lock_time: time_lock.lock_time, + } + .into_boxed()) + } + + pub fn time_relock_order_from_proto( + coin: &dyn CoinContext, + time_relock: &Proto::TimeRelockOrder<'_>, + ) -> SigningResult { + use crate::transaction::message::time_lock_order::TimeRelockOrder; + + let from = + BinanceAddress::from_key_hash_with_coin(coin, time_relock.from_address.to_vec())?; + + let amount = if time_relock.amount.is_empty() { + None + } else { + Some( + time_relock + .amount + .iter() + .map(Self::token_from_proto) + .collect(), + ) + }; + + Ok(TimeRelockOrder { + from, + time_lock_id: time_relock.id, + description: time_relock.description.to_string(), + amount, + lock_time: time_relock.lock_time, + } + .into_boxed()) + } + + pub fn time_unlock_order_from_proto( + coin: &dyn CoinContext, + time_unlock: &Proto::TimeUnlockOrder<'_>, + ) -> SigningResult { + use crate::transaction::message::time_lock_order::TimeUnlockOrder; + + let from = + BinanceAddress::from_key_hash_with_coin(coin, time_unlock.from_address.to_vec())?; + Ok(TimeUnlockOrder { + from, + time_lock_id: time_unlock.id, + } + .into_boxed()) + } + + fn token_from_proto(token: &Proto::mod_SendOrder::Token) -> Token { + Token { + denom: token.denom.to_string(), + amount: token.amount, + } + } +} diff --git a/rust/chains/tw_binance/src/signature.rs b/rust/chains/tw_binance/src/signature.rs new file mode 100644 index 00000000000..5e58a2967a1 --- /dev/null +++ b/rust/chains/tw_binance/src/signature.rs @@ -0,0 +1,9 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_keypair::ecdsa::secp256k1; + +pub type BinanceSignature = secp256k1::VerifySignature; diff --git a/rust/chains/tw_binance/src/signer.rs b/rust/chains/tw_binance/src/signer.rs new file mode 100644 index 00000000000..b8b1c02739e --- /dev/null +++ b/rust/chains/tw_binance/src/signer.rs @@ -0,0 +1,53 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::modules::preimager::{JsonPreimager, JsonTxPreimage}; +use crate::modules::serializer::BinanceAminoSerializer; +use crate::modules::tx_builder::TxBuilder; +use crate::signature::BinanceSignature; +use crate::transaction::SignerInfo; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::SigningResult; +use tw_coin_entry::signing_output_error; +use tw_keypair::ecdsa::secp256k1; +use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; +use tw_proto::Binance::Proto; + +pub struct BinanceSigner; + +impl BinanceSigner { + pub fn sign( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> Proto::SigningOutput<'static> { + Self::sign_impl(coin, input) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn sign_impl( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> SigningResult> { + let unsigned_tx = TxBuilder::unsigned_tx_from_proto(coin, &input)?; + let JsonTxPreimage { tx_hash, .. } = JsonPreimager::preimage_hash(&unsigned_tx)?; + + let key_pair = secp256k1::KeyPair::try_from(input.private_key.as_ref())?; + + let signature = BinanceSignature::from(key_pair.sign(tx_hash)?); + let public_key = key_pair.public().clone(); + + let signed_tx = unsigned_tx.into_signed(SignerInfo { + public_key, + signature, + }); + let encoded_tx = BinanceAminoSerializer::serialize_signed_tx(&signed_tx)?; + + Ok(Proto::SigningOutput { + encoded: encoded_tx.into(), + ..Proto::SigningOutput::default() + }) + } +} diff --git a/rust/chains/tw_binance/src/transaction/message/htlt_order.rs b/rust/chains/tw_binance/src/transaction/message/htlt_order.rs new file mode 100644 index 00000000000..c0969bb305d --- /dev/null +++ b/rust/chains/tw_binance/src/transaction/message/htlt_order.rs @@ -0,0 +1,153 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::BinanceAddress; +use crate::amino::AminoEncoder; +use crate::transaction::message::{message_to_json, BinanceMessage, Token}; +use serde::Serialize; +use serde_json::Value as Json; +use tw_coin_entry::coin_entry::CoinAddress; +use tw_coin_entry::error::SigningResult; +use tw_encoding::hex::as_hex; +use tw_memory::Data; +use tw_proto::Binance::Proto; + +#[derive(Serialize)] +pub struct HTLTOrder { + pub from: BinanceAddress, + pub to: BinanceAddress, + pub recipient_other_chain: String, + pub sender_other_chain: String, + #[serde(serialize_with = "as_hex")] + pub random_number_hash: Data, + pub timestamp: i64, + pub amount: Vec, + pub expected_income: String, + pub height_span: i64, + pub cross_chain: bool, +} + +impl HTLTOrder { + /// cbindgen:ignore + pub const PREFIX: [u8; 4] = [0xB3, 0x3F, 0x9A, 0x24]; +} + +impl BinanceMessage for HTLTOrder { + fn to_json(&self) -> SigningResult { + message_to_json(self) + } + + fn to_amino_protobuf(&self) -> SigningResult { + let msg = Proto::HTLTOrder { + from: self.from.data().into(), + to: self.to.data().into(), + recipient_other_chain: self.recipient_other_chain.clone().into(), + sender_other_chain: self.sender_other_chain.clone().into(), + random_number_hash: self.random_number_hash.clone().into(), + timestamp: self.timestamp, + amount: self.amount.iter().map(Token::to_proto).collect(), + expected_income: self.expected_income.clone().into(), + height_span: self.height_span, + cross_chain: self.cross_chain, + }; + + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&msg)? + .encode()) + } +} + +#[derive(Serialize)] +pub struct DepositHTLTOrder { + pub from: BinanceAddress, + pub amount: Vec, + #[serde(serialize_with = "as_hex")] + pub swap_id: Data, +} + +impl DepositHTLTOrder { + /// cbindgen:ignore + pub const PREFIX: [u8; 4] = [0x63, 0x98, 0x64, 0x96]; +} + +impl BinanceMessage for DepositHTLTOrder { + fn to_json(&self) -> SigningResult { + message_to_json(self) + } + + fn to_amino_protobuf(&self) -> SigningResult { + let msg = Proto::DepositHTLTOrder { + from: self.from.data().into(), + amount: self.amount.iter().map(Token::to_proto).collect(), + swap_id: self.swap_id.clone().into(), + }; + + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&msg)? + .encode()) + } +} + +#[derive(Serialize)] +pub struct ClaimHTLTOrder { + pub from: BinanceAddress, + #[serde(serialize_with = "as_hex")] + pub swap_id: Data, + #[serde(serialize_with = "as_hex")] + pub random_number: Data, +} + +impl ClaimHTLTOrder { + /// cbindgen:ignore + pub const PREFIX: [u8; 4] = [0xC1, 0x66, 0x53, 0x00]; +} + +impl BinanceMessage for ClaimHTLTOrder { + fn to_json(&self) -> SigningResult { + message_to_json(self) + } + + fn to_amino_protobuf(&self) -> SigningResult { + let msg = Proto::ClaimHTLOrder { + from: self.from.data().into(), + swap_id: self.swap_id.clone().into(), + random_number: self.random_number.clone().into(), + }; + + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&msg)? + .encode()) + } +} + +#[derive(Serialize)] +pub struct RefundHTLTOrder { + pub from: BinanceAddress, + #[serde(serialize_with = "as_hex")] + pub swap_id: Data, +} + +impl RefundHTLTOrder { + /// cbindgen:ignore + pub const PREFIX: [u8; 4] = [0x34, 0x54, 0xA2, 0x7C]; +} + +impl BinanceMessage for RefundHTLTOrder { + fn to_json(&self) -> SigningResult { + message_to_json(self) + } + + fn to_amino_protobuf(&self) -> SigningResult { + let msg = Proto::RefundHTLTOrder { + from: self.from.data().into(), + swap_id: self.swap_id.clone().into(), + }; + + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&msg)? + .encode()) + } +} diff --git a/rust/chains/tw_binance/src/transaction/message/mod.rs b/rust/chains/tw_binance/src/transaction/message/mod.rs new file mode 100644 index 00000000000..6729a4276e4 --- /dev/null +++ b/rust/chains/tw_binance/src/transaction/message/mod.rs @@ -0,0 +1,84 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde::ser::Error as SerError; +use serde::{Serialize, Serializer}; +use serde_json::Value as Json; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_memory::Data; +use tw_proto::Binance::Proto; + +pub mod htlt_order; +pub mod send_order; +pub mod side_chain_delegate; +pub mod time_lock_order; +pub mod token_order; +pub mod trade_order; +pub mod tranfer_out_order; + +pub type BinanceMessageBox = Box; + +pub trait BinanceMessage { + fn into_boxed(self) -> BinanceMessageBox + where + Self: 'static + Sized, + { + Box::new(self) + } + + fn to_json(&self) -> SigningResult; + + fn to_amino_protobuf(&self) -> SigningResult; +} + +impl Serialize for dyn BinanceMessage { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.to_json() + .map_err(|e| SerError::custom(format!("{e:?}")))? + .serialize(serializer) + } +} + +pub fn message_to_json(msg: T) -> SigningResult { + serde_json::to_value(msg).map_err(|_| SigningError(SigningErrorType::Error_internal)) +} + +#[derive(Serialize)] +pub struct Token { + /// Token ID. + pub denom: String, + /// Amount. + pub amount: i64, +} + +impl Token { + pub fn to_proto(&self) -> Proto::mod_SendOrder::Token { + Proto::mod_SendOrder::Token { + denom: self.denom.clone().into(), + amount: self.amount, + } + } + + pub fn serialize_with_string_amount(&self, serializer: S) -> Result + where + S: Serializer, + { + #[derive(Serialize)] + struct TokenWithStringAmount<'a> { + denom: &'a str, + amount: String, + } + + TokenWithStringAmount { + denom: &self.denom, + amount: self.amount.to_string(), + } + .serialize(serializer) + } +} diff --git a/rust/chains/tw_binance/src/transaction/message/send_order.rs b/rust/chains/tw_binance/src/transaction/message/send_order.rs new file mode 100644 index 00000000000..f8e5632a85d --- /dev/null +++ b/rust/chains/tw_binance/src/transaction/message/send_order.rs @@ -0,0 +1,69 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::BinanceAddress; +use crate::amino::AminoEncoder; +use crate::transaction::message::{message_to_json, BinanceMessage, Token}; +use serde::Serialize; +use serde_json::Value as Json; +use tw_coin_entry::coin_entry::CoinAddress; +use tw_coin_entry::error::SigningResult; +use tw_memory::Data; +use tw_proto::Binance::Proto; + +/// Either an input or output of a `SendOrder`. +#[derive(Serialize)] +pub struct InOut { + /// Source address. + pub address: BinanceAddress, + + /// Input coin amounts. + pub coins: Vec, +} + +impl InOut { + pub fn to_input_proto(&self) -> Proto::mod_SendOrder::Input { + Proto::mod_SendOrder::Input { + address: self.address.data().into(), + coins: self.coins.iter().map(Token::to_proto).collect(), + } + } + + pub fn to_output_proto(&self) -> Proto::mod_SendOrder::Output { + Proto::mod_SendOrder::Output { + address: self.address.data().into(), + coins: self.coins.iter().map(Token::to_proto).collect(), + } + } +} + +#[derive(Serialize)] +pub struct SendOrder { + pub inputs: Vec, + pub outputs: Vec, +} + +impl SendOrder { + /// cbindgen:ignore + pub const PREFIX: [u8; 4] = [0x2A, 0x2C, 0x87, 0xFA]; +} + +impl BinanceMessage for SendOrder { + fn to_json(&self) -> SigningResult { + message_to_json(self) + } + + fn to_amino_protobuf(&self) -> SigningResult { + let msg = Proto::SendOrder { + inputs: self.inputs.iter().map(InOut::to_input_proto).collect(), + outputs: self.outputs.iter().map(InOut::to_output_proto).collect(), + }; + + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&msg)? + .encode()) + } +} diff --git a/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs b/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs new file mode 100644 index 00000000000..d376e530eee --- /dev/null +++ b/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs @@ -0,0 +1,138 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::BinanceAddress; +use crate::amino::AminoEncoder; +use crate::transaction::message::{message_to_json, BinanceMessage, Token}; +use serde::Serialize; +use serde_json::Value as Json; +use tw_coin_entry::coin_entry::CoinAddress; +use tw_coin_entry::error::SigningResult; +use tw_cosmos_sdk::modules::serializer::json_serializer::AnyMsg; +use tw_memory::Data; +use tw_proto::Binance::Proto; + +/// cosmos-sdk/MsgSideChainDelegate +#[derive(Serialize)] +pub struct SideDelegateOrder { + pub delegator_addr: BinanceAddress, + pub validator_addr: BinanceAddress, + #[serde(serialize_with = "Token::serialize_with_string_amount")] + pub delegation: Token, + pub side_chain_id: String, +} + +impl SideDelegateOrder { + /// cbindgen:ignore + pub const PREFIX: [u8; 4] = [0xE3, 0xA0, 0x7F, 0xD2]; + /// cbindgen:ignore + pub const MESSAGE_TYPE: &'static str = "cosmos-sdk/MsgSideChainDelegate"; +} + +impl BinanceMessage for SideDelegateOrder { + fn to_json(&self) -> SigningResult { + let any_msg = AnyMsg { + msg_type: Self::MESSAGE_TYPE.to_string(), + value: message_to_json(self)?, + }; + message_to_json(any_msg) + } + + fn to_amino_protobuf(&self) -> SigningResult { + let msg = Proto::SideChainDelegate { + delegator_addr: self.delegator_addr.data().into(), + validator_addr: self.validator_addr.data().into(), + delegation: Some(self.delegation.to_proto()), + chain_id: self.side_chain_id.clone().into(), + }; + + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&msg)? + .encode()) + } +} + +/// cosmos-sdk/MsgSideChainRedelegate +#[derive(Serialize)] +pub struct SideRedelegateOrder { + pub delegator_addr: BinanceAddress, + pub validator_src_addr: BinanceAddress, + pub validator_dst_addr: BinanceAddress, + #[serde(serialize_with = "Token::serialize_with_string_amount")] + pub amount: Token, + pub side_chain_id: String, +} + +impl SideRedelegateOrder { + /// cbindgen:ignore + pub const PREFIX: [u8; 4] = [0xE3, 0xCE, 0xD3, 0x64]; + /// cbindgen:ignore + pub const MESSAGE_TYPE: &'static str = "cosmos-sdk/MsgSideChainRedelegate"; +} + +impl BinanceMessage for SideRedelegateOrder { + fn to_json(&self) -> SigningResult { + let any_msg = AnyMsg { + msg_type: Self::MESSAGE_TYPE.to_string(), + value: message_to_json(self)?, + }; + message_to_json(any_msg) + } + + fn to_amino_protobuf(&self) -> SigningResult { + let msg = Proto::SideChainRedelegate { + delegator_addr: self.delegator_addr.data().into(), + validator_src_addr: self.validator_src_addr.data().into(), + validator_dst_addr: self.validator_dst_addr.data().into(), + amount: Some(self.amount.to_proto()), + chain_id: self.side_chain_id.clone().into(), + }; + + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&msg)? + .encode()) + } +} + +/// cosmos-sdk/MsgSideChainUndelegate +#[derive(Serialize)] +pub struct SideUndelegateOrder { + pub delegator_addr: BinanceAddress, + pub validator_addr: BinanceAddress, + #[serde(serialize_with = "Token::serialize_with_string_amount")] + pub amount: Token, + pub side_chain_id: String, +} + +impl SideUndelegateOrder { + /// cbindgen:ignore + pub const PREFIX: [u8; 4] = [0x51, 0x4F, 0x7E, 0x0E]; + /// cbindgen:ignore + pub const MESSAGE_TYPE: &'static str = "cosmos-sdk/MsgSideChainUndelegate"; +} + +impl BinanceMessage for SideUndelegateOrder { + fn to_json(&self) -> SigningResult { + let any_msg = AnyMsg { + msg_type: Self::MESSAGE_TYPE.to_string(), + value: message_to_json(self)?, + }; + message_to_json(any_msg) + } + + fn to_amino_protobuf(&self) -> SigningResult { + let msg = Proto::SideChainUndelegate { + delegator_addr: self.delegator_addr.data().into(), + validator_addr: self.validator_addr.data().into(), + amount: Some(self.amount.to_proto()), + chain_id: self.side_chain_id.clone().into(), + }; + + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&msg)? + .encode()) + } +} diff --git a/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs b/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs new file mode 100644 index 00000000000..83dbdd6cf24 --- /dev/null +++ b/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs @@ -0,0 +1,115 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::BinanceAddress; +use crate::amino::AminoEncoder; +use crate::transaction::message::{message_to_json, BinanceMessage, Token}; +use serde::Serialize; +use serde_json::Value as Json; +use tw_coin_entry::coin_entry::CoinAddress; +use tw_coin_entry::error::SigningResult; +use tw_memory::Data; +use tw_proto::Binance::Proto; + +#[derive(Serialize)] +pub struct TimeLockOrder { + pub from: BinanceAddress, + pub description: String, + pub amount: Vec, + pub lock_time: i64, +} + +impl TimeLockOrder { + /// cbindgen:ignore + pub const PREFIX: [u8; 4] = [0x07, 0x92, 0x15, 0x31]; +} + +impl BinanceMessage for TimeLockOrder { + fn to_json(&self) -> SigningResult { + message_to_json(self) + } + + fn to_amino_protobuf(&self) -> SigningResult { + let msg = Proto::TimeLockOrder { + from_address: self.from.data().into(), + description: self.description.clone().into(), + amount: self.amount.iter().map(Token::to_proto).collect(), + lock_time: self.lock_time, + }; + + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&msg)? + .encode()) + } +} + +#[derive(Serialize)] +pub struct TimeRelockOrder { + pub from: BinanceAddress, + pub time_lock_id: i64, + pub description: String, + /// If the amount is empty or omitted, set null to avoid signature verification error. + pub amount: Option>, + pub lock_time: i64, +} + +impl TimeRelockOrder { + /// cbindgen:ignore + pub const PREFIX: [u8; 4] = [0x50, 0x47, 0x11, 0xDA]; +} + +impl BinanceMessage for TimeRelockOrder { + fn to_json(&self) -> SigningResult { + message_to_json(self) + } + + fn to_amino_protobuf(&self) -> SigningResult { + let amount = match self.amount { + Some(ref tokens) => tokens.iter().map(Token::to_proto).collect(), + None => Vec::default(), + }; + + let msg = Proto::TimeRelockOrder { + from_address: self.from.data().into(), + id: self.time_lock_id, + description: self.description.clone().into(), + amount, + lock_time: self.lock_time, + }; + + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&msg)? + .encode()) + } +} + +#[derive(Serialize)] +pub struct TimeUnlockOrder { + pub from: BinanceAddress, + pub time_lock_id: i64, +} + +impl TimeUnlockOrder { + /// cbindgen:ignore + pub const PREFIX: [u8; 4] = [0xC4, 0x05, 0x0C, 0x6C]; +} + +impl BinanceMessage for TimeUnlockOrder { + fn to_json(&self) -> SigningResult { + message_to_json(self) + } + + fn to_amino_protobuf(&self) -> SigningResult { + let msg = Proto::TimeUnlockOrder { + from_address: self.from.data().into(), + id: self.time_lock_id, + }; + + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&msg)? + .encode()) + } +} diff --git a/rust/chains/tw_binance/src/transaction/message/token_order.rs b/rust/chains/tw_binance/src/transaction/message/token_order.rs new file mode 100644 index 00000000000..16f2594fbcb --- /dev/null +++ b/rust/chains/tw_binance/src/transaction/message/token_order.rs @@ -0,0 +1,169 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::BinanceAddress; +use crate::amino::AminoEncoder; +use crate::transaction::message::{message_to_json, BinanceMessage}; +use serde::Serialize; +use serde_json::Value as Json; +use tw_coin_entry::coin_entry::CoinAddress; +use tw_coin_entry::error::SigningResult; +use tw_memory::Data; +use tw_proto::Binance::Proto; + +#[derive(Serialize)] +pub struct TokenFreezeOrder { + pub from: BinanceAddress, + pub symbol: String, + pub amount: i64, +} + +impl TokenFreezeOrder { + /// cbindgen:ignore + pub const PREFIX: [u8; 4] = [0xE7, 0x74, 0xB3, 0x2D]; +} + +impl BinanceMessage for TokenFreezeOrder { + fn to_json(&self) -> SigningResult { + message_to_json(self) + } + + fn to_amino_protobuf(&self) -> SigningResult { + let msg = Proto::TokenFreezeOrder { + from: self.from.data().into(), + symbol: self.symbol.clone().into(), + amount: self.amount, + }; + + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&msg)? + .encode()) + } +} + +#[derive(Serialize)] +pub struct TokenUnfreezeOrder { + pub from: BinanceAddress, + pub symbol: String, + pub amount: i64, +} + +impl TokenUnfreezeOrder { + /// cbindgen:ignore + pub const PREFIX: [u8; 4] = [0x65, 0x15, 0xFF, 0x0D]; +} + +impl BinanceMessage for TokenUnfreezeOrder { + fn to_json(&self) -> SigningResult { + message_to_json(self) + } + + fn to_amino_protobuf(&self) -> SigningResult { + let msg = Proto::TokenUnfreezeOrder { + from: self.from.data().into(), + symbol: self.symbol.clone().into(), + amount: self.amount, + }; + + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&msg)? + .encode()) + } +} + +#[derive(Serialize)] +pub struct TokenIssueOrder { + pub from: BinanceAddress, + pub name: String, + pub symbol: String, + pub total_supply: i64, + pub mintable: bool, +} + +impl TokenIssueOrder { + /// cbindgen:ignore + pub const PREFIX: [u8; 4] = [0x17, 0xEF, 0xAB, 0x80]; +} + +impl BinanceMessage for TokenIssueOrder { + fn to_json(&self) -> SigningResult { + message_to_json(self) + } + + fn to_amino_protobuf(&self) -> SigningResult { + let msg = Proto::TokenIssueOrder { + from: self.from.data().into(), + name: self.name.clone().into(), + symbol: self.symbol.clone().into(), + total_supply: self.total_supply, + mintable: self.mintable, + }; + + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&msg)? + .encode()) + } +} + +#[derive(Serialize)] +pub struct TokenMintOrder { + pub from: BinanceAddress, + pub symbol: String, + pub amount: i64, +} + +impl TokenMintOrder { + /// cbindgen:ignore + pub const PREFIX: [u8; 4] = [0x46, 0x7E, 0x08, 0x29]; +} + +impl BinanceMessage for TokenMintOrder { + fn to_json(&self) -> SigningResult { + message_to_json(self) + } + + fn to_amino_protobuf(&self) -> SigningResult { + let msg = Proto::TokenMintOrder { + from: self.from.data().into(), + symbol: self.symbol.clone().into(), + amount: self.amount, + }; + + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&msg)? + .encode()) + } +} + +#[derive(Serialize)] +pub struct TokenBurnOrder { + pub from: BinanceAddress, + pub symbol: String, + pub amount: i64, +} + +impl TokenBurnOrder { + /// cbindgen:ignore + pub const PREFIX: [u8; 4] = [0x7E, 0xD2, 0xD2, 0xA0]; +} + +impl BinanceMessage for TokenBurnOrder { + fn to_json(&self) -> SigningResult { + message_to_json(self) + } + + fn to_amino_protobuf(&self) -> SigningResult { + let msg = Proto::TokenBurnOrder { + from: self.from.data().into(), + symbol: self.symbol.clone().into(), + amount: self.amount, + }; + + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&msg)? + .encode()) + } +} diff --git a/rust/chains/tw_binance/src/transaction/message/trade_order.rs b/rust/chains/tw_binance/src/transaction/message/trade_order.rs new file mode 100644 index 00000000000..8e02780e214 --- /dev/null +++ b/rust/chains/tw_binance/src/transaction/message/trade_order.rs @@ -0,0 +1,103 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::BinanceAddress; +use crate::amino::AminoEncoder; +use crate::transaction::message::{message_to_json, BinanceMessage}; +use serde::Serialize; +use serde_json::Value as Json; +use tw_coin_entry::coin_entry::CoinAddress; +use tw_coin_entry::error::SigningResult; +use tw_memory::Data; +use tw_proto::Binance::Proto; + +#[repr(i64)] +#[derive(Clone, Copy, serde_repr::Serialize_repr, strum_macros::FromRepr)] +pub enum OrderType { + /// https://github.com/bnb-chain/python-sdk/blob/0f6b8a6077f486b26eda3e448f3e84ef35bfff75/binance_chain/constants.py#L62 + Limit = 2, +} + +#[derive(Serialize)] +pub struct NewTradeOrder { + /// Order id, optional. + pub id: String, + /// Order type. + #[serde(rename = "ordertype")] + pub order_type: OrderType, + /// Price of the order, which is the real price multiplied by 1e8 (10^8) and rounded to integer. + pub price: i64, + /// Quantity of the order, which is the real price multiplied by 1e8 (10^8) and rounded to integer. + pub quantity: i64, + /// Originating address. + pub sender: BinanceAddress, + /// 1 for buy and 2 for sell. + pub side: i64, + /// Symbol for trading pair in full name of the tokens. + pub symbol: String, + /// 1 for Good Till Expire(GTE) order and 3 for Immediate Or Cancel (IOC). + #[serde(rename = "timeinforce")] + pub time_in_force: i64, +} + +impl NewTradeOrder { + /// cbindgen:ignore + pub const PREFIX: [u8; 4] = [0xCE, 0x6D, 0xC0, 0x43]; +} + +impl BinanceMessage for NewTradeOrder { + fn to_json(&self) -> SigningResult { + message_to_json(self) + } + + fn to_amino_protobuf(&self) -> SigningResult { + let msg = Proto::TradeOrder { + id: self.id.clone().into(), + ordertype: self.order_type as i64, + price: self.price, + quantity: self.quantity, + sender: self.sender.data().into(), + side: self.side, + symbol: self.symbol.clone().into(), + timeinforce: self.time_in_force, + }; + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&msg)? + .encode()) + } +} + +#[derive(Serialize)] +pub struct CancelTradeOrder { + /// Originating address. + pub sender: BinanceAddress, + /// Symbol for trading pair in full name of the tokens. + pub symbol: String, + /// Order id to cancel. + pub refid: String, +} + +impl CancelTradeOrder { + /// cbindgen:ignore + pub const PREFIX: [u8; 4] = [0x16, 0x6E, 0x68, 0x1B]; +} + +impl BinanceMessage for CancelTradeOrder { + fn to_json(&self) -> SigningResult { + message_to_json(self) + } + + fn to_amino_protobuf(&self) -> SigningResult { + let msg = Proto::CancelTradeOrder { + sender: self.sender.data().into(), + symbol: self.symbol.clone().into(), + refid: self.refid.clone().into(), + }; + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&msg)? + .encode()) + } +} diff --git a/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs b/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs new file mode 100644 index 00000000000..3f838793e11 --- /dev/null +++ b/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs @@ -0,0 +1,48 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::BinanceAddress; +use crate::amino::AminoEncoder; +use crate::transaction::message::{message_to_json, BinanceMessage, Token}; +use serde::Serialize; +use serde_json::Value as Json; +use tw_coin_entry::coin_entry::CoinAddress; +use tw_coin_entry::error::SigningResult; +use tw_evm::address::Address as EthereumAddress; +use tw_memory::Data; +use tw_proto::Binance::Proto; + +#[derive(Serialize)] +pub struct TransferOutOrder { + pub from: BinanceAddress, + pub to: EthereumAddress, + pub amount: Token, + pub expire_time: i64, +} + +impl TransferOutOrder { + /// cbindgen:ignore + pub const PREFIX: [u8; 4] = [0x80, 0x08, 0x19, 0xC0]; +} + +impl BinanceMessage for TransferOutOrder { + fn to_json(&self) -> SigningResult { + message_to_json(self) + } + + fn to_amino_protobuf(&self) -> SigningResult { + let msg = Proto::TransferOut { + from: self.from.data().into(), + to: self.to.data().into(), + amount: Some(self.amount.to_proto()), + expire_time: self.expire_time, + }; + + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&msg)? + .encode()) + } +} diff --git a/rust/chains/tw_binance/src/transaction/mod.rs b/rust/chains/tw_binance/src/transaction/mod.rs new file mode 100644 index 00000000000..74e96f2595b --- /dev/null +++ b/rust/chains/tw_binance/src/transaction/mod.rs @@ -0,0 +1,47 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::signature::BinanceSignature; +use crate::transaction::message::BinanceMessageBox; +use serde::Serialize; +use tw_keypair::ecdsa::secp256k1; +use tw_memory::Data; +use tw_misc::serde::as_string; + +pub mod message; + +#[derive(Serialize)] +pub struct UnsignedTransaction { + #[serde(serialize_with = "as_string")] + pub account_number: i64, + pub chain_id: String, + pub data: Option, + pub memo: String, + pub msgs: Vec, + #[serde(serialize_with = "as_string")] + pub sequence: i64, + #[serde(serialize_with = "as_string")] + pub source: i64, +} + +impl UnsignedTransaction { + pub fn into_signed(self, signer: SignerInfo) -> SignedTransaction { + SignedTransaction { + unsigned: self, + signer, + } + } +} + +pub struct SignerInfo { + pub public_key: secp256k1::PublicKey, + pub signature: BinanceSignature, +} + +pub struct SignedTransaction { + pub unsigned: UnsignedTransaction, + pub signer: SignerInfo, +} diff --git a/rust/coverage.stats b/rust/coverage.stats index 7d7ab43dc7c..f2511044fdd 100644 --- a/rust/coverage.stats +++ b/rust/coverage.stats @@ -1 +1 @@ -92.0 \ No newline at end of file +93.0 \ No newline at end of file diff --git a/rust/tw_any_coin/Cargo.toml b/rust/tw_any_coin/Cargo.toml index e2aeea6d021..d2f85f6fa4d 100644 --- a/rust/tw_any_coin/Cargo.toml +++ b/rust/tw_any_coin/Cargo.toml @@ -11,17 +11,22 @@ tw_hash = { path = "../tw_hash" } tw_keypair = { path = "../tw_keypair" } tw_memory = { path = "../tw_memory" } tw_misc = { path = "../tw_misc" } +tw_proto = { path = "../tw_proto", optional = true } [features] -test-utils = [] +test-utils = [ + "tw_keypair/test-utils", + "tw_memory/test-utils", + "tw_misc/test-utils", + "tw_proto" +] [dev-dependencies] -serde = { version = "1.0.163", features = ["derive"] } -serde_json = { version = "1.0.96" } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" tw_any_coin = { path = "./", features = ["test-utils"] } tw_cosmos_sdk = { path = "../tw_cosmos_sdk", features = ["test-utils"] } tw_keypair = { path = "../tw_keypair", features = ["test-utils"] } tw_memory = { path = "../tw_memory", features = ["test-utils"] } tw_misc = { path = "../tw_misc", features = ["test-utils"] } tw_number = { path = "../tw_number" } -tw_proto = { path = "../tw_proto" } diff --git a/rust/tw_any_coin/src/test_utils/mod.rs b/rust/tw_any_coin/src/test_utils/mod.rs index 7bdd5ac4e99..098e098ea64 100644 --- a/rust/tw_any_coin/src/test_utils/mod.rs +++ b/rust/tw_any_coin/src/test_utils/mod.rs @@ -5,3 +5,4 @@ // file LICENSE at the root of the source code distribution tree. pub mod address_utils; +pub mod sign_utils; diff --git a/rust/tw_any_coin/src/test_utils/sign_utils.rs b/rust/tw_any_coin/src/test_utils/sign_utils.rs new file mode 100644 index 00000000000..ff61b07dcce --- /dev/null +++ b/rust/tw_any_coin/src/test_utils/sign_utils.rs @@ -0,0 +1,96 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ffi::tw_any_signer::tw_any_signer_sign; +use crate::ffi::tw_transaction_compiler::{ + tw_transaction_compiler_compile, tw_transaction_compiler_pre_image_hashes, +}; +use std::marker::PhantomData; +use tw_coin_registry::coin_type::CoinType; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_memory::test_utils::tw_data_vector_helper::TWDataVectorHelper; +use tw_memory::Data; +use tw_proto::{deserialize, serialize, MessageRead, MessageWrite}; + +#[derive(Default)] +pub struct AnySignerHelper<'a, Output: MessageRead<'a>> { + output_data: Data, + _output_type: PhantomData<&'a Output>, +} + +impl<'a, Output: MessageRead<'a>> AnySignerHelper<'a, Output> { + pub fn sign(&'a mut self, coin_type: CoinType, input: Input) -> Output { + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + self.output_data = + TWDataHelper::wrap(unsafe { tw_any_signer_sign(input_data.ptr(), coin_type as u32) }) + .to_vec() + .expect("!tw_any_signer_sign returned nullptr"); + + let output: Output = deserialize(&self.output_data).unwrap(); + output + } +} + +#[derive(Default)] +pub struct PreImageHelper<'a, Output: MessageRead<'a>> { + output_data: Data, + _output_type: PhantomData<&'a Output>, +} + +impl<'a, Output: MessageRead<'a>> PreImageHelper<'a, Output> { + pub fn pre_image_hashes( + &'a mut self, + coin_type: CoinType, + input: &Input, + ) -> Output { + let input_data = TWDataHelper::create(serialize(input).unwrap()); + + self.output_data = TWDataHelper::wrap(unsafe { + tw_transaction_compiler_pre_image_hashes(coin_type as u32, input_data.ptr()) + }) + .to_vec() + .expect("!tw_transaction_compiler_pre_image_hashes returned nullptr"); + + let output: Output = deserialize(&self.output_data).unwrap(); + output + } +} + +#[derive(Default)] +pub struct CompilerHelper<'a, Output: MessageRead<'a>> { + output_data: Data, + _output_type: PhantomData<&'a Output>, +} + +impl<'a, Output: MessageRead<'a>> CompilerHelper<'a, Output> { + pub fn compile( + &'a mut self, + coin_type: CoinType, + input: &Input, + signatures: Vec, + public_keys: Vec, + ) -> Output { + let input_data = TWDataHelper::create(serialize(input).unwrap()); + + let signatures = TWDataVectorHelper::create(signatures); + let public_keys = TWDataVectorHelper::create(public_keys); + + self.output_data = TWDataHelper::wrap(unsafe { + tw_transaction_compiler_compile( + coin_type as u32, + input_data.ptr(), + signatures.ptr(), + public_keys.ptr(), + ) + }) + .to_vec() + .expect("!tw_transaction_compiler_compile returned nullptr"); + + let output: Output = deserialize(&self.output_data).unwrap(); + output + } +} diff --git a/rust/tw_any_coin/tests/chains/binance/binance_address.rs b/rust/tw_any_coin/tests/chains/binance/binance_address.rs new file mode 100644 index 00000000000..d915ef496a8 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/binance/binance_address.rs @@ -0,0 +1,65 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_any_coin::test_utils::address_utils::{ + test_address_bech32_is_valid, test_address_get_data, test_address_invalid, + test_address_normalization, test_address_valid, AddressBech32IsValid, +}; +use tw_coin_registry::coin_type::CoinType; + +#[test] +fn test_binance_address_normalization() { + test_address_normalization( + CoinType::Binance, + "bnb1h8xf9htasu9aclra954dnnve8fgcda4ae7qfa8", + "bnb1h8xf9htasu9aclra954dnnve8fgcda4ae7qfa8", + ); +} + +#[test] +fn test_binance_address_is_valid() { + test_address_valid( + CoinType::Binance, + "bnb1h8xf9htasu9aclra954dnnve8fgcda4ae7qfa8", + ); + // Validator address. + test_address_valid( + CoinType::Binance, + "bva10npy5809y303f227g4leqw7vs3s6ep5ul26sq2", + ); +} + +#[test] +fn test_binance_address_invalid() { + // Testnet address. + test_address_invalid( + CoinType::Binance, + "tbnb1x4hxmtdf7pwea9dghq73dufh3qspm8gp5qht9c", + ); + // Unknown `bfa` hrp. + test_address_invalid( + CoinType::Binance, + "bfa10npy5809y303f227g4leqw7vs3s6ep5ul26sq2", + ); +} + +#[test] +fn test_binance_address_get_data() { + test_address_get_data( + CoinType::Binance, + "bnb1h8xf9htasu9aclra954dnnve8fgcda4ae7qfa8", + "b9cc92dd7d870bdc7c7d2d2ad9cd993a5186f6bd", + ); +} + +#[test] +fn test_binance_address_is_valid_bech32() { + test_address_bech32_is_valid(AddressBech32IsValid { + coin: CoinType::Binance, + address: "bva10npy5809y303f227g4leqw7vs3s6ep5ul26sq2", + hrp: "bva", + }); +} diff --git a/rust/tw_any_coin/tests/chains/binance/binance_compile.rs b/rust/tw_any_coin/tests/chains/binance/binance_compile.rs new file mode 100644 index 00000000000..f1104b9657e --- /dev/null +++ b/rust/tw_any_coin/tests/chains/binance/binance_compile.rs @@ -0,0 +1,77 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::chains::binance::make_token; +use tw_any_coin::test_utils::sign_utils::{CompilerHelper, PreImageHelper}; +use tw_coin_registry::coin_type::CoinType; +use tw_encoding::hex::DecodeHex; +use tw_encoding::hex::ToHex; +use tw_proto::Binance::Proto; +use tw_proto::Binance::Proto::mod_SigningInput::OneOforder_oneof as OrderType; +use tw_proto::Common::Proto::SigningError; +use tw_proto::TxCompiler::Proto as CompilerProto; + +#[test] +fn test_binance_compile() { + // bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2 + let from_address_key_hash = "40c2979694bbc961023d1d27be6fc4d21a9febe6"; + // bnb1hlly02l6ahjsgxw9wlcswnlwdhg4xhx38yxpd5 + let to_address_key_hash = "bffe47abfaede50419c577f1074fee6dd1535cd1"; + + let send_order = Proto::SendOrder { + inputs: vec![Proto::mod_SendOrder::Input { + address: from_address_key_hash.decode_hex().unwrap().into(), + coins: vec![make_token("BNB", 1)], + }], + outputs: vec![Proto::mod_SendOrder::Output { + address: to_address_key_hash.decode_hex().unwrap().into(), + coins: vec![make_token("BNB", 1)], + }], + }; + let input = Proto::SigningInput { + // testnet chainId + chain_id: "Binance-Chain-Nile".into(), + order_oneof: OrderType::send_order(send_order), + ..Proto::SigningInput::default() + }; + + // Step 2: Obtain preimage hash + let mut pre_imager = PreImageHelper::::default(); + let preimage_output = pre_imager.pre_image_hashes(CoinType::Binance, &input); + + assert_eq!(preimage_output.error, SigningError::OK); + assert_eq!( + preimage_output.data.to_hex(), + "7b226163636f756e745f6e756d626572223a2230222c22636861696e5f6964223a2242696e616e63652d436861696e2d4e696c65222c2264617461223a6e756c6c2c226d656d6f223a22222c226d736773223a5b7b22696e70757473223a5b7b2261646472657373223a22626e623167727066303935356830796b7a71336172356e6d756d3779366764666c366c78666e34366832222c22636f696e73223a5b7b22616d6f756e74223a312c2264656e6f6d223a22424e42227d5d7d5d2c226f757470757473223a5b7b2261646472657373223a22626e6231686c6c7930326c3661686a7367787739776c6373776e6c776468673478687833387978706435222c22636f696e73223a5b7b22616d6f756e74223a312c2264656e6f6d223a22424e42227d5d7d5d7d5d2c2273657175656e6365223a2230222c22736f75726365223a2230227d" + ); + assert_eq!( + preimage_output.data_hash.to_hex(), + "3f3fece9059e714d303a9a1496ddade8f2c38fa78fc4cc2e505c5dbb0ea678d1" + ); + + // Step 3: Compile transaction info + + // Simulate signature, normally obtained from signature server. + let signature = "1b1181faec30b60a2ddaa2804c253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a504f9e8310679" + .decode_hex() + .unwrap(); + let public_key = "026a35920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e502" + .decode_hex() + .unwrap(); + + let mut compiler = CompilerHelper::::default(); + let output = compiler.compile(CoinType::Binance, &input, vec![signature], vec![public_key]); + + assert_eq!(output.error, SigningError::OK); + let expected_tx = concat!( + "b801f0625dee0a462a2c87fa0a1f0a1440c2979694bbc961023d1d27be6fc4d21a9febe612070a03424e421001", + "121f0a14bffe47abfaede50419c577f1074fee6dd1535cd112070a03424e421001126a0a26eb5ae98721026a35", + "920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e50212401b1181faec30b60a2ddaa2804c", + "253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a5", + "04f9e8310679", + ); + assert_eq!(output.encoded.to_hex(), expected_tx); +} diff --git a/rust/tw_any_coin/tests/chains/binance/binance_sign.rs b/rust/tw_any_coin/tests/chains/binance/binance_sign.rs new file mode 100644 index 00000000000..564b750971e --- /dev/null +++ b/rust/tw_any_coin/tests/chains/binance/binance_sign.rs @@ -0,0 +1,687 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::chains::binance::make_token; +use tw_any_coin::test_utils::sign_utils::AnySignerHelper; +use tw_coin_registry::coin_type::CoinType; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_proto::Binance::Proto; +use tw_proto::Binance::Proto::mod_SigningInput::OneOforder_oneof as OrderEnum; + +const ACCOUNT_12_PRIVATE_KEY: &str = + "90335b9d2153ad1a9799a3ccc070bd64b4164e9642ee1dd48053c33f9a3a05e9"; +const ACCOUNT_19_PRIVATE_KEY: &str = + "95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832"; +const ACCOUNT_15_PRIVATE_KEY: &str = + "eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d"; +const ACCOUNT_16_PRIVATE_KEY: &str = + "851fab89c14f4bbec0cc06f5e445ec065efc641068d78b308c67217d9bd5c88a"; + +#[test] +fn test_binance_sign_trade_order() { + // bnb1hgm0p7khfk85zpz5v0j8wnej3a90w709vhkdfu + let sender_key_hash = "ba36f0fad74d8f41045463e4774f328f4af779e5" + .decode_hex() + .unwrap(); + + let new_order = Proto::TradeOrder { + sender: sender_key_hash.into(), + id: "BA36F0FAD74D8F41045463E4774F328F4AF779E5-36".into(), + symbol: "NNB-338_BNB".into(), + ordertype: 2, + side: 1, + price: 136350000, + quantity: 100000000, + timeinforce: 1, + }; + + let input = Proto::SigningInput { + chain_id: "chain-bnb".into(), + account_number: 12, + sequence: 35, + source: 1, + private_key: ACCOUNT_12_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::trade_order(new_order), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Binance, input); + + assert_eq!( + output.encoded.to_hex(), + "dc01f0625dee0a64ce6dc0430a14ba36f0fad74d8f41045463e4774f328f4af779e5122b424133364630464144373444384634313034353436334534373734463332384634414637373945352d33361a0b4e4e422d3333385f424e422002280130b09282413880c2d72f4001126e0a26eb5ae98721029729a52e4e3c2b4a4e52aa74033eedaf8ba1df5ab6d1f518fd69e67bbd309b0e12409123cb6906bb20aeb753f4a121d4d88ff0e9750ba75b0c4e10d76caee1e7d2481290fa3b9887a6225d6997f5f939ef834ea61d596a314237c48e560da9e17b5a180c20232001" + ); +} + +#[test] +fn test_binance_sign_cancel_trade_order() { + // bnb1hgm0p7khfk85zpz5v0j8wnej3a90w709vhkdfu + let sender_key_hash = "ba36f0fad74d8f41045463e4774f328f4af779e5" + .decode_hex() + .unwrap(); + + let new_order = Proto::CancelTradeOrder { + sender: sender_key_hash.into(), + symbol: "NNB-338_BNB".into(), + refid: "BA36F0FAD74D8F41045463E4774F328F4AF779E5-36".into(), + }; + + let input = Proto::SigningInput { + chain_id: "chain-bnb".into(), + account_number: 12, + sequence: 36, + source: 1, + private_key: ACCOUNT_12_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::cancel_trade_order(new_order), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Binance, input); + + assert_eq!( + output.encoded.to_hex(), + "cc01f0625dee0a54166e681b0a14ba36f0fad74d8f41045463e4774f328f4af779e5120b4e4e422d3333385f424e421a2b424133364630464144373444384634313034353436334534373734463332384634414637373945352d3336126e0a26eb5ae98721029729a52e4e3c2b4a4e52aa74033eedaf8ba1df5ab6d1f518fd69e67bbd309b0e12403df6603426b991f7040bce22ce0137c12137df01e1d4d425cf3d9104103aec6335ac05c825e08ba26b9f72aa4cc45aa75cacfb6082df86b00692fef9701eb0f5180c20242001" + ); +} + +#[test] +fn test_binance_sign_send_order() { + let amount = 1_001_000_000; + // bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2 + let from_address_key_hash = "40c2979694bbc961023d1d27be6fc4d21a9febe6"; + let to_address_key_hash = "88b37d5e05f3699e2a1406468e5d87cb9dcceb95"; + + let send_order = Proto::SendOrder { + inputs: vec![Proto::mod_SendOrder::Input { + address: from_address_key_hash.decode_hex().unwrap().into(), + coins: vec![make_token("BNB", amount)], + }], + outputs: vec![Proto::mod_SendOrder::Output { + address: to_address_key_hash.decode_hex().unwrap().into(), + coins: vec![make_token("BNB", amount)], + }], + }; + + let input = Proto::SigningInput { + chain_id: "chain-bnb".into(), + account_number: 19, + sequence: 23, + source: 1, + memo: "test".into(), + private_key: ACCOUNT_19_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::send_order(send_order), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Binance, input); + + let expected_encoded = concat!( + "cc01", + "f0625dee", + "0a4e", + "2a2c87fa", + "0a23", "0a1440c2979694bbc961023d1d27be6fc4d21a9febe6120b0a03424e4210c098a8dd03", + "1223", "0a1488b37d5e05f3699e2a1406468e5d87cb9dcceb95120b0a03424e4210c098a8dd03", + "126e", + "0a26", + "eb5ae987", + "21026a35920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e502", + "1240", "c65a13440f18a155bd971ee40b9e0dd58586f5bf344e12ec4c76c439aebca8c7789bab7bfbfb4ce89aadc4a02df225b6b6efc861c13bbeb5f7a3eea2d7ffc80f", + "1813", + "2017", + "1a04", "74657374", + "2001", + ); + assert_eq!(output.encoded.to_hex(), expected_encoded); +} + +#[test] +fn test_binance_sign_token_freeze_order() { + let from_address_key_hash = "08c7c918f6b72c3c0c21b7d08eb6fc66509998e1"; + + let freeze_order = Proto::TokenFreezeOrder { + from: from_address_key_hash.decode_hex().unwrap().into(), + symbol: "NNB-338_BNB".into(), + amount: 1000000, + }; + + let input = Proto::SigningInput { + chain_id: "test-chain".into(), + account_number: 15, + sequence: 1, + private_key: ACCOUNT_15_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::freeze_order(freeze_order), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Binance, input); + + let expected_encoded = concat!( + "a101f0625dee0a2b", + "e774b32d", + "0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1120b", + "4e4e422d3333385f424e42", + "18c0843d126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f", + "6b6a8fc71240e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162", + "722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991180f2001", + ); + assert_eq!(output.encoded.to_hex(), expected_encoded); +} + +#[test] +fn test_binance_sign_token_unfreeze_order() { + let from_address_key_hash = "08c7c918f6b72c3c0c21b7d08eb6fc66509998e1"; + + let unfreeze_order = Proto::TokenUnfreezeOrder { + from: from_address_key_hash.decode_hex().unwrap().into(), + symbol: "NNB-338_BNB".into(), + amount: 1000000, + }; + + let input = Proto::SigningInput { + chain_id: "test-chain".into(), + account_number: 15, + sequence: 1, + private_key: ACCOUNT_15_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::unfreeze_order(unfreeze_order), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Binance, input); + + let expected_encoded = concat!( + "a101f0625dee0a2b", + "6515ff0d", + "0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1120b", + "4e4e422d3333385f424e42", + "18c0843d126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f", + "6b6a8fc71240e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162", + "722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991180f2001", + ); + assert_eq!(output.encoded.to_hex(), expected_encoded); +} + +#[test] +fn test_binance_sign_token_issue_order() { + let from_address_key_hash = "08c7c918f6b72c3c0c21b7d08eb6fc66509998e1"; + + let issue_order = Proto::TokenIssueOrder { + from: from_address_key_hash.decode_hex().unwrap().into(), + name: "NewBinanceToken".into(), + symbol: "NNB-338_BNB".into(), + total_supply: 1000000000, + mintable: true, + }; + + let input = Proto::SigningInput { + chain_id: "test-chain".into(), + account_number: 15, + sequence: 1, + private_key: ACCOUNT_15_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::issue_order(issue_order), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Binance, input); + + let expected_encoded = concat!( + "b601f0625dee0a40", + "17efab80", + "0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1120f", + "4e657742696e616e6365546f6b656e", + "1a0b", + "4e4e422d3333385f424e42", + "208094ebdc032801126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc712401fbb993d643f03b3e8e757a502035f58c4c45aaaa6e107a3059ab7c6164283c10f1254e87feee21477c64c87b1a27d8481048533ae2f685b3ac0dc66e4edbc0b180f2001", + ); + assert_eq!(output.encoded.to_hex(), expected_encoded); +} + +#[test] +fn test_binance_sign_token_mint_order() { + let from_address_key_hash = "08c7c918f6b72c3c0c21b7d08eb6fc66509998e1"; + + let mint_order = Proto::TokenMintOrder { + from: from_address_key_hash.decode_hex().unwrap().into(), + symbol: "NNB-338_BNB".into(), + amount: 1000000, + }; + + let input = Proto::SigningInput { + chain_id: "test-chain".into(), + account_number: 15, + sequence: 1, + private_key: ACCOUNT_15_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::mint_order(mint_order), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Binance, input); + + let expected_encoded = concat!( + "a101f0625dee0a2b", + "467e0829", + "0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1120b", + "4e4e422d3333385f424e42", + "18c0843d126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991180f2001", + ); + assert_eq!(output.encoded.to_hex(), expected_encoded); +} + +#[test] +fn test_binance_sign_token_burn_order() { + let from_address_key_hash = "08c7c918f6b72c3c0c21b7d08eb6fc66509998e1"; + + let burn_order = Proto::TokenBurnOrder { + from: from_address_key_hash.decode_hex().unwrap().into(), + symbol: "NNB-338_BNB".into(), + amount: 1000000, + }; + + let input = Proto::SigningInput { + chain_id: "test-chain".into(), + account_number: 15, + sequence: 1, + private_key: ACCOUNT_15_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::burn_order(burn_order), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Binance, input); + + let expected_encoded = concat!( + "a101f0625dee0a2b", + "7ed2d2a0", + "0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1120b", + "4e4e422d3333385f424e42", + "18c0843d126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991180f2001", + ); + assert_eq!(output.encoded.to_hex(), expected_encoded); +} + +#[test] +fn test_binance_sign_htlt_order() { + let from_address_key_hash = "08c7c918f6b72c3c0c21b7d08eb6fc66509998e1"; + let to_address_key_hash = "0153f11d6db7e69c7d51e771c697378018fb6c24"; + let random_number_hash = "e8eae926261ab77d018202434791a335249b470246a7b02e28c3b2fb6ffad8f3"; + + let htlt_order = Proto::HTLTOrder { + from: from_address_key_hash.decode_hex().unwrap().into(), + to: to_address_key_hash.decode_hex().unwrap().into(), + random_number_hash: random_number_hash.decode_hex().unwrap().into(), + timestamp: 1_567_746_273, + amount: vec![make_token("BNB", 100000000)], + expected_income: "100000000:BTC-1DC".into(), + height_span: 400, + cross_chain: false, + ..Proto::HTLTOrder::default() + }; + + let input = Proto::SigningInput { + chain_id: "test-chain".into(), + account_number: 15, + sequence: 0, + private_key: ACCOUNT_15_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::htlt_order(htlt_order), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Binance, input); + + let expected_encoded = concat!( + "ee01f0625dee0a7ab33f9a240a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e112140153f11d6db7", + "e69c7d51e771c697378018fb6c242a20e8eae926261ab77d018202434791a335249b470246a7b02e28c3", + "b2fb6ffad8f330e1d1c7eb053a0a0a03424e421080c2d72f42113130303030303030303a4254432d3144", + "43489003126c0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f", + "6b6a8fc7124051439de2da19fe9fd22137c903cfc5dc87553bf05dca0bb202c0e07c47f9b51269efa272", + "43eb7b55888f5384a84ac1eac6d325c830d1be0ed042838e2dc0f6a9180f", + ); + assert_eq!(output.encoded.to_hex(), expected_encoded); +} + +#[test] +fn test_binance_sign_deposit_htlt_order() { + let from_address_key_hash = "0153f11d6db7e69c7d51e771c697378018fb6c24"; + let swap_id = "dd8fd4719741844d35eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e5"; + + let deposit_htlt = Proto::DepositHTLTOrder { + from: from_address_key_hash.decode_hex().unwrap().into(), + amount: vec![make_token("BTC-1DC", 100000000)], + swap_id: swap_id.decode_hex().unwrap().into(), + }; + + let input = Proto::SigningInput { + chain_id: "test-chain".into(), + account_number: 16, + sequence: 0, + private_key: ACCOUNT_16_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::depositHTLT_order(deposit_htlt), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Binance, input); + + let expected_encoded = concat!( + "c001f0625dee0a4c639864960a140153f11d6db7e69c7d51e771c697378018fb6c24120e0a074254432d", + "3144431080c2d72f1a20dd8fd4719741844d35eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e5", + "126c0a26eb5ae98721038df6960084e20b2d07d50e1422f94105c6241d9f1482a4eb79ce8bfd460f19e4", + "12400ca4144c6818e2836d09b4faf3161781d85f9adfc00badb2eaa0953174610a233b81413dafcf8471", + "6abad48a4ed3aeb9884d90eb8416eec5d5c0c6930ab60bd01810", + ); + assert_eq!(output.encoded.to_hex(), expected_encoded); +} + +#[test] +fn test_binance_sign_claim_htlt_order() { + let from_address_key_hash = "08c7c918f6b72c3c0c21b7d08eb6fc66509998e1"; + let swap_id = "dd8fd4719741844d35eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e5"; + let random_number = "bda6933c7757d0ca428aa01fb9d0935a231f87bf2deeb9b409cea3f2d580a2cc"; + + let claim_htlt = Proto::ClaimHTLOrder { + from: from_address_key_hash.decode_hex().unwrap().into(), + swap_id: swap_id.decode_hex().unwrap().into(), + random_number: random_number.decode_hex().unwrap().into(), + }; + + let input = Proto::SigningInput { + chain_id: "test-chain".into(), + account_number: 15, + sequence: 1, + private_key: ACCOUNT_15_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::claimHTLT_order(claim_htlt), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Binance, input); + + let expected_encoded = concat!( + "d401f0625dee0a5ec16653000a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e11220dd8fd4719741844d35", + "eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e51a20bda6933c7757d0ca428aa01fb9d0935a231f87bf", + "2deeb9b409cea3f2d580a2cc126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561a", + "ac993dbe0f6b6a8fc71240fa30ba50111aa31d8329dacb6d044c1c7d54f1cb782bc9aa2a50c3fabce02a4579d7", + "5b76ca69a9fab11b676d9da66b5af7aa4c9ad3d18e24fffeb16433be39fb180f2001", + ); + assert_eq!(output.encoded.to_hex(), expected_encoded); +} + +#[test] +fn test_binance_sign_refund_htlt_order() { + let from_address_key_hash = "08c7c918f6b72c3c0c21b7d08eb6fc66509998e1"; + let swap_id = "dd8fd4719741844d35eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e5"; + + let refund_htlt = Proto::RefundHTLTOrder { + from: from_address_key_hash.decode_hex().unwrap().into(), + swap_id: swap_id.decode_hex().unwrap().into(), + }; + + let input = Proto::SigningInput { + chain_id: "test-chain".into(), + account_number: 15, + sequence: 1, + private_key: ACCOUNT_15_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::refundHTLT_order(refund_htlt), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Binance, input); + + let expected_encoded = concat!( + "b201f0625dee0a3c3454a27c0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e11220dd8fd4719741", + "844d35eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e5126e0a26eb5ae9872103a9a55c040c8e", + "b8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240c9f36142534d16ec8ce656f8eb73", + "70b32206a2d15198b7165acf1e2a18952c9e4570b0f862e1ab7bb868c30781a42c9e3ec0ae08982e8d6c", + "91c55b83c71b7b1e180f2001", + ); + assert_eq!(output.encoded.to_hex(), expected_encoded); +} + +#[test] +fn test_binance_sign_transfer_out_order() { + let from_address_key_hash = "08c7c918f6b72c3c0c21b7d08eb6fc66509998e1"; + let to_address_data = "0x35552c16704d214347f29Fa77f77DA6d75d7C752"; + + let transfer_out = Proto::TransferOut { + from: from_address_key_hash.decode_hex().unwrap().into(), + to: to_address_data.decode_hex().unwrap().into(), + amount: Some(make_token("BNB", 100000000)), + expire_time: 12345678, + }; + + let input = Proto::SigningInput { + chain_id: "test-chain".into(), + account_number: 15, + sequence: 1, + private_key: ACCOUNT_15_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::transfer_out_order(transfer_out), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Binance, input); + + let expected_encoded = concat!( + "b701f0625dee0a41800819c00a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1121435552c16704d", + "214347f29fa77f77da6d75d7c7521a0a0a03424e421080c2d72f20cec2f105126e0a26eb5ae9872103a9", + "a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc712407eda148e1167b1be12", + "71a788ccf4e3eade1c7e1773e9d2093982d7f802f8f85f35ef550049011728206e4eda1a272f9e96fd95", + "ef3983cad85a29cd14262c22e0180f2001", + ); + assert_eq!(output.encoded.to_hex(), expected_encoded); +} + +#[test] +fn test_binance_sign_side_chain_delegate_order() { + let delegator_key_hash = "08c7c918f6b72c3c0c21b7d08eb6fc66509998e1"; + // bva10npy5809y303f227g4leqw7vs3s6ep5ul26sq2 + let validator_key_hash = "7cc24a1de5245f14a95e457f903bcc8461ac869c"; + + let side_delegate = Proto::SideChainDelegate { + delegator_addr: delegator_key_hash.decode_hex().unwrap().into(), + validator_addr: validator_key_hash.decode_hex().unwrap().into(), + delegation: Some(make_token("BNB", 200000000)), + chain_id: "chapel".into(), + }; + + let input = Proto::SigningInput { + chain_id: "test-chain".into(), + account_number: 15, + sequence: 1, + private_key: ACCOUNT_15_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::side_delegate_order(side_delegate), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Binance, input); + + let expected_encoded = concat!( + "ba01f0625dee0a44e3a07fd20a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e112147cc24a1de524", + "5f14a95e457f903bcc8461ac869c1a0a0a03424e42108084af5f220663686170656c126e0a26eb5ae987", + "2103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc7124039302c9975fb", + "2a09ac2b6b6fb1d3b9fb5b4c03630d3d7a7da42b1c6736d6127142a3fcdca0b70a3d065da8d4f4df8b5d", + "9d8f46aeb3627a7d7aa901fe186af34c180f2001", + ); + assert_eq!(output.encoded.to_hex(), expected_encoded); +} + +#[test] +fn test_binance_sign_side_chain_redelegate_order() { + let delegator_key_hash = "08c7c918f6b72c3c0c21b7d08eb6fc66509998e1"; + // bva1echrty7p8r23cwx8g3ezwcza9azy4zq7ca0pzw + let validator_src_key_hash = "ce2e3593c138d51c38c7447227605d2f444a881e"; + // bva1p7s26ervsmv3w83k5696glautc9sm5rchz5f5e + let validator_dst_key_hash = "0fa0ad646c86d9171e36a68ba47fbc5e0b0dd078"; + + let side_redelegate = Proto::SideChainRedelegate { + delegator_addr: delegator_key_hash.decode_hex().unwrap().into(), + validator_src_addr: validator_src_key_hash.decode_hex().unwrap().into(), + validator_dst_addr: validator_dst_key_hash.decode_hex().unwrap().into(), + amount: Some(make_token("BNB", 100000000)), + chain_id: "chapel".into(), + }; + + let input = Proto::SigningInput { + chain_id: "test-chain".into(), + account_number: 15, + sequence: 1, + private_key: ACCOUNT_15_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::side_redelegate_order(side_redelegate), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Binance, input); + + let expected_encoded = concat!( + "d001f0625dee0a5ae3ced3640a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e11214ce2e3593c138", + "d51c38c7447227605d2f444a881e1a140fa0ad646c86d9171e36a68ba47fbc5e0b0dd078220a0a03424e", + "421080c2d72f2a0663686170656c126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08", + "af44ea561aac993dbe0f6b6a8fc71240114c6927423e95ecc831ec763b629b3a40db8feeb330528a941f", + "d74843c0d63b4271b23916770d4901988c1f56b20086e5768a12290ebec265e30a80f8f3d88e180f2001", + ); + assert_eq!(output.encoded.to_hex(), expected_encoded); +} + +#[test] +fn test_binance_sign_side_chain_undelegate_order() { + let delegator_key_hash = "08c7c918f6b72c3c0c21b7d08eb6fc66509998e1"; + // bva1echrty7p8r23cwx8g3ezwcza9azy4zq7ca0pzw + let validator_key_hash = "ce2e3593c138d51c38c7447227605d2f444a881e"; + + let side_undelegate = Proto::SideChainUndelegate { + delegator_addr: delegator_key_hash.decode_hex().unwrap().into(), + validator_addr: validator_key_hash.decode_hex().unwrap().into(), + amount: Some(make_token("BNB", 100000000)), + chain_id: "chapel".into(), + }; + + let input = Proto::SigningInput { + chain_id: "test-chain".into(), + account_number: 15, + sequence: 1, + private_key: ACCOUNT_15_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::side_undelegate_order(side_undelegate), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Binance, input); + + let expected_encoded = concat!( + "ba01f0625dee0a44514f7e0e0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e11214ce2e3593c138", + "d51c38c7447227605d2f444a881e1a0a0a03424e421080c2d72f220663686170656c126e0a26eb5ae987", + "2103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240a622b7ca7a28", + "75e5eaa675a5ed976b2ec3b8ca055a2b05e7fb471d328bd04df854789437dd06407e41ebb1e5a345604c", + "93663dfb660e223800636c0b94c2e798180f2001", + ); + assert_eq!(output.encoded.to_hex(), expected_encoded); +} + +#[test] +fn test_binance_sign_time_lock_order() { + let from_key_hash = "08c7c918f6b72c3c0c21b7d08eb6fc66509998e1"; + + let time_lock = Proto::TimeLockOrder { + from_address: from_key_hash.decode_hex().unwrap().into(), + description: "Description locked for offer".into(), + amount: vec![make_token("BNB", 1000000)], + lock_time: 1600001371, + }; + + let input = Proto::SigningInput { + chain_id: "test-chain".into(), + account_number: 15, + sequence: 1, + private_key: ACCOUNT_15_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::time_lock_order(time_lock), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Binance, input); + + let expected_encoded = concat!( + "bf01f0625dee0a49", + "07921531", + "0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1121c4465736372697074696f6e206c6f636b656420666f72206f666665721a090a03424e4210c0843d20dbaaf8fa05126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240c270822b9515ba486c6a6b3472d388a5aea872ed960c0b53de0fafdc8682ef473a126f01e7dd2c00f04a0138a601b9540f54b14026846de362f7ab7f9fed948b180f2001", + ); + assert_eq!(output.encoded.to_hex(), expected_encoded); +} + +#[test] +fn test_binance_sign_time_relock_order() { + let from_key_hash = "08c7c918f6b72c3c0c21b7d08eb6fc66509998e1"; + + let time_relock = Proto::TimeRelockOrder { + from_address: from_key_hash.decode_hex().unwrap().into(), + id: 333, + description: "Description locked for offer".into(), + amount: vec![make_token("BNB", 1000000)], + lock_time: 1600001371, + }; + + let input = Proto::SigningInput { + chain_id: "test-chain".into(), + account_number: 15, + sequence: 1, + private_key: ACCOUNT_15_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::time_relock_order(time_relock), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Binance, input); + + let expected_encoded = concat!( + "c201f0625dee0a4c504711da0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e110cd021a1c446573", + "6372697074696f6e206c6f636b656420666f72206f6666657222090a03424e4210c0843d28dbaaf8fa05", + "126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc7", + "124086ddaa077c8ae551d402fa409cf7e91663982b0542200967c03c0b5876b181353250f689d342f221", + "7624a077b671ce7d09649187e29879f40abbbee9de7ab27c180f2001", + ); + assert_eq!(output.encoded.to_hex(), expected_encoded); +} + +#[test] +fn test_binance_sign_time_unlock_order() { + let from_key_hash = "08c7c918f6b72c3c0c21b7d08eb6fc66509998e1"; + + let time_unlock = Proto::TimeUnlockOrder { + from_address: from_key_hash.decode_hex().unwrap().into(), + id: 333, + }; + + let input = Proto::SigningInput { + chain_id: "test-chain".into(), + account_number: 15, + sequence: 1, + private_key: ACCOUNT_15_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::time_unlock_order(time_unlock), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Binance, input); + + let expected_encoded = concat!( + "9301f0625dee0a1dc4050c6c0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e110cd02126e0a26eb", + "5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240da777b", + "fd2032834f59ec9fe69fd6eaa4aca24242dfbc5ec4ef8c435cb9da7eb05ab78e1b8ca9f109657cb77996", + "898f1b59137b3d8f1e00f842e409e18033b347180f2001", + ); + assert_eq!(output.encoded.to_hex(), expected_encoded); +} diff --git a/rust/tw_any_coin/tests/chains/binance/mod.rs b/rust/tw_any_coin/tests/chains/binance/mod.rs new file mode 100644 index 00000000000..5dd8c0fa9fb --- /dev/null +++ b/rust/tw_any_coin/tests/chains/binance/mod.rs @@ -0,0 +1,18 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_proto::Binance::Proto; + +mod binance_address; +mod binance_compile; +mod binance_sign; + +fn make_token(denom: &str, amount: i64) -> Proto::mod_SendOrder::Token { + Proto::mod_SendOrder::Token { + denom: denom.into(), + amount, + } +} diff --git a/rust/tw_any_coin/tests/chains/mod.rs b/rust/tw_any_coin/tests/chains/mod.rs index ddd04b53632..7cbe502a6d2 100644 --- a/rust/tw_any_coin/tests/chains/mod.rs +++ b/rust/tw_any_coin/tests/chains/mod.rs @@ -5,10 +5,12 @@ // file LICENSE at the root of the source code distribution tree. mod aptos; +mod binance; mod bitcoin; mod cosmos; mod ethereum; mod internet_computer; mod native_evmos; mod native_injective; +mod tbinance; mod thorchain; diff --git a/src/Cosmos/Address.cpp b/rust/tw_any_coin/tests/chains/tbinance/mod.rs similarity index 82% rename from src/Cosmos/Address.cpp rename to rust/tw_any_coin/tests/chains/tbinance/mod.rs index 009d5e97fd1..0ccb0a3eebc 100644 --- a/src/Cosmos/Address.cpp +++ b/rust/tw_any_coin/tests/chains/tbinance/mod.rs @@ -1,8 +1,7 @@ -// Copyright © 2017 Pieter Wuille // Copyright © 2017-2023 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "Address.h" +mod tbinance_address; diff --git a/rust/tw_any_coin/tests/chains/tbinance/tbinance_address.rs b/rust/tw_any_coin/tests/chains/tbinance/tbinance_address.rs new file mode 100644 index 00000000000..c24b8d9bee6 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/tbinance/tbinance_address.rs @@ -0,0 +1,49 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_any_coin::test_utils::address_utils::{ + test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, +}; +use tw_coin_registry::coin_type::CoinType; + +#[test] +fn test_tbinance_address_normalization() { + test_address_normalization( + CoinType::TBinance, + "tbnb18mtcq20875cr0p7l4upm0u5zx4r9jpj2kfu9f8", + "tbnb18mtcq20875cr0p7l4upm0u5zx4r9jpj2kfu9f8", + ); +} + +#[test] +fn test_tbinance_address_is_valid() { + test_address_valid( + CoinType::TBinance, + "tbnb18mtcq20875cr0p7l4upm0u5zx4r9jpj2kfu9f8", + ); +} + +#[test] +fn test_tbinance_address_invalid() { + test_address_invalid( + CoinType::TBinance, + "tbnb18mtcq20875cr0p7l4upm0u5zx4r9jpj2kfu9aa", + ); + // Mainnet. + test_address_invalid( + CoinType::TBinance, + "bnb1h8xf9htasu9aclra954dnnve8fgcda4ae7qfa8", + ); +} + +#[test] +fn test_tbinance_address_get_data() { + test_address_get_data( + CoinType::TBinance, + "tbnb18mtcq20875cr0p7l4upm0u5zx4r9jpj2kfu9f8", + "3ed78029e7f5303787dfaf03b7f282354659064a", + ); +} diff --git a/rust/tw_any_coin/tests/coin_address_derivation_test.rs b/rust/tw_any_coin/tests/coin_address_derivation_test.rs index ef3eabd910b..260243fa0ea 100644 --- a/rust/tw_any_coin/tests/coin_address_derivation_test.rs +++ b/rust/tw_any_coin/tests/coin_address_derivation_test.rs @@ -144,6 +144,8 @@ fn test_coin_address_derivation() { CoinType::NativeInjective => "inj14s0vgnj0pjnazu4hsqlksdk7slah9vcfyrp6ct", CoinType::NativeCanto => "canto14s0vgnj0pjnazu4hsqlksdk7slah9vcfuuhw7m", CoinType::InternetComputer => "290cc7c359f44c8516fc169c5ed4f0f3ae2e24bf5de0d4c51f5e7545b5474faa", + CoinType::Binance => "bnb1ten42eesehw0ktddcp0fws7d3ycsqez3aqvnpg", + CoinType::TBinance => "tbnb1ten42eesehw0ktddcp0fws7d3ycsqez3n49hpe", // end_of_coin_address_derivation_tests_marker_do_not_modify _ => panic!("{:?} must be covered", coin), }; diff --git a/rust/tw_aptos/Cargo.toml b/rust/tw_aptos/Cargo.toml index 49e4d120138..38b0270b8f1 100644 --- a/rust/tw_aptos/Cargo.toml +++ b/rust/tw_aptos/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -serde_json = "1.0.108" +serde_json = "1.0" tw_coin_entry = { path = "../tw_coin_entry" } tw_encoding = { path = "../tw_encoding" } tw_keypair = { path = "../tw_keypair" } @@ -13,7 +13,7 @@ tw_number = { path = "../tw_number" } tw_hash = { path = "../tw_hash" } tw_memory = { path = "../tw_memory" } move-core-types = { git = "https://github.com/move-language/move", rev = "ea70797099baea64f05194a918cebd69ed02b285", features = ["address32"] } -serde = { version = "1.0.189", features = ["derive"] } +serde = { version = "1.0", features = ["derive"] } serde_bytes = "0.11.12" [dev-dependencies] diff --git a/rust/tw_aptos/fuzz/.gitignore b/rust/tw_aptos/fuzz/.gitignore index 1a45eee7760..5c404b9583f 100644 --- a/rust/tw_aptos/fuzz/.gitignore +++ b/rust/tw_aptos/fuzz/.gitignore @@ -2,3 +2,4 @@ target corpus artifacts coverage +Cargo.lock diff --git a/rust/tw_bech32_address/Cargo.toml b/rust/tw_bech32_address/Cargo.toml index 1ca0cfa493f..8650363cf9e 100644 --- a/rust/tw_bech32_address/Cargo.toml +++ b/rust/tw_bech32_address/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -serde = { version = "1.0.163", features = [ "derive" ] } +serde = { version = "1.0", features = ["derive"] } tw_coin_entry = { path = "../tw_coin_entry" } tw_encoding = { path = "../tw_encoding" } tw_hash = { path = "../tw_hash" } diff --git a/rust/tw_bech32_address/src/lib.rs b/rust/tw_bech32_address/src/lib.rs index 67c361f90a7..d8493362219 100644 --- a/rust/tw_bech32_address/src/lib.rs +++ b/rust/tw_bech32_address/src/lib.rs @@ -92,15 +92,22 @@ impl Bech32Address { Bech32Address::with_public_key_hasher(hrp, &public_key, address_hasher) } - pub fn from_str_checked( - expected_hrp: &str, + pub fn from_str_checked( + possible_hrps: I, address_str: String, - ) -> AddressResult { + ) -> AddressResult + where + I: IntoIterator, + { let bech32::Decoded { hrp, bytes } = bech32::decode(&address_str).map_err(|_| AddressError::InvalidInput)?; + // Try to find at least one hrp matches the actual value. // Copied from the legacy Bech32Address.cpp: // https://github.com/trustwallet/wallet-core/blob/d67078daa580b37063c97be66a625aaee9664882/src/Bech32Address.cpp#L21 - if !hrp.starts_with(expected_hrp) { + if !possible_hrps + .into_iter() + .any(|possible_hrp| hrp.starts_with(&possible_hrp)) + { return Err(AddressError::InvalidHrp); } Ok(Bech32Address { @@ -114,15 +121,20 @@ impl Bech32Address { coin: &dyn CoinContext, address_str: String, prefix: Option, - ) -> AddressResult - where - Self: Sized, - { + ) -> AddressResult { let hrp = match prefix { Some(Bech32Prefix { hrp }) => hrp, None => coin.hrp().ok_or(AddressError::InvalidHrp)?, }; - Self::from_str_checked(&hrp, address_str) + Self::from_str_checked([hrp], address_str) + } + + pub fn from_key_hash_with_coin( + coin: &dyn CoinContext, + key_hash: Data, + ) -> AddressResult { + let hrp = coin.hrp().ok_or(AddressError::InvalidHrp)?; + Bech32Address::new(hrp, key_hash) } pub fn key_hash(&self) -> &[u8] { @@ -202,7 +214,7 @@ mod tests { #[test] fn test_address_from_str_checked_valid() { fn test_impl(addr: &str, hrp: &str) { - Bech32Address::from_str_checked(hrp, addr.to_string()) + Bech32Address::from_str_checked([hrp.to_string()], addr.to_string()) .unwrap_or_else(|e| panic!("ERROR={:?}: hrp={} addr={}", e, hrp, addr)); } @@ -249,7 +261,7 @@ mod tests { #[test] fn test_address_from_str_checked_invalid() { fn test_impl(addr: &str, hrp: &str) { - Bech32Address::from_str_checked(hrp, addr.to_string()) + Bech32Address::from_str_checked([hrp.to_string()], addr.to_string()) .expect_err(&format!("hrp={} addr={}", hrp, addr)); } @@ -293,7 +305,8 @@ mod tests { #[test] fn test_decode() { fn test_impl(addr: &str, hrp: &str, expected_hash: &str) { - let actual = Bech32Address::from_str_checked(hrp, addr.to_string()).unwrap(); + let actual = + Bech32Address::from_str_checked([hrp.to_string()], addr.to_string()).unwrap(); assert_eq!(actual.key_hash.to_hex(), expected_hash); } diff --git a/rust/tw_bitcoin/Cargo.toml b/rust/tw_bitcoin/Cargo.toml index 451491eb345..f82af92fd55 100644 --- a/rust/tw_bitcoin/Cargo.toml +++ b/rust/tw_bitcoin/Cargo.toml @@ -6,8 +6,8 @@ edition = "2021" [dependencies] bitcoin = "0.30.0" secp256k1 = { version = "0.27.0", features = [ "global-context", "rand-std" ] } -serde = { version = "1.0.163", features = [ "derive" ] } -serde_json = "1.0.96" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" tw_coin_entry = { path = "../tw_coin_entry", features = ["test-utils"] } tw_utxo = { path = "../tw_utxo" } tw_encoding = { path = "../tw_encoding" } diff --git a/rust/tw_coin_entry/Cargo.toml b/rust/tw_coin_entry/Cargo.toml index d3df7dd56da..083908cbac4 100644 --- a/rust/tw_coin_entry/Cargo.toml +++ b/rust/tw_coin_entry/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -serde_json = "1.0.95" +serde_json = "1.0" tw_encoding = { path = "../tw_encoding" } tw_hash = { path = "../tw_hash" } tw_keypair = { path = "../tw_keypair" } diff --git a/rust/tw_coin_entry/src/coin_entry.rs b/rust/tw_coin_entry/src/coin_entry.rs index 785021d409b..1ec5e590f48 100644 --- a/rust/tw_coin_entry/src/coin_entry.rs +++ b/rust/tw_coin_entry/src/coin_entry.rs @@ -34,15 +34,31 @@ pub trait CoinAddress: fmt::Display { /// (e.g adding/deleting a method or an associated type): /// https://github.com/trustwallet/wallet-core/blob/master/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs pub trait CoinEntry { + /// Address prefix that is used to derive or validate an address for a different network. + /// It can be a Bech32 HRP prefix, or p2pkh/p2sh Base58 prefix, or even a set of different address prefixes. + /// **Optional**. Use `NoPrefix` if the blockchain does not support any address prefixes. type AddressPrefix: Prefix; + /// Address type that should be parsable from a string and have other required methods. type Address: CoinAddress; + /// Protobuf message that provides the information about a transaction to be generated and signed. type SigningInput<'a>: MessageRead<'a> + MessageWrite; + /// Protobuf message - result of the request to sign or compile a transaction. + /// Contains a serialized transaction or an error if occurred. type SigningOutput: MessageWrite; + /// Protobuf message - result of the request to obtain a transaction preimage hashes. type PreSigningOutput: MessageWrite; - // Optional modules: + /// Not supported at this moment. + /// Use `NoJsonSigner`. type JsonSigner: JsonSigner; + /// Transaction Planner - the module provides transaction planning functionality. + /// Used mostly in Bitcoin and UTXO-based chains. + /// + /// **Optional**. Use `NoPlanBuilder` if the blockchain does not support transaction planning. type PlanBuilder: PlanBuilder; + /// Message Signer - the module provides regular message signing functionality. + /// + /// **Optional**. Use `NoMessageSigner` if the blockchain does not support message signing. type MessageSigner: MessageSigner; /// Tries to parse `Self::Address` from the given `address` string by `coin` type and address `prefix`. diff --git a/rust/tw_coin_entry/src/error.rs b/rust/tw_coin_entry/src/error.rs index 179e8b73b3f..b6bc7b923cc 100644 --- a/rust/tw_coin_entry/src/error.rs +++ b/rust/tw_coin_entry/src/error.rs @@ -27,7 +27,7 @@ macro_rules! signing_output_error { pub type AddressResult = Result; -#[derive(Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum AddressError { UnknownCoinType, MissingPrefix, diff --git a/rust/tw_coin_entry/src/prefix.rs b/rust/tw_coin_entry/src/prefix.rs index 52898e19ee5..b48bfcccc66 100644 --- a/rust/tw_coin_entry/src/prefix.rs +++ b/rust/tw_coin_entry/src/prefix.rs @@ -6,12 +6,14 @@ use crate::error::AddressError; +/// An address prefix. It can contain a bech32 prefix that can be used by `Cosmos` based chains. /// Extend when adding new blockchains. #[derive(Clone)] pub enum AddressPrefix { Hrp(String), } +/// A blockchain's address prefix should be convertable from an `AddressPrefix`. pub trait Prefix: TryFrom {} impl Prefix for T where T: TryFrom {} diff --git a/rust/tw_coin_registry/Cargo.toml b/rust/tw_coin_registry/Cargo.toml index a63ba08b267..bd289c150a1 100644 --- a/rust/tw_coin_registry/Cargo.toml +++ b/rust/tw_coin_registry/Cargo.toml @@ -5,11 +5,12 @@ edition = "2021" [dependencies] lazy_static = "1.4.0" -serde = { version = "1.0.163", features = ["derive"] } -serde_json = "1.0.96" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" strum = "0.25" strum_macros = "0.25" tw_aptos = { path = "../tw_aptos" } +tw_binance = { path = "../chains/tw_binance" } tw_bitcoin = { path = "../tw_bitcoin" } tw_coin_entry = { path = "../tw_coin_entry" } tw_cosmos = { path = "../chains/tw_cosmos" } @@ -27,5 +28,5 @@ tw_thorchain = { path = "../chains/tw_thorchain" } [build-dependencies] itertools = "0.10.5" -serde = { version = "1.0.163", features = ["derive"] } -serde_json = "1.0.96" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" diff --git a/rust/tw_coin_registry/src/blockchain_type.rs b/rust/tw_coin_registry/src/blockchain_type.rs index 357697cd83c..28780562cd8 100644 --- a/rust/tw_coin_registry/src/blockchain_type.rs +++ b/rust/tw_coin_registry/src/blockchain_type.rs @@ -12,6 +12,7 @@ use serde::Deserialize; pub enum BlockchainType { // start_of_blockchain_type - USED TO GENERATE CODE Aptos, + Binance, Bitcoin, Cosmos, Ethereum, diff --git a/rust/tw_coin_registry/src/dispatcher.rs b/rust/tw_coin_registry/src/dispatcher.rs index 61e6f0d5998..d6e8fed9043 100644 --- a/rust/tw_coin_registry/src/dispatcher.rs +++ b/rust/tw_coin_registry/src/dispatcher.rs @@ -10,6 +10,7 @@ use crate::coin_type::CoinType; use crate::error::{RegistryError, RegistryResult}; use crate::registry::get_coin_item; use tw_aptos::entry::AptosEntry; +use tw_binance::entry::BinanceEntry; use tw_bitcoin::entry::BitcoinEntry; use tw_coin_entry::coin_entry_ext::CoinEntryExt; use tw_cosmos::entry::CosmosEntry; @@ -26,6 +27,7 @@ pub type EvmEntryExtStaticRef = &'static dyn EvmEntryExt; // start_of_blockchain_entries - USED TO GENERATE CODE const APTOS: AptosEntry = AptosEntry; +const BINANCE: BinanceEntry = BinanceEntry; const BITCOIN: BitcoinEntry = BitcoinEntry; const COSMOS: CosmosEntry = CosmosEntry; const ETHEREUM: EthereumEntry = EthereumEntry; @@ -40,6 +42,7 @@ pub fn blockchain_dispatcher(blockchain: BlockchainType) -> RegistryResult Ok(&APTOS), + BlockchainType::Binance => Ok(&BINANCE), BlockchainType::Bitcoin => Ok(&BITCOIN), BlockchainType::Cosmos => Ok(&COSMOS), BlockchainType::Ethereum => Ok(ÐEREUM), diff --git a/rust/tw_cosmos_sdk/Cargo.toml b/rust/tw_cosmos_sdk/Cargo.toml index c2b1be4fd94..3e209b4f6df 100644 --- a/rust/tw_cosmos_sdk/Cargo.toml +++ b/rust/tw_cosmos_sdk/Cargo.toml @@ -8,8 +8,8 @@ test-utils = [] [dependencies] quick-protobuf = "0.8.1" -serde = { version = "1.0.163", features = ["derive"] } -serde_json = "1.0.96" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" tw_bech32_address = { path = "../tw_bech32_address" } tw_coin_entry = { path = "../tw_coin_entry" } tw_encoding = { path = "../tw_encoding" } diff --git a/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs b/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs index 897276b8c90..f8211227822 100644 --- a/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs +++ b/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs @@ -12,7 +12,6 @@ use crate::modules::serializer::json_serializer::JsonSerializer; use crate::modules::serializer::protobuf_serializer::ProtobufSerializer; use crate::modules::tx_builder::TxBuilder; use crate::public_key::CosmosPublicKey; -use crate::signature::CosmosSignature; use std::borrow::Cow; use std::marker::PhantomData; use tw_coin_entry::coin_context::CoinContext; @@ -20,6 +19,7 @@ use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; use tw_coin_entry::common::compile_input::SingleSignaturePubkey; use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_coin_entry::signing_output_error; +use tw_misc::traits::ToBytesVec; use tw_proto::Cosmos::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; @@ -105,21 +105,21 @@ impl TWTransactionCompiler { signature, public_key, } = SingleSignaturePubkey::from_sign_pubkey_list(signatures, public_keys)?; - let signature = Context::Signature::from_bytes(&signature)?; + let signature = Context::Signature::try_from(&signature)?; let public_key = Context::PublicKey::from_bytes(coin, &public_key)?; let signed_tx_raw = match TxBuilder::::try_sign_direct_args(&input) { // If there was a `SignDirect` message in the signing input, generate the `TxRaw` directly. Ok(Some(sign_direct_args)) => ProtobufSerializer::::build_direct_signed_tx( &sign_direct_args, - signature.to_bytes(), + signature.to_vec(), ), // Otherwise, generate the `TxRaw` by using `TxBuilder`. _ => { // Set the public key. It will be used to construct a signer info. input.public_key = Cow::from(public_key.to_bytes()); let unsigned_tx = TxBuilder::::unsigned_tx_from_proto(coin, &input)?; - let signed_tx = unsigned_tx.into_signed(signature.to_bytes()); + let signed_tx = unsigned_tx.into_signed(signature.to_vec()); ProtobufSerializer::build_signed_tx(&signed_tx)? }, @@ -129,12 +129,12 @@ impl TWTransactionCompiler { let broadcast_tx = BroadcastMsg::raw(broadcast_mode, &signed_tx_raw).to_json_string(); let signature_json = - JsonSerializer::::serialize_signature(&public_key, signature.to_bytes()); + JsonSerializer::::serialize_signature(&public_key, signature.to_vec()); let signature_json = serde_json::to_string(&[signature_json]) .map_err(|_| SigningError(SigningErrorType::Error_internal))?; Ok(Proto::SigningOutput { - signature: Cow::from(signature.to_bytes()), + signature: Cow::from(signature.to_vec()), signature_json: Cow::from(signature_json), serialized: Cow::from(broadcast_tx), ..Proto::SigningOutput::default() @@ -151,13 +151,13 @@ impl TWTransactionCompiler { signature, public_key, } = SingleSignaturePubkey::from_sign_pubkey_list(signatures, public_keys)?; - let signature = Context::Signature::from_bytes(&signature)?; + let signature = Context::Signature::try_from(&signature)?; let public_key = Context::PublicKey::from_bytes(coin, &public_key)?; // Set the public key. It will be used to construct a signer info. input.public_key = Cow::from(public_key.to_bytes()); let unsigned_tx = TxBuilder::::unsigned_tx_from_proto(coin, &input)?; - let signed_tx = unsigned_tx.into_signed(signature.to_bytes()); + let signed_tx = unsigned_tx.into_signed(signature.to_vec()); let signed_tx_json = JsonSerializer::build_signed_tx(&signed_tx)?; @@ -168,7 +168,7 @@ impl TWTransactionCompiler { .map_err(|_| SigningError(SigningErrorType::Error_internal))?; Ok(Proto::SigningOutput { - signature: Cow::from(signature.to_bytes()), + signature: Cow::from(signature.to_vec()), signature_json: Cow::from(signature_json), json: Cow::from(broadcast_tx), ..Proto::SigningOutput::default() diff --git a/rust/tw_cosmos_sdk/src/signature/mod.rs b/rust/tw_cosmos_sdk/src/signature/mod.rs index d2c5ba2169e..7184a3c0fd2 100644 --- a/rust/tw_cosmos_sdk/src/signature/mod.rs +++ b/rust/tw_cosmos_sdk/src/signature/mod.rs @@ -4,13 +4,9 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use tw_keypair::KeyPairResult; -use tw_memory::Data; +use tw_keypair::KeyPairError; +use tw_misc::traits::{FromSlice, ToBytesVec}; pub mod secp256k1; -pub trait CosmosSignature: Sized { - fn from_bytes(signature_bytes: &[u8]) -> KeyPairResult; - - fn to_bytes(&self) -> Data; -} +pub trait CosmosSignature: FromSlice + ToBytesVec {} diff --git a/rust/tw_cosmos_sdk/src/signature/secp256k1.rs b/rust/tw_cosmos_sdk/src/signature/secp256k1.rs index f07ec5957ff..a5e41ec4fb2 100644 --- a/rust/tw_cosmos_sdk/src/signature/secp256k1.rs +++ b/rust/tw_cosmos_sdk/src/signature/secp256k1.rs @@ -5,30 +5,8 @@ // file LICENSE at the root of the source code distribution tree. use crate::signature::CosmosSignature; -use tw_hash::{H512, H520}; -use tw_keypair::{KeyPairError, KeyPairResult}; -use tw_memory::Data; -use tw_misc::traits::ToBytesVec; +use tw_keypair::ecdsa::secp256k1; -pub struct Secp256k1Signature { - signature: H512, -} +pub type Secp256k1Signature = secp256k1::VerifySignature; -impl CosmosSignature for Secp256k1Signature { - fn from_bytes(signature_bytes: &[u8]) -> KeyPairResult { - let signature_slice = if signature_bytes.len() == H520::len() { - // Discard the last `v` recovery byte. - &signature_bytes[0..H512::len()] - } else { - signature_bytes - }; - - let signature = - H512::try_from(signature_slice).map_err(|_| KeyPairError::InvalidSignature)?; - Ok(Secp256k1Signature { signature }) - } - - fn to_bytes(&self) -> Data { - self.signature.to_vec() - } -} +impl CosmosSignature for Secp256k1Signature {} diff --git a/rust/tw_encoding/Cargo.toml b/rust/tw_encoding/Cargo.toml index 88008908278..f36c1580246 100644 --- a/rust/tw_encoding/Cargo.toml +++ b/rust/tw_encoding/Cargo.toml @@ -11,7 +11,7 @@ bs58 = "0.4.0" ciborium = "0.2.1" data-encoding = "2.3.3" hex = "0.4.3" -serde = { version = "1.0.136", features = ["derive"] } +serde = { version = "1.0", features = ["derive"] } tw_memory = { path = "../tw_memory" } [dev-dependencies] diff --git a/rust/tw_encoding/fuzz/Cargo.toml b/rust/tw_encoding/fuzz/Cargo.toml index 81d65cd9818..2bcec734cf5 100644 --- a/rust/tw_encoding/fuzz/Cargo.toml +++ b/rust/tw_encoding/fuzz/Cargo.toml @@ -9,7 +9,7 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = { version = "0.4", features = ["arbitrary-derive"] } -serde = { version = "1.0.136", features = ["derive"] } +serde = { version = "1.0", features = ["derive"] } serde_bytes = "0.11.12" [dependencies.tw_encoding] diff --git a/rust/tw_encoding/src/ffi.rs b/rust/tw_encoding/src/ffi.rs index 83f830793bd..1be7b07bc48 100644 --- a/rust/tw_encoding/src/ffi.rs +++ b/rust/tw_encoding/src/ffi.rs @@ -17,6 +17,7 @@ pub enum CEncodingCode { Ok = 0, InvalidInput = 1, InvalidAlphabet = 2, + Internal = 3, } impl From for CEncodingCode { @@ -24,6 +25,7 @@ impl From for CEncodingCode { match error { EncodingError::InvalidInput => CEncodingCode::InvalidInput, EncodingError::InvalidAlphabet => CEncodingCode::InvalidAlphabet, + EncodingError::Internal => CEncodingCode::Internal, } } } diff --git a/rust/tw_encoding/src/hex.rs b/rust/tw_encoding/src/hex.rs index 38dc8b2e44f..b43ca64d979 100644 --- a/rust/tw_encoding/src/hex.rs +++ b/rust/tw_encoding/src/hex.rs @@ -5,6 +5,7 @@ // file LICENSE at the root of the source code distribution tree. pub use hex::FromHexError; +use serde::{Serialize, Serializer}; use tw_memory::Data; pub type FromHexResult = Result; @@ -65,6 +66,15 @@ pub fn encode>(data: T, prefixed: bool) -> String { encoded } +/// Serializes the `value` as a hex. +pub fn as_hex(value: &T, serializer: S) -> Result +where + T: ToHex, + S: Serializer, +{ + value.to_hex().serialize(serializer) +} + #[cfg(test)] mod tests { use super::*; diff --git a/rust/tw_encoding/src/lib.rs b/rust/tw_encoding/src/lib.rs index 871a6a42887..ed1a7002451 100644 --- a/rust/tw_encoding/src/lib.rs +++ b/rust/tw_encoding/src/lib.rs @@ -19,4 +19,5 @@ pub type EncodingResult = Result; pub enum EncodingError { InvalidInput, InvalidAlphabet, + Internal, } diff --git a/rust/tw_evm/Cargo.toml b/rust/tw_evm/Cargo.toml index 43c3f334c72..857a8f1bd5a 100644 --- a/rust/tw_evm/Cargo.toml +++ b/rust/tw_evm/Cargo.toml @@ -7,8 +7,8 @@ edition = "2021" itertools = "0.10.5" lazy_static = "1.4.0" rlp = "0.5.2" -serde = { version = "1.0.159", features = ["derive"] } -serde_json = "1.0.95" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" tw_coin_entry = { path = "../tw_coin_entry" } tw_encoding = { path = "../tw_encoding" } tw_hash = { path = "../tw_hash" } diff --git a/rust/tw_evm/fuzz/Cargo.toml b/rust/tw_evm/fuzz/Cargo.toml index e1ac2e6c65c..e008ae78164 100644 --- a/rust/tw_evm/fuzz/Cargo.toml +++ b/rust/tw_evm/fuzz/Cargo.toml @@ -9,7 +9,7 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = { version = "0.4", features = ["arbitrary-derive"] } -serde_json = "1.0.96" +serde_json = "1.0" tw_number = { path = "../../tw_number" } tw_proto = { path = "../../tw_proto", features = ["fuzz"] } diff --git a/rust/tw_hash/Cargo.toml b/rust/tw_hash/Cargo.toml index 9d22a32c414..2e1740b15c1 100644 --- a/rust/tw_hash/Cargo.toml +++ b/rust/tw_hash/Cargo.toml @@ -14,7 +14,7 @@ digest = "0.10.6" groestl = "0.10.1" hmac = "0.12.1" ripemd = "0.1.3" -serde = { version = "1.0.159", features = ["derive"], optional = true } +serde = { version = "1.0", features = ["derive"], optional = true } sha1 = "0.10.5" sha2 = "0.10.6" sha3 = "0.10.6" @@ -23,4 +23,4 @@ tw_memory = { path = "../tw_memory" } zeroize = "1.6.0" [dev-dependencies] -serde_json = "1.0.95" +serde_json = "1.0" diff --git a/rust/tw_internet_computer/Cargo.toml b/rust/tw_internet_computer/Cargo.toml index 7fd375e1071..e40261494e7 100644 --- a/rust/tw_internet_computer/Cargo.toml +++ b/rust/tw_internet_computer/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] quick-protobuf = "0.8.1" -serde = { version = "1.0.136", features = ["derive"] } +serde = { version = "1.0", features = ["derive"] } tw_coin_entry = { path = "../tw_coin_entry" } tw_encoding = { path = "../tw_encoding" } tw_hash = { path = "../tw_hash" } diff --git a/rust/tw_keypair/Cargo.toml b/rust/tw_keypair/Cargo.toml index 52b0a350fa8..d29ab7b89b3 100644 --- a/rust/tw_keypair/Cargo.toml +++ b/rust/tw_keypair/Cargo.toml @@ -9,7 +9,7 @@ test-utils = [] [dependencies] arbitrary = { version = "1", features = ["derive"], optional = true } lazy_static = "1.4.0" -serde = { version = "1.0.159", features = ["derive"] } +serde = { version = "1.0", features = ["derive"] } starknet-crypto = "0.5.0" starknet-ff = "0.3.2" tw_encoding = { path = "../tw_encoding" } @@ -31,7 +31,7 @@ digest = "0.9.0" sha2 = "0.9" [dev-dependencies] -serde_json = "1.0.95" +serde_json = "1.0" [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] ring = "0.16.20" diff --git a/rust/tw_keypair/src/ecdsa/signature.rs b/rust/tw_keypair/src/ecdsa/signature.rs index 64fe61bfe83..7660bc12a0c 100644 --- a/rust/tw_keypair/src/ecdsa/signature.rs +++ b/rust/tw_keypair/src/ecdsa/signature.rs @@ -8,7 +8,7 @@ use crate::ecdsa::EcdsaCurve; use crate::{KeyPairError, KeyPairResult}; use ecdsa::elliptic_curve::FieldBytes; use std::ops::{Range, RangeInclusive}; -use tw_hash::{H256, H520}; +use tw_hash::{H256, H512, H520}; use tw_misc::traits::ToBytesVec; /// Represents an ECDSA signature. @@ -118,7 +118,26 @@ impl<'a, C: EcdsaCurve> TryFrom<&'a [u8]> for Signature { /// To verify the signature, it's enough to check `r` and `s` parts without the recovery ID. pub struct VerifySignature { - pub signature: ecdsa::Signature, + pub(crate) signature: ecdsa::Signature, +} + +impl VerifySignature { + /// Returns a standard binary signature representation: + /// RS, where R - 32 byte array, S - 32 byte array. + pub fn to_bytes(&self) -> H512 { + let (r, s) = self.signature.split_bytes(); + + let mut dest = H512::default(); + dest[Signature::::R_RANGE].copy_from_slice(r.as_slice()); + dest[Signature::::S_RANGE].copy_from_slice(s.as_slice()); + dest + } +} + +impl ToBytesVec for VerifySignature { + fn to_vec(&self) -> Vec { + self.to_bytes().to_vec() + } } impl<'a, C: EcdsaCurve> TryFrom<&'a [u8]> for VerifySignature { diff --git a/rust/tw_misc/Cargo.toml b/rust/tw_misc/Cargo.toml index 60fe9a1033d..13747bab355 100644 --- a/rust/tw_misc/Cargo.toml +++ b/rust/tw_misc/Cargo.toml @@ -7,6 +7,6 @@ edition = "2021" test-utils = ["serde", "serde_json"] [dependencies] -serde = { version = "1.0.163", features = ["derive"], optional = true } -serde_json = { version = "1.0.96", optional = true } +serde = { version = "1.0", features = ["derive"], optional = true } +serde_json = { version = "1.0", optional = true } zeroize = "1.6.0" diff --git a/rust/tw_misc/src/lib.rs b/rust/tw_misc/src/lib.rs index a6017087f76..80fa96cffc9 100644 --- a/rust/tw_misc/src/lib.rs +++ b/rust/tw_misc/src/lib.rs @@ -5,6 +5,7 @@ // file LICENSE at the root of the source code distribution tree. pub mod macros; +pub mod serde; #[cfg(feature = "test-utils")] pub mod test_utils; pub mod traits; diff --git a/rust/tw_misc/src/serde.rs b/rust/tw_misc/src/serde.rs new file mode 100644 index 00000000000..0e1e85d5256 --- /dev/null +++ b/rust/tw_misc/src/serde.rs @@ -0,0 +1,16 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde::{Serialize, Serializer}; + +/// Serializes the `value` as a string. +pub fn as_string(value: &T, serializer: S) -> Result +where + T: ToString, + S: Serializer, +{ + value.to_string().serialize(serializer) +} diff --git a/rust/tw_number/Cargo.toml b/rust/tw_number/Cargo.toml index 3a180315669..d76f8b6ed6d 100644 --- a/rust/tw_number/Cargo.toml +++ b/rust/tw_number/Cargo.toml @@ -12,7 +12,7 @@ helpers = [] arbitrary = { version = "1", features = ["derive"], optional = true } lazy_static = "1.4.0" primitive-types = "0.12.1" -serde = { version = "1.0.159", features = ["derive"], optional = true } +serde = { version = "1.0", features = ["derive"], optional = true } tw_hash = { path = "../tw_hash" } tw_memory = { path = "../tw_memory" } diff --git a/rust/wallet_core_rs/Cargo.toml b/rust/wallet_core_rs/Cargo.toml index 37b8c878d1d..a72042ac6cb 100644 --- a/rust/wallet_core_rs/Cargo.toml +++ b/rust/wallet_core_rs/Cargo.toml @@ -28,7 +28,7 @@ tw_misc = { path = "../tw_misc" } tw_proto = { path = "../tw_proto" } [dev-dependencies] -serde_json = "1.0.96" +serde_json = "1.0" tw_any_coin = { path = "../tw_any_coin", features = ["test-utils"] } tw_memory = { path = "../tw_memory", features = ["test-utils"] } tw_number = { path = "../tw_number", features = ["helpers"] } diff --git a/samples/go/core/transactionHelper.go b/samples/go/core/transactionHelper.go index 6062be8b753..df2a78c64dd 100644 --- a/samples/go/core/transactionHelper.go +++ b/samples/go/core/transactionHelper.go @@ -6,25 +6,6 @@ package core import "C" import "tw/types" -func BuildInput(c CoinType, from, to string, amount string, asset string, memo string, chainId string) []byte { - fromStr := types.TWStringCreateWithGoString(from) - defer C.TWStringDelete(fromStr) - toStr := types.TWStringCreateWithGoString(to) - defer C.TWStringDelete(toStr) - amountStr := types.TWStringCreateWithGoString(amount) - defer C.TWStringDelete(amountStr) - assetStr := types.TWStringCreateWithGoString(asset) - defer C.TWStringDelete(assetStr) - memoStr := types.TWStringCreateWithGoString(memo) - defer C.TWStringDelete(memoStr) - chainIdStr := types.TWStringCreateWithGoString(chainId) - defer C.TWStringDelete(chainIdStr) - - result := C.TWTransactionCompilerBuildInput(C.enum_TWCoinType(c), fromStr, toStr, amountStr, assetStr, memoStr, chainIdStr) - defer C.TWDataDelete(result) - return types.TWDataGoBytes(result) -} - func PreImageHashes(c CoinType, txInputData []byte) []byte { input := types.TWDataCreateWithGoBytes(txInputData) defer C.TWDataDelete(input) diff --git a/samples/go/sample/external_signing.go b/samples/go/sample/external_signing.go index 161de4380db..0cbf729f812 100644 --- a/samples/go/sample/external_signing.go +++ b/samples/go/sample/external_signing.go @@ -25,16 +25,37 @@ func SignExternalBinanceDemo() { coin := core.CoinTypeBinance + // bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2 + fromAddress, _ := hex.DecodeString("40c2979694bbc961023d1d27be6fc4d21a9febe6") + // bnb1hlly02l6ahjsgxw9wlcswnlwdhg4xhx38yxpd5 + toAddress, _ := hex.DecodeString("bffe47abfaede50419c577f1074fee6dd1535cd1") + + inOutToken := binance.SendOrder_Token { + Denom: "BNB", + Amount: 1, + } + + orderInput := binance.SendOrder_Input { + Address: fromAddress, + Coins: []*binance.SendOrder_Token{&inOutToken}, + } + orderOutput := binance.SendOrder_Output { + Address: toAddress, + Coins: []*binance.SendOrder_Token{&inOutToken}, + } + + input := binance.SigningInput { + ChainId: "Binance-Chain-Nile", + OrderOneof: &binance.SigningInput_SendOrder { + SendOrder: &binance.SendOrder { + Inputs: []*binance.SendOrder_Input{&orderInput}, + Outputs: []*binance.SendOrder_Output{&orderOutput}, + }, + }, + } + fmt.Println("\n==> Step 1: Prepare transaction input (protobuf)") - txInputData := core.BuildInput( - coin, - "bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2", // from - "bnb1hlly02l6ahjsgxw9wlcswnlwdhg4xhx38yxpd5", // to - "1", // amount - "BNB", // asset - "", // memo - "", // chainId - ) + txInputData, _ := proto.Marshal(&input) fmt.Println("txInputData len: ", len(txInputData)) fmt.Println("\n==> Step 2: Obtain preimage hash") diff --git a/src/Binance/Entry.cpp b/src/Binance/Entry.cpp index 75ed2a72e9e..3d65666ba7f 100644 --- a/src/Binance/Entry.cpp +++ b/src/Binance/Entry.cpp @@ -6,119 +6,18 @@ #include "Entry.h" -#include "../proto/TransactionCompiler.pb.h" -#include "Address.h" -#include "Coin.h" -#include "Signer.h" +#include "HexCoding.h" +#include "proto/Binance.pb.h" namespace TW::Binance { -bool Entry::validateAddress(TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - if (std::holds_alternative(addressPrefix)) { - if (const auto* hrp = std::get(addressPrefix); hrp) { - return Address::isValid(address, hrp); - } - } - return Address::isValid(coin, address); -} - -std::string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, const PrefixVariant& addressPrefix) const { - const std::string hrp = getFromPrefixHrpOrDefault(addressPrefix, coin); - return Address(publicKey, hrp).string(); -} - -Data Entry::addressToData(TWCoinType coin, const std::string& address) const { - const char* hrp = stringForHRP(TW::hrp(coin)); - Address addr(hrp); - if (Address::decode(address, addr)) { - return addr.getKeyHash(); - } - return {}; -} - -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { - auto input = Proto::SigningInput(); - input.ParseFromArray(dataIn.data(), (int)dataIn.size()); - const char* hrp = stringForHRP(TW::hrp(coin)); - auto serializedOut = Signer::sign(input, hrp).SerializeAsString(); - dataOut.insert(dataOut.end(), serializedOut.begin(), serializedOut.end()); -} - std::string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { - const char* hrp = stringForHRP(TW::hrp(coin)); - return Signer::signJSON(json, key, hrp); -} - -Data Entry::preImageHashes([[maybe_unused]] TWCoinType coin, const Data& txInputData) const { - const char* hrp = stringForHRP(TW::hrp(coin)); - - return txCompilerTemplate( - txInputData, [hrp](const auto& input, auto& output) { - Signer signer(input, hrp); - - auto preImageHash = signer.preImageHash(); - auto preImage = signer.signaturePreimage(); - output.set_data_hash(preImageHash.data(), preImageHash.size()); - output.set_data(preImage.data(), preImage.size()); - }); -} - -void Entry::compile([[maybe_unused]] TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const { - const char* hrp = stringForHRP(TW::hrp(coin)); - - dataOut = txCompilerSingleTemplate( - txInputData, signatures, publicKeys, - [hrp](const auto& input, auto& output, const auto& signature, const auto& publicKey) { - output = Signer(input, hrp).compile(signature, publicKey); - }); -} - -Data Entry::buildTransactionInput(TWCoinType coinType, const std::string& from, const std::string& to, const uint256_t& amount, const std::string& asset, const std::string& memo, const std::string& chainId) const { - auto input = Proto::SigningInput(); - input.set_chain_id(chainId); - input.set_account_number(0); - input.set_sequence(0); - input.set_source(0); - input.set_memo(memo); - // do not set private_key! - input.set_private_key(""); - - auto& order = *input.mutable_send_order(); - - const char* hrp = stringForHRP(TW::hrp(coinType)); - - Data fromKeyhash; - Data toKeyhash; - - Address fromAddress(hrp); - if (!Address::decode(from, fromAddress)) { - throw std::invalid_argument("Invalid from address"); - } - fromKeyhash = fromAddress.getKeyHash(); - - Address toAddress(hrp); - if (!Address::decode(to, toAddress)) { - throw std::invalid_argument("Invalid to address"); - } - toKeyhash = toAddress.getKeyHash(); - - { - auto* sendOrderInputs = order.add_inputs(); - sendOrderInputs->set_address(fromKeyhash.data(), fromKeyhash.size()); - auto* inputCoin = sendOrderInputs->add_coins(); - inputCoin->set_denom(asset); - inputCoin->set_amount(static_cast(amount)); - } - { - auto* output = order.add_outputs(); - output->set_address(toKeyhash.data(), toKeyhash.size()); - auto* outputCoin = output->add_coins(); - outputCoin->set_denom(asset); - outputCoin->set_amount(static_cast(amount)); - } - - const auto txInputData = data(input.SerializeAsString()); - return txInputData; + return signJSONHelper( + coin, + json, + key, + [](const Proto::SigningOutput& output) { return hex(output.encoded()); } + ); } } // namespace TW::Binance diff --git a/src/Binance/Entry.h b/src/Binance/Entry.h index f921b085055..579bdba74be 100644 --- a/src/Binance/Entry.h +++ b/src/Binance/Entry.h @@ -6,24 +6,16 @@ #pragma once -#include "../CoinEntry.h" +#include "rust/RustCoinEntry.h" namespace TW::Binance { /// Binance entry dispatcher. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry final : public CoinEntry { +class Entry final : public Rust::RustCoinEntryWithSignJSON { public: - bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; - Data addressToData(TWCoinType coin, const std::string& address) const; - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - bool supportsJSONSigning() const { return true; } - std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; - - Data preImageHashes(TWCoinType coin, const Data& txInputData) const; - void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const; - Data buildTransactionInput(TWCoinType coinType, const std::string& from, const std::string& to, const uint256_t& amount, const std::string& asset, const std::string& memo, const std::string& chainId) const; + bool supportsJSONSigning() const override { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const override; }; } // namespace TW::Binance diff --git a/src/Binance/Serialization.cpp b/src/Binance/Serialization.cpp deleted file mode 100644 index 2ceeaca9df7..00000000000 --- a/src/Binance/Serialization.cpp +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Serialization.h" - -#include "Address.h" -#include "Bech32Address.h" -#include "Ethereum/Address.h" -#include "../HexCoding.h" - -namespace TW::Binance { - -using json = nlohmann::json; - -static inline std::string addressString(const std::string& bytes, const std::string& chainHrp) { - auto data = Data(bytes.begin(), bytes.end()); - return Address(data, chainHrp).string(); -} - -static inline std::string validatorAddress(const std::string& bytes) { - auto data = Data(bytes.begin(), bytes.end()); - return Bech32Address(Address::hrpValidator, data).string(); -} - -json signatureJSON(const Proto::SigningInput& input, const std::string& chainHrp) { - json j; - j["account_number"] = std::to_string(input.account_number()); - j["chain_id"] = input.chain_id(); - j["data"] = nullptr; - j["memo"] = input.memo(); - j["msgs"] = json::array({orderJSON(input, chainHrp)}); - j["sequence"] = std::to_string(input.sequence()); - j["source"] = std::to_string(input.source()); - return j; -} - -json orderJSON(const Proto::SigningInput& input, const std::string& chainHrp) { - json j; - if (input.has_trade_order()) { - j["id"] = input.trade_order().id(); - j["ordertype"] = 2; - j["price"] = input.trade_order().price(); - j["quantity"] = input.trade_order().quantity(); - j["sender"] = addressString(input.trade_order().sender(), chainHrp); - j["side"] = input.trade_order().side(); - j["symbol"] = input.trade_order().symbol(); - j["timeinforce"] = input.trade_order().timeinforce(); - } else if (input.has_cancel_trade_order()) { - j["refid"] = input.cancel_trade_order().refid(); - j["sender"] = addressString(input.cancel_trade_order().sender(), chainHrp); - j["symbol"] = input.cancel_trade_order().symbol(); - } else if (input.has_send_order()) { - j["inputs"] = inputsJSON(input.send_order(), chainHrp); - j["outputs"] = outputsJSON(input.send_order(), chainHrp); - } else if (input.has_freeze_order()) { - j["from"] = addressString(input.freeze_order().from(), chainHrp); - j["symbol"] = input.freeze_order().symbol(); - j["amount"] = input.freeze_order().amount(); - } else if (input.has_unfreeze_order()) { - j["from"] = addressString(input.unfreeze_order().from(), chainHrp); - j["symbol"] = input.unfreeze_order().symbol(); - j["amount"] = input.unfreeze_order().amount(); - } else if (input.has_htlt_order()) { - j["from"] = addressString(input.htlt_order().from(), chainHrp); - j["to"] = addressString(input.htlt_order().to(), chainHrp); - j["recipient_other_chain"] = input.htlt_order().recipient_other_chain(); - j["sender_other_chain"] = input.htlt_order().sender_other_chain(); - j["random_number_hash"] = hex(input.htlt_order().random_number_hash()); - j["timestamp"] = input.htlt_order().timestamp(); - j["amount"] = tokensJSON(input.htlt_order().amount()); - j["expected_income"] = input.htlt_order().expected_income(); - j["height_span"] = input.htlt_order().height_span(); - j["cross_chain"] = input.htlt_order().cross_chain(); - } else if (input.has_deposithtlt_order()) { - j["from"] = addressString(input.deposithtlt_order().from(), chainHrp); - j["swap_id"] = hex(input.deposithtlt_order().swap_id()); - j["amount"] = tokensJSON(input.deposithtlt_order().amount()); - } else if (input.has_claimhtlt_order()) { - j["from"] = addressString(input.claimhtlt_order().from(), chainHrp); - j["swap_id"] = hex(input.claimhtlt_order().swap_id()); - j["random_number"] = hex(input.claimhtlt_order().random_number()); - } else if (input.has_refundhtlt_order()) { - j["from"] = addressString(input.refundhtlt_order().from(), chainHrp); - j["swap_id"] = hex(input.refundhtlt_order().swap_id()); - } else if (input.has_issue_order()) { - j["from"] = addressString(input.issue_order().from(), chainHrp); - j["total_supply"] = input.issue_order().total_supply(); - j["name"] = input.issue_order().name(); - j["symbol"] = input.issue_order().symbol(); - j["mintable"] = input.issue_order().mintable(); - } else if (input.has_mint_order()) { - j["from"] = addressString(input.mint_order().from(), chainHrp); - j["amount"] = input.mint_order().amount(); - j["symbol"] = input.mint_order().symbol(); - } else if (input.has_burn_order()) { - j["from"] = addressString(input.burn_order().from(), chainHrp); - j["amount"] = input.burn_order().amount(); - j["symbol"] = input.burn_order().symbol(); - } else if (input.has_transfer_out_order()) { - auto to = input.transfer_out_order().to(); - auto addr = Ethereum::Address(Data(to.begin(), to.end())); - j["from"] = addressString(input.transfer_out_order().from(), chainHrp); - j["to"] = addr.string(); - j["amount"] = tokenJSON(input.transfer_out_order().amount()); - j["expire_time"] = input.transfer_out_order().expire_time(); - } else if (input.has_side_delegate_order()) { - j["type"] = "cosmos-sdk/MsgSideChainDelegate"; - j["value"] = { - {"delegator_addr", addressString(input.side_delegate_order().delegator_addr(), chainHrp)}, - {"validator_addr",validatorAddress(input.side_delegate_order().validator_addr())}, - {"delegation", tokenJSON(input.side_delegate_order().delegation(), true)}, - {"side_chain_id", input.side_delegate_order().chain_id()}, - }; - } else if (input.has_side_redelegate_order()) { - j["type"] = "cosmos-sdk/MsgSideChainRedelegate"; - j["value"] = { - {"delegator_addr", addressString(input.side_redelegate_order().delegator_addr(), chainHrp)}, - {"validator_src_addr", validatorAddress(input.side_redelegate_order().validator_src_addr())}, - {"validator_dst_addr", validatorAddress(input.side_redelegate_order().validator_dst_addr())}, - {"amount", tokenJSON(input.side_redelegate_order().amount(), true)}, - {"side_chain_id", input.side_redelegate_order().chain_id()}, - }; - } else if (input.has_side_undelegate_order()) { - j["type"] = "cosmos-sdk/MsgSideChainUndelegate"; - j["value"] = { - {"delegator_addr", addressString(input.side_undelegate_order().delegator_addr(), chainHrp)}, - {"validator_addr", validatorAddress(input.side_undelegate_order().validator_addr())}, - {"amount", tokenJSON(input.side_undelegate_order().amount(), true)}, - {"side_chain_id", input.side_undelegate_order().chain_id()}, - }; - } else if (input.has_time_lock_order()) { - j["from"] = addressString(input.time_lock_order().from_address(), chainHrp); - j["description"] = input.time_lock_order().description(); - j["amount"] = tokensJSON(input.time_lock_order().amount()); - j["lock_time"] = input.time_lock_order().lock_time(); - } else if (input.has_time_relock_order()) { - const auto amount = input.time_relock_order().amount(); - j["from"] = addressString(input.time_relock_order().from_address(), chainHrp); - j["time_lock_id"] = input.time_relock_order().id(); - j["description"] = input.time_relock_order().description(); - // if amount is empty or omitted, set null to avoid signature verification error - j["amount"] = nullptr; - if (!amount.empty()) { - j["amount"] = tokensJSON(amount); - } - j["lock_time"] = input.time_relock_order().lock_time(); - } else if (input.has_time_unlock_order()) { - j["from"] = addressString(input.time_unlock_order().from_address(), chainHrp); - j["time_lock_id"] = input.time_unlock_order().id(); - } - - return j; -} - -json inputsJSON(const Proto::SendOrder& order, const std::string& chainHrp) { - json j = json::array(); - for (auto& input : order.inputs()) { - j.push_back({ - {"address", addressString(input.address(), chainHrp)}, - {"coins", tokensJSON(input.coins())} - }); - } - return j; -} - -json outputsJSON(const Proto::SendOrder& order, const std::string& chainHrp) { - json j = json::array(); - for (auto& output : order.outputs()) { - j.push_back({ - {"address", addressString(output.address(), chainHrp)}, - {"coins", tokensJSON(output.coins())} - }); - } - return j; -} - -json tokenJSON(const Proto::SendOrder_Token& token, bool stringAmount) { - json j = {{"denom", token.denom()}}; - if (stringAmount) { - j["amount"] = std::to_string(token.amount()); - } else { - j["amount"] = token.amount(); - } - return j; -} - -json tokensJSON(const google::protobuf::RepeatedPtrField& tokens) { - json j = json::array(); - for (auto& token : tokens) { - j.push_back(tokenJSON(token)); - } - return j; -} - -} // namespace TW::Binance diff --git a/src/Binance/Serialization.h b/src/Binance/Serialization.h deleted file mode 100644 index a49046eb035..00000000000 --- a/src/Binance/Serialization.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "../proto/Binance.pb.h" -#include - -namespace TW::Binance { - -nlohmann::json signatureJSON(const Proto::SigningInput& input, const std::string& chainHrp); -nlohmann::json orderJSON(const Proto::SigningInput& input, const std::string& chainHrp); -nlohmann::json inputsJSON(const Proto::SendOrder& order, const std::string& chainHrp); -nlohmann::json outputsJSON(const Proto::SendOrder& order, const std::string& chainHrp); -nlohmann::json tokenJSON(const Proto::SendOrder_Token& token, bool stringAmount = false); -nlohmann::json tokensJSON(const ::google::protobuf::RepeatedPtrField& tokens); - -} // namespace TW::Binance diff --git a/src/Binance/Signer.cpp b/src/Binance/Signer.cpp deleted file mode 100644 index 3b0856428d3..00000000000 --- a/src/Binance/Signer.cpp +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Signer.h" -#include "Serialization.h" -#include "../HexCoding.h" -#include "../PrivateKey.h" -#include "../Coin.h" - -#include - -#include -#include -#include - -#include - -namespace TW::Binance { - -// Message prefixes -// see https://docs.binance.org/api-reference/transactions.html#amino-types -static const auto sendOrderPrefix = Data{0x2A, 0x2C, 0x87, 0xFA}; -static const auto tradeOrderPrefix = Data{0xCE, 0x6D, 0xC0, 0x43}; -static const auto cancelTradeOrderPrefix = Data{0x16, 0x6E, 0x68, 0x1B}; -static const auto HTLTOrderPrefix = Data{0xB3, 0x3F, 0x9A, 0x24}; -static const auto depositHTLTOrderPrefix = Data{0x63, 0x98, 0x64, 0x96}; -static const auto claimHTLTOrderPrefix = Data{0xC1, 0x66, 0x53, 0x00}; -static const auto refundHTLTOrderPrefix = Data{0x34, 0x54, 0xA2, 0x7C}; -static const auto pubKeyPrefix = Data{0xEB, 0x5A, 0xE9, 0x87}; -static const auto transactionPrefix = Data{0xF0, 0x62, 0x5D, 0xEE}; -static const auto tokenIssueOrderPrefix = Data{0x17, 0xEF, 0xAB, 0x80}; -static const auto tokenMintOrderPrefix = Data{0x46, 0x7E, 0x08, 0x29}; -static const auto tokenBurnOrderPrefix = Data{0x7E, 0xD2, 0xD2, 0xA0}; -static const auto tokenFreezeOrderPrefix = Data{0xE7, 0x74, 0xB3, 0x2D}; -static const auto tokenUnfreezeOrderPrefix = Data{0x65, 0x15, 0xFF, 0x0D}; -static const auto transferOutOrderPrefix = Data{0x80, 0x08, 0x19, 0xC0}; -static const auto sideDelegateOrderPrefix = Data{0xE3, 0xA0, 0x7F, 0xD2}; -static const auto sideRedelegateOrderPrefix = Data{0xE3, 0xCE, 0xD3, 0x64}; -static const auto sideUndelegateOrderPrefix = Data{0x51, 0x4F, 0x7E, 0x0E}; -static const auto timeLockOrderPrefix = Data{0x07, 0x92, 0x15, 0x31}; -static const auto timeRelockOrderPrefix = Data{0x50, 0x47, 0x11, 0xDA}; -static const auto timeUnlockOrderPrefix = Data{0xC4, 0x05, 0x0C, 0x6C}; - -Proto::SigningOutput Signer::sign(const Proto::SigningInput& input, const std::string& chainHrp) noexcept { - auto signer = Signer(input, chainHrp); - auto encoded = signer.build(); - auto output = Proto::SigningOutput(); - output.set_encoded(encoded.data(), encoded.size()); - return output; -} - -std::string Signer::signJSON(const std::string& json, const Data& key, const std::string& chainHrp) { - auto input = Proto::SigningInput(); - google::protobuf::util::JsonStringToMessage(json, &input); - input.set_private_key(key.data(), key.size()); - auto output = Signer::sign(input, chainHrp); - return hex(output.encoded()); -} - -Signer::Signer(Proto::SigningInput input, TWCoinType coin): - input(std::move(input)), - chainHrp(stringForHRP(TW::hrp(coin))) { -} - -Data Signer::build() const { - auto signature = encodeSignature(sign()); - return encodeTransaction(signature); -} - -Data Signer::sign() const { - auto hash = preImageHash(); - auto key = PrivateKey(input.private_key()); - auto signature = key.sign(hash, TWCurveSECP256k1); - return {signature.begin(), signature.end() - 1}; -} - -Data Signer::preImageHash() const { - return Hash::sha256(signaturePreimage()); -} - -Proto::SigningOutput Signer::compile(const Data& signature, const PublicKey& publicKey) const { - // validate public key - if (publicKey.type != TWPublicKeyTypeSECP256k1) { - throw std::invalid_argument("Invalid public key"); - } - { - // validate correctness of signature - const auto hash = this->preImageHash(); - if (!publicKey.verify(signature, hash)) { - throw std::invalid_argument("Invalid signature/hash/publickey combination"); - } - } - const auto encoded = encodeTransaction(encodeSignature(signature, publicKey)); - auto output = Proto::SigningOutput(); - output.set_encoded(encoded.data(), encoded.size()); - return output; -} - -std::string Signer::signaturePreimage() const { - auto json = signatureJSON(input, this->chainHrp); - return json.dump(); -} - -Data Signer::encodeTransaction(const Data& signature) const { - auto msg = encodeOrder(); - auto transaction = Binance::Proto::Transaction(); - transaction.add_msgs(msg.data(), msg.size()); - transaction.add_signatures(signature.data(), signature.size()); - transaction.set_memo(input.memo()); - transaction.set_source(input.source()); - - auto data = transaction.SerializeAsString(); - return aminoWrap(data, transactionPrefix, true); -} - -Data Signer::encodeOrder() const { - std::string data; - Data prefix; - if (input.has_trade_order()) { - data = input.trade_order().SerializeAsString(); - prefix = tradeOrderPrefix; - } else if (input.has_cancel_trade_order()) { - data = input.cancel_trade_order().SerializeAsString(); - prefix = cancelTradeOrderPrefix; - } else if (input.has_send_order()) { - data = input.send_order().SerializeAsString(); - prefix = sendOrderPrefix; - } else if (input.has_issue_order()) { - data = input.issue_order().SerializeAsString(); - prefix = tokenIssueOrderPrefix; - } else if (input.has_mint_order()) { - data = input.mint_order().SerializeAsString(); - prefix = tokenMintOrderPrefix; - } else if (input.has_burn_order()) { - data = input.burn_order().SerializeAsString(); - prefix = tokenBurnOrderPrefix; - } else if (input.has_freeze_order()) { - data = input.freeze_order().SerializeAsString(); - prefix = tokenFreezeOrderPrefix; - } else if (input.has_unfreeze_order()) { - data = input.unfreeze_order().SerializeAsString(); - prefix = tokenUnfreezeOrderPrefix; - } else if (input.has_htlt_order()) { - data = input.htlt_order().SerializeAsString(); - prefix = HTLTOrderPrefix; - } else if (input.has_deposithtlt_order()) { - data = input.deposithtlt_order().SerializeAsString(); - prefix = depositHTLTOrderPrefix; - } else if (input.has_claimhtlt_order()) { - data = input.claimhtlt_order().SerializeAsString(); - prefix = claimHTLTOrderPrefix; - } else if (input.has_refundhtlt_order()) { - data = input.refundhtlt_order().SerializeAsString(); - prefix = refundHTLTOrderPrefix; - } else if (input.has_transfer_out_order()) { - data = input.transfer_out_order().SerializeAsString(); - prefix = transferOutOrderPrefix; - } else if (input.has_side_delegate_order()) { - data = input.side_delegate_order().SerializeAsString(); - prefix = sideDelegateOrderPrefix; - } else if (input.has_side_redelegate_order()) { - data = input.side_redelegate_order().SerializeAsString(); - prefix = sideRedelegateOrderPrefix; - } else if (input.has_side_undelegate_order()) { - data = input.side_undelegate_order().SerializeAsString(); - prefix = sideUndelegateOrderPrefix; - } else if (input.has_time_lock_order()) { - data = input.time_lock_order().SerializeAsString(); - prefix = timeLockOrderPrefix; - } else if (input.has_time_relock_order()) { - data = input.time_relock_order().SerializeAsString(); - prefix = timeRelockOrderPrefix; - } else if (input.has_time_unlock_order()) { - data = input.time_unlock_order().SerializeAsString(); - prefix = timeUnlockOrderPrefix; - } else { - return {}; - } - return aminoWrap(data, prefix, false); -} - -Data Signer::encodeSignature(const Data& signature) const { - auto key = PrivateKey(input.private_key()); - auto publicKey = key.getPublicKey(TWPublicKeyTypeSECP256k1); - return encodeSignature(signature, publicKey); -} - -Data Signer::encodeSignature(const Data& signature, const PublicKey& publicKey) const { - auto encodedPublicKey = pubKeyPrefix; - encodedPublicKey.insert(encodedPublicKey.end(), static_cast(publicKey.bytes.size())); - encodedPublicKey.insert(encodedPublicKey.end(), publicKey.bytes.begin(), publicKey.bytes.end()); - - auto object = Binance::Proto::Signature(); - object.set_pub_key(encodedPublicKey.data(), encodedPublicKey.size()); - object.set_signature(signature.data(), signature.size()); - object.set_account_number(input.account_number()); - object.set_sequence(input.sequence()); - - return aminoWrap(object.SerializeAsString(), {}, false); -} - -Data Signer::aminoWrap(const std::string& raw, const Data& typePrefix, bool prefixWithSize) const { - const auto contentsSize = raw.size() + typePrefix.size(); - auto size = contentsSize; - if (prefixWithSize) { - size += 10; - } - - std::string msg; - msg.reserve(size); - { - google::protobuf::io::StringOutputStream output(&msg); - google::protobuf::io::CodedOutputStream cos(&output); - if (prefixWithSize) { - cos.WriteVarint64(contentsSize); - } - cos.WriteRaw(typePrefix.data(), static_cast(typePrefix.size())); - cos.WriteRaw(raw.data(), static_cast(raw.size())); - } - - return {msg.begin(), msg.end()}; -} - -} // namespace TW::Binance diff --git a/src/Binance/Signer.h b/src/Binance/Signer.h deleted file mode 100644 index 88d462f1f6f..00000000000 --- a/src/Binance/Signer.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "Data.h" -#include "PublicKey.h" -#include "../proto/Binance.pb.h" - -#include -#include -#include - -namespace TW::Binance { - -/// Helper class that performs Binance transaction signing. -class Signer { - public: - /// Signs a Proto::SigningInput transaction - static Proto::SigningOutput sign(const Proto::SigningInput& input, const std::string& chainHrp) noexcept; - /// Signs a json Proto::SigningInput with private key - static std::string signJSON(const std::string& json, const Data& key, const std::string& chainHrp); - public: - Proto::SigningInput input; - std::string chainHrp; - - /// Initializes a transaction signer. - explicit Signer(Proto::SigningInput input, std::string chainHrp): - input(std::move(input)), - chainHrp(std::move(chainHrp)) { - } - - /// Initializes a transaction signer. - explicit Signer(Proto::SigningInput input, TWCoinType coin = TWCoinTypeBinance); - - /// Builds a signed transaction. - /// - /// \returns the signed transaction data or an empty vector if there is an - /// error. - TW::Data build() const; - - /// Signs the transaction. - /// - /// \returns the transaction signature or an empty vector if there is an - /// error. - TW::Data sign() const; - - TW::Data preImageHash() const; - Proto::SigningOutput compile(const Data& signature, const PublicKey& publicKey) const; - std::string signaturePreimage() const; - - private: - TW::Data encodeTransaction(const TW::Data& signature) const; - TW::Data encodeOrder() const; - TW::Data encodeSignature(const TW::Data& signature) const; - TW::Data encodeSignature(const TW::Data& signature, const PublicKey& publicKey) const; - TW::Data aminoWrap(const std::string& raw, const TW::Data& typePrefix, - bool isPrefixLength) const; -}; - -} // namespace TW::Binance diff --git a/src/Coin.cpp b/src/Coin.cpp index 943324470a7..a9ccd452233 100644 --- a/src/Coin.cpp +++ b/src/Coin.cpp @@ -323,12 +323,6 @@ void TW::anyCoinCompileWithSignatures(TWCoinType coinType, const Data& txInputDa dispatcher->compile(coinType, txInputData, signatures, publicKeys, txOutputOut); } -Data TW::anyCoinBuildTransactionInput(TWCoinType coinType, const std::string& from, const std::string& to, const uint256_t& amount, const std::string& asset, const std::string& memo, const std::string& chainId) { - auto* dispatcher = coinDispatcher(coinType); - assert(dispatcher != nullptr); - return dispatcher->buildTransactionInput(coinType, from, to, amount, asset, memo, chainId); -} - // Coin info accessors extern const CoinInfo getCoinInfo(TWCoinType coin); // in generated CoinInfoData.cpp file diff --git a/src/Coin.h b/src/Coin.h index 3c2bc45ca89..63ac9f07fbe 100644 --- a/src/Coin.h +++ b/src/Coin.h @@ -126,8 +126,6 @@ Data anyCoinPreImageHashes(TWCoinType coinType, const Data& txInputData); void anyCoinCompileWithSignatures(TWCoinType coinType, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& txOutputOut); -Data anyCoinBuildTransactionInput(TWCoinType coinType, const std::string& from, const std::string& to, const uint256_t& amount, const std::string& asset, const std::string& memo, const std::string& chainId); - // Describes a derivation: path + optional format + optional name struct Derivation { TWDerivation name = TWDerivationDefault; diff --git a/src/CoinEntry.h b/src/CoinEntry.h index c8b0b81bbaf..2581fe17e7b 100644 --- a/src/CoinEntry.h +++ b/src/CoinEntry.h @@ -67,9 +67,6 @@ class CoinEntry { virtual Data preImageHashes([[maybe_unused]] TWCoinType coin, [[maybe_unused]] const Data& txInputData) const { return {}; } // Optional method for compiling a transaction with externally-supplied signatures & pubkeys. virtual void compile([[maybe_unused]] TWCoinType coin, [[maybe_unused]] const Data& txInputData, [[maybe_unused]] const std::vector& signatures, [[maybe_unused]] const std::vector& publicKeys, [[maybe_unused]] Data& dataOut) const {} - // Optional helper to prepare a SigningInput from simple parameters. - // Not suitable for UTXO chains. Some parameters, like chain-specific fee/gas paraemters, may need to be set in the SigningInput. - virtual Data buildTransactionInput([[maybe_unused]] TWCoinType coinType, [[maybe_unused]] const std::string& from, [[maybe_unused]] const std::string& to, [[maybe_unused]] const uint256_t& amount, [[maybe_unused]] const std::string& asset, [[maybe_unused]] const std::string& memo, [[maybe_unused]] const std::string& chainId) const { return Data(); } }; // In each coin's Entry.cpp the specific types of the coin are used, this template enforces the Signer implement: diff --git a/src/Cosmos/Entry.cpp b/src/Cosmos/Entry.cpp index 2886108b0d9..2d64e48b4ea 100644 --- a/src/Cosmos/Entry.cpp +++ b/src/Cosmos/Entry.cpp @@ -16,24 +16,13 @@ using namespace std; namespace TW::Cosmos { -// TODO call `signRustJSON` when it's done. string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { - auto input = Proto::SigningInput(); - google::protobuf::util::JsonStringToMessage(json, &input); - input.set_private_key(key.data(), key.size()); - - auto inputData = data(input.SerializeAsString()); - Data dataOut; - sign(coin, inputData, dataOut); - - if (dataOut.empty()) { - return {}; - } - - Proto::SigningOutput output; - output.ParseFromArray(dataOut.data(), static_cast(dataOut.size())); - - return output.json(); + return signJSONHelper( + coin, + json, + key, + [](const Proto::SigningOutput& output) { return output.json(); } + ); } } // namespace TW::Cosmos diff --git a/src/Cosmos/Entry.h b/src/Cosmos/Entry.h index 66b40cb1403..26cbc60fde8 100644 --- a/src/Cosmos/Entry.h +++ b/src/Cosmos/Entry.h @@ -12,7 +12,7 @@ namespace TW::Cosmos { /// Entry point for implementation of Cosmos coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry : public Rust::RustCoinEntry { +class Entry : public Rust::RustCoinEntryWithSignJSON { public: ~Entry() override = default; bool supportsJSONSigning() const final { return true; } diff --git a/src/Ethereum/Entry.cpp b/src/Ethereum/Entry.cpp index 78834004477..49d43c73ba7 100644 --- a/src/Ethereum/Entry.cpp +++ b/src/Ethereum/Entry.cpp @@ -10,28 +10,15 @@ #include "HexCoding.h" #include "proto/Ethereum.pb.h" -#include - namespace TW::Ethereum { -// TODO call `signRustJSON` when it's done. -std::string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { - auto input = Proto::SigningInput(); - google::protobuf::util::JsonStringToMessage(json, &input); - input.set_private_key(key.data(), key.size()); - - auto inputData = data(input.SerializeAsString()); - Data dataOut; - sign(coin, inputData, dataOut); - - if(dataOut.empty()) { - return {}; - } - - Proto::SigningOutput output; - output.ParseFromArray(dataOut.data(), static_cast(dataOut.size())); - - return hex(output.encoded()); +std::string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { + return signJSONHelper( + coin, + json, + key, + [](const Proto::SigningOutput& output) { return hex(output.encoded()); } + ); } } // namespace TW::Ethereum diff --git a/src/Ethereum/Entry.h b/src/Ethereum/Entry.h index f2a5203a0d2..94b93c57792 100644 --- a/src/Ethereum/Entry.h +++ b/src/Ethereum/Entry.h @@ -12,7 +12,7 @@ namespace TW::Ethereum { /// Entry point for Ethereum and Ethereum-fork coins. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry : public Rust::RustCoinEntry { +class Entry : public Rust::RustCoinEntryWithSignJSON { public: bool supportsJSONSigning() const final { return true; } std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const final; diff --git a/src/TransactionCompiler.cpp b/src/TransactionCompiler.cpp index 721120c1259..5289bffb5b1 100644 --- a/src/TransactionCompiler.cpp +++ b/src/TransactionCompiler.cpp @@ -10,13 +10,6 @@ using namespace TW; - -Data TransactionCompiler::buildInput(TWCoinType coinType, const std::string& from, const std::string& to, const std::string& amount, const std::string& asset, const std::string& memo, const std::string& chainId) { - // parse amount - uint256_t amount256 { amount }; - return anyCoinBuildTransactionInput(coinType, from, to, amount256, asset, memo, chainId); -} - Data TransactionCompiler::preImageHashes(TWCoinType coinType, const Data& txInputData) { return anyCoinPreImageHashes(coinType, txInputData); } diff --git a/src/TransactionCompiler.h b/src/TransactionCompiler.h index 68688af0827..d4963d4c311 100644 --- a/src/TransactionCompiler.h +++ b/src/TransactionCompiler.h @@ -18,9 +18,6 @@ namespace TW { /// Non-core transaction utility methods, like building a transaction using an external signature class TransactionCompiler { public: - /// Build a coin-specific SigningInput protobuf transaction input, from simple transaction parameters - static Data buildInput(TWCoinType coinType, const std::string& from, const std::string& to, const std::string& amount, const std::string& asset, const std::string& memo, const std::string& chainId); - /// Obtain pre-signing hash of a transaction. /// It will return a proto object named `PreSigningOutput` which will include hash. /// We provide a default `PreSigningOutput` in TransactionCompiler.proto. diff --git a/src/interface/TWTransactionCompiler.cpp b/src/interface/TWTransactionCompiler.cpp index 454e8f9aa0a..dffd88fbb5e 100644 --- a/src/interface/TWTransactionCompiler.cpp +++ b/src/interface/TWTransactionCompiler.cpp @@ -14,23 +14,6 @@ using namespace TW; - -TWData *_Nonnull TWTransactionCompilerBuildInput(enum TWCoinType coinType, TWString *_Nonnull from, TWString *_Nonnull to, TWString *_Nonnull amount, TWString *_Nonnull asset, TWString *_Nonnull memo, TWString *_Nonnull chainId) { - Data result; - try { - result = TransactionCompiler::buildInput( - coinType, - std::string(TWStringUTF8Bytes(from)), - std::string(TWStringUTF8Bytes(to)), - std::string(TWStringUTF8Bytes(amount)), - std::string(TWStringUTF8Bytes(asset)), - std::string(TWStringUTF8Bytes(memo)), - std::string(TWStringUTF8Bytes(chainId)) - ); - } catch (...) {} // return empty - return TWDataCreateWithBytes(result.data(), result.size()); -} - static std::vector createFromTWDataVector(const struct TWDataVector* _Nonnull dataVector) { std::vector ret; const auto n = TWDataVectorSize(dataVector); diff --git a/src/rust/RustCoinEntry.h b/src/rust/RustCoinEntry.h index edcf926d9e2..5c481aaa284 100644 --- a/src/rust/RustCoinEntry.h +++ b/src/rust/RustCoinEntry.h @@ -8,8 +8,14 @@ #include "CoinEntry.h" +#include + namespace TW::Rust { +/// The function takes a Protobuf output message `const Output&` and returns `std::string`. +template +using SignJSONOutputHandler = std::function; + class RustCoinEntry : public CoinEntry { public: ~RustCoinEntry() noexcept override = default; @@ -23,4 +29,37 @@ class RustCoinEntry : public CoinEntry { void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const override; }; +class RustCoinEntryWithSignJSON: public RustCoinEntry { +public: + ~RustCoinEntryWithSignJSON() noexcept override = default; + +protected: + /// Helper function that can be used by [`Entry::signJSON`] to: + /// 1. Deserialize the given `json` as an `Input` object. + /// 2. Put the given `key` as `Input::private_key`. + /// 3. Serialize the input as bytes and call `Entry::sign`. + /// 4. Deserialize the output bytes as an `Output` object. + /// 5. Map the output object to a string. + template + std::string signJSONHelper(TWCoinType coin, const std::string &json, const Data &key, + SignJSONOutputHandler mapOutput) const { + Input input; + google::protobuf::util::JsonStringToMessage(json, &input); + input.set_private_key(key.data(), key.size()); + + auto inputData = data(input.SerializeAsString()); + Data dataOut; + sign(coin, inputData, dataOut); + + if (dataOut.empty()) { + return {}; + } + + Output output; + output.ParseFromArray(dataOut.data(), static_cast(dataOut.size())); + + return mapOutput(output); + } +}; + } // namespace TW::Rust diff --git a/tests/chains/Binance/SignerTests.cpp b/tests/chains/Binance/SignerTests.cpp deleted file mode 100644 index dd9280894e3..00000000000 --- a/tests/chains/Binance/SignerTests.cpp +++ /dev/null @@ -1,702 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Bech32Address.h" -#include "Binance/Address.h" -#include "Binance/Signer.h" -#include "Coin.h" -#include "Ethereum/Address.h" -#include "HDWallet.h" -#include "HexCoding.h" -#include "proto/Binance.pb.h" - -#include - -namespace TW::Binance { - -TEST(BinanceSigner, Sign) { - auto input = Proto::SigningInput(); - input.set_chain_id("chain-bnb"); - input.set_account_number(12); - input.set_sequence(35); - input.set_memo(""); - input.set_source(1); - - auto key = parse_hex("90335b9d2153ad1a9799a3ccc070bd64b4164e9642ee1dd48053c33f9a3a05e9"); - input.set_private_key(key.data(), key.size()); - - auto& order = *input.mutable_trade_order(); - Binance::Address address; - ASSERT_TRUE(Binance::Address::decode("bnb1hgm0p7khfk85zpz5v0j8wnej3a90w709vhkdfu", address)); - auto keyhash = address.getKeyHash(); - order.set_sender(keyhash.data(), keyhash.size()); - order.set_id("BA36F0FAD74D8F41045463E4774F328F4AF779E5-36"); - order.set_symbol("NNB-338_BNB"); - order.set_ordertype(2); - order.set_side(1); - order.set_price(136350000); - order.set_quantity(100000000); - order.set_timeinforce(1); - - auto signer = Binance::Signer(std::move(input)); - auto signature = signer.sign(); - - ASSERT_EQ(hex(signature), - "9123cb6906bb20aeb753f4a121d4d88ff0e9750ba75b0c4e10d76caee1e7d2481290fa3b9887a6225d69" - "97f5f939ef834ea61d596a314237c48e560da9e17b5a"); -} - -TEST(BinanceSigner, Build) { - auto input = Proto::SigningInput(); - input.set_chain_id("chain-bnb"); - input.set_account_number(1); - input.set_sequence(10); - - auto key = parse_hex("90335b9d2153ad1a9799a3ccc070bd64b4164e9642ee1dd48053c33f9a3a05e9"); - input.set_private_key(key.data(), key.size()); - - auto& order = *input.mutable_trade_order(); - auto address = Binance::Address(parse_hex("b6561dcc104130059a7c08f48c64610c1f6f9064")); - auto keyhash = address.getKeyHash(); - order.set_sender(keyhash.data(), keyhash.size()); - order.set_id("B6561DCC104130059A7C08F48C64610C1F6F9064-11"); - order.set_symbol("BTC-5C4_BNB"); - order.set_ordertype(2); - order.set_side(1); - order.set_price(100000000); - order.set_quantity(1200000000); - order.set_timeinforce(1); - - auto signer = Binance::Signer(std::move(input)); - auto result = signer.build(); - - ASSERT_EQ(hex(result), "db01" - "f0625dee" - "0a65" - "ce6dc043" - "0a14""b6561dcc104130059a7c08f48c64610c1f6f9064" - "122b""423635363144434331303431333030353941374330384634384336343631304331463646393036342d3131" - "1a0b""4254432d3543345f424e42" - "2002" - "2801" - "3080c2d72f" - "3880989abc04" - "4001" - "126e" - "0a26" - "eb5ae987" - "21029729a52e4e3c2b4a4e52aa74033eedaf8ba1df5ab6d1f518fd69e67bbd309b0e" - "1240""2a78b6d9a108eb9440221802b626e24d80179395ac984f016db012ef1a0c16d71b4d7053e05366ae3ea2681fc8052398fda20551c965d74c5970bbc66b94b48e" - "1801" - "200a" - ); -} - -TEST(BinanceSigner, BuildSend) { - auto signingInput = Proto::SigningInput(); - signingInput.set_chain_id("chain-bnb"); - signingInput.set_account_number(19); - signingInput.set_sequence(23); - signingInput.set_memo("test"); - signingInput.set_source(1); - - auto key = parse_hex("95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832"); - signingInput.set_private_key(key.data(), key.size()); - - auto& order = *signingInput.mutable_send_order(); - - auto fromKeyhash = parse_hex("40c2979694bbc961023d1d27be6fc4d21a9febe6"); - auto fromAddress = Binance::Address(fromKeyhash); - - auto toKeyhash = parse_hex("88b37d5e05f3699e2a1406468e5d87cb9dcceb95"); - auto toAddress = Binance::Address(toKeyhash); - - auto input = order.add_inputs(); - input->set_address(fromKeyhash.data(), fromKeyhash.size()); - auto inputCoin = input->add_coins(); - inputCoin->set_denom("BNB"); - inputCoin->set_amount(1'001'000'000); - - auto output = order.add_outputs(); - output->set_address(toKeyhash.data(), toKeyhash.size()); - auto outputCoin = output->add_coins(); - outputCoin->set_denom("BNB"); - outputCoin->set_amount(1'001'000'000); - - auto signer = Binance::Signer(std::move(signingInput)); - auto signature = signer.sign(); - ASSERT_EQ(hex(signature), - "c65a13440f18a155bd971ee40b9e0dd58586f5bf344e12ec4c76c439aebca8c7789bab7bfbfb4ce89aad" - "c4a02df225b6b6efc861c13bbeb5f7a3eea2d7ffc80f"); - - auto result = signer.build(); - - ASSERT_EQ(hex(result), "cc01" - "f0625dee" - "0a4e" - "2a2c87fa" - "0a23""0a1440c2979694bbc961023d1d27be6fc4d21a9febe6120b0a03424e4210c098a8dd03" - "1223""0a1488b37d5e05f3699e2a1406468e5d87cb9dcceb95120b0a03424e4210c098a8dd03" - "126e" - "0a26" - "eb5ae987" - "21026a35920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e502" - "1240""c65a13440f18a155bd971ee40b9e0dd58586f5bf344e12ec4c76c439aebca8c7789bab7bfbfb4ce89aadc4a02df225b6b6efc861c13bbeb5f7a3eea2d7ffc80f" - "1813" - "2017" - "1a04""74657374" - "2001" - ); -} - -TEST(BinanceSigner, BuildSend2) { - const auto derivationPath = TW::derivationPath(TWCoinTypeBinance); - - const auto fromWallet = HDWallet("swift slam quote sail high remain mandate sample now stamp title among fiscal captain joy puppy ghost arrow attract ozone situate install gain mean", ""); - const auto fromPrivateKey = fromWallet.getKey(TWCoinTypeBinance, derivationPath); - const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - - const auto toWallet = HDWallet( "bottom quick strong ranch section decide pepper broken oven demand coin run jacket curious business achieve mule bamboo remain vote kid rigid bench rubber", ""); - const auto toPrivateKey = toWallet.getKey(TWCoinTypeBinance, derivationPath); - const auto toPublicKey = PublicKey(toPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - - auto signingInput = Proto::SigningInput(); - signingInput.set_chain_id("bnbchain-1000"); - signingInput.set_account_number(0); - signingInput.set_sequence(1); - signingInput.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); - - auto token = Proto::SendOrder::Token(); - token.set_denom("BNB"); - token.set_amount(100000000000000); - - auto input = Proto::SendOrder::Input(); - auto fromKeyHash = Binance::Address(fromPublicKey).getKeyHash(); - input.set_address(fromKeyHash.data(), fromKeyHash.size()); - *input.add_coins() = token; - - auto output = Proto::SendOrder::Output(); - auto toKeyHash = Binance::Address(toPublicKey).getKeyHash(); - output.set_address(toKeyHash.data(), toKeyHash.size()); - *output.add_coins() = token; - - auto sendOrder = Proto::SendOrder(); - *sendOrder.add_inputs() = input; - *sendOrder.add_outputs() = output; - - *signingInput.mutable_send_order() = sendOrder; - - const auto data = Signer(std::move(signingInput)).build(); - ASSERT_EQ(hex(data), - "c601" - "f0625dee" - "0a52" - "2a2c87fa" - "0a25""0a141d0e3086e8e4e0a53c38a90d55bd58b34d57d2fa120d0a03424e42108080e983b1de16" - "1225""0a146b571fc0a9961a7ddf45e49a88a4d83941fcabbe120d0a03424e42108080e983b1de16" - "126c" - "0a26" - "eb5ae987" - "21027e69d96640300433654e016d218a8d7ffed751023d8efe81e55dedbd6754c971" - "1240""8b23eecfa8237a27676725173e58154e6c204bb291b31c3b7b507c8f04e2773909ba70e01b54f4bd0bc76669f5712a5a66b9508acdf3aa5e4fde75fbe57622a1" - "2001" - ); -} - -TEST(BinanceSigner, BuildHTLT) { - const auto fromPrivateKey = - PrivateKey(parse_hex("eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d")); - const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - auto fromAddr = Binance::Address(fromPublicKey).getKeyHash(); - - const auto toPrivateKey = - PrivateKey(parse_hex("851fab89c14f4bbec0cc06f5e445ec065efc641068d78b308c67217d9bd5c88a")); - const auto toPublicKey = PublicKey(toPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - auto toAddr = Binance::Address(toPublicKey).getKeyHash(); - - auto signingInput = Proto::SigningInput(); - signingInput.set_chain_id("test-chain"); - signingInput.set_account_number(15); - signingInput.set_sequence(0); - signingInput.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); - - auto token = Proto::SendOrder::Token(); - token.set_denom("BNB"); - token.set_amount(100000000); - - auto randomNumberHash = - parse_hex("e8eae926261ab77d018202434791a335249b470246a7b02e28c3b2fb6ffad8f3"); - - auto& htltOrder = *signingInput.mutable_htlt_order(); - htltOrder.set_from(fromAddr.data(), fromAddr.size()); - htltOrder.set_to(toAddr.data(), toAddr.size()); - htltOrder.set_recipient_other_chain(""); - htltOrder.set_sender_other_chain(""); - *htltOrder.add_amount() = token; - htltOrder.set_height_span(400); - htltOrder.set_expected_income("100000000:BTC-1DC"); - htltOrder.set_timestamp(1567746273); - htltOrder.set_random_number_hash(randomNumberHash.data(), randomNumberHash.size()); - htltOrder.set_cross_chain(false); - - const auto data = Binance::Signer(std::move(signingInput)).build(); - ASSERT_EQ(hex(data), - "ee01f0625dee0a7ab33f9a240a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e112140153f11d6db7" - "e69c7d51e771c697378018fb6c242a20e8eae926261ab77d018202434791a335249b470246a7b02e28c3" - "b2fb6ffad8f330e1d1c7eb053a0a0a03424e421080c2d72f42113130303030303030303a4254432d3144" - "43489003126c0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f" - "6b6a8fc7124051439de2da19fe9fd22137c903cfc5dc87553bf05dca0bb202c0e07c47f9b51269efa272" - "43eb7b55888f5384a84ac1eac6d325c830d1be0ed042838e2dc0f6a9180f"); -} - -TEST(BinanceSigner, BuildDepositHTLT) { - const auto fromPrivateKey = - PrivateKey(parse_hex("851fab89c14f4bbec0cc06f5e445ec065efc641068d78b308c67217d9bd5c88a")); - const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - auto fromAddr = Binance::Address(fromPublicKey).getKeyHash(); - - auto signingInput = Proto::SigningInput(); - signingInput.set_chain_id("test-chain"); - signingInput.set_account_number(16); - signingInput.set_sequence(0); - signingInput.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); - - auto token = Proto::SendOrder::Token(); - token.set_denom("BTC-1DC"); - token.set_amount(100000000); - - auto swapID = parse_hex("dd8fd4719741844d35eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e5"); - - auto& depositHTLTOrder = *signingInput.mutable_deposithtlt_order(); - depositHTLTOrder.set_from(fromAddr.data(), fromAddr.size()); - depositHTLTOrder.set_swap_id(swapID.data(), swapID.size()); - *depositHTLTOrder.add_amount() = token; - - const auto data = Binance::Signer(std::move(signingInput)).build(); - ASSERT_EQ(hex(data), - "c001f0625dee0a4c639864960a140153f11d6db7e69c7d51e771c697378018fb6c24120e0a074254432d" - "3144431080c2d72f1a20dd8fd4719741844d35eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e5" - "126c0a26eb5ae98721038df6960084e20b2d07d50e1422f94105c6241d9f1482a4eb79ce8bfd460f19e4" - "12400ca4144c6818e2836d09b4faf3161781d85f9adfc00badb2eaa0953174610a233b81413dafcf8471" - "6abad48a4ed3aeb9884d90eb8416eec5d5c0c6930ab60bd01810"); -} - -TEST(BinanceSigner, BuildClaimHTLT) { - const auto fromPrivateKey = - PrivateKey(parse_hex("eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d")); - const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - auto fromAddr = Binance::Address(fromPublicKey).getKeyHash(); - - auto signingInput = Proto::SigningInput(); - signingInput.set_chain_id("test-chain"); - signingInput.set_account_number(15); - signingInput.set_sequence(1); - signingInput.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); - - auto randomNumber = - parse_hex("bda6933c7757d0ca428aa01fb9d0935a231f87bf2deeb9b409cea3f2d580a2cc"); - auto swapID = parse_hex("dd8fd4719741844d35eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e5"); - - auto& claimHTLTOrder = *signingInput.mutable_claimhtlt_order(); - claimHTLTOrder.set_from(fromAddr.data(), fromAddr.size()); - claimHTLTOrder.set_swap_id(swapID.data(), swapID.size()); - claimHTLTOrder.set_random_number(randomNumber.data(), randomNumber.size()); - - const auto data = Binance::Signer(std::move(signingInput)).build(); - ASSERT_EQ( - hex(data), - "d401f0625dee0a5ec16653000a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e11220dd8fd4719741844d35" - "eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e51a20bda6933c7757d0ca428aa01fb9d0935a231f87bf" - "2deeb9b409cea3f2d580a2cc126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561a" - "ac993dbe0f6b6a8fc71240fa30ba50111aa31d8329dacb6d044c1c7d54f1cb782bc9aa2a50c3fabce02a4579d7" - "5b76ca69a9fab11b676d9da66b5af7aa4c9ad3d18e24fffeb16433be39fb180f2001"); -} - -TEST(BinanceSigner, BuildRefundHTLT) { - const auto fromPrivateKey = - PrivateKey(parse_hex("eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d")); - const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - auto fromAddr = Binance::Address(fromPublicKey).getKeyHash(); - - auto signingInput = Proto::SigningInput(); - signingInput.set_chain_id("test-chain"); - signingInput.set_account_number(15); - signingInput.set_sequence(1); - signingInput.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); - - auto swapID = parse_hex("dd8fd4719741844d35eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e5"); - - auto& refundHTLTOrder = *signingInput.mutable_refundhtlt_order(); - refundHTLTOrder.set_from(fromAddr.data(), fromAddr.size()); - refundHTLTOrder.set_swap_id(swapID.data(), swapID.size()); - - const auto data = Binance::Signer(std::move(signingInput)).build(); - ASSERT_EQ(hex(data), - "b201f0625dee0a3c3454a27c0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e11220dd8fd4719741" - "844d35eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e5126e0a26eb5ae9872103a9a55c040c8e" - "b8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240c9f36142534d16ec8ce656f8eb73" - "70b32206a2d15198b7165acf1e2a18952c9e4570b0f862e1ab7bb868c30781a42c9e3ec0ae08982e8d6c" - "91c55b83c71b7b1e180f2001"); -} - -TEST(BinanceSigner, BuildIssueOrder) { - const auto fromPrivateKey = - PrivateKey(parse_hex("eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d")); - const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - const Data fromAddr = Binance::Address(fromPublicKey).getKeyHash(); - - auto signingInput = Proto::SigningInput(); - signingInput.set_chain_id("test-chain"); - signingInput.set_account_number(15); - signingInput.set_sequence(1); - signingInput.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); - - auto& issueOrder = *signingInput.mutable_issue_order(); - issueOrder.set_from(fromAddr.data(), fromAddr.size()); - issueOrder.set_name("NewBinanceToken"); - issueOrder.set_symbol("NNB-338_BNB"); - issueOrder.set_total_supply(1000000000); - issueOrder.set_mintable(true); - - const auto data = Binance::Signer(std::move(signingInput)).build(); - ASSERT_EQ(hex(data), - "b601f0625dee0a40" - "17efab80" - "0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1120f" - "4e657742696e616e6365546f6b656e" - "1a0b" - "4e4e422d3333385f424e42" - "208094ebdc032801126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc712401fbb993d643f03b3e8e757a502035f58c4c45aaaa6e107a3059ab7c6164283c10f1254e87feee21477c64c87b1a27d8481048533ae2f685b3ac0dc66e4edbc0b180f2001" - ); -} - -TEST(BinanceSigner, BuildMintOrder) { - const auto fromPrivateKey = - PrivateKey(parse_hex("eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d")); - const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - const Data fromAddr = Binance::Address(fromPublicKey).getKeyHash(); - - auto signingInput = Proto::SigningInput(); - signingInput.set_chain_id("test-chain"); - signingInput.set_account_number(15); - signingInput.set_sequence(1); - signingInput.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); - - auto& mintOrder = *signingInput.mutable_mint_order(); - mintOrder.set_from(fromAddr.data(), fromAddr.size()); - mintOrder.set_symbol("NNB-338_BNB"); - mintOrder.set_amount(1000000); - - const auto data = Binance::Signer(std::move(signingInput)).build(); - ASSERT_EQ(hex(data), - "a101f0625dee0a2b" - "467e0829" - "0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1120b" - "4e4e422d3333385f424e42" - "18c0843d126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991180f2001" - ); -} - -TEST(BinanceSigner, BuildBurnOrder) { - const auto fromPrivateKey = - PrivateKey(parse_hex("eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d")); - const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - const Data fromAddr = Binance::Address(fromPublicKey).getKeyHash(); - - auto signingInput = Proto::SigningInput(); - signingInput.set_chain_id("test-chain"); - signingInput.set_account_number(15); - signingInput.set_sequence(1); - signingInput.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); - - auto& burnOrder = *signingInput.mutable_burn_order(); - burnOrder.set_from(fromAddr.data(), fromAddr.size()); - burnOrder.set_symbol("NNB-338_BNB"); - burnOrder.set_amount(1000000); - - const auto data = Binance::Signer(std::move(signingInput)).build(); - ASSERT_EQ(hex(data), - "a101f0625dee0a2b" - "7ed2d2a0" - "0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1120b" - "4e4e422d3333385f424e42" - "18c0843d126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991180f2001" - ); -} - -TEST(BinanceSigner, BuildFreezeOrder) { - const auto fromPrivateKey = - PrivateKey(parse_hex("eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d")); - const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - const Data fromAddr = Binance::Address(fromPublicKey).getKeyHash(); - - auto signingInput = Proto::SigningInput(); - signingInput.set_chain_id("test-chain"); - signingInput.set_account_number(15); - signingInput.set_sequence(1); - signingInput.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); - - auto& freezeOrder = *signingInput.mutable_freeze_order(); - freezeOrder.set_from(fromAddr.data(), fromAddr.size()); - freezeOrder.set_symbol("NNB-338_BNB"); - freezeOrder.set_amount(1000000); - - const auto data = Binance::Signer(std::move(signingInput)).build(); - ASSERT_EQ(hex(data), - "a101f0625dee0a2b" - "e774b32d" - "0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1120b" - "4e4e422d3333385f424e42" - "18c0843d126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f" - "6b6a8fc71240e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162" - "722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991180f2001"); -} - -TEST(BinanceSigner, BuildUnfreezeOrder) { - const auto fromPrivateKey = - PrivateKey(parse_hex("eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d")); - const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - const Data fromAddr = Binance::Address(fromPublicKey).getKeyHash(); - - auto signingInput = Proto::SigningInput(); - signingInput.set_chain_id("test-chain"); - signingInput.set_account_number(15); - signingInput.set_sequence(1); - signingInput.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); - - auto& unfreezeOrder = *signingInput.mutable_unfreeze_order(); - unfreezeOrder.set_from(fromAddr.data(), fromAddr.size()); - unfreezeOrder.set_symbol("NNB-338_BNB"); - unfreezeOrder.set_amount(1000000); - - const auto data = Binance::Signer(std::move(signingInput)).build(); - ASSERT_EQ(hex(data), - "a101f0625dee0a2b" - "6515ff0d" - "0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1120b" - "4e4e422d3333385f424e42" - "18c0843d126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f" - "6b6a8fc71240e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162" - "722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991180f2001"); -} - -TEST(BinanceSigner, BuildTransferOutOrder) { - const auto fromPrivateKey = - PrivateKey(parse_hex("eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d")); - const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - const Data fromAddr = Binance::Address(fromPublicKey).getKeyHash(); - - const auto toAddr = Ethereum::Address("0x35552c16704d214347f29Fa77f77DA6d75d7C752"); - const auto toBytes = Data(toAddr.bytes.begin(), toAddr.bytes.end()); - - auto input = Proto::SigningInput(); - input.set_chain_id("test-chain"); - input.set_account_number(15); - input.set_sequence(1); - input.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); - - auto& order = *input.mutable_transfer_out_order(); - order.set_from(fromAddr.data(), fromAddr.size()); - order.set_to(toBytes.data(), toBytes.size()); - order.set_expire_time(12345678); - - auto& token = *order.mutable_amount(); - token.set_denom("BNB"); - token.set_amount(100000000); - - const auto data = Binance::Signer(std::move(input)).build(); - ASSERT_EQ(hex(data), - "b701f0625dee0a41800819c00a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1121435552c16704d" - "214347f29fa77f77da6d75d7c7521a0a0a03424e421080c2d72f20cec2f105126e0a26eb5ae9872103a9" - "a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc712407eda148e1167b1be12" - "71a788ccf4e3eade1c7e1773e9d2093982d7f802f8f85f35ef550049011728206e4eda1a272f9e96fd95" - "ef3983cad85a29cd14262c22e0180f2001"); -} - -TEST(BinanceSigner, BuildSideChainDelegate) { - const auto fromPrivateKey = - PrivateKey(parse_hex("eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d")); - const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - const Data fromAddr = Binance::Address(fromPublicKey).getKeyHash(); - auto validator = Bech32Address(""); - Bech32Address::decode("bva10npy5809y303f227g4leqw7vs3s6ep5ul26sq2", validator, "bva"); - - auto input = Proto::SigningInput(); - input.set_chain_id("test-chain"); - input.set_account_number(15); - input.set_sequence(1); - input.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); - - auto& order = *input.mutable_side_delegate_order(); - order.set_delegator_addr(fromAddr.data(), fromAddr.size()); - order.set_validator_addr(validator.getKeyHash().data(), validator.getKeyHash().size()); - order.set_chain_id("chapel"); - - auto& token = *order.mutable_delegation(); - token.set_denom("BNB"); - token.set_amount(200000000); - - const auto data = Binance::Signer(std::move(input)).build(); - ASSERT_EQ(hex(data), - "ba01f0625dee0a44e3a07fd20a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e112147cc24a1de524" - "5f14a95e457f903bcc8461ac869c1a0a0a03424e42108084af5f220663686170656c126e0a26eb5ae987" - "2103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc7124039302c9975fb" - "2a09ac2b6b6fb1d3b9fb5b4c03630d3d7a7da42b1c6736d6127142a3fcdca0b70a3d065da8d4f4df8b5d" - "9d8f46aeb3627a7d7aa901fe186af34c180f2001"); -} - -TEST(BinanceSigner, BuildSideChainRedelegate) { - const auto fromPrivateKey = - PrivateKey(parse_hex("eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d")); - const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - const Data fromAddr = Binance::Address(fromPublicKey).getKeyHash(); - auto validator1 = Bech32Address(""); - auto validator2 = Bech32Address(""); - Bech32Address::decode("bva1echrty7p8r23cwx8g3ezwcza9azy4zq7ca0pzw", validator1, "bva"); - Bech32Address::decode("bva1p7s26ervsmv3w83k5696glautc9sm5rchz5f5e", validator2, "bva"); - - auto input = Proto::SigningInput(); - input.set_chain_id("test-chain"); - input.set_account_number(15); - input.set_sequence(1); - input.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); - - auto& order = *input.mutable_side_redelegate_order(); - order.set_delegator_addr(fromAddr.data(), fromAddr.size()); - order.set_validator_src_addr(validator1.getKeyHash().data(), validator1.getKeyHash().size()); - order.set_validator_dst_addr(validator2.getKeyHash().data(), validator2.getKeyHash().size()); - order.set_chain_id("chapel"); - - auto& token = *order.mutable_amount(); - token.set_denom("BNB"); - token.set_amount(100000000); - - const auto data = Binance::Signer(std::move(input)).build(); - ASSERT_EQ(hex(data), - "d001f0625dee0a5ae3ced3640a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e11214ce2e3593c138" - "d51c38c7447227605d2f444a881e1a140fa0ad646c86d9171e36a68ba47fbc5e0b0dd078220a0a03424e" - "421080c2d72f2a0663686170656c126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08" - "af44ea561aac993dbe0f6b6a8fc71240114c6927423e95ecc831ec763b629b3a40db8feeb330528a941f" - "d74843c0d63b4271b23916770d4901988c1f56b20086e5768a12290ebec265e30a80f8f3d88e180f2001" - ); -} - -TEST(BinanceSigner, BuildSideChainUndelegate) { - const auto fromPrivateKey = - PrivateKey(parse_hex("eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d")); - const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - const Data fromAddr = Binance::Address(fromPublicKey).getKeyHash(); - auto validator = Bech32Address(""); - Bech32Address::decode("bva1echrty7p8r23cwx8g3ezwcza9azy4zq7ca0pzw", validator, "bva"); - - auto input = Proto::SigningInput(); - input.set_chain_id("test-chain"); - input.set_account_number(15); - input.set_sequence(1); - input.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); - - auto& order = *input.mutable_side_undelegate_order(); - order.set_delegator_addr(fromAddr.data(), fromAddr.size()); - order.set_validator_addr(validator.getKeyHash().data(), validator.getKeyHash().size()); - order.set_chain_id("chapel"); - - auto& token = *order.mutable_amount(); - token.set_denom("BNB"); - token.set_amount(100000000); - - const auto data = Binance::Signer(std::move(input)).build(); - ASSERT_EQ(hex(data), - "ba01f0625dee0a44514f7e0e0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e11214ce2e3593c138" - "d51c38c7447227605d2f444a881e1a0a0a03424e421080c2d72f220663686170656c126e0a26eb5ae987" - "2103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240a622b7ca7a28" - "75e5eaa675a5ed976b2ec3b8ca055a2b05e7fb471d328bd04df854789437dd06407e41ebb1e5a345604c" - "93663dfb660e223800636c0b94c2e798180f2001" - ); -} - -TEST(BinanceSigner, BuildTimeLockOrder) { - const auto fromPrivateKey = - PrivateKey(parse_hex("eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d")); - const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - const Data fromAddr = Binance::Address(fromPublicKey).getKeyHash(); - - auto signingInput = Proto::SigningInput(); - signingInput.set_chain_id("test-chain"); - signingInput.set_account_number(15); - signingInput.set_sequence(1); - signingInput.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); - - auto& lockOrder = *signingInput.mutable_time_lock_order(); - lockOrder.set_from_address(fromAddr.data(), fromAddr.size()); - lockOrder.set_description("Description locked for offer"); - auto amount = lockOrder.add_amount(); - amount->set_denom("BNB"); - amount->set_amount(1000000); - lockOrder.set_lock_time(1600001371); - - const auto data = Binance::Signer(std::move(signingInput)).build(); - EXPECT_EQ(hex(data), - "bf01f0625dee0a49" - "07921531" - "0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1121c4465736372697074696f6e206c6f636b656420666f72206f666665721a090a03424e4210c0843d20dbaaf8fa05126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240c270822b9515ba486c6a6b3472d388a5aea872ed960c0b53de0fafdc8682ef473a126f01e7dd2c00f04a0138a601b9540f54b14026846de362f7ab7f9fed948b180f2001" - ); -} - -TEST(BinanceSigner, BuildTimeRelockOrder) { - const auto fromPrivateKey = - PrivateKey(parse_hex("eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d")); - const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - const Data fromAddr = Binance::Address(fromPublicKey).getKeyHash(); - - auto signingInput = Proto::SigningInput(); - signingInput.set_chain_id("test-chain"); - signingInput.set_account_number(15); - signingInput.set_sequence(1); - signingInput.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); - - auto& relockOrder = *signingInput.mutable_time_relock_order(); - relockOrder.set_from_address(fromAddr.data(), fromAddr.size()); - relockOrder.set_id(333); - relockOrder.set_description("Description locked for offer"); - auto amount = relockOrder.add_amount(); - amount->set_denom("BNB"); - amount->set_amount(1000000); - relockOrder.set_lock_time(1600001371); - - const auto data = Binance::Signer(std::move(signingInput)).build(); - EXPECT_EQ(hex(data), - "c201f0625dee0a4c504711da0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e110cd021a1c446573" - "6372697074696f6e206c6f636b656420666f72206f6666657222090a03424e4210c0843d28dbaaf8fa05" - "126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc7" - "124086ddaa077c8ae551d402fa409cf7e91663982b0542200967c03c0b5876b181353250f689d342f221" - "7624a077b671ce7d09649187e29879f40abbbee9de7ab27c180f2001"); -} - -TEST(BinanceSigner, BuildTimeUnlockOrder) { - const auto fromPrivateKey = - PrivateKey(parse_hex("eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d")); - const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - const Data fromAddr = Binance::Address(fromPublicKey).getKeyHash(); - - auto signingInput = Proto::SigningInput(); - signingInput.set_chain_id("test-chain"); - signingInput.set_account_number(15); - signingInput.set_sequence(1); - signingInput.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); - - auto& unlockOrder = *signingInput.mutable_time_unlock_order(); - unlockOrder.set_from_address(fromAddr.data(), fromAddr.size()); - unlockOrder.set_id(333); - - const auto data = Binance::Signer(std::move(signingInput)).build(); - EXPECT_EQ(hex(data), - "9301f0625dee0a1dc4050c6c0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e110cd02126e0a26eb" - "5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240da777b" - "fd2032834f59ec9fe69fd6eaa4aca24242dfbc5ec4ef8c435cb9da7eb05ab78e1b8ca9f109657cb77996" - "898f1b59137b3d8f1e00f842e409e18033b347180f2001"); -} - -} // namespace TW::Binance diff --git a/tests/chains/Binance/TransactionCompilerTests.cpp b/tests/chains/Binance/TransactionCompilerTests.cpp index 8e5580d5b7c..5b5a4e7ef19 100644 --- a/tests/chains/Binance/TransactionCompilerTests.cpp +++ b/tests/chains/Binance/TransactionCompilerTests.cpp @@ -25,27 +25,29 @@ using namespace TW; TEST(BinanceCompiler, CompileWithSignatures) { /// Step 1: Prepare transaction input (protobuf) const auto coin = TWCoinTypeBinance; - const auto txInputData = TransactionCompiler::buildInput( - coin, - "bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2", // from - "bnb1hlly02l6ahjsgxw9wlcswnlwdhg4xhx38yxpd5", // to - "1", // amount - "BNB", // asset - "", // memo - "Binance-Chain-Nile" // testnet chainId - ); + // bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2 + const auto fromAddressData = parse_hex("40c2979694bbc961023d1d27be6fc4d21a9febe6"); + // bnb1hlly02l6ahjsgxw9wlcswnlwdhg4xhx38yxpd5 + const auto toAddressData = parse_hex("bffe47abfaede50419c577f1074fee6dd1535cd1"); - { - // Check, by parsing - EXPECT_EQ(txInputData.size(), 88ul); - Binance::Proto::SigningInput input; - ASSERT_TRUE(input.ParseFromArray(txInputData.data(), (int)txInputData.size())); - EXPECT_EQ(input.chain_id(), "Binance-Chain-Nile"); - EXPECT_TRUE(input.has_send_order()); - ASSERT_EQ(input.send_order().inputs_size(), 1); - EXPECT_EQ(hex(data(input.send_order().inputs(0).address())), - "40c2979694bbc961023d1d27be6fc4d21a9febe6"); - } + Binance::Proto::SigningInput txInput; + + txInput.set_chain_id("Binance-Chain-Nile"); + auto& sendOrder = *txInput.mutable_send_order(); + + auto& input1 = *sendOrder.add_inputs(); + input1.set_address(fromAddressData.data(), fromAddressData.size()); + auto& input1Coin = *input1.add_coins(); + input1Coin.set_amount(1); + input1Coin.set_denom("BNB"); + + auto& output1 = *sendOrder.add_outputs(); + output1.set_address(toAddressData.data(), toAddressData.size()); + auto& output1Coin = *output1.add_coins(); + output1Coin.set_amount(1); + output1Coin.set_denom("BNB"); + + auto txInputData = data(txInput.SerializeAsString()); /// Step 2: Obtain preimage hash const auto preImageHashes = TransactionCompiler::preImageHashes(coin, txInputData); @@ -115,6 +117,6 @@ TEST(BinanceCompiler, CompileWithSignatures) { Binance::Proto::SigningOutput output; ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); EXPECT_EQ(output.encoded().size(), 0ul); - EXPECT_EQ(output.error(), Common::Proto::Error_invalid_params); + EXPECT_EQ(output.error(), Common::Proto::Error_signatures_count); } } diff --git a/tests/chains/Ethereum/TransactionCompilerTests.cpp b/tests/chains/Ethereum/TransactionCompilerTests.cpp index 5a37950eeb6..64c208d5bb5 100644 --- a/tests/chains/Ethereum/TransactionCompilerTests.cpp +++ b/tests/chains/Ethereum/TransactionCompilerTests.cpp @@ -108,18 +108,3 @@ TEST(EthereumCompiler, CompileWithSignatures) { EXPECT_EQ(output.error(), Common::Proto::Error_signatures_count); } } - -TEST(EthereumCompiler, BuildTransactionInputShouldFail) { - const auto coin = TWCoinTypeEthereum; - const auto txInputData0 = - TransactionCompiler::buildInput(coin, - "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F", // from - "0x3535353535353535353535353535353535353535", // to - "1000000000000000000", // amount - "ETH", // asset - "Memo", // memo - "05" // chainId - ); - // Ethereum doesn't support `buildInput`. - EXPECT_TRUE(txInputData0.empty()); -} diff --git a/tests/chains/TBinance/AddressTests.cpp b/tests/chains/TBinance/AddressTests.cpp deleted file mode 100644 index 259a0f9505a..00000000000 --- a/tests/chains/TBinance/AddressTests.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright © 2017-2022 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "Binance/Address.h" -#include "PublicKey.h" -#include "PrivateKey.h" -#include -#include - -using namespace TW; -using namespace TW::Binance; - -TEST(TBinanceAddress, Valid) { - ASSERT_TRUE(Address::isValid(TWCoinTypeTBinance, "tbnb18mtcq20875cr0p7l4upm0u5zx4r9jpj2kfu9f8")); -} - -TEST(TBinanceAddress, Invalid) { - ASSERT_FALSE(Address::isValid(TWCoinTypeTBinance, "tbnb18mtcq20875cr0p7l4upm0u5zx4r9jpj2kfu9aa")); - ASSERT_FALSE(Address::isValid(TWCoinTypeTBinance, "bnb1h8xf9htasu9aclra954dnnve8fgcda4ae7qfa8")); -} - -TEST(TBinanceAddress, FromPrivateKey) { - auto privateKey = PrivateKey(parse_hex("fc75070f2d9939be82a378ec9a47912cb6df458055f51da022021f42a6bb5ee8")); - auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1), TWCoinTypeTBinance); - ASSERT_EQ(address.string(), "tbnb18mtcq20875cr0p7l4upm0u5zx4r9jpj2kfu9f8"); -} - -TEST(TBinanceAddress, FromPublicKey) { - auto publicKey = PublicKey(parse_hex("02d32c10f9f4b72d0213123d58257e0558e164e5373e719aee73ce5505852c1692"), TWPublicKeyTypeSECP256k1); - auto address = Address(publicKey, TWCoinTypeTBinance); - ASSERT_EQ(address.string(), "tbnb18mtcq20875cr0p7l4upm0u5zx4r9jpj2kfu9f8"); -} - -TEST(TBinanceAddress, FromString) { - Address address(TWCoinTypeTBinance); - Address::decode("tbnb18mtcq20875cr0p7l4upm0u5zx4r9jpj2kfu9f8", address); - ASSERT_EQ(address.string(), "tbnb18mtcq20875cr0p7l4upm0u5zx4r9jpj2kfu9f8"); -} diff --git a/tests/interface/TWTransactionCompilerTests.cpp b/tests/interface/TWTransactionCompilerTests.cpp index a131760e7ce..f2be40920e9 100644 --- a/tests/interface/TWTransactionCompilerTests.cpp +++ b/tests/interface/TWTransactionCompilerTests.cpp @@ -38,22 +38,35 @@ using namespace TW; TEST(TWTransactionCompiler, ExternalSignatureSignBinance) { /// Step 1: Prepare transaction input (protobuf) const auto coin = TWCoinTypeBinance; - const auto txInputData = WRAPD(TWTransactionCompilerBuildInput( - coin, - STRING("bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2").get(), // from - STRING("bnb1hlly02l6ahjsgxw9wlcswnlwdhg4xhx38yxpd5").get(), // to - STRING("1").get(), // amount - STRING("BNB").get(), // asset - STRING("").get(), // memo - STRING("Binance-Chain-Nile").get() // testnet chainId - )); + // bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2 + const auto fromAddressData = parse_hex("40c2979694bbc961023d1d27be6fc4d21a9febe6"); + // bnb1hlly02l6ahjsgxw9wlcswnlwdhg4xhx38yxpd5 + const auto toAddressData = parse_hex("bffe47abfaede50419c577f1074fee6dd1535cd1"); + + Binance::Proto::SigningInput txInput; + + txInput.set_chain_id("Binance-Chain-Nile"); + auto& sendOrder = *txInput.mutable_send_order(); + + auto& input1 = *sendOrder.add_inputs(); + input1.set_address(fromAddressData.data(), fromAddressData.size()); + auto& input1Coin = *input1.add_coins(); + input1Coin.set_amount(1); + input1Coin.set_denom("BNB"); + + auto& output1 = *sendOrder.add_outputs(); + output1.set_address(toAddressData.data(), toAddressData.size()); + auto& output1Coin = *output1.add_coins(); + output1Coin.set_amount(1); + output1Coin.set_denom("BNB"); + + auto txInputData = data(txInput.SerializeAsString()); { // Check, by parsing - EXPECT_EQ((int)TWDataSize(txInputData.get()), 88); + EXPECT_EQ((int)txInputData.size(), 88); Binance::Proto::SigningInput input; - ASSERT_TRUE(input.ParseFromArray(TWDataBytes(txInputData.get()), - (int)TWDataSize(txInputData.get()))); + ASSERT_TRUE(input.ParseFromArray(txInputData.data(), (int)txInputData.size())); EXPECT_EQ(input.chain_id(), "Binance-Chain-Nile"); EXPECT_TRUE(input.has_send_order()); ASSERT_EQ(input.send_order().inputs_size(), 1); @@ -62,7 +75,8 @@ TEST(TWTransactionCompiler, ExternalSignatureSignBinance) { } /// Step 2: Obtain preimage hash - const auto preImageHashes = WRAPD(TWTransactionCompilerPreImageHashes(coin, txInputData.get())); + auto txInputDataPtr = WRAPD(TWDataCreateWithBytes(txInputData.data(), txInputData.size())); + const auto preImageHashes = WRAPD(TWTransactionCompilerPreImageHashes(coin, txInputDataPtr.get())); auto preImageHash = data(TWDataBytes(preImageHashes.get()), TWDataSize(preImageHashes.get())); TxCompiler::Proto::PreSigningOutput preSigningOutput; @@ -86,7 +100,7 @@ TEST(TWTransactionCompiler, ExternalSignatureSignBinance) { /// Step 3: Compile transaction info const auto outputData = WRAPD(TWTransactionCompilerCompileWithSignatures( - coin, txInputData.get(), + coin, txInputDataPtr.get(), WRAP(TWDataVector, TWDataVectorCreateWithData((TWData*)&signature)).get(), WRAP(TWDataVector, TWDataVectorCreateWithData((TWData*)&publicKeyData)).get())); @@ -108,8 +122,8 @@ TEST(TWTransactionCompiler, ExternalSignatureSignBinance) { { // Double check: check if simple signature process gives the same result. Note that private // keys were not used anywhere up to this point. Binance::Proto::SigningInput input; - ASSERT_TRUE(input.ParseFromArray(TWDataBytes(txInputData.get()), - (int)TWDataSize(txInputData.get()))); + ASSERT_TRUE(input.ParseFromArray(TWDataBytes(txInputDataPtr.get()), + (int)TWDataSize(txInputDataPtr.get()))); auto key = parse_hex("95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832"); input.set_private_key(key.data(), key.size()); diff --git a/tools/new-blockchain b/tools/new-blockchain new file mode 100755 index 00000000000..8ccdd0e740b --- /dev/null +++ b/tools/new-blockchain @@ -0,0 +1,11 @@ +#!/bin/bash +# +# This script generates new Blockchain skeleton files. + +pushd codegen-v2 +cargo run -- new-blockchain $1 +popd # codegen-v2 + +pushd codegen +codegen/bin/newcoin-mobile-tests $1 +popd # codegen diff --git a/tools/new-evmchain b/tools/new-evmchain new file mode 100755 index 00000000000..3eb2c89410d --- /dev/null +++ b/tools/new-evmchain @@ -0,0 +1,7 @@ +#!/bin/bash +# +# This script generates new EVM chain skeleton files. + +pushd codegen-v2 +cargo run -- new-evmchain $1 +popd # codegen-v2 From ec24ab00c2b3a43b34ad7d95d88f7db12a99d3e8 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:01:45 +0100 Subject: [PATCH 028/128] [CI/Linux]: Bump libc6 downgrade version (#3600) --- .github/workflows/linux-ci.yml | 3 ++- .github/workflows/linux-sampleapp-ci.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml index 239858760cc..eb9d47d4432 100644 --- a/.github/workflows/linux-ci.yml +++ b/.github/workflows/linux-ci.yml @@ -15,12 +15,13 @@ jobs: runs-on: ubuntu-latest if: github.event.pull_request.draft == false steps: + # Work around https://github.com/actions/runner-images/issues/8659 - name: Remove GCC 13 from runner image shell: bash run: | sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r-ubuntu-test-jammy.list sudo apt-get update - sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.4 libc6-dev=2.35-0ubuntu3.4 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 + sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.5 libc6-dev=2.35-0ubuntu3.5 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 - uses: actions/checkout@v3 - name: Install system dependencies run: | diff --git a/.github/workflows/linux-sampleapp-ci.yml b/.github/workflows/linux-sampleapp-ci.yml index fa44291f2f4..ebed950f55a 100644 --- a/.github/workflows/linux-sampleapp-ci.yml +++ b/.github/workflows/linux-sampleapp-ci.yml @@ -15,12 +15,13 @@ jobs: runs-on: ubuntu-latest if: github.event.pull_request.draft == false steps: + # Work around https://github.com/actions/runner-images/issues/8659 - name: Remove GCC 13 from runner image shell: bash run: | sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r-ubuntu-test-jammy.list sudo apt-get update - sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.4 libc6-dev=2.35-0ubuntu3.4 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 + sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.5 libc6-dev=2.35-0ubuntu3.5 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 - uses: actions/checkout@v3 - name: Install system dependencies run: | From 374da804d4a7c7d868ad2ab66d931b785c3d8e37 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 20 Dec 2023 14:02:14 +0100 Subject: [PATCH 029/128] [Eth/Eip712]: Fix `bytes` hashing (#3613) * [Eth/Eip712]: Fix `bytes` hashing * [KMP]: Fix KMP sample --- .../src/message/eip712/eip712_message.rs | 3 +- .../tests/data/eip712_different_bytes.json | 58 +++++++++++++++++ rust/tw_evm/tests/data/eip712_long_bytes.json | 63 +++++++++++++++++++ rust/tw_evm/tests/message_signer.rs | 24 +++++++ rust/tw_misc/src/lib.rs | 1 + samples/kmp/shared/build.gradle.kts | 2 +- 6 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 rust/tw_evm/tests/data/eip712_different_bytes.json create mode 100644 rust/tw_evm/tests/data/eip712_long_bytes.json diff --git a/rust/tw_evm/src/message/eip712/eip712_message.rs b/rust/tw_evm/src/message/eip712/eip712_message.rs index 9f21b15efaa..d31d01608dc 100644 --- a/rust/tw_evm/src/message/eip712/eip712_message.rs +++ b/rust/tw_evm/src/message/eip712/eip712_message.rs @@ -176,7 +176,8 @@ fn encode_bytes(value: &Json) -> MessageSigningResult { .decode_hex() .map_err(|_| MessageSigningError::InvalidParameterValue)?; let hash = keccak256(&bytes); - Ok(encode_tokens(&[Token::Bytes(hash)])) + let checked_bytes = NonEmptyBytes::new(hash).expect("`hash` must not be empty"); + Ok(encode_tokens(&[Token::FixedBytes(checked_bytes)])) } fn encode_array( diff --git a/rust/tw_evm/tests/data/eip712_different_bytes.json b/rust/tw_evm/tests/data/eip712_different_bytes.json new file mode 100644 index 00000000000..27f166d60a5 --- /dev/null +++ b/rust/tw_evm/tests/data/eip712_different_bytes.json @@ -0,0 +1,58 @@ +{ + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "BytesTest": [ + { + "name": "data_long", + "type": "bytes" + }, + { + "name": "data_short", + "type": "bytes" + }, + { + "name": "fix_data32", + "type": "bytes32" + }, + { + "name": "fix_data32_short", + "type": "bytes32" + }, + { + "name": "fix_data16", + "type": "bytes16" + }, + { + "name": "fix_data16_short", + "type": "bytes16" + } + ] + }, + "domain": { + "name": "Test", + "version": "0.0.0", + "verifyingContract": "0xb8d6eb2c8236ba373b40adfad4decb8e05ac9230" + }, + "primaryType": "BytesTest", + "message": { + "data_long": "0xcb5a7173d109f8a59c23109189056c862d843808d00bac069e9b76ca7217d845a94d0ad600000000000000000000000000000000000000000000000000000000000000a07c1a7062fbf633885ae02e0919ec07020a96aa20bf9c79554d2ddcd013113c64579837cd54cb5f05c65c46c04fac9abe54f839a74fa3cc1c47fc9048db037a84000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d61414e35575832525851385852374b5158475a6f726931517461597972356e3547537471456a71706a4e71640000000000000000000000", + "data_short": "0xcb5a71", + "fix_data32": "0xcb5a7173d109f8a59c23109189056c862d843808d00bac069e9b76ca7217d845", + "fix_data32_short": "0xcb5a71", + "fix_data16": "0xcb5a7173d109f8a59c23109189056c86", + "fix_data16_short": "0xcb5a71" + } +} \ No newline at end of file diff --git a/rust/tw_evm/tests/data/eip712_long_bytes.json b/rust/tw_evm/tests/data/eip712_long_bytes.json new file mode 100644 index 00000000000..c39d82ac077 --- /dev/null +++ b/rust/tw_evm/tests/data/eip712_long_bytes.json @@ -0,0 +1,63 @@ +{ + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "ForwardRequest": [ + { + "name": "info", + "type": "string" + }, + { + "name": "from", + "type": "address" + }, + { + "name": "to", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "name": "gas", + "type": "uint256" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "data", + "type": "bytes" + } + ] + }, + "domain": { + "name": "MinimalForwarder", + "version": "0.0.2", + "verifyingContract": "0x65CDf66C6FDDCD0a43042F237Aa871414b724f4d" + }, + "primaryType": "ForwardRequest", + "message": { + "info": "Please sign this message, so OwnerChip can send this transaction on your behalf.", + "from": "0xff7abfb4ad52ba6ec9af37955365a7691c8167b9", + "to": "0xb8d6eb2c8236ba373b40adfad4decb8e05ac9230", + "value": 0, + "gas": 500000, + "nonce": 228, + "data": "0xcb5a7173d109f8a59c23109189056c862d843808d00bac069e9b76ca7217d845a94d0ad600000000000000000000000000000000000000000000000000000000000000a07c1a7062fbf633885ae02e0919ec07020a96aa20bf9c79554d2ddcd013113c64579837cd54cb5f05c65c46c04fac9abe54f839a74fa3cc1c47fc9048db037a84000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d61414e35575832525851385852374b5158475a6f726931517461597972356e3547537471456a71706a4e71640000000000000000000000" + } +} \ No newline at end of file diff --git a/rust/tw_evm/tests/message_signer.rs b/rust/tw_evm/tests/message_signer.rs index 2f3b8327d92..fe5acaca9aa 100644 --- a/rust/tw_evm/tests/message_signer.rs +++ b/rust/tw_evm/tests/message_signer.rs @@ -20,6 +20,8 @@ const EIP712_UNEQUAL_ARRAY_LEN: &str = include_str!("data/eip712_unequal_array_l const EIP712_WITH_CHAIN_ID_STR: &str = include_str!("data/eip712_with_chain_id_string.json"); const EIP712_GREENFIELD: &str = include_str!("data/eip712_greenfield.json"); const EIP712_FIXED_BYTES: &str = include_str!("data/eip712_fixed_bytes.json"); +const EIP712_LONG_BYTES: &str = include_str!("data/eip712_long_bytes.json"); +const EIP712_DIFFERENT_BYTES: &str = include_str!("data/eip712_different_bytes.json"); struct SignVerifyTestInput { private_key: &'static str, @@ -277,3 +279,25 @@ fn test_message_signer_sign_verify_eip712_fixed_bytes() { signature: "7ee9b54fedf355e40fa86bbe23e63b318ef797bd8fdbc5bb714edbace042d4cb60111912218234e856f2cf300b3b47c91383b98e263ecf69c6c10193fef6c9581b", }); } + +#[test] +fn test_message_signer_sign_verify_eip712_long_bytes() { + test_message_signer_sign_verify(SignVerifyTestInput { + private_key: "6f96f3aa7e8052170f1864f72a9a53606ee9c0d185188266cab895512a4bcf84", + msg: EIP712_LONG_BYTES, + msg_type: Proto::MessageType::MessageType_typed, + chain_id: None, + signature: "3f78f5860dc9c38d3bf68fd0759c0e4963f104ba6c7fa44e915ed41a1575dbd50d6fd946919e6cfa7eecb869a5d90658b16b1d7b79ec6380acd1841fc21c77f71c", + }); +} + +#[test] +fn test_message_signer_sign_verify_eip712_different_bytes() { + test_message_signer_sign_verify(SignVerifyTestInput { + private_key: "6f96f3aa7e8052170f1864f72a9a53606ee9c0d185188266cab895512a4bcf84", + msg: EIP712_DIFFERENT_BYTES, + msg_type: Proto::MessageType::MessageType_typed, + chain_id: None, + signature: "48dc667cd8a53beb58ea6b1745f98c21b12e1a57587ce28bae07689dba3600d40cef2685dc8a68028d38f3e63289891868ecdf05e8affc275fee3001e51d6c581c", + }); +} diff --git a/rust/tw_misc/src/lib.rs b/rust/tw_misc/src/lib.rs index 80fa96cffc9..b99e9f66996 100644 --- a/rust/tw_misc/src/lib.rs +++ b/rust/tw_misc/src/lib.rs @@ -5,6 +5,7 @@ // file LICENSE at the root of the source code distribution tree. pub mod macros; +#[cfg(feature = "serde")] pub mod serde; #[cfg(feature = "test-utils")] pub mod test_utils; diff --git a/samples/kmp/shared/build.gradle.kts b/samples/kmp/shared/build.gradle.kts index d7ba445c535..c637d0306de 100644 --- a/samples/kmp/shared/build.gradle.kts +++ b/samples/kmp/shared/build.gradle.kts @@ -35,7 +35,7 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation("com.trustwallet:wallet-core-kotlin:4.0.10") + implementation("com.trustwallet:wallet-core-kotlin:4.0.13") } } val commonTest by getting { From 949b0c3357f2465fd4ecd608d14f5dc1841c8305 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 20 Dec 2023 16:37:18 +0100 Subject: [PATCH 030/128] feat(Manta): Add support for Manta Pacific Mainnet (#3617) * feat(manta): Add support for Manta Pacific Mainnet * feat(manta): Fix iOS, Android, Kotlin tests * feat(manta): Fix some tests, codegen-v2 --- .../blockchains/CoinAddressDerivationTests.kt | 5 ++- .../codegen/cpp/templates/TWCoinTypeTests.cpp | 2 +- codegen-v2/src/codegen/template_generator.rs | 1 + docs/registry.md | 1 + include/TrustWalletCore/TWCoinType.h | 1 + .../core/test/CoinAddressDerivationTests.kt | 2 +- registry.json | 30 ++++++++++++++++++ .../tests/coin_address_derivation_test.rs | 1 + swift/Tests/CoinAddressDerivationTests.swift | 3 +- tests/chains/MantaPacific/TWCoinTypeTests.cpp | 31 +++++++++++++++++++ tests/common/CoinAddressDerivationTests.cpp | 1 + 11 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 tests/chains/MantaPacific/TWCoinTypeTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index 7e44af34e2e..1436bb783cf 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -42,10 +42,13 @@ class CoinAddressDerivationTests { CALLISTO -> assertEquals("0x3E6FFC80745E6669135a76F4A7ce6BCF02436e04", address) DASH -> assertEquals("XqHiz8EXYbTAtBEYs4pWTHh7ipEDQcNQeT", address) DIGIBYTE -> assertEquals("dgb1qtjgmerfqwdffyf8ghcrkgy52cghsqptynmyswu", address) + ETHEREUM, SMARTCHAIN, POLYGON, OPTIMISM, ZKSYNC, ARBITRUM, ARBITRUMNOVA, ECOCHAIN, AVALANCHECCHAIN, XDAI, FANTOM, CELO, CRONOSCHAIN, SMARTBITCOINCASH, KUCOINCOMMUNITYCHAIN, BOBA, METIS, AURORA, EVMOS, MOONRIVER, MOONBEAM, KAVAEVM, KLAYTN, METER, OKXCHAIN, POLYGONZKEVM, SCROLL, - CONFLUXESPACE, ACALAEVM, OPBNB, NEON, BASE, LINEA, GREENFIELD, MANTLE, ZENEON -> assertEquals("0x8f348F300873Fd5DA36950B2aC75a26584584feE", address) + CONFLUXESPACE, ACALAEVM, OPBNB, NEON, BASE, LINEA, GREENFIELD, MANTLE, ZENEON, MANTAPACIFIC, + -> assertEquals("0x8f348F300873Fd5DA36950B2aC75a26584584feE", address) + RONIN -> assertEquals("ronin:8f348F300873Fd5DA36950B2aC75a26584584feE", address) ETHEREUMCLASSIC -> assertEquals("0x078bA3228F3E6C08bEEac9A005de0b7e7089aD1c", address) GOCHAIN -> assertEquals("0x5940ce4A14210d4Ccd0ac206CE92F21828016aC2", address) diff --git a/codegen-v2/src/codegen/cpp/templates/TWCoinTypeTests.cpp b/codegen-v2/src/codegen/cpp/templates/TWCoinTypeTests.cpp index 83124519c83..59a9d1a2e3f 100644 --- a/codegen-v2/src/codegen/cpp/templates/TWCoinTypeTests.cpp +++ b/codegen-v2/src/codegen/cpp/templates/TWCoinTypeTests.cpp @@ -19,7 +19,7 @@ TEST(TW{COIN_TYPE}CoinType, TWCoinType) { const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); assertStringsEqual(id, "{COIN_ID}"); - assertStringsEqual(name, "{COIN_TYPE}"); + assertStringsEqual(name, "{COIN_NAME}"); assertStringsEqual(symbol, "{SYMBOL}"); ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), {DECIMALS}); ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchain{BLOCKCHAIN}); diff --git a/codegen-v2/src/codegen/template_generator.rs b/codegen-v2/src/codegen/template_generator.rs index 39f1780f1f1..add3da7fcb7 100644 --- a/codegen-v2/src/codegen/template_generator.rs +++ b/codegen-v2/src/codegen/template_generator.rs @@ -40,6 +40,7 @@ impl TemplateGenerator { .add_pattern("{TW_CRATE_NAME}", coin.id.to_tw_crate_name()) .add_pattern("{COIN_ID}", coin.id.as_str()) .add_pattern("{COIN_TYPE}", coin.coin_type()) + .add_pattern("{COIN_NAME}", &coin.name) .add_pattern("{SYMBOL}", &coin.symbol) .add_pattern("{DECIMALS}", coin.decimals) .add_pattern("{P2PKH_PREFIX}", coin.p2pkh_prefix) diff --git a/docs/registry.md b/docs/registry.md index b49763ff960..490965bc8b1 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -31,6 +31,7 @@ This list is generated from [./registry.json](../registry.json) | 148 | Stellar | XLM | | | | 156 | Bitcoin Gold | BTG | | | | 165 | Nano | XNO | | | +| 169 | Manta Pacific | ETH | | | | 175 | Ravencoin | RVN | | | | 178 | POA Network | POA | | | | 194 | EOS | EOS | | | diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 6a09ac2f74f..9aeca0a9953 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -179,6 +179,7 @@ enum TWCoinType { TWCoinTypeZenEON = 7332, TWCoinTypeInternetComputer = 223, TWCoinTypeTia = 21000118, + TWCoinTypeMantaPacific = 169, // end_of_tw_coin_type_marker_do_not_modify }; diff --git a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt index cec9314a3be..db177dc1b16 100644 --- a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt +++ b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt @@ -39,7 +39,7 @@ class CoinAddressDerivationTests { Ethereum, SmartChain, Polygon, Optimism, Zksync, Arbitrum, ArbitrumNova, ECOChain, AvalancheCChain, XDai, Fantom, Celo, CronosChain, SmartBitcoinCash, KuCoinCommunityChain, Boba, Metis, Aurora, Evmos, Moonriver, Moonbeam, KavaEvm, Klaytn, Meter, OKXChain, PolygonzkEVM, Scroll, - ConfluxeSpace, AcalaEVM, OpBNB, Neon, Base, Linea, Greenfield, Mantle, ZenEON, + ConfluxeSpace, AcalaEVM, OpBNB, Neon, Base, Linea, Greenfield, Mantle, ZenEON, MantaPacific, -> "0x8f348F300873Fd5DA36950B2aC75a26584584feE" Ronin -> "ronin:8f348F300873Fd5DA36950B2aC75a26584584feE" diff --git a/registry.json b/registry.json index d78c60aae73..ce1fc2fbba5 100644 --- a/registry.json +++ b/registry.json @@ -4508,5 +4508,35 @@ "rpc": "", "documentation": "https://internetcomputer.org/docs" } + }, + { + "id": "manta", + "name": "Manta Pacific", + "coinId": 169, + "symbol": "ETH", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "169", + "addressHasher": "keccak256", + "explorer": { + "url": "https://pacific-explorer.manta.network", + "txPath": "/tx/", + "accountPath": "/address/", + "sampleTx": "0x2bbd5d85b0ed05d1416e30ce1197a6f0c27d10ce02593a2719e2baf486d2e8c2", + "sampleAccount": "0xF122a1aC569a36a5Cf6d0F828A22254c8A9afF84" + }, + "info": { + "url": "https://pacific.manta.network", + "source": "https://github.com/manta-network", + "rpc": "https://pacific-rpc.manta.network/http", + "documentation": "https://docs.manta.network/docs/Introduction" + } } ] diff --git a/rust/tw_any_coin/tests/coin_address_derivation_test.rs b/rust/tw_any_coin/tests/coin_address_derivation_test.rs index 260243fa0ea..58f522939c9 100644 --- a/rust/tw_any_coin/tests/coin_address_derivation_test.rs +++ b/rust/tw_any_coin/tests/coin_address_derivation_test.rs @@ -86,6 +86,7 @@ fn test_coin_address_derivation() { | CoinType::Greenfield | CoinType::Mantle | CoinType::ZenEON + | CoinType::MantaPacific // end_of_evm_address_derivation_tests_marker_do_not_modify => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", CoinType::Bitcoin diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index fdc68d10b43..00e6aecd5f0 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -114,7 +114,8 @@ class CoinAddressDerivationTests: XCTestCase { .linea, .greenfield, .mantle, - .zenEON: + .zenEON, + .mantaPacific: let expectedResult = "0x8f348F300873Fd5DA36950B2aC75a26584584feE" assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .ronin: diff --git a/tests/chains/MantaPacific/TWCoinTypeTests.cpp b/tests/chains/MantaPacific/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..4f053c7fc01 --- /dev/null +++ b/tests/chains/MantaPacific/TWCoinTypeTests.cpp @@ -0,0 +1,31 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" +#include +#include + +TEST(TWMantaPacificCoinType, TWCoinType) { + const auto coin = TWCoinTypeMantaPacific; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x2bbd5d85b0ed05d1416e30ce1197a6f0c27d10ce02593a2719e2baf486d2e8c2")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xF122a1aC569a36a5Cf6d0F828A22254c8A9afF84")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "manta"); + assertStringsEqual(name, "Manta Pacific"); + assertStringsEqual(symbol, "ETH"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainEthereum); + ASSERT_EQ(TWCoinTypeP2pkhPrefix(coin), 0); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0); + assertStringsEqual(txUrl, "https://pacific-explorer.manta.network/tx/0x2bbd5d85b0ed05d1416e30ce1197a6f0c27d10ce02593a2719e2baf486d2e8c2"); + assertStringsEqual(accUrl, "https://pacific-explorer.manta.network/address/0xF122a1aC569a36a5Cf6d0F828A22254c8A9afF84"); +} diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index da1bd05e983..059290d6c46 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -84,6 +84,7 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeGreenfield: case TWCoinTypeMantle: case TWCoinTypeZenEON: + case TWCoinTypeMantaPacific: // end_of_evm_address_derivation_tests_marker_do_not_modify EXPECT_EQ(address, "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); break; From 1d0031817822e3e130a6a11dfc4be9a3d9015229 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Thu, 21 Dec 2023 12:09:37 +0100 Subject: [PATCH 031/128] [Kotlin]: Update Kotlin yarn.lock (#3598) * [Kotlin]: Update Kotlin yarn.lock * [Kotlin]: Update Kotlin yarn.lock with node@21 * [Kotlin]: Upgrade Webpack to 5.89.0 * [CI] Trigger CI * [CI/KMP]: Bump version to 4.0.14 * [CI/iOS]: Use 16.4 iOS runtime --- kotlin/build-logic/build.gradle.kts | 4 + kotlin/kotlin-js-store/yarn.lock | 985 ++++++++++++++------- kotlin/wallet-core-kotlin/build.gradle.kts | 4 + samples/kmp/shared/build.gradle.kts | 2 +- tools/ios-test | 2 +- 5 files changed, 662 insertions(+), 335 deletions(-) diff --git a/kotlin/build-logic/build.gradle.kts b/kotlin/build-logic/build.gradle.kts index 83e8afc4707..1f41e03775b 100644 --- a/kotlin/build-logic/build.gradle.kts +++ b/kotlin/build-logic/build.gradle.kts @@ -6,6 +6,10 @@ plugins { } allprojects { + tasks.withType { + sourceCompatibility = "17" + targetCompatibility = "17" + } tasks.withType { compilerOptions { allWarningsAsErrors.set(true) diff --git a/kotlin/kotlin-js-store/yarn.lock b/kotlin/kotlin-js-store/yarn.lock index f2fd767d23d..0995b488885 100644 --- a/kotlin/kotlin-js-store/yarn.lock +++ b/kotlin/kotlin-js-store/yarn.lock @@ -2,6 +2,28 @@ # yarn lockfile v1 +"@babel/code-frame@^7.10.4": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" + integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== + dependencies: + "@babel/highlight" "^7.23.4" + chalk "^2.4.2" + +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + +"@babel/highlight@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" + integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -13,44 +35,86 @@ integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== "@jridgewell/gen-mapping@^0.3.0": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== dependencies: "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/source-map@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" - integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" + integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== dependencies: "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - -"@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.17" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" - integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.20" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" + integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@rollup/plugin-commonjs@^21.0.1": + version "21.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-21.1.0.tgz#45576d7b47609af2db87f55a6d4b46e44fc3a553" + integrity sha512-6ZtHx3VHIp2ReNNDxHjuUml6ur+WcQ28N1yHgCQwsbNkQg2suhxGMDQGJOn/KuDxKtd1xuZP5xSTwBA4GQ8hbA== + dependencies: + "@rollup/pluginutils" "^3.1.0" + commondir "^1.0.1" + estree-walker "^2.0.1" + glob "^7.1.6" + is-reference "^1.2.1" + magic-string "^0.25.7" + resolve "^1.17.0" + +"@rollup/plugin-node-resolve@^13.1.3": + version "13.3.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz#da1c5c5ce8316cef96a2f823d111c1e4e498801c" + integrity sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw== + dependencies: + "@rollup/pluginutils" "^3.1.0" + "@types/resolve" "1.17.1" + deepmerge "^4.2.2" + is-builtin-module "^3.1.0" + is-module "^1.0.0" + resolve "^1.19.0" + +"@rollup/plugin-typescript@^8.3.0": + version "8.5.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.5.0.tgz#7ea11599a15b0a30fa7ea69ce3b791d41b862515" + integrity sha512-wMv1/scv0m/rXx21wD2IsBbJFba8wGF3ErJIr6IKRfRj49S85Lszbxb4DCo8iILpluTjk2GAAu9CoZt4G3ppgQ== + dependencies: + "@rollup/pluginutils" "^3.1.0" + resolve "^1.17.0" + +"@rollup/pluginutils@^3.0.9", "@rollup/pluginutils@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" + integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" + "@types/estree" "0.0.39" + estree-walker "^1.0.1" + picomatch "^2.2.2" "@socket.io/component-emitter@~3.1.0": version "3.1.0" @@ -63,172 +127,186 @@ integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== "@types/cors@^2.8.12": - version "2.8.13" - resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.13.tgz#b8ade22ba455a1b8cb3b5d3f35910fd204f84f94" - integrity sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA== + version "2.8.17" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.17.tgz#5d718a5e494a8166f569d986794e49c48b216b2b" + integrity sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA== dependencies: "@types/node" "*" "@types/eslint-scope@^3.7.3": - version "3.7.4" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" - integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== dependencies: "@types/eslint" "*" "@types/estree" "*" "@types/eslint@*": - version "8.4.10" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.10.tgz#19731b9685c19ed1552da7052b6f668ed7eb64bb" - integrity sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw== + version "8.44.9" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.9.tgz#5799663009645637bd1c45b2e1a7c8f4caf89534" + integrity sha512-6yBxcvwnnYoYT1Uk2d+jvIfsuP4mb2EdIxFnrPABj5a/838qe5bGkNLFOiipX4ULQ7XVQvTxOh7jO+BTAiqsEw== dependencies: "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" - integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== +"@types/estree@*", "@types/estree@^1.0.0": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== -"@types/estree@^0.0.51": - version "0.0.51" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" - integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== +"@types/estree@0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" + integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== "@types/json-schema@*", "@types/json-schema@^7.0.8": - version "7.0.11" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" - integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/node@*", "@types/node@>=10.0.0": - version "18.11.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" - integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== + version "20.10.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.4.tgz#b246fd84d55d5b1b71bf51f964bd514409347198" + integrity sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg== + dependencies: + undici-types "~5.26.4" + +"@types/node@^12.12.14": + version "12.20.55" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" + integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== + +"@types/resolve@1.17.1": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" + integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== + dependencies: + "@types/node" "*" "@ungap/promise-all-settled@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== -"@webassemblyjs/ast@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" - integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== +"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" + integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== dependencies: - "@webassemblyjs/helper-numbers" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" -"@webassemblyjs/floating-point-hex-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" - integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" + integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== -"@webassemblyjs/helper-api-error@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" - integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" + integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== -"@webassemblyjs/helper-buffer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" - integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== +"@webassemblyjs/helper-buffer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" + integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== -"@webassemblyjs/helper-numbers@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" - integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" + integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== dependencies: - "@webassemblyjs/floating-point-hex-parser" "1.11.1" - "@webassemblyjs/helper-api-error" "1.11.1" + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" "@xtuc/long" "4.2.2" -"@webassemblyjs/helper-wasm-bytecode@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" - integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" + integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== -"@webassemblyjs/helper-wasm-section@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" - integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== +"@webassemblyjs/helper-wasm-section@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" + integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" -"@webassemblyjs/ieee754@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" - integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" + integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" - integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" + integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== dependencies: "@xtuc/long" "4.2.2" -"@webassemblyjs/utf8@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" - integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== - -"@webassemblyjs/wasm-edit@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" - integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/helper-wasm-section" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" - "@webassemblyjs/wasm-opt" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - "@webassemblyjs/wast-printer" "1.11.1" - -"@webassemblyjs/wasm-gen@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" - integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/ieee754" "1.11.1" - "@webassemblyjs/leb128" "1.11.1" - "@webassemblyjs/utf8" "1.11.1" - -"@webassemblyjs/wasm-opt@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" - integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-buffer" "1.11.1" - "@webassemblyjs/wasm-gen" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - -"@webassemblyjs/wasm-parser@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" - integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== - dependencies: - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/helper-api-error" "1.11.1" - "@webassemblyjs/helper-wasm-bytecode" "1.11.1" - "@webassemblyjs/ieee754" "1.11.1" - "@webassemblyjs/leb128" "1.11.1" - "@webassemblyjs/utf8" "1.11.1" - -"@webassemblyjs/wast-printer@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" - integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== - dependencies: - "@webassemblyjs/ast" "1.11.1" +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" + integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== + +"@webassemblyjs/wasm-edit@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" + integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-opt" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + "@webassemblyjs/wast-printer" "1.11.6" + +"@webassemblyjs/wasm-gen@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" + integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" + integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + +"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" + integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" + integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== + dependencies: + "@webassemblyjs/ast" "1.11.6" "@xtuc/long" "4.2.2" "@webpack-cli/configtest@^1.2.0": @@ -271,15 +349,15 @@ accepts@~1.3.4: mime-types "~2.1.34" negotiator "0.6.3" -acorn-import-assertions@^1.7.6: - version "1.8.0" - resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" - integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== -acorn@^8.5.0, acorn@^8.7.1: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== +acorn@^8.7.1, acorn@^8.8.2: + version "8.11.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b" + integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w== ajv-keywords@^3.5.2: version "3.5.2" @@ -306,6 +384,13 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" @@ -326,6 +411,11 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -342,12 +432,12 @@ binary-extensions@^2.0.0: integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== body-parser@^1.19.0: - version "1.20.1" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" - integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== + version "1.20.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" + integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== dependencies: bytes "3.1.2" - content-type "~1.0.4" + content-type "~1.0.5" debug "2.6.9" depd "2.0.0" destroy "1.2.0" @@ -355,7 +445,7 @@ body-parser@^1.19.0: iconv-lite "0.4.24" on-finished "2.4.1" qs "6.11.0" - raw-body "2.5.1" + raw-body "2.5.2" type-is "~1.6.18" unpipe "1.0.0" @@ -387,42 +477,57 @@ browser-stdout@1.3.1: integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== browserslist@^4.14.5: - version "4.21.5" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" - integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== + version "4.22.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.2.tgz#704c4943072bd81ea18997f3bd2180e89c77874b" + integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A== dependencies: - caniuse-lite "^1.0.30001449" - electron-to-chromium "^1.4.284" - node-releases "^2.0.8" - update-browserslist-db "^1.0.10" + caniuse-lite "^1.0.30001565" + electron-to-chromium "^1.4.601" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +builtin-modules@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" + integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== + bytes@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== call-bind@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" camelcase@^6.0.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001449: - version "1.0.30001449" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001449.tgz#a8d11f6a814c75c9ce9d851dc53eb1d1dfbcd657" - integrity sha512-CPB+UL9XMT/Av+pJxCKGhdx+yg1hzplvFJQlJ2n68PyQGMz9L/E2zCyLdOL8uasbouTUgnPl+y0tccI/se+BEw== +caniuse-lite@^1.0.30001565: + version "1.0.30001570" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz#b4e5c1fa786f733ab78fc70f592df6b3f23244ca" + integrity sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw== + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" chalk@^4.1.0: version "4.1.2" @@ -470,6 +575,13 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -477,15 +589,20 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== colorette@^2.0.14: - version "2.0.19" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" - integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== commander@^2.20.0: version "2.20.3" @@ -497,6 +614,11 @@ commander@^7.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -512,7 +634,7 @@ connect@^3.7.0: parseurl "~1.3.3" utils-merge "1.0.1" -content-type@~1.0.4: +content-type@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== @@ -568,6 +690,25 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== +decode-uri-component@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" @@ -603,10 +744,10 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -electron-to-chromium@^1.4.284: - version "1.4.284" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" - integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== +electron-to-chromium@^1.4.601: + version "1.4.612" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.612.tgz#350c6fd4201d677307519b931949fa64dae6a5cc" + integrity sha512-dM8BMtXtlH237ecSMnYdYuCkib2QHq0kpWfUnavjdYsyr/6OsAwg5ZGUfnQ9KD1Ga4QgB2sqXlB2NT8zy2GnVg== emoji-regex@^8.0.0: version "8.0.0" @@ -618,15 +759,15 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== -engine.io-parser@~5.0.3: - version "5.0.6" - resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.6.tgz#7811244af173e157295dec9b2718dfe42a64ef45" - integrity sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw== +engine.io-parser@~5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.1.tgz#9f213c77512ff1a6cc0c7a86108a7ffceb16fcfb" + integrity sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ== -engine.io@~6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.2.1.tgz#e3f7826ebc4140db9bbaa9021ad6b1efb175878f" - integrity sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA== +engine.io@~6.5.2: + version "6.5.4" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.5.4.tgz#6822debf324e781add2254e912f8568508850cdc" + integrity sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg== dependencies: "@types/cookie" "^0.4.1" "@types/cors" "^2.8.12" @@ -636,13 +777,13 @@ engine.io@~6.2.1: cookie "~0.4.1" cors "~2.8.5" debug "~4.3.1" - engine.io-parser "~5.0.3" - ws "~8.2.3" + engine.io-parser "~5.2.1" + ws "~8.11.0" -enhanced-resolve@^5.10.0: - version "5.12.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" - integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== +enhanced-resolve@^5.15.0: + version "5.15.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" + integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -653,14 +794,14 @@ ent@~2.2.0: integrity sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA== envinfo@^7.7.3: - version "7.8.1" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" - integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== + version "7.11.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.0.tgz#c3793f44284a55ff8c82faf1ffd91bc6478ea01f" + integrity sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg== -es-module-lexer@^0.9.0: - version "0.9.3" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" - integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== +es-module-lexer@^1.2.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.4.1.tgz#41ea21b43908fe6a287ffcbe4300f790555331f5" + integrity sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w== escalade@^3.1.1: version "3.1.1" @@ -677,6 +818,11 @@ escape-string-regexp@4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + eslint-scope@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -702,6 +848,16 @@ estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== +estree-walker@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" + integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== + +estree-walker@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + eventemitter3@^4.0.0: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -774,16 +930,16 @@ flat@^5.0.2: integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== flatted@^3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" - integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== + version "3.2.9" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== follow-redirects@^1.0.0: - version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + version "1.15.3" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" + integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== -format-util@1.0.5: +format-util@1.0.5, format-util@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg== @@ -803,28 +959,29 @@ fs.realpath@^1.0.0: integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" - integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== dependencies: - function-bind "^1.1.1" - has "^1.0.3" + function-bind "^1.1.2" + has-proto "^1.0.1" has-symbols "^1.0.3" + hasown "^2.0.0" glob-parent@~5.1.2: version "5.1.2" @@ -850,7 +1007,7 @@ glob@7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.3, glob@^7.1.7: +glob@^7.1.3, glob@^7.1.6, glob@^7.1.7: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -862,27 +1019,51 @@ glob@^7.1.3, glob@^7.1.7: once "^1.3.0" path-is-absolute "^1.0.0" +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: - version "4.2.10" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== + dependencies: + get-intrinsic "^1.2.2" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== dependencies: - function-bind "^1.1.1" + function-bind "^1.1.2" he@1.2.0: version "1.2.0" @@ -956,12 +1137,24 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-core-module@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== +is-builtin-module@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" + integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== dependencies: - has "^1.0.3" + builtin-modules "^3.3.0" + +is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== is-extglob@^2.1.1: version "2.1.1" @@ -980,6 +1173,11 @@ is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g== + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -997,11 +1195,25 @@ is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-reference@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" + integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== + dependencies: + "@types/estree" "*" + is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + isbinaryfile@^4.0.8: version "4.0.10" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3" @@ -1017,6 +1229,15 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== +jest-worker@^26.2.1: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + jest-worker@^27.4.5: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" @@ -1026,6 +1247,11 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + js-yaml@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -1148,15 +1374,22 @@ log-symbols@4.1.0: is-unicode-supported "^0.1.0" log4js@^6.4.1: - version "6.7.1" - resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.7.1.tgz#06e12b1ac915dd1067146ffad8215f666f7d2c51" - integrity sha512-lzbd0Eq1HRdWM2abSD7mk6YIVY0AogGJzb/z+lqzRk+8+XJP+M6L1MS5FUSc3jjGru4dbKjEMJmqlsoYYpuivQ== + version "6.9.1" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.9.1.tgz#aba5a3ff4e7872ae34f8b4c533706753709e38b6" + integrity sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g== dependencies: date-format "^4.0.14" debug "^4.3.4" flatted "^3.2.7" rfdc "^1.3.0" - streamroller "^3.1.3" + streamroller "^3.1.5" + +magic-string@^0.25.7: + version "0.25.9" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" + integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== + dependencies: + sourcemap-codec "^1.4.8" media-typer@0.3.0: version "0.3.0" @@ -1200,9 +1433,9 @@ minimatch@^3.0.4, minimatch@^3.1.1: brace-expansion "^1.1.7" minimist@^1.2.3, minimist@^1.2.6: - version "1.2.7" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" - integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== mkdirp@^0.5.5: version "0.5.6" @@ -1269,10 +1502,10 @@ neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -node-releases@^2.0.8: - version "2.0.9" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.9.tgz#fe66405285382b0c4ac6bcfbfbe7e8a510650b4d" - integrity sha512-2xfmOrRkGogbTK9R6Leda0DGiXeY3p2NJpy4+gNCffdUvV6mdEJnaDEic1i3Ec2djAo8jWYoJMR5PB0MSMpxUA== +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" @@ -1285,9 +1518,9 @@ object-assign@^4: integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== object-inspect@^1.9.0: - version "1.12.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" - integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== on-finished@2.4.1: version "2.4.1" @@ -1373,7 +1606,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -1386,9 +1619,9 @@ pkg-dir@^4.2.0: find-up "^4.0.0" punycode@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" - integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== qjobs@^1.2.0: version "1.2.0" @@ -1414,10 +1647,10 @@ range-parser@^1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" - integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== dependencies: bytes "3.1.2" http-errors "2.0.0" @@ -1460,12 +1693,12 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve@^1.9.0: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== +resolve@^1.17.0, resolve@^1.19.0, resolve@^1.9.0: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -1481,6 +1714,31 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" +rollup-plugin-sourcemaps@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.6.3.tgz#bf93913ffe056e414419607f1d02780d7ece84ed" + integrity sha512-paFu+nT1xvuO1tPFYXGe+XnQvg4Hjqv/eIhG8i5EspfYYPBKL57X7iVbfv55aNVASg3dzWvES9dmWsL2KhfByw== + dependencies: + "@rollup/pluginutils" "^3.0.9" + source-map-resolve "^0.6.0" + +rollup-plugin-terser@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz#e8fbba4869981b2dc35ae7e8a502d5c6c04d324d" + integrity sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ== + dependencies: + "@babel/code-frame" "^7.10.4" + jest-worker "^26.2.1" + serialize-javascript "^4.0.0" + terser "^5.0.0" + +rollup@^2.68.0: + version "2.79.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" + integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== + optionalDependencies: + fsevents "~2.3.2" + safe-buffer@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -1491,10 +1749,10 @@ safe-buffer@^5.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -schema-utils@^3.1.0, schema-utils@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" - integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== +schema-utils@^3.1.1, schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== dependencies: "@types/json-schema" "^7.0.8" ajv "^6.12.5" @@ -1507,13 +1765,30 @@ serialize-javascript@6.0.0: dependencies: randombytes "^2.1.0" -serialize-javascript@^6.0.0: +serialize-javascript@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" + +serialize-javascript@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== dependencies: randombytes "^2.1.0" +set-function-length@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" + integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== + dependencies: + define-data-property "^1.1.1" + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" @@ -1547,30 +1822,33 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -socket.io-adapter@~2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz#b50a4a9ecdd00c34d4c8c808224daa1a786152a6" - integrity sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg== +socket.io-adapter@~2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz#5de9477c9182fdc171cd8c8364b9a8894ec75d12" + integrity sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA== + dependencies: + ws "~8.11.0" -socket.io-parser@~4.2.1: - version "4.2.2" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.2.tgz#1dd384019e25b7a3d374877f492ab34f2ad0d206" - integrity sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw== +socket.io-parser@~4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83" + integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew== dependencies: "@socket.io/component-emitter" "~3.1.0" debug "~4.3.1" socket.io@^4.4.1: - version "4.5.4" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.5.4.tgz#a4513f06e87451c17013b8d13fdfaf8da5a86a90" - integrity sha512-m3GC94iK9MfIEeIBfbhJs5BqFibMtkRk8ZpKwG2QwxV0m/eEhPIV4ara6XCF1LWNAus7z58RodiZlAH71U3EhQ== + version "4.7.2" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.7.2.tgz#22557d76c3f3ca48f82e73d68b7add36a22df002" + integrity sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw== dependencies: accepts "~1.3.4" base64id "~2.0.0" + cors "~2.8.5" debug "~4.3.2" - engine.io "~6.2.1" - socket.io-adapter "~2.4.0" - socket.io-parser "~4.2.1" + engine.io "~6.5.2" + socket.io-adapter "~2.5.2" + socket.io-parser "~4.2.4" source-map-js@^1.0.2: version "1.0.2" @@ -1586,6 +1864,14 @@ source-map-loader@4.0.0: iconv-lite "^0.6.3" source-map-js "^1.0.2" +source-map-resolve@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2" + integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -1599,6 +1885,11 @@ source-map@^0.6.0, source-map@^0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +sourcemap-codec@^1.4.8: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" @@ -1609,10 +1900,10 @@ statuses@~1.5.0: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== -streamroller@^3.1.3: - version "3.1.4" - resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.1.4.tgz#844a18e795d39c1089a8216e66a1cf1151271df0" - integrity sha512-Ha1Ccw2/N5C/IF8Do6zgNe8F3jQo8MPBnMBGvX0QjNv/I97BcNRzK6/mzOpZHHK7DjMLTI3c7Xw7Y1KvdChkvw== +streamroller@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.1.5.tgz#1263182329a45def1ffaef58d31b15d13d2ee7ff" + integrity sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw== dependencies: date-format "^4.0.14" debug "^4.3.4" @@ -1646,7 +1937,14 @@ supports-color@8.1.1, supports-color@^8.0.0: dependencies: has-flag "^4.0.0" -supports-color@^7.1.0: +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -1663,24 +1961,24 @@ tapable@^2.1.1, tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -terser-webpack-plugin@^5.1.3: - version "5.3.6" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz#5590aec31aa3c6f771ce1b1acca60639eab3195c" - integrity sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ== +terser-webpack-plugin@^5.3.7: + version "5.3.9" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1" + integrity sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA== dependencies: - "@jridgewell/trace-mapping" "^0.3.14" + "@jridgewell/trace-mapping" "^0.3.17" jest-worker "^27.4.5" schema-utils "^3.1.1" - serialize-javascript "^6.0.0" - terser "^5.14.1" + serialize-javascript "^6.0.1" + terser "^5.16.8" -terser@^5.14.1: - version "5.16.2" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.2.tgz#8f495819439e8b5c150e7530fc434a6e70ea18b2" - integrity sha512-JKuM+KvvWVqT7muHVyrwv7FVRPnmHDwF6XwoIxdbF5Witi0vu99RYpxDexpJndXt3jbZZmmWr2/mQa6HvSNdSg== +terser@^5.0.0, terser@^5.16.8: + version "5.26.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.26.0.tgz#ee9f05d929f4189a9c28a0feb889d96d50126fe1" + integrity sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ== dependencies: - "@jridgewell/source-map" "^0.3.2" - acorn "^8.5.0" + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" commander "^2.20.0" source-map-support "~0.5.20" @@ -1703,6 +2001,11 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +tslib@^2.3.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -1711,10 +2014,25 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +typescript@4.7.4: + version "4.7.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" + integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== + +typescript@^3.7.2: + version "3.9.10" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" + integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== + ua-parser-js@^0.7.30: - version "0.7.33" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.33.tgz#1d04acb4ccef9293df6f70f2c3d22f3030d8b532" - integrity sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw== + version "0.7.37" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.37.tgz#e464e66dac2d33a7a1251d7d7a99d6157ec27832" + integrity sha512-xV8kqRKM+jhMvcHWUKthV9fNebIzrNy//2O9ZwWcfiBFR5f25XVZPLlEajk/sf3Ra15V92isyQqnIEXRDaZWEA== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== universalify@^0.1.0: version "0.1.2" @@ -1726,10 +2044,10 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== -update-browserslist-db@^1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" - integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -1790,11 +2108,12 @@ webpack-merge@^4.1.5: lodash "^4.17.15" webpack-merge@^5.7.3: - version "5.8.0" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" - integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== + version "5.10.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== dependencies: clone-deep "^4.0.1" + flat "^5.0.2" wildcard "^2.0.0" webpack-sources@^3.2.3: @@ -1802,22 +2121,22 @@ webpack-sources@^3.2.3: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@5.74.0: - version "5.74.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.74.0.tgz#02a5dac19a17e0bb47093f2be67c695102a55980" - integrity sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA== +webpack@5.89.0: + version "5.89.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.89.0.tgz#56b8bf9a34356e93a6625770006490bf3a7f32dc" + integrity sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw== dependencies: "@types/eslint-scope" "^3.7.3" - "@types/estree" "^0.0.51" - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/wasm-edit" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" + "@types/estree" "^1.0.0" + "@webassemblyjs/ast" "^1.11.5" + "@webassemblyjs/wasm-edit" "^1.11.5" + "@webassemblyjs/wasm-parser" "^1.11.5" acorn "^8.7.1" - acorn-import-assertions "^1.7.6" + acorn-import-assertions "^1.9.0" browserslist "^4.14.5" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.10.0" - es-module-lexer "^0.9.0" + enhanced-resolve "^5.15.0" + es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" @@ -1826,9 +2145,9 @@ webpack@5.74.0: loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" - schema-utils "^3.1.0" + schema-utils "^3.2.0" tapable "^2.1.1" - terser-webpack-plugin "^5.1.3" + terser-webpack-plugin "^5.3.7" watchpack "^2.4.0" webpack-sources "^3.2.3" @@ -1847,9 +2166,9 @@ which@^2.0.1: isexe "^2.0.0" wildcard@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" - integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== workerpool@6.2.1: version "6.2.1" @@ -1870,10 +2189,10 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -ws@~8.2.3: - version "8.2.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" - integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== +ws@~8.11.0: + version "8.11.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" + integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== y18n@^5.0.5: version "5.0.8" diff --git a/kotlin/wallet-core-kotlin/build.gradle.kts b/kotlin/wallet-core-kotlin/build.gradle.kts index 3bea97954ca..63d879ca7c3 100644 --- a/kotlin/wallet-core-kotlin/build.gradle.kts +++ b/kotlin/wallet-core-kotlin/build.gradle.kts @@ -80,6 +80,10 @@ kotlin { getByName("jsMain") { kotlin.srcDir(projectDir.resolve("src/jsMain/generated")) + + dependencies { + implementation(npm(name = "webpack", version = "5.89.0")) + } } } diff --git a/samples/kmp/shared/build.gradle.kts b/samples/kmp/shared/build.gradle.kts index c637d0306de..34b9cbe9377 100644 --- a/samples/kmp/shared/build.gradle.kts +++ b/samples/kmp/shared/build.gradle.kts @@ -35,7 +35,7 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation("com.trustwallet:wallet-core-kotlin:4.0.13") + implementation("com.trustwallet:wallet-core-kotlin:4.0.14") } } val commonTest by getting { diff --git a/tools/ios-test b/tools/ios-test index fddb7806e87..38014c0723d 100755 --- a/tools/ios-test +++ b/tools/ios-test @@ -11,7 +11,7 @@ xcodegen && pod install xcodebuild -workspace TrustWalletCore.xcworkspace \ -scheme WalletCore \ -sdk iphonesimulator \ - -destination "platform=iOS Simulator,name=iPhone 14" \ + -destination "platform=iOS Simulator,name=iPhone 14,OS=16.4" \ test | xcbeautify popd From d2d66f2c9776532dfccfa9ad149140cfb0e1b788 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Thu, 21 Dec 2023 13:09:44 +0100 Subject: [PATCH 032/128] [Rust/Greenfield]: Move BNB Greenfield to Rust (#3597) * [Greenfield]: Generate Rust skeleton for BNB Greenfield * [Greenfield]: Add `Eip712Preimager` * [Greenfield]: Continue implementing tx signing * Add `TxBuilder` * TODO add `TransferOut` message * [Greenfield]: Small refactoring * [Greenfield]: Add a signer test, fix bugs * [Greenfield]: Add support for TransferOut message * [Greenfield]: Finish implementing `Signer` and `Compiler` * [Greenfield]: Add a signing test, fix Signer * [Greenfield]: Add missing tests, mainnet as well * [Greenfield]: Add compile test * [Greenfield]: Fix TODO * [Greenfield]: Remove C++ implementation * [Greenfield]: Minor changes * [Greenfield]: Move Cosmos protobuf directory to `tw_cosmos_sdk` * [Greenfield]: Add fuzz test * [Greenfield]: Update chainId * [CI] Trigger CI * [Greenfield]: Fix fmt * [Greenfield]: Fix kmp sample --- registry.json | 2 +- rust/Cargo.lock | 19 + rust/Cargo.toml | 1 + rust/chains/tw_greenfield/Cargo.toml | 22 + rust/chains/tw_greenfield/fuzz/.gitignore | 5 + rust/chains/tw_greenfield/fuzz/Cargo.toml | 30 + .../tw_greenfield/fuzz/fuzz_targets/sign.rs | 11 + rust/chains/tw_greenfield/src/address.rs | 56 ++ rust/chains/tw_greenfield/src/compiler.rs | 111 ++++ rust/chains/tw_greenfield/src/context.rs | 23 + rust/chains/tw_greenfield/src/eip712_types.rs | 321 +++++++++++ rust/chains/tw_greenfield/src/entry.rs | 94 +++ rust/chains/tw_greenfield/src/lib.rs | 16 + .../src/modules/eip712_signer.rs | 82 +++ rust/chains/tw_greenfield/src/modules/mod.rs | 8 + .../tw_greenfield/src/modules/tx_builder.rs | 167 ++++++ rust/chains/tw_greenfield/src/public_key.rs | 49 ++ rust/chains/tw_greenfield/src/signature.rs | 42 ++ rust/chains/tw_greenfield/src/signer.rs | 50 ++ .../src/transaction/message/mod.rs | 25 + .../src/transaction/message/send_order.rs | 97 ++++ .../src/transaction/message/transfer_out.rs | 105 ++++ .../transaction/message/type_msg_amount.rs | 25 + .../tw_greenfield/src/transaction/mod.rs | 83 +++ .../tests/data/send_order_eip712.json | 149 +++++ .../tests/data/transfer_out_eip712.json | 147 +++++ .../tw_greenfield/tests/eip712_signer.rs | 109 ++++ .../tests/chains/binance/binance_sign.rs | 20 + .../chains/greenfield/greenfield_address.rs | 48 ++ .../chains/greenfield/greenfield_compile.rs | 78 +++ .../chains/greenfield/greenfield_sign.rs | 250 ++++++++ .../tests/chains/greenfield/mod.rs | 22 + rust/tw_any_coin/tests/chains/mod.rs | 1 + rust/tw_coin_registry/Cargo.toml | 1 + rust/tw_coin_registry/src/blockchain_type.rs | 1 + rust/tw_coin_registry/src/dispatcher.rs | 3 + .../tw_cosmos_sdk}/Protobuf/authz_tx.proto | 0 .../tw_cosmos_sdk}/Protobuf/bank_tx.proto | 0 .../tw_cosmos_sdk}/Protobuf/coin.proto | 0 .../Protobuf/cosmwasm_wasm_v1_tx.proto | 0 .../Protobuf/crypto_multisig.proto | 0 .../Protobuf/crypto_secp256k1_keys.proto | 0 .../Protobuf/distribution_tx.proto | 0 .../Protobuf/ethermint_keys.proto | 0 .../tw_cosmos_sdk}/Protobuf/gov_tx.proto | 0 .../Protobuf/greenfield_ethsecp256k1.proto | 0 .../Protobuf/greenfield_tx.proto | 0 .../ibc_applications_transfer_tx.proto | 0 .../Protobuf/ibc_core_client.proto | 0 .../Protobuf/injective_keys.proto | 0 .../tw_cosmos_sdk}/Protobuf/staking_tx.proto | 0 .../Protobuf/stride_liquid_staking.proto | 0 .../Protobuf/terra_wasm_v1beta1_tx.proto | 0 .../Protobuf/thorchain_bank_tx.proto | 0 .../tw_cosmos_sdk}/Protobuf/tx.proto | 0 .../tw_cosmos_sdk}/Protobuf/tx_signing.proto | 0 rust/tw_cosmos_sdk/build.rs | 7 +- .../modules/serializer/protobuf_serializer.rs | 17 +- rust/tw_cosmos_sdk/src/modules/tx_builder.rs | 22 +- .../message/cosmos_bank_message.rs | 2 +- rust/tw_cosmos_sdk/src/transaction/mod.rs | 2 + .../src/message/eip712/eip712_message.rs | 18 +- .../src/message/eip712/message_types.rs | 55 ++ rust/tw_evm/src/message/eip712/mod.rs | 1 + rust/tw_evm/src/message/eip712/property.rs | 26 +- rust/tw_evm/src/message/signature.rs | 9 +- rust/tw_evm/src/modules/message_signer.rs | 2 +- rust/tw_misc/src/test_utils/json.rs | 7 + rust/tw_misc/src/traits.rs | 14 + rust/tw_proto/src/lib.rs | 4 + src/Cosmos/Protobuf/.clang-tidy | 6 - src/Cosmos/Protobuf/.gitignore | 3 - src/Greenfield/Constants.h | 19 - src/Greenfield/Entry.cpp | 54 -- src/Greenfield/Entry.h | 13 +- src/Greenfield/ProtobufSerialization.cpp | 147 ----- src/Greenfield/ProtobufSerialization.h | 35 -- src/Greenfield/Signer.cpp | 84 --- src/Greenfield/Signer.h | 31 - src/Greenfield/SignerEip712.cpp | 266 --------- src/Greenfield/SignerEip712.h | 49 -- tests/chains/Cosmos/SignerTests.cpp | 20 +- tests/chains/Greenfield/SignerTests.cpp | 541 ------------------ tests/chains/Greenfield/TWCoinTypeTests.cpp | 2 +- .../Greenfield/TransactionCompilerTests.cpp | 9 +- tools/generate-files | 1 - 86 files changed, 2417 insertions(+), 1322 deletions(-) create mode 100644 rust/chains/tw_greenfield/Cargo.toml create mode 100644 rust/chains/tw_greenfield/fuzz/.gitignore create mode 100644 rust/chains/tw_greenfield/fuzz/Cargo.toml create mode 100644 rust/chains/tw_greenfield/fuzz/fuzz_targets/sign.rs create mode 100644 rust/chains/tw_greenfield/src/address.rs create mode 100644 rust/chains/tw_greenfield/src/compiler.rs create mode 100644 rust/chains/tw_greenfield/src/context.rs create mode 100644 rust/chains/tw_greenfield/src/eip712_types.rs create mode 100644 rust/chains/tw_greenfield/src/entry.rs create mode 100644 rust/chains/tw_greenfield/src/lib.rs create mode 100644 rust/chains/tw_greenfield/src/modules/eip712_signer.rs create mode 100644 rust/chains/tw_greenfield/src/modules/mod.rs create mode 100644 rust/chains/tw_greenfield/src/modules/tx_builder.rs create mode 100644 rust/chains/tw_greenfield/src/public_key.rs create mode 100644 rust/chains/tw_greenfield/src/signature.rs create mode 100644 rust/chains/tw_greenfield/src/signer.rs create mode 100644 rust/chains/tw_greenfield/src/transaction/message/mod.rs create mode 100644 rust/chains/tw_greenfield/src/transaction/message/send_order.rs create mode 100644 rust/chains/tw_greenfield/src/transaction/message/transfer_out.rs create mode 100644 rust/chains/tw_greenfield/src/transaction/message/type_msg_amount.rs create mode 100644 rust/chains/tw_greenfield/src/transaction/mod.rs create mode 100644 rust/chains/tw_greenfield/tests/data/send_order_eip712.json create mode 100644 rust/chains/tw_greenfield/tests/data/transfer_out_eip712.json create mode 100644 rust/chains/tw_greenfield/tests/eip712_signer.rs create mode 100644 rust/tw_any_coin/tests/chains/greenfield/greenfield_address.rs create mode 100644 rust/tw_any_coin/tests/chains/greenfield/greenfield_compile.rs create mode 100644 rust/tw_any_coin/tests/chains/greenfield/greenfield_sign.rs create mode 100644 rust/tw_any_coin/tests/chains/greenfield/mod.rs rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/authz_tx.proto (100%) rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/bank_tx.proto (100%) rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/coin.proto (100%) rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/cosmwasm_wasm_v1_tx.proto (100%) rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/crypto_multisig.proto (100%) rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/crypto_secp256k1_keys.proto (100%) rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/distribution_tx.proto (100%) rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/ethermint_keys.proto (100%) rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/gov_tx.proto (100%) rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/greenfield_ethsecp256k1.proto (100%) rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/greenfield_tx.proto (100%) rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/ibc_applications_transfer_tx.proto (100%) rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/ibc_core_client.proto (100%) rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/injective_keys.proto (100%) rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/staking_tx.proto (100%) rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/stride_liquid_staking.proto (100%) rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/terra_wasm_v1beta1_tx.proto (100%) rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/thorchain_bank_tx.proto (100%) rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/tx.proto (100%) rename {src/Cosmos => rust/tw_cosmos_sdk}/Protobuf/tx_signing.proto (100%) create mode 100644 rust/tw_evm/src/message/eip712/message_types.rs delete mode 100644 src/Cosmos/Protobuf/.clang-tidy delete mode 100644 src/Cosmos/Protobuf/.gitignore delete mode 100644 src/Greenfield/Constants.h delete mode 100644 src/Greenfield/Entry.cpp delete mode 100644 src/Greenfield/ProtobufSerialization.cpp delete mode 100644 src/Greenfield/ProtobufSerialization.h delete mode 100644 src/Greenfield/Signer.cpp delete mode 100644 src/Greenfield/Signer.h delete mode 100644 src/Greenfield/SignerEip712.cpp delete mode 100644 src/Greenfield/SignerEip712.h delete mode 100644 tests/chains/Greenfield/SignerTests.cpp diff --git a/registry.json b/registry.json index ce1fc2fbba5..87f01e776f9 100644 --- a/registry.json +++ b/registry.json @@ -4180,7 +4180,7 @@ "coinId": 5600, "symbol": "BNB", "decimals": 18, - "chainId": "9000", + "chainId": "1017", "blockchain": "Greenfield", "derivation": [ { diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 8954202beeb..efbc436e0d4 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1750,6 +1750,7 @@ dependencies = [ "tw_cosmos", "tw_ethereum", "tw_evm", + "tw_greenfield", "tw_hash", "tw_internet_computer", "tw_keypair", @@ -1838,6 +1839,24 @@ dependencies = [ "tw_proto", ] +[[package]] +name = "tw_greenfield" +version = "0.1.0" +dependencies = [ + "serde", + "serde_json", + "tw_coin_entry", + "tw_cosmos_sdk", + "tw_encoding", + "tw_evm", + "tw_hash", + "tw_keypair", + "tw_memory", + "tw_misc", + "tw_number", + "tw_proto", +] + [[package]] name = "tw_hash" version = "0.1.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 6b80ffb89e6..5896e9dcb5e 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -2,6 +2,7 @@ members = [ "chains/tw_binance", "chains/tw_cosmos", + "chains/tw_greenfield", "chains/tw_native_evmos", "chains/tw_native_injective", "chains/tw_thorchain", diff --git a/rust/chains/tw_greenfield/Cargo.toml b/rust/chains/tw_greenfield/Cargo.toml new file mode 100644 index 00000000000..ab4ee6f224f --- /dev/null +++ b/rust/chains/tw_greenfield/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "tw_greenfield" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +tw_coin_entry = { path = "../../tw_coin_entry" } +tw_cosmos_sdk = { path = "../../tw_cosmos_sdk" } +tw_encoding = { path = "../../tw_encoding" } +tw_evm = { path = "../../tw_evm" } +tw_hash = { path = "../../tw_hash" } +tw_keypair = { path = "../../tw_keypair" } +tw_memory = { path = "../../tw_memory" } +tw_misc = { path = "../../tw_misc" } +tw_number = { path = "../../tw_number" } +tw_proto = { path = "../../tw_proto" } + +[dev-dependencies] +tw_coin_entry = { path = "../../tw_coin_entry", features = ["test-utils"] } +tw_misc = { path = "../../tw_misc", features = ["test-utils"] } diff --git a/rust/chains/tw_greenfield/fuzz/.gitignore b/rust/chains/tw_greenfield/fuzz/.gitignore new file mode 100644 index 00000000000..5c404b9583f --- /dev/null +++ b/rust/chains/tw_greenfield/fuzz/.gitignore @@ -0,0 +1,5 @@ +target +corpus +artifacts +coverage +Cargo.lock diff --git a/rust/chains/tw_greenfield/fuzz/Cargo.toml b/rust/chains/tw_greenfield/fuzz/Cargo.toml new file mode 100644 index 00000000000..fbc0f5790cc --- /dev/null +++ b/rust/chains/tw_greenfield/fuzz/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "tw_greenfield-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" +tw_any_coin = { path = "../../../tw_any_coin", features = ["test-utils"] } +tw_coin_registry = { path = "../../../tw_coin_registry" } +tw_proto = { path = "../../../tw_proto", features = ["fuzz"] } + +[dependencies.tw_greenfield] +path = ".." + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[profile.release] +debug = 1 + +[[bin]] +name = "sign" +path = "fuzz_targets/sign.rs" +test = false +doc = false diff --git a/rust/chains/tw_greenfield/fuzz/fuzz_targets/sign.rs b/rust/chains/tw_greenfield/fuzz/fuzz_targets/sign.rs new file mode 100644 index 00000000000..f27808e5e42 --- /dev/null +++ b/rust/chains/tw_greenfield/fuzz/fuzz_targets/sign.rs @@ -0,0 +1,11 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; +use tw_any_coin::test_utils::sign_utils::AnySignerHelper; +use tw_coin_registry::coin_type::CoinType; +use tw_proto::Greenfield::Proto; + +fuzz_target!(|input: Proto::SigningInput<'_>| { + let mut signer = AnySignerHelper::::default(); + let _ = signer.sign(CoinType::Greenfield, input); +}); diff --git a/rust/chains/tw_greenfield/src/address.rs b/rust/chains/tw_greenfield/src/address.rs new file mode 100644 index 00000000000..679da7b4e35 --- /dev/null +++ b/rust/chains/tw_greenfield/src/address.rs @@ -0,0 +1,56 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use serde::Serialize; +use std::fmt; +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::CoinAddress; +use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_cosmos_sdk::address::CosmosAddress; +use tw_evm::address::Address as EthereumAddress; +use tw_keypair::ecdsa::secp256k1; +use tw_memory::Data; + +#[derive(Clone, Serialize)] +pub struct GreenfieldAddress(EthereumAddress); + +impl GreenfieldAddress { + /// Initializes an address with a `secp256k1` public key. + pub fn with_secp256k1_pubkey(pubkey: &secp256k1::PublicKey) -> GreenfieldAddress { + GreenfieldAddress(EthereumAddress::with_secp256k1_pubkey(pubkey)) + } +} + +impl CosmosAddress for GreenfieldAddress { + fn from_str_with_coin(_coin: &dyn CoinContext, addr: &str) -> AddressResult + where + Self: Sized, + { + GreenfieldAddress::from_str(addr) + } +} + +impl CoinAddress for GreenfieldAddress { + #[inline] + fn data(&self) -> Data { + self.0.data() + } +} + +impl FromStr for GreenfieldAddress { + type Err = AddressError; + + fn from_str(s: &str) -> Result { + EthereumAddress::from_str(s).map(GreenfieldAddress) + } +} + +impl fmt::Display for GreenfieldAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} diff --git a/rust/chains/tw_greenfield/src/compiler.rs b/rust/chains/tw_greenfield/src/compiler.rs new file mode 100644 index 00000000000..e1133c119d1 --- /dev/null +++ b/rust/chains/tw_greenfield/src/compiler.rs @@ -0,0 +1,111 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::context::GreenfieldContext; +use crate::modules::eip712_signer::{Eip712Signer, Eip712TxPreimage}; +use crate::modules::tx_builder::TxBuilder; +use crate::public_key::GreenfieldPublicKey; +use crate::signature::GreenfieldSignature; +use std::borrow::Cow; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::common::compile_input::SingleSignaturePubkey; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::signing_output_error; +use tw_cosmos_sdk::modules::broadcast_msg::{BroadcastMode, BroadcastMsg}; +use tw_cosmos_sdk::modules::serializer::json_serializer::JsonSerializer; +use tw_cosmos_sdk::modules::serializer::protobuf_serializer::ProtobufSerializer; +use tw_cosmos_sdk::public_key::CosmosPublicKey; +use tw_misc::traits::ToBytesVec; +use tw_proto::Greenfield::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct GreenfieldCompiler; + +impl GreenfieldCompiler { + /// Please note that [`Proto::SigningInput::public_key`] must be set. + /// If the public key should be derived from a private key, please do it before this method is called. + #[inline] + pub fn preimage_hashes( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> CompilerProto::PreSigningOutput<'static> { + Self::preimage_hashes_impl(coin, input) + .unwrap_or_else(|e| signing_output_error!(CompilerProto::PreSigningOutput, e)) + } + + fn preimage_hashes_impl( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> SigningResult> { + let unsigned = TxBuilder::unsigned_tx_from_proto(coin, &input)?; + let Eip712TxPreimage { eip712_tx, tx_hash } = Eip712Signer::preimage_hash(&unsigned)?; + + Ok(CompilerProto::PreSigningOutput { + data: Cow::from(eip712_tx.to_vec()), + data_hash: Cow::from(tx_hash.to_vec()), + ..CompilerProto::PreSigningOutput::default() + }) + } + + #[inline] + pub fn compile( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Proto::SigningOutput<'static> { + Self::compile_impl(coin, input, signatures, public_keys) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + pub(crate) fn compile_impl( + coin: &dyn CoinContext, + mut input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> SigningResult> { + let SingleSignaturePubkey { + signature: raw_signature, + public_key, + } = SingleSignaturePubkey::from_sign_pubkey_list(signatures, public_keys)?; + + let public_key = GreenfieldPublicKey::from_bytes(coin, &public_key)?; + let signature = GreenfieldSignature::try_from(raw_signature.as_slice())?; + let signature_bytes = signature.to_vec(); + + // Set the public key. It will be used to construct a signer info. + input.public_key = Cow::from(public_key.to_bytes()); + let unsigned = TxBuilder::unsigned_tx_from_proto(coin, &input)?; + + let signed_tx = unsigned.into_signed(signature); + let signed_tx_raw = ProtobufSerializer::::build_signed_tx(&signed_tx)?; + + let broadcast_mode = Self::broadcast_mode(input.mode); + let broadcast_tx = BroadcastMsg::raw(broadcast_mode, &signed_tx_raw).to_json_string(); + + let signature_json = JsonSerializer::::serialize_signature( + &public_key, + signature_bytes.clone(), + ); + let signature_json = serde_json::to_string(&[signature_json]) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + + Ok(Proto::SigningOutput { + signature: Cow::from(signature_bytes), + signature_json: Cow::from(signature_json), + serialized: Cow::from(broadcast_tx), + ..Proto::SigningOutput::default() + }) + } + + fn broadcast_mode(input: Proto::BroadcastMode) -> BroadcastMode { + match input { + Proto::BroadcastMode::SYNC => BroadcastMode::Sync, + Proto::BroadcastMode::ASYNC => BroadcastMode::Async, + } + } +} diff --git a/rust/chains/tw_greenfield/src/context.rs b/rust/chains/tw_greenfield/src/context.rs new file mode 100644 index 00000000000..5bc6bbfcd72 --- /dev/null +++ b/rust/chains/tw_greenfield/src/context.rs @@ -0,0 +1,23 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::GreenfieldAddress; +use crate::public_key::GreenfieldPublicKey; +use tw_cosmos_sdk::context::CosmosContext; +use tw_cosmos_sdk::hasher::keccak256_hasher::Keccak256Hasher; +use tw_cosmos_sdk::private_key::secp256k1::Secp256PrivateKey; +use tw_cosmos_sdk::signature::secp256k1::Secp256k1Signature; + +pub struct GreenfieldContext; + +impl CosmosContext for GreenfieldContext { + type Address = GreenfieldAddress; + /// Greenfield uses EIP712 message signing algorithm built upon `keccak256` hash. + type TxHasher = Keccak256Hasher; + type PrivateKey = Secp256PrivateKey; + type PublicKey = GreenfieldPublicKey; + type Signature = Secp256k1Signature; +} diff --git a/rust/chains/tw_greenfield/src/eip712_types.rs b/rust/chains/tw_greenfield/src/eip712_types.rs new file mode 100644 index 00000000000..6cb4807080f --- /dev/null +++ b/rust/chains/tw_greenfield/src/eip712_types.rs @@ -0,0 +1,321 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::transaction::GreenfieldFee; +use core::fmt; +use serde::{Serialize, Serializer}; +use serde_json::Value as Json; +use std::collections::BTreeMap; +use tw_cosmos_sdk::transaction::message::JsonMessage; +use tw_cosmos_sdk::transaction::Coin; +use tw_evm::abi::param_type::constructor::TypeConstructor; +use tw_evm::message::eip712::message_types::MessageTypesBuilder; +use tw_evm::message::eip712::property::PropertyType; +use tw_number::U256; + +const DOMAIN_NAME: &str = "Greenfield Tx"; +const DOMAIN_VERSION: &str = "1.0.0"; +const DOMAIN_VERIFY_CONTRACT: &str = "greenfield"; +const DOMAIN_SALT: &str = "0"; + +#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct MsgPropertyName(pub usize); + +impl fmt::Display for MsgPropertyName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "msg{}", self.0) + } +} + +pub struct MsgPropertyType(pub usize); + +impl fmt::Display for MsgPropertyType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Msg{}", self.0) + } +} + +impl Serialize for MsgPropertyName { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.to_string().serialize(serializer) + } +} + +/// # EIP712 type +/// +/// ```json +/// { +/// "EIP712Domain": [ +/// { +/// "name": "chainId", +/// "type": "uint256" +/// }, +/// { +/// "name": "name", +/// "type": "string" +/// }, +/// { +/// "name": "salt", +/// "type": "string" +/// }, +/// { +/// "name": "verifyingContract", +/// "type": "string" +/// }, +/// { +/// "name": "version", +/// "type": "string" +/// } +/// ] +/// } +/// ``` +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Eip712Domain { + pub name: String, + pub version: String, + #[serde(serialize_with = "U256::as_decimal_str")] + pub chain_id: U256, + pub verifying_contract: String, + pub salt: String, +} + +impl Eip712Domain { + const TYPE_NAME: &'static str = "EIP712Domain"; + + /// Returns a Serializable data of the `EIP712Domain` type. + /// https://github.com/bnb-chain/greenfield-cosmos-sdk/blob/b48770f5e210b28536f92734b6228913666d4da1/x/auth/tx/eip712.go#L35-L40 + pub fn new(chain_id: U256) -> Eip712Domain { + Eip712Domain { + name: DOMAIN_NAME.to_string(), + version: DOMAIN_VERSION.to_string(), + chain_id, + verifying_contract: DOMAIN_VERIFY_CONTRACT.to_string(), + salt: DOMAIN_SALT.to_string(), + } + } + + pub fn declare_eip712_types(&self, builder: &mut MessageTypesBuilder) { + if let Some(mut domain_builder) = builder.add_custom_type(Self::TYPE_NAME.to_string()) { + domain_builder + .add_property("chainId", PropertyType::Uint) + .add_property("name", PropertyType::String) + .add_property("salt", PropertyType::String) + .add_property("verifyingContract", PropertyType::String) + .add_property("version", PropertyType::String); + } + } +} + +/// # EIP712 type +/// +/// ```json +/// { +/// "Coin": [ +/// { +/// "name": "amount", +/// "type": "uint256" +/// }, +/// { +/// "name": "denom", +/// "type": "string" +/// } +/// ] +/// } +/// ``` +pub struct Eip712Coin; + +impl Eip712Coin { + const TYPE_NAME: &'static str = "Coin"; + + pub fn declare_eip712_types(builder: &mut MessageTypesBuilder) { + if let Some(mut coin_builder) = builder.add_custom_type(Self::TYPE_NAME.to_string()) { + coin_builder + .add_property("amount", PropertyType::Uint) + .add_property("denom", PropertyType::String); + } + } +} + +/// # EIP712 type +/// +/// ```json +/// { +/// "Fee": [ +/// { +/// "name": "amount", +/// "type": "Coin[]" +/// }, +/// { +/// "name": "gas_limit", +/// "type": "uint256" +/// }, +/// { +/// "name": "granter", +/// "type": "string" +/// }, +/// { +/// "name": "payer", +/// "type": "string" +/// } +/// ] +/// } +/// ``` +#[derive(Serialize)] +pub struct Eip712Fee { + pub amount: Vec, + #[serde(serialize_with = "U256::as_decimal_str")] + pub gas_limit: U256, + pub payer: String, + pub granter: String, +} + +impl Eip712Fee { + const TYPE_NAME: &'static str = "Fee"; + + pub fn declare_eip712_types(builder: &mut MessageTypesBuilder) { + // `Tx` depends on `Coin` and `Fee` custom types. + Eip712Coin::declare_eip712_types(builder); + + if let Some(mut fee_builder) = builder.add_custom_type(Self::TYPE_NAME.to_string()) { + let amount_type = PropertyType::Custom(Eip712Coin::TYPE_NAME.to_string()); + fee_builder + .add_property("amount", PropertyType::array(amount_type)) + .add_property("gas_limit", PropertyType::Uint) + .add_property("granter", PropertyType::String) + .add_property("payer", PropertyType::String); + } + } +} + +impl From for Eip712Fee { + fn from(fee: GreenfieldFee) -> Self { + let payer = fee + .payer + .as_ref() + .map(|addr| addr.to_string()) + .unwrap_or_default(); + let granter = fee + .granter + .as_ref() + .map(|addr| addr.to_string()) + .unwrap_or_default(); + + Eip712Fee { + amount: fee.amounts.clone(), + gas_limit: U256::from(fee.gas_limit), + payer, + granter, + } + } +} + +#[derive(Clone, Serialize)] +pub struct Eip712TypedMsg { + #[serde(rename = "type")] + pub msg_type: String, + #[serde(flatten)] + pub value: Json, +} + +impl From for Eip712TypedMsg { + fn from(msg: JsonMessage) -> Self { + Eip712TypedMsg { + msg_type: msg.msg_type, + value: msg.value, + } + } +} + +/// # EIP712 type +/// +/// ```json +/// { +/// "Tx": [ +/// { +/// "name": "account_number", +/// "type": "uint256" +/// }, +/// { +/// "name": "chain_id", +/// "type": "uint256" +/// }, +/// { +/// "name": "fee", +/// "type": "Fee" +/// }, +/// { +/// "name": "memo", +/// "type": "string" +/// }, +/// { +/// "name": "msg1", +/// "type": "Msg1" +/// }, +/// { +/// "name": "sequence", +/// "type": "uint256" +/// }, +/// { +/// "name": "timeout_height", +/// "type": "uint256" +/// } +/// ] +/// } +/// ``` +#[derive(Serialize)] +pub struct Eip712Transaction { + #[serde(serialize_with = "U256::as_decimal_str")] + pub account_number: U256, + #[serde(serialize_with = "U256::as_decimal_str")] + pub chain_id: U256, + pub fee: Eip712Fee, + pub memo: String, + /// Will be flatten as `"msg1": { ... }, "msg2": { ... }`. + #[serde(flatten)] + pub msgs: BTreeMap, + #[serde(serialize_with = "U256::as_decimal_str")] + pub sequence: U256, + #[serde(serialize_with = "U256::as_decimal_str")] + pub timeout_height: U256, +} + +impl Eip712Transaction { + /// cbindgen::ignore + pub const TYPE_NAME: &'static str = "Tx"; + + pub fn declare_eip712_types(&self, builder: &mut MessageTypesBuilder) { + Eip712Fee::declare_eip712_types(builder); + + let Some(mut tx_builder) = builder.add_custom_type(Self::TYPE_NAME.to_string()) else { + return; + }; + + tx_builder + .add_property("account_number", PropertyType::Uint) + .add_property("chain_id", PropertyType::Uint) + .add_property( + "fee", + PropertyType::Custom(Eip712Fee::TYPE_NAME.to_string()), + ) + .add_property("memo", PropertyType::String) + .add_property("sequence", PropertyType::Uint) + .add_property("timeout_height", PropertyType::Uint); + + for (msg_property_name, _msg) in self.msgs.iter() { + let msg_property_type = MsgPropertyType(msg_property_name.0).to_string(); + let msg_property_type = PropertyType::Custom(msg_property_type.to_string()); + + let msg_property_name = msg_property_name.to_string(); + tx_builder.add_property(&msg_property_name, msg_property_type); + } + + tx_builder.sort_by_names(); + } +} diff --git a/rust/chains/tw_greenfield/src/entry.rs b/rust/chains/tw_greenfield/src/entry.rs new file mode 100644 index 00000000000..c78823b9b27 --- /dev/null +++ b/rust/chains/tw_greenfield/src/entry.rs @@ -0,0 +1,94 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::GreenfieldAddress; +use crate::compiler::GreenfieldCompiler; +use crate::signer::GreenfieldSigner; +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::derivation::Derivation; +use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::modules::json_signer::NoJsonSigner; +use tw_coin_entry::modules::message_signer::NoMessageSigner; +use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::prefix::NoPrefix; +use tw_keypair::tw::PublicKey; +use tw_proto::Greenfield::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct GreenfieldEntry; + +impl CoinEntry for GreenfieldEntry { + type AddressPrefix = NoPrefix; + type Address = GreenfieldAddress; + type SigningInput<'a> = Proto::SigningInput<'a>; + type SigningOutput = Proto::SigningOutput<'static>; + type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + + // Optional modules: + type JsonSigner = NoJsonSigner; + type PlanBuilder = NoPlanBuilder; + type MessageSigner = NoMessageSigner; + + #[inline] + fn parse_address( + &self, + _coin: &dyn CoinContext, + address: &str, + _prefix: Option, + ) -> AddressResult { + GreenfieldAddress::from_str(address) + } + + #[inline] + fn parse_address_unchecked( + &self, + _coin: &dyn CoinContext, + address: &str, + ) -> AddressResult { + GreenfieldAddress::from_str(address) + } + + #[inline] + fn derive_address( + &self, + _coin: &dyn CoinContext, + public_key: PublicKey, + _derivation: Derivation, + _prefix: Option, + ) -> AddressResult { + let public_key = public_key + .to_secp256k1() + .ok_or(AddressError::PublicKeyTypeMismatch)?; + Ok(GreenfieldAddress::with_secp256k1_pubkey(public_key)) + } + + #[inline] + fn sign(&self, coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput { + GreenfieldSigner::sign(coin, input) + } + + #[inline] + fn preimage_hashes( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + ) -> Self::PreSigningOutput { + GreenfieldCompiler::preimage_hashes(coin, input) + } + + #[inline] + fn compile( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Self::SigningOutput { + GreenfieldCompiler::compile(coin, input, signatures, public_keys) + } +} diff --git a/rust/chains/tw_greenfield/src/lib.rs b/rust/chains/tw_greenfield/src/lib.rs new file mode 100644 index 00000000000..8c018a0cdba --- /dev/null +++ b/rust/chains/tw_greenfield/src/lib.rs @@ -0,0 +1,16 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod address; +pub mod compiler; +pub mod context; +pub mod eip712_types; +pub mod entry; +pub mod modules; +pub mod public_key; +pub mod signature; +pub mod signer; +pub mod transaction; diff --git a/rust/chains/tw_greenfield/src/modules/eip712_signer.rs b/rust/chains/tw_greenfield/src/modules/eip712_signer.rs new file mode 100644 index 00000000000..a44796c821b --- /dev/null +++ b/rust/chains/tw_greenfield/src/modules/eip712_signer.rs @@ -0,0 +1,82 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::eip712_types::{ + Eip712Domain, Eip712Fee, Eip712Transaction, Eip712TypedMsg, MsgPropertyName, +}; +use crate::transaction::GreenfieldUnsignedTransaction; +use std::collections::BTreeMap; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_evm::message::eip712::eip712_message::Eip712Message; +use tw_evm::message::eip712::message_types::MessageTypesBuilder; +use tw_evm::message::EthMessage; +use tw_hash::H256; +use tw_number::U256; + +pub struct Eip712TxPreimage { + pub eip712_tx: String, + pub tx_hash: H256, +} + +pub struct Eip712Signer; + +impl Eip712Signer { + pub fn preimage_hash( + unsigned: &GreenfieldUnsignedTransaction, + ) -> SigningResult { + // `types_builder` will be used to declare all custom types like `Tx`, `Fee`, `Msg1` etc. + let mut types_builder = MessageTypesBuilder::default(); + + // Step 1: Convert all [`TxBody::messages`] to a map of `msg1: GreenfieldTypedMsg`, `msg2: GreenfieldTypedMsg`. + // at the same time, declare the message custom types. + let mut msgs = BTreeMap::new(); + for (msg_idx, msg) in unsigned.tx_body.messages.iter().enumerate() { + // Index of the transaction messages starts from 1. + let msg_idx = msg_idx + 1; + + let property_name = MsgPropertyName(msg_idx); + let property_value = Eip712TypedMsg::from(msg.to_json()?); + + msgs.insert(property_name, property_value); + + // Declare message custom types like `Msg1`, `TypeMsg1Amount`, etc. + msg.declare_eip712_type(msg_idx, &mut types_builder); + } + + // Step 2: Generate `Tx` and `Domain` objects - the main parts of the EIP712 message. + let tx_to_sign = Eip712Transaction { + account_number: U256::from(unsigned.account_number), + chain_id: unsigned.eth_chain_id, + fee: Eip712Fee::from(unsigned.fee.clone()), + memo: unsigned.tx_body.memo.clone(), + msgs, + sequence: U256::from(unsigned.signer.sequence), + timeout_height: U256::zero(), + }; + let domain = Eip712Domain::new(unsigned.eth_chain_id); + + // Step 3: Declare `Tx`, `Domain` and all types they depend on. + tx_to_sign.declare_eip712_types(&mut types_builder); + domain.declare_eip712_types(&mut types_builder); + + // Step 4: Generate EIP712 message with all declared custom types, `Domain` and `Tx`, + // and compute the EIP712 message hash. + let msg_to_sign = Eip712Message { + types: types_builder.build(), + domain: serde_json::to_value(domain) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?, + primary_type: Eip712Transaction::TYPE_NAME.to_string(), + message: serde_json::to_value(tx_to_sign) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?, + }; + + let tx_hash = msg_to_sign.hash()?; + let eip712_tx = serde_json::to_string(&msg_to_sign) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + + Ok(Eip712TxPreimage { eip712_tx, tx_hash }) + } +} diff --git a/rust/chains/tw_greenfield/src/modules/mod.rs b/rust/chains/tw_greenfield/src/modules/mod.rs new file mode 100644 index 00000000000..4ca8bc01e20 --- /dev/null +++ b/rust/chains/tw_greenfield/src/modules/mod.rs @@ -0,0 +1,8 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod eip712_signer; +pub mod tx_builder; diff --git a/rust/chains/tw_greenfield/src/modules/tx_builder.rs b/rust/chains/tw_greenfield/src/modules/tx_builder.rs new file mode 100644 index 00000000000..78ae1a73811 --- /dev/null +++ b/rust/chains/tw_greenfield/src/modules/tx_builder.rs @@ -0,0 +1,167 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::GreenfieldAddress; +use crate::public_key::GreenfieldPublicKey; +use crate::transaction::message::GreenfieldMessageBox; +use crate::transaction::{ + GreenfieldFee, GreenfieldSignMode, GreenfieldSignerInfo, GreenfieldTxBody, + GreenfieldUnsignedTransaction, +}; +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_cosmos_sdk::public_key::CosmosPublicKey; +use tw_cosmos_sdk::transaction::{Coin, SignerInfo}; +use tw_misc::traits::OptionalEmpty; +use tw_number::U256; +use tw_proto::Greenfield::Proto; + +const DEFAULT_TIMEOUT_HEIGHT: u64 = 0; + +/// [`TxBuilder`] is used to build `UnsignedTransaction` +/// from the `TW::Greenfield::Proto::SigningInput` protobuf message. +pub struct TxBuilder; + +impl TxBuilder { + /// Please note that [`Proto::SigningInput::public_key`] must be set. + /// If the public key should be derived from a private key, please do it before this method is called. + pub fn unsigned_tx_from_proto( + coin: &dyn CoinContext, + input: &Proto::SigningInput<'_>, + ) -> SigningResult { + let signer = Self::signer_info_from_proto(coin, input)?; + + let fee = input + .fee + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_wrong_fee))?; + let fee = Self::fee_from_proto(fee, &signer)?; + + let eth_chain_id = U256::from_str(&input.eth_chain_id) + .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?; + + Ok(GreenfieldUnsignedTransaction { + signer, + fee, + cosmos_chain_id: input.cosmos_chain_id.to_string(), + eth_chain_id, + account_number: input.account_number, + tx_body: Self::tx_body_from_proto(input)?, + }) + } + + pub fn signer_info_from_proto( + coin: &dyn CoinContext, + input: &Proto::SigningInput, + ) -> SigningResult { + let public_key = GreenfieldPublicKey::from_bytes(coin, &input.public_key)?; + let sign_mode = match input.signing_mode { + Proto::SigningMode::Eip712 => GreenfieldSignMode::Eip712, + }; + Ok(SignerInfo { + public_key, + sequence: input.sequence, + sign_mode: sign_mode.into(), + }) + } + + fn fee_from_proto( + input: &Proto::Fee, + signer: &GreenfieldSignerInfo, + ) -> SigningResult { + let payer = GreenfieldAddress::with_secp256k1_pubkey(&signer.public_key.0); + + let amounts = input + .amounts + .iter() + .map(Self::coin_from_proto) + .collect::>()?; + Ok(GreenfieldFee { + amounts, + gas_limit: input.gas, + payer: Some(payer), + granter: None, + }) + } + + fn coin_from_proto(input: &Proto::Amount<'_>) -> SigningResult { + let amount = U256::from_str(&input.amount)?; + Ok(Coin { + amount, + denom: input.denom.to_string(), + }) + } + + fn tx_body_from_proto(input: &Proto::SigningInput<'_>) -> SigningResult { + if input.messages.is_empty() { + return Err(SigningError(SigningErrorType::Error_invalid_params)); + } + + let messages = input + .messages + .iter() + .map(Self::tx_message_from_proto) + .collect::>()?; + + Ok(GreenfieldTxBody { + messages, + memo: input.memo.to_string(), + timeout_height: DEFAULT_TIMEOUT_HEIGHT, + }) + } + + pub fn tx_message_from_proto(input: &Proto::Message) -> SigningResult { + use Proto::mod_Message::OneOfmessage_oneof as MessageEnum; + + match input.message_oneof { + MessageEnum::send_coins_message(ref send) => Self::send_msg_from_proto(send), + MessageEnum::bridge_transfer_out(ref transfer_out) => { + Self::bridge_transfer_out_from_proto(transfer_out) + }, + MessageEnum::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + } + } + + pub fn send_msg_from_proto( + send: &Proto::mod_Message::Send<'_>, + ) -> SigningResult { + use crate::transaction::message::send_order::GreenfieldSendMessage; + use tw_cosmos_sdk::transaction::message::cosmos_bank_message::SendMessage; + + let amounts = send + .amounts + .iter() + .map(Self::coin_from_proto) + .collect::>()?; + let msg = SendMessage { + custom_type_prefix: send.type_prefix.to_string().empty_or_some(), + from_address: GreenfieldAddress::from_str(&send.from_address)?, + to_address: GreenfieldAddress::from_str(&send.to_address)?, + amount: amounts, + }; + Ok(Box::new(GreenfieldSendMessage(msg))) + } + + pub fn bridge_transfer_out_from_proto( + transfer_out: &Proto::mod_Message::BridgeTransferOut<'_>, + ) -> SigningResult { + use crate::transaction::message::transfer_out::GreenfieldTransferOut; + + let amount = transfer_out + .amount + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_wrong_fee))?; + + let msg = GreenfieldTransferOut { + custom_type_prefix: transfer_out.type_prefix.to_string().empty_or_some(), + amount: Self::coin_from_proto(amount)?, + from: GreenfieldAddress::from_str(&transfer_out.from_address)?, + to: GreenfieldAddress::from_str(&transfer_out.to_address)?, + }; + Ok(Box::new(msg)) + } +} diff --git a/rust/chains/tw_greenfield/src/public_key.rs b/rust/chains/tw_greenfield/src/public_key.rs new file mode 100644 index 00000000000..8486fef04e0 --- /dev/null +++ b/rust/chains/tw_greenfield/src/public_key.rs @@ -0,0 +1,49 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::coin_context::CoinContext; +use tw_cosmos_sdk::public_key::{CosmosPublicKey, JsonPublicKey, ProtobufPublicKey}; +use tw_keypair::ecdsa::secp256k1; +use tw_keypair::tw::{PrivateKey, PublicKeyType}; +use tw_keypair::{KeyPairError, KeyPairResult}; +use tw_memory::Data; +use tw_proto::{google, to_any}; + +pub struct GreenfieldPublicKey(pub secp256k1::PublicKey); + +impl JsonPublicKey for GreenfieldPublicKey { + fn public_key_type(&self) -> String { + "/cosmos.crypto.eth.ethsecp256k1.PubKey".to_string() + } +} + +impl ProtobufPublicKey for GreenfieldPublicKey { + fn to_proto(&self) -> google::protobuf::Any { + let proto = tw_cosmos_sdk::proto::cosmos::crypto::eth::ethsecp256k1::PubKey { + key: self.0.compressed().to_vec(), + }; + to_any(&proto) + } +} + +impl CosmosPublicKey for GreenfieldPublicKey { + fn from_private_key(_coin: &dyn CoinContext, private_key: &PrivateKey) -> KeyPairResult { + let public_key = private_key + .get_public_key_by_type(PublicKeyType::Secp256k1)? + .to_secp256k1() + .ok_or(KeyPairError::InvalidPublicKey)? + .clone(); + Ok(GreenfieldPublicKey(public_key)) + } + + fn from_bytes(_coin: &dyn CoinContext, public_key_bytes: &[u8]) -> KeyPairResult { + secp256k1::PublicKey::try_from(public_key_bytes).map(GreenfieldPublicKey) + } + + fn to_bytes(&self) -> Data { + self.0.compressed().to_vec() + } +} diff --git a/rust/chains/tw_greenfield/src/signature.rs b/rust/chains/tw_greenfield/src/signature.rs new file mode 100644 index 00000000000..bee96a99b9f --- /dev/null +++ b/rust/chains/tw_greenfield/src/signature.rs @@ -0,0 +1,42 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_cosmos_sdk::signature::CosmosSignature; +use tw_evm::message::signature::{MessageSignature, SignatureType}; +use tw_keypair::ecdsa::secp256k1; +use tw_keypair::KeyPairError; +use tw_misc::traits::ToBytesVec; + +/// secp256k1 signature with the legacy ETH replay protection. +#[derive(Clone)] +pub struct GreenfieldSignature(MessageSignature); + +impl TryFrom for GreenfieldSignature { + type Error = KeyPairError; + + fn try_from(sign: secp256k1::Signature) -> Result { + MessageSignature::prepared(sign, SignatureType::Legacy).map(GreenfieldSignature) + } +} + +impl CosmosSignature for GreenfieldSignature {} + +/// [`GreenfieldSignature::try_from`] tries to parse a standard secp256k1 signature from the given bytes, +/// and applies the legacy ETH replay protection. +impl<'a> TryFrom<&'a [u8]> for GreenfieldSignature { + type Error = KeyPairError; + + fn try_from(bytes: &'a [u8]) -> Result { + let standard_sign = secp256k1::Signature::try_from(bytes)?; + GreenfieldSignature::try_from(standard_sign) + } +} + +impl ToBytesVec for GreenfieldSignature { + fn to_vec(&self) -> Vec { + self.0.to_bytes().to_vec() + } +} diff --git a/rust/chains/tw_greenfield/src/signer.rs b/rust/chains/tw_greenfield/src/signer.rs new file mode 100644 index 00000000000..43b1900c66f --- /dev/null +++ b/rust/chains/tw_greenfield/src/signer.rs @@ -0,0 +1,50 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::compiler::GreenfieldCompiler; +use crate::modules::eip712_signer::{Eip712Signer, Eip712TxPreimage}; +use crate::modules::tx_builder::TxBuilder; +use std::borrow::Cow; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::SigningResult; +use tw_coin_entry::signing_output_error; +use tw_keypair::ecdsa::secp256k1; +use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; +use tw_misc::traits::ToBytesVec; +use tw_proto::Greenfield::Proto; + +pub struct GreenfieldSigner; + +impl GreenfieldSigner { + pub fn sign( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> Proto::SigningOutput<'static> { + Self::sign_impl(coin, input) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn sign_impl( + coin: &dyn CoinContext, + mut input: Proto::SigningInput<'_>, + ) -> SigningResult> { + let key_pair = secp256k1::KeyPair::try_from(input.private_key.as_ref())?; + let public_key = key_pair.public().compressed().to_vec(); + + // Set the public key. It will be used to construct a signer info. + input.public_key = Cow::from(public_key.clone()); + let unsigned_tx = TxBuilder::unsigned_tx_from_proto(coin, &input)?; + + let Eip712TxPreimage { tx_hash, .. } = Eip712Signer::preimage_hash(&unsigned_tx)?; + // Get the standard secp256k1 signature. It will be EIP155 protected at the `GreenfieldCompiler::compile_impl`. + let signature = key_pair.sign(tx_hash)?; + + let signatures = vec![signature.to_vec()]; + let public_keys = vec![public_key]; + + GreenfieldCompiler::compile_impl(coin, input, signatures, public_keys) + } +} diff --git a/rust/chains/tw_greenfield/src/transaction/message/mod.rs b/rust/chains/tw_greenfield/src/transaction/message/mod.rs new file mode 100644 index 00000000000..2c7f8430c65 --- /dev/null +++ b/rust/chains/tw_greenfield/src/transaction/message/mod.rs @@ -0,0 +1,25 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::eip712_types::MsgPropertyType; +use tw_cosmos_sdk::transaction::message::{CosmosMessage, CosmosMessageBox}; +use tw_evm::message::eip712::message_types::MessageTypesBuilder; + +pub mod send_order; +pub mod transfer_out; +pub mod type_msg_amount; + +pub type GreenfieldMessageBox = Box; + +pub trait GreenfieldMessage: CosmosMessage { + fn eip712_type(&self, msg_idx: usize) -> String { + MsgPropertyType(msg_idx).to_string() + } + + fn declare_eip712_type(&self, msg_idx: usize, message_types: &mut MessageTypesBuilder); + + fn to_cosmos_message(&self) -> CosmosMessageBox; +} diff --git a/rust/chains/tw_greenfield/src/transaction/message/send_order.rs b/rust/chains/tw_greenfield/src/transaction/message/send_order.rs new file mode 100644 index 00000000000..85fb9da35f3 --- /dev/null +++ b/rust/chains/tw_greenfield/src/transaction/message/send_order.rs @@ -0,0 +1,97 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::GreenfieldAddress; +use crate::transaction::message::type_msg_amount::TypeMsgAmount; +use crate::transaction::message::GreenfieldMessage; +use tw_coin_entry::error::SigningResult; +use tw_cosmos_sdk::proto::cosmos as CosmosProto; +use tw_cosmos_sdk::transaction::message::cosmos_bank_message::SendMessage; +use tw_cosmos_sdk::transaction::message::{ + message_to_json, CosmosMessage, CosmosMessageBox, JsonMessage, ProtobufMessage, +}; +use tw_evm::abi::param_type::constructor::TypeConstructor; +use tw_evm::message::eip712::message_types::MessageTypesBuilder; +use tw_evm::message::eip712::property::PropertyType; +use tw_proto::type_url; + +/// cosmos.bank.v1beta1.MsgSend +/// +/// # EIP712 custom types +/// +/// ```json +/// { +/// "TypeMsgAmount": [ +/// { +/// "name": "amount", +/// "type": "string" +/// }, +/// { +/// "name": "denom", +/// "type": "string" +/// } +/// ], +/// "Msg": [ +/// { +/// "name": "amount", +/// "type": "TypeMsgAmount[]" +/// }, +/// { +/// "name": "from_address", +/// "type": "string" +/// }, +/// { +/// "name": "to_address", +/// "type": "string" +/// }, +/// { +/// "name": "type", +/// "type": "string" +/// } +/// ] +/// } +/// ``` +#[derive(Clone)] +pub struct GreenfieldSendMessage(pub SendMessage); + +impl CosmosMessage for GreenfieldSendMessage { + fn to_proto(&self) -> SigningResult { + self.0.to_proto() + } + + /// [`GreenfieldSendMessage::to_json`] implementation differs from the original [`SendMessage::to_json`]: + /// * [`JsonMessage::msg_type`] should be "cosmos.bank.v1beta1.MsgSend" if other is not specified. + /// * [`JsonMessage::value`] is the same. + fn to_json(&self) -> SigningResult { + let msg_type = self + .0 + .custom_type_prefix + .clone() + .unwrap_or_else(type_url::); + message_to_json(&msg_type, &self.0) + } +} + +impl GreenfieldMessage for GreenfieldSendMessage { + fn declare_eip712_type(&self, msg_idx: usize, message_types: &mut MessageTypesBuilder) { + let this_msg_type_name = self.eip712_type(msg_idx); + + TypeMsgAmount::declare_eip712_type(msg_idx, message_types); + + if let Some(mut builder) = message_types.add_custom_type(this_msg_type_name) { + let amount_msg_type = PropertyType::Custom(TypeMsgAmount::eip712_type(msg_idx)); + builder + .add_property("amount", PropertyType::array(amount_msg_type)) + .add_property("from_address", PropertyType::String) + .add_property("to_address", PropertyType::String) + .add_property("type", PropertyType::String); + } + } + + fn to_cosmos_message(&self) -> CosmosMessageBox { + Box::new(self.clone()) + } +} diff --git a/rust/chains/tw_greenfield/src/transaction/message/transfer_out.rs b/rust/chains/tw_greenfield/src/transaction/message/transfer_out.rs new file mode 100644 index 00000000000..dec3e261fac --- /dev/null +++ b/rust/chains/tw_greenfield/src/transaction/message/transfer_out.rs @@ -0,0 +1,105 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::GreenfieldAddress; +use crate::transaction::message::type_msg_amount::TypeMsgAmount; +use crate::transaction::message::GreenfieldMessage; +use serde::Serialize; +use tw_coin_entry::error::SigningResult; +use tw_cosmos_sdk::modules::serializer::protobuf_serializer::build_coin; +use tw_cosmos_sdk::proto::greenfield as GreenfieldProto; +use tw_cosmos_sdk::transaction::message::{ + message_to_json, CosmosMessage, CosmosMessageBox, JsonMessage, ProtobufMessage, +}; +use tw_cosmos_sdk::transaction::Coin; +use tw_evm::message::eip712::message_types::MessageTypesBuilder; +use tw_evm::message::eip712::property::PropertyType; +use tw_proto::{to_any, type_url}; + +/// greenfield.bridge.MsgTransferOut +/// +/// # EIP712 custom types +/// +/// ```json +/// { +/// "TypeMsgAmount": [ +/// { +/// "name": "amount", +/// "type": "string" +/// }, +/// { +/// "name": "denom", +/// "type": "string" +/// } +/// ], +/// "Msg": [ +/// { +/// "name": "amount", +/// "type": "TypeMsgAmount" +/// }, +/// { +/// "name": "from", +/// "type": "string" +/// }, +/// { +/// "name": "to", +/// "type": "string" +/// }, +/// { +/// "name": "type", +/// "type": "string" +/// } +/// ] +/// } +/// ``` +#[derive(Clone, Serialize)] +pub struct GreenfieldTransferOut { + #[serde(skip)] + pub custom_type_prefix: Option, + pub amount: Coin, + pub from: GreenfieldAddress, + pub to: GreenfieldAddress, +} + +impl CosmosMessage for GreenfieldTransferOut { + fn to_proto(&self) -> SigningResult { + let msg = GreenfieldProto::bridge::MsgTransferOut { + from: self.from.to_string(), + to: self.to.to_string(), + amount: Some(build_coin(&self.amount)), + }; + Ok(to_any(&msg)) + } + + fn to_json(&self) -> SigningResult { + let msg_type = self + .custom_type_prefix + .clone() + .unwrap_or_else(type_url::); + message_to_json(&msg_type, self) + } +} + +impl GreenfieldMessage for GreenfieldTransferOut { + fn declare_eip712_type(&self, msg_idx: usize, message_types: &mut MessageTypesBuilder) { + let this_msg_type_name = self.eip712_type(msg_idx); + + TypeMsgAmount::declare_eip712_type(msg_idx, message_types); + + if let Some(mut builder) = message_types.add_custom_type(this_msg_type_name) { + let amount_msg_type = PropertyType::Custom(TypeMsgAmount::eip712_type(msg_idx)); + builder + .add_property("amount", amount_msg_type) + .add_property("from", PropertyType::String) + .add_property("to", PropertyType::String) + .add_property("type", PropertyType::String); + } + } + + fn to_cosmos_message(&self) -> CosmosMessageBox { + Box::new(self.clone()) + } +} diff --git a/rust/chains/tw_greenfield/src/transaction/message/type_msg_amount.rs b/rust/chains/tw_greenfield/src/transaction/message/type_msg_amount.rs new file mode 100644 index 00000000000..3b338c3acb7 --- /dev/null +++ b/rust/chains/tw_greenfield/src/transaction/message/type_msg_amount.rs @@ -0,0 +1,25 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_evm::message::eip712::message_types::MessageTypesBuilder; +use tw_evm::message::eip712::property::PropertyType; + +/// Represents an amount type that belongs to a particular order. +pub struct TypeMsgAmount; + +impl TypeMsgAmount { + pub fn eip712_type(msg_idx: usize) -> String { + format!("TypeMsg{msg_idx}Amount") + } + + pub fn declare_eip712_type(msg_idx: usize, message_types: &mut MessageTypesBuilder) { + let type_msg_amount_type = Self::eip712_type(msg_idx); + if let Some(mut builder) = message_types.add_custom_type(type_msg_amount_type) { + builder.add_property("amount", PropertyType::String); + builder.add_property("denom", PropertyType::String); + } + } +} diff --git a/rust/chains/tw_greenfield/src/transaction/mod.rs b/rust/chains/tw_greenfield/src/transaction/mod.rs new file mode 100644 index 00000000000..718cb9fd0a3 --- /dev/null +++ b/rust/chains/tw_greenfield/src/transaction/mod.rs @@ -0,0 +1,83 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::GreenfieldAddress; +use crate::context::GreenfieldContext; +use crate::public_key::GreenfieldPublicKey; +use crate::signature::GreenfieldSignature; +use crate::transaction::message::GreenfieldMessageBox; +use tw_cosmos_sdk::transaction::{ + Fee, SignMode, SignedTransaction, SignerInfo, TxBody, UnsignedTransaction, +}; +use tw_misc::traits::ToBytesVec; +use tw_number::U256; + +pub type GreenfieldSignerInfo = SignerInfo; +pub type GreenfieldFee = Fee; +pub type GreenfieldSignedTransaction = SignedTransaction; + +pub mod message; + +pub enum GreenfieldSignMode { + Eip712, +} + +impl From for SignMode { + fn from(mode: GreenfieldSignMode) -> Self { + use tw_cosmos_sdk::proto::cosmos::signing::v1beta1 as signing_proto; + + match mode { + GreenfieldSignMode::Eip712 => { + SignMode::Other(signing_proto::SignMode::SIGN_MODE_EIP_712 as i32) + }, + } + } +} + +pub struct GreenfieldTxBody { + pub messages: Vec, + pub memo: String, + pub timeout_height: u64, +} + +impl GreenfieldTxBody { + fn into_cosmos_tx_body(self) -> TxBody { + TxBody { + messages: self + .messages + .into_iter() + .map(|greenfield_msg| greenfield_msg.to_cosmos_message()) + .collect(), + memo: self.memo, + timeout_height: self.timeout_height, + } + } +} + +pub struct GreenfieldUnsignedTransaction { + pub signer: GreenfieldSignerInfo, + pub fee: GreenfieldFee, + pub cosmos_chain_id: String, + pub eth_chain_id: U256, + pub account_number: u64, + pub tx_body: GreenfieldTxBody, +} + +impl GreenfieldUnsignedTransaction { + pub fn into_signed(self, signature: GreenfieldSignature) -> GreenfieldSignedTransaction { + self.into_cosmos_unsigned().into_signed(signature.to_vec()) + } + + fn into_cosmos_unsigned(self) -> UnsignedTransaction { + UnsignedTransaction { + signer: self.signer, + fee: self.fee, + chain_id: self.cosmos_chain_id, + account_number: self.account_number, + tx_body: self.tx_body.into_cosmos_tx_body(), + } + } +} diff --git a/rust/chains/tw_greenfield/tests/data/send_order_eip712.json b/rust/chains/tw_greenfield/tests/data/send_order_eip712.json new file mode 100644 index 00000000000..01f861be82b --- /dev/null +++ b/rust/chains/tw_greenfield/tests/data/send_order_eip712.json @@ -0,0 +1,149 @@ +{ + "types": { + "Coin": [ + { + "name": "amount", + "type": "uint256" + }, + { + "name": "denom", + "type": "string" + } + ], + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "name", + "type": "string" + }, + { + "name": "salt", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "string" + }, + { + "name": "version", + "type": "string" + } + ], + "Fee": [ + { + "name": "amount", + "type": "Coin[]" + }, + { + "name": "gas_limit", + "type": "uint256" + }, + { + "name": "granter", + "type": "string" + }, + { + "name": "payer", + "type": "string" + } + ], + "Msg1": [ + { + "name": "amount", + "type": "TypeMsg1Amount[]" + }, + { + "name": "from_address", + "type": "string" + }, + { + "name": "to_address", + "type": "string" + }, + { + "name": "type", + "type": "string" + } + ], + "Tx": [ + { + "name": "account_number", + "type": "uint256" + }, + { + "name": "chain_id", + "type": "uint256" + }, + { + "name": "fee", + "type": "Fee" + }, + { + "name": "memo", + "type": "string" + }, + { + "name": "msg1", + "type": "Msg1" + }, + { + "name": "sequence", + "type": "uint256" + }, + { + "name": "timeout_height", + "type": "uint256" + } + ], + "TypeMsg1Amount": [ + { + "name": "amount", + "type": "string" + }, + { + "name": "denom", + "type": "string" + } + ] + }, + "primaryType": "Tx", + "domain": { + "name": "Greenfield Tx", + "version": "1.0.0", + "chainId": "5600", + "verifyingContract": "greenfield", + "salt": "0" + }, + "message": { + "account_number": "15560", + "chain_id": "5600", + "fee": { + "amount": [ + { + "amount": "2000000000000000", + "denom": "BNB" + } + ], + "gas_limit": "200000", + "granter": "", + "payer": "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1" + }, + "memo": "", + "msg1": { + "amount": [ + { + "amount": "1000000000000000", + "denom": "BNB" + } + ], + "from_address": "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1", + "to_address": "0x280b27f3676db1C4475EE10F75D510Eb527fd155", + "type": "/cosmos.bank.v1beta1.MsgSend" + }, + "sequence": "2", + "timeout_height": "0" + } +} diff --git a/rust/chains/tw_greenfield/tests/data/transfer_out_eip712.json b/rust/chains/tw_greenfield/tests/data/transfer_out_eip712.json new file mode 100644 index 00000000000..c17ecd1bea2 --- /dev/null +++ b/rust/chains/tw_greenfield/tests/data/transfer_out_eip712.json @@ -0,0 +1,147 @@ +{ + "types": { + "Coin": [ + { + "name": "amount", + "type": "uint256" + }, + { + "name": "denom", + "type": "string" + } + ], + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "name", + "type": "string" + }, + { + "name": "salt", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "string" + }, + { + "name": "version", + "type": "string" + } + ], + "Fee": [ + { + "name": "amount", + "type": "Coin[]" + }, + { + "name": "gas_limit", + "type": "uint256" + }, + { + "name": "granter", + "type": "string" + }, + { + "name": "payer", + "type": "string" + } + ], + "Msg1": [ + { + "name": "amount", + "type": "TypeMsg1Amount" + }, + { + "name": "from", + "type": "string" + }, + { + "name": "to", + "type": "string" + }, + { + "name": "type", + "type": "string" + } + ], + "Tx": [ + { + "name": "account_number", + "type": "uint256" + }, + { + "name": "chain_id", + "type": "uint256" + }, + { + "name": "fee", + "type": "Fee" + }, + { + "name": "memo", + "type": "string" + }, + { + "name": "msg1", + "type": "Msg1" + }, + { + "name": "sequence", + "type": "uint256" + }, + { + "name": "timeout_height", + "type": "uint256" + } + ], + "TypeMsg1Amount": [ + { + "name": "amount", + "type": "string" + }, + { + "name": "denom", + "type": "string" + } + ] + }, + "primaryType": "Tx", + "domain": { + "name": "Greenfield Tx", + "version": "1.0.0", + "chainId": "5600", + "verifyingContract": "greenfield", + "salt": "0" + }, + "message": { + "account_number": "15560", + "chain_id": "5600", + "fee": { + "amount": [ + { + "amount": "2000000000000000", + "denom": "BNB" + } + ], + "gas_limit": "200000", + "granter": "", + "payer": "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1" + }, + "memo": "", + "msg1": { + "amount": { + "amount": "1000000000000000", + "denom": "BNB" + }, + "from": "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1", + "to": "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1", + "type": "/greenfield.bridge.MsgTransferOut" + }, + "sequence": "2", + "timeout_height": "0" + } +} \ No newline at end of file diff --git a/rust/chains/tw_greenfield/tests/eip712_signer.rs b/rust/chains/tw_greenfield/tests/eip712_signer.rs new file mode 100644 index 00000000000..75d77eb1ba4 --- /dev/null +++ b/rust/chains/tw_greenfield/tests/eip712_signer.rs @@ -0,0 +1,109 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::test_utils::test_context::TestCoinContext; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_greenfield::modules::eip712_signer::{Eip712Signer, Eip712TxPreimage}; +use tw_greenfield::modules::tx_builder::TxBuilder; +use tw_misc::assert_eq_json; +use tw_proto::Greenfield::Proto; +use Proto::mod_Message::OneOfmessage_oneof as MessageEnum; + +const SEND_ORDER_EIP712: &str = include_str!("data/send_order_eip712.json"); +const TRANSFER_OUT_EIP712: &str = include_str!("data/transfer_out_eip712.json"); + +const PUBLIC_KEY_15560: &str = "0279ef34064da10db0463c70480616ba020703ec3a45026def7bebd2082f5d6fc8"; + +fn make_amount<'a>(denom: &'a str, amount: &'a str) -> Proto::Amount<'a> { + Proto::Amount { + denom: denom.into(), + amount: amount.into(), + } +} + +/// Testnet transaction: +/// https://greenfieldscan.com/tx/0x9f895cf2dd64fb1f428cefcf2a6585a813c3540fc9fe1ef42db1da2cb1df55ab +#[test] +fn test_eip712_signer_encode_send() { + let coin = TestCoinContext::default(); + + let send_order = Proto::mod_Message::Send { + from_address: "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1".into(), + to_address: "0x280b27f3676db1C4475EE10F75D510Eb527fd155".into(), + amounts: vec![make_amount("BNB", "1000000000000000")], + ..Proto::mod_Message::Send::default() + }; + + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::Eip712, + account_number: 15560, + eth_chain_id: "5600".into(), + cosmos_chain_id: "greenfield_5600-1".into(), + fee: Some(Proto::Fee { + amounts: vec![make_amount("BNB", "2000000000000000")], + gas: 200000, + }), + sequence: 2, + messages: vec![Proto::Message { + message_oneof: MessageEnum::send_coins_message(send_order), + }], + public_key: PUBLIC_KEY_15560.decode_hex().unwrap().into(), + ..Proto::SigningInput::default() + }; + + let unsigned_tx = TxBuilder::unsigned_tx_from_proto(&coin, &input) + .expect("Error on generating an unsigned transaction"); + + let Eip712TxPreimage { eip712_tx, tx_hash } = + Eip712Signer::preimage_hash(&unsigned_tx).expect("Error on TX preimage"); + + assert_eq_json!(eip712_tx, SEND_ORDER_EIP712); + assert_eq!( + tx_hash.to_hex(), + "b8c62654582ca96b37ca94966199682bf70ed934e740d2f874ff54675a0ac344" + ); +} + +#[test] +fn test_eip712_signer_encode_transfer_out() { + let coin = TestCoinContext::default(); + + let send_order = Proto::mod_Message::BridgeTransferOut { + from_address: "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1".into(), + to_address: "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1".into(), + amount: Some(make_amount("BNB", "1000000000000000")), + ..Proto::mod_Message::BridgeTransferOut::default() + }; + + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::Eip712, + account_number: 15560, + eth_chain_id: "5600".into(), + cosmos_chain_id: "greenfield_5600-1".into(), + fee: Some(Proto::Fee { + amounts: vec![make_amount("BNB", "2000000000000000")], + gas: 200000, + }), + sequence: 2, + messages: vec![Proto::Message { + message_oneof: MessageEnum::bridge_transfer_out(send_order), + }], + public_key: PUBLIC_KEY_15560.decode_hex().unwrap().into(), + ..Proto::SigningInput::default() + }; + + let unsigned_tx = TxBuilder::unsigned_tx_from_proto(&coin, &input) + .expect("Error on generating an unsigned transaction"); + + let Eip712TxPreimage { eip712_tx, tx_hash } = + Eip712Signer::preimage_hash(&unsigned_tx).expect("Error on TX preimage"); + + assert_eq_json!(eip712_tx, TRANSFER_OUT_EIP712); + assert_eq!( + tx_hash.to_hex(), + "ea7731461041f5f652ab424bb767c670e484cfe1f4a85179deba8a6596873af4" + ); +} diff --git a/rust/tw_any_coin/tests/chains/binance/binance_sign.rs b/rust/tw_any_coin/tests/chains/binance/binance_sign.rs index 564b750971e..de227f4efc5 100644 --- a/rust/tw_any_coin/tests/chains/binance/binance_sign.rs +++ b/rust/tw_any_coin/tests/chains/binance/binance_sign.rs @@ -10,6 +10,7 @@ use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_proto::Binance::Proto; use tw_proto::Binance::Proto::mod_SigningInput::OneOforder_oneof as OrderEnum; +use tw_proto::Common::Proto::SigningError; const ACCOUNT_12_PRIVATE_KEY: &str = "90335b9d2153ad1a9799a3ccc070bd64b4164e9642ee1dd48053c33f9a3a05e9"; @@ -51,6 +52,7 @@ fn test_binance_sign_trade_order() { let mut signer = AnySignerHelper::::default(); let output = signer.sign(CoinType::Binance, input); + assert_eq!(output.error, SigningError::OK); assert_eq!( output.encoded.to_hex(), "dc01f0625dee0a64ce6dc0430a14ba36f0fad74d8f41045463e4774f328f4af779e5122b424133364630464144373444384634313034353436334534373734463332384634414637373945352d33361a0b4e4e422d3333385f424e422002280130b09282413880c2d72f4001126e0a26eb5ae98721029729a52e4e3c2b4a4e52aa74033eedaf8ba1df5ab6d1f518fd69e67bbd309b0e12409123cb6906bb20aeb753f4a121d4d88ff0e9750ba75b0c4e10d76caee1e7d2481290fa3b9887a6225d6997f5f939ef834ea61d596a314237c48e560da9e17b5a180c20232001" @@ -83,6 +85,7 @@ fn test_binance_sign_cancel_trade_order() { let mut signer = AnySignerHelper::::default(); let output = signer.sign(CoinType::Binance, input); + assert_eq!(output.error, SigningError::OK); assert_eq!( output.encoded.to_hex(), "cc01f0625dee0a54166e681b0a14ba36f0fad74d8f41045463e4774f328f4af779e5120b4e4e422d3333385f424e421a2b424133364630464144373444384634313034353436334534373734463332384634414637373945352d3336126e0a26eb5ae98721029729a52e4e3c2b4a4e52aa74033eedaf8ba1df5ab6d1f518fd69e67bbd309b0e12403df6603426b991f7040bce22ce0137c12137df01e1d4d425cf3d9104103aec6335ac05c825e08ba26b9f72aa4cc45aa75cacfb6082df86b00692fef9701eb0f5180c20242001" @@ -138,6 +141,7 @@ fn test_binance_sign_send_order() { "1a04", "74657374", "2001", ); + assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); } @@ -172,6 +176,7 @@ fn test_binance_sign_token_freeze_order() { "6b6a8fc71240e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162", "722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991180f2001", ); + assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); } @@ -206,6 +211,7 @@ fn test_binance_sign_token_unfreeze_order() { "6b6a8fc71240e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162", "722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991180f2001", ); + assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); } @@ -242,6 +248,7 @@ fn test_binance_sign_token_issue_order() { "4e4e422d3333385f424e42", "208094ebdc032801126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc712401fbb993d643f03b3e8e757a502035f58c4c45aaaa6e107a3059ab7c6164283c10f1254e87feee21477c64c87b1a27d8481048533ae2f685b3ac0dc66e4edbc0b180f2001", ); + assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); } @@ -274,6 +281,7 @@ fn test_binance_sign_token_mint_order() { "4e4e422d3333385f424e42", "18c0843d126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991180f2001", ); + assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); } @@ -306,6 +314,7 @@ fn test_binance_sign_token_burn_order() { "4e4e422d3333385f424e42", "18c0843d126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991180f2001", ); + assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); } @@ -347,6 +356,7 @@ fn test_binance_sign_htlt_order() { "6b6a8fc7124051439de2da19fe9fd22137c903cfc5dc87553bf05dca0bb202c0e07c47f9b51269efa272", "43eb7b55888f5384a84ac1eac6d325c830d1be0ed042838e2dc0f6a9180f", ); + assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); } @@ -380,6 +390,7 @@ fn test_binance_sign_deposit_htlt_order() { "12400ca4144c6818e2836d09b4faf3161781d85f9adfc00badb2eaa0953174610a233b81413dafcf8471", "6abad48a4ed3aeb9884d90eb8416eec5d5c0c6930ab60bd01810", ); + assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); } @@ -414,6 +425,7 @@ fn test_binance_sign_claim_htlt_order() { "ac993dbe0f6b6a8fc71240fa30ba50111aa31d8329dacb6d044c1c7d54f1cb782bc9aa2a50c3fabce02a4579d7", "5b76ca69a9fab11b676d9da66b5af7aa4c9ad3d18e24fffeb16433be39fb180f2001", ); + assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); } @@ -446,6 +458,7 @@ fn test_binance_sign_refund_htlt_order() { "70b32206a2d15198b7165acf1e2a18952c9e4570b0f862e1ab7bb868c30781a42c9e3ec0ae08982e8d6c", "91c55b83c71b7b1e180f2001", ); + assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); } @@ -480,6 +493,7 @@ fn test_binance_sign_transfer_out_order() { "71a788ccf4e3eade1c7e1773e9d2093982d7f802f8f85f35ef550049011728206e4eda1a272f9e96fd95", "ef3983cad85a29cd14262c22e0180f2001", ); + assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); } @@ -515,6 +529,7 @@ fn test_binance_sign_side_chain_delegate_order() { "2a09ac2b6b6fb1d3b9fb5b4c03630d3d7a7da42b1c6736d6127142a3fcdca0b70a3d065da8d4f4df8b5d", "9d8f46aeb3627a7d7aa901fe186af34c180f2001", ); + assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); } @@ -553,6 +568,7 @@ fn test_binance_sign_side_chain_redelegate_order() { "af44ea561aac993dbe0f6b6a8fc71240114c6927423e95ecc831ec763b629b3a40db8feeb330528a941f", "d74843c0d63b4271b23916770d4901988c1f56b20086e5768a12290ebec265e30a80f8f3d88e180f2001", ); + assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); } @@ -588,6 +604,7 @@ fn test_binance_sign_side_chain_undelegate_order() { "75e5eaa675a5ed976b2ec3b8ca055a2b05e7fb471d328bd04df854789437dd06407e41ebb1e5a345604c", "93663dfb660e223800636c0b94c2e798180f2001", ); + assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); } @@ -619,6 +636,7 @@ fn test_binance_sign_time_lock_order() { "07921531", "0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e1121c4465736372697074696f6e206c6f636b656420666f72206f666665721a090a03424e4210c0843d20dbaaf8fa05126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240c270822b9515ba486c6a6b3472d388a5aea872ed960c0b53de0fafdc8682ef473a126f01e7dd2c00f04a0138a601b9540f54b14026846de362f7ab7f9fed948b180f2001", ); + assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); } @@ -653,6 +671,7 @@ fn test_binance_sign_time_relock_order() { "124086ddaa077c8ae551d402fa409cf7e91663982b0542200967c03c0b5876b181353250f689d342f221", "7624a077b671ce7d09649187e29879f40abbbee9de7ab27c180f2001", ); + assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); } @@ -683,5 +702,6 @@ fn test_binance_sign_time_unlock_order() { "fd2032834f59ec9fe69fd6eaa4aca24242dfbc5ec4ef8c435cb9da7eb05ab78e1b8ca9f109657cb77996", "898f1b59137b3d8f1e00f842e409e18033b347180f2001", ); + assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); } diff --git a/rust/tw_any_coin/tests/chains/greenfield/greenfield_address.rs b/rust/tw_any_coin/tests/chains/greenfield/greenfield_address.rs new file mode 100644 index 00000000000..6223ac6218d --- /dev/null +++ b/rust/tw_any_coin/tests/chains/greenfield/greenfield_address.rs @@ -0,0 +1,48 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_any_coin::test_utils::address_utils::{ + test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, +}; +use tw_coin_registry::coin_type::CoinType; + +#[test] +fn test_greenfield_address_normalization() { + test_address_normalization( + CoinType::Greenfield, + "0xb16db98b365b1f89191996942612b14f1da4bd5f", + "0xb16Db98B365B1f89191996942612B14F1Da4Bd5f", + ); +} + +#[test] +fn test_greenfield_address_is_valid() { + test_address_valid( + CoinType::Greenfield, + "0xb16db98b365b1f89191996942612b14f1da4bd5f", + ); +} + +#[test] +fn test_greenfield_address_invalid() { + test_address_invalid( + CoinType::Greenfield, + "b16Db98B365B1f89191996942612B14F1Da4Bd5f", + ); + test_address_invalid( + CoinType::Greenfield, + "bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2", + ); +} + +#[test] +fn test_greenfield_address_get_data() { + test_address_get_data( + CoinType::Greenfield, + "0xb16Db98B365B1f89191996942612B14F1Da4Bd5f", + "b16Db98B365B1f89191996942612B14F1Da4Bd5f", + ); +} diff --git a/rust/tw_any_coin/tests/chains/greenfield/greenfield_compile.rs b/rust/tw_any_coin/tests/chains/greenfield/greenfield_compile.rs new file mode 100644 index 00000000000..a48363ef791 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/greenfield/greenfield_compile.rs @@ -0,0 +1,78 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::chains::greenfield::{make_amount, PUBLIC_KEY_15560}; +use std::borrow::Cow; +use tw_any_coin::test_utils::sign_utils::{CompilerHelper, PreImageHelper}; +use tw_coin_registry::coin_type::CoinType; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_proto::Common::Proto::SigningError; +use tw_proto::Greenfield::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; +use Proto::mod_Message::OneOfmessage_oneof as MessageEnum; + +#[test] +fn test_greenfield_compile() { + let public_key_data = PUBLIC_KEY_15560.decode_hex().unwrap(); + + let send_order = Proto::mod_Message::Send { + from_address: "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1".into(), + to_address: "0x280b27f3676db1C4475EE10F75D510Eb527fd155".into(), + amounts: vec![make_amount("BNB", "1000000000000000")], + ..Proto::mod_Message::Send::default() + }; + + let mut input = Proto::SigningInput { + signing_mode: Proto::SigningMode::Eip712, + account_number: 15560, + eth_chain_id: "5600".into(), + cosmos_chain_id: "greenfield_5600-1".into(), + fee: Some(Proto::Fee { + amounts: vec![make_amount("BNB", "2000000000000000")], + gas: 200000, + }), + sequence: 2, + messages: vec![Proto::Message { + message_oneof: MessageEnum::send_coins_message(send_order), + }], + mode: Proto::BroadcastMode::ASYNC, + public_key: public_key_data.clone().into(), + ..Proto::SigningInput::default() + }; + + // Step 2: Obtain preimage hash + let mut pre_imager = PreImageHelper::::default(); + let preimage_output = pre_imager.pre_image_hashes(CoinType::Greenfield, &input); + + assert_eq!(preimage_output.error, SigningError::OK); + assert_eq!( + preimage_output.data_hash.to_hex(), + "b8c62654582ca96b37ca94966199682bf70ed934e740d2f874ff54675a0ac344" + ); + + // Step 3: Compile transaction info + + // Simulate signature, normally obtained from signature server. + let signature = "cb3a4684a991014a387a04a85b59227ebb79567c2025addcb296b4ca856e9f810d3b526f2a0d0fad6ad1b126b3b9516f8b3be020a7cca9c03ce3cf47f4199b6d00"; + let protected_signature = "cb3a4684a991014a387a04a85b59227ebb79567c2025addcb296b4ca856e9f810d3b526f2a0d0fad6ad1b126b3b9516f8b3be020a7cca9c03ce3cf47f4199b6d1b"; + let signature_bytes = signature.decode_hex().unwrap(); + + // Reset the `SigningInput::public_key`. + // The public key argument of the `TWTransactionCompilerCompileWithSignatures` should be used instead. + input.public_key = Cow::default(); + let mut compiler = CompilerHelper::::default(); + let output = compiler.compile( + CoinType::Greenfield, + &input, + vec![signature_bytes], + vec![public_key_data], + ); + + let expected = r#"{"mode":"BROADCAST_MODE_ASYNC","tx_bytes":"CpQBCpEBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnEKKjB4OWQxZDk3YURGY2QzMjRCYmQ2MDNEMzg3MkJENzhlMDQwOTg1MTBiMRIqMHgyODBiMjdmMzY3NmRiMUM0NDc1RUUxMEY3NUQ1MTBFYjUyN2ZkMTU1GhcKA0JOQhIQMTAwMDAwMDAwMDAwMDAwMBJ5ClgKTQomL2Nvc21vcy5jcnlwdG8uZXRoLmV0aHNlY3AyNTZrMS5QdWJLZXkSIwohAnnvNAZNoQ2wRjxwSAYWugIHA+w6RQJt73vr0ggvXW/IEgUKAwjIBRgCEh0KFwoDQk5CEhAyMDAwMDAwMDAwMDAwMDAwEMCaDBpByzpGhKmRAUo4egSoW1kifrt5VnwgJa3cspa0yoVun4ENO1JvKg0PrWrRsSazuVFvizvgIKfMqcA8489H9BmbbRs="}"#; + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.serialized, expected); + assert_eq!(output.signature.to_hex(), protected_signature); +} diff --git a/rust/tw_any_coin/tests/chains/greenfield/greenfield_sign.rs b/rust/tw_any_coin/tests/chains/greenfield/greenfield_sign.rs new file mode 100644 index 00000000000..f6469f95194 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/greenfield/greenfield_sign.rs @@ -0,0 +1,250 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::chains::greenfield::{make_amount, PRIVATE_KEY_15560, PRIVATE_KEY_1686}; +use tw_any_coin::test_utils::sign_utils::AnySignerHelper; +use tw_coin_registry::coin_type::CoinType; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_proto::Common::Proto::SigningError; +use tw_proto::Greenfield::Proto; +use Proto::mod_Message::OneOfmessage_oneof as MessageEnum; + +/// **Testnet** +/// Successfully broadcasted: https://greenfieldscan.com/tx/0x9f895cf2dd64fb1f428cefcf2a6585a813c3540fc9fe1ef42db1da2cb1df55ab +#[test] +fn test_greenfield_sign_send_order_9f895c() { + let send_order = Proto::mod_Message::Send { + from_address: "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1".into(), + to_address: "0x280b27f3676db1C4475EE10F75D510Eb527fd155".into(), + amounts: vec![make_amount("BNB", "1000000000000000")], + ..Proto::mod_Message::Send::default() + }; + + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::Eip712, + account_number: 15560, + eth_chain_id: "5600".into(), + cosmos_chain_id: "greenfield_5600-1".into(), + fee: Some(Proto::Fee { + amounts: vec![make_amount("BNB", "2000000000000000")], + gas: 200000, + }), + sequence: 2, + messages: vec![Proto::Message { + message_oneof: MessageEnum::send_coins_message(send_order), + }], + private_key: PRIVATE_KEY_15560.decode_hex().unwrap().into(), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Greenfield, input); + + let expected = r#"{"mode":"BROADCAST_MODE_SYNC","tx_bytes":"CpQBCpEBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnEKKjB4OWQxZDk3YURGY2QzMjRCYmQ2MDNEMzg3MkJENzhlMDQwOTg1MTBiMRIqMHgyODBiMjdmMzY3NmRiMUM0NDc1RUUxMEY3NUQ1MTBFYjUyN2ZkMTU1GhcKA0JOQhIQMTAwMDAwMDAwMDAwMDAwMBJ5ClgKTQomL2Nvc21vcy5jcnlwdG8uZXRoLmV0aHNlY3AyNTZrMS5QdWJLZXkSIwohAnnvNAZNoQ2wRjxwSAYWugIHA+w6RQJt73vr0ggvXW/IEgUKAwjIBRgCEh0KFwoDQk5CEhAyMDAwMDAwMDAwMDAwMDAwEMCaDBpByzpGhKmRAUo4egSoW1kifrt5VnwgJa3cspa0yoVun4ENO1JvKg0PrWrRsSazuVFvizvgIKfMqcA8489H9BmbbRs="}"#; + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.serialized, expected); + assert_eq!(output.signature.to_hex(), "cb3a4684a991014a387a04a85b59227ebb79567c2025addcb296b4ca856e9f810d3b526f2a0d0fad6ad1b126b3b9516f8b3be020a7cca9c03ce3cf47f4199b6d1b"); +} + +/// Successfully broadcasted: https://greenfieldscan.com/tx/DA50E269A13D2C42770CD6A4C35EA7EBEE46C677465ED92985215E67C340E28D +#[test] +fn test_greenfield_sign_send_order_b798ab() { + let send_order = Proto::mod_Message::Send { + from_address: "0xF1DB7D5256d721fE3C144F5c1ed4b3A3A94Dc444".into(), + to_address: "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1".into(), + amounts: vec![make_amount("BNB", "100000000000000")], + ..Proto::mod_Message::Send::default() + }; + + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::Eip712, + account_number: 1686, + eth_chain_id: "1017".into(), + cosmos_chain_id: "greenfield_1017-1".into(), + fee: Some(Proto::Fee { + amounts: vec![make_amount("BNB", "6000000000000")], + gas: 1200, + }), + sequence: 0, + messages: vec![Proto::Message { + message_oneof: MessageEnum::send_coins_message(send_order), + }], + private_key: PRIVATE_KEY_1686.decode_hex().unwrap().into(), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Greenfield, input); + + let expected = r#"{"mode":"BROADCAST_MODE_SYNC","tx_bytes":"CpMBCpABChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnAKKjB4RjFEQjdENTI1NmQ3MjFmRTNDMTQ0RjVjMWVkNGIzQTNBOTREYzQ0NBIqMHg5ZDFkOTdhREZjZDMyNEJiZDYwM0QzODcyQkQ3OGUwNDA5ODUxMGIxGhYKA0JOQhIPMTAwMDAwMDAwMDAwMDAwEnMKVgpNCiYvY29zbW9zLmNyeXB0by5ldGguZXRoc2VjcDI1NmsxLlB1YktleRIjCiEDCbufQiEfB0Z6DbH3BQTjvz/DjTWbiziPi702Ws2BbJYSBQoDCMgFEhkKFAoDQk5CEg02MDAwMDAwMDAwMDAwELAJGkGvQ8FyO09ECauebgRGWal94uZtp0K1vkqnl0xHCzzEylWLS7PJcBepUCkIkG4xnswnN4n7EqHZoZ+qVTH8Ue7fGw=="}"#; + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.serialized, expected); + assert_eq!(output.signature.to_hex(), "af43c1723b4f4409ab9e6e044659a97de2e66da742b5be4aa7974c470b3cc4ca558b4bb3c97017a9502908906e319ecc273789fb12a1d9a19faa5531fc51eedf1b"); + + let expected_signature_json = r#"[{"pub_key":{"type":"/cosmos.crypto.eth.ethsecp256k1.PubKey","value":"Awm7n0IhHwdGeg2x9wUE478/w401m4s4j4u9NlrNgWyW"},"signature":"r0PBcjtPRAmrnm4ERlmpfeLmbadCtb5Kp5dMRws8xMpVi0uzyXAXqVApCJBuMZ7MJzeJ+xKh2aGfqlUx/FHu3xs="}]"#; + assert_eq!(output.signature_json, expected_signature_json); +} + +/// **Testnet** +/// Successfully broadcasted Greenfield: https://greenfieldscan.com/tx/38C29C530A74946CFD22EE07DA642F5EF9399BC9AEA59EC56A9B5BE16DE16CE7 +/// BSC (parent transaction): https://testnet.bscscan.com/tx/0x7f73c8a362e14e58cb5e0ec17616afc50eff7aa398db472383a6d017c8a5861a +#[test] +fn test_greenfield_sign_transfer_out() { + let transfer_out = Proto::mod_Message::BridgeTransferOut { + from_address: "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1".into(), + to_address: "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1".into(), + amount: Some(make_amount("BNB", "5670000000000000")), + ..Proto::mod_Message::BridgeTransferOut::default() + }; + + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::Eip712, + account_number: 15560, + eth_chain_id: "5600".into(), + cosmos_chain_id: "greenfield_5600-1".into(), + fee: Some(Proto::Fee { + amounts: vec![make_amount("BNB", "6000000000000")], + gas: 1200, + }), + sequence: 7, + messages: vec![Proto::Message { + message_oneof: MessageEnum::bridge_transfer_out(transfer_out), + }], + private_key: PRIVATE_KEY_15560.decode_hex().unwrap().into(), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Greenfield, input); + + let expected = r#"{"mode":"BROADCAST_MODE_SYNC","tx_bytes":"CpkBCpYBCiEvZ3JlZW5maWVsZC5icmlkZ2UuTXNnVHJhbnNmZXJPdXQScQoqMHg5ZDFkOTdhREZjZDMyNEJiZDYwM0QzODcyQkQ3OGUwNDA5ODUxMGIxEioweDlkMWQ5N2FERmNkMzI0QmJkNjAzRDM4NzJCRDc4ZTA0MDk4NTEwYjEaFwoDQk5CEhA1NjcwMDAwMDAwMDAwMDAwEnUKWApNCiYvY29zbW9zLmNyeXB0by5ldGguZXRoc2VjcDI1NmsxLlB1YktleRIjCiECee80Bk2hDbBGPHBIBha6AgcD7DpFAm3ve+vSCC9db8gSBQoDCMgFGAcSGQoUCgNCTkISDTYwMDAwMDAwMDAwMDAQsAkaQc4DDByhu80Uy/M3sQePvAmhmbFWZeGq359rugtskEiXKfCzSB86XmBi+l+Q5ETDS2folMxbufHSE8gM4WBCHzgc"}"#; + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.serialized, expected); + assert_eq!(output.signature.to_hex(), "ce030c1ca1bbcd14cbf337b1078fbc09a199b15665e1aadf9f6bba0b6c90489729f0b3481f3a5e6062fa5f90e444c34b67e894cc5bb9f1d213c80ce160421f381c"); +} + +#[test] +fn test_greenfield_sign_no_messages() { + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::Eip712, + account_number: 15560, + eth_chain_id: "5600".into(), + cosmos_chain_id: "greenfield_5600-1".into(), + fee: Some(Proto::Fee { + amounts: vec![make_amount("BNB", "6000000000000")], + gas: 1200, + }), + sequence: 3, + messages: Vec::default(), + private_key: PRIVATE_KEY_15560.decode_hex().unwrap().into(), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Greenfield, input); + + assert_eq!(output.error, SigningError::Error_invalid_params); +} + +/// Successfully broadcasted: https://greenfieldscan.com/tx/82E967B702AFFA9227C3E987CD35B85DD862AE1C49F3027A639E354480138C40 +#[test] +fn test_greenfield_sign_multiple_messages_82e967() { + let send_order1 = Proto::mod_Message::Send { + from_address: "0xF1DB7D5256d721fE3C144F5c1ed4b3A3A94Dc444".into(), + to_address: "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1".into(), + amounts: vec![make_amount("BNB", "100000000000000")], + ..Proto::mod_Message::Send::default() + }; + let send_order2 = Proto::mod_Message::Send { + from_address: "0xF1DB7D5256d721fE3C144F5c1ed4b3A3A94Dc444".into(), + to_address: "0x280b27f3676db1C4475EE10F75D510Eb527fd155".into(), + amounts: vec![make_amount("BNB", "90000000000000")], + ..Proto::mod_Message::Send::default() + }; + + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::Eip712, + account_number: 1686, + eth_chain_id: "1017".into(), + cosmos_chain_id: "greenfield_1017-1".into(), + fee: Some(Proto::Fee { + amounts: vec![make_amount("BNB", "15000000000000")], + gas: 3000, + }), + sequence: 1, + messages: vec![ + Proto::Message { + message_oneof: MessageEnum::send_coins_message(send_order1), + }, + Proto::Message { + message_oneof: MessageEnum::send_coins_message(send_order2), + }, + ], + private_key: PRIVATE_KEY_1686.decode_hex().unwrap().into(), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Greenfield, input); + + let expected = r#"{"mode":"BROADCAST_MODE_SYNC","tx_bytes":"CqUCCpABChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnAKKjB4RjFEQjdENTI1NmQ3MjFmRTNDMTQ0RjVjMWVkNGIzQTNBOTREYzQ0NBIqMHg5ZDFkOTdhREZjZDMyNEJiZDYwM0QzODcyQkQ3OGUwNDA5ODUxMGIxGhYKA0JOQhIPMTAwMDAwMDAwMDAwMDAwCo8BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm8KKjB4RjFEQjdENTI1NmQ3MjFmRTNDMTQ0RjVjMWVkNGIzQTNBOTREYzQ0NBIqMHgyODBiMjdmMzY3NmRiMUM0NDc1RUUxMEY3NUQ1MTBFYjUyN2ZkMTU1GhUKA0JOQhIOOTAwMDAwMDAwMDAwMDASdgpYCk0KJi9jb3Ntb3MuY3J5cHRvLmV0aC5ldGhzZWNwMjU2azEuUHViS2V5EiMKIQMJu59CIR8HRnoNsfcFBOO/P8ONNZuLOI+LvTZazYFslhIFCgMIyAUYARIaChUKA0JOQhIOMTUwMDAwMDAwMDAwMDAQuBcaQS23kxqz7R/QZTegc1Ic1U0g+YUm9r8Y8Zx0+sHzKREYWg04yxy/EKvVY7LIqATQ2zi4rb9TZSIyXuMcyGk2JQYb"}"#; + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.serialized, expected); + assert_eq!(output.signature.to_hex(), "2db7931ab3ed1fd06537a073521cd54d20f98526f6bf18f19c74fac1f32911185a0d38cb1cbf10abd563b2c8a804d0db38b8adbf536522325ee31cc8693625061b"); + + let expected_signature_json = r#"[{"pub_key":{"type":"/cosmos.crypto.eth.ethsecp256k1.PubKey","value":"Awm7n0IhHwdGeg2x9wUE478/w401m4s4j4u9NlrNgWyW"},"signature":"LbeTGrPtH9BlN6BzUhzVTSD5hSb2vxjxnHT6wfMpERhaDTjLHL8Qq9VjssioBNDbOLitv1NlIjJe4xzIaTYlBhs="}]"#; + assert_eq!(output.signature_json, expected_signature_json); +} + +/// Successfully broadcasted: https://greenfieldscan.com/tx/E3539ED65D195BE8CE43C1A6567384C758E40432B8FDC7C25841E21F5F53375B +#[test] +fn test_greenfield_sign_multiple_messages_e3539e() { + let send_order = Proto::mod_Message::Send { + from_address: "0xF1DB7D5256d721fE3C144F5c1ed4b3A3A94Dc444".into(), + to_address: "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1".into(), + amounts: vec![make_amount("BNB", "100000000000000")], + ..Proto::mod_Message::Send::default() + }; + let transfer_out = Proto::mod_Message::BridgeTransferOut { + from_address: "0xF1DB7D5256d721fE3C144F5c1ed4b3A3A94Dc444".into(), + to_address: "0xF1DB7D5256d721fE3C144F5c1ed4b3A3A94Dc444".into(), + amount: Some(make_amount("BNB", "200000000000000")), + ..Proto::mod_Message::BridgeTransferOut::default() + }; + + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::Eip712, + account_number: 1686, + eth_chain_id: "1017".into(), + cosmos_chain_id: "greenfield_1017-1".into(), + fee: Some(Proto::Fee { + amounts: vec![make_amount("BNB", "15000000000000")], + gas: 3000, + }), + sequence: 2, + messages: vec![ + Proto::Message { + message_oneof: MessageEnum::send_coins_message(send_order), + }, + Proto::Message { + message_oneof: MessageEnum::bridge_transfer_out(transfer_out), + }, + ], + private_key: PRIVATE_KEY_1686.decode_hex().unwrap().into(), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Greenfield, input); + + let expected = r#"{"mode":"BROADCAST_MODE_SYNC","tx_bytes":"CqsCCpABChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnAKKjB4RjFEQjdENTI1NmQ3MjFmRTNDMTQ0RjVjMWVkNGIzQTNBOTREYzQ0NBIqMHg5ZDFkOTdhREZjZDMyNEJiZDYwM0QzODcyQkQ3OGUwNDA5ODUxMGIxGhYKA0JOQhIPMTAwMDAwMDAwMDAwMDAwCpUBCiEvZ3JlZW5maWVsZC5icmlkZ2UuTXNnVHJhbnNmZXJPdXQScAoqMHhGMURCN0Q1MjU2ZDcyMWZFM0MxNDRGNWMxZWQ0YjNBM0E5NERjNDQ0EioweEYxREI3RDUyNTZkNzIxZkUzQzE0NEY1YzFlZDRiM0EzQTk0RGM0NDQaFgoDQk5CEg8yMDAwMDAwMDAwMDAwMDASdgpYCk0KJi9jb3Ntb3MuY3J5cHRvLmV0aC5ldGhzZWNwMjU2azEuUHViS2V5EiMKIQMJu59CIR8HRnoNsfcFBOO/P8ONNZuLOI+LvTZazYFslhIFCgMIyAUYAhIaChUKA0JOQhIOMTUwMDAwMDAwMDAwMDAQuBcaQfkHNYUs2oMR0/ZauYXv8Nd/9bbrQ4w323P4GpyEO0j8IadMVE8TkmI6lIx4H/hPSbpG9wRrqhZZfzTsqSv5rDgb"}"#; + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.serialized, expected); + assert_eq!(output.signature.to_hex(), "f90735852cda8311d3f65ab985eff0d77ff5b6eb438c37db73f81a9c843b48fc21a74c544f1392623a948c781ff84f49ba46f7046baa16597f34eca92bf9ac381b"); + + let expected_signature_json = r#"[{"pub_key":{"type":"/cosmos.crypto.eth.ethsecp256k1.PubKey","value":"Awm7n0IhHwdGeg2x9wUE478/w401m4s4j4u9NlrNgWyW"},"signature":"+Qc1hSzagxHT9lq5he/w13/1tutDjDfbc/ganIQ7SPwhp0xUTxOSYjqUjHgf+E9Jukb3BGuqFll/NOypK/msOBs="}]"#; + assert_eq!(output.signature_json, expected_signature_json); +} diff --git a/rust/tw_any_coin/tests/chains/greenfield/mod.rs b/rust/tw_any_coin/tests/chains/greenfield/mod.rs new file mode 100644 index 00000000000..a14f03efddd --- /dev/null +++ b/rust/tw_any_coin/tests/chains/greenfield/mod.rs @@ -0,0 +1,22 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_proto::Greenfield::Proto; + +mod greenfield_address; +mod greenfield_compile; +mod greenfield_sign; + +const PRIVATE_KEY_15560: &str = "9066aa168c379a403becb235c15e7129c133c244e56a757ab07bc369288bcab0"; +const PUBLIC_KEY_15560: &str = "0279ef34064da10db0463c70480616ba020703ec3a45026def7bebd2082f5d6fc8"; +const PRIVATE_KEY_1686: &str = "6f96f3aa7e8052170f1864f72a9a53606ee9c0d185188266cab895512a4bcf84"; + +fn make_amount<'a>(denom: &'a str, amount: &'a str) -> Proto::Amount<'a> { + Proto::Amount { + denom: denom.into(), + amount: amount.into(), + } +} diff --git a/rust/tw_any_coin/tests/chains/mod.rs b/rust/tw_any_coin/tests/chains/mod.rs index 7cbe502a6d2..cafbd33253c 100644 --- a/rust/tw_any_coin/tests/chains/mod.rs +++ b/rust/tw_any_coin/tests/chains/mod.rs @@ -9,6 +9,7 @@ mod binance; mod bitcoin; mod cosmos; mod ethereum; +mod greenfield; mod internet_computer; mod native_evmos; mod native_injective; diff --git a/rust/tw_coin_registry/Cargo.toml b/rust/tw_coin_registry/Cargo.toml index bd289c150a1..35dd638a29d 100644 --- a/rust/tw_coin_registry/Cargo.toml +++ b/rust/tw_coin_registry/Cargo.toml @@ -16,6 +16,7 @@ tw_coin_entry = { path = "../tw_coin_entry" } tw_cosmos = { path = "../chains/tw_cosmos" } tw_ethereum = { path = "../tw_ethereum" } tw_evm = { path = "../tw_evm" } +tw_greenfield = { path = "../chains/tw_greenfield" } tw_hash = { path = "../tw_hash" } tw_internet_computer = { path = "../tw_internet_computer" } tw_keypair = { path = "../tw_keypair" } diff --git a/rust/tw_coin_registry/src/blockchain_type.rs b/rust/tw_coin_registry/src/blockchain_type.rs index 28780562cd8..b6a27712ea7 100644 --- a/rust/tw_coin_registry/src/blockchain_type.rs +++ b/rust/tw_coin_registry/src/blockchain_type.rs @@ -16,6 +16,7 @@ pub enum BlockchainType { Bitcoin, Cosmos, Ethereum, + Greenfield, InternetComputer, NativeEvmos, NativeInjective, diff --git a/rust/tw_coin_registry/src/dispatcher.rs b/rust/tw_coin_registry/src/dispatcher.rs index d6e8fed9043..29e17dcd870 100644 --- a/rust/tw_coin_registry/src/dispatcher.rs +++ b/rust/tw_coin_registry/src/dispatcher.rs @@ -16,6 +16,7 @@ use tw_coin_entry::coin_entry_ext::CoinEntryExt; use tw_cosmos::entry::CosmosEntry; use tw_ethereum::entry::EthereumEntry; use tw_evm::evm_entry::EvmEntryExt; +use tw_greenfield::entry::GreenfieldEntry; use tw_internet_computer::entry::InternetComputerEntry; use tw_native_evmos::entry::NativeEvmosEntry; use tw_native_injective::entry::NativeInjectiveEntry; @@ -31,6 +32,7 @@ const BINANCE: BinanceEntry = BinanceEntry; const BITCOIN: BitcoinEntry = BitcoinEntry; const COSMOS: CosmosEntry = CosmosEntry; const ETHEREUM: EthereumEntry = EthereumEntry; +const GREENFIELD: GreenfieldEntry = GreenfieldEntry; const INTERNET_COMPUTER: InternetComputerEntry = InternetComputerEntry; const NATIVE_EVMOS: NativeEvmosEntry = NativeEvmosEntry; const NATIVE_INJECTIVE: NativeInjectiveEntry = NativeInjectiveEntry; @@ -46,6 +48,7 @@ pub fn blockchain_dispatcher(blockchain: BlockchainType) -> RegistryResult Ok(&BITCOIN), BlockchainType::Cosmos => Ok(&COSMOS), BlockchainType::Ethereum => Ok(ÐEREUM), + BlockchainType::Greenfield => Ok(&GREENFIELD), BlockchainType::InternetComputer => Ok(&INTERNET_COMPUTER), BlockchainType::NativeEvmos => Ok(&NATIVE_EVMOS), BlockchainType::NativeInjective => Ok(&NATIVE_INJECTIVE), diff --git a/src/Cosmos/Protobuf/authz_tx.proto b/rust/tw_cosmos_sdk/Protobuf/authz_tx.proto similarity index 100% rename from src/Cosmos/Protobuf/authz_tx.proto rename to rust/tw_cosmos_sdk/Protobuf/authz_tx.proto diff --git a/src/Cosmos/Protobuf/bank_tx.proto b/rust/tw_cosmos_sdk/Protobuf/bank_tx.proto similarity index 100% rename from src/Cosmos/Protobuf/bank_tx.proto rename to rust/tw_cosmos_sdk/Protobuf/bank_tx.proto diff --git a/src/Cosmos/Protobuf/coin.proto b/rust/tw_cosmos_sdk/Protobuf/coin.proto similarity index 100% rename from src/Cosmos/Protobuf/coin.proto rename to rust/tw_cosmos_sdk/Protobuf/coin.proto diff --git a/src/Cosmos/Protobuf/cosmwasm_wasm_v1_tx.proto b/rust/tw_cosmos_sdk/Protobuf/cosmwasm_wasm_v1_tx.proto similarity index 100% rename from src/Cosmos/Protobuf/cosmwasm_wasm_v1_tx.proto rename to rust/tw_cosmos_sdk/Protobuf/cosmwasm_wasm_v1_tx.proto diff --git a/src/Cosmos/Protobuf/crypto_multisig.proto b/rust/tw_cosmos_sdk/Protobuf/crypto_multisig.proto similarity index 100% rename from src/Cosmos/Protobuf/crypto_multisig.proto rename to rust/tw_cosmos_sdk/Protobuf/crypto_multisig.proto diff --git a/src/Cosmos/Protobuf/crypto_secp256k1_keys.proto b/rust/tw_cosmos_sdk/Protobuf/crypto_secp256k1_keys.proto similarity index 100% rename from src/Cosmos/Protobuf/crypto_secp256k1_keys.proto rename to rust/tw_cosmos_sdk/Protobuf/crypto_secp256k1_keys.proto diff --git a/src/Cosmos/Protobuf/distribution_tx.proto b/rust/tw_cosmos_sdk/Protobuf/distribution_tx.proto similarity index 100% rename from src/Cosmos/Protobuf/distribution_tx.proto rename to rust/tw_cosmos_sdk/Protobuf/distribution_tx.proto diff --git a/src/Cosmos/Protobuf/ethermint_keys.proto b/rust/tw_cosmos_sdk/Protobuf/ethermint_keys.proto similarity index 100% rename from src/Cosmos/Protobuf/ethermint_keys.proto rename to rust/tw_cosmos_sdk/Protobuf/ethermint_keys.proto diff --git a/src/Cosmos/Protobuf/gov_tx.proto b/rust/tw_cosmos_sdk/Protobuf/gov_tx.proto similarity index 100% rename from src/Cosmos/Protobuf/gov_tx.proto rename to rust/tw_cosmos_sdk/Protobuf/gov_tx.proto diff --git a/src/Cosmos/Protobuf/greenfield_ethsecp256k1.proto b/rust/tw_cosmos_sdk/Protobuf/greenfield_ethsecp256k1.proto similarity index 100% rename from src/Cosmos/Protobuf/greenfield_ethsecp256k1.proto rename to rust/tw_cosmos_sdk/Protobuf/greenfield_ethsecp256k1.proto diff --git a/src/Cosmos/Protobuf/greenfield_tx.proto b/rust/tw_cosmos_sdk/Protobuf/greenfield_tx.proto similarity index 100% rename from src/Cosmos/Protobuf/greenfield_tx.proto rename to rust/tw_cosmos_sdk/Protobuf/greenfield_tx.proto diff --git a/src/Cosmos/Protobuf/ibc_applications_transfer_tx.proto b/rust/tw_cosmos_sdk/Protobuf/ibc_applications_transfer_tx.proto similarity index 100% rename from src/Cosmos/Protobuf/ibc_applications_transfer_tx.proto rename to rust/tw_cosmos_sdk/Protobuf/ibc_applications_transfer_tx.proto diff --git a/src/Cosmos/Protobuf/ibc_core_client.proto b/rust/tw_cosmos_sdk/Protobuf/ibc_core_client.proto similarity index 100% rename from src/Cosmos/Protobuf/ibc_core_client.proto rename to rust/tw_cosmos_sdk/Protobuf/ibc_core_client.proto diff --git a/src/Cosmos/Protobuf/injective_keys.proto b/rust/tw_cosmos_sdk/Protobuf/injective_keys.proto similarity index 100% rename from src/Cosmos/Protobuf/injective_keys.proto rename to rust/tw_cosmos_sdk/Protobuf/injective_keys.proto diff --git a/src/Cosmos/Protobuf/staking_tx.proto b/rust/tw_cosmos_sdk/Protobuf/staking_tx.proto similarity index 100% rename from src/Cosmos/Protobuf/staking_tx.proto rename to rust/tw_cosmos_sdk/Protobuf/staking_tx.proto diff --git a/src/Cosmos/Protobuf/stride_liquid_staking.proto b/rust/tw_cosmos_sdk/Protobuf/stride_liquid_staking.proto similarity index 100% rename from src/Cosmos/Protobuf/stride_liquid_staking.proto rename to rust/tw_cosmos_sdk/Protobuf/stride_liquid_staking.proto diff --git a/src/Cosmos/Protobuf/terra_wasm_v1beta1_tx.proto b/rust/tw_cosmos_sdk/Protobuf/terra_wasm_v1beta1_tx.proto similarity index 100% rename from src/Cosmos/Protobuf/terra_wasm_v1beta1_tx.proto rename to rust/tw_cosmos_sdk/Protobuf/terra_wasm_v1beta1_tx.proto diff --git a/src/Cosmos/Protobuf/thorchain_bank_tx.proto b/rust/tw_cosmos_sdk/Protobuf/thorchain_bank_tx.proto similarity index 100% rename from src/Cosmos/Protobuf/thorchain_bank_tx.proto rename to rust/tw_cosmos_sdk/Protobuf/thorchain_bank_tx.proto diff --git a/src/Cosmos/Protobuf/tx.proto b/rust/tw_cosmos_sdk/Protobuf/tx.proto similarity index 100% rename from src/Cosmos/Protobuf/tx.proto rename to rust/tw_cosmos_sdk/Protobuf/tx.proto diff --git a/src/Cosmos/Protobuf/tx_signing.proto b/rust/tw_cosmos_sdk/Protobuf/tx_signing.proto similarity index 100% rename from src/Cosmos/Protobuf/tx_signing.proto rename to rust/tw_cosmos_sdk/Protobuf/tx_signing.proto diff --git a/rust/tw_cosmos_sdk/build.rs b/rust/tw_cosmos_sdk/build.rs index a434de70fb2..06fdb15ce6b 100644 --- a/rust/tw_cosmos_sdk/build.rs +++ b/rust/tw_cosmos_sdk/build.rs @@ -15,12 +15,7 @@ fn main() { let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()).join("proto"); - let proto_dir = cargo_manifest_dir - .join("..") - .join("..") - .join("src") - .join("Cosmos") - .join("Protobuf"); + let proto_dir = cargo_manifest_dir.join("Protobuf"); let proto_dir_str = proto_dir.to_str().expect("Invalid proto directory path"); // Re-run this build.rs if the `proto` directory has been changed (i.e. a new file is added). println!("cargo:rerun-if-changed={}", proto_dir_str); diff --git a/rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs b/rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs index ee67300b747..6f77b43c9db 100644 --- a/rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs +++ b/rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs @@ -138,28 +138,19 @@ impl ProtobufSerializer { } fn build_fee(fee: &Fee) -> tx_proto::Fee { - let payer = fee - .payer - .as_ref() - .map(Context::Address::to_string) - .unwrap_or_default(); - let granter = fee - .granter - .as_ref() - .map(Context::Address::to_string) - .unwrap_or_default(); - tx_proto::Fee { amount: fee.amounts.iter().map(build_coin).collect(), gas_limit: fee.gas_limit, - payer, - granter, + // Ignore `payer` and `granter` even if they set. + payer: String::default(), + granter: String::default(), } } fn build_sign_mode(sign_mode: SignMode) -> signing_proto::SignMode { match sign_mode { SignMode::Direct => signing_proto::SignMode::SIGN_MODE_DIRECT, + SignMode::Other(other) => signing_proto::SignMode::from(other), } } } diff --git a/rust/tw_cosmos_sdk/src/modules/tx_builder.rs b/rust/tw_cosmos_sdk/src/modules/tx_builder.rs index da5e87a94d1..a673bc1124d 100644 --- a/rust/tw_cosmos_sdk/src/modules/tx_builder.rs +++ b/rust/tw_cosmos_sdk/src/modules/tx_builder.rs @@ -15,7 +15,7 @@ use std::marker::PhantomData; use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; -use tw_misc::traits::ToBytesVec; +use tw_misc::traits::{OptionalEmpty, ToBytesVec}; use tw_number::U256; use tw_proto::Cosmos::Proto; use tw_proto::{google, serialize}; @@ -210,7 +210,7 @@ where .map(Self::coin_from_proto) .collect::>()?; let msg = SendMessage { - custom_type_prefix: Self::custom_msg_type(&send.type_prefix), + custom_type_prefix: send.type_prefix.to_string().empty_or_some(), from_address: Address::from_str_with_coin(coin, &send.from_address)?, to_address: Address::from_str_with_coin(coin, &send.to_address)?, amount: amounts, @@ -262,7 +262,7 @@ where .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; let amount = Self::coin_from_proto(amount)?; let msg = DelegateMessage { - custom_type_prefix: Self::custom_msg_type(&delegate.type_prefix), + custom_type_prefix: delegate.type_prefix.to_string().empty_or_some(), amount, delegator_address: Address::from_str_with_coin(coin, &delegate.delegator_address)?, validator_address: Address::from_str_with_coin(coin, &delegate.validator_address)?, @@ -283,7 +283,7 @@ where let amount = Self::coin_from_proto(amount)?; let msg = UndelegateMessage { - custom_type_prefix: Self::custom_msg_type(&undelegate.type_prefix), + custom_type_prefix: undelegate.type_prefix.to_string().empty_or_some(), amount, delegator_address: Address::from_str_with_coin(coin, &undelegate.delegator_address)?, validator_address: Address::from_str_with_coin(coin, &undelegate.validator_address)?, @@ -298,7 +298,7 @@ where use crate::transaction::message::cosmos_staking_message::WithdrawDelegationRewardMessage; let msg = WithdrawDelegationRewardMessage { - custom_type_prefix: Self::custom_msg_type(&withdraw.type_prefix), + custom_type_prefix: withdraw.type_prefix.to_string().empty_or_some(), delegator_address: Address::from_str_with_coin(coin, &withdraw.delegator_address)?, validator_address: Address::from_str_with_coin(coin, &withdraw.validator_address)?, }; @@ -312,7 +312,7 @@ where use crate::transaction::message::cosmos_staking_message::SetWithdrawAddressMessage; let msg = SetWithdrawAddressMessage { - custom_type_prefix: Self::custom_msg_type(&set.type_prefix), + custom_type_prefix: set.type_prefix.to_string().empty_or_some(), delegator_address: Address::from_str_with_coin(coin, &set.delegator_address)?, withdraw_address: Address::from_str_with_coin(coin, &set.withdraw_address)?, }; @@ -336,7 +336,7 @@ where Address::from_str_with_coin(coin, &redelegate.validator_dst_address)?; let msg = BeginRedelegateMessage { - custom_type_prefix: Self::custom_msg_type(&redelegate.type_prefix), + custom_type_prefix: redelegate.type_prefix.to_string().empty_or_some(), amount, delegator_address: Address::from_str_with_coin(coin, &redelegate.delegator_address)?, validator_src_address, @@ -644,12 +644,4 @@ where }; Ok(msg.into_boxed()) } - - fn custom_msg_type(type_prefix: &str) -> Option { - if type_prefix.is_empty() { - None - } else { - Some(type_prefix.to_string()) - } - } } diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs index dddfb065839..85c3a0a4470 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs @@ -16,7 +16,7 @@ use tw_proto::to_any; const DEFAULT_JSON_SEND_TYPE: &str = "cosmos-sdk/MsgSend"; /// cosmos-sdk/MsgSend -#[derive(Serialize)] +#[derive(Clone, Serialize)] pub struct SendMessage { #[serde(skip)] pub custom_type_prefix: Option, diff --git a/rust/tw_cosmos_sdk/src/transaction/mod.rs b/rust/tw_cosmos_sdk/src/transaction/mod.rs index 0d3d02812de..c54cace61e2 100644 --- a/rust/tw_cosmos_sdk/src/transaction/mod.rs +++ b/rust/tw_cosmos_sdk/src/transaction/mod.rs @@ -17,8 +17,10 @@ use message::CosmosMessageBox; #[derive(Clone, Copy)] pub enum SignMode { Direct, + Other(i32), } +#[derive(Clone, Serialize)] pub struct Fee
{ pub amounts: Vec, pub gas_limit: u64, diff --git a/rust/tw_evm/src/message/eip712/eip712_message.rs b/rust/tw_evm/src/message/eip712/eip712_message.rs index d31d01608dc..be1dff96a3a 100644 --- a/rust/tw_evm/src/message/eip712/eip712_message.rs +++ b/rust/tw_evm/src/message/eip712/eip712_message.rs @@ -8,12 +8,12 @@ use crate::abi::encode::encode_tokens; use crate::abi::non_empty_array::NonEmptyBytes; use crate::abi::token::Token; use crate::address::Address; -use crate::message::eip712::property::{Property, PropertyType}; +use crate::message::eip712::message_types::CustomTypes; +use crate::message::eip712::property::PropertyType; use crate::message::{EthMessage, MessageSigningError, MessageSigningResult}; use itertools::Itertools; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use serde_json::Value as Json; -use std::collections::HashMap; use std::str::FromStr; use tw_encoding::hex::{self, DecodeHex}; use tw_hash::sha3::keccak256; @@ -27,15 +27,13 @@ const PREFIX: &[u8; 2] = b"\x19\x01"; /// cbindgen:ignore const EIP712_DOMAIN: &str = "EIP712Domain"; -type CustomTypes = HashMap>; - -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Serialize)] pub struct Eip712Message { - types: CustomTypes, - domain: Json, + pub types: CustomTypes, + pub domain: Json, #[serde(rename = "primaryType")] - primary_type: String, - message: Json, + pub primary_type: String, + pub message: Json, } impl Eip712Message { diff --git a/rust/tw_evm/src/message/eip712/message_types.rs b/rust/tw_evm/src/message/eip712/message_types.rs new file mode 100644 index 00000000000..9a4971402fc --- /dev/null +++ b/rust/tw_evm/src/message/eip712/message_types.rs @@ -0,0 +1,55 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::message::eip712::property::{Property, PropertyType}; +use std::collections::hash_map::Entry; +use std::collections::HashMap; + +pub type CustomTypes = HashMap>; + +pub trait DeclareCustomType { + /// A custom type may also depend on other custom types. + fn declare_custom_type(&self, builder: &mut MessageTypesBuilder); +} + +#[derive(Default)] +pub struct MessageTypesBuilder { + types: CustomTypes, +} + +impl MessageTypesBuilder { + pub fn add_custom_type(&mut self, type_name: String) -> Option { + match self.types.entry(type_name) { + Entry::Vacant(entry) => { + let type_properties = entry.insert(Vec::default()); + Some(CustomTypeBuilder { type_properties }) + }, + Entry::Occupied(_) => None, + } + } + + pub fn build(self) -> CustomTypes { + self.types + } +} + +pub struct CustomTypeBuilder<'a> { + type_properties: &'a mut Vec, +} + +impl<'a> CustomTypeBuilder<'a> { + pub fn add_property(&mut self, name: &str, property_type: PropertyType) -> &mut Self { + self.type_properties.push(Property { + name: name.to_string(), + property_type: property_type.to_string(), + }); + self + } + + pub fn sort_by_names(&mut self) { + self.type_properties.sort_by(|x, y| x.name.cmp(&y.name)); + } +} diff --git a/rust/tw_evm/src/message/eip712/mod.rs b/rust/tw_evm/src/message/eip712/mod.rs index 78ee85335ad..74f7d3c885b 100644 --- a/rust/tw_evm/src/message/eip712/mod.rs +++ b/rust/tw_evm/src/message/eip712/mod.rs @@ -5,4 +5,5 @@ // file LICENSE at the root of the source code distribution tree. pub mod eip712_message; +pub mod message_types; pub mod property; diff --git a/rust/tw_evm/src/message/eip712/property.rs b/rust/tw_evm/src/message/eip712/property.rs index d61340873ad..343516ea042 100644 --- a/rust/tw_evm/src/message/eip712/property.rs +++ b/rust/tw_evm/src/message/eip712/property.rs @@ -10,10 +10,11 @@ use crate::abi::param_type::reader::Reader; use crate::abi::uint::UintBits; use crate::abi::{AbiError, AbiErrorKind, AbiResult}; use crate::message::MessageSigningError; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; +use std::fmt; use std::str::FromStr; -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct Property { pub name: String, #[serde(rename = "type")] @@ -88,6 +89,27 @@ impl TypeConstructor for PropertyType { } } +impl fmt::Display for PropertyType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + PropertyType::Bool => write!(f, "bool"), + PropertyType::String => write!(f, "string"), + PropertyType::Int => write!(f, "int256"), + PropertyType::Uint => write!(f, "uint256"), + PropertyType::Address => write!(f, "address"), + PropertyType::FixBytes { len } => write!(f, "bytes{len}"), + PropertyType::Bytes => write!(f, "bytes"), + PropertyType::Custom(custom) => write!(f, "{custom}"), + PropertyType::Array(element_type) => { + write!(f, "{element_type}[]") + }, + PropertyType::FixArray { len, element_type } => { + write!(f, "{element_type}[{len}]") + }, + } + } +} + impl FromStr for PropertyType { type Err = MessageSigningError; diff --git a/rust/tw_evm/src/message/signature.rs b/rust/tw_evm/src/message/signature.rs index 847df7f4b4a..74802cd5037 100644 --- a/rust/tw_evm/src/message/signature.rs +++ b/rust/tw_evm/src/message/signature.rs @@ -17,9 +17,10 @@ use tw_number::U256; pub enum SignatureType { Standard, Legacy, - Eip155 { chain_id: u64 }, + Eip155 { chain_id: U256 }, } +#[derive(Clone)] pub struct MessageSignature { r: H256, s: H256, @@ -37,10 +38,8 @@ impl MessageSignature { SignatureType::Legacy => { legacy_replay_protection(sign.v()).map_err(|_| KeyPairError::InvalidSignature)? }, - SignatureType::Eip155 { chain_id } => { - eip155_replay_protection(U256::from(chain_id), sign.v()) - .map_err(|_| KeyPairError::InvalidSignature)? - }, + SignatureType::Eip155 { chain_id } => eip155_replay_protection(chain_id, sign.v()) + .map_err(|_| KeyPairError::InvalidSignature)?, }; Ok(MessageSignature { r: sign.r(), diff --git a/rust/tw_evm/src/modules/message_signer.rs b/rust/tw_evm/src/modules/message_signer.rs index e0f76f9e116..b15d95698ea 100644 --- a/rust/tw_evm/src/modules/message_signer.rs +++ b/rust/tw_evm/src/modules/message_signer.rs @@ -140,7 +140,7 @@ impl EthMessageSigner { }, Proto::MessageType::MessageType_eip155 | Proto::MessageType::MessageType_typed_eip155 => { - let chain_id = maybe_chain_id.unwrap_or_default().chain_id; + let chain_id = U256::from(maybe_chain_id.unwrap_or_default().chain_id); SignatureType::Eip155 { chain_id } }, } diff --git a/rust/tw_misc/src/test_utils/json.rs b/rust/tw_misc/src/test_utils/json.rs index bcab8c3f6d3..6b4969362e4 100644 --- a/rust/tw_misc/src/test_utils/json.rs +++ b/rust/tw_misc/src/test_utils/json.rs @@ -25,6 +25,13 @@ impl<'a> ToJson for Cow<'a, str> { } } +impl ToJson for String { + #[track_caller] + fn to_json(&self) -> Json { + self.as_str().to_json() + } +} + impl<'a> ToJson for &'a str { #[track_caller] fn to_json(&self) -> Json { diff --git a/rust/tw_misc/src/traits.rs b/rust/tw_misc/src/traits.rs index 36fb534853a..cf1ba9a1e80 100644 --- a/rust/tw_misc/src/traits.rs +++ b/rust/tw_misc/src/traits.rs @@ -42,3 +42,17 @@ impl IntoOption for Option { pub trait FromSlice: for<'a> TryFrom<&'a [u8]> {} impl FromSlice for T where for<'a> T: TryFrom<&'a [u8]> {} + +pub trait OptionalEmpty: Sized { + fn empty_or_some(self) -> Option; +} + +impl OptionalEmpty for String { + fn empty_or_some(self) -> Option { + if self.is_empty() { + None + } else { + Some(self) + } + } +} diff --git a/rust/tw_proto/src/lib.rs b/rust/tw_proto/src/lib.rs index 45eb41fac60..94f2db92860 100644 --- a/rust/tw_proto/src/lib.rs +++ b/rust/tw_proto/src/lib.rs @@ -52,6 +52,10 @@ where google::protobuf::Any { type_url, value } } +pub fn type_url() -> String { + format!("/{}", T::PATH) +} + /// There is no way to create an instance of the `NoMessage` enum as it doesn't has variants. pub enum NoMessage {} diff --git a/src/Cosmos/Protobuf/.clang-tidy b/src/Cosmos/Protobuf/.clang-tidy deleted file mode 100644 index 2c22f7387dd..00000000000 --- a/src/Cosmos/Protobuf/.clang-tidy +++ /dev/null @@ -1,6 +0,0 @@ ---- -InheritParentConfig: false -Checks: '-*,misc-definitions-in-headers' -CheckOptions: - - { key: HeaderFileExtensions, value: "x" } -... diff --git a/src/Cosmos/Protobuf/.gitignore b/src/Cosmos/Protobuf/.gitignore deleted file mode 100644 index c96d61208c0..00000000000 --- a/src/Cosmos/Protobuf/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.cc -*.h - diff --git a/src/Greenfield/Constants.h b/src/Greenfield/Constants.h deleted file mode 100644 index f2af62674dd..00000000000 --- a/src/Greenfield/Constants.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include - -#pragma once - -namespace TW::Greenfield { - -static constexpr uint64_t TIMEOUT_HEIGHT = 0; -static constexpr auto* TIMEOUT_HEIGHT_STR = "0"; -static constexpr auto* FEE_GRANTER = ""; -static constexpr auto* MSG_SEND_TYPE = "/cosmos.bank.v1beta1.MsgSend"; -static constexpr auto* MSG_TRANSFER_OUT_TYPE = "/greenfield.bridge.MsgTransferOut"; - -} // namespace TW::Greenfield diff --git a/src/Greenfield/Entry.cpp b/src/Greenfield/Entry.cpp deleted file mode 100644 index 4e1d2941b72..00000000000 --- a/src/Greenfield/Entry.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Entry.h" - -#include "Ethereum/Address.h" -#include "Signer.h" - -namespace TW::Greenfield { - -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - return Ethereum::Address::isValid(address); -} - -std::string Entry::normalizeAddress([[maybe_unused]] TWCoinType coin, const std::string& address) const { - // normalized with EIP55 checksum - return Ethereum::Address(address).string(); -} - -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - if (publicKey.type != TWPublicKeyTypeSECP256k1Extended) { - return Ethereum::Address(publicKey.extended()).string(); - } - return Ethereum::Address(publicKey).string(); -} - -Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { - const auto addr = Ethereum::Address(address); - return {addr.bytes.begin(), addr.bytes.end()}; -} - -void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { - signTemplate(dataIn, dataOut); -} - -TW::Data Entry::preImageHashes([[maybe_unused]] TWCoinType coin, const Data& txInputData) const { - return txCompilerTemplate( - txInputData, [](const auto& input, auto& output) { - output = Signer::preImageHashes(input); - }); -} - -void Entry::compile([[maybe_unused]] TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const { - dataOut = txCompilerSingleTemplate( - txInputData, signatures, publicKeys, - [](const auto& input, auto& output, const auto& signature, const auto& publicKey) { - output = Signer::compile(input, publicKey, signature); - }); -} - -} // namespace TW::Greenfield diff --git a/src/Greenfield/Entry.h b/src/Greenfield/Entry.h index 7613878d0ca..c54d843fc99 100644 --- a/src/Greenfield/Entry.h +++ b/src/Greenfield/Entry.h @@ -6,22 +6,13 @@ #pragma once -#include "../CoinEntry.h" +#include "rust/RustCoinEntry.h" namespace TW::Greenfield { /// Entry point for implementation of Greenfield coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry final : public CoinEntry { -public: - bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string normalizeAddress(TWCoinType coin, const std::string& address) const final; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; - Data addressToData(TWCoinType coin, const std::string& address) const final; - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - - Data preImageHashes(TWCoinType coin, const Data& txInputData) const; - void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const; +struct Entry final : public Rust::RustCoinEntry { }; } // namespace TW::Greenfield diff --git a/src/Greenfield/ProtobufSerialization.cpp b/src/Greenfield/ProtobufSerialization.cpp deleted file mode 100644 index 45fdd59647f..00000000000 --- a/src/Greenfield/ProtobufSerialization.cpp +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "ProtobufSerialization.h" - -#include "Base64.h" -#include "Constants.h" -#include "Cosmos/Protobuf/bank_tx.pb.h" -#include "Cosmos/Protobuf/greenfield_ethsecp256k1.pb.h" -#include "Cosmos/Protobuf/greenfield_tx.pb.h" -#include "Cosmos/Protobuf/tx.pb.h" -#include "PrivateKey.h" - -namespace TW::Greenfield { - -static constexpr auto ProtobufAnyNamespacePrefix = ""; // to override default 'type.googleapis.com' - -using Any = google::protobuf::Any; - -static std::string broadcastMode(Proto::BroadcastMode mode) { - switch (mode) { - case Proto::BroadcastMode::ASYNC: - return "BROADCAST_MODE_ASYNC"; - case Proto::BroadcastMode::SYNC: - default: return "BROADCAST_MODE_SYNC"; - } -} - -static json broadcastJSON(std::string data, Proto::BroadcastMode mode) { - return { - {"tx_bytes", data}, - {"mode", broadcastMode(mode)} - }; -} - -static cosmos::base::v1beta1::Coin convertCoin(const Proto::Amount& amount) { - cosmos::base::v1beta1::Coin coin; - coin.set_denom(amount.denom()); - coin.set_amount(amount.amount()); - return coin; -} - -static SigningResult convertMessage(const Proto::Message& msg) { - Any any; - - switch (msg.message_oneof_case()) { - case Proto::Message::kSendCoinsMessage: { - const auto& send = msg.send_coins_message(); - - auto msgSend = cosmos::bank::v1beta1::MsgSend(); - msgSend.set_from_address(send.from_address()); - msgSend.set_to_address(send.to_address()); - - for (auto i = 0; i < send.amounts_size(); ++i) { - *msgSend.add_amount() = convertCoin(send.amounts(i)); - } - any.PackFrom(msgSend, ProtobufAnyNamespacePrefix); - break; - } - case Proto::Message::kBridgeTransferOut: { - const auto& transferOut = msg.bridge_transfer_out(); - - auto msgTransferOut = greenfield::bridge::MsgTransferOut(); - msgTransferOut.set_from(transferOut.from_address()); - msgTransferOut.set_to(transferOut.to_address()); - *msgTransferOut.mutable_amount() = convertCoin(transferOut.amount()); - - any.PackFrom(msgTransferOut, ProtobufAnyNamespacePrefix); - break; - } - default: { - return SigningResult::failure(Common::Proto::SigningError::Error_invalid_params); - } - } - - return SigningResult::success(std::move(any)); -} - -SigningResult ProtobufSerialization::encodeTxProtobuf(const Proto::SigningInput& input, const PublicKey& publicKey, const Data& signature) { - const auto txBodyResult = encodeTxBody(input); - const auto serializedAuthInfo = encodeAuthInfo(input, publicKey); - - if (txBodyResult.isFailure()) { - return SigningResult::failure(txBodyResult.error()); - } - const auto serializedTxBody = txBodyResult.payload(); - - auto txRaw = cosmos::tx::v1beta1::TxRaw(); - txRaw.set_body_bytes(serializedTxBody.data(), serializedTxBody.size()); - txRaw.set_auth_info_bytes(serializedAuthInfo.data(), serializedAuthInfo.size()); - *txRaw.add_signatures() = std::string(signature.begin(), signature.end()); - - const auto txRawData = data(txRaw.SerializeAsString()); - auto txJson = broadcastJSON(Base64::encode(txRawData), input.mode()); - - return SigningResult::success(std::move(txJson)); -} - -SigningResult ProtobufSerialization::encodeTxBody(const Proto::SigningInput& input) { - cosmos::tx::v1beta1::TxBody txBody; - - // At this moment, we support only one message. - if (input.messages_size() != 1) { - return SigningResult::failure(Common::Proto::SigningError::Error_invalid_params); - } - const auto msgAny = convertMessage(input.messages(0)); - if (msgAny.isFailure()) { - return SigningResult::failure(msgAny.error()); - } - - *txBody.add_messages() = msgAny.payload(); - txBody.set_memo(input.memo()); - txBody.set_timeout_height(TIMEOUT_HEIGHT); - - return SigningResult::success(data(txBody.SerializeAsString())); -} - -Data ProtobufSerialization::encodeAuthInfo(const Proto::SigningInput& input, const PublicKey& publicKey) { - // AuthInfo - auto authInfo = cosmos::tx::v1beta1::AuthInfo(); - auto* signerInfo = authInfo.add_signer_infos(); - - // At this moment, we support Eip712 signing mode only. - signerInfo->mutable_mode_info()->mutable_single()->set_mode(cosmos::signing::v1beta1::SIGN_MODE_EIP_712); - signerInfo->set_sequence(input.sequence()); - - // `cosmos::crypto::eth::ethsecp256k1` is used only with `SIGN_MODE_EIP_712`. - auto pubKey = cosmos::crypto::eth::ethsecp256k1::PubKey(); - pubKey.set_key(publicKey.bytes.data(), publicKey.bytes.size()); - signerInfo->mutable_public_key()->PackFrom(pubKey, ProtobufAnyNamespacePrefix); - - auto& fee = *authInfo.mutable_fee(); - for (auto i = 0; i < input.fee().amounts_size(); ++i) { - *fee.add_amount() = convertCoin(input.fee().amounts(i)); - } - - fee.set_gas_limit(input.fee().gas()); - fee.set_payer(""); - fee.set_granter(""); - // tip is omitted - return data(authInfo.SerializeAsString()); -} - -} // namespace TW::Greenfield diff --git a/src/Greenfield/ProtobufSerialization.h b/src/Greenfield/ProtobufSerialization.h deleted file mode 100644 index 63cae96589b..00000000000 --- a/src/Greenfield/ProtobufSerialization.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "Data.h" -#include "proto/Greenfield.pb.h" -#include "proto/Common.pb.h" -#include "PublicKey.h" -#include "Result.h" - -#include - -namespace TW::Greenfield { - -using json = nlohmann::json; -template -using SigningResult = Result; - -struct ProtobufSerialization { - // Returns a JSON `{ "mode": "$MODE", "tx_bytes": "$TX_BASE64" }`, - // where `TX_BASE64` is a serialized Protobuf packaged encoded into base64. - static SigningResult encodeTxProtobuf(const Proto::SigningInput& input, const PublicKey& publicKey, const Data& signature); - - // Returns a serialized `cosmos::TxBody` Protobuf package. - static SigningResult encodeTxBody(const Proto::SigningInput& input); - - // Returns a serialized `cosmos::AuthInfo` Protobuf package. - static Data encodeAuthInfo(const Proto::SigningInput& input, const PublicKey& publicKey); -}; - -} // namespace TW::Greenfield diff --git a/src/Greenfield/Signer.cpp b/src/Greenfield/Signer.cpp deleted file mode 100644 index 987fdfe1fd9..00000000000 --- a/src/Greenfield/Signer.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Signer.h" - -#include "ProtobufSerialization.h" -#include "PublicKey.h" -#include "SignerEip712.h" - -namespace TW::Greenfield { - -Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) { - Proto::SigningOutput output; - - const auto signatureResult = SignerEip712::sign(input); - if (signatureResult.isFailure()) { - output.set_error(signatureResult.error()); - output.set_error_message(Common::Proto::SigningError_Name(signatureResult.error())); - return output; - } - - const auto signature = signatureResult.payload(); - - PrivateKey privateKey(data(input.private_key())); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - - // At this moment, we support Protobuf serialization only. - auto jsonResult = ProtobufSerialization::encodeTxProtobuf(input, publicKey, signature); - if (jsonResult.isFailure()) { - output.set_error(jsonResult.error()); - output.set_error_message(Common::Proto::SigningError_Name(jsonResult.error())); - return output; - } - - output.set_signature(signature.data(), signature.size()); - output.set_serialized(jsonResult.payload().dump()); - return output; -} - -TxCompiler::Proto::PreSigningOutput Signer::preImageHashes(const Proto::SigningInput& input) { - TxCompiler::Proto::PreSigningOutput output; - - // At this moment, we support Eip712 signing mode only. - const auto preImageResult = SignerEip712::preImageHash(input); - if (preImageResult.isFailure()) { - output.set_error(preImageResult.error()); - output.set_error_message(Common::Proto::SigningError_Name(preImageResult.error())); - return output; - } - - const auto preImage = preImageResult.payload(); - auto preImageData = data(preImage.typedData.dump()); - - output.set_data_hash(preImage.typedDataHash.data(), preImage.typedDataHash.size()); - output.set_data(preImageData.data(), preImageData.size()); - return output; -} - -Proto::SigningOutput Signer::compile(const Proto::SigningInput& input, const PublicKey& publicKey, const Data& signature) { - Proto::SigningOutput output; - - Data sign = signature; - if (input.signing_mode() == Proto::SigningMode::Eip712) { - // Prepare the signature to be compiled. - SignerEip712::prepareSignature(sign); - } - - // At this moment, we support Protobuf serialization only. - auto jsonResult = ProtobufSerialization::encodeTxProtobuf(input, publicKey, sign); - if (jsonResult.isFailure()) { - output.set_error(jsonResult.error()); - output.set_error_message(Common::Proto::SigningError_Name(jsonResult.error())); - return output; - } - - output.set_signature(sign.data(), sign.size()); - output.set_serialized(jsonResult.payload().dump()); - return output; -} - -} // namespace TW::Greenfield diff --git a/src/Greenfield/Signer.h b/src/Greenfield/Signer.h deleted file mode 100644 index 982705e4550..00000000000 --- a/src/Greenfield/Signer.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "Data.h" -#include "PrivateKey.h" -#include "proto/Greenfield.pb.h" -#include "proto/TransactionCompiler.pb.h" - -namespace TW::Greenfield { - -/// Helper class that performs Greenfield transaction signing. -class Signer { -public: - /// Hide default constructor - Signer() = delete; - - /// Signs a Proto::SigningInput transaction - static Proto::SigningOutput sign(const Proto::SigningInput& input); - - /// Collect pre-image hashes to be signed - static TxCompiler::Proto::PreSigningOutput preImageHashes(const Proto::SigningInput& input); - - static Proto::SigningOutput compile(const Proto::SigningInput& input, const PublicKey& publicKey, const Data& signature); -}; - -} // namespace TW::Greenfield diff --git a/src/Greenfield/SignerEip712.cpp b/src/Greenfield/SignerEip712.cpp deleted file mode 100644 index ce803cda6f6..00000000000 --- a/src/Greenfield/SignerEip712.cpp +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "SignerEip712.h" - -#include "Constants.h" -#include "Ethereum/MessageSigner.h" -#include "HexCoding.h" - -#include - -namespace TW::Greenfield { - -namespace internal::types { - -using TypesMap = std::map>; - -json namedParam(const char *name, const char *type) { - return json { - {"name", name}, - {"type", type} - }; -} - -// https://github.com/bnb-chain/greenfield-cosmos-sdk/blob/b48770f5e210b28536f92734b6228913666d4da1/x/auth/tx/eip712.go#L119-L160 -json makeEip712Types(const TypesMap& msgTypes) { - auto types = json { - {"EIP712Domain", json::array({ - namedParam("chainId", "uint256"), - namedParam("name", "string"), - namedParam("salt", "string"), - namedParam("verifyingContract", "string"), - namedParam("version", "string") - })}, - {"Coin", json::array({ - namedParam("amount", "uint256"), - namedParam("denom", "string") - })}, - {"Fee", json::array({ - namedParam("amount", "Coin[]"), - namedParam("gas_limit", "uint256"), - namedParam("granter", "string"), - namedParam("payer", "string") - })}, - }; - - for (const auto& [msgTypeName, msgType] : msgTypes) { - types[msgTypeName] = msgType; - } - return types; -} - -// `TypeMsg1Amount` and `Msg1` type names are chosen automatically at the function: -// https://github.com/bnb-chain/greenfield-cosmos-sdk/blob/master/x/auth/tx/eip712.go#L90 -// Please note that all parameters repeat the same scheme as `cosmos.bank.v1beta1.MsgSend`. -// -// Use `https://dcellar.io/` with MetaMask to get proper names of types and -json msgSendTypes() { - // `MsgSend` specific types. - TypesMap msgTypes = { - // `TypeMsg1Amount` type represents `cosmos.bank.v1beta1.MsgSend.amount`. - {"TypeMsg1Amount", json::array({ - namedParam("amount", "string"), - namedParam("denom", "string"), - })}, - {"Msg1", json::array({ - namedParam("amount", "TypeMsg1Amount[]"), - namedParam("from_address", "string"), - namedParam("to_address", "string"), - namedParam("type", "string") - })}, - {"Tx", json::array({ - namedParam("account_number", "uint256"), - namedParam("chain_id", "uint256"), - namedParam("fee", "Fee"), - namedParam("memo", "string"), - namedParam("msg1", "Msg1"), - namedParam("sequence", "uint256"), - namedParam("timeout_height", "uint256") - })} - }; - - return makeEip712Types(msgTypes); -} - -// `TypeMsg1Amount` and `Msg1` type names are chosen automatically at the function: -// https://github.com/bnb-chain/greenfield-cosmos-sdk/blob/master/x/auth/tx/eip712.go#L90 -// Please note that all parameters repeat the same scheme as `greenfield.bridge.MsgTransferOut`. -// -// Use `https://dcellar.io/` with MetaMask to get proper names of types and -json msgTransferOutTypes() { - // `MsgSend` specific types. - TypesMap msgTypes = { - // `TypeMsg1Amount` type represents `cosmos.bank.v1beta1.MsgSend.amount`. - {"TypeMsg1Amount", json::array({ - namedParam("amount", "string"), - namedParam("denom", "string"), - })}, - {"Msg1", json::array({ - namedParam("amount", "TypeMsg1Amount"), - namedParam("from", "string"), - namedParam("to", "string"), - namedParam("type", "string") - })}, - {"Tx", json::array({ - namedParam("account_number", "uint256"), - namedParam("chain_id", "uint256"), - namedParam("fee", "Fee"), - namedParam("memo", "string"), - namedParam("msg1", "Msg1"), - namedParam("sequence", "uint256"), - namedParam("timeout_height", "uint256") - })} - }; - - return makeEip712Types(msgTypes); -} - -} // namespace internal::types - -json feeToJsonData(const Proto::SigningInput& input, const std::string& feePayer) { - auto feeAmounts = json::array(); - for (const auto& fAmount : input.fee().amounts()) { - feeAmounts.push_back(json{ - {"amount", fAmount.amount()}, - {"denom", fAmount.denom()} - }); - } - - return json{ - {"amount", feeAmounts}, - {"gas_limit", std::to_string(input.fee().gas())}, - {"granter", FEE_GRANTER}, - {"payer", feePayer}, - }; -} - -// Returns a JSON data of the `EIP712Domain` type. -// https://github.com/bnb-chain/greenfield-cosmos-sdk/blob/b48770f5e210b28536f92734b6228913666d4da1/x/auth/tx/eip712.go#L35-L40 -json domainDataJson(const std::string& chainId) { - return json{ - {"name", "Greenfield Tx"}, - {"version", "1.0.0"}, - {"chainId", chainId}, - {"verifyingContract", "greenfield"}, - {"salt", "0"} - }; -} - -// Returns a JSON data of the `EIP712Domain` type using `MsgSend` transaction. -json SignerEip712::wrapMsgSendToTypedData(const Proto::SigningInput& input, const Proto::Message_Send& msgSendProto) { - auto sendAmounts = json::array(); - for (const auto& sAmount : msgSendProto.amounts()) { - sendAmounts.push_back(json{ - {"amount", sAmount.amount()}, - {"denom", sAmount.denom()}, - }); - } - - std::string typePrefix = MSG_SEND_TYPE; - if (!msgSendProto.type_prefix().empty()) { - typePrefix = msgSendProto.type_prefix(); - } - - return json{ - {"types", internal::types::msgSendTypes()}, - {"primaryType", "Tx"}, - {"domain", domainDataJson(input.eth_chain_id())}, - {"message", json{ - {"account_number", std::to_string(input.account_number())}, - {"chain_id", input.eth_chain_id()}, - {"fee", feeToJsonData(input, msgSendProto.from_address())}, - {"memo", input.memo()}, - {"msg1", json{ - {"amount", sendAmounts}, - {"from_address", msgSendProto.from_address()}, - {"to_address", msgSendProto.to_address()}, - {"type", MSG_SEND_TYPE} - }}, - {"sequence", std::to_string(input.sequence())}, - {"timeout_height", TIMEOUT_HEIGHT_STR} - }} - }; -} - -// Returns a JSON data of the `EIP712Domain` type using `MsgSend` transaction. -json SignerEip712::wrapMsgTransferOutToTypedData(const Proto::SigningInput& input, const Proto::Message_BridgeTransferOut& msgTransferOut) { - std::string typePrefix = MSG_TRANSFER_OUT_TYPE; - if (!msgTransferOut.type_prefix().empty()) { - typePrefix = msgTransferOut.type_prefix(); - } - - return json{ - {"types", internal::types::msgTransferOutTypes()}, - {"primaryType", "Tx"}, - {"domain", domainDataJson(input.eth_chain_id())}, - {"message", json{ - {"account_number", std::to_string(input.account_number())}, - {"chain_id", input.eth_chain_id()}, - {"fee", feeToJsonData(input, msgTransferOut.from_address())}, - {"memo", input.memo()}, - {"msg1", json{ - {"amount", json{ - {"amount", msgTransferOut.amount().amount()}, - {"denom", msgTransferOut.amount().denom()} - }}, - {"from", msgTransferOut.from_address()}, - {"to", msgTransferOut.to_address()}, - {"type", typePrefix} - }}, - {"sequence", std::to_string(input.sequence())}, - {"timeout_height", TIMEOUT_HEIGHT_STR} - }} - }; -} - -SigningResult SignerEip712::wrapTxToTypedData(const Proto::SigningInput& input) { - if (input.messages_size() != 1) { - return SigningResult::failure(Common::Proto::SigningError::Error_invalid_params); - } - - switch(input.messages(0).message_oneof_case()) { - case Proto::Message::kBridgeTransferOut: { - const auto &msgTransferOut = input.messages(0).bridge_transfer_out(); - return SigningResult::success(wrapMsgTransferOutToTypedData(input, msgTransferOut)); - } - case Proto::Message::kSendCoinsMessage: - default: { - const auto& msgSendProto = input.messages(0).send_coins_message(); - return SigningResult::success(wrapMsgSendToTypedData(input, msgSendProto)); - } - } -} - -SigningResult SignerEip712::preImageHash(const Proto::SigningInput& input) { - const auto txTypedDataResult = wrapTxToTypedData(input); - if (txTypedDataResult.isFailure()) { - return SigningResult::failure(txTypedDataResult.error()); - } - - const auto txTypedData = txTypedDataResult.payload(); - const auto txTypedDataHash = Ethereum::MessageSigner::typedDataPreImageHash(txTypedData.dump()); - return SigningResult::success({.typedData = txTypedData, .typedDataHash = txTypedDataHash}); -} - -SigningResult SignerEip712::sign(const Proto::SigningInput& input) { - const PrivateKey privateKey(data(input.private_key())); - const auto txTypedDataResult = wrapTxToTypedData(input); - if (txTypedDataResult.isFailure()) { - return SigningResult::failure(txTypedDataResult.error()); - } - const auto txTypedData = txTypedDataResult.payload().dump(); - const auto chainId = std::stoull(input.eth_chain_id()); - - const auto signatureStr = Ethereum::MessageSigner::signTypedData(privateKey, txTypedData, Ethereum::MessageType::Legacy, chainId); - return SigningResult::success(parse_hex(signatureStr)); -} - -void SignerEip712::prepareSignature(Data& signature) { - Ethereum::MessageSigner::prepareSignature(signature, Ethereum::MessageType::Legacy); -} - -} // namespace TW::Greenfield diff --git a/src/Greenfield/SignerEip712.h b/src/Greenfield/SignerEip712.h deleted file mode 100644 index f36444b3aa2..00000000000 --- a/src/Greenfield/SignerEip712.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "Data.h" -#include "proto/Greenfield.pb.h" -#include "Result.h" - -#include - -namespace TW::Greenfield { - -using json = nlohmann::json; -template -using SigningResult = Result; - -struct Eip712PreImage { - json typedData; - Data typedDataHash; -}; - -struct SignerEip712 { - ~SignerEip712() = delete; - - /// Signs a Proto::SigningInput transaction as EIP712. - /// Returns an rsv signature. - static SigningResult sign(const Proto::SigningInput& input); - - /// Returns a pre-image hash that needs to be signed. - static SigningResult preImageHash(const Proto::SigningInput& input); - - /// Packs the Tx input in a EIP712 object. - static SigningResult wrapTxToTypedData(const Proto::SigningInput& input); - - /// Packs the `MsgSend` Tx input in a EIP712 object. - static json wrapMsgSendToTypedData(const Proto::SigningInput& input, const Proto::Message_Send& msgSendProto); - - /// Packs the `MsgTransferOut` Tx input in a EIP712 object. - static json wrapMsgTransferOutToTypedData(const Proto::SigningInput& input, const Proto::Message_BridgeTransferOut& msgTransferOut); - - /// Prepares the given `signature` to make it Ethereum compatible. - static void prepareSignature(Data& signature); -}; - -} // namespace TW::Greenfield diff --git a/tests/chains/Cosmos/SignerTests.cpp b/tests/chains/Cosmos/SignerTests.cpp index 20ec6a027a7..54b5fbb9344 100644 --- a/tests/chains/Cosmos/SignerTests.cpp +++ b/tests/chains/Cosmos/SignerTests.cpp @@ -11,8 +11,6 @@ #include "Cosmos/Address.h" #include "TrustWalletCore/TWAnySigner.h" #include "TestUtilities.h" -#include "Cosmos/Protobuf/bank_tx.pb.h" -#include "Cosmos/Protobuf/coin.pb.h" #include #include @@ -291,21 +289,11 @@ TEST(CosmosSigner, SignDirect1) { } TEST(CosmosSigner, SignDirect_0a90010a) { + // MsgSend: + // from: cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6 + // to: cosmos1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5lzv7xu + // amount: 1234567 ucosm const auto bodyBytes = parse_hex("0a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d120731323334353637"); - { // verify contents of body - auto msgSend = cosmos::bank::v1beta1::MsgSend(); - msgSend.set_from_address("cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6"); - msgSend.set_to_address("cosmos1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5lzv7xu"); - auto amount = msgSend.add_amount(); - amount->set_denom("ucosm"); - amount->set_amount("1234567"); - const auto msgSendSer = msgSend.SerializeAsString(); - const auto bodyBytes1 = data(msgSendSer); - ASSERT_EQ(hex(bodyBytes1), "0a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d120731323334353637"); - const auto prefix = "/cosmos.bank.v1beta1.MsgSend"; - const auto bodyBytes2 = parse_hex("0a90010a1c" + hex(data(prefix)) + "1270" + hex(bodyBytes1)); - ASSERT_EQ(hex(bodyBytes2), hex(bodyBytes)); - } auto input = Proto::SigningInput(); input.set_signing_mode(Proto::Protobuf); diff --git a/tests/chains/Greenfield/SignerTests.cpp b/tests/chains/Greenfield/SignerTests.cpp deleted file mode 100644 index 0cf63636e34..00000000000 --- a/tests/chains/Greenfield/SignerTests.cpp +++ /dev/null @@ -1,541 +0,0 @@ -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Greenfield/Signer.h" -#include "Greenfield/SignerEip712.h" -#include "HexCoding.h" -#include "PrivateKey.h" -#include "PublicKey.h" - -#include - -namespace TW::Greenfield::tests { - -TEST(GreenfieldSigner, SignerEip712Send) { - Proto::SigningInput input; - input.set_signing_mode(Proto::Eip712); - input.set_account_number(15560); - input.set_cosmos_chain_id("greenfield_5600-1"); - input.set_eth_chain_id("5600"); - input.set_sequence(2); - - auto& msg = *input.add_messages(); - auto& msgSend = *msg.mutable_send_coins_message(); - msgSend.set_from_address("0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1"); - msgSend.set_to_address("0x280b27f3676db1C4475EE10F75D510Eb527fd155"); - auto amountOfTx = msgSend.add_amounts(); - amountOfTx->set_denom("BNB"); - amountOfTx->set_amount("1000000000000000"); - - auto& fee = *input.mutable_fee(); - fee.set_gas(200000); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("BNB"); - amountOfFee->set_amount("2000000000000000"); - - auto typedData = SignerEip712::wrapTxToTypedData(input).payload(); - auto expectedJson = json::parse(R"( -{ - "types": { - "Coin": [ - { - "name": "amount", - "type": "uint256" - }, - { - "name": "denom", - "type": "string" - } - ], - "EIP712Domain": [ - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "name", - "type": "string" - }, - { - "name": "salt", - "type": "string" - }, - { - "name": "verifyingContract", - "type": "string" - }, - { - "name": "version", - "type": "string" - } - ], - "Fee": [ - { - "name": "amount", - "type": "Coin[]" - }, - { - "name": "gas_limit", - "type": "uint256" - }, - { - "name": "granter", - "type": "string" - }, - { - "name": "payer", - "type": "string" - } - ], - "Msg1": [ - { - "name": "amount", - "type": "TypeMsg1Amount[]" - }, - { - "name": "from_address", - "type": "string" - }, - { - "name": "to_address", - "type": "string" - }, - { - "name": "type", - "type": "string" - } - ], - "Tx": [ - { - "name": "account_number", - "type": "uint256" - }, - { - "name": "chain_id", - "type": "uint256" - }, - { - "name": "fee", - "type": "Fee" - }, - { - "name": "memo", - "type": "string" - }, - { - "name": "msg1", - "type": "Msg1" - }, - { - "name": "sequence", - "type": "uint256" - }, - { - "name": "timeout_height", - "type": "uint256" - } - ], - "TypeMsg1Amount": [ - { - "name": "amount", - "type": "string" - }, - { - "name": "denom", - "type": "string" - } - ] - }, - "primaryType": "Tx", - "domain": { - "name": "Greenfield Tx", - "version": "1.0.0", - "chainId": "5600", - "verifyingContract": "greenfield", - "salt": "0" - }, - "message": { - "account_number": "15560", - "chain_id": "5600", - "fee": { - "amount": [ - { - "amount": "2000000000000000", - "denom": "BNB" - } - ], - "gas_limit": "200000", - "granter": "", - "payer": "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1" - }, - "memo": "", - "msg1": { - "amount": [ - { - "amount": "1000000000000000", - "denom": "BNB" - } - ], - "from_address": "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1", - "to_address": "0x280b27f3676db1C4475EE10F75D510Eb527fd155", - "type": "/cosmos.bank.v1beta1.MsgSend" - }, - "sequence": "2", - "timeout_height": "0" - } -} - )"); - EXPECT_EQ(typedData, expectedJson); - - auto expectedPreHash = "b8c62654582ca96b37ca94966199682bf70ed934e740d2f874ff54675a0ac344"; - auto preHash = SignerEip712::preImageHash(input).payload(); - EXPECT_EQ(hex(preHash.typedDataHash), expectedPreHash); - - auto privateKey = parse_hex("9066aa168c379a403becb235c15e7129c133c244e56a757ab07bc369288bcab0"); - input.set_private_key(privateKey.data(), privateKey.size()); - - auto signature = SignerEip712::sign(input).payload(); - auto expectedSignature = "cb3a4684a991014a387a04a85b59227ebb79567c2025addcb296b4ca856e9f810d3b526f2a0d0fad6ad1b126b3b9516f8b3be020a7cca9c03ce3cf47f4199b6d1b"; - EXPECT_EQ(hex(signature), expectedSignature); -} - -TEST(GreenfieldSigner, SignerEip712TransferOut) { - Proto::SigningInput input; - input.set_signing_mode(Proto::Eip712); - input.set_account_number(15560); - input.set_cosmos_chain_id("greenfield_5600-1"); - input.set_eth_chain_id("5600"); - input.set_sequence(2); - - auto& msg = *input.add_messages(); - auto& msgSend = *msg.mutable_bridge_transfer_out(); - msgSend.set_from_address("0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1"); - msgSend.set_to_address("0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1"); - msgSend.mutable_amount()->set_denom("BNB"); - msgSend.mutable_amount()->set_amount("1000000000000000"); - - auto& fee = *input.mutable_fee(); - fee.set_gas(200000); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("BNB"); - amountOfFee->set_amount("2000000000000000"); - - auto typedData = SignerEip712::wrapTxToTypedData(input).payload(); - auto expectedJson = json::parse(R"( -{ - "types": { - "Coin": [ - { - "name": "amount", - "type": "uint256" - }, - { - "name": "denom", - "type": "string" - } - ], - "EIP712Domain": [ - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "name", - "type": "string" - }, - { - "name": "salt", - "type": "string" - }, - { - "name": "verifyingContract", - "type": "string" - }, - { - "name": "version", - "type": "string" - } - ], - "Fee": [ - { - "name": "amount", - "type": "Coin[]" - }, - { - "name": "gas_limit", - "type": "uint256" - }, - { - "name": "granter", - "type": "string" - }, - { - "name": "payer", - "type": "string" - } - ], - "Msg1": [ - { - "name": "amount", - "type": "TypeMsg1Amount" - }, - { - "name": "from", - "type": "string" - }, - { - "name": "to", - "type": "string" - }, - { - "name": "type", - "type": "string" - } - ], - "Tx": [ - { - "name": "account_number", - "type": "uint256" - }, - { - "name": "chain_id", - "type": "uint256" - }, - { - "name": "fee", - "type": "Fee" - }, - { - "name": "memo", - "type": "string" - }, - { - "name": "msg1", - "type": "Msg1" - }, - { - "name": "sequence", - "type": "uint256" - }, - { - "name": "timeout_height", - "type": "uint256" - } - ], - "TypeMsg1Amount": [ - { - "name": "amount", - "type": "string" - }, - { - "name": "denom", - "type": "string" - } - ] - }, - "primaryType": "Tx", - "domain": { - "name": "Greenfield Tx", - "version": "1.0.0", - "chainId": "5600", - "verifyingContract": "greenfield", - "salt": "0" - }, - "message": { - "account_number": "15560", - "chain_id": "5600", - "fee": { - "amount": [ - { - "amount": "2000000000000000", - "denom": "BNB" - } - ], - "gas_limit": "200000", - "granter": "", - "payer": "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1" - }, - "memo": "", - "msg1": { - "amount": { - "amount": "1000000000000000", - "denom": "BNB" - }, - "from": "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1", - "to": "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1", - "type": "/greenfield.bridge.MsgTransferOut" - }, - "sequence": "2", - "timeout_height": "0" - } -} - )"); - EXPECT_EQ(typedData, expectedJson); - - auto expectedPreHash = "ea7731461041f5f652ab424bb767c670e484cfe1f4a85179deba8a6596873af4"; - auto preHash = SignerEip712::preImageHash(input).payload(); - EXPECT_EQ(hex(preHash.typedDataHash), expectedPreHash); - - auto privateKey = parse_hex("9066aa168c379a403becb235c15e7129c133c244e56a757ab07bc369288bcab0"); - input.set_private_key(privateKey.data(), privateKey.size()); - - auto signature = SignerEip712::sign(input).payload(); - auto expectedSignature = "c345fe0deb4fd93da5e808f6bd8aac3fb9de70fea2774e4657c37b02143135e37a02d53e8696edaede4a3e2e624eebd3261f43e02972812c11b356e236c834141c"; - EXPECT_EQ(hex(signature), expectedSignature); -} - -TEST(GreenfieldSigner, SignMsgSend9F895C) { - // Successfully broadcasted https://greenfieldscan.com/tx/0x9f895cf2dd64fb1f428cefcf2a6585a813c3540fc9fe1ef42db1da2cb1df55ab - - Proto::SigningInput input; - input.set_signing_mode(Proto::Eip712); - input.set_account_number(15560); - input.set_cosmos_chain_id("greenfield_5600-1"); - input.set_eth_chain_id("5600"); - input.set_sequence(2); - input.set_mode(Proto::BroadcastMode::SYNC); - - auto& msg = *input.add_messages(); - auto& msgSend = *msg.mutable_send_coins_message(); - msgSend.set_from_address("0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1"); - msgSend.set_to_address("0x280b27f3676db1C4475EE10F75D510Eb527fd155"); - auto amountOfTx = msgSend.add_amounts(); - amountOfTx->set_denom("BNB"); - amountOfTx->set_amount("1000000000000000"); - - auto& fee = *input.mutable_fee(); - fee.set_gas(200000); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("BNB"); - amountOfFee->set_amount("2000000000000000"); - - auto privateKey = parse_hex("9066aa168c379a403becb235c15e7129c133c244e56a757ab07bc369288bcab0"); - input.set_private_key(privateKey.data(), privateKey.size()); - - auto output = Signer::sign(input); - EXPECT_EQ(output.error(), Common::Proto::SigningError::OK); - EXPECT_EQ(hex(output.signature()), "cb3a4684a991014a387a04a85b59227ebb79567c2025addcb296b4ca856e9f810d3b526f2a0d0fad6ad1b126b3b9516f8b3be020a7cca9c03ce3cf47f4199b6d1b"); - EXPECT_EQ(output.serialized(), R"({"mode":"BROADCAST_MODE_SYNC","tx_bytes":"CpQBCpEBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnEKKjB4OWQxZDk3YURGY2QzMjRCYmQ2MDNEMzg3MkJENzhlMDQwOTg1MTBiMRIqMHgyODBiMjdmMzY3NmRiMUM0NDc1RUUxMEY3NUQ1MTBFYjUyN2ZkMTU1GhcKA0JOQhIQMTAwMDAwMDAwMDAwMDAwMBJ5ClgKTQomL2Nvc21vcy5jcnlwdG8uZXRoLmV0aHNlY3AyNTZrMS5QdWJLZXkSIwohAnnvNAZNoQ2wRjxwSAYWugIHA+w6RQJt73vr0ggvXW/IEgUKAwjIBRgCEh0KFwoDQk5CEhAyMDAwMDAwMDAwMDAwMDAwEMCaDBpByzpGhKmRAUo4egSoW1kifrt5VnwgJa3cspa0yoVun4ENO1JvKg0PrWrRsSazuVFvizvgIKfMqcA8489H9BmbbRs="})"); -} - -TEST(GreenfieldSigner, SignMsgSendB798AB) { - // Successfully broadcasted https://greenfieldscan.com/tx/B798AB548B74B9B410F9464CA2B29C6AFEC3B4F45050338FC34F9DFC057C4D2A - - Proto::SigningInput input; - input.set_signing_mode(Proto::Eip712); - input.set_account_number(15560); - input.set_cosmos_chain_id("greenfield_5600-1"); - input.set_eth_chain_id("5600"); - input.set_sequence(3); - input.set_mode(Proto::BroadcastMode::SYNC); - - auto& msg = *input.add_messages(); - auto& msgSend = *msg.mutable_send_coins_message(); - msgSend.set_from_address("0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1"); - msgSend.set_to_address("0x280b27f3676db1C4475EE10F75D510Eb527fd155"); - auto amountOfTx = msgSend.add_amounts(); - amountOfTx->set_denom("BNB"); - amountOfTx->set_amount("5000000000000000"); - - auto& fee = *input.mutable_fee(); - fee.set_gas(1200); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("BNB"); - amountOfFee->set_amount("6000000000000"); - - auto privateKey = parse_hex("9066aa168c379a403becb235c15e7129c133c244e56a757ab07bc369288bcab0"); - input.set_private_key(privateKey.data(), privateKey.size()); - - auto output = Signer::sign(input); - EXPECT_EQ(output.error(), Common::Proto::SigningError::OK); - EXPECT_EQ(hex(output.signature()), "37bc208e75cb16417f53bec4e6092f42da95aca22413ed4da9af41b64fd59fec0b35da17e54a31ceeda083e1185ef23df8d9805e8886b9b892fce570587b5a951b"); - EXPECT_EQ(output.serialized(), R"({"mode":"BROADCAST_MODE_SYNC","tx_bytes":"CpQBCpEBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnEKKjB4OWQxZDk3YURGY2QzMjRCYmQ2MDNEMzg3MkJENzhlMDQwOTg1MTBiMRIqMHgyODBiMjdmMzY3NmRiMUM0NDc1RUUxMEY3NUQ1MTBFYjUyN2ZkMTU1GhcKA0JOQhIQNTAwMDAwMDAwMDAwMDAwMBJ1ClgKTQomL2Nvc21vcy5jcnlwdG8uZXRoLmV0aHNlY3AyNTZrMS5QdWJLZXkSIwohAnnvNAZNoQ2wRjxwSAYWugIHA+w6RQJt73vr0ggvXW/IEgUKAwjIBRgDEhkKFAoDQk5CEg02MDAwMDAwMDAwMDAwELAJGkE3vCCOdcsWQX9TvsTmCS9C2pWsoiQT7U2pr0G2T9Wf7As12hflSjHO7aCD4Rhe8j342YBeiIa5uJL85XBYe1qVGw=="})"); -} - -TEST(GreenfieldSigner, SignMsgTransferOut) { - // Successfully broadcasted Greenfield: https://greenfieldscan.com/tx/38C29C530A74946CFD22EE07DA642F5EF9399BC9AEA59EC56A9B5BE16DE16CE7 - // BSC (parent transaction): https://testnet.bscscan.com/tx/0x7f73c8a362e14e58cb5e0ec17616afc50eff7aa398db472383a6d017c8a5861a - - Proto::SigningInput input; - input.set_signing_mode(Proto::Eip712); - input.set_account_number(15560); - input.set_cosmos_chain_id("greenfield_5600-1"); - input.set_eth_chain_id("5600"); - input.set_sequence(7); - input.set_mode(Proto::BroadcastMode::SYNC); - - auto& msg = *input.add_messages(); - auto& msgTransferOut = *msg.mutable_bridge_transfer_out(); - msgTransferOut.set_from_address("0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1"); - msgTransferOut.set_to_address("0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1"); - auto amountOfTx = msgTransferOut.mutable_amount(); - amountOfTx->set_denom("BNB"); - amountOfTx->set_amount("5670000000000000"); - - auto& fee = *input.mutable_fee(); - fee.set_gas(1200); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("BNB"); - amountOfFee->set_amount("6000000000000"); - - auto privateKey = parse_hex("9066aa168c379a403becb235c15e7129c133c244e56a757ab07bc369288bcab0"); - input.set_private_key(privateKey.data(), privateKey.size()); - - auto output = Signer::sign(input); - EXPECT_EQ(output.error(), Common::Proto::SigningError::OK); - EXPECT_EQ(hex(output.signature()), "ce030c1ca1bbcd14cbf337b1078fbc09a199b15665e1aadf9f6bba0b6c90489729f0b3481f3a5e6062fa5f90e444c34b67e894cc5bb9f1d213c80ce160421f381c"); - EXPECT_EQ(output.serialized(), R"({"mode":"BROADCAST_MODE_SYNC","tx_bytes":"CpkBCpYBCiEvZ3JlZW5maWVsZC5icmlkZ2UuTXNnVHJhbnNmZXJPdXQScQoqMHg5ZDFkOTdhREZjZDMyNEJiZDYwM0QzODcyQkQ3OGUwNDA5ODUxMGIxEioweDlkMWQ5N2FERmNkMzI0QmJkNjAzRDM4NzJCRDc4ZTA0MDk4NTEwYjEaFwoDQk5CEhA1NjcwMDAwMDAwMDAwMDAwEnUKWApNCiYvY29zbW9zLmNyeXB0by5ldGguZXRoc2VjcDI1NmsxLlB1YktleRIjCiECee80Bk2hDbBGPHBIBha6AgcD7DpFAm3ve+vSCC9db8gSBQoDCMgFGAcSGQoUCgNCTkISDTYwMDAwMDAwMDAwMDAQsAkaQc4DDByhu80Uy/M3sQePvAmhmbFWZeGq359rugtskEiXKfCzSB86XmBi+l+Q5ETDS2folMxbufHSE8gM4WBCHzgc"})"); -} - -TEST(GreenfieldSigner, SignNoMessages) { - Proto::SigningInput input; - input.set_signing_mode(Proto::Eip712); - input.set_account_number(15560); - input.set_cosmos_chain_id("greenfield_5600-1"); - input.set_eth_chain_id("5600"); - input.set_sequence(3); - input.set_mode(Proto::BroadcastMode::SYNC); - - auto& fee = *input.mutable_fee(); - fee.set_gas(1200); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("BNB"); - amountOfFee->set_amount("6000000000000"); - - auto privateKey = parse_hex("9066aa168c379a403becb235c15e7129c133c244e56a757ab07bc369288bcab0"); - input.set_private_key(privateKey.data(), privateKey.size()); - - auto output = Signer::sign(input); - EXPECT_EQ(output.error(), Common::Proto::SigningError::Error_invalid_params); -} - -TEST(GreenfieldSigner, SignMultipleMessages) { - Proto::SigningInput input; - input.set_signing_mode(Proto::Eip712); - input.set_account_number(15560); - input.set_cosmos_chain_id("greenfield_5600-1"); - input.set_eth_chain_id("5600"); - input.set_sequence(3); - input.set_mode(Proto::BroadcastMode::SYNC); - - // Append two empty messages. - input.add_messages(); - input.add_messages(); - - auto& fee = *input.mutable_fee(); - fee.set_gas(1200); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("BNB"); - amountOfFee->set_amount("6000000000000"); - - auto privateKey = parse_hex("9066aa168c379a403becb235c15e7129c133c244e56a757ab07bc369288bcab0"); - input.set_private_key(privateKey.data(), privateKey.size()); - - auto output = Signer::sign(input); - EXPECT_EQ(output.error(), Common::Proto::SigningError::Error_invalid_params); -} - -} // namespace TW::Greenfield::tests diff --git a/tests/chains/Greenfield/TWCoinTypeTests.cpp b/tests/chains/Greenfield/TWCoinTypeTests.cpp index 09299b33942..657ba1fca39 100644 --- a/tests/chains/Greenfield/TWCoinTypeTests.cpp +++ b/tests/chains/Greenfield/TWCoinTypeTests.cpp @@ -31,7 +31,7 @@ TEST(TWGreenfieldCoinType, TWCoinType) { ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainGreenfield); ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); - assertStringsEqual(chainId, "9000"); + assertStringsEqual(chainId, "1017"); assertStringsEqual(txUrl, "https://greenfieldscan.com/tx/0x150eac42070957115fd538b1f348fadd78d710fb641c248120efcf35d1e7e4f3"); assertStringsEqual(accUrl, "https://greenfieldscan.com/account/0xcf0f6b88ed72653b00fdebbffc90b98072cb3285"); } diff --git a/tests/chains/Greenfield/TransactionCompilerTests.cpp b/tests/chains/Greenfield/TransactionCompilerTests.cpp index a9c21fc9c9e..ba5220babe1 100644 --- a/tests/chains/Greenfield/TransactionCompilerTests.cpp +++ b/tests/chains/Greenfield/TransactionCompilerTests.cpp @@ -4,7 +4,6 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "Greenfield/Signer.h" #include "HexCoding.h" #include "PrivateKey.h" #include "PublicKey.h" @@ -25,12 +24,17 @@ namespace TW::Greenfield { TEST(GreenfieldCompiler, PreHashCompile) { // Successfully broadcasted https://greenfieldscan.com/tx/0x9f895cf2dd64fb1f428cefcf2a6585a813c3540fc9fe1ef42db1da2cb1df55ab + auto privateKeyData = parse_hex("9066aa168c379a403becb235c15e7129c133c244e56a757ab07bc369288bcab0"); + PrivateKey privateKey(privateKeyData); + auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + Proto::SigningInput input; input.set_signing_mode(Proto::Eip712); input.set_account_number(15560); input.set_cosmos_chain_id("greenfield_5600-1"); input.set_eth_chain_id("5600"); input.set_sequence(2); + input.set_public_key(publicKey.bytes.data(), publicKey.bytes.size()); auto& msg = *input.add_messages(); auto& msgSend = *msg.mutable_send_coins_message(); @@ -58,9 +62,6 @@ TEST(GreenfieldCompiler, PreHashCompile) { // Step 2: Sign "remotely" - auto privateKeyData = parse_hex("9066aa168c379a403becb235c15e7129c133c244e56a757ab07bc369288bcab0"); - PrivateKey privateKey(privateKeyData); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); auto signature = privateKey.sign(data(preOutput.data_hash()), TWCurveSECP256k1); EXPECT_EQ(hex(signature), "cb3a4684a991014a387a04a85b59227ebb79567c2025addcb296b4ca856e9f810d3b526f2a0d0fad6ad1b126b3b9516f8b3be020a7cca9c03ce3cf47f4199b6d00"); diff --git a/tools/generate-files b/tools/generate-files index 35616f51fe7..bd0fb1638c0 100755 --- a/tools/generate-files +++ b/tools/generate-files @@ -70,7 +70,6 @@ fi # Generate internal message protocol Protobuf files "$PROTOC" -I=$PREFIX/include -I=src/Tron/Protobuf --cpp_out=src/Tron/Protobuf src/Tron/Protobuf/*.proto "$PROTOC" -I=$PREFIX/include -I=src/Zilliqa/Protobuf --cpp_out=src/Zilliqa/Protobuf src/Zilliqa/Protobuf/*.proto -"$PROTOC" -I=$PREFIX/include -I=src/Cosmos/Protobuf --cpp_out=src/Cosmos/Protobuf src/Cosmos/Protobuf/*.proto "$PROTOC" -I=$PREFIX/include -I=src/Hedera/Protobuf --cpp_out=src/Hedera/Protobuf src/Hedera/Protobuf/*.proto "$PROTOC" -I=$PREFIX/include -I=tests/chains/Cosmos/Protobuf --cpp_out=tests/chains/Cosmos/Protobuf tests/chains/Cosmos/Protobuf/*.proto From beb19bede9f1a02423f6c1712eb0a828660400d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Dec 2023 12:21:59 +0100 Subject: [PATCH 033/128] Bump unsafe-libyaml from 0.2.8 to 0.2.10 in /codegen-v2 (#3620) Bumps [unsafe-libyaml](https://github.com/dtolnay/unsafe-libyaml) from 0.2.8 to 0.2.10. - [Release notes](https://github.com/dtolnay/unsafe-libyaml/releases) - [Commits](https://github.com/dtolnay/unsafe-libyaml/compare/0.2.8...0.2.10) --- updated-dependencies: - dependency-name: unsafe-libyaml dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- codegen-v2/Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codegen-v2/Cargo.lock b/codegen-v2/Cargo.lock index 6caac47e7df..d52a97822f6 100644 --- a/codegen-v2/Cargo.lock +++ b/codegen-v2/Cargo.lock @@ -389,9 +389,9 @@ checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unsafe-libyaml" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6" +checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" [[package]] name = "version_check" From e71c25d4d2b6054fbc2d3296c3394928cd812c4b Mon Sep 17 00:00:00 2001 From: Roman Kokhatskyi <32820910+rkokhatskyi@users.noreply.github.com> Date: Mon, 8 Jan 2024 08:26:12 +0400 Subject: [PATCH 034/128] Update CODEOWNERS for Kotlin files (#3641) Update owners for kotlin/ related files --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index db9d1056626..0b82e075fc0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,4 +4,4 @@ # review when someone opens a pull request. * @milerius @satoshiotomakan @lamafab -kotlin/ @MaximPestryakov +kotlin/ @ar-g @JaimeToca @rkokhatskyi From d0af57ede5aa65a1f032763d7de110eb5bd0e991 Mon Sep 17 00:00:00 2001 From: GoodDaisy <90915921+GoodDaisy@users.noreply.github.com> Date: Mon, 8 Jan 2024 18:24:51 +0800 Subject: [PATCH 035/128] chore: fix typos (#3643) Co-authored-by: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> --- rust/tw_bitcoin/tests/p2pkh.rs | 2 +- samples/cpp/sample.cpp | 2 +- src/Bitcoin/MessageSigner.cpp | 2 +- src/Bitcoin/SigningInput.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/tw_bitcoin/tests/p2pkh.rs b/rust/tw_bitcoin/tests/p2pkh.rs index cb7a49829a4..7ace4a48d5b 100644 --- a/rust/tw_bitcoin/tests/p2pkh.rs +++ b/rust/tw_bitcoin/tests/p2pkh.rs @@ -9,7 +9,7 @@ use tw_proto::BitcoinV2::Proto; use tw_proto::Utxo::Proto as UtxoProto; #[test] -fn coin_entry_emtpy() { +fn coin_entry_empty() { let _coin = TestCoinContext::default(); let alice_private_key = hex("56429688a1a6b00b90ccd22a0de0a376b6569d8684022ae92229a28478bfb657"); diff --git a/samples/cpp/sample.cpp b/samples/cpp/sample.cpp index bb69ec05b1c..11221948e9d 100644 --- a/samples/cpp/sample.cpp +++ b/samples/cpp/sample.cpp @@ -24,7 +24,7 @@ int main() { cout << endl; cout << " *** DISCLAIMER ***" << endl; cout << " THIS IS A SAMPLE APPLICATION WITH DEMONSTRATION PURPOSES ONLY." << endl; - cout << " DO NOT USE WITH REAL SECRETS, REAL ADDRESSESS, OR REAL TRANSACTIONS. USE IT AT YOUR OWN RISK." << endl; + cout << " DO NOT USE WITH REAL SECRETS, REAL ADDRESSES, OR REAL TRANSACTIONS. USE IT AT YOUR OWN RISK." << endl; cout << " *** DISCLAIMER ***" << endl; cout << endl; } diff --git a/src/Bitcoin/MessageSigner.cpp b/src/Bitcoin/MessageSigner.cpp index 2557aed24ae..155b947ca3c 100644 --- a/src/Bitcoin/MessageSigner.cpp +++ b/src/Bitcoin/MessageSigner.cpp @@ -17,7 +17,7 @@ using namespace TW; namespace TW::Bitcoin { -// lenght-encode a message string +// length-encode a message string Data messageToData(const std::string& message) { Data d; TW::encodeVarInt(message.size(), d); diff --git a/src/Bitcoin/SigningInput.h b/src/Bitcoin/SigningInput.h index 69635a1676c..c408c947ed5 100644 --- a/src/Bitcoin/SigningInput.h +++ b/src/Bitcoin/SigningInput.h @@ -68,7 +68,7 @@ class SigningInput { uint32_t lockTime = 0; uint32_t time = 0; - // Besides to_address and change_addres, + // Besides to_address and change_address, // we have other outputs that include address and value std::vector> extraOutputs; From 03e5082d939f89278966927f16a76888fae10bd2 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Tue, 9 Jan 2024 19:29:15 +0700 Subject: [PATCH 036/128] [Sec]: Add Kudelski security audit certificate from 2023-09-15 (#3649) --- README.md | 4 ++++ ...let_SecureCodeReviewReport_Public_v2.00.pdf | Bin 0 -> 262855 bytes 2 files changed, 4 insertions(+) create mode 100644 audit/2023-09-15_TrustWallet_SecureCodeReviewReport_Public_v2.00.pdf diff --git a/README.md b/README.md index 3452964595f..78085a5c69e 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,10 @@ Swift for iOS and Java (Kotlin) for Android. For comprehensive documentation, see [developer.trustwallet.com](https://developer.trustwallet.com/wallet-core). +# Audit Reports + +Security Audit reports can be found in the [audit](audit) directory. + # Supported Blockchains Wallet Core supports more than **130** blockchains: Bitcoin, Ethereum, BNB, Cosmos, Solana, and most major blockchain platforms. diff --git a/audit/2023-09-15_TrustWallet_SecureCodeReviewReport_Public_v2.00.pdf b/audit/2023-09-15_TrustWallet_SecureCodeReviewReport_Public_v2.00.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d0efac0de8d05c73103dbfb16a68c303d8060828 GIT binary patch literal 262855 zcmbTdbzD?k*FStfQvUJ zxw5jjMO2`>xR!IMvu}W>xS6x3dkCi__!uE>zhq;DY1iQN<0s=xg z;S$p44UCDo2r|xc?U^{nJRdHkYP-i!=M^0HOne&!bDEA2WV6$L%5BFeq ze^+n_St(ik6IE48a(92X^SR(h{a=$hpNzOxK$t&%OmRb$TZlcU6#g|%`1uStCC_Kd zDS18$r=-I9GpWBfrT#YgI}WFm{P{C!3H+BM=YNlj@A3aLF1QllOhf-!3WR$|Kv*z1 zZ;(_QQx|V{*HG|G-w#|Z8P2~Xo?pNvz=`5V02e_!GE^5C>Ky70w$w$+a-P!*o+)si zPZMmR6X1{UMhDI*hkr+0N0JkMK6mh$S#W?W(mm8(9Aurig?nV^-_bPxerf&v(#I15 zuC^P>Su-Hg9_&PdQx@z(PC?R<6I?j|P>_TW&cEE!4Gstk1cy84_xXi5Qrsfg**_!@ zPpWH_xE50UihDT9)g7U$3HEE@>=FVt`OEyj%MrpU@!u-gSI!#?%pS8W{8*EToGqfb&<*(vq@ zgnEs*tseZGJ~(1@#|CX>^BTBY(DW-zNOI>?GSy=Iw`Zv7U-nI?xH=_xDspXT2k=ogt8j^r&^FySf2>dv5wVU>Ab^b8u`u`!KtquOLyw>_0J zt$g0sXhbom@h;eTsPa6g(MI@)QpnJ0L-m_vUS)4AnDh267$FYKu4M4qD@5ux`qJt@ zvK&f(%FnIeE=jfHQ(onkUxy1fw_fFPG1F-x}}aP8De`4 z_eGvd0Os>p!7KZ29XgE1S>x4U{#=XPyD1_Y{beVsrQI~q!{%a)2$$Z6oF((2cX{x- zSgmwsf2%o`e#3q9w)|J6U%QpkHwe2v3|x|Y*Fw3Rm<3chaDiV|TNzme=)^$YlO6QDQ>(4Hqd2(^lrd0XkHd5qIeta?OTM3&6m_9?l{+)EGo z_KI%SLp1N0*}Zq>-%zc(LTk4Yh_rNFQZ3f+P-OF63aNZr{BR4_blbz<*7LwZ%UPbG z$#Us}i;Y~&Mi|lK8@I+Ul-~ANEKOS1ShGEW2QEJjtd7gUap$@;WNk0k$whL|*~vRN z{fUB$ecVdV{<(T(Oxj*7*_ps_W9XA;9f~sJ5WcqlFy+bp4C{hSgUK~Z*>nP)e5XlL z3h6hh^}Pdj#13ZIZpUbr$xDBBHCu>=2JP5PeqB91d4+r5#tt>kWS-hjv2ePnAr3Uq14+T{&>bHDjJNYHj87L?ju#EvOoInr_OB zJBbrtWy#f$xmH=kcQeUCf9nqemW1L1?TS!g+rxR*&}j2S5s#d-sXrHIMfSX<$D*F* zZ@U-R(JTZaoEeEdHM9;Y7@7-(m0jnAj6&WoE5@N*7O$mPpIv(4cr{fkHwQCDk=B*w zw=9d0U3yLzPcjwAQ=;Kx!oOGCQ@ZJG`^A69=HkJ|@3DsNDU%y<6bz4`S?PDvZr|{J z`I00(X>s%G_n(U$vs6hpj^tOa7z#dbyFqX%yxg+VuE|OQMa$wJRM!WsN^oER9_P>P{l73TEhQ^|ZXN!e<)6hrYXFUbj=l~+KtKREfq%fC zcYvaHB+3H-jEw;y0077Uh&c^F0G@$O0Kl9UAi|%62VhS3pL6{6pJhM`AR{3mB_Spw zB_+LZfsC92N=b3?A_Ws2JvEe#nS-5;nU$53S4@DDTZD&|RZtElA_13{mgW#pP?48Z z7L$^e#CJk);lc%qixiBMl#G&GtXz`+$DcpF04*6IJ){{zzzq=65zjT?AiJ6aIKoBM*Eh8%@ub`-PMO#N#Pu~D(VQFP; zV{7N?=I-I?h4Kyw4GWKmjEYXUm6&up`3@#CD?2AQFTbF$yrQxSS6x$E_o$_{t-YhO z>+$pcfx)5Sk*3Myx0COuXLz~@0LWjm{)e*v z7hSXHItS5nOU98wH8buv_g&zTLVRX$37#gu z#4G)d?+c!^bIShz5f=Y{McMxl_8+>I015~J$UF!wpaz_r36`U2(n*YE+$f|*&ZbXN zO*Ql?GRbh<2C#eZ6BEG~R26ClS{&M(PBAl61lmxu|9x;d!JbLQm_hCm7@yu-L?jXx zt5cE_vt8RUh=ksqRc!@!!T!m?gW25F8w)%k&q*cLP0NSlxYYft3msR>i?zA1j0#Lu zq-|X0oy<(8W~gL4qv#;@QKqUbG(E3*(ahSc9zXgL#*3Gg84)N5;Iecg%yhj0kByS- zCo>t^63i&61C#PNO5AKMB_KV+943oq(KnG^@yJgqG(y%265K-2nn>s4xU!dG!ABTB z)y(`PW>ochp*A3yKAXaYgTVDo2j!q-&d{YO+$_zkF?T6OQ;<*!yvq+ucISfrM9`{J zRC1T(4>Nfs@gNhiy2!U+YfC4M#h8Vc|1&l#B zOppMs0}Ik7aBg)4+hLsKpsnEBXd`n&9YMzIR>TYeu{;8qD4Bi+^D6TUGE!3*_CJF2 z9NKp|=tuz>lUOG%;34(iVjR^U8?XXt{-Ab+Q!sgwq0jjIvNF3hObL*pHF%1Y^7KM59nOc);_BZ^Xfh8ggO;fl2R;1t|h&_m-v zKw=iPiEznJqBS1W)0UosP#d7|oHQbF$pXKTG~);tkfTHr7$f8teRYaK#%hEaBna#f zIv@qj;lfQpf^=Yu6;J@*5M&F!g$tflAhZ8ofl|=xAvmZ8bxEhF0Q&@)s;vYC|6|}L zgN(TI&ctBg`|HKp5=61{0;PV-O`@=^u%M=sKLubeQY;O0WpZsGek)|20$H`=T*<#TSY_k zO+Zb%jq!)gC823%QFGbAte!Tk5(hzbR?}(|vS3iqGc9%68J7|i4E-DiH>oVsBC8x! zOrHh$Mr%6A2S|WTz$K5P;J$~6g689hLXpk-7#AjOHE8xt@5A*?1K2lz{-EI8;c}>>zkBe01vzZ!__H4MEG6- zB|E=#FoUaEm3;PJ`D-rZnw64c;R0Z9_DOb3ZB|nW$X} zSe;}9GsyT`Zca1P>O+v$1GPlebS%cZB+!chFvY7a? z>^qy=tWGy21qp*zrc}H;N>$Wz&gNpv15P)~Vpyk$6Z+0(!nq5!Hm|>Ub^oe1 zp~Z+&bBV$_nqvuLGPesDI5Sqy&wle_y_+dxc1Z-T@g$6E6dZ1AYLTmQYxUG zRfC*N7`)g3(<*5Ly`I{cT{SC=Fu|szv4h4 zSdIbyJwV76B&bLlZd5E>eVYAo4!B)qndvf`-0yZIUyD7S3z!+!b0V`q3>)b;$6PCM}a+(RJ9p(@WB={?; zaWv|+)Uw8_{J0Tj4V37hmJP5&;T%Rmr{+#wArGPzE$MlN4<*q`*&9cn5W}!+5`Bx> zPpMu^p3v;M>pvTU`;S3@01Z7}6ktP{aiBeDH3LsUBMmVbU19 z1kGjlqAIIFW|6r=_Yus1bLAl3YB--`triMYQhv}vSK{Fq%`?cj{x8bAV9mHHkwK>C z65>(}O2N1}<^~K827e_>uo`Rvf<5RifX4k?0?$qSUGx$jW_tb3oB2U2L3#Rn4oVds zFPL;d2`wWU)4500=ITS6C(zk6Erhu`t$R1pPP0^Y_EQ{k zmU?_~UvZWpY50($bgT-O{ZU>zE?M4N{WgeC|AJ12#b`Va}HKej*JvV1$n*?ey1#rD7%Q|%f12PE`{it&Kkdc!w8wAn zs+n;VcvAp63C5sgf!-xvLb%VZ2d&8<2^w#3c3OK4C>1l)lU%Uoymu%}MX;Bo5#z%T z*?bryTTCnt!TgOuW?7TML1ERUZ}ge=EuIVoK~0PHlg?2lYkDrEKw9*5#cb=`<@MMH z*tJ*UO@mKOrMi?C(i7iQYU{stWO?>EYv&&3W4i0pNue1Z=IiYTiYwHjL7i(~3fCC& zMlu))Z31I3_e`^&KXM*}Gm@MH<7?d%qHd(zGm3MG3b;QzRFg7?Z=I@%4zXntOt(xPa zKxaYCQeXRFELeD4rVYi$mk>SL5=gnr@5fQPtpOnf)<+N$J8)Z}oA^&`#Fz zU4sd>gHOo~zBsQtC|eev(?}%~#n|W@VK*kfUfc^SAzi6HqF}gd2+Vb*0Ywy;8;aK|6 z{gI!jx%pkx+&@Mdbk%^=KncY{OGfBClTwN*np{%~oW2xs^bL!(-kBK&5->nYDIH@9 z`I>r67SZ{mz~&Fov!_m{(rdL;Gr4-#AX#H&Tl|c=N+gJEn6+f9RGreDqLMZpbw@I} z*W>gyQN)Cy4oL_G@X9gt+oYf6fu6b6g!TQTuHsgh9Qx6Jd{RajaO}o@8$&iWEhkhl zfBSGk6p-k&5E%x-s#Js?|Ni!2`=d@I`?1o|a3h>n%n;7DQ?7kz;FPepR0#=qZv;!8 zemVb)IfCJ~Tc~uRwpWYwSkDb;P(yl#T{hf4IqRT(CSYOW-MX8uRg*P%ucqj-L zuZ;iiI^RegI z+1!fNI`&P2e65v2J)B53*z*%)kRUjUsVaj(pZ2Su|AxUp_`;_^TY@!;qS@@$AeHXm z%CE4R{R9Y)3To4ByX$^DPl;CmH!*$UA7JkWNs6e4$&*(`M>nbW zYU`KQ9czP2g~g3m*V~(~YWu~=G_b4~Zq?IuU*0lb%vkE7?ms(HOwG+QRTdWd{8pZ6 zqe;n{&CcO*W9qI<_EF3|8xTl@Awi%+p$eJ>FhWoTozQ=r!v97M2>fr9`HyP}CKyUF zMxZ>O5a=T25eb4!c@z185%2cja2srCu~~g zdihgftlamVzu1Fh+47|x0&_Ox5`qMC=P3X^JqYhQ`cg%vB{t;M!KPhpJAd+{O*2SMB?YA@09{b;FL^@- zCT`H2HU%s^KBZQBS4x7`1cBqFl{wJYzp9oc+<_vM|5jakuh7<7IHR8`sUqc3I6G4` z#w(XTq#a(=!5Yp0gD?(5U!M97Pf_ogst7VUP^NX%m6XhVB|CvOC1wftQ)%F~G`obJ zslWc6Js=^jz>%ZI!)l=(m09-=-N#FdogF7L(OHW@55?JBVOe?R-21zY3t9&_5Maa0 z<1&uqE1(TsXLC-?(gQH)jC}R1o-_eP^JP}oz#a3N5mL288vzEg;iCSyj3Ok>tsrxF zz5KVjuT7Gu`|z$eR++px7H3r2=2r_y3riIAfMDjN3waj#>Q=nNQ}Bc1S|P@j4kC(B z?!vx`7J6t(d15{zDCT%J8Q7n;U12hKYpPibeUTDCTb~M_arsO zjVfs7N#Smk|Hp~V@vDf}*RvArq8rbyIZgy}(LQ;Ur?8o(VSCG;s6%VV@u;?jXR^_9 z;*>?A-*0fh=0(jURnP(QqM5!@no2#@P*~~}tE;!0KJ7S?Z^|?He@K3v@^qF^m-U@S zN^>7;{mn#O=IcGeeqvCq_eS^Au{7H5dZYA@u?NVZSRo#P7U^unJmq3gZw()v8+5O* z8CtjKq-~SBIFVUNB{iY6X!Ue3l1no7Cu9X#dB-)jPJcuCnAo0lN3Xu^&VgRJda`f6 z6!7j5={wuZ0WCLrtFK0t+t^UtNPplpJ0A@dz4=(7+8&I;0#|O7E)kW`X9wodS5Dk- zcvAqO6~*IgzuOS=i+3D_E5wmgZk^Ahme(4(T(h2cyjtxDU4KxvC^mnMk4ZvBtKD>? zaHu)v-rcO*_x1-LO%MG6-~uy`?`%HQdn3|IQ%^(hPae8Pc0@YgflKu1*2JPjZJSa$$(BS+Nai7hn$BvyW zRAgt!t(k!zD*j=Lm+v%sv|w1e(V3gvQrOFkYlS;OSEX3n3!RHEvb=sdnzTmnA=U>s z{lq-1n#|>iwogQ#{Pp~~&+vl-hc>~mJFg9CXLQS4ut?F5+>aAI5>I$SWYOQ*K1#_f7Cs8~+<4D~@{GuomRj7jo`1-yQG0oMvT!VgT#0tL$m zF|gDhfuthQ)Cqdh8c1BojJEo<&lrJ+EdJ zm1DAVVXQcb65#9NFa0S!(U1TeD}8r#X4b@{6t5UBNYMXrmZ?3~RAbM;;ny5fz!V}% zLoe7+;-eR#Nay{Rph&-E=rjM&L3Y{5ooYK!Ff|X`l z^oF58ac!kpgCcVfbUeKsKp58fXu=$BdvU=;kwc;j{ENRc4uw%6FY11C*dO-=tp}_I{*{|sBjqxjf8;~k; zl|k6DYgNma%S#U`k_~)x>v- z`2#$T-r%tFXNxoKdd(aV9Y1-t`deI*ltO4oSaJyT!1(}K&=td{VVUi;)G4GvKOLX& z*+)8N@`~-()k)f>0ytuy7oCl?T4Q&33ur%0UKqX}-gMD&j(=`4fI4@&^vw`D?s%)? zxPB^3i?!~l5ht;KH7}v*Z1sEF%LW-&PrtV4(>0kfmwsfWb zdaWGn{b=}W(E_eY`ZD4Uw+`h`TeeuC*Bc4m-1%rs#bvC`Vu zEXTV!qXxlbd}pkIxJjs_aOG4{weY~!HeE^syhnvw6M|lj4IHK!#K_)o2w8eSkj-J> zcZa*nP3DRDt2|0kMG_Gt9m)?EeSCkM0z zwn|u<%CmUy?PQmLwkw+{_w*odpB**KV4SQNSEyKC?b$b)zYq^M88&oF7TVS{MkKVliK*piCD_Y^6wBt`4tjT!hUf{c9`@mjTckCl zZ4uueusCYE#kr=vxENQA3HiR&qtqYq2k@V6{5j-NKtn1zcgFl~0NL>*O_vg}ms5yO z)d*+rv0z|}=jOHbpI8sf*--d_5nbMY3NrZYu^>Ty1wNBQbYAWOV?=xmhz}yqMG&aM zrwEORK-W$X;#U14&IFlZXvC_(-d3QsLqi`8LBL z^$)*I-Yf=G@&$Q|2#jpyRLL_kGD?ofk|N+DF`jM0xHXADjS*?!%)nbet1W4Ptp{}k zWZ!~-IewC_PuJ7a2gsv#%oATutjyAE?HG_kpX+hTsDULPRyYF`v$&EVB0p%b+PZoK z)O6EOPiB7wQ+ovDm&~At2!FN@0F6fFPG6bkjCsdQ`lcD2d{!kzKSNIkm3${l1*Sey z^*;Kj0-rJtnc!M4S;3?U^bkU6EsH3}Z^u*VcGj9`jNtT$O2!qLoiwJ_%Y!CNZa9}< z6)~<4nCh;;@8c*&P>1NRk%>oeYw2qk>X7*-P1EOvVlW?u_oK+xhN{a zgyDzv68+4SX-tv{NeD%Yh43$vC84y^9$bg-AE~5zdoU3lW?9^k66nAzO4q~8Y@EV+ zkA|biM8ssDkN18&RcE~+(-lS}OdE`TR?jY!J+1YAXfi{%J#bl%@yw%^ZWtw3>KbgP zkvKx2_=A4&l{L;g$lIJQ-xm6Fv_w~#W2iHEiQ$&Ew5BCrJB|8^uItW1p(XI$%bv@N&vx>_QHcz1HIm`IoBMv<_mW4OeXyD}`Ey3Osexlxh_wV|^apHH$YR ze<&196D%x8l43C{vVPB?sY%s}BpZ{NG(&4RbHD3x(u~TXEWrn*LLz-j<6mV_G%K85 zlT7-392=IK^8@~U>q0%ygv^`IA8zK#Uk;&o=AoRm>!bJlM+U5_*~fCe%J_=5Uoa83 z+C4AQt+QY7nfmtf4l88i(KA6N)4cdMEpdC8Nsl|(WJ7O1d7fEDebtc++Oec8yr}>{-4_D_Efx4TC!<95_iTYVQb!6s54F7zVvjkqQcynueXmmxZ+I2*n?> zjYZ2V!c8hv(N^Qnnd~>+mGY9<>H<8x@+5_HMZ4~}q>LC%-(`D`cPsz0XyHT4t12&6iz(cbwwb!ZT>RZ4Hws3J|5F6sJXv?f1K+b`A+` zh)`kOErqdct2xxK5yH}l0Ws5&Xx6#rG}+6Vm*qEjFr%MB+*+IOL{v9~`|^Z&=o`|> z1MF|d9%+gf%26q8{{b59PcWjJUTL(CsS@IK8HiI0`3l)KQXN}%mf8`I=AN5%PYuEo zl`J-+U+KR~(P;SO%u*C@(D{-@C1}q!B3CpZaM$AC<(RIfun7yM_AM0D=rx+ zRiHbHgLwVT#%qf*mJyZbVgLh}S0&*;b#=HQ28ISv94x|j;;|eo_SnHq%3RFBY?|*Z z$E%rAjU6*M!AKSE{;mq_pBUIy<9@LPT5ohmBWSr0aHS)mUh`HDmNMqLm(YuQNc#Y} zPa5rV$wYyH<)c$W(L;{>iWkL6MrEGg4YrSQS}?3%k)Q8IgsXa($bDi0HnJ)as9QQ| zj}_G#G#R#?u!tC@N$Cx_GqM}pb+T_N1X8K1^u}rRRcf0z zbA#bo!!u|GdZ)e2D1n9lF5~@M&I9A>Qw{@vzMZZcowb5xWX;@1O25)b&@;5Z8>s6o zKnuij?`H1e=%(uouBJflu!wf*%q(-8Gc7-iFphe6&W|U5FJH2{yed z9_jJ+-`St;&7kS!<)vF^vzF2?u`&Zeg!j_44D^pGN)23?V+Wv@R1-sy8=b8Ll9~5xuuwkAW9Q)Hb0;2l1j+)WmoJ!N^(TtJmb}8&!)Z&k4w~9Ev z;J5|kca~c0cxtca^P~%!e~p({#XY!|!F;_sYoQdk#6lEvFX?n-sdn#^$r<_5oe;8? zjvp%R45p&pnQv1(e|gUjG>Tw{d#oV>4CYe>-Xm2+_b+04^|+;Nv*c& zZ|@k(t9ak>ls)r3ARPH&MxXFxEp8W;Pj}GLo-SUWshb}wTvo`qW!570o>WDzE{(W> z`bQ_!$zQZ6UDu#;)a!Z4(%13k$k7yeF-4l#UwaY#gZv%Yy)PfD{xF!# zx$s8nn9NOBh0~*^1)fBKMwkjIw8>t7S%K(t2xcnv`=jgmq91$|rj{nS8K{|yDz-CK zc6q`h+;xORgFlU3{o&Y~e%PllX!R_LufV9NhC^_eFx!OuBX@bBsX{RP`w=cD|K4Na zOYXv3&Afr~d&hOJY8oWvhkNu4ay`T@QXP$w?xT!Eb7D^{N9ng^n6z0-Zse4E_%Y0rIi#}4C_iA$U6+Z2 zrbbY8RdPD5g;nWI3cBdwYE+%9Gp1&#)(0|87am9gR6>*mxcnkLE6#n%AS;QH$UPH` zvRg)}K@|4k1piP5_d(ZfpE40eH;Z(GM@h+8`StP+7B`-SVv*q3GGzn9h~8S^@f;V< zqpCm`h@Y*AF3%REkne8vwv3$E5`$4y9!}^I(O2)`yrl&8$m;tdiJBX6$Mv+7wOKRe zWPRU=KOve=+e^?cNzxWQ?NUQlT(4#qYIe-iXHqrztWOu7{s9EP%+$ZuQEO#FCUQvK zEFG#<5i!JU8D*J`2ReDa;`C%}7G!$G?^j(Cc~XAqVRh&c zk6|Wlrlt-3bQi;oRlW4KemLzz+9tK67Cvl4<4%dt^+&zj`!`%vc9*y%n`>sNMXl7{ zk}4Te8$7uAqlL^jM?n6-!Z~AV4GvkHKYDfuIjw1IbthoB)FP@c9cl+@>>1C`(*6hZ|NVyq^+|%27f&3KtGn| zo}%rRqT7t<>t$+s*%{-8$ak+ zH@u+3BBnv0B?HTgf6M3~_?D4}K`D&>DW&8=!OE@Ipd!|c5V4+{3vL8J*5ITJQEb%j z3^X8BG?!4JKQ7mHNeKF=7>hzDzM0fm?CIQ`e(EB@!*ncw z$s)&?R?&`JQ*i85fl*-XlZeJ5n)fo|z9Zw6ZwAJzu>AD46C_lVrhdZ6fkV+;j@_3a zC8q>U0CWhDQ=ztt<)HxUxe~~aUsYfnTR?3;RrZkr<55{t6q^?<-NS<0Xj4}!!Sj^rzYzRC-+Fp%#&Ud*$$=nFBx4I~dALVj;DPimkq$-{JGE29An!ai zlgh&0x=TRQ(8&uHET;8;TS!IS5~R5N{Snzu*l9nrnPe~ujrM%)1<=L9hU4@qFzPyM z@r+??D#KL@Hu59VUnPb3ktcbd(iZZm)FKhH5hRw*CS{ODu9GUzn`Wn?6qJ^QPv~Y9 zg&*U%-Y4>3s2rjc^CF-Ea2wE#NkJ7a0!j(&OvX~hkLoNV62(I^qaYT+q43PuM4P;E zi5BiqR{GVND<(fpx^a8m9q*0lUhwKXtj~TCyZ>;LqsltdRKk;YdjaPmP+MaVH&dwT zcGFW#>T$WHFegK}WT)Q%1(jsrIWJyn@ zwmR6Z*(xmaiesj)*^Pia-Ua2E6|KjYZ$wbMNF82y44K7D_N2;*q*h)=9{LGX&(zY5 zCw1l;((=^MX>eMAyX8w?R7rmQv}rpvK{V0t=Npt-F~@9Eu3u@hAWNahUY4<|)@b%vMFq z!@CoD@9tQpUJiuFSNnw6i#*a4El*V-)5%zd1a2z_#ib41?=dtq)?FTD8xtPbcBM!H z-re3Xls08PU=@xzZs2=1=jV68pZU(cBjx$N#!2><%otZJ`R=U|*B1|XnxSu|atczv z9310nIp=G;LJs{;hJ+|WEQ1+`I8?ReWml9a4rdqgz;j}KJ10cO1>QMlIabRdvq;=& zOjKQ$7%w%jAA=Bb+Ma1}jj;0{6T2id(nV7K+PWPw2ruu$fopzcy@;-w45;uEPRnk% z8Q#}yeelL1>Do~JNWpsLBTekUuO(9_>n^|evxq7oMNEj8HD`!fR3)p7e%V4Ar3vj> zSS`E@HGMpInIW0Tjn34rX}QC>E5ISF{%bVE5)+KVQ8H|D@acQf4X`6mO5?tp3(>N$ zhHh0@zoBbidusC`CKGkU;~~12ek)ZdJm0J*X)b48^+P3FHfpkvGuTf>ikpSL$I!^ky!Lg z^mU|Ka%lBk^T?a6WiE{m(e6eRs;h4TW87k1*~#R z&$lQ6;9gQgb;^HyWH8HVCrBVzRSs5_M4<%t4@O>P@XDcYTq%zRzj8&E*%Woez?;!ry_FxG07?|XpwQ)1M&9jQ8vwXuM zrik(O^b!gt+d<6P+sX2gyjMN|GonOsaaA??t}_CpyJ|yO6X~%ZKC1)^5=IM&%W*{M zvG>%eYcP>Y1Fe4xQ1(!Oj?~V7{cY$C>6yf2{^HCYUG~-i!K>R@CYAVYtw~U@q0FyX z*v04&4Zco}Vr~E|HxF<+sG9Q5WzU`@Dy9#0R4Lw@|DC-Hz#5cNTXzkBxZhe&VrNf! z(i)AI3b<1$IUeg!%EBNz1Pac5IIg36-Q*IcNJF$BL3gYd4i1!LG?1oi-L6bH z?%j?T9G!L6pZC+{KfIl65?q?neM?lB8+j$!Y_8e^X{|%M4(<=^mZ=coY93LZzs9eV z{J2&~F~h1*s)f@PbE`s@g%tH6sIT!Ni_DXvp?youkGx@x7bkFV@&{w}U*ncieUu1O z<|h#!92IgIFJKwJUw4xYCVT0f;Z*1+DjM)~{0qNCM%cBJdrhWa>w~W-7Jb>VpuB^5 z8|uE9PeFLi&sH(N941=z>+K7A1s%FD#!cFTJ1nxsXZg;p%#gfj-E(WE_=gDx%N$_T%)xdw&x91=>n&4Ui; z+P%l=a3plkJWidK;Ru&8Xfq6b9$!4@!YN-7AS1RUI~~|e3q`Y{g*00Q`#sR2NvG;H z0x1rJikN0r_dK3byYa^hp$Dp4i*HXyt5I)=BB zyxxE1_u^YL9owkkT$|bd*5u_Q3)l1Wok6|t%;glP@uiTBpM3p3bv=d}X%F9ZmtEGs zXsn1c#B@d}XUGjJd5VL zMp@>UFYrAQ`#BWlyicW2c2U)aC$*I^*&)N>+ug>~854mL8Qam^x8r&8M4!9V27gc3 zzWQ`b7CN#z@cS72VQAf1tQ7VKvZW`%(BZY^u=jT57o&cQu9?bKY{w*LC!ODZAobJ8 z#+TGS;aSph%=xm~w(}Lf601FCSyLI4)aEk}+YU&zOD&_03JT~#sirGF+DwMzu*Dr6 zPw;*cmX<9{nHAk*XPU7Zd$a%khnt23vo=fiMrzD2%BbA3+?{qomlB0?kw>QkZbe^u zcdjPjp0~e6wRdEyL$D1EdI+81il;u8?H~T0O2NryY6me1cd0w|QXHlReM)^|l^#A_ zU|~JaQ<4CWlKPYmW3&xQ6aAHT^QQMgZg@}2wbtQ_4&Y|pGD$sBM){qWo3T<3`!ypt zRh?*br1W?9l2r+=u>+^&?xcO)DwHel8FQ|OlHtQkO!>mj2hExK8wsycg{!8IYkiOT za>wf&htu`?g{dT|qS@i!tras=8rq|??}@okvE|B6_Rm3f4vrdM-1UjoAmu8tI1w&5$TRl7LyoFNR6>P-AY|r)!z!t(5ZTp=Aio$=hGiThHDJec>Uv_kq~6E{>wa;N z!%9#TOv*m5Yt+)-_6)^UsNV9?F;i6!QWw-WczI#0FK4b@l7vc9*lPweHDu{_tO9v{ z73Sw4lUnf}S4FczUYZ;F`|BUz_9>g?^*?}n;3|7}QY2I6?qyMW+AdpD{~NfTkmtW) zzgG{rI#UJ3%(aMg1Oh*59PQXxRgHaYm#eTYu>BaCjhMD!xr6^{1$cJ8PnQIGegazS zLW_c$L!QqheY?>!r?5rZj~AF~X}&pC@R#XZmX3Rv4*{fA2HfVmHyTV2tJlbZw3l~V zF|lzKFYi7{R>hUZ=@0{QlWCk$cVC)#q0D2xMiVR)x5WyQSCkQ=8697~F^Xvu$US|T z9}z46vknZKRAykOSv>Q`?A~Xxv9g7k7$7+*8yjDzus4d^(tArBQTvh7)Yv&go%ZxW zw-@%~rjGM^AG>1{@4*vWnlksPu;=FH&Ah^e&zVS zHEW5nBAVvcJ43x2pR3A97`8dr(TndoLEi%#qE4;{kzRE;Bllm}J45zE4vaM8;xIme zy|X)HBbD&RFdnmF7|_{O8}HYRIleW6}?euGkDSt%JXGF=I02VS^>R zrRQzRGDg%5!8kbtSF3B;>b5!EKCA3>&lQ8&fkq=MsZE7!9ewK5@@l?Y4ww6LU$Z56-U);WP93j4>45obx_xA z#-fl{;%C-fT29M2-bjoLzj_5O7UDUP=_6aJnYU5PB{y@tmptHibuvdfUUL~m5Og|x zUZe6RAvPUvkm?=Fus-^ws_-sTpSIFw@twu5--=c@liT!{Y?z;KS=wy9max?5Max<6!w~UU)$NH8#+l| z`b7Ta#_z~F#4N%=X6iQU^rK4J7FJ>fHXTJ|?uA8nQC!%u#d_U`5-aOvot47$l*S}0 zX{D*%|BdPSTeciEuQznOWZA2 zvO31~J%w&{hwr@YlaQO=4Bhm3;;&eyZ95b%Gtwzh1;*ZKa{2PHVe77S@%W6R-r$oj zo&KzDe}EVu;d`1v4f3W}!My#z(annkxsRiFXvb#t5}(d|>~tDd6X+U7PSBu+*u3R! zDKbbWgZkKy2X=12ZtJ9;$waw}a?-kw@=3}$cL%C5AFGIY9g|$+zLE}H+z(t1S=^A3 zLL>xqg|2vHI#S-#AFG@JEEj|4q4{Vh_|1}8?yEe$!7#R)`|T?V-iFqjp}PJWx+boGpcZJ1(6N>mxF||-@5k$aqcquF?(~Xk zGHTA^N`IAT`k#AhW>2a8ef@Bc!L42~o}1Tuq=uE}U-H{8+lpviwCN|IIOuZUP_kwX zDT*t7Z)qibd*)e7WrEw(vwMur@%_d9r6z6VdZX>S{!_+b7q1$SHvyd2JK1~w04B%F z8bOSAcn{gQoljW(9Xor^VuiD2_X`eqvqnT&1Q9<%+FFKH9^KS(jBU%ZvukWkUCTd# zs|CT)>MGBEh0!%vN$*>9QpRw$S)vO_{AttEdqcz2s?}x+YQ0jR9-j=@jn`8N*%>3Q zMh+j>B4%K8+}Ii{M{9gqeIzat`n_?pkhl;+lrWwtH;_R;{Qt1^mO*i~VVGv)t^tC(H5S|(4ess) zcL)H8Uh4&cMXjM4;~1R>2G#sw{~}aoT~G?>vWy-KF@t$7n^@#@r6^W z{$h;zgWP4?ubkxg&3-qVJPU4ZMC{$;GTlRn`>oW7QlUfqRwN5n2%F2rtPgj>r z5>15wm($ENMFXfdsvjsYC|w(iiThI)DI%J@g-IwhpVc7(Wy}1&l-{C%*GK$g@K36Ue=%f^BkM{0{FcaFR z9v_=lx369Nlz!7@igaVEd^^lz=97<;CYVT^`zf-SCF6X;{z-FoZ-aG2+}O7BZLWd ze1;iUcB2nvp`{|)hVEisrz8G8EIGsF1?`?b4!6(er;U$$vz=`)4|mL0Lp7QjuvX|^n3)aRU;Rhct2fU z7A_ZJ)rd)E6n`HjxC(@{t;n7|Y;gyjWnDm_Kh5;n`zl)VwcI#MxMiQ|1+8?bJC6jm z{s;}fWn)e(1Jo!Ly+H91CcmSj4!}fJ=!}+5R_5IqicQJV^+rR@ib@9*`fH6LCMQz9 zdJU#g=jEU*W+jO(c$lneTgHq*UGGGStPs)E9on#fa(_-dR1L-4vH++er(K?QE4nlo};P zO`6R83mP})xj{++Ia7@4rj5SE_G4kmIRW2jF_r0b@qfY4NpgaT;r|R&*6_HO-%)AC zIT{t>FZZa#3;x<(?(xcH&|dggf}+7P?W11~lNhew5!F9{=mkLV)sS|IfrkQuI{Qg7 z(mf}#b-J|5bgCms9iL}BbZqwt4_;dgNY!yBx-~QTD?HjBWOFszP3Af4Mf%KMJ15uC ze!~H^j~rug@Bd(ZY1%?AFn;DqKe#i9jx%yCsk}2^>dZnAgAl*pfDxZF?`R?eY%3_` z+Ut0o7a~++^$3zOZ9rOuA8Yl?u&U!$@p5|YY|M6{3{)P0FKiAu>`nDnG{8q+gk$-msJC3Y-D=Q1{a_`b;ooksWcyP8Uc^|5q@CuV6t@**?b(xokS_+Ot2W-AlAX2HL)5q)A z=6Fs3As3Hj$xXit*-=390f|mgVv0q1oG*h~DEkSp!+!wmSMTZ^9(P}$tDc6S0l^Pa zcEv3O5L=)OOf}%cnf2R()cQ3l9Ur5Rb=_N-$g^;b1Z_uawBmKp2-PGqY{GsWjY>^x z?!1;hzAmp}Mxa4L(-%gS^%OcIvG~>7N6cvT(9OH-_uu+lXY8cNZ0pW%c_=J^kcoCl zE{unB)8QItqz~KX#~xE)o4#SDVO+(ro#ch@fqnHEf##04HGd}#-Z^(jURqHGOKOUGC!DtW~kLk=p$0A+J$^s&wI8LGI+KVTQVX+}4$mBVbae zOd;_(j*Q)T4No#P zs*a;0M{kOfFh7D}183a7jbVOgj+eoQrWkIv8Shb1U>y0`;tkQLk-bkv#TS@8>3A9jCOTxt5?hT^9?#vq+!2WXV3?= z1*ZI2W$TUcsOf8si542^<@Pj={%}{04jjnVkXjCZSMtD?gTZp!A?dtHE;wVCpf$Ss zAHdwvrQX~z1y!c`)5PB@1hF)4G7ye;Chyk9WCdr_@G5pK`o;zNlzm`>_>N1ony|5@KWl-KR6BJbZw2%|&bg^keVB)NUwU-LJYVufHzzu#9(&#-QkwkxND`N7 z+~)3W9gg4=`ze_6uWGSRhZ|I!P@*-p+M&s~Eait#-$;sxE&QbplWEuVRsNObWRS^1 z;=|LHi-@RY-3P0BT85O(ZyldBWB{45kjXxKkDC8EHC>8G8F}*qf@~9V4w3h_xT_Ca zVs7q^o#uYWpj|I(!EXdoc)XO=C-;@B<6=*X)cYY?wQ+dC;zxyk`=ocwxtDKdlVHeu z?gA;D&zZc+CK1+|WC0%mjw;lJSx7IZWGN?ol~WZ)AI}fyj0LChBnpk=a+qAPGzdw* zHN|Si6ElRdXD#ys$!$9tBZ{R{4+@I>aXVkV7oRs!cM&cSrrs$N)dJ-}GvYmopL^-6 z2X!z;wvl>!44Olf=t50nG&XhSdYVtoTF(fjj1?^)d-T4b?)sqeK)hBD(J)!@LuZG0 z#=J+1BeX^GC9$k=K^tE+GR*iC1z}-}*PaBomz-%FwryZNOIv4*_>iB*Ob5i^?w2#k z`YTx2dFZeDJlXqid5J+C_m$Yu=Wdrk_o4rp9*9-mHiVFS~trdp3q6Mt+DLB$r0bKPq zcCn;;5Q}tQ^3~04q|v?JZBX{=rxlR8IK;3&YTIS zReD>dh!@3@O@OpoiFGRb(wrlCRHW_eB8bSZ1ff9_nRzK$Yxxz;Qz9&~oB0}?}0!1I0M)j6seR6#+?HJt% z@_aV$JpTt+uA{TL<*V{(Wpy-&!|7e?OBz2yqVfGo^|Yd5zvt-H4dWM5wf~R~GyMMN zR%G{;x^;qH$sQa-k1+DPVYH6yb;0k-l$YH!eKe#(h1tLA{P~5gEOj%htqnvtu*WMB z>zr%Fkw5s6)y+zx%$MdU=aM0rImR|7d>FL-F)ZGTl?ZEafm}MZPq(S#%N}t@EVFfp6p#QU1e@{QoX(8$<*K zmU8%%QYu9eR~fOEmuFEo<++bmyqzZ6U!M}Z9zxbTZ9zl{oUrk|tx`nxKzLeImQK&| z0wCQ%J&xw!N#l1M?xH#&0IO8C7B^5NBKZ}fED(;mDI+BAOS0Z+6*~m^l};dk-kjx8 z3Ej?uO`2N#rBT*k9eJ$*1bf{=Il{#Zi9f<=BlLE$T?#c`-yRiE^(m(_-86;$4 zsX`LqtF+xq>>eUz^vASfYZKMc`7Hi!x^Qcn=eDYh^j!03Ju7CQv|jotd_A|tb+pot zvoU}^*CCx8HUPMo5T6oH#q5bRb8ekoA zdnCqt_(YbY4^bZ1uX-Yoe_2lAjWlgMOxXaEmQ}>S=F=C_(dx?RUddg3=3_TOB{ya>#o{?pu@ND^ZUK z?*=*?xTq_Uy(Ka5k658|qxw2>NrgcHo^@q_l0KirH#yCt%S3o0*=aDIdw@vo;ko3C zi_gWRkFf49={vYYfzsxkdu44My;#)N8HQ`^BxBft%{j+Pc~cc5a@Ctk{E*4y5Jeuz zkHO|k?laY-R-G;Qo!Y)^B1ezl^Bkj=ec$#R3dIn>MA%I`L8dyT4{LZ1L#V4*_}muf$Ud z^`J2$@nojSX(2v&D$PQz4fy=ibg2pb;?22{RJPZ;;=AMU#Xu+Ljlm8L7B!=JeD~7= z#%bX@hS<{t){*xf%FX`G70f4x7+h|7igOJgw!l4}E7S!`V}7v~EA4pp@Z7;XDnWOW zvd`rV-%EHc;ka4iwO60?fy-~>dz zJw-5v4mTp5lhgv^GJJ)qdeC@i-?zXf^Nk`iCkxZn&`xHfdsOguYL-`h<%7rPzYlqgxoP18OXt zKmwwX}F9%1b$fk%yZ@%H|9Y}E4(&olgqtYa-Q4By& z*J-OQik&)kc|N3i0khHNoblnR`&n7%&AaOse;qHD7z~z4i6&#An)2zY&sfptN!zm} zLlURaGSv+kp~FU}-3?!zk9f=|%@ZMusrrR`Jr-aizsGCE(A)k;Um=?Ma7Pcp&F@!Wp(*i#QC~&K`rhR8Q6Hx*iS^s zZNFzQL`i!cyI+S;o&hMagZwUa%e&g=@2FB*DvTDeXf@K`7Od^>>rZNbiVM^2`rWVY z?qoJs(#((y-$+Foczhj&hUU4m<1#P#sQFAF%O+GlKK?m2BOq4MYEQY|7*Cipp?w}; zfd9p0EE)Bi7K^)E43aZMx$qP|OEpcXdo0KuyUOVd**Q{igPU;_Ei?tzKO{*c!B zKfWp?4RKWnH?}mal0Uwep6am_6D@S2C)82fbmQKibcdQpv&8%R!RA?A!bw(^f2S zTA-7$DqbjIRV*I+lt_0l0W)vK0mP}TI7^QW|FWyJu*5zWpZw0%kY$M|C1>FxqtyHKBqgxeTzS_I_jM3eY{?yV< z8CkE!S}krd@lKFN!UfRjJ`g9?G$i-6Wr$$m^wJ(ExPz*7-E`6+jd!dtJ;M574v*S# z%&%WR9GFfw*KBN$Hd4_092ZSPYF}Kw$h%KbAkEKb)Jha;)X75}xq7GvKX(|j zo|BGs4qSL635(-7b&B(;dGou=_tLta6GaqLjcv)w#x;I7U*497pBvw2Q8mNpGXIu` zoSIM*F6k(A=FpSXl_7+O4&Z&QyshWmK4Di0eVffdpJer>P0vL9P3&y0ZqD|$iJ|s8 zHV=JG1+oziwBLoYI|5un`U3H17Xf(p%ensmlF7k4x(1Mc0BiWmA`o7MekIbE)Hj<> zboxspH77?r=<;6x>;HQ(aBHPFaTYjoz}krX0~T_#)BF*GXO7^^Jwk3MV}?XU3~31B z9Sh|>bb;v~Kwq5?;#Y*eOjt_Mar$R57o;YlXp2l4t|C>-Ja??}zpz6YVFGADOLUov? zB<#t;J5s;V5uW*=C52ZK%L@2k7sCH0VJXYo>cJ5wzU7tWG@@@F6mHI?h}R%z7GG|X zG>Bx@P&ywI)VN4Ud;sUjH(JB>bCuGq-~pQ7)y#wW3W!Nan+tl0lL-%*cTl8LDFk!2 z?s_q++66OTOhAQWgzgmCZRM&Sq4bR$o!11?qFt>5m@?08z`A&^ls$d=(`_YFw8sN= zgf|iD(87$eBPlUed_+gA>z!eCkXssFF%kGNCGxz$S^B&LgN3V9}A>yyz zQ#MF}xY+zXg3MQd7cuofY75#EttEyF>Cn zXJI*2x(2ga&a*VPU$9Vy*0b}^kQa$*)%WcD>{c{m)n ztGU_PaI8!=;xd}U1M52IOdF^Jo`7d)@Xwp|eaHC`yIg3Ri?%xAQO%nTq$hpU(27k* z8JqOva0@i~O$wUV=H+rGyEV1$)q-nh5Li5$HrWjDahqAN9oh>Yf;4zjiK@wLGuv6w zQz-luK94|5~%+URi&kU~%U>S}rf9c-<>7;_mCtooCee3|FgMajAa*yOA!KV=DT$P%=W>N9r~ zdLFv6e0|^pSXjg?>JLFCt$J1gkhh+VYXuX$?M@v&VO3`6<8; zYaXt4@!#A_a4Cv7G*~F-6AU;>VChU$<|E`!s#hBPeD5;FErNc%?1A&}ew5~jRsPh= zN{{8z7r~PWK)4Jw5Qy3D>Q1>f`n<*@vWdD=G~$+?8_i)fX2-(90&*@lU$N(F^2czc zmwk7s*_9k-(7x-mxxo3Oxk~X~UXX5v6o!;ITvMqxzZT}tB!ZD-Qq=o+S#QN4@mZAh%Zz&Sv7yq zvS|-fzyw^6&~Wc2{z!GYCy#5L@c8vMry?0@X1qQ|o1m)E8DjO#6(n&HtUR(v7|5p4 zSi?ICuxfmn1`?}C3JHI@Fg~`v!8JqOgr$IHJdOQbGQVQUV#6^(@iw*5oPT_WG<%wIH9B}i z^g*(?u;q+#qvZo-LDJtsUJ}DyR5>CGjwgm!$n$||CPFgOZqlHqrH;1>>1V;sOG{-f z*EVP=wq%=zfg)(awRJ|Y6kC@>k*G0LbaVwciAW;dP6JPasqK`#m;<}OZfk^WQrL0{&vZnm1iVKo+`Q<U< zL-cFZ#7%m)P}wV``ds^`N*-50jsO-{nP5f@bmNP4iV{B0y6JxCu>pzH#BH_nr#CZ< z@NRepVAr}%)qSo*emEeObdKAwjLb$K{wz&Ndpfk!N&Bc$K10c<)79tgTIjegf4YrF zq6fPLJn3a$(*D>xwqo4nAtSpjndGXKV@mk3^(mtn{U_rmIJ1Dq#k#PH%3mK93azLj z=x*Kn>E}Hx6nuAD7R;-pSvUTYSA}yBpAx75g->{D4BHF&N9p2KLomu@04ywM)Gv-h ztC>W%!V?zB8A?{0kNqrv6hxk#0w{cLRFhK=-y!3cYmm|8JDofk@wHCX4-_!!-Q88* zu9ig|lU5KbOe-Ogy$05({IV;p+{i_X0#aK^A)%p#8kM|fbC;6pmS5_ZfYQR(CucUc zDfB~FT)00^Pfb5JbJXt3f2n!U1@N3JbHP#+RRa9ZzXXJhp9#r4K1!o?R6qFIUF*B& zTF)Sn4t;1-BuLwwpqc4^b3qprqF+~^ZgJ1IIp(orZ_DU1rVS6^(Jr|C(E9k^(qv78 zA-J?;eB#%@)6zqCsw|Jmur(j|j>M;tl7E0gW5~5DGpX+Cr#<~XjsZG_uge+R7enXc zTe4)}K8r#=ysw_Ji_W96)f%ErBRE-OqcJ7gAYqms-KYxh2)BiEk^ z$)$`JWNZVZ_hp8tgDzo%Fo#n$Ljl3p9XO71rt=@*I|^~({U^yzITE?8LV5VN$$mk| z|J~2|Uj$S*5tmM03C;_?atAFgrW08-lTk>-75pgwpWqMlY*l-IJRhx{e}J#r?5LXX z;(F`br%uVWkF);(cfSyg|4*A!XIk5Sc-@Hrft7Sr_)_JN_vKbnYf1^8N;fmKRpdXh zeqZYuFI2g^5bT%#kQjsmP?6)Y)pqx&`}JQFUV9!MqZcTX6XrmJsXBJNtUpGtUOmnt zM*}>ZfK=v!a1IA440@!PmGpXZ8SH-l4?ynud^BMmeo63Li6|)c58%@)WCx1WSkwj( zS-7LneN?uhOCxqY;RS~QYv|i$|LE3*v7rHOM=fV#tr+$DhlKZPO8J0WA=3SbdAP!- zmr|yl+|;-It=uk==w)#m$JAr zd{^hvUF>tT5l&bWqv6X~!yhKY^W>Eu%daOyv5`(=XK9K10g1ADHZ6FRHHF_jKncq2 zTD^2_H;<*Bls3DVO-`+?g_*5cg_%V)^3fKuj*gv&`BK9h>aIzAG zxUFjfSIgnp8F}DhHa!1ZgqX{G$=QZagXRcAsQ5NUgGUd1umD!r^ofO{6{BWBeDz#a zMj>RBSyz7_GyyJG?NE)YnhgEDZET)t(I?EgU@luWDO(0)T|y%TdbaCo3Lhevz~^$(7j;88hAA1C&QS9#04HHB~c2@<3H-Uv4t7XbVwTp(@y zKzH51uE|IV+e$^@3!KSDOQwJM_;*DN>BQ&3$3J17fMO|7z?!FQY zF~nfnTZe|T9YsG8EKpUOicm*Z-PEbF|0VjaSx$UbPEQwFi5uvZ9LQN2Bz?+Ppt-~foMd#HJnVHU9h5JCXQrfL_bf$CnV z1nh!A)_rKjQx+8`e7-2icAFcKgICz>fwwe$Tkr&-a)f^D*BlHPK1lY@(CT+c#lL+1 z0oc~~NTRF-2sa1Dvx&fS9jA9ynNdS}i<~IWNl}lgXdZlA2NJ8*Oxx=?Jqxf!!95K| zSV}B6Y-c%6Mdb?}(Cd`P;L(Ii;p1@g!C$$alb0L@Z1*4HOB~8PN1O#)!OBbckB!4W zvl>sv;fUTN3D|n>9EQ{Q1QfUww9>C?Qy&~xZc6ZZ|0sMtg|}Q*DqM{vvN=G)ZodBpbBGvF(9ZVB?^RL~6tHmfy@6y%Y~gw27NeYRcXzQ{@}yByYqB zs^IA#YG8SN#MrT&upHEURW&ONNbFl>OPgFUT)BCywINFsYl}U2F^XNy(?1IDu<4M| zE1l1M3jwb%1+{vfU9TI;xwku1NGB6EZiSx?OJdd-=5D#zAi~gor*wOsDdHAEp6iaMUPu6r98mW0n??+KbTR zWb9mjHzed0*RT4515lDvI6_!54enDaoz^f0q{PZu4A8=D6tUjVf+Gq0qcRo5XulVEi5pjr25@!rSG9~ zd6thKjXBs)#M_Ie_^TwAIQeIJ!o+ni$pr9POqAn5IRY-EOS`Y)6+{?<*btl=L% zR^Mxw6`4ZKEGjEPPaNMKPP>J2k5-OQ>fpEXv+|^fR19|KN~E)gC#LPC0&y=@wAcat zDxX)6wE@!mi}A+Ry3ytG4hSXr>q(f}jv<>=+lwb3MWFDXjTFusC(MY4|AI=%t4nIy ze;l8@+L3KTek6L%cXoVJ=G13wZ@)QzZt!`Go|M19g>xd6an4k7G<1TsdIfc)dl9!) zYZ|6?VGOsZ9a}mE$*n2}O`WNHytOWgk;AA#-_w^y3pa9nE6a zywR{WA3sqdtxa}WM-!PonMb3qG#(a2f`FWMvWN`CHn&_>WxOgUxY|m5A zRq=#NTt_gM2)u4YS~7sYA8jxrTd;W(yT5HhHb;0eab!;7AfYdE+MW02Qq80@HrL)) z@E^cWp#b5ol9`rul_zJNTZ<=eGtS4KqN8B$P&=XZc~D!5p$Pf6pX7M!XTItYly4b2 zHA-Nbr}pFy!zX-<<3tVr>4jDijy3E2?4Kav1BrqqocZ(bjt0MBwWdCJ(n!cgWvNi8Z9Bm7?`OR`dMnZWy^i+~Aga zzIkZN@AJsKJqv1i+4LKVNQs_aW4CXvmTZJQQ=}HL+mc*Fy5=2P@rA~p$_Uv5e^6|a z8}Exo4B0yix^J0O*(ET_k9R-$>o$%Bc#2;seY|%dXlTOVoyXIH6ejQn)QPF2Z!Ha` zc=#}g2;Yt@SDAq@e4KT@B$q8*ti7YMiZ!=<*Z0wsrp}Bsg+7a{zHR>Cj>4-zML!fn zSZU?(3D(Ms)$P2JBgw{F*~d;;Cb`hTP=VuoPGc>gF}ZsXQ<{nvzMUuP!Dq(RSgb^M zQwu};I~V?x_`BXUsn}v%_8l_yQj1+#yzN^Ot!~YSS@}jYX6Y?{Gr;RbaJfvi9YR@t z94l$3{P5K;@N1yREy1Qc*+7bAjfkF$u~1`bKUr zk0Q1q%Yy>cpHu<70ZT5xA2$MbYG@tnaTW(mtTaTCZ1SEV;q#mXqNxX-s=iO8TtQfr zMM5r+p581#6+J2_xFgPt<@Ab+ZZA~pO4XOQF-*>+3er>*$IYg^(eZV~YU=&QG!t=` zvAII9!CRPbX%uxZqJLBgZ+ACd#FHRw__- zrTMFdGEl_1QvI3U`4;FQs@DT#h7j5&@d^sEx>P^yJ`ih946XRSGAg-TST+SjUmxUJ z()E$bj8Dbi|KcgQ+jQ&_e@ciGgxY>|^j@-Q`COKL*_`~riWJV-o@;d(y5X z^Zij4p015S{SsW~VwLH^y0=NLHeSrFw2APH7W@O+)|<1X>S-6C@b{{7Iw-oa2G>;p z{kzt_IYJ8FW-5FZSTs0{tqM<`=yxH)H@gJ*{|z#RujT z0o;E8T3R3E+bSdBV>O~S`o5Bib=C^LUtO#v-@@nqXS7!yUJ45XtDfG_{0JdyE%O-q zG9rZ|#v#m{mxrBW9rX8r#>%N*Nbby6m^*7m*Tml-~yY12pwkRaGJ&hR!FixDP8nz!6P|{2Y`u z_yOVSAHM;iF^f-CG~+2s01GtEV;MwaYvs75NSOCXj!6*Ptmk|4DR_vcbLH|rXA z;0$VLsM_RQUe?3Azbwgr^r~g&L+Di^EVw-j()NzXcJbiYy71306d*a)5PKgbiNmZ` zd?d!)=IhG6@>L)&+>V5eLyL{+$oQM}KLv-fdyNz}!)@6J!*-rLVR9!a(ID5EyZ^jJ zyEzJl2j6xcu0jtIMLgzzo&aFJdLVELZbU*1A=+xVLu6`~q?-ao z`IWt2?YLAwRZ75Di}Yr=^r^DmEamVlG^SEAhx5m(QAV@SB)L3Hs45YU`3J`K^0?*w z=a%6v2nxqtxVFL#7@)NdF9A+*vGx^Ns>G`58t90VoLv?Bz%UW`(T~0vwj1bQmAA+% z(g$x7???SYm84m2un1F=IFsX*Z~8jvTYxa^4qo(vEqAFgoAkDbs^ARwsm~8MRlZhq zCMbpUeB*jdopK4@(VN41W)VNXB7fKhcD6vUO@2_0bS}2}zie9k26e4)oCa3UFwMy) zXAkd%hp5TXa!CtVEqgj+wb(ok{tP7Y6}}Uwj*Xoin;JVYNLl~sHt9L8GcJ6C>kc?d zG+v0TxBBjAG+2||L4QssshmROVHFn3pnh|w; z#kNryiLE1X=BE9*g{pI868wDPC}3wNeVvwCYM=LH)nw^dKcLvoGZ?E*k4vs`@W(D& zurz>7#1Lk&!~ACqIiMgtlV0E`P;I7V2&;OE)xa%eDa+RAP>ZOtFYvrBFM#|ga&OaE z{=pZsiH{(wIclVh-B+=l#u09P^4=VA!}tdfsY-K+i|ZEBxrp^CPu*qm@Bu|-#wO-yTFc+$TIreN1;r>d$kCsKI-n!5h&32{;p zQ>w3z0Jk{FxJ|_W4(wj>uK6V@lFrQWq1pec3;cCEbqCqaRVVyjK zT?&CN`Uk8SSLcXOF=t1DAhU_sWBjgt?mRQ|ZC)VUfXG1wPXHE)u3YdA78X@!G?|)}CAeD=bbn4pxM71OJtyZqfi<^H`#%LRr@54O zXqV1d@sJ~pj$JOasZdqUG5B;bHAXJW`{*Q>)r#B>+E z%9zWn6ic~B?p z$&R&Dcy~TG8rH7uej5vsi_Dyh=*y?(~o6xglPioXa`!xAAjy5v;$% zpZ#erV5v_A&V1|J82g1TwU4M0?&BT^jDdJa>m79_QYJjUzQn6hX!=}&ww)sDJgWul z^*A81F)e{wMaiGsT?>}i94@pFvJW$M9n{vUBY$%9e0Htl4Mb3PS~1+PB=(FTpBSGC zzZ&;Cy%X?Qel=Fv|Dl|L@LriGH92v)I6!1Kf!r{QG2F{V9w{Eiuo1h&7*CPbYCGYl z*!<02_T8GbMdiSQlM29uYDTio;w5|EYfKN$Qv^&>`E*f1@1nH8yVGZ{WL`w@qDGpvwRObaL?aeHi6k`X8rvCt;^_hL^WnJcF zP1{Z}DDmKPFqna$y{Y8s=(@Z;=V9>aa}5}v0utSv{J|*sEfpCLI_qyBLQi;7*LSM< za=SNWeIdYFFtr!3@vEix91Ej3WyxxLb`0NQcQ@*2{oB*>rTp?|`D9RNqY?zUQ7N9% z+m^)*ujs{J|0u*;)))B+wDGOSjBG=ai>JMC=BCzFb^xPnbCc{S*2Ftvg0R`uQCb)f z0BMaNi~|ScPIVfqSeHO%26&vY%9ZSy?yf|P>{2~K{Ow7p@Kl4mC7OTl{kT{wn-e{M=JR^TT+Pn~bi2DXE|2*S;y4AnV`o?K+jZQ@(69NcCG!0<>(zgoE5K z{UCLoN6L98Cu828F$G=lia%;=Sw=vegqQRHkJ_)GG8?^&o~F(Q!yNiadF9lMM5OA} zm0xNBHd>9Xn)7aUTAhp5;1S~+Jc>U)VnE}2Qss{>jZ%m-I*^faU3SlIUKWL9a~8Jc zAB(c$IvB*Ao23^G=g(EtDeqF=#=vH>kl6jLdvD6Ks9--GGQO4fY4zF z1mBiuwodgCkV7zcJfKwKu}TU6Km}CX1(ze@%N2eb>gm?a8c}8~vZ6-eF<=1!H73E2 z3%~HUppB(Cr*NlZ!ebThcG?qM;RrcZ8gYqDiPvN&*oT|4ro@ouQ!Q>Q5WZ=L;rYAu zD;?nJ$cI)j@hTzWdCuzi2p_W?vay;3kWqU73`#TP;sC(BdQVCBpLC};43I9V0hJGk zh5&UC&>BVE<{;4@fsp=83J!eK=E3Au2Vxt`TNGu`A}r=4T1!qUQHS_c#MyFoY1_DM zbm0{pOJUpdbv;_PVLxvnJdyjR#=C)@aMP`FUMdlM5<2=u%H{b8FoTY)kI%K|@k#OF z2CfB))V3T9+1Acm{yVA}e}E}C_Wl2q_vP2!6`?K4`4$L2Kt13cq&^}na0u~}w%hjV zE!3u6J&kxoy`>L*h6oH{g_{&Z6U=#8EjPW3o6z|?i)4^;l+pV~iwgLpu*KWwGjlY# zfmoOTS+#QJ98Xp0#l13u%Kl3w%D^eL5TGDGccHWt^1`twIVx#IJ7>I3Oi24e)mN^}qIvSl2 zr3>b@sH6hCsyd9j?p2)$19=V)M)pOYy;7mQJ?6Jg)Z9hHANE}Cp7dwqAQgORw#5+) zTBITqlyIWEH9~d&gorTZ6Q+3ac3Wp;M1t&S^)&vQ_<}{#zJ|%~rW5oHCq3U=j-=P~ zU9ojYc0>pJZY>uB9cYqe-d=ws)TVH1U~xQb)6vO2cOgf0!1&QsC7IZx=j{Bs;Vn9N zb6$?%Y6n_AYKpi0(wQ;JErV!U5ML~%rZktth_s__orqF2|0#FBOht# z6j&A?s-Mhr?{x{sQrUf2#GDm~CqzET))TaqI^sx_xKIBX!-Ui1!hXOp(XszoAgv1j zGstP`@IUK>6k(p*A~(r#G)DWyuxk8H)psZ>0?cbkl_A8EE%V#^U{>=Q<^h09`qyM! zh(k?6ZJ%q$_rnz0fFTp?w>?HifFFvQ#IAj??@ptP@vxf;6XSkoe8@Iq0-7ypm7?oz znBW8H{_GigqI2dLgPSPaDG~$}hI>Sk;Lnw|HbNR)AEAm2x1nY8H=0N@-Uus}YEMqh zB1ob6Uk4MOyz$E%_kr#!n9~8B;XL~4`ZA6 z#chlbj+Un{HQA6|PIM8ER z#^)tjG(s$ikh*EDkqbI#ChuwokOmNyg>lt4X_QxPINbOL_#4Q?zr=En=@ato9`yz} zdX!xl@HuTz4zQHV;~&=5%0WZ}ofLnX{YeG*3QswEQkWjQwC2L6@eX_2ea4$|?4Jt! zu^WUR!|^m^O;_bpe3BBsdB~YAv|`kj=4|tSi3>p@QEjIRJzs92ZxKziVrOTCrErO4 zeM%7&LD2Zn2R8c5b+|*EoL#mp5CtXPbf}%{MTkZII_cTeV&G>6;>gBQz zTW@ZqAZhL;;8%M?q|Y{h2K{pw$_U;VC#Skr&edvu5ZQvh36^R1O_VPt~NGmG%7R?yQ?{(CDwAM(|OtKQYjCjR>adxXgF zCUwMX`mf(5LrzuXj83HohOfV96*N^GUCQ3NRbeI!t%kgzy7t$r zJW$GH{W;=+>pcD0c@L@5JlJMc`EwTOOUZjTswdYcyrbJ%VE?!=`Lr_Y*mCbrnp8u} z3aqO3ut6!*sn<0!q~A|k&~>@;;Ib3Jv+E1YSP|M_*$sgY>dgr@2A`g*(14A768``R zZjXX17}tlYT=Ro@udQoMjgzgu9FJZ0&wqqok?~&HVy9lm{{f)c{{WZOKqF$$Wo4iU z8{a8h+%@dx&K_}0bnOn5>MO$5o<*caq4%1?_8Ve7mcmlA4!u+t$F#cka6lO620Hwo z%diCBkE{FA*GT4hd2$+F*!VJO?{)erXt55G+b>|{N&}ek41fg{`wK*kV<{x11NW^b z?dr<`9<&amqB5&g;1 zi3?y8%lH^f?&aDEdLkzRr*7wX#2b3xX-N^<6Th+TJY&_r!2_T9FJyvZW3HUDMlrG8(Nf-?vG(=joP|iX=T6#Sgklw93VO$`)8?D|CMO5!g;1*b^rH-K+)c+rx;)FvKfjcP-r68nh9D zf3pDTRX)EpBLoS8&Wa|3so-*rc5A4DT{nctn;W4@$IP*c?{!_Jh6(91+Xy_i5K7SNk=ujs0dNr@k=hh zHiCK;X;%3VQ40V;y{sezNv$YH8DI>T4;OcR(nh`3@rw9dx*ft&xxkw;FV|3m-|4dN z|0T{h)%}If1Dx9Wq`OB6D!4(8*4*tTzEA3T7r{x#>Wrk+Z>&fx~@RiCw5z$ z@tO*BICL_T161FkIlY`q6YufX4GDPxfMB+usXM})U#>I0DF1`Cw~mUV>Apq>mmnb- z$lwGBZi57OcXxM}!6A?!!6t({!QBZCAtboF22Ega4IUxnHhJIYr{BHn`{S-Pv!8jJa_HNh#q)OpJc|C1k!0N%}lVq~gKPk+)RKFY&{*wNwduU*|+7Xw^w_WeR zU%nk6u2fz^BJ(y$;J3T_FA=@0ND}ZWIOGd-Py+PxB{4;M^!uf@we4P7JRkSuax6*! zKiNEE`nmZ!2irI*el$5?`Bkb`0r}EX0k$yHBGxcgJ=)F_Gh`Z%> zc$}XN)oYy!=N(Cy*g^uHo8`bva0mRbE{HoFw^5of6+*+q)P?pldrnV`Qh_*jSW$5T zOdyDMRTErTAK#cdAq*M_JUJeOR2&~Ow*3L=<0V;F=rR&78u$mUFY^TyJyr=!;%>i0 zyLI+lRLin6uzEd^dDx1}tW+r-OL8M{OxUYlxwIxa~TI5@G%*Ae;tu6fU zBD1(X=B5f29E^Rs-bxqQE7O={ZxbOj%$n^6gv&+rJpB_nI5)$Vm&)K)s3Wx?bJL|g z`4X>@;@y@(9mfIk6C3}Xg^8VY!P55D)+}x*64C%s08KA&z1!@?Z7OTyI_;X}EGx-^ zU+~}&HeR27E&#}yHxGPOqloL}&CfZlSaFpzt|w{JDzRig zxU*ZwxWsfQP;#*-D~niJ-IQb3u6aK2Z^WFGHqF(9!11TAq*d#5olRmjGEI;^cz1V$CrO)nx6G)e z%6mb@@_aKjeuUB=Z3?-Z|E{&AfzXd_ia8-y&HnVM|o?!NHy>_yKxyaq& zy0HFL3&$VO9>KF=TK;lEUJu#6kdgOM9cC0f%!d z-}dItp)nq-vaZ6^05wjVxTd3>aK(7QBVrGg?k;2l+Q@PEtbtQ<4uDYT166AOnRq2E zZHWg8foA}RG!1Xl!u6rVO+LoqObXt=ubD9r`&2%#%1Z?PIX*;q7S&n^zl)1a*sfA( zz#t|CqxKBn~#cz>bcuCh}wxx!7?j1!A*nX&v+DZgEjUtdv9Cq=^w)Mo@Nk3 z`K4znMj|L`g}kd|_~$|sl0^6In*3+ItoL&r&sr8&<<(M>mCOZC^(#oEkL+5bA!}Td zw>D>&mHII)JCZAc;Ny~Lkf$SqR5i*W{L!518gVF#rw|f6Zxx@QaGW@&Qvy?=%T1L@ zPonRP%~e6GExw_*GRj`#V{p*G`vpz0h;hyFE>@8afoK;WN><(7w5j`15nD_0 z0N=v2FA;38GnrO^+y3kJ?EI%T!i`yJ(8P9y=p}Rot48~R|8d;aTZY2fVq9{oT7C>V zN+GgOptW%hV=TT=Xjxzepo{}PNYiN2R93LOhc;wM%Q@nMz)dObUE?5veLPqCxN`Wt zpj5S~pZ>%BFlF=gY6Liyv4C_!e&fe{joLJ4q3-*{q%U{=2-L}yJabF4myzes{jCkH z^}Y@cAw>ar&O-L%xsl@Ksl~T_`hID&*h}mt7PdH~KmCZ4uMH*f88#=laRk@$%uQyg z7~{4kg@538_#WL%S_m&xIcpS2Bv!ul2$d;;J{1+*Eo*jn4r1W{)W@UN@Ilu`q2<)m zbn6FfHt$u5)3RAVz*UpcxNvgOfu41YOb!Dd@O`dxZQ9EbG9RzrO7IW#Iq(CON(cUiPRlMf}N z#+!V2e>6Q||125Gf`Daxy7qqjqgx`lL-C53xFosb#6wrVtkax%xiNWehE@8JL8sy8KvZbFKYwd%Fx{sx!Jkt_~&=O}EnkC9mG{>e* zDi8`ue7Nw+qIVGRt=6pJz;PydeYSm;ETol;=>3Ax&wvko=mD_yXF4Duf*i+m)}qfV zlXO8<=!N=A$-gF`S0eBwhiz7dVbmn9c9RIPk5|L~Z!}vU1&7URk8=M=m*Mbx9$@xL ze55IM>&Tu}mg1!Ol}`g7Wl5RK6>su50F?fX@b}NJT z$ZHa*-q8r}5iEK4#&y2uB*x$&jTAKR<~{Pz#3f0Uj(^#7S=O`?I*S(H)1sVd2x%R< zofvmxzj6*TbUQy@d@b&8-+sdv@8yo#FtJeTC_nTZk(g&~@chP4@0>pEyvgl#L*nN} zRd-(>a%-P$mqh;zl=G7-s)0OCCND4WPKUAb+ZVbqpZvK9{IO=*f6VpzHy%G?eRR-J zN2w-BZM1I}7Eq?Jpw5m-?XPmNuOEgfMd`_d;G&pkHpn&3AoERn1`FWQ{gOm089{m* z_(E?rmO_~SeLmwS^}A-a23dQ|YZIEUEb-e0%dtoND-Sb^VkAXS(!^D!>KH_6V^6s` ze;nl9Ocl0^ru_k}Cg1%8Xp1z}Qv~Ck3gN7;4{+u7l~qNWL7b8bf`YtMYX4Sg`QM;k zFi6|rt9HDS1$i8Dwi!f((B7|_$&tW!%e*Y6esQRNtQpsYodihxSt{s|0mPPK8o#t- zFdXL}+-BEps~8io|J2|f$OyTz99PYA0;z^W`e|Ecn?K?Do;pcMQoyB&;vvPX=kYc2 zIc0h8Unym9@%9P9Rp9)%(j-Z-JqRGG)&|5{0EwAABf|2M&1A@)1B&qOLW*B$GF+n< z+$WS=rzF<&K?`CHw&u0b1m2&rKc#*9iK+>-=_LB~ak2_$=kH1e$``ZmYS|hE1x!#5 zb(ahSCS*k;2~}nd8#-AMpZOjRmF6)K-E{u07|)6ct)$n-9^8f{PAK%tGt;B?3hX() zRddpLGZTGDPd zXw0|nqQbR?Kj@r5n9%4Whyr(fw7xPPRxeXeqXVIy$dj~{W$Ob8(qC8B1lGhq#ap7( z&%brzXG+`I5jIe1$VQn8%v0)}*-~`d!hdKN)NZfwpgC6de67 zwk#*a^F!Q-c#SC_7tq3Ey8PZ>%x;hX_ae+W$zq)0cuc)EpytJ~BRP|G!_A|_2$P6OwqOjHwcZ?`s?O66F8WN3keqz$QY`Zt6N~K%?yR`T zFEO0#C5@2^CnTy3-x9}A=-R1}i!ILV6Pf;rCC&ZQsp;rEQ~-X4rOYY~RnPa?h}a`7 zY`MX*?_^7G)|ZTN&+vLrQRehoH1(jLo)MlCF~o3BAkcNDz<_)d4tc}4^^ZKtX8ucKz!)V z@NAgYh;-OoK1{_Zx%p`Xq%fNqRG5~O7K%t8gC>&p=AymwThDKL2cJ>2#mIGxvq}8f z3aq23nNrYDq(K?xHNmTU=tNk$AF(NrCP!5C%J1VVrOky+cJ`Sa|Ao!HmW{{B=W!ix zSFdjmo98a~`N%VC^YC@j))qs?Trb}3H+Xf_sjjO1tdMcVjzULD zcT44x^%|yC-M0dhPP?bxit8ten>M}IA`f3I%DRV=YA@&TUT;2(R2Wd#T4OGqol;{y zfj!!vS?NxXBxA~{diiDCW5;~6o{l6d_!kg&|jxtI8ZQ#$r39o{j1>?fm-?>2|C zl`lH*>hE;TbU1E@cR~e9EPRs+rt~($0OET%MVn6KSWF2Z9@-AE{KgJr# z>rbMoDjB3*V9)xCr;BpVr7IVZ!P`c)KOk*Awvsp*T6*kC4~dEyaTcB?)b-MWNSSz8 zB2qFJ)P)Aj&}1(kJTh68cZMXXe9gwg9h!H4eJ1y=eHq}%pcK~KXKekLApsVO|KXTz zy>GBs)8HEmBI;AB(IF`w-jmo{#4rgl@Q|-jVK0toAg77%h{I5JnQr@`vqhj-W58xm zm+>8oR2LZhsOdeE+kSx^`<^^=mBu|)=bQSEeg)e!wy}CeF5h4ITcO9t8b#r5`9`Tg&+M zLXS)KJRX#2G;rHu$KA>|Eo}12>EK~`tHNeHqgAe0Y0beCJ7JM7eoSG%f(>UXrnO>= zSVUaF0kdgoY)~Gwg?eL>I#r*AG4pd{nZesZ#}m>_$pM`0_=V;bRm?{%M^h9hW5JW1 zvcHrh$ot&RtRbsYQXumFe z%jEW~C_LGV!Hl2>iZ7^;M!q7Sz)k|2t6vR=rj(lugjBj-wFxgiM1x4kMA zohj)G8Tiff0T1z0t8hpODl8{vJMhXV2AO?;JBk}!9~s=sK1lI`4Aj=i+>=3kv&BCe zrw>+ttm<}F981pC<#WIyr>Lyi6PL&`)SW?)uV>a=5y1ScSZTm6{#oe2lw*mX4gQkJ zf+G>XZKTu*oycap^IA zyVr{;4VXqd#!Id+-Ei9TQJLSZ026~t#IBT?w|6E({P9=!7iyy5d4 zHW{kV@3zkQyl?cto_e_#&o+Ps3vDLn2*+gf7yIDtFaB*=Z-c^gVa`-aQLa$6t?3Zf zl+V#8ljRkX=rD=W#PEJ7VPOIwo&3MqK8qQ_posP^fL?PU^>4r?s=TE5js^733O7in zLfPYe%_P4q0_Lv>?MHal$r7Z3yaZ~hLJ2*F9G9$IfkjIJ2S!Qz+4k!beSw@c9*|R95*Rwo{*&4z4 zf`J-OBP4e+mhQ;v;ns|T;1d#BjTf`^S}vPKv>^~#-tq|;5EH^L?q)>IeZmgUq{H1c`w*lf(3X!pHX9F4(zHCbcm*edgRSo{bG zP0$+pJNlzVNKS4#7@^G#6E!MGRjlN`*2`%wl;LkcTq8?V*`|9U<&J+W*!)DyY|}}A zb48t+ZT6zpoNx_`^q7SCYwQ~`&npdHQ>sIV?PiA7+_)Ljh~LNE%~xI9L^41bpZcS< zmwC55;BTMEG_ib`s=FW8R9mPV8Mmeq633617r1mc5ofIZtAL_fVCxLfQ)v2rjETHZ z3T!Q#iL>F*)0^d;#bij2q>B)((t|tl;jaPgauLO)>0r!p8cM~iFhiOol4rd%hfP~A zQ=YEpGoZ#Rv7s+rmezL-7db)(k(ws_qi_p};--?}&FVN=E!_TYbYOO5rWvXlgVWLw zBr(Rm8U37ic=cmjzY7$*Bwo_s=3PmDFK+`@7mB7LIo4GfWj}khm)*l6hO9&}95mY6 zG2Edc0j)r7$WXKv6{tjigR}zS^OSJ1SPP%9~>89*6wG; zrzFk_`K5ef!L4|xkAv3fd8TrvU^C1i$E~VzGb5DhFpF*-YS_l3%bPfLY7)GWr!w_n zkxBWHdX2;9FU{|{JkIL}ri_w9=1$xnJ+ujX8P2=)bU+Sv0@bZKp;AX7#`? z@5A`q-18?7+@G7VAMRl>G&7_<$av=blH3i3uvlo>o*KDbPH{V0HnjE$E|HfKH+pwn z!=^}kJVF8Jr?v9&lBJ;N?!Jy8_N|>xjPILom{z3PV)5U@-b{9JeClfWT`ne4AC~-G zz1bvzw(Dps0*yX=+^fRp1$v=knJ~^a&V+nxiNnV_^rdKdI!+3JHt9{_W2 zsAwgrE8-Q`_6yyN<07d^q963+5geD2+aq0=U+r>!VKmK+CD9x} z3siX~xePP5%fkT}nFTX@{U`1i;FiNyV+Nf#h`$g^0jUe-V`mU@{daoW#1 z1j~rbN|iuW0$7>-obk&fTEVT8^NYw1tx2k}=b*}DRgtz4%CgGi3Rm)NB!sc7Gw$KU;(~@g&S6% z(#M&V-aRS}qjyX-FDt8gLK%I0B=RM5_`#m=LYR?Eb%%DDnyMNYC3HFAo%V3|@nGP1 z%cvsl7L1;`?QD>K=10-?GNQX9w5y!`I4APQT1Ik0@i&$+#6wSyX1pc^rUEq`qV>Y| zY~z)tC{jwSiee~7**W{#oSOwVhSw9^b7~=Zycy4cEuEcBPwWfG$-#>(r%mIXv&K*+rRH^@YSYbhh7;85PNX5E1^koi9v^H5(Y|LbNRoFQZ#3bkYWw~>`;|<+v?ej~Y z1ALiL?S`t2Z~VUHxwN;a@@=J|2sH4mMj|&J$*L$!y&sCVq71;5UzV07hYC z`Qt4Z^g??dgqjwD%gh5LM3n$fc>vXWxeq@cIH1TY83Co-m-M%^nHfO4)CN#01%on+ zd96}#Ezq)zu)tcNVt||IQCcW4A-)$u{VU;<4^5}L;`i>1$U~OJAUfC5Y~R!3B1Bq=7z0Ld72avQpKVF?*^A`ud$C*frrY0F3ag9U~LIlCR9yVi10M}yug>sR?H9lZ%ap? z&kn6ER}EqdH%Mr%Hn^qoLW0>1r&l7En&?gl<)K8>~wvWqk z!dX@D5F)rV(W_U<&NuNcMnmYVFyW}kSb9kqrMn6|1Idx3aEh`>G@X${yTAl}_O=PRYxlL?9A=Zo)HNxMC9LgYh4;8mzS#yMDg{KEB5RF&In}*8 zeEU3uomw-6--mF^2Zm!&I_`Hs*y97)p0JIIR0tJjZv{_J^68x|JL<|ApoE5JXVpVu zQ}v!xP`{-^2go8SH@iLsNMOz3a#}9$=#bRLb}mwd%;&MeJGbI4BiWb-8JjsQ>J!=9 zFERu{fph9E(}?&Csw?~9%n7CEFOpb%V&?-WbWPrZ)6kjE3A4TQh^QeJokBNNia0L= zwW8H-eM?}U$GDUZUP=WV!W}~fKKlqBnxf`whH7xT40!I=I_yw0E6NWERPHbYaLZws zzshVxXyazvXu=FI(IsKCB_jDYcr2(U6rpJ7WMOx)F7FSh|1|*Rs zJQjztJ$(V!J1GR*PAd24(Klf2y;96nu%=J2|Tbrz&$J+ zN>fiI9xSAQgm4{(%IrdLP3~s`Dfeqs*VDr9SyREFRhumCK&#MxP9qS{q!K>mzABJc zKX4%9rg&>6PsuC=loDP8_L3kd9?Hix0;xj?m3&AT^4^!S5eG-0TL+DVGL6TqtNC-iEq@`cy5t>bbnjZBgPem&knn)*9&HB0RSjmYNDS(}swB;T}p{sKh0th^g z;EFd?o1U`5Lo^;vG4zkH1nDqKj&=2(uVuA_9PcEP5s>=68!F#Ilz+7=djOYuo#!OY zd^(73l?v3W?ja){lMXHy%WHmOv_6w>>XV#P&ujxdf7ToF`U0g~$_h2`G!~R^TBnJv(X?oc4S)gv9r~dt1ZvGtrGUJa;~XfN^3k&XO%DSg0l=X9 zZ%WD$K(?kNG_wi3A^JDa@X^1dSMP1|-Z%^4AUOdbyuC|od3P*3j08_*yp0(I=Rb0i zRX2Kx0pg;exGC9loXa?I#Isw?dI1g3?hZd#B`APsr1LNCobhYCnhv z3}A-i>BU8vL#jigl%hrj`;CggCb-80Sl~&v`45`F(1@8>ETxb;px?ObvII~EhHp~D zIqE(9c=epwH#fz^mkup_P$`~0x3oHeSo8pu2_<%TcqcFlAJ$rUftiGx=t98+$Wp@=Rk z02m9A!=dW^kbF!lg!Y*n6G}!AG2nMW^7nf=0Il}oesRFI5x5w_%^<+}QZhr8WzooG zh2ZGVI0TBnPD}Kpkf}NL-0R0A5ETBuz9;~E@_#v2eDB!*Y5)JP-xT0Y+y7<_|3B<| z>Y_Dg^T+{Z1JDw_P8=7x%tvPkRlosWA=v#TL-^M`?e!EId9osa2kvqNXOw9nfz}i# z?pFYoQevMexANWs&xlCZ;(kRr9tnKZoGDF&rbJIVZ%SJ+s_PCyyMjvB|3Xe=13(~*ccF%{7{vXKz{mOrjg8#Jv$wx0K0&u4(_xH+Q-YdY=0Cvy+Fpc{I z?7+nS|H=gZFVDjPOaXP~zQlhj`R|1IqK@2vO9rWm0T&M}+VheHclm%d6;R{mfY1Uy z;Nt%?+0%WXmaAF7`5Bn1q`aCEILUJAH9+l?4;7OSfn#oBO~6M}0uy;ChXi(DKcF_^ zrv_#X)}aFV$-oIVJ1O@+1S$4eOvR{B+AITP4Oknr8tedk3PT^X{haxmLI3bo@!viF ztgYP=COV68>xBQU6)O$F+5{@$&Yx zwRHWv>2BqOkNcd4i{|f!hzO@nfQK!otiQLswzs9XEvJ+!r<%K`tECI4wIvNyOzb}s z@&C_6a_(;4oZ3EC-uF4^c>37>%}UME)s|BZ$|+^(W&8IGNl!;h7gZfjSvPBU8%H;L zPJKr=NjERY|M-7@mW-{JwWp(px4S2e08ow=r<|jwm$$Tor6&y!H>Zl_zqh%8Kl+X~ z-VR=XZZh=##Q$2u`;`Cfvi^I{ocDkKR_~uG$+=kCd)*hH=xyoZXf5ex?_x{C#VP4! zZR_SuBgna?lh6O6d&H3;83i5GE zySuo1YI|5(+X7AD>u3##BTI2gIeL3(*m_F4yL!00-M5Yar=pv;t*3{(3t&Ptc9t$) zww&6kmR`;@(7&Ao^rj1NoD*noZ(A8#zz%GIhH$g@cA$as@$m87U*bPD>YsfS9A(Th z+`1hUg?Zz9vU-zq8u9BSz-wZOr_ug2c=zs;JKyk0ZDlFB zCS#3z@JsBq`FY)|Dq^R3mI|Fpz1Spw4-W%|8Qy&DRB-P; zg&pja+69JQU0pd{K?d2!$H$9D63RrbOUhJMskP!4I#kdGvZ9p$lcKzAN z-xRyHL^UQ~#Kb7Jn-H`nC~32#Zkg$|wzejpY(l8Q(WEtcXZm}k>71^ZVt)O?`tCjc z>oML^mQf<4Epnjwo2{xU`|bBQUG3O%agmodPv0O1Pf_o_j*a03F81_D(dIInU8f*t zV%1~(17WZVZ|G&>)k2!;t3I)3Ib&|FC+|k0pcbv|`Q=i>2r{vfq^0d`+XZ$p{gC%! zZ|jD&@09HNaa+^H#O*$X8$EmMST@_(*eMJZLqW@((*V8JIVa-M3Q~$o>Pk$lgg0djv_i`Tp@ypoeu;`tFMJ8KO&T6`y@WiUE)lo5yRWGNF^1I~n6i!QC8u>o2#uDlNQWxI||gxVa{fw0^p_eG_Sj ziH(ND{v*e=zBlz1PdD?l0_VjG48ArIu8$itcA-zHr34y-7L-AJGx-2Wdy6bepq}?rYPc*462}9|nvy zAd|RV0tQ+;qbp&AQ5Bav+h2b!V;&MsLI(G*KXAn%YV~{k3zUYB?(R@VANQvrtL5l2 zh^wv474`L5QqK&ME`_EzHI7m3v9BiUM>k4^+aM5mj)8*%(8lhMyo$`Ot$m+gn(>3~ z8gadElx@xzLiYzEF37@-ZN7cPo(qCd72FK*|302Q93S~emfdUs#pw}%1UEK%C3;kI z>0}gcP*_hhSn_0vB5s=*3!*Kl{1_K1r?PAp>(zvJ&@GdN0)$-Xi)aVD+FLxZIRlUkOhd#R>#I>;f z*J3|koHO3XYkd8SHqCWWlNvu3i|qA9-l0Y=j^tqi!Z>E<2mIOjUVeUrWDiF-MWJRM zzM?-r6oiNme$4+u9jb5f3olV#MOAfrbkb5}bU>g6-M`z0R{1NQ2R%jJ+qXsO!vg|^ z8y9`wW0&CVokLSGWaA^7n+1ndWzuMqoQs)E5 z%gwI8rP||wP1rrDYun0NSx5|U+Ww8v7Xg!rJM_j(YQ+~iTMk-TScnu03x&yy6eo#r znT1Zq`UW#Wj`#L#OzZSwBT+R85MNde_7(pTNNBS5Cw^|f295cj1|8affvU~hqg zKBMujqm$G7k1gCm8coLeE!`F=&_@D`6WoqUYP!)d6O8ZH6kv-uTY`{AlZj}Y>^yT& zK&+$j$i*KA-r8yN%wsR>T8kN|m^gnP&Ey|0>zf-wi$n8oRYi4w7$E7q%`KBIO5Scx zj=$CNN;%sLSUIYIPqszt+-wOXK7BB zt4_^4k2Ao#OY_eX55g_`8VY&X z$En1Hgq|&Z@3OnVSB!qMZXHa)GFxhvvUl{!@i)b`$(Ty)z;vfSjROkhH5*`oTE`&Y-;fpuoXo#Mz>O@Z&eB zl|SC6VvQ7CoxH0-G@snfuVlRs%yl=RLh|s`)YKHuJAwS^lQgR21*tUcg0p+;%tA%u z%2PfuBIx)w!eJ3oC~JKvk27TJINSqY{Ce#+V`D86KKOm$r<`Oo9XncS>>$#<|9Rsc zJCq>VK>Lf#@hREYfQ1(?F7;>ZF!EYo#(DMDgzi8;eyEO-`6e6#LXwP9`Y~==8@@3K z&Lj8Ke+Jo|n%}Qd={{>gF@j&%4%P&`$|=uQgaO!9(a(iHb28 z8S5M^9Ese}0S}GEL38o6RGWAnuDCH%0mPG=Vj4_kp&$Ofz8FRxOPFQax(kD$ykxGl zxeRI2!G98G+InJodyCfB&2s0FJ^rv`m%u;VuqhhBV@JStu{v zwBR~>V9kNc<@_NP7#Z21yt@i>E0E}O>Ws?m!D1>E4-^%fNivKX`97b#G|u1UZzy=j zjp$X`zKN=>a|0|t$Ol$4e_(*Cu6uMAD0`)yVL`wsa^#YM&Wv*7H23cJ zpWnapDz@m}CqDhO?;q?M-fNpxc2J!&%`O=iY%ic2s;oa)8)54OAtYLun3AVSkgvkm zUYef9an2iUq?qJ>^C%jY|3O5xOzf*Y`+LL0yNBPR3XmfVNt*)#-g~oAuU-TN^@u@p z0xtu?L`AoKBBP@82mNbw7Cv}fceHlnD5R5{s=_(q7I)K-b1gFGW?wC6M8w1lKSr?5 z&I?)@Vw|wETgM|Qh*BnFU&9_HBuedx6G7EliZCoG?2J0s_bwv8O7@v3ajfHGFWJUmD^xkV7YyKb3Wf49gkItw@z9(@cJ z;}bmg5D8(|CP4<6K7$~t9rVDs7%dW!6uVs4)6IAzM(XaaZu5)j=3fF_5jSca{ClFW zD0nDFh1?f6#T1Kn;zEaag7}K_3KRQ!daRb@I4rWM=;V9BPX_9f>bVgQX66NtU`Y%r zKO^I7)gs$z5W5v+WxIRdGCaEzX@(E_JZ{f78#>65IveZ8S>qlR0r^$JN3|1b*LpN5 zxzM;-EVJk{J6ZXQ!Ye{eymT)^359!P)N^BdT4}+a4hvOwSwY&hH}2xFb zBi>6MR_L%aQHTsQ{&4(;{Bn0K{^`%%tAuMZ`JR~R>9dArb_)t!Om5Epwzg8a_Mns% z)$4Ug5ZXe=w(>5!YldPfIWoqdg(M~_9NIJRr_6&%jC+iVoUL-=BhNC#KUIL=46++- z`&$B5IdnNGh}n(1nd%V#*e+&-E4Qvjsib9fo%p%8*BJd$*!sx@MpR^^L4?QNLB8to zDdo{Ig~fvIo8*vrC+kjbrAaaVY@y3Xh*m(YAf4@%2YY8<6-xc|qClZ!<92$izBym5 z7ETa0168?A)Gv7o#qY8rTpU5iY{q;D>)%_e&i0>M73`$Dqkpekwk{pFV5OW^U+W^t zM#X}k37Qq==B{^kcKWl8Gp5t2V3@^!N^Y6{IJRuaVHW(-SBwH~xf?rZ@?FL-ew_8k z{9$p!PUdg%QqG({IrF#E3v&b4^?=(!TnBx4Z)Q&;E3>Q9P_DHU>bfh^=@3l8-}+#j zsJmG#&Z>oJv29^65lfSqyO!P5)HDa|W7%F(647j6GO&AXh1fOrbW}uMeHXY!q(IiK z?49H9M*I=GSxoUTC-RS^ONDRQW6lY8MxEV=zOg`_ys;H^idkUbz7u@yALEl-YxuYd zI47>I&7xq-s)#5~3<#k|uBFF=V;cGy?{U8-{HEj-s&V=#MaTapDDjStD4;w$h(5s ztyS)OS#K-Xv0?5!gS>*_A#_+=3l!pzBtIFz^VUl=HeTK1yK7Iu z=bly6mhq~Xv(3F8>ufF^bc+xE%9$cu*j!cFSaR3{>T#3Z>%&pNTD|4#t*zHS!TR=3 zQU_CCpUG5unQBu-@#CnF#{5F*a5{vk{(DPu-{x7Z zDD&9uLH?EC5l50Pf#^%mpyj#O`vkNDUM7-MkSWG#=@+aCNWH39qS2AAqf<=eVANnS z%`?#A@G$9e%Nf&jklHhzLuX#ZE^>aFj5feYklw`AljCY;)bX;( z($aL*#t^`$qPM(QEhzk+=$q-SX1pTKBmZQukaH|bJCGbdI9TSeBB$V)J#vr$B^w<+ zv!Z`QiS!JpLf(baqvkXGdV%#BkNCU)l@;BC{z%B|W;#pvGv^gUf6LIl_}?90P;|sr*8=GBEHp zqJ(XW;O_&2rHfpCMhi)PUwe9e;&SP8>c8?!hj#rC`78^ok(I(4?`rcnC7ChN)XYqh z+`Mc)k-KdBx~{*K-|xId1W|Q&7b-j|YRABAacj+zDp!;rV^6jtw>UAe{xb`wH0#!U z%H33ra5%_+YzY~K6?J(&+SkC)aDsJuy>K#dJu$IEaK-<}vhn-r4gpP=58HGj#h`Au zr>?1Ci^|H;j3XZt4mu)o9kCyuo}C52k0?Niu_sKsJ#7+#>%~WjVAG*I%T;sk?+E3$6n+;A8GMWKu z_E|vi5rO}5zi14+LaKWEGGsq;%88(2G=9pf%Y(?Ms-30d&ROx zaG2(a%!l2}+%Z_HGDV8zvlmYiZx5D~xYn2XIrwfvxm!B%khHN?0S0KD24ZPGc9HW* z#>OiYhS*lq^jYf01sbbsOSp`vkKMKm=-GdxTY(b_7fGk?`nLi%nG(XiD#Iqptpuan ze6AX7#dd5&>n;WbaSJ+4CwlNJ=8NV?pM`2Hm9kz0Yd#hh5-R+}Eu7X&F;Fmlazb&0 z+;?pK74%e3tHY@#CseT7b55HfXHK}(eSC>9Cz`rg;DMR&%EWKwddE8uSz%Ogp*U0X@dO z!P|K?q1d?HVzM_33yb{wuB;Bn)iNW#C10NO%zfHZ6BPec+@T!}b3^sHiY!e_>rdwA z8;JcVR8%MX!t@J|I9G|I7SpuRaF#LsNOucPxYfSS`-4*YU*uLFrojoktmrx7W`)3G z*#ok~2SC5hfw@A10o%#JL5tBbhPu8Xf(|=OvaR*S)ZFVy1|xLdoZk>9g=sf zA*n#Z6QTM3l%xI?jJm>r-fvxheygN@%Yb8cv zaUa3d*1Q^3U58J;h~~fzBc^rj1f^JIQk)3WkqN2Ktz2fh%I+#DK&h~CR9uZ z4>boPqF}tdCJVyh@SL9MO`ofBVVGhDX?`B?w`za~Zw@j4_`_HPkMooK_|L_De*TUG zNDR@s--Qi1MS{|l#oB@BraSf|Y>=$z#RoD~8cOi@7qp^9;)e^SB(m4TWS*#n3EpHeRMn6`C8VWQ=v? zIN6}@kv$;RZBPCROZrK7fs9ytIk^mWy)bLL{@dSQK6ZAW{A9LyO1-^^vg0gmY}R+K z<87Rpy>4zEoT3cv$A_vceBXy2H&H|KuGG0d%NJp$w&`(;Hph|*Vtt4bRgP6aX5 zrvHj!Vc6;l2G*AO6_%@(OioVd$k_tX;l|;YIZ|^nF3mN2skc*qfU+(&A|qv0?LKBA zKYdq%<+bTVJ$S+1i^6GbX`!yi@v`OEaB;=E2kGf)ap~UN-nLa&$F`o_{Fz!`F!XqR zDJ^}|%oHy%5J$VF!IxMwlE#co zN1ef>b~IHLFAy%w&o^>3HgLL@yy$y|@T_>Ir)!Z>blt*r7pb^hhGAI$Y;l-}A9ZLB zkVn1brK81s`NUm&i(d8VTS8~fH^Ymez3ZbA^Gwywh87zZYg?Oh8{2azTkCT_Zew7X z;0h`VYT|h0Dy8Li@dtu1mAw7^eFxbu3=Q6V-DgMfnRkX+-U=3~zZGG{dd^a0g$heKb)1lrQq^H=GcT zW-#@JeAvJ4PK7Jqab~WtmpyaHBz(hr744+4IKpkGH%=mX!gW0V4|@ycr~6$!Uk(d6Sc_UkJ& z>ntn8i7uLznUZpPd`w;7>>&zyK;(Y`kf zM!tEJ>o8kmE?|WyNK22-&D~9AckWNWg0FbTI^X_I3i!T0Tp^@u=1Le!{#6g07lZ9k z#oHlR80(0<1N!x@o~}Nn9UWc{IT$%}w@?3|Pn9^}B9RX6Rcu{zcQ@xqZE*g|{S95b zG&(XWl|x1=okvUSlP&J&*D#kzQ}>_Hxk@D}1q5q;;#Oy?tLp7p-*}To%5fD>>=m>c zM~&L+iHS*U%hEr*td;_;tw$JUIZ9X{!=^v&0zjaCK*-M5&ib}CnaVcE0QDqN`ywKt zs-vO0CNDiLL$&me$)q0~!S`qz%;VbX>Jrt`8^-bTa}ruF%lL;HA$LbdPcN&s%1VI^ zJTT+LYfD`ncpQT=FF3==z#vTJbnF(O*V&RM-=e!`6m7vCHL*1AL1*vZQC~l2)$w|} zW5(T;&7Ad&?C~rw;44{BL!TI4Cq!9M5_>PhmhScazK)59hJ*14EiE;*wWA>@fZ4)K zl{{Y3d#F$V7jzT*#+A7;jvYa1#X$qsQev{QJrn7XY^oVrI+(m7q8Yi&+{^mK3XY6S zXR3Vd07B3=#-Hm8n4Pomcsa8WLJU<)*Ywq?=3IXK013*PJfwA<9Y&^^pwz=fwvsGg zP8i0IX3rXNaB{ZlbktPJKy+E+2@@`oVXM=+1M-;KG_XShVs?65U`cp45 z8a>{h_Ba2Y}OBNk^Wai?@zOnoyIJr>gf@wls2aX=9+)iu=YJF zRSL$FQS)7$o%sTSi>^O8!%a7L`UdF~udS_lt}l;$E|S`YsKQPvz2DY0c>DyVMZi0Lw-^=##V>yis0$c7K1rvckg_j$=WBC`L_v z$z?4jKDb?=?3NPPcY$_)YnR#it>|fff_g{Cd8Yc~NZkZRFW17(@yR3; zp<&v%o$}MZO7f0d7=&$hAxtBS9ptT^^MNqZ5`gqK64KevA%uGUdIn-^D}3L?;R$!f zgn;-(MMImcsYZnH!S)!56hdf-XdYBQF!1oy^viSR8rD~h;y4aWS>M82iZ!1nR9~G( zU9GVOxUW|~dLvR>55qADwodhXBKUEOn`dhD0vC=Vvbouqz(>^+n zBjV$yncx?Hj`~IG1tO3&&)ZLKtv^ePkRlAo}CnI2ve}!Tr35^{pHn931Q? z05(PSw+-geN`lJAiy432jyAU1 z1m(OWeysY@c27I@AjdrC^&Bvj1FM>Cc^@b_@rC9nrSfuEOkY1| z&^Uq0y-`zh$sOV|EFW0n8#M{+z2xC(eHauFnC@poYFhO!y7z+J;+qb^n;U^GE>ii*fg+~>a8dZmw1QCDXN8RI8 z8u@s&o0U~rM+dM{sz3?v3m#`e5|(wyt*o(1;SYKyW+i{aap4Zy_6F<$<-S5UYc3MR zelc-z@nG>B0;7DRhzRMG6;A~>j>yh+8^@YH?aw-y!S=<&Fj4v}jjQThz;nAMTs*vK z!ok5oX>x>tZ)ARoom(#HM#CODfni-!d#IhwY)27q+@#$nRTD~{vdp|F`Q%S*B*MNdY2oLp=%~=_> z5fc0Z=4oHtPg?OTxn1J$ueLX8;Qjkab{raV5grI%g1CbYj02f3zdRHF!1(B)FprI} z6+ijSob0)<$V{WLc+LVnO5IPnhdl0=-q-*Li`t7>*9t2aKV%qdt>;7exP*y}rCsIV zq|~?ZS&{>r-Ps*SRB$ib1Wn)7JzQ-7oU#WcQ9;NTxH?GhBQ41Q41u$0On{oe1$8V^AE$jFpDz=akF!=6sQrL*E4gqV0wfuP~swQ zkfE2UX_C{Meix}>ccgW+TJV*!97Oh>&G(QnW-DWX5jU9e{Fs-{2e6dEPP?n8-G7R5wME)e2{&JvomSeX) zL-FwQAdgGH$6w9sv!I4lJz~go{GbPPLrILd^DG{~97PDk(bq0HmFSzL1VejygqMg) z!Js?xcvGrF4IBwq=tpDWR*mQrT~Q(>?x_DG-$FB9;{O$0huXo_#kWmtG-PK?5sIwa z1J*8qxxcZPd#PPhrsfIPxlPIkK%;95k9xB2a`;2}`uIHdqP@SoJQ5B^-rs$NyOzix z6g!+P{HZ74`*?q~^OsnAi`mdUTFT)1x<*9x)v&$Dr^zmD_~{AGh|vV~&q}&h%ZP}9 z*F`ACW*S{kvyhql>KkOv=TT3atpy0AYbFP~((+XO*2&^)^3PSEU1g*mFqeXD1Fg-D zUN(OL;^{{x&&}Dh{?-RS3scz|XN5vyW4hgR)dppqUiLtL`V5i#0*Wh5nZB)CRw1tpIjou7G&LABNE znl*LS;x##Cc%N^?$In(U4k!k~f_LC#_V8%{P@lR$*6nD4_Hl-j8O0Kq58=m)$IEgP`YU{js8 zL&|(V#7~v80(CNF0Z{VvF!b&&K5x;!*RUXjX3A*pqXDP=UZAo<4M9V-rONN>(55qt z_E!624T03OwEa*!Xd^pY*UsIoAVrkyL>M7u+iD{XbLc_~d?wBD98necouI84LfI}wl1#WG*?Un`>>*Q2HJYIj$L-=KwRq9 z76z*M`XckHMccurXbj8Dk?Mk(yZmFtD1TWhKGgn(2A68ZP?NA~7gbTNs8V{#C?3+) zeqa_k-g~4N=7pdAywhOV)#GEo&=}f{NAbZpih(x~VI?E>4rp8ueIVD&*0$_04&{%R zQ7?>bOQ6<#kX&;Uu+yv4_hiV5*hLH8t9Cab`pDuq?1QbXZ6gnf&%+kh;-=TFBj$`` zJhWv#Vo&FO{^k@&jC;5>1e=iL_!I|wN7J?FK8+-zH0CR0_|PDK1I?a{#^q`2tO9K9 z;2AIw;^*dO)>}^*=lr9$9|DQOdD@R^>;*+t<99YL<3}j;yM~YmqIviBH8aB=ra!KV zaMhyH2WHv9ziR?Ge%y~r%iK!aoFMMM80Jz|lv<{;%pIm^$vS75wj2Gh2-^`0`_;W= z_x3Q$B)|$4#a!K4O>Bb?@-@b0R1~QhVcb6hbe(UnqKJ2d?dpxGT_XNq zS9U&*z7z!FJPpQHksf5Mu1;`yczL+uu`yrI`*D$}8$XuCaR`Vrk?uX!7z|Pj(1Ir- zCcb$Na09a{B8;33U(u~04a%ArYABQ1*xHP%EtopSR)%Lh|Gm8p=tz!4+JW)ZMu(j2 z8a~c~)n#7534lQ6h*-~+IweWOK>oBFxoa`ZKy&8X=G_AgP2tp&B;EbYD?0mNsZj z&)5X_Kt9=YG<50Gef0YL?KM-u>>%pigc=E+D#Ac}0^Q%i_5m5?nr+;gaG?N~p_Mko8GX$*Dw705nAgzOXggTn^HB?NZTjRE5 zu%997Z2F-(#mY_@=(9!0viu{ZqDAhCqe9#$j#<$GFBX%6Cn3c!#5rMA_>r5>2;x13 zsZD$H2BdBGfa0s~o5;uwd9}WSldTOt_MUopdCkww$@U!CqU?MtEw!}Y-K;(M4Csxa zfbzv>|F-T2asEr#o!YRx;hR~Lh|4;+kVI{mN726j>C~ge)27et&my5s_{LcIc|o`v z#w6XymDqGic;ayriZ7d@BIV-E{v#H1mdOeJ_kjoL`N4=rq%M$P4TW6o;MrMb6zcY{ z!sI>30`=2%DHc72iW;8cAi%|=9m-G$ZTZ0IIq^GPssbzdF#dRy%K!F^s3dxZ?;Tj9x=gs_Cir&C9KwxQB93q zZWzjyQTC|Wgf>&EP&+acze>Dpw)CxL>mt|il`{G0Xa^!9wV?+5Z7RyG5+Bd%q}y-Q z_OK;-b-WZ1sR}g!8N+8UbZg)jG5qyh^-oITlwH=-V}e}w-cM%DFk_m5a(0CVz$iJ` zF>f<3pzdn!Jr%ArnghvTSGNAk*d$X>fWfxp)mS%j=8ujxQ`nmD!^hPi@D}FI9w@hN zp~vm2_5&DcZZ2Y{4Lch;`=;dA6-w6Ir$lA~RgJsBl^r4G34`b&*p;2U)ki%=)eb#l zRr#Wt1GD6^@|sFn&CK)nmqD~sXFKYrs%$QzeLeIqx&!p$3?3ODvkxXay`C4Mv%71G z0c0pt_+9!>Yimm^>M0a)oS!F8&(3Pe6F~EWH%4=&o>fa3lnv@uSXl7|GwVYKVsTks z^MmH&N+dgD^Ow(oj|xCq~@pN z1nox_dE*fH-cO+zpCr|VAOsC4%5DtorLO@DQ!{Q*2p=g7$CZQ9N+-sy2*+g{GN>Pa~4>;vs$Nef9G zR}+5tg<@htV&dK15k%`K84R8J3!Cq4XNRwI_r1^U%}m;{7nD=js)Yn6P?}>e0(*2t zd2agqZZK1Bft@@Uij5~iKXMquzoInXqwO2GG}<4dprnM3C~w;2W?U%b;Na99$b&;} zWV90^PJ@jiCrpdVMldkUeOrj_%sodcAa3ec`Hu86ufM;E%nx?uMTk-+cM2N`6ele` zsc7bJSgKdif{nFv>g;@o{2`K^;TtK5jIsHX4}j?_&Q9vh4zkDBN*X_oirDU^-=IcJ z{k9h|uEwpt_+@Pm_e*_2_G0T=bbmZfE-os@BBOg?$v6AkVYdxr0@^W>Z=A~#Rig$O z@1NI}RYilGWmTsXQQWH40Zrlnl)mLvr<|&S=bdDW3NkPj1&|4Ru^V)?i`M(VWPP?t zt9Sx^Z~5zXr$5XDXEF)15@ZWjOHeB-w^6`UyBZG`u6}#FAYM0RLp{Cl{Y4fLZr@W& z-EKQiPS`A%L@_WtEdF5U@Z@Z6u8~Oc*Hf5Z&rh>${lmVVSyOoypt^>E5{pTP5smxz zp1VdwuziPukp>PhOW!DCk&21^Z_USnunBnR>E$%dI)_*;LIi2L&URLm>?}{~$lxn$ zj2*aoYh2p#afYznDE#ITrE!)wcBI^J{36h%Nbd5D(2~GIUXm~cNs;`s(9v;%!a;X2 z2?0@*D5hju%?|W(JVaW}5YWZ0peevlK^{=!vVd2zuy*cmucfq^1DIVWsYcvU;nVD+ zHqGgLY|lk8-ak#It+{>|Uagd_U=>@4D{G+~ZIm%-wVGQ)rI(c!f$r^dq8L1Z?yk}tIh3Q!SefZjT21L zo`9dT_t^YAKL_R2LuHS zxAK9@dTr_^N3uXn623G+==YS>Q5Jl&%F<-S9nozMp}lY;?cSSAi0Ak0yn~NlvbLeY z{Q0wpmbIa+JZp8`o!7Ea^T2rI@GxbOY?W>iq3qNO7_6pS=Hlek)duw= zAlusdY;?2~0XuMp8TT?|N*u~#Y3Hi8#+}+dDXyZTweigvX*mR*g_n&8Rjh=e=pmoG z(15iix}?f=fX+aBnJOhGrNJX^rr=@{ngY>e9OY=`dsh=#j>x_>lI{ANkDC)BH!)eT z$JDjpZ)4CZ7YFpzw$YKEye|*8-230Pw_|Sjo$|})8oq{|GQS}+|JvyT6cDIUK)9>E zH(XW9q-=?yeLa-Bys2F7!xu*wXof57d&q%yR$K_2(9!mi%=9|Inqb?ff`%;t**ulD8oY?IL zi{R3KQ1-fC=@xup4yc#EPSTGSs<6jE4a?j!`-%^@2b`#y(|TW%f`G7VnECCgt}_j; z@p_ub^rxL&Tr|pAne^IYY5WZ7ZPLE`%C*v4*rh0~w{N&|uXzMk|1S0degFO)6+ZS- zjRdUX%AS;w-A@ePB2WRhPdgyEyu7@6{!OVG!*HCmb;R+Gr%GJ`MSy^lOI%0>=eL6x zCX$Rud^}pUv)g_%E60FVniflQoB7{3tby34_lF$|*q1VhzBn7og)2?u_0n@aUV>zz zGWnYEfTY*q&BZHu*4D6e+F1vAAZ`++&HMS9O(&C(lEDFg&(@h^2A1-6+A0_MVlohX z*$%?g$oEk^3{ zfj+CHCy759wAlj>?B9woI}(VNp9u+}xey?7^XgdF)d|aCKvbs|4y3Le?d<3NCUryl z^9uRG`)fmc^@Kr35t&9*sCFTeCNc6(+E~xfq_XmCPJd0CBNw^ihfP()GQFG7)I{)( z*b<5x0ZFo6^Xso)pT2yn{$c37Hcb}^g-eMBURan55uQfEG&O(KCo@bqx58!J_NLBy0U;yK`79B8`lYhuJl7u_@_rbL!!DY_3si$dLc8AL^?>Ms02H|VC0-8e%+$uE z^5U?59Kj`3LDu0 zz{DyK%HC}y_y`a2^Gln4t^NHQQJ3>XPEB>bxRS&RzOh$&9u2h>#kz&G(J92?eXdT>c}?29UaBrysqw+7ay#Ue%JI0Jo#;Xa~ujB576~n zs)QqYXz}><2da?%dc0Pf+rOkEFRum?`_ggWA-8}Qj|0|CAHUZ(oXO$5J$b6vw6?a^ zad-cjr_|*0>LF@Cp1qDrVoNP)vNt|3Kz+u?u&C&9Sn1RC10Q%8xoHoX;k#^{RaA7K z$nJ5vU!SQp)YW}cwtG&hMrgI}wwc5}xh>PRJv};t?niOXDKw;v8_Vpu`Q-y!=g6gP z^ywaPm-M0Da)eGozigy4>LssyGlFG2Dd1|86l3rGx+h)pf0?QNZ~pLq$?5+8kh?7I zVBikP)OK<*(Sl?|LtZ*$|Ic!n|M%(3>>w_d|C+r#p6UJ#+k|w`ENL%ESP)edHM%Qa z1vsvy*Ote|hQmHCB`td6CaO2CHT?Bw4+SqhgY;~T%xvACHd_p)1ON=vE+%>spXtV0 zs=JTl#RM{~&-*slLFy#`O-AQIcP+e)Gzp`eN2dZQRsb8bcX#d$<@_ z$%w&UL4f>661_Vw%eh>z-mpuDbZ5BQ#%iX!Gs4NVwt2UE81hJ^jKdGS8ETZsmNQto zy`$Twcp-n*GPBExwg}2=ztZ7}!$-b8BdPU&+sur`Q+ZYSa zJa#<)X1yOl%hzgwUp2jpM6QlwhGIF<6ml(v%fCm!DJW)Wl!0uojUuWkCtyh0_cS`W zZLCCssx+dgTq^qaVLBeST(0`ZUHE&)yURxM2^X#LQs!+JnxA}2G;z`uCSZ86bzA$e zXK&b$D@B=+_vsszz)v4rk~A*3a)s|UEapanF^{f z=ByD7mzHQ=Fhw=OM>ROlj8G$8e9NuQEXrX(Ly80B?L3WneuZ^Z9~-C6qtWhgf)ckB&o2 zkAE~rK*2_YT$Me^^u}z$LL>Nl=z-g`Vh|NSvG;agJee~fQz)1Oqa^wx4x`lxbutw{ zfirerZ!vxC*p=d_1GyX4Q=G#-14^W*`utj;kFQaUDvEU5)~_1}3C+azvIv=^dd@FJ zSB^{tfIC-+_Pw}y%U7U*`D$Igin8u2Pt5Leq-=MOY4ABcQXN<6EI}wKYRC6$qi{?) zAyV3i;SxU**Ugcua@V!Jwy|-k+JK%vM~M1>i8=2g{GZVUIO)dp&DUz&-Zj?%$EKKq z6Y@W@7y>ZA^*_HV$F53AmTZqn;5`G_XLIXUH@-2ChFA}M@;0EQH5%z>#HslbGdyF`wp|!U zmFL7~TRd#=$OXFJ{I35e0HW%8Opw7N#y%yXsj^zB@PDM6b=+Dkpw&`Z**6-@6=)Q-SbvQo=jP z%t=%`DHkOJ>V|3agw1r}E@G0kloNF^lPpd2Mo#|M3h{IUGj&W4u=?hyi}GG$a)S7_ zeV6K(9*a1?@hQ^_c2v*T)p1O?Az_=>SC!FIl?9%!OI4KWaow1Xp{mrvD2UTU- zwFp(GH%Y^o7dMqk+AeIPVS_9@KAZ&9=;l)sY8NSo>$w6kh0QBwVFu+&&0p6w7@7dd zo#Vz6r^JhdugFlNZ=3WUdT`QNnOb+%tEjS?h(|Vv8z%j{-9Bii1%!V? zmd%=*p0uPRWIWekpJU8oMlWTpTLH3s{Tiy?(c6S1qb--nYhGLYlmr|Y?pH1v^CPMf zl3AgifiTZI%C|$Q($Ncn+LI_j$C}t5=alEM!opLz69t%Fyxq%=@2GAfp*6SdAKZ1fYwMV)J){%% zRDbLCT&N|{)~%?2!WusP!2@6~&8bnTl+p5j0JP8hZw<&=RDWyzBuW+)drJLnrQqhE z29ma|!4T4`)cE)zdT+tY(*$=E3UD#$H-X}?!-klpaoI7IRm_>t1^jN5Uhvm;@>g>m zZE0=Sgf%v+JBjdX90Gd2TcW`J)Eg+Fu#O{nFMfB4bW*p8F8;HYZ;y$2OT9hTOz=E! zQm63ZjH#r8Cs>Q;2@dTUYk--{`zOHi9R`PNbb(`(;|LLBILQZQqOlVh^r4?$-aF>q zA2*n`)BVAX!>H18By2Qp>br`{A@bOR>_+Bj9PsmJl+9}6o(k8`DEW>;*?(;(unJjp z5HYGvGS!i%Lsm>52WpQz%F@m#tB`cN%RtIswNcQ*(QLv-(Ciqc{m<#EJB^)cr6a_Z zG;F#>)IGF=E_p~A)VW{e8}85zSX5LAz}N8igln(>EoLO8kfAe%gZIQRQ;F3aLKVrR z;5$Zojcr28^<01$nChMm%R7c4={#F9F_Fe-J$#M~D4PhBRX8Q0J*;~zG#P_oNWC2QzeXx`2IOd6i<}Yp)z?Z>jp`=CCP{+kF8@jRBVG2`jo2innR(k(B1uZA zVHoFg)<#__i@3tG5jkK*cy~m;eg;6nI(F||NM(x+_M};oH=(8$Go#29A{@KgbT;BW z+nciPt#5OXR<>gKjPi8-VyRdElZK0Ea{pw$0NQd#{~cu#U8L%FB|G4yLSjg<#@g<* z_H{YGMk=uThx@OJ-SraK$mz<)5B+VCNwwNOL4Te#4IW50m%BNXx|a`@MU|>$cgM0^ zRH9z(k7ODPG3wIDkU)5s;h&5-C{G5*HBwvlN5@Z;;}1|!4Bj1>L@#kpf7VLRh(LyU zg}VpGGeVTb%r&HlUJUl4Qpd~nCBu>k#OmUf$N4E+P=|jJ4H3_N8p!CPWG+%2X&R$R zn5P{+vJ5`>p1BBq?ZaG-$)~Upn29L0xo?TxHbzMQd=TqGbG^a0;aXzuTwCjAu0Bz7 zXFXM5raYiMeKcOwFq?+9(1kRYb6Q2Ilw01H-@wO#f#Zu};Bo8Y0_KaFANo3%VpT&a zr9_suP)0sd>)@)@r-tP@XbKN=H&bq)vH=AR*EY?{;-~k4XC|mVy8f3JLre8O*7$ zP!Za3lm%4YEje9ihwUH6EZNR#-O$a-7QX}KOjTNXNJUE<)2i*rWX!m8-{$R87;3E? zmY&9Yqn*M2BnEB{=0p}$g}8?zPBmYMS6yuxR!_l_h^l?SV{D9dtBF|}&9F9o^joEZf-@2@H#nCv5A`1D$ZOV-pvp*?gwjK(K>e0&4 zX{-vph%SQ;S=FOOMK51*I59D)3$E$V)U||dP56& zVwexBh=b0UfVsz4?2pgE!PJ=jy&j7^ema|adj=m6})Zts0Wy%P%_>{Fn`Z)leLt~{m)+?xxv(`u(oMand@ zBkjiDRh0b7W0<7kXx*|K53%M~!n8pDKqZGvT3aX!^Xex$@>0V>=}ZinK@7}&2^3f034r-I ztYEA&1>~ZwXi>wI@gtusf!d6+Wy{4)W5{3uyP7G(%OBypfg~xnS|6q`jfgZ|Yxg#+^JDfL=2`9)FS1QtrU(;)lp~ryZ1srVv)KI;@pn5z`47odSHJL` z`r;}NP(RVF-%U?_hL_4g7f-Ba+ZFK1&>K6gC&8`hTOVP{f#IPI>WH}>&JHvPyX{4$ zkaW|_R}1fU^N{^9AeF!zfq5QL0MEyn){8bvqxNfD#_1H!ccWVKMcjXBGj@PCiQ+lX z!$W<5txBv|y4L4Q{elpmcQJVHjzc!R;V2tsm2 zKxFF>;|(0~9*J4^_;1{?3G|U7{m&U)S2~P$<*|FmA6Ro1Y!NJM`}KKIi3ZCfWba(Q z95m0j*=DF!cuc_v`~t|Q*xMmggzmu}2kmntY(^EuP=!7c+(Y6RDe`NCPROdEUTaf0 zKAl@#J^a+mzs3{hzf)TlGUv!TL2Q52a(&EI%d5KDTr{KFyrFW?wHY)T)&hGV%;(e; zTW11)h-S6W8M?IZF=Yu+d(4VT)pP0M9^)=A=UZ6+uLXkHr zL=8UvWNo#g4EP??craslgc=p){h6Dr4W5%?jfhAZWVAhprtX`vWF{C8rAD)`Z)qq8~teTth&!5r=p67KagMnU2P?z+R0#o z8xIf!cu5}NB{SzM37619c#P6C;_xM^@D(@ROHE-W24`X;D+ zHOXA@yA7Ad;7g+N@ujnNYulE&A^91IYw3d8<>kRt9NgI+n*yXWdb)zrGTK=?Z(~gk zPA0T&55K}z>&=CeDGU@#x-;^V+V+()GpM7ClvHcV`OHc)`>$IoV`lP76AR8&O--r2 zjNVcERI06fl16fGiTF@>8NHkHr0t+p_3hGxo>gauC&nz=EhW%18t!#^8`!_d;8E9H zcw_0T(30ww1GxHm1;!+ar5E(vvX5vb6SUc%X7;VbMS`~6#O+!AH_tL)dA5fe24prJ zP0be5(%y(F5R3OOONJo$^Oe}Gp2!=7f0m+&Wz<(#g*Ea8)+dH zD_RL#v@=kJZog|ApSaMRN0uHvxG8V(A?h`)KIs+6bx>%jRPhGQxB)cDl{^17wPqH! z$Fld$l%?mznc!Qxb~g=6!%2CmWLYEy@yi;w$V;tccw+rx91k3gljsa}YSF8&X~Pis zu^CD^Kz{#ADhV{|)#wyTR=E zp&YlA-O6`jX)6LFCi_|n0R@@r0301r&12fs+7li;4d9(}T}mF;uhGRY32W?XUdg2| z?K+9|`by;n?#oU6A*8g0%->rJ57pEN-Cb8lYVsck)ILiKn3g)`5hPXZy9lUINoU7j z4jsJ!nYR#*&k@DAyNr&Gg+}Kz4WLz7n&Z<`j@7A)4L%2A?sBRP*Kk;s;mF7IViMB3 zQ%WZ(xotd@uhi8Hu}%nr4SMgW+56%&6N(8&VSG}G(?+?i$P4n`+TJ1`i-*}^&&aQ(jj!fTo9V#hi&ke?C=M)>{im5r>L|EQ~sK^%*havnkKc@K;{yf1QtOD>n?Z6A=i;lE(F| z_xFizUSE0s)}SK43D)S_0GaV#s&9w_?-hC946FO<&XRQ!c~~b`I7E)KiBMyu92Z*r z3<3*ocuR`ZI>h&hn4EV;5Uzg%p+>XV2WMu|T(jH-<)X7%O=32ybGjV2!;p8eXOeX6 zlC=y4aZMCFi8?UHV{GbcX-6m6hi7J#*}M*7GO>J~q;K*qR~{zQAJC_DT*ylaO+yM! zK2En%go~eeQ;~^@)LJW&j^1M>#tk4=1IQn zA$q4@!C%dm?wQKkbN&Tn=?J#B*8#6KkjcvVEAH9SRsS|DX*-UArE??$ukp~e<5*kU zi?$M}E%yET5;DeF&PDS;dS~l7ffKXHQgebL#_1zJ2?sa3Fnpr2FT);+y4vk4?A|@Y zPsC_G!RE@-hBTjT)>*|M$+CR!;h`I;w9SYc8DamT;G5nAgoUO#6vSFuP?s{rvoBWs zOw&VsxVPDL+#mP&^|OVs!Jl{8d|qCnyC_RMO|W&RoV*6tyOI|jOBAbvk?-1(TKXsc zSo=O5rsE@5fy+;=%v_ch`Kwcy{>sG5`KSmB#3X|pU6(L=^4*LusQ&zU?=%-0C-0zv z)c*gqBcJ7ehLHXXhyCB+V_E(ud@O^It(Ecr3x+H+E9BSycMw@-7Uur}k^QGz{{_0#L-%zrg|FP+y9W@-NY=#I z+~8ka?>~(=AXebyVu7p|hFAzG)q}E>kJ+uG>w4M~m%Ivk@Pjf8J}^ z;8^dmaM|z9sC(05niTo8y#Z=yof~OpPfFkV6J9TcT|K$a^t?W6*T}EkTs3aA9{k;a zTSI%@x+@lFCy8z`bImSFx%HXqB$DxYi9I_#tMRq9NeD+WQ7_?(AL-_rjU()(4jhlv zd!GDriHT5=$kg(I$2b$E)b${{F#ZkB`nLRKc)vYc` zHu%bAXqDf)vH7Y*KXYU0QGk>4zI$;J;0r4@_cvQeuMZ)cxy+oT-xBahE<%kQ=Km<5|P-l+vf zVg%V?e4x-z==T;h`c}uf(Zx5#1lexbHv}brYxqHyV(c$y=bw!Wej#F;$PJ^17vSer z+lcMC4$K}sP&3`a?_AxMOfgBnw8ft@N}=Gdv>cX=5SPS7bv_p@R1OFhNG8AU^+8%KBD z1zK|QC5~MDX9|OtTg!7*F*;qn2cvBLMVkGhh(Wjb&^gVFjJcp!6V=Q{V$PGx=}O{p zeDh0vlJBJQUl3>rTFcN6+vZOiv;w4u;P17>RM>C_lq`>N$9)C!hb4Y(ug_5-s7vZB zE!F&l{c=dpia-JMP5QHN{)o`KVh$UZ-Pl0;lH(YbOX7 zB(MXQbWJtH@txGoM!A7Z->Hb$okAA*vkS)5g6-W_*SS{BXcm8>EizJPILX+9QJve` zeUSx5w##(Qq^9YOnh~RuIW~rLs`I3X=oiOXh}G_YGCA!vfaD*I~g!ZM}L~ zJdcpFQ0__TCY1}#%-hpx{WPw+X~l^h`cZZpI_>6I&yrJ{TAio`r{JYFK;-H4$U0?)68xF8~vtoH_u^vZ|vy~9c@-rxOlPiRBP4MFaJ;VsH=#X z!w)TW6YA$er7?9J+J~xNww5y|*kahfXds-w$BFFHuQ-X9sBl<|cUIIiAS6GrSQPyYvd zZvmI(w(O6SBHe;CBB{J|hjdAIcjrrYmvl)=NC*f>my|R}Nh1vcQc9PUzX#mzjpyjz z=iIaZ|9e08bN##sZ#>UCYt5`#Yi7PPqxCErET?5WWeF9cY5i>LLHtba=ZB;X{)p(-`V5X~Vew zxHjTbYKg`Cbm*B%V(RQOouf24y6iy?>H&rgL0*^2_skuw8hZQ2SPV?y9pfJa`ZDfG z#D6AWa?H<kz1tP!GJrqDgQ>_s7Sq(PY4Douo~J!1H%UWF!a>upsC( zQ7|5VQ8BfU`x9n&e#Kie_|e_u)$0TD;+9hbj2P2Ge4#Yc9L5_@ z6v@j5UWf!SMGql5=yuJ^THYAV|RRE7Y}U8{j+Gs)|C-G;*ww& z&f0ZTzRt~#yn_vkxJX`IytLv>CA_5a3S(qTbom#)Rsty&3&Uc|c<&uXikRuGx{57_}FqBiYDeEG$OSa)-w&(~K&sLf)e(RonL=!v=$$y;Nts@UnLp z=npPyYtGPo9~mlPt+9&)Ru?`wx3S&Fbcx|Xx?6@Ta*Dr&8ezf^Jlj1gyR5qB{eRyE1`o4H^4+saOIvY}AK9$gCl91Wj%-?G8oG>V!&92^*#+;rnJLR&&NPkb@NWX<<;y zwZwSAF90Flc^NPH>_XwbU4&KLgd*OvY6Z}4sx%ooS(fE$624pQ?F=ayBxKPuLbiR5 z`HbhmwK!41lu1yaYzNrI-0Vr~cS!JGtl#CN9c~?2Vh(%Mm^_RASkE&Zz&!u?^+Sx; zovgDeG!atMyN^vR$wH58l!09bzs>%mr<97%IsY|$M`LtVI+%bX$$!AQhDbs2%>3-p8w z=9|cAic23Q4eZrOG(k%GM|DjBX;u9jf>@cXq=6V(^Q@$IZA|F8-JmftCCxnC-t3Nt zI0@vEbxHONQ|j!+Jx!+27grRHHn65j2#~ROT0UMVsjMD!iFxl8c#};|L!!w>WMFRL zY*cpkxc{8kV~RK9=T^|o&Kn|*)&>q{Hr7|?L!j$~f|(nDOJZTTI!b=)k#L1lx;l_v z{{XMb>sgyzLziy8_**ju1wAV;(7b}3PRPbe&&>MzQs9#7CL7n+urM$H&t>ckjqFGm z{-hxTBRkuVN5_a!IO%piEa<_rc_`JMVZHUnii0_><$Ge)mKTPMa&wT?eKlHGYTlpE zSx;Z?Ypp)mRXdV9B5HswX?eW!P-YL8ySs7bE$vKqm1fHe&F0ssEiY5~h$B~Sqb%|r z>7hCq!By)avKm2;8zC#}!6q2N-!bBMqo=cHL}Y5Ilzf{b39~ee-03$|9#g(3M9J`! zA&G5Jy)AgTiR~hA{Z0OPe-Byvame<^u5h|fCdZ^6DK2!{gK&o-uC{Q^F}eZ-xF3Vn z^ot3YatBBaDUxN3$A*kzlg)!ulT9wQ;Hv$lY;E6@O@z|gufOM4874O}(b7wtVu_kP zCULp)Lb)} zkxs$%y8Q=W75ucb#&2)`_F+LA8#_b5nqlN%0|9+OSlB`AfGP9+85@X^k?D%XE`Zfx zs6hf)4Mx}h0k3@*EfR)nV}}>`cf*Ld*(c?@0WN7~Z2{PO1`Zk|tQ;UV782I0uVW@* zVFbPhc*e#8VqymFET90;u0jHs7jz;d%z&WU16N$zlJaz_YU;qnK-ZFQ(7Ky;3<={8 z6kM57-zVtWJo+B_a_+2=zMdeq*!T8Nf@Mpqt-2mphjeGt1?-X1M$Nwb91Tzx}@BpOg%3tyCJ0_UF z`5>6Tc@~(jS0^xWf9XVWwN3a%#QuPLDC05SbR!8JXBeJMabnD0ZwrP;hgb{xt<`tT)8&8$JIT1O2>=e@ZxSTJ1{@3i=EcCu z061U&>x+T)njR9?Yi&-#`b{wd$1GrVk+A;Dikaoxg#SIhUdA6zN!LE$rzl-~gs0JG= z3Gn#0)%RbX4%n`R_is|uj6W!d>rlw|`Tw;LGSJ@?!fS!}r3ic@_Rscah980+z(Mox z+ML-**niVU93<@DH`2dra|Sk?t3Bu6JsSQccL6KxO0fPW74zc)zj^NY33tCOaApps zn+yD!*T3ZM^$h&!2E_2Amb<#(yMX;C3!F~m`wsNiOZ`ulH}KYt70ba&0zCeHo^t%1 zJZ1V(WByCW8fL(t2e!W7&hy_m{7asGQ*?j2Vt+80KBdd_gT}kI<-gD5uY>pu%*@y3${&q!`_-*v@Hx;fiKxG} z5If7@l*~^Q7fA5=E}Bs+x(*G3_Sz9QLGHjW^R;0$UsZc+(pyxdV-% z#)l%rywS5!?#tW8WH?XZh3(AXac-Ro*%e^j#qRH%(CL0Y=`x-(D_s#o2>u|pS}?f~ z&_YK!V_s0{U1E=ji838@(A9s-Kat#5nbWe0BD;y$iKWdXxWCg?VAv*S-@T#?HO%v< znfD#%j9L%H1M!ZR1BKOPUh;>mPpd|6$Kp#T?{U4H<0IS}SmU)yHVl{g!r~~-X7zU2 zeM)|?Hm5ny&d4Uyv`SIY(#J`ucTucQ#I3OMn8HbMU}-4)<2?wOX^nzl&S|wjB+w@P*#~OpW zlWVMr(@Q+v5KLP9m{&&j7vBYKN5~%0dp88An5Onqq3n({m1$6unzHY@XOOI6j%=B4 z!jb8X;nA7+>V8d4vQJG3X*+9hqHn>4Bbv!>U4$1BAnO42<5HC9Sx@UfZQ~uZ5x+lX z`s9UrV8KV7g*8*L+UMf|_iRmb!t-d;OiBDCb)zz6n+O3_kbEnDQqNptuN^{EIIKZGOww;F3`#~KwiMHn8vtUwb7 z?KkV6-9~4UWVv0_Hj{^t;j5w6|0bTu&n^g zhKeK(6W=k9jwTtrNWF<&>WONJ3fz+|lTAz)*xN*x~;b;sm$&d}fa!sZ5!r zxD+H;h5Rn2Z)&~YreB6zNVWCDbby$;2+$-@sE2(asUJcZI-9hGg(VV=XtPk+*0DF~ z@RE*o*IQh7p-UANhRAG$C3@w-(3=xP1aQ(*4fe%lCQH8Ks_l+8`0sq|&7T{X+}%mLhnc zT`2XyvsbDkr*w3dnD^n=UoC0Cikm$SoTL%MY$~wzUGq??kZwvZ*_&7>RK2>pPDsfD zh1qykG1M|EjABSJPrdB(gqv9VdqpyZ|Zh6G>2PXj{CS6kHY8vp;oe*Zhe ze%En7q>FT=b1+diqiGm*- zuGayVzodYLgB}F<#(tyVnw(!zaP6D;{TR)3F`x!uD*@Cur{rw9t;D6~y_=@;gTg zlsk7}vQ!r)LY6i&zMk%Nbj9F)eLQo%v)6(D_!DvW`$rpy$?fSfPb*`z(GHQ9z675J z>wS2S82?0I{mU)U;YScS^K2c~9)ywAJ93wt(jx08>QTH=B@q{5B6rUfi6X41YJ6GK zIsK=X)QASeUB(*@i;p=rO)-`!cN)cz7gnK*N3jxY;Z%f;Rb(0l7TsIeqeu6Y2Fj+p0pdnai9y;32*g&nd4IJiAL?5G6eL6Uu^cv`&6ujzj~%8Y6*MObe-83ROSimHbfhV!0nPO zp&j|dH~cc{bBi=NO;bN8J#gz2IETTHp+k(gjz=QmR;rI@ZYA5eejRq)F$MbcOCzdS<$F-*K4s&rI&J?F&mBwK19P+1pYL$M~UM{+F8jf>K3p zQ;hUBiSm-XmcuN4=<<+}7U(l3i zOoc*ky=&yEWSc)n72A3?@n**7>-kZJ4b|Z33gL{`@y6;`LJIy3dS>Pj97^xh#wL%^ z7R4T>ykTAQ6Bn8`F?TtSr=(|Rp331R@eynoVEI?VoYJpQC}^w7xcd0N1iY4A?F-~m z!&NVt%O?x6)+Itn@}IpUSgF(1LiMTup@oX8m;Kv-~gtbn$9 z%7StpH+QF&Z&k*CKKATZ9f)#}aSR^Bgw5PisZCm~eWMa?hzXrC$j@q&4OB@L0=;=r zX_LmX>Y_cp9!L85sZ_H68rJ%)L_1d#gHCE|QpNm9p1uRZTw5X-ZqBaN6IB*@wC!E6X!RWwcBl{-KoT zC(oC$r*5~%Bb`+jEmt5a>vRn>_^LoXT~YTLp7eqCJI!yv53Y1}KD3rbJPRMqp6UEx z3^PSdWE9@{AhMBV4i?uuwHu6~mV>rsNc$tJx+%*xE<)JD;&C>t$NOL)1O z&^*tJXwluJqyj+|k6u&D_s}SUZ}1-S@8&MEc&KMHbZQW#ED~<4xbwPO#J(Q^3AUJ7 zznGw#X76XrQj?xwYH}^MdY*vB>B-?5gK+8cNK09PkG*-dlPzOkSccO}-%v%LDX1Bz zU<4P%-`*p|z3W@gdpaj#atQNbRGNgov)5(xCVSiD(SmMWm_kl>__RX)36bSE-A{P+CM- z8>~GIWk?1b%AEy3;yP4`_c6uzk#xH?pO8M%CokYDDhH279I{LzRB18;HLLO3llV%XA*;L?xAeZE&Q_;PI*HSB8TH z&~r^Mds)a1e##Ho27#R5$h-tPSv3g_(t3RdCgye&oD4A^Yswgf9$iBV2eXghWSMD~ zKyJc|F%PUuLCh$F1Omc%Ut(`{?iY(3yz*(HDe^QvvQ7y`*xXs7G3l za7LPSXkJ25uHn>|6(QUE_|gnK_t;*w4QRp?t|>OviQ@n^stft;`?dp8ax71{L zh?qsqs=8EELuoVwW2Jk}MgGO*DOjS*G4>^*NZma$MnT~t{LwGsqAWVBofH;3%0es~EZrhI;Fm!5cL% zHE=wXr`By6xrbRxVIk%zj3e&(mTLFBy#qPJ?P*!NOJ2qRHT^^dNt?`?Xfp;J&PdeW zLzgW~1Ur&xocG;?(vjH9v+Tvp_txUa1CyOK_3PYs_zDU5A6dOU3*pa`1NM;$~B&LK@Z^94(XUbq|
& addresses; - - /// Supplied address vector is expected to contain all addresses and programId from the instruction; they are replaced by index into the address vector. - CompiledInstruction(const Instruction& instruction, const std::vector
& addresses): addresses(addresses) { - programIdIndex = findAccount(instruction.programId); - accounts.reserve(instruction.accounts.size()); - for (auto&& account: instruction.accounts) { - accounts.emplace_back(findAccount(account.account)); - } - data = instruction.data; - } - - uint8_t findAccount(const Address& address); -}; - -} diff --git a/src/Solana/Constants.h b/src/Solana/Constants.h deleted file mode 100644 index 8b0ab567db2..00000000000 --- a/src/Solana/Constants.h +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include - -namespace TW::Solana { -// https://docs.solana.com/developing/programming-model/transactions - -static const std::string SYSTEM_PROGRAM_ID_ADDRESS = "11111111111111111111111111111111"; -static const std::string STAKE_PROGRAM_ID_ADDRESS = "Stake11111111111111111111111111111111111111"; -static const std::string TOKEN_PROGRAM_ID_ADDRESS = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"; -static const std::string ASSOCIATED_TOKEN_PROGRAM_ID_ADDRESS = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"; -static const std::string SYSVAR_RENT_ID_ADDRESS = "SysvarRent111111111111111111111111111111111"; -static const std::string SYSVAR_CLOCK_ID_ADDRESS = "SysvarC1ock11111111111111111111111111111111"; -static const std::string STAKE_CONFIG_ID_ADDRESS = "StakeConfig11111111111111111111111111111111"; -static const std::string NULL_ID_ADDRESS = "11111111111111111111111111111111"; -static const std::string SYSVAR_STAKE_HISTORY_ID_ADDRESS = "SysvarStakeHistory1111111111111111111111111"; -static const std::string MEMO_PROGRAM_ID_ADDRESS = "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"; -static const std::string SYSVAR_RECENT_BLOCKHASHS_ADDRESS = "SysvarRecentB1ockHashes11111111111111111111"; -// https://github.com/solana-labs/solana/blob/master/sdk/program/src/message/versions/mod.rs#L24 -static const std::uint8_t MESSAGE_VERSION_PREFIX{0x80}; - -} diff --git a/src/Solana/Encoding.h b/src/Solana/Encoding.h deleted file mode 100644 index bcef897df3a..00000000000 --- a/src/Solana/Encoding.h +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "Data.h" - -namespace TW::Solana { - -template -Data shortVecLength(std::vector vec) { - auto bytes = Data(); - auto remLen = vec.size(); - while (true) { - uint8_t elem = remLen & 0x7f; - remLen >>= 7; - if (remLen == 0) { - bytes.push_back(elem); - break; - } else { - elem |= 0x80; - bytes.push_back(elem); - } - } - return bytes; -} - -} diff --git a/src/Solana/Entry.cpp b/src/Solana/Entry.cpp index b814a12d344..1fc18b03274 100644 --- a/src/Solana/Entry.cpp +++ b/src/Solana/Entry.cpp @@ -4,57 +4,20 @@ #include "Entry.h" -#include "Address.h" -#include "Signer.h" +#include "proto/Solana.pb.h" using namespace TW; using namespace std; namespace TW::Solana { -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - return Address::isValid(address); -} - -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - return Address(publicKey).string(); -} - -Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { - return Address(address).vector(); -} - -void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { - signTemplate(dataIn, dataOut); -} - -string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { - return Signer::signJSON(json, key); -} - -TW::Data Entry::preImageHashes([[maybe_unused]] TWCoinType coin, const TW::Data& txInputData) const { - return txCompilerTemplate( - txInputData, [](const auto& input, auto& output) { - auto signer = Signer(input); - auto preimageHash = signer.preImageHash(); - // for Solana, there is no need to hash data. - output.set_data(preimageHash.data(), preimageHash.size()); - auto signers = signer.signers(); - auto nSigners = output.mutable_signers(); - for (auto i = 0ul; i < signers.size();i++) { - auto newSigner = nSigners->Add(); - *newSigner = signers[i]; - } - }); -} - -void Entry::compile([[maybe_unused]] TWCoinType coin, const Data& txInputData, const std::vector& signatures, - const std::vector& publicKeys, Data& dataOut) const { - dataOut = txCompilerTemplate( - txInputData, [&](const auto& input, auto& output) { - auto signer = Signer(input); - output = signer.compile(signatures, publicKeys); - }); +string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { + return signJSONHelper( + coin, + json, + key, + [](const Proto::SigningOutput& output) { return output.encoded(); } + ); } } // namespace TW::Solana diff --git a/src/Solana/Entry.h b/src/Solana/Entry.h index f4c66fac926..90d02756e5f 100644 --- a/src/Solana/Entry.h +++ b/src/Solana/Entry.h @@ -5,22 +5,16 @@ #pragma once #include "PublicKey.h" -#include "CoinEntry.h" +#include "rust/RustCoinEntry.h" namespace TW::Solana { /// Entry point for implementation of Solana coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry final : public CoinEntry { +class Entry final : public Rust::RustCoinEntryWithSignJSON { public: - bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const; - Data addressToData(TWCoinType coin, const std::string& address) const; - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - bool supportsJSONSigning() const { return true; } - std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; - Data preImageHashes(TWCoinType coin, const Data& txInputData) const; - void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const; + bool supportsJSONSigning() const override { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const override; }; } // namespace TW::Solana diff --git a/src/Solana/Instruction.h b/src/Solana/Instruction.h deleted file mode 100644 index 83cfc232874..00000000000 --- a/src/Solana/Instruction.h +++ /dev/null @@ -1,182 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "BinaryCoding.h" -#include "Solana/Address.h" -#include "Solana/AccountMeta.h" -#include "Solana/Constants.h" - -namespace TW::Solana { - -// System instruction types -enum SystemInstruction { - CreateAccount, - Assign, - Transfer, - CreateAccountWithSeed, - AdvanceNonceAccount, - WithdrawNonceAccount, - InitializeNonceAccount -}; - -// Stake instruction types -enum StakeInstruction { - Initialize = 0, - DelegateStake = 2, - Withdraw = 4, - Deactivate = 5, -}; - -// Token instruction types -enum TokenInstruction { - CreateTokenAccount = 1, - //SetAuthority = 6, - TokenTransfer = 12, -}; - -// An instruction to execute a program -struct Instruction { - // The address of the program account that executes this instruction - Address programId; - // List of accounts to pass to the program - std::vector accounts; - // The program input data - Data data; - - Instruction(const Address& programId, const std::vector& accounts, const Data& data) - : programId(programId), accounts(accounts), data(data) {} - - // This creator creates a default System Transfer instruction - static Instruction createTransfer(const std::vector& accounts, uint64_t value) { - const SystemInstruction type = Transfer; - auto data = Data(); - encode32LE(static_cast(type), data); - encode64LE(static_cast(value), data); - - return Instruction(Address(SYSTEM_PROGRAM_ID_ADDRESS), accounts, data); - } - - static Instruction createAccountWithSeed(const std::vector& accounts, uint64_t value, uint64_t space, const Address& programId, - const Address& voteAddress, uint64_t seedLength, const Address& signer) { - const SystemInstruction type = CreateAccountWithSeed; - auto data = Data(); - std::string seed = voteAddress.string(); - Data vecSeed(seed.begin(), seed.end()); - vecSeed.resize(static_cast(seedLength)); - encode32LE(static_cast(type), data); - append(data, signer.vector()); - encode64LE(static_cast(seedLength), data); - append(data, vecSeed); - encode64LE(static_cast(value), data); - encode64LE(static_cast(space), data); - append(data, programId.vector()); - - return Instruction(Address(SYSTEM_PROGRAM_ID_ADDRESS), accounts, data); - } - - // creates an Initialize Stake instruction - static Instruction createStakeInitialize(const std::vector& accounts, const Address& signer) { - const StakeInstruction type = Initialize; - auto data = Data(); - encode32LE(static_cast(type), data); - append(data, signer.vector()); - append(data, signer.vector()); - auto lockup = Data(48); - append(data, lockup); - - return Instruction(Address(STAKE_PROGRAM_ID_ADDRESS), accounts, data); - } - - // creates a Withdraw Stake instruction - static Instruction createStakeWithdraw(const std::vector& accounts, uint64_t value) { - const StakeInstruction type = Withdraw; - auto data = Data(); - encode32LE(static_cast(type), data); - encode64LE(static_cast(value), data); - - return Instruction(Address(STAKE_PROGRAM_ID_ADDRESS), accounts, data); - } - - // creates a Stake instruction - static Instruction createStake(StakeInstruction type, const std::vector& accounts) { - auto data = Data(); - encode32LE(static_cast(type), data); - - return Instruction(Address(STAKE_PROGRAM_ID_ADDRESS), accounts, data); - } - - // creates a createAccount token instruction. - static Instruction createTokenCreateAccount(const std::vector& accounts) { - auto data = Data(); - return Instruction(Address(ASSOCIATED_TOKEN_PROGRAM_ID_ADDRESS), accounts, data); - } - - // creates a transfer token instruction. - static Instruction createTokenTransfer(const std::vector& accounts, uint64_t value, uint8_t decimals) { - const TokenInstruction type = TokenTransfer; - auto data = Data(); - data.push_back(static_cast(type)); - encode64LE(value, data); - data.push_back(static_cast(decimals)); - - return Instruction(Address(TOKEN_PROGRAM_ID_ADDRESS), accounts, data); - } - - static Instruction createMemo(std::string memo) { - auto data = TW::data(memo); - std::vector accounts; // empty - return Instruction(Address(MEMO_PROGRAM_ID_ADDRESS), accounts, data); - } - - // create a system advance nonce account instruction to update nonce - static Instruction advanceNonceAccount(const Address authorizer, const Address nonceAccount) { - std::vector accountMetas = { - AccountMeta(nonceAccount, false, false), - AccountMeta(Address(SYSVAR_RECENT_BLOCKHASHS_ADDRESS), false, true), - AccountMeta(authorizer, true, true), - }; - const SystemInstruction type = AdvanceNonceAccount; - auto data = Data(); - encode32LE(static_cast(type), data); - - return Instruction(Address(SYSTEM_PROGRAM_ID_ADDRESS), accountMetas, data); - } - - // create a System initialize nonce instruction - static Instruction createInitializeNonce(const std::vector& accounts, - const Address authorizer) { - const SystemInstruction type = InitializeNonceAccount; - auto data = Data(); - encode32LE(static_cast(type), data); - append(data, authorizer.vector()); - - return Instruction(Address(SYSTEM_PROGRAM_ID_ADDRESS), accounts, data); - } - - // create a system create account instruction - static Instruction createAccount(const std::vector& accounts, uint64_t value, - uint64_t space, const Address owner) { - const SystemInstruction type = CreateAccount; - auto data = Data(); - encode32LE(static_cast(type), data); - encode64LE(static_cast(value), data); - encode64LE(static_cast(space), data); - append(data, owner.vector()); - - return Instruction(Address(SYSTEM_PROGRAM_ID_ADDRESS), accounts, data); - } - - static Instruction withdrawNonceAccount(const std::vector& accounts, - uint64_t value) { - const SystemInstruction type = WithdrawNonceAccount; - auto data = Data(); - encode32LE(static_cast(type), data); - encode64LE(static_cast(value), data); - return Instruction(Address(SYSTEM_PROGRAM_ID_ADDRESS), accounts, data); - } -}; - -} diff --git a/src/Solana/LegacyMessage.cpp b/src/Solana/LegacyMessage.cpp deleted file mode 100644 index 3ce19808524..00000000000 --- a/src/Solana/LegacyMessage.cpp +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "Solana/LegacyMessage.h" -#include "Solana/Encoding.h" - -namespace TW::Solana { -void LegacyMessage::addAccount(const AccountMeta& account) { - bool inSigned = (std::find(signedAccounts.begin(), signedAccounts.end(), account.account) != signedAccounts.end()); - bool inUnsigned = (std::find(unsignedAccounts.begin(), unsignedAccounts.end(), account.account) != unsignedAccounts.end()); - bool inReadOnly = (std::find(readOnlyAccounts.begin(), readOnlyAccounts.end(), account.account) != readOnlyAccounts.end()); - if (account.isSigner) { - if (!inSigned) { - signedAccounts.push_back(account.account); - } - } else if (!account.isReadOnly) { - if (!inSigned && !inUnsigned) { - unsignedAccounts.push_back(account.account); - } - } else { - if (!inSigned && !inUnsigned && !inReadOnly) { - readOnlyAccounts.push_back(account.account); - } - } -} - -void LegacyMessage::addAccountKeys(const Address& account) { - if (std::find(accountKeys.begin(), accountKeys.end(), account) == accountKeys.end()) { - accountKeys.push_back(account); - } -} - -Data LegacyMessage::serialize() const { - Data buffer; - - buffer.push_back(this->header.numRequiredSignatures); - buffer.push_back(this->header.numReadOnlySignedAccounts); - buffer.push_back(this->header.numReadOnlyUnsignedAccounts); - append(buffer, shortVecLength
(this->accountKeys)); - for (auto account_key : this->accountKeys) { - Data account_key_vec(account_key.bytes.begin(), account_key.bytes.end()); - append(buffer, account_key_vec); - } - append(buffer, mRecentBlockHash); - - // apppend compiled instructions - append(buffer, shortVecLength(compiledInstructions)); - for (auto instruction : compiledInstructions) { - buffer.push_back(instruction.programIdIndex); - append(buffer, shortVecLength(instruction.accounts)); - append(buffer, instruction.accounts); - append(buffer, shortVecLength(instruction.data)); - append(buffer, instruction.data); - } - - return buffer; -} - -void LegacyMessage::compileAccounts(const std::string& feePayerStr) { - if (Address::isValid(feePayerStr)) { - addAccount(AccountMeta(Address(feePayerStr), true, false)); - } - - for (auto& instr : instructions) { - for (auto& address : instr.accounts) { - addAccount(address); - } - } - // add programIds (read-only, at end) - for (auto& instr : instructions) { - addAccount(AccountMeta{instr.programId, false, true}); - } - - header = MessageHeader{ - (uint8_t)signedAccounts.size(), - 0, - (uint8_t)readOnlyAccounts.size()}; - - // merge the three buckets - accountKeys.clear(); - for (auto& a : signedAccounts) { - addAccountKeys(a); - } - for (auto& a : unsignedAccounts) { - addAccountKeys(a); - } - for (auto& a : readOnlyAccounts) { - addAccountKeys(a); - } - - compileInstructions(); -} - -void LegacyMessage::compileInstructions() { - compiledInstructions.clear(); - for (auto instruction : instructions) { - compiledInstructions.emplace_back(CompiledInstruction(instruction, accountKeys)); - } -} -} diff --git a/src/Solana/LegacyMessage.h b/src/Solana/LegacyMessage.h deleted file mode 100644 index dd9e1a887b7..00000000000 --- a/src/Solana/LegacyMessage.h +++ /dev/null @@ -1,337 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "Solana/MessageHeader.h" -#include "Solana/Address.h" -#include "Solana/Instruction.h" -#include "Solana/CompiledInstruction.h" -#include "Solana/Constants.h" - -#include - -namespace TW::Solana { - -class LegacyMessage { -public: - // The message header, identifying signed and credit-only `accountKeys` - MessageHeader header; - // All the account keys used by this transaction - std::vector
accountKeys; - // The id of a recent ledger entry. - Data mRecentBlockHash; - // Programs that will be executed in sequence and committed in one atomic - // transaction if all succeed. - std::vector instructions; - - // three buckets of different account types - std::vector
signedAccounts; - std::vector
unsignedAccounts; - std::vector
readOnlyAccounts; - std::vector compiledInstructions; - - LegacyMessage() - : mRecentBlockHash(Base58::decode(NULL_ID_ADDRESS)){}; - - LegacyMessage(Data recentBlockHash, const std::vector& instructions, const std::string& feePayerStr = "") - : mRecentBlockHash(recentBlockHash) - , instructions(instructions) { - compileAccounts(feePayerStr); - } - - // add an acount, to the corresponding bucket - void addAccount(const AccountMeta& account); - // add an account to accountKeys if not yet present - void addAccountKeys(const Address& account); - // compile the single accounts lists from the buckets - void compileAccounts(const std::string& feePayerStr = ""); - // compile the instructions; replace instruction accounts with indices - void compileInstructions(); - - // Serialize to msg data - Data serialize() const; - - static void appendReferences(std::vector& accountMetas, const std::vector
& references) { - for (auto&& reference : references) { - accountMetas.emplace_back(reference, false, true); - } - } - - // This constructor creates a default single-signer Transfer message - static LegacyMessage createTransfer(const Address& from, const Address& to, uint64_t value, Data mRecentBlockHash, - std::string memo = "", std::vector
references = {}, const std::string& nonceAccountStr = "") { - std::vector instructions; - if (Address::isValid(nonceAccountStr)) { - instructions.push_back( - Instruction::advanceNonceAccount(from, Address(nonceAccountStr))); - } - if (memo.length() > 0) { - // Optional memo. Order: before transfer, as per documentation. - instructions.push_back(Instruction::createMemo(memo)); - } - std::vector accountMetas = { - AccountMeta(from, true, false), - AccountMeta(to, false, false), - }; - appendReferences(accountMetas, references); - instructions.push_back(Instruction::createTransfer(accountMetas, value)); - return LegacyMessage(mRecentBlockHash, instructions); - } - - // This constructor creates a create_account_with_seed_and_delegate_stake message - // see delegate_stake() solana/programs/stake/src/stake_instruction.rs - static LegacyMessage createStake(const Address& signer, const Address& stakeAddress, const Address& voteAddress, uint64_t value, Data mRecentBlockHash) { - auto sysvarRentId = Address(SYSVAR_RENT_ID_ADDRESS); - auto sysvarClockId = Address(SYSVAR_CLOCK_ID_ADDRESS); - auto stakeConfigId = Address(STAKE_CONFIG_ID_ADDRESS); - auto sysvarStakeHistoryId = Address(SYSVAR_STAKE_HISTORY_ID_ADDRESS); - auto stakeProgramId = Address(STAKE_PROGRAM_ID_ADDRESS); - std::vector instructions; - instructions.reserve(3); - // create_account_with_seed instruction - Address seed = Address(mRecentBlockHash); - auto createAccountInstruction = Instruction::createAccountWithSeed(std::vector{ - AccountMeta(signer, true, true), - AccountMeta(stakeAddress, false, false), - AccountMeta(signer, true, true), - }, - value, 200, stakeProgramId, seed, 32, signer); - instructions.push_back(createAccountInstruction); - // initialize instruction - auto initializeInstruction = Instruction::createStakeInitialize(std::vector{ - AccountMeta(stakeAddress, false, false), - AccountMeta(sysvarRentId, false, true)}, - signer); - instructions.push_back(initializeInstruction); - // delegate_stake instruction - auto delegateInstruction = Instruction::createStake(DelegateStake, - std::vector{ - AccountMeta(stakeAddress, false, false), // 0. `[WRITE]` Initialized stake account to be delegated - AccountMeta(voteAddress, false, true), // 1. `[]` Vote account to which this stake will be delegated - AccountMeta(sysvarClockId, false, true), // 2. `[]` Clock sysvar - AccountMeta(sysvarStakeHistoryId, false, true), // 3. `[]` Stake history sysvar that carries stake warmup/cooldown history - AccountMeta(stakeConfigId, false, true), // 4. `[]` Address of config account that carries stake config - AccountMeta(signer, true, true), // 5. `[SIGNER]` Stake authority - }); - instructions.push_back(delegateInstruction); - return LegacyMessage(mRecentBlockHash, instructions); - } - - // This constructor creates a deactivate_stake message - static LegacyMessage createStakeDeactivate(const Address& signer, const Address& stakeAddress, Data mRecentBlockHash) { - auto sysvarClockId = Address(SYSVAR_CLOCK_ID_ADDRESS); - auto instruction = Instruction::createStake(Deactivate, std::vector{ - AccountMeta(stakeAddress, false, false), // 0. `[WRITE]` Delegated stake account - AccountMeta(sysvarClockId, false, true), // 1. `[]` Clock sysvar - AccountMeta(signer, true, false), // 2. `[SIGNER]` Stake authority - }); - return LegacyMessage(mRecentBlockHash, {instruction}); - } - - // This constructor creates a deactivate_stake message with multiple stake accounts - static LegacyMessage createStakeDeactivateAll(const Address& signer, const std::vector
& stakeAddresses, Data mRecentBlockHash) { - auto sysvarClockId = Address(SYSVAR_CLOCK_ID_ADDRESS); - std::vector instructions; - for (auto& address : stakeAddresses) { - auto instruction = Instruction::createStake(Deactivate, std::vector{ - AccountMeta(address, false, false), // 0. `[WRITE]` Delegated stake account - AccountMeta(sysvarClockId, false, true), // 1. `[]` Clock sysvar - AccountMeta(signer, true, false), // 2. `[SIGNER]` Stake authority - }); - instructions.push_back(instruction); - } - return LegacyMessage(mRecentBlockHash, instructions); - } - - // This constructor creates a withdraw message, with the signer as the default recipient - static LegacyMessage createStakeWithdraw(const Address& signer, const Address& stakeAddress, uint64_t value, Data mRecentBlockHash) { - auto sysvarClockId = Address(SYSVAR_CLOCK_ID_ADDRESS); - auto sysvarStakeHistoryId = Address(SYSVAR_STAKE_HISTORY_ID_ADDRESS); - auto instruction = Instruction::createStakeWithdraw(std::vector{ - AccountMeta(stakeAddress, false, false), // 0. `[WRITE]` Stake account from which to withdraw - AccountMeta(signer, false, false), // 1. `[WRITE]` Recipient account - AccountMeta(sysvarClockId, false, true), // 2. `[]` Clock sysvar - AccountMeta(sysvarStakeHistoryId, false, true), // 3. `[]` Stake history sysvar that carries stake warmup/cooldown history - AccountMeta(signer, true, false), // 4. `[SIGNER]` Withdraw authority - }, - value); - return LegacyMessage(mRecentBlockHash, {instruction}); - } - - // This constructor creates a withdraw message, with multiple stake accounts - static LegacyMessage createStakeWithdrawAll(const Address& signer, const std::vector>& stakes, Data mRecentBlockHash) { - auto sysvarClockId = Address(SYSVAR_CLOCK_ID_ADDRESS); - auto sysvarStakeHistoryId = Address(SYSVAR_STAKE_HISTORY_ID_ADDRESS); - std::vector instructions; - for (auto& stake : stakes) { - auto instruction = Instruction::createStakeWithdraw(std::vector{ - AccountMeta(stake.first, false, false), // 0. `[WRITE]` Stake account from which to withdraw - AccountMeta(signer, false, false), // 1. `[WRITE]` Recipient account - AccountMeta(sysvarClockId, false, true), // 2. `[]` Clock sysvar - AccountMeta(sysvarStakeHistoryId, false, true), // 3. `[]` Stake history sysvar that carries stake warmup/cooldown history - AccountMeta(signer, true, false), // 4. `[SIGNER]` Withdraw authority - }, - stake.second); - instructions.push_back(instruction); - } - return LegacyMessage(mRecentBlockHash, instructions); - } - - // This constructor creates a createAccount token message - // see create_associated_token_account() solana-program-library/associated-token-account/program/src/lib.rs - static LegacyMessage createTokenCreateAccount(const Address& signer, const Address& otherMainAccount, const Address& tokenMintAddress, const Address& tokenAddress, Data mRecentBlockHash, const std::string& nonceAccountStr = "") { - auto sysvarRentId = Address(SYSVAR_RENT_ID_ADDRESS); - auto systemProgramId = Address(SYSTEM_PROGRAM_ID_ADDRESS); - auto tokenProgramId = Address(TOKEN_PROGRAM_ID_ADDRESS); - std::vector instructions; - if (Address::isValid(nonceAccountStr)) { - instructions.push_back( - Instruction::advanceNonceAccount(signer, Address(nonceAccountStr))); - } - auto instruction = Instruction::createTokenCreateAccount(std::vector{ - AccountMeta(signer, true, false), // fundingAddress, - AccountMeta(tokenAddress, false, false), - AccountMeta(otherMainAccount, false, true), - AccountMeta(tokenMintAddress, false, true), - AccountMeta(systemProgramId, false, true), - AccountMeta(tokenProgramId, false, true), - AccountMeta(sysvarRentId, false, true), - }); - instructions.push_back(instruction); - return LegacyMessage(mRecentBlockHash, instructions); - } - - // This constructor creates a transfer token message. - // see transfer_checked() solana-program-library/token/program/src/instruction.rs - static LegacyMessage createTokenTransfer(const Address& signer, const Address& tokenMintAddress, - const Address& senderTokenAddress, const Address& recipientTokenAddress, uint64_t amount, uint8_t decimals, Data mRecentBlockHash, - std::string memo = "", std::vector
references = {}, - const std::string& nonceAccountStr = "", const std::string& feePayerStr = "") { - std::vector instructions; - if (Address::isValid(nonceAccountStr)) { - instructions.push_back( - Instruction::advanceNonceAccount(signer, Address(nonceAccountStr))); - } - if (memo.length() > 0) { - // Optional memo. Order: before transfer, as per documentation. - instructions.push_back(Instruction::createMemo(memo)); - } - std::vector accountMetas = { - AccountMeta(senderTokenAddress, false, false), - AccountMeta(tokenMintAddress, false, true), - AccountMeta(recipientTokenAddress, false, false), - AccountMeta(signer, true, false), - }; - appendReferences(accountMetas, references); - instructions.push_back(Instruction::createTokenTransfer(accountMetas, amount, decimals)); - - if (Address::isValid(feePayerStr)) { - return LegacyMessage(mRecentBlockHash, instructions, feePayerStr); - } - - return LegacyMessage(mRecentBlockHash, instructions); - } - - // This constructor creates a createAndTransferToken message, combining createAccount and transfer. - static LegacyMessage createTokenCreateAndTransfer(const Address& signer, const Address& recipientMainAddress, const Address& tokenMintAddress, - const Address& recipientTokenAddress, const Address& senderTokenAddress, uint64_t amount, uint8_t decimals, Data mRecentBlockHash, - std::string memo = "", std::vector
references = {}, - const std::string& nonceAccountStr = "", const std::string& feePayerStr = "") { - const auto sysvarRentId = Address(SYSVAR_RENT_ID_ADDRESS); - const auto systemProgramId = Address(SYSTEM_PROGRAM_ID_ADDRESS); - const auto tokenProgramId = Address(TOKEN_PROGRAM_ID_ADDRESS); - std::vector instructions; - instructions.reserve(3); - if (Address::isValid(nonceAccountStr)) { - instructions.push_back( - Instruction::advanceNonceAccount(signer, Address(nonceAccountStr))); - } - std::vector createTokenAccountAccountMetas; - if (Address::isValid(feePayerStr)) { - createTokenAccountAccountMetas.emplace_back(AccountMeta(Address(feePayerStr), true, false)); - } else { - createTokenAccountAccountMetas.emplace_back(AccountMeta(signer, true, false)); - } - createTokenAccountAccountMetas.emplace_back(AccountMeta(recipientTokenAddress, false, false)); - createTokenAccountAccountMetas.emplace_back(AccountMeta(recipientMainAddress, false, true)); - createTokenAccountAccountMetas.emplace_back(AccountMeta(tokenMintAddress, false, true)); - createTokenAccountAccountMetas.emplace_back(AccountMeta(systemProgramId, false, true)); - createTokenAccountAccountMetas.emplace_back(AccountMeta(tokenProgramId, false, true)); - createTokenAccountAccountMetas.emplace_back(AccountMeta(sysvarRentId, false, true)); - instructions.emplace_back(Instruction::createTokenCreateAccount(createTokenAccountAccountMetas)); - if (memo.length() > 0) { - // Optional memo. Order: before transfer, as per documentation. - instructions.emplace_back(Instruction::createMemo(memo)); - } - std::vector accountMetas = { - AccountMeta(senderTokenAddress, false, false), - AccountMeta(tokenMintAddress, false, true), - AccountMeta(recipientTokenAddress, false, false), - AccountMeta(signer, true, false), - }; - appendReferences(accountMetas, references); - instructions.push_back(Instruction::createTokenTransfer(accountMetas, amount, decimals)); - if (Address::isValid(feePayerStr)) { - return LegacyMessage(mRecentBlockHash, instructions, feePayerStr); - } - return LegacyMessage(mRecentBlockHash, instructions); - } - - static LegacyMessage createNonceAccount(const Address& sender, const Address& newNonceAccountAddress, - uint64_t rent, Data mRecentBlockHash, - std::string nonceAccountStr = "") { - auto sysvarRentId = Address(SYSVAR_RENT_ID_ADDRESS); - auto sysvarRecentBlockhashsId = Address(SYSVAR_RECENT_BLOCKHASHS_ADDRESS); - auto systemProgramId = Address(SYSTEM_PROGRAM_ID_ADDRESS); - std::vector createAccountAccountMetas = { - AccountMeta(sender, true, false), AccountMeta(newNonceAccountAddress, true, false)}; - std::vector initializeNonceAccountAccountMetas = { - AccountMeta(newNonceAccountAddress, false, false), - AccountMeta(sysvarRecentBlockhashsId, false, true), - AccountMeta(sysvarRentId, false, true)}; - std::vector instructions; - if (Address::isValid(nonceAccountStr)) { - instructions.push_back( - Instruction::advanceNonceAccount(sender, Address(nonceAccountStr))); - } - instructions.push_back( - Instruction::createAccount(createAccountAccountMetas, rent, 80, systemProgramId)); - instructions.push_back( - Instruction::createInitializeNonce(initializeNonceAccountAccountMetas, sender)); - return LegacyMessage(mRecentBlockHash, instructions); - } - - static LegacyMessage createWithdrawNonceAccount(const Address& authorizer, - const Address& nonceAccountAddress, const Address& to, - uint64_t value, Data mRecentBlockHash, - std::string nonceAccountStr = "") { - auto sysvarRecentBlockhashsId = Address(SYSVAR_RECENT_BLOCKHASHS_ADDRESS); - auto sysvarRentId = Address(SYSVAR_RENT_ID_ADDRESS); - std::vector accountMetas = { - AccountMeta(nonceAccountAddress, false, false), AccountMeta(to, false, false), - AccountMeta(sysvarRecentBlockhashsId, false, true), - AccountMeta(sysvarRentId, false, true), AccountMeta(authorizer, true, true)}; - std::vector instructions; - if (Address::isValid(nonceAccountStr)) { - instructions.push_back( - Instruction::advanceNonceAccount(authorizer, Address(nonceAccountStr))); - } - instructions.push_back(Instruction::withdrawNonceAccount(accountMetas, value)); - return LegacyMessage(mRecentBlockHash, instructions); - } - - static LegacyMessage advanceNonceAccount(const Address& authorizer, - const Address& nonceAccountAddress, Data mRecentBlockHash) { - auto sysvarRecentBlockhashsId = Address(SYSVAR_RECENT_BLOCKHASHS_ADDRESS); - std::vector accountMetas = {AccountMeta(nonceAccountAddress, false, false), - AccountMeta(sysvarRecentBlockhashsId, false, true), - AccountMeta(authorizer, true, true)}; - std::vector instructions; - instructions.push_back(Instruction::advanceNonceAccount(authorizer, nonceAccountAddress)); - return LegacyMessage(mRecentBlockHash, instructions); - } -}; - -} diff --git a/src/Solana/MessageHeader.h b/src/Solana/MessageHeader.h deleted file mode 100644 index b73e6c0376e..00000000000 --- a/src/Solana/MessageHeader.h +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include // << std::uint8_t - -struct MessageHeader { - // The number of signatures required for this message to be considered - // valid. The signatures must match the first `numRequiredSignatures` of - // `accountKeys`. - uint8_t numRequiredSignatures = 0; - // The last numRequiredSignatures of the signed keys are - // read-only accounts. - uint8_t numReadOnlySignedAccounts = 0; - // The last numReadOnlyUnsignedAccounts of the unsigned keys are - // read-only accounts. - uint8_t numReadOnlyUnsignedAccounts = 0; -}; diff --git a/src/Solana/Program.cpp b/src/Solana/Program.cpp deleted file mode 100644 index c835a220827..00000000000 --- a/src/Solana/Program.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "Program.h" -#include "Address.h" -#include "Transaction.h" - -#include - -namespace TW::Solana { - -Address StakeProgram::addressFromValidatorSeed(const Address& fromAddress, const Address& validatorAddress, - const Address& programId) { - Data extended = fromAddress.vector(); - std::string seed = validatorAddress.string(); - Data vecSeed(seed.begin(), seed.end()); - vecSeed.resize(32); - Data additional = programId.vector(); - extended.insert(extended.end(), vecSeed.begin(), vecSeed.end()); - extended.insert(extended.end(), additional.begin(), additional.end()); - Data hash = TW::Hash::sha256(extended); - return Address(hash); -} - -Address StakeProgram::addressFromRecentBlockhash(const Address& fromAddress, const Data& recentBlockhash, const Address& programId) { - Data extended = fromAddress.vector(); - std::string seed = Base58::encode(recentBlockhash); - Data vecSeed(seed.begin(), seed.end()); - vecSeed.resize(32); - Data additional = programId.vector(); - extended.insert(extended.end(), vecSeed.begin(), vecSeed.end()); - extended.insert(extended.end(), additional.begin(), additional.end()); - Data hash = TW::Hash::sha256(extended); - return Address(hash); -} - -/* - * Based on solana-program-library code, get_associated_token_address() - * https://github.com/solana-labs/solana-program-library/blob/master/associated-token-account/program/src/lib.rs#L35 - * https://github.com/solana-labs/solana-program-library/blob/master/associated-token-account/program/src/lib.rs#L19 - */ -Address TokenProgram::defaultTokenAddress(const Address& mainAddress, const Address& tokenMintAddress) { - auto programId = Address(TOKEN_PROGRAM_ID_ADDRESS); - std::vector seeds = { - TW::data(mainAddress.bytes.data(), mainAddress.bytes.size()), - TW::data(programId.bytes.data(), programId.bytes.size()), - TW::data(tokenMintAddress.bytes.data(), tokenMintAddress.bytes.size())}; - return findProgramAddress(seeds, Address(ASSOCIATED_TOKEN_PROGRAM_ID_ADDRESS)); -} - -/* - * Based on solana code, find_program_address() - * https://github.com/solana-labs/solana/blob/master/sdk/program/src/pubkey.rs#L193 - */ -Address TokenProgram::findProgramAddress(const std::vector& seeds, [[maybe_unused]] const Address& programId) { - Address result(Data(32)); - // cycle through seeds for the rare case when result is not valid - auto bumpSeed = Data{std::numeric_limits::max()}; - for (std::uint8_t seed = 0; seed <= std::numeric_limits::max(); ++seed) { - std::vector seedsCopy = seeds; - seedsCopy.emplace_back(bumpSeed); - Address address = createProgramAddress(seedsCopy, programId); - PublicKey publicKey = PublicKey(TW::data(address.bytes.data(), address.bytes.size()), TWPublicKeyTypeED25519); - if (!publicKey.isValidED25519()) { - result = address; - break; - } - bumpSeed[0] -= 1; - } - return result; -} - -/* - * Based on solana code, create_program_address() - * https://github.com/solana-labs/solana/blob/master/sdk/program/src/pubkey.rs#L135 - */ -Address TokenProgram::createProgramAddress(const std::vector& seeds, const Address& programId) { - // concatenate seeds - Data hashInput; - for (auto& seed : seeds) { - append(hashInput, seed); - } - // append programId - append(hashInput, TW::data(programId.bytes.data(), programId.bytes.size())); - append(hashInput, TW::data("ProgramDerivedAddress")); - // compute hash - Data hash = TW::Hash::sha256(hashInput.data(), hashInput.size()); - return Address(hash); -} - -} // namespace TW::Solana - diff --git a/src/Solana/Program.h b/src/Solana/Program.h deleted file mode 100644 index 814d37d4c9b..00000000000 --- a/src/Solana/Program.h +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "Address.h" -#include "Transaction.h" - -#include - -namespace TW::Solana { - -class StakeProgram { -public: - static Address addressFromValidatorSeed(const Address& fromAddress, - const Address& validatorAddress, - const Address& programId); - - static Address addressFromRecentBlockhash(const Address& fromAddress, const Data& recentBlockhash, const Address& programId); -}; - - -class TokenProgram { -public: - /// Derive default token address for main address and token - static Address defaultTokenAddress(const Address& mainAddress, const Address& tokenMintAddress); - - /// Create a new valid address, if neeed, trying several - static Address findProgramAddress(const std::vector& seeds, const Address& programId); - - /// Create a new address for program, with given seeds - static Address createProgramAddress(const std::vector& seeds, const Address& programId); -}; - -} // namespace TW::Solana diff --git a/src/Solana/Signer.cpp b/src/Solana/Signer.cpp deleted file mode 100644 index b1677f97f02..00000000000 --- a/src/Solana/Signer.cpp +++ /dev/null @@ -1,530 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "Signer.h" -#include "Address.h" -#include "Program.h" -#include "Solana/Encoding.h" -#include "Solana/VersionedTransaction.h" - -#include - -#include - -namespace TW::Solana { - -void Signer::sign(const std::vector& privateKeys, VersionedTransaction& transaction) { - for (auto privateKey : privateKeys) { - auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeED25519)); - auto index = transaction.getAccountIndex(address); - auto message = transaction.messageData(); - transaction.signatures[index] = privateKey.sign(message, TWCurveED25519); - } -} - -// Helper to convert protobuf-string-collection references to Address vector -std::vector
-convertReferences(const google::protobuf::RepeatedPtrField& references) { - std::vector
ret; - for (auto i = 0; i < references.size(); ++i) { - if (Address::isValid(references[i])) { - ret.emplace_back(references[i]); - } - } - return ret; -} - -Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { - auto blockhash = Base58::decode(input.recent_blockhash()); - auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); - LegacyMessage message; - std::vector signerKeys; - - if (Address::isValid(input.fee_payer())) { - auto feePayerKey = PrivateKey(Data(input.fee_payer_private_key().begin(), input.fee_payer_private_key().end())); - signerKeys.push_back(feePayerKey); - } - - switch (input.transaction_type_case()) { - case Proto::SigningInput::TransactionTypeCase::kTransferTransaction: { - auto protoMessage = input.transfer_transaction(); - message = LegacyMessage::createTransfer( - /* from */ Address(key.getPublicKey(TWPublicKeyTypeED25519)), - /* to */ Address(protoMessage.recipient()), - /* value */ protoMessage.value(), - /* recent_blockhash */ blockhash, - /* memo */ protoMessage.memo(), - convertReferences(protoMessage.references())); - signerKeys.push_back(key); - } break; - - case Proto::SigningInput::TransactionTypeCase::kDelegateStakeTransaction: { - auto protoMessage = input.delegate_stake_transaction(); - auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); - auto validatorAddress = Address(protoMessage.validator_pubkey()); - auto stakeProgramId = Address(STAKE_PROGRAM_ID_ADDRESS); - std::optional
stakeAddress; - if (protoMessage.stake_account().size() == 0) { - // no stake address specified, generate a new unique - stakeAddress = StakeProgram::addressFromRecentBlockhash(userAddress, blockhash, stakeProgramId); - } else { - // stake address specified, use it - stakeAddress = Address(protoMessage.stake_account()); - } - message = LegacyMessage::createStake( - /* signer */ userAddress, - /* stakeAddress */ stakeAddress.value(), - /* voteAddress */ validatorAddress, - /* value */ protoMessage.value(), - /* recent_blockhash */ blockhash); - signerKeys.push_back(key); - } break; - - case Proto::SigningInput::TransactionTypeCase::kDeactivateStakeTransaction: { - auto protoMessage = input.deactivate_stake_transaction(); - auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); - auto stakeAddress = Address(protoMessage.stake_account()); - message = LegacyMessage::createStakeDeactivate( - /* signer */ userAddress, - /* stakeAddress */ stakeAddress, - /* recent_blockhash */ blockhash); - signerKeys.push_back(key); - } break; - - case Proto::SigningInput::TransactionTypeCase::kDeactivateAllStakeTransaction: { - auto protoMessage = input.deactivate_all_stake_transaction(); - auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); - std::vector
addresses; - for (auto i = 0; i < protoMessage.stake_accounts_size(); ++i) { - addresses.emplace_back(Address(protoMessage.stake_accounts(i))); - } - message = LegacyMessage::createStakeDeactivateAll(userAddress, addresses, blockhash); - signerKeys.push_back(key); - } break; - - case Proto::SigningInput::TransactionTypeCase::kWithdrawTransaction: { - auto protoMessage = input.withdraw_transaction(); - auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); - auto stakeAddress = Address(protoMessage.stake_account()); - message = LegacyMessage::createStakeWithdraw( - /* signer */ userAddress, - /* stakeAddress */ stakeAddress, - /* value */ protoMessage.value(), - /* recent_blockhash */ blockhash); - signerKeys.push_back(key); - } break; - - case Proto::SigningInput::TransactionTypeCase::kWithdrawAllTransaction: { - auto protoMessage = input.withdraw_all_transaction(); - auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); - std::vector> stakes; - for (auto i = 0; i < protoMessage.stake_accounts_size(); ++i) { - stakes.push_back(std::make_pair( - Address(protoMessage.stake_accounts(i).stake_account()), - protoMessage.stake_accounts(i).value())); - } - message = LegacyMessage::createStakeWithdrawAll(userAddress, stakes, blockhash); - signerKeys.push_back(key); - } break; - - case Proto::SigningInput::TransactionTypeCase::kCreateTokenAccountTransaction: { - auto protoMessage = input.create_token_account_transaction(); - auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); - auto mainAddress = Address(protoMessage.main_address()); - auto tokenMintAddress = Address(protoMessage.token_mint_address()); - auto tokenAddress = Address(protoMessage.token_address()); - message = LegacyMessage::createTokenCreateAccount(userAddress, mainAddress, tokenMintAddress, - tokenAddress, blockhash, input.nonce_account()); - signerKeys.push_back(key); - } break; - - case Proto::SigningInput::TransactionTypeCase::kTokenTransferTransaction: { - auto protoMessage = input.token_transfer_transaction(); - auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); - auto tokenMintAddress = Address(protoMessage.token_mint_address()); - auto senderTokenAddress = Address(protoMessage.sender_token_address()); - auto recipientTokenAddress = Address(protoMessage.recipient_token_address()); - auto amount = protoMessage.amount(); - auto decimals = static_cast(protoMessage.decimals()); - const auto memo = protoMessage.memo(); - message = LegacyMessage::createTokenTransfer(userAddress, tokenMintAddress, senderTokenAddress, - recipientTokenAddress, amount, decimals, blockhash, - memo, convertReferences(protoMessage.references()), - input.nonce_account(), input.fee_payer()); - signerKeys.push_back(key); - } break; - - case Proto::SigningInput::TransactionTypeCase::kCreateAndTransferTokenTransaction: { - auto protoMessage = input.create_and_transfer_token_transaction(); - auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); - auto recipientMainAddress = Address(protoMessage.recipient_main_address()); - auto tokenMintAddress = Address(protoMessage.token_mint_address()); - auto recipientTokenAddress = Address(protoMessage.recipient_token_address()); - auto senderTokenAddress = Address(protoMessage.sender_token_address()); - auto amount = protoMessage.amount(); - auto decimals = static_cast(protoMessage.decimals()); - const auto memo = protoMessage.memo(); - message = LegacyMessage::createTokenCreateAndTransfer( - userAddress, recipientMainAddress, tokenMintAddress, recipientTokenAddress, - senderTokenAddress, amount, decimals, blockhash, memo, - convertReferences(protoMessage.references()), input.nonce_account(), input.fee_payer()); - signerKeys.push_back(key); - } break; - - case Proto::SigningInput::TransactionTypeCase::kCreateNonceAccount: { - auto createNonceAccountTransaction = input.create_nonce_account(); - auto nonceAccountKey = - PrivateKey(Data(createNonceAccountTransaction.nonce_account_private_key().begin(), - createNonceAccountTransaction.nonce_account_private_key().end())); - message = LegacyMessage::createNonceAccount( - /* owner */ Address(key.getPublicKey(TWPublicKeyTypeED25519)), - /* new nonce_account */ Address(nonceAccountKey.getPublicKey(TWPublicKeyTypeED25519)), - /* rent */ createNonceAccountTransaction.rent(), - /* recent_blockhash */ blockhash, - /* nonce_account */ input.nonce_account()); - signerKeys.push_back(key); - signerKeys.push_back(nonceAccountKey); - } break; - - case Proto::SigningInput::TransactionTypeCase::kWithdrawNonceAccount: { - auto withdrawNonceAccountTransaction = input.withdraw_nonce_account(); - message = LegacyMessage::createWithdrawNonceAccount( - /* owner */ Address(key.getPublicKey(TWPublicKeyTypeED25519)), - /* sender */ Address(withdrawNonceAccountTransaction.nonce_account()), - /* recipient */ Address(withdrawNonceAccountTransaction.recipient()), - /* value */ withdrawNonceAccountTransaction.value(), - /* recent_blockhash */ blockhash, - /* nonce_account */ input.nonce_account()); - signerKeys.push_back(key); - } break; - case Proto::SigningInput::TransactionTypeCase::kAdvanceNonceAccount: { - auto advanceNonceAccountTransaction = input.advance_nonce_account(); - auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); - auto nonceAccountAddress = Address(advanceNonceAccountTransaction.nonce_account()); - message = LegacyMessage::advanceNonceAccount(userAddress, nonceAccountAddress, blockhash); - signerKeys.push_back(key); - } break; - - default: - assert(input.transaction_type_case() != Proto::SigningInput::TransactionTypeCase::TRANSACTION_TYPE_NOT_SET); - } - auto msg = VersionedMessage(message); - if (input.v0_msg()) { - msg = VersionedMessage(V0Message{.msg = message}); - } - auto transaction = VersionedTransaction(msg); - - sign(signerKeys, transaction); - - auto protoOutput = Proto::SigningOutput(); - auto encoded = transaction.serialize(); - protoOutput.set_encoded(encoded); - - auto unsignedTx = Base58::encode(transaction.messageData()); - protoOutput.set_unsigned_tx(unsignedTx.data(), unsignedTx.size()); - - return protoOutput; -} - -void Signer::signUpdateBlockhash(const std::vector& privateKeys, - VersionedTransaction& transaction, Data& recentBlockhash) { - updateRecentHash(transaction.message, recentBlockhash); - Signer::sign(privateKeys, transaction); -} - -// This method does not confirm that PrivateKey order matches that encoded in the messageData -// That order must be correct for the Transaction to succeed on Solana -Data Signer::signRawMessage(const std::vector& privateKeys, const Data messageData) { - std::vector signatures; - for (auto &&privateKey : privateKeys) { - signatures.emplace_back(privateKey.sign(messageData, TWCurveED25519)); - } - Data buffer; - append(buffer, shortVecLength(signatures)); - for (auto &&signature : signatures) { - append(buffer, signature); - } - append(buffer, messageData); - - return buffer; -} - -std::string Signer::signJSON(const std::string& json, const Data& key) { - auto input = Proto::SigningInput(); - google::protobuf::util::JsonStringToMessage(json, &input); - input.set_private_key(key.data(), key.size()); - return Signer::sign(input).encoded(); -} - -TW::Data Signer::preImageHash() const { - TW::Data preImageHash; - auto recentBlockhash = Base58::decode(input.recent_blockhash()); - switch (input.transaction_type_case()) { - case Proto::SigningInput::TransactionTypeCase::kTransferTransaction: { - auto transferTransaction = input.transfer_transaction(); - auto from = input.sender(); - auto message = LegacyMessage::createTransfer( - /* from */ Address(from), - /* to */ Address(transferTransaction.recipient()), - /* value */ transferTransaction.value(), - /* recent_blockhash */ recentBlockhash, - /* memo */ transferTransaction.memo(), - /* references */ convertReferences(transferTransaction.references()), - /* nonce_account */ input.nonce_account()); - auto transaction = Transaction(message); - preImageHash = transaction.messageData(); - } break; - case Proto::SigningInput::TransactionTypeCase::kCreateNonceAccount: { - auto createNonceAccountTransaction = input.create_nonce_account(); - auto from = input.sender(); - auto nonceAccount = createNonceAccountTransaction.nonce_account(); - auto message = LegacyMessage::createNonceAccount( - /* owner */ Address(from), - /* new nonce_account */ Address(createNonceAccountTransaction.nonce_account()), - /* rent */ createNonceAccountTransaction.rent(), - /* recent_blockhash */ recentBlockhash, - /* nonce_account */ input.nonce_account()); - auto transaction = Transaction(message); - preImageHash = transaction.messageData(); - } break; - case Proto::SigningInput::TransactionTypeCase::kWithdrawNonceAccount: { - auto withdrawNonceAccountTransaction = input.withdraw_nonce_account(); - auto owner = input.sender(); - auto message = LegacyMessage::createWithdrawNonceAccount( - /* owner */ Address(owner), - /* sender */ Address(withdrawNonceAccountTransaction.nonce_account()), - /* recipient */ Address(withdrawNonceAccountTransaction.recipient()), - /* value */ withdrawNonceAccountTransaction.value(), - /* recent_blockhash */ recentBlockhash, - /* nonce_account */ input.nonce_account()); - auto transaction = Transaction(message); - preImageHash = transaction.messageData(); - } break; - case Proto::SigningInput::TransactionTypeCase::kCreateTokenAccountTransaction: { - auto createTokenAccontTransaction = input.create_token_account_transaction(); - auto userAddress = Address(input.sender()); - auto mainAddress = Address(createTokenAccontTransaction.main_address()); - auto tokenMintAddress = Address(createTokenAccontTransaction.token_mint_address()); - auto tokenAddress = Address(createTokenAccontTransaction.token_address()); - auto message = - LegacyMessage::createTokenCreateAccount(userAddress, mainAddress, tokenMintAddress, - tokenAddress, recentBlockhash, input.nonce_account()); - auto transaction = Transaction(message); - preImageHash = transaction.messageData(); - } break; - case Proto::SigningInput::TransactionTypeCase::kTokenTransferTransaction: { - auto tokenTransferTransaction = input.token_transfer_transaction(); - auto userAddress = Address(input.sender()); - auto tokenMintAddress = Address(tokenTransferTransaction.token_mint_address()); - auto senderTokenAddress = Address(tokenTransferTransaction.sender_token_address()); - auto recipientTokenAddress = Address(tokenTransferTransaction.recipient_token_address()); - auto amount = tokenTransferTransaction.amount(); - auto decimals = static_cast(tokenTransferTransaction.decimals()); - const auto memo = tokenTransferTransaction.memo(); - auto message = LegacyMessage::createTokenTransfer( - userAddress, tokenMintAddress, senderTokenAddress, recipientTokenAddress, amount, - decimals, recentBlockhash, memo, - convertReferences(tokenTransferTransaction.references()), input.nonce_account(), input.fee_payer()); - auto transaction = Transaction(message); - preImageHash = transaction.messageData(); - } break; - case Proto::SigningInput::TransactionTypeCase::kCreateAndTransferTokenTransaction: { - auto createAndTransferTokenTransaction = input.create_and_transfer_token_transaction(); - auto userAddress = Address(input.sender()); - auto recipientMainAddress = - Address(createAndTransferTokenTransaction.recipient_main_address()); - auto tokenMintAddress = Address(createAndTransferTokenTransaction.token_mint_address()); - auto recipientTokenAddress = - Address(createAndTransferTokenTransaction.recipient_token_address()); - auto senderTokenAddress = Address(createAndTransferTokenTransaction.sender_token_address()); - auto amount = createAndTransferTokenTransaction.amount(); - auto decimals = static_cast(createAndTransferTokenTransaction.decimals()); - const auto memo = createAndTransferTokenTransaction.memo(); - auto message = LegacyMessage::createTokenCreateAndTransfer( - userAddress, recipientMainAddress, tokenMintAddress, recipientTokenAddress, - senderTokenAddress, amount, decimals, recentBlockhash, memo, - convertReferences(createAndTransferTokenTransaction.references()), - input.nonce_account(), input.fee_payer()); - auto transaction = Transaction(message); - preImageHash = transaction.messageData(); - } break; - case Proto::SigningInput::TransactionTypeCase::kAdvanceNonceAccount: { - auto advanceNonceAccountTransaction = input.advance_nonce_account(); - auto userAddress = Address(input.sender()); - auto nonceAccountAddress = Address(advanceNonceAccountTransaction.nonce_account()); - auto message = - LegacyMessage::advanceNonceAccount(userAddress, nonceAccountAddress, recentBlockhash); - auto transaction = Transaction(message); - preImageHash = transaction.messageData(); - } break; - default: - if (input.transaction_type_case() == - Proto::SigningInput::TransactionTypeCase::TRANSACTION_TYPE_NOT_SET) { - throw std::invalid_argument("transaction type not set"); - } - } - return preImageHash; -}; - -std::vector Signer::signers() const { - std::vector signers; - if (Address::isValid(input.fee_payer())) { - signers.push_back(input.fee_payer()); - } - switch (input.transaction_type_case()) { - case Proto::SigningInput::TransactionTypeCase::kTransferTransaction: - case Proto::SigningInput::TransactionTypeCase::kWithdrawNonceAccount: - case Proto::SigningInput::TransactionTypeCase::kCreateTokenAccountTransaction: - case Proto::SigningInput::TransactionTypeCase::kTokenTransferTransaction: - case Proto::SigningInput::TransactionTypeCase::kCreateAndTransferTokenTransaction: - case Proto::SigningInput::TransactionTypeCase::kAdvanceNonceAccount: { - auto sender = input.sender(); - signers.push_back(sender); - } break; - case Proto::SigningInput::TransactionTypeCase::kCreateNonceAccount: { - auto from = input.sender(); - auto createNonceAccountTransaction = input.create_nonce_account(); - auto nonceAccount = createNonceAccountTransaction.nonce_account(); - signers.push_back(from); - signers.push_back(nonceAccount); - } break; - default: - if (input.transaction_type_case() == - Proto::SigningInput::TransactionTypeCase::TRANSACTION_TYPE_NOT_SET) { - throw std::invalid_argument("transaction type not set"); - } - } - return signers; -}; - -Proto::SigningOutput Signer::compile(const std::vector& signatures, - const std::vector& publicKeys) const { - auto output = Proto::SigningOutput(); - LegacyMessage message; - auto recentBlockhash = Base58::decode(input.recent_blockhash()); - switch (input.transaction_type_case()) { - case Proto::SigningInput::TransactionTypeCase::kTransferTransaction: { - if (signatures.size() < 1) { - throw std::invalid_argument("too few signatures"); - } - auto transferTransaction = input.transfer_transaction(); - message = LegacyMessage::createTransfer( - /* from */ Address(input.sender()), - /* to */ Address(transferTransaction.recipient()), - /* value */ transferTransaction.value(), - /* recent_blockhash */ recentBlockhash, - /* memo */ transferTransaction.memo(), - /* references */ convertReferences(transferTransaction.references()), - /* nonce_account */ input.nonce_account()); - } break; - case Proto::SigningInput::TransactionTypeCase::kCreateNonceAccount: { - if (signatures.size() < 2) { - throw std::invalid_argument("too few signatures"); - } - auto createNonceAccountTransaction = input.create_nonce_account(); - auto from = input.sender(); - auto nonceAccount = createNonceAccountTransaction.nonce_account(); - message = LegacyMessage::createNonceAccount( - /* owner */ Address(from), - /* nonce_account */ Address(createNonceAccountTransaction.nonce_account()), - /* rent */ createNonceAccountTransaction.rent(), - /* recent_blockhash */ recentBlockhash, - /* nonce_account */ input.nonce_account()); - } break; - case Proto::SigningInput::TransactionTypeCase::kWithdrawNonceAccount: { - if (signatures.size() < 1) { - throw std::invalid_argument("too few signatures"); - } - auto withdrawNonceAccountTransaction = input.withdraw_nonce_account(); - message = LegacyMessage::createWithdrawNonceAccount( - /* owner */ Address(input.sender()), - /* sender */ Address(withdrawNonceAccountTransaction.nonce_account()), - /* recipient */ Address(withdrawNonceAccountTransaction.recipient()), - /* value */ withdrawNonceAccountTransaction.value(), - /* recent_blockhash */ recentBlockhash, - /* nonce_account */ input.nonce_account()); - } break; - case Proto::SigningInput::TransactionTypeCase::kCreateTokenAccountTransaction: { - if (signatures.size() < 1) { - throw std::invalid_argument("too few signatures"); - } - auto createTokenAccontTransaction = input.create_token_account_transaction(); - auto userAddress = Address(input.sender()); - auto mainAddress = Address(createTokenAccontTransaction.main_address()); - auto tokenMintAddress = Address(createTokenAccontTransaction.token_mint_address()); - auto tokenAddress = Address(createTokenAccontTransaction.token_address()); - message = - LegacyMessage::createTokenCreateAccount(userAddress, mainAddress, tokenMintAddress, - tokenAddress, recentBlockhash, input.nonce_account()); - } break; - case Proto::SigningInput::TransactionTypeCase::kTokenTransferTransaction: { - if (signatures.size() < 1) { - throw std::invalid_argument("too few signatures"); - } - auto tokenTransferTransaction = input.token_transfer_transaction(); - auto userAddress = Address(input.sender()); - auto tokenMintAddress = Address(tokenTransferTransaction.token_mint_address()); - auto senderTokenAddress = Address(tokenTransferTransaction.sender_token_address()); - auto recipientTokenAddress = Address(tokenTransferTransaction.recipient_token_address()); - auto amount = tokenTransferTransaction.amount(); - auto decimals = static_cast(tokenTransferTransaction.decimals()); - const auto memo = tokenTransferTransaction.memo(); - message = LegacyMessage::createTokenTransfer( - userAddress, tokenMintAddress, senderTokenAddress, recipientTokenAddress, amount, - decimals, recentBlockhash, memo, - convertReferences(tokenTransferTransaction.references()), input.nonce_account(), input.fee_payer()); - } break; - case Proto::SigningInput::TransactionTypeCase::kCreateAndTransferTokenTransaction: { - auto createAndTransferTokenTransaction = input.create_and_transfer_token_transaction(); - auto userAddress = Address(input.sender()); - auto recipientMainAddress = - Address(createAndTransferTokenTransaction.recipient_main_address()); - auto tokenMintAddress = Address(createAndTransferTokenTransaction.token_mint_address()); - auto recipientTokenAddress = - Address(createAndTransferTokenTransaction.recipient_token_address()); - auto senderTokenAddress = Address(createAndTransferTokenTransaction.sender_token_address()); - auto amount = createAndTransferTokenTransaction.amount(); - auto decimals = static_cast(createAndTransferTokenTransaction.decimals()); - const auto memo = createAndTransferTokenTransaction.memo(); - message = LegacyMessage::createTokenCreateAndTransfer( - userAddress, recipientMainAddress, tokenMintAddress, recipientTokenAddress, - senderTokenAddress, amount, decimals, recentBlockhash, memo, - convertReferences(createAndTransferTokenTransaction.references()), - input.nonce_account(), input.fee_payer()); - } break; - case Proto::SigningInput::TransactionTypeCase::kAdvanceNonceAccount: { - if (signatures.size() < 1) { - throw std::invalid_argument("too few signatures"); - } - auto advanceNonceAccountTransaction = input.advance_nonce_account(); - auto userAddress = Address(input.sender()); - auto nonceAccountAddress = Address(advanceNonceAccountTransaction.nonce_account()); - message = LegacyMessage::advanceNonceAccount(userAddress, nonceAccountAddress, recentBlockhash); - } break; - default: - if (input.transaction_type_case() == - Proto::SigningInput::TransactionTypeCase::TRANSACTION_TYPE_NOT_SET) { - throw std::invalid_argument("transaction type not set"); - } - } - auto transaction = Transaction(message); - auto preImageHash = transaction.messageData(); - if (publicKeys.size() != signatures.size()) { - throw std::invalid_argument( - "the number of public keys and the number of signatures not aligned"); - } - for (auto i = 0ul; i < signatures.size(); i++) { - if (!publicKeys[i].verify(signatures[i], preImageHash)) { - throw std::invalid_argument("invalid signature at " + std::to_string(i)); - } - auto addressIdx = transaction.getAccountIndex(Address(publicKeys[i])); - transaction.signatures[addressIdx] = signatures[i]; - } - // construst the output - auto encoded = transaction.serialize(); - output.set_encoded(encoded); - return output; -}; - -} // namespace TW::Solana diff --git a/src/Solana/Signer.h b/src/Solana/Signer.h deleted file mode 100644 index 77cb86183fb..00000000000 --- a/src/Solana/Signer.h +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "VersionedTransaction.h" -#include "Data.h" -#include "../Hash.h" -#include "../PrivateKey.h" -#include "../proto/Solana.pb.h" - -namespace TW::Solana { - -/// Helper class that performs Solana transaction signing. -class Signer { - public: - Proto::SigningInput input; - - /// Initializes a transaction signer. - explicit Signer(const Proto::SigningInput& input) : input(input) {} - - /// Signs the given transaction. - static void sign(const std::vector& privateKeys, VersionedTransaction& transaction); - - /// Signs a json Proto::SigningInput with private key - static std::string signJSON(const std::string& json, const Data& key); - - static void signUpdateBlockhash(const std::vector& privateKeys, - VersionedTransaction& transaction, Data& recentBlockhash); - static Data signRawMessage(const std::vector& privateKeys, const Data messageData); - - static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept; - - TW::Data preImageHash() const; - Proto::SigningOutput compile(const std::vector& signatures, - const std::vector& publicKeys) const; - std::vector signers() const; -}; -} // namespace TW::Solana diff --git a/src/Solana/Transaction.cpp b/src/Solana/Transaction.cpp deleted file mode 100644 index 6eb79f5dfce..00000000000 --- a/src/Solana/Transaction.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "Transaction.h" -#include "Solana/Encoding.h" - -#include "Hash.h" -#include "Signer.h" - -#include - -namespace TW::Solana { - -std::string Transaction::serialize() const { - Data buffer; - - append(buffer, shortVecLength(this->signatures)); - for (auto &&signature : this->signatures) { - append(buffer, signature); - } - append(buffer, this->messageData()); - - return Base58::encode(buffer); -} - -Data Transaction::messageData() const { - return this->message.serialize(); -} - -uint8_t Transaction::getAccountIndex(Address publicKey) { - auto item = - std::find(this->message.accountKeys.begin(), this->message.accountKeys.end(), publicKey); - if (item == this->message.accountKeys.end()) { - throw std::invalid_argument("publicKey not found in message.accountKeys"); - } - return (uint8_t)std::distance(this->message.accountKeys.begin(), item); -} - -} // namespace TW::Solana diff --git a/src/Solana/Transaction.h b/src/Solana/Transaction.h deleted file mode 100644 index 282397bcf46..00000000000 --- a/src/Solana/Transaction.h +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "Solana/Address.h" -#include "Solana/LegacyMessage.h" -#include "Data.h" -#include "BinaryCoding.h" - -#include -#include - -namespace TW::Solana { - -class Transaction { - public: - // Signatures - std::vector signatures; - // The message to sign - LegacyMessage message; - - Transaction(const LegacyMessage& message) : message(message) { - this->signatures.resize(message.header.numRequiredSignatures, defaultSignature); - } - - // Default basic transfer transaction - Transaction(const Address& from, const Address& to, uint64_t value, Data recentBlockhash, std::string memo = "", std::vector
references = {}) - : message(LegacyMessage::createTransfer(from, to, value, recentBlockhash, memo, references)) { - this->signatures.resize(1, defaultSignature); - } - - public: - std::string serialize() const; - std::vector messageData() const; - uint8_t getAccountIndex(Address publicKey); - - private: - TW::Data defaultSignature = TW::Data(64); -}; - -} // namespace TW::Solana diff --git a/src/Solana/V0Message.h b/src/Solana/V0Message.h deleted file mode 100644 index 9c76eb74210..00000000000 --- a/src/Solana/V0Message.h +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "Solana/LegacyMessage.h" -#include "Solana/AddressLookupTable.h" - -namespace TW::Solana { - -struct V0Message { - LegacyMessage msg; - MessageAddressTableLookup addressTableLookups; -}; - -} diff --git a/src/Solana/VersionedMessage.cpp b/src/Solana/VersionedMessage.cpp deleted file mode 100644 index 2267a0a777f..00000000000 --- a/src/Solana/VersionedMessage.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "Solana/VersionedMessage.h" -#include "Solana/Constants.h" -#include "Solana/Encoding.h" - -namespace TW::Solana { - -Data serialize(const VersionedMessage& message) { - auto visit_functor = [](const VersionedMessage& message) -> Data { - if (auto* msg = std::get_if(&message); msg) { - Data out; - append(out, MESSAGE_VERSION_PREFIX); - append(out, msg->msg.serialize()); - append(out, shortVecLength(msg->addressTableLookups)); - return out; - } else if (auto* legacyMsg = std::get_if(&message); legacyMsg) { - return legacyMsg->serialize(); - } else { - return {}; - } - }; - - return std::visit(visit_functor, message); -} - -MessageHeader header(const VersionedMessage& message) { - auto visit_functor = [](const VersionedMessage& message) -> MessageHeader { - if (auto* msg = std::get_if(&message); msg) { - return msg->msg.header; - } else if (auto* legacyMsg = std::get_if(&message); legacyMsg) { - return legacyMsg->header; - } else { - return {}; - } - }; - - return std::visit(visit_functor, message); -} - -std::vector
accountKeys(const VersionedMessage& message) { - auto visit_functor = [](const VersionedMessage& message) -> std::vector
{ - if (auto* msg = std::get_if(&message); msg) { - return msg->msg.accountKeys; - } else if (auto* legacyMsg = std::get_if(&message); legacyMsg) { - return legacyMsg->accountKeys; - } else { - return {}; - } - }; - - return std::visit(visit_functor, message); -} - -void updateRecentHash(VersionedMessage& message, const Data& recentHash) { - if (auto* msg = std::get_if(&message); msg) { - msg->msg.mRecentBlockHash = recentHash; - } else if (auto* legacyMsg = std::get_if(&message); legacyMsg) { - legacyMsg->mRecentBlockHash = recentHash; - } -} - -} // namespace TW::Solana diff --git a/src/Solana/VersionedMessage.h b/src/Solana/VersionedMessage.h deleted file mode 100644 index 08c21d37168..00000000000 --- a/src/Solana/VersionedMessage.h +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include -#include "Solana/V0Message.h" -#include "Solana/LegacyMessage.h" -#include "Solana/MessageHeader.h" - -namespace TW::Solana { - using VersionedMessage = std::variant; - - Data serialize(const VersionedMessage& message); - MessageHeader header(const VersionedMessage& message); - std::vector
accountKeys(const VersionedMessage& message); - void updateRecentHash(VersionedMessage& message, const Data& recentHash); -} diff --git a/src/Solana/VersionedTransaction.cpp b/src/Solana/VersionedTransaction.cpp deleted file mode 100644 index c5679b1ddf3..00000000000 --- a/src/Solana/VersionedTransaction.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "Solana/VersionedTransaction.h" -#include "Solana/Encoding.h" - -namespace TW::Solana { - -std::string VersionedTransaction::serialize() const { - Data buffer; - - append(buffer, shortVecLength(this->signatures)); - for (auto &&signature : this->signatures) { - append(buffer, signature); - } - append(buffer, this->messageData()); - - return Base58::encode(buffer); -} - -Data VersionedTransaction::messageData() const { - return Solana::serialize(this->message); -} - -uint8_t VersionedTransaction::getAccountIndex(Address publicKey) { - const auto accountKeys = Solana::accountKeys(this->message); - auto item = std::find(accountKeys.begin(), accountKeys.end(), publicKey); - if (item == accountKeys.end()) { - throw std::invalid_argument("publicKey not found in message.accountKeys"); - } - return (uint8_t)std::distance(accountKeys.begin(), item); -} -} // namespace TW::Solana diff --git a/src/Solana/VersionedTransaction.h b/src/Solana/VersionedTransaction.h deleted file mode 100644 index f7fdd263caf..00000000000 --- a/src/Solana/VersionedTransaction.h +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "Solana/Address.h" -#include "Solana/VersionedMessage.h" -#include "Data.h" -#include "BinaryCoding.h" - -#include -#include - -namespace TW::Solana { - -class VersionedTransaction { - public: - // Signatures - std::vector signatures; - // The message to sign - VersionedMessage message; - - VersionedTransaction(const VersionedMessage& message) : message(message) { - this->signatures.resize(header(message).numRequiredSignatures, defaultSignature); - } - - // Default basic transfer transaction - VersionedTransaction(const Address& from, const Address& to, uint64_t value, Data recentBlockhash, std::string memo = "", std::vector
references = {}) - : message(VersionedMessage(LegacyMessage::createTransfer(from, to, value, recentBlockhash, memo, references))) { - this->signatures.resize(1, defaultSignature); - } - - public: - std::string serialize() const; - std::vector messageData() const; - uint8_t getAccountIndex(Address publicKey); - - private: - TW::Data defaultSignature = TW::Data(64); -}; - -} // namespace TW::Solana diff --git a/src/interface/TWSolanaAddress.cpp b/src/interface/TWSolanaAddress.cpp index 02b3ad631f1..5599061bf65 100644 --- a/src/interface/TWSolanaAddress.cpp +++ b/src/interface/TWSolanaAddress.cpp @@ -21,9 +21,15 @@ TWString* _Nullable TWSolanaAddressDefaultTokenAddress(struct TWSolanaAddress* _ if (address == nullptr || tokenMintAddress == nullptr) { return nullptr; } - Solana::Address tokenMint = Solana::Address(TWStringUTF8Bytes(tokenMintAddress)); - std::string defaultAddress = address->impl.defaultTokenAddress(tokenMint).string(); - return TWStringCreateWithUTF8Bytes(defaultAddress.c_str()); + Rust::TWStringWrapper tokenMint = TWStringUTF8Bytes(tokenMintAddress); + Rust::TWStringWrapper mainAddress = address->impl.string(); + + Rust::TWStringWrapper newTokenAddress = Rust::tw_solana_address_default_token_address(mainAddress.get(), tokenMint.get()); + + if (!newTokenAddress) { + return nullptr; + } + return TWStringCreateWithUTF8Bytes(newTokenAddress.c_str()); } catch (...) { return nullptr; } diff --git a/src/interface/TWTransactionDecoder.cpp b/src/interface/TWTransactionDecoder.cpp new file mode 100644 index 00000000000..b8083494e42 --- /dev/null +++ b/src/interface/TWTransactionDecoder.cpp @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#include "TrustWalletCore/TWTransactionDecoder.h" +#include "rust/Wrapper.h" + +using namespace TW; + +TWData *_Nonnull TWTransactionDecoderDecode(enum TWCoinType coinType, TWData *_Nonnull encodedTx) { + const Data& txData = *(reinterpret_cast(encodedTx)); + + const Rust::TWDataWrapper txDataPtr(txData); + const Rust::TWDataWrapper outputDataPtr = Rust::tw_transaction_decoder_decode(static_cast(coinType), txDataPtr.get()); + + const auto outputData = outputDataPtr.toDataOrDefault(); + return TWDataCreateWithBytes(outputData.data(), outputData.size()); +} diff --git a/src/proto/Solana.proto b/src/proto/Solana.proto index 45d4e858f7f..24a411c8002 100644 --- a/src/proto/Solana.proto +++ b/src/proto/Solana.proto @@ -99,7 +99,7 @@ message TokenTransfer { uint32 decimals = 5; // optional memo§ - string memo = 6; + string memo = 6; // optional referenced public keys repeated string references = 7; @@ -150,6 +150,84 @@ message AdvanceNonceAccount { string nonce_account = 1; } +message PubkeySignature { + string pubkey = 1; + // base58 encoded signature. + string signature = 2; +} + +message RawMessage { + message MessageHeader { + uint32 num_required_signatures = 1; + uint32 num_readonly_signed_accounts = 2; + uint32 num_readonly_unsigned_accounts = 3; + } + + message Instruction { + uint32 program_id = 1; + repeated uint32 accounts = 2 [packed = true]; + bytes program_data = 3; + } + + message MessageAddressTableLookup { + string account_key = 1; + repeated uint32 writable_indexes = 2 [packed = true]; + repeated uint32 readonly_indexes = 3 [packed = true]; + } + + message MessageLegacy { + MessageHeader header = 1; + repeated string account_keys = 2; + // Relatively recent block hash (base58 encoded). + string recent_blockhash = 3; + repeated Instruction instructions = 4; + } + + message MessageV0 { + MessageHeader header = 1; + repeated string account_keys = 2; + // Relatively recent block hash (base58 encoded). + string recent_blockhash = 3; + repeated Instruction instructions = 4; + repeated MessageAddressTableLookup address_table_lookups = 5; + } + + // Transaction signatures. + // If private keys are set in `SigningInput`, corresponding signatures will be overriden. + // It's also possible some or all the signatures are be used to compile a transaction if corresponding private keys are not set. + repeated PubkeySignature signatures = 1; + oneof message { + MessageLegacy legacy = 2; + MessageV0 v0 = 3; + } +} + +message DecodingTransactionOutput { + // Decoded transaction info. + RawMessage transaction = 1; + + // Error code, 0 is ok, other codes will be treated as errors + Common.Proto.SigningError error = 2; + + // Error code description + string error_message = 3; +} + +enum Encoding { + Base58 = 0; + Base64 = 1; +} + +// Specific compute unit limit that the transaction is allowed to consume. +message PriorityFeePrice { + uint64 price = 1; +} + +// Compute unit price in "micro-lamports" to pay a higher transaction fee for higher transaction prioritization. +message PriorityFeeLimit { + uint32 limit = 2; +} + // Input data necessary to create a signed transaction. message SigningInput { // The secret private key used for signing (32 bytes). @@ -183,35 +261,45 @@ message SigningInput { bytes fee_payer_private_key = 17; // Optional external fee payer. support: TokenTransfer, CreateAndTransferToken string fee_payer = 18; + // Optional message plan. For signing an already prepared message. + RawMessage raw_message = 20; + // Output transaction encoding. + Encoding tx_encoding = 21; + // Optional. Set a specific compute unit limit that the transaction is allowed to consume. + // https://solana.com/docs/intro/transaction_fees#prioritization-fee + PriorityFeePrice priority_fee_price = 22; + // Optional. Set a compute unit price in "micro-lamports" to pay a higher transaction + // fee for higher transaction prioritization. + // https://solana.com/docs/intro/transaction_fees#prioritization-fee + PriorityFeeLimit priority_fee_limit = 23; } // Result containing the signed and encoded transaction. message SigningOutput { // The encoded transaction string encoded = 1; - + // error code, 0 is ok, other codes will be treated as errors Common.Proto.SigningError error = 2; // error code description string error_message = 3; - // The unsigned transaction + // The encoded message. Can be used to estimate a transaction fee required to execute the message. string unsigned_tx = 4; - // The encoded message. Can be used to estimate a transaction fee required to execute the message. - // Please note that this is set only on `SolanaTransaction.updateBlockhashAndSign`, but no on `AnySigner.sign`. - string message_encoded = 5; + // Transaction signatures (may include external signatures). + repeated PubkeySignature signatures = 5; } /// Transaction pre-signing output message PreSigningOutput { - /// Signer list + // Signer list repeated bytes signers = 1; - /// Pre-image data. There is no hashing for Solana presign image + // Pre-image data. There is no hashing for Solana presign image bytes data = 2; - + // Error code, 0 is ok, other codes will be treated as errors Common.Proto.SigningError error = 3; diff --git a/src/proto/WalletConnect.proto b/src/proto/WalletConnect.proto index 37b8a9796c0..7d9139b70d5 100644 --- a/src/proto/WalletConnect.proto +++ b/src/proto/WalletConnect.proto @@ -5,6 +5,7 @@ option java_package = "wallet.core.jni.proto"; import "Binance.proto"; import "Common.proto"; +import "Solana.proto"; // The transaction protocol may differ from version to version. enum Protocol { @@ -16,6 +17,8 @@ enum Method { Unknown = 0; // cosmos_signAmino CosmosSignAmino = 1; + // solana_signTransaction + SolanaSignTransaction = 2; } message ParseRequestInput { @@ -40,5 +43,6 @@ message ParseRequestOutput { // Prepared unsigned transaction input, on the source chain. Some fields must be completed, and it has to be signed. oneof signing_input_oneof { Binance.Proto.SigningInput binance = 3; + Solana.Proto.SigningInput solana = 4; } } diff --git a/src/rust/Wrapper.h b/src/rust/Wrapper.h index dda425dc319..6c730e99782 100644 --- a/src/rust/Wrapper.h +++ b/src/rust/Wrapper.h @@ -82,6 +82,12 @@ struct TWStringWrapper { TWStringWrapper(TWString *ptr): ptr(std::shared_ptr(ptr, tw_string_delete)) { } + /// Implicit constructor. + TWStringWrapper(const char* string) { + auto* stringRaw = tw_string_create_with_utf8_bytes(string); + ptr = std::shared_ptr(stringRaw, tw_string_delete); + } + ~TWStringWrapper() = default; TWString* get() const { @@ -97,6 +103,14 @@ struct TWStringWrapper { return {bytes}; } + const char* c_str() const { + return ptr ? tw_string_utf8_bytes(ptr.get()) : nullptr; + } + + explicit operator bool() const { + return static_cast(ptr); + } + std::shared_ptr ptr; }; diff --git a/swift/Tests/Blockchains/SolanaTests.swift b/swift/Tests/Blockchains/SolanaTests.swift index 82df57bac86..e813fe6c245 100644 --- a/swift/Tests/Blockchains/SolanaTests.swift +++ b/swift/Tests/Blockchains/SolanaTests.swift @@ -226,4 +226,68 @@ class SolanaTests: XCTestCase { XCTAssertEqual(result, "3p2kzZ1DvquqC6LApPuxpTg5CCDVPqJFokGSnGhnBHrta4uq7S2EyehV1XNUVXp51D69GxGzQZUjikfDzbWBG2aFtG3gHT1QfLzyFKHM4HQtMQMNXqay1NAeiiYZjNhx9UvMX4uAQZ4Q6rx6m2AYfQ7aoMUrejq298q1wBFdtS9XVB5QTiStnzC7zs97FUEK2T4XapjF1519EyFBViTfHpGpnf5bfizDzsW9kYUtRDW1UC2LgHr7npgq5W9TBmHf9hSmRgM9XXucjXLqubNWE7HUMhbKjuBqkirRM") } + + func testDecodeUpdateBlockhashAndSign() throws { + // https://explorer.solana.com/tx/3KbvREZUat76wgWMtnJfWbJL74Vzh4U2eabVJa3Z3bb2fPtW8AREP5pbmRwUrxZCESbTomWpL41PeKDcPGbojsej?cluster=devnet + let encodedTx = "AnQTYwZpkm3fs4SdLxnV6gQj3hSLsyacpxDdLMALYWObm722f79IfYFTbZeFK9xHtMumiDOWAM2hHQP4r/GtbARpncaXgOVFv7OgbRLMbuCEJHO1qwcdCbtH72VzyzU8yw9sqqHIAaCUE8xaQTgT6Z5IyZfeyMe2QGJIfOjz65UPAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8Aqe6sdLXiXSDILEtzckCjkjchiSf6zVGpMYiAE5BE2IqHAQUEAgQDAQoMoA8AAAAAAAAG" + let newBlockhash = "CyPYVsYWrsJNfVpi8aazu7WsrswNFuDd385z6GNoBGUg" + let encodedTxData = Base64.decode(string: encodedTx)! + + let senderPrivateKey = Data(hexString: "7f0932159226ddec9e1a4b0b8fe7cdc135049f9e549a867d722aa720dd64f32e")! + let feePayerPrivateKey = Data(hexString: "4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7")! + + // Step 1: Decode the transaction. + + let decodeOutputData = TransactionDecoder.decode(coinType: .solana, encodedTx: encodedTxData) + var decodeOutput = try SolanaDecodingTransactionOutput(serializedData: decodeOutputData) + + XCTAssertEqual(decodeOutput.error, .ok) + + // Step 2: Update recent blockhash. + + decodeOutput.transaction.legacy.recentBlockhash = newBlockhash + + // Step 3: Re-sign the updated transaction. + + let signingInput = SolanaSigningInput.with { + $0.privateKey = senderPrivateKey + $0.feePayerPrivateKey = feePayerPrivateKey + $0.rawMessage = decodeOutput.transaction + $0.txEncoding = .base64 + } + + let output: SolanaSigningOutput = AnySigner.sign(input: signingInput, coin: .solana) + XCTAssertEqual(output.error, .ok) + XCTAssertEqual(output.encoded, "Ajzc/Tke0CG8Cew5qFa6xZI/7Ya3DN0M8Ige6tKPsGzhg8Bw9DqL18KUrEZZ1F4YqZBo4Rv+FsDT8A7Nss7p4A6BNVZzzGprCJqYQeNg0EVIbmPc6mDitNniHXGeKgPZ6QZbM4FElw9O7IOFTpOBPvQFeqy0vZf/aayncL8EK/UEAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqbHiki6ThNH3auuyZPQpJntnN0mA//56nMpK/6HIuu8xAQUEAgQDAQoMoA8AAAAAAAAG") + } + + func testSignFromWalletConnectRequest() throws { + // Step 1: Parse a signing request received through WalletConnect. + + let requestPayload = """ + {"transaction":"AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEDZsL1CMnFVcrMn7JtiOiN1U4hC7WovOVof2DX51xM0H/GizyJTHgrBanCf8bGbrFNTn0x3pCGq30hKbywSTr6AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgIAAQwCAAAAKgAAAAAAAAA="} + """ + let parsingInput = WalletConnectParseRequestInput.with { + $0.method = .solanaSignTransaction + $0.payload = requestPayload + } + let parsingInputBytes = try parsingInput.serializedData() + + let parsingOutputBytes = WalletConnectRequest.parse(coin: .solana, input: parsingInputBytes) + let parsingOutput = try WalletConnectParseRequestOutput(serializedData: parsingOutputBytes) + + var signingInput = parsingOutput.solana + + // Step 2: Set missing fields. + + signingInput.privateKey = Base58.decodeNoCheck(string: "A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr")! + signingInput.txEncoding = .base64 + + // Step 3: Sign the transaction. + + let output: SolanaSigningOutput = AnySigner.sign(input: signingInput, coin: .solana) + + let expected = "AQPWaOi7dMdmQpXi8HyQQKwiqIftrg1igGQxGtZeT50ksn4wAnyH4DtDrkkuE0fqgx80LTp4LwNN9a440SrmoA8BAAEDZsL1CMnFVcrMn7JtiOiN1U4hC7WovOVof2DX51xM0H/GizyJTHgrBanCf8bGbrFNTn0x3pCGq30hKbywSTr6AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgIAAQwCAAAAKgAAAAAAAAA=" + XCTAssertEqual(output.encoded, expected) + } } diff --git a/tests/chains/Solana/AddressTests.cpp b/tests/chains/Solana/AddressTests.cpp index 4ae3da89d43..6e040826979 100644 --- a/tests/chains/Solana/AddressTests.cpp +++ b/tests/chains/Solana/AddressTests.cpp @@ -6,7 +6,6 @@ #include "HexCoding.h" #include "PrivateKey.h" #include "Solana/Address.h" -#include "Solana/Program.h" #include @@ -66,14 +65,4 @@ TEST(SolanaAddress, isValidOnCurve) { EXPECT_FALSE(PublicKey(Base58::decode("AbygL37RheNZv327cMvZPqKYLLkZ6wqWYexRxgNiZyeP"), TWPublicKeyTypeED25519).isValidED25519()); } -TEST(SolanaAddress, defaultTokenAddress) { - const Address serumToken = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - EXPECT_EQ(Address("HBYC51YrGFAZ8rM7Sj8e9uqKggpSrDYrinQDZzvMtqQp").defaultTokenAddress(serumToken).string(), - "6X4X1Ae24mkoWeCEpktevySVG9jzeCufut5vtUW3wFrD"); - EXPECT_EQ(Address("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V").defaultTokenAddress(serumToken).string(), - "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - EXPECT_EQ(Address("Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ").defaultTokenAddress(serumToken).string(), - "ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf"); -} - } // namespace TW::Solana::tests diff --git a/tests/chains/Solana/ProgramTests.cpp b/tests/chains/Solana/ProgramTests.cpp deleted file mode 100644 index c3b8f7ab317..00000000000 --- a/tests/chains/Solana/ProgramTests.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "Base58.h" -#include "PrivateKey.h" -#include "Solana/Address.h" -#include "Solana/Program.h" - -#include - -using namespace std; -using namespace TW; - -namespace TW::Solana::tests { - -TEST(SolanaStakeProgram, addressFromValidatorSeed) { - auto user = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - auto validator = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); - auto programId = Address("Stake11111111111111111111111111111111111111"); - auto expected = Address("6u9vJH9pRj66N5oJFCBADEbpMTrLxQATcL6q5p5MXwYv"); - EXPECT_EQ(StakeProgram::addressFromValidatorSeed(user, validator, programId), expected); -} - -TEST(SolanaStakeProgram, addressFromRecentBlockhash) { - { - auto user = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - Data recentBlockhash = Base58::decode("11111111111111111111111111111111"); - auto programId = Address("Stake11111111111111111111111111111111111111"); - auto expected = Address("GQDDc5EVGJZFC7AvpEJ8eoCQ75Yy4gr7eu17frCjvQRQ"); - EXPECT_EQ(StakeProgram::addressFromRecentBlockhash(user, recentBlockhash, programId), expected); - } - { - auto user = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - Data recentBlockhash = Base58::decode("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); - auto programId = Address("Stake11111111111111111111111111111111111111"); - auto expected = Address("2Kos1xJRBq3Ae1GnVNBx7HgJhq8KvdUe2bXE4QGdNaXb"); - EXPECT_EQ(StakeProgram::addressFromRecentBlockhash(user, recentBlockhash, programId), expected); - } -} - -TEST(SolanaTokenProgram, defaultTokenAddress) { - const Address serumToken = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - EXPECT_EQ(TokenProgram::defaultTokenAddress(Address("HBYC51YrGFAZ8rM7Sj8e9uqKggpSrDYrinQDZzvMtqQp"), serumToken).string(), - "6X4X1Ae24mkoWeCEpktevySVG9jzeCufut5vtUW3wFrD"); -} - -TEST(SolanaTokenProgram, findProgramAddress) { - std::vector seeds = { - Base58::decode("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"), - Base58::decode("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), - Base58::decode("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"), - }; - { - Address address = TokenProgram::findProgramAddress(seeds, Address("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")); - EXPECT_EQ(address.string(), "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - } - { - Address address = TokenProgram::findProgramAddress(seeds, Address("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr")); - EXPECT_EQ(address.string(), "CuS1kE1wvGTmwGk3FYNQK85g4jU7gMySWwFRQQ9LFunp"); - } -} - -TEST(SolanaTokenProgram, createProgramAddress) { - std::vector seeds4 = { - Base58::decode("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"), - Base58::decode("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), - Base58::decode("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"), - Data{255}}; - { - Address address = TokenProgram::createProgramAddress(seeds4, Address("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")); - EXPECT_EQ(address.string(), "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - } - { - Address address = TokenProgram::createProgramAddress(seeds4, Address("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr")); - EXPECT_EQ(address.string(), "CuS1kE1wvGTmwGk3FYNQK85g4jU7gMySWwFRQQ9LFunp"); - } - // https://github.com/solana-labs/solana/blob/f25c969ad87e64e6d1fd07d2d37096ac71cf8d06/sdk/program/src/pubkey.rs#L353-L435 - { - std::vector seeds = {TW::data(""), {1}}; - Address address = TokenProgram::createProgramAddress(seeds, Address("BPFLoader1111111111111111111111111111111111")); - EXPECT_EQ(address.string(), "3gF2KMe9KiC6FNVBmfg9i267aMPvK37FewCip4eGBFcT"); - } - { - std::vector seeds = {TW::data("Talking"), TW::data("Squirrels")}; - Address address = TokenProgram::createProgramAddress(seeds, Address("BPFLoader1111111111111111111111111111111111")); - EXPECT_EQ(address.string(), "HwRVBufQ4haG5XSgpspwKtNd3PC9GM9m1196uJW36vds"); - } - { - std::vector seeds = {Base58::decode("SeedPubey1111111111111111111111111111111111")}; - Address address = TokenProgram::createProgramAddress(seeds, Address("BPFLoader1111111111111111111111111111111111")); - EXPECT_EQ(address.string(), "GUs5qLUfsEHkcMB9T38vjr18ypEhRuNWiePW2LoK4E3K"); - } -} - -} // namespace TW::Solana::tests diff --git a/tests/chains/Solana/SignerTests.cpp b/tests/chains/Solana/SignerTests.cpp deleted file mode 100644 index c8ffaf4fe51..00000000000 --- a/tests/chains/Solana/SignerTests.cpp +++ /dev/null @@ -1,656 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "HexCoding.h" -#include "PublicKey.h" -#include "Solana/Program.h" -#include "Solana/Signer.h" -#include "Solana/Transaction.h" - -#include - -using namespace TW; -namespace TW::Solana::tests { - -TEST(SolanaSigner, CompiledInstruction) { - const auto privateKey0 = - PrivateKey(Base58::decode("96PKHuMPtniu1T74RvUNkbDPXPPRZ8Mg1zXwciCAyaDq")); - const auto publicKey0 = privateKey0.getPublicKey(TWPublicKeyTypeED25519); - const auto address0 = Address(publicKey0); - ASSERT_EQ(Data(publicKey0.bytes.begin(), publicKey0.bytes.end()), - Base58::decode("GymAh18wHuFTytfSJWi8eYTA9x5S3sNb9CJSGBWoPRE3")); - const auto privateKey1 = - PrivateKey(Base58::decode("GvGmNPMQLZE2VNx3KG2GdiC4ndS8uCqd7PjioPgm9Qhi")); - const auto publicKey1 = privateKey1.getPublicKey(TWPublicKeyTypeED25519); - const auto address1 = Address(publicKey1); - ASSERT_EQ(Data(publicKey1.bytes.begin(), publicKey1.bytes.end()), - Base58::decode("2oKoYSAHgveX91917v4DUEuN8BNKXDg8KJWpaGyEay9V")); - Address programId("11111111111111111111111111111111"); - - std::vector
addresses = {address0, address1, programId}; - - std::vector instrAddresses = { - AccountMeta(address1, false, false), - AccountMeta(address0, false, false), - AccountMeta(programId, false, false), - AccountMeta(address1, false, false), - AccountMeta(address0, false, false), - }; - Data data = {0, 1, 2, 4}; - Instruction instruction(programId, instrAddresses, data); - - auto compiledInstruction = CompiledInstruction(instruction, addresses); - - EXPECT_EQ(compiledInstruction.programIdIndex, 2); - ASSERT_EQ(compiledInstruction.accounts.size(), 5ul); - EXPECT_EQ(compiledInstruction.accounts[0], 1); - EXPECT_EQ(compiledInstruction.accounts[1], 0); - EXPECT_EQ(compiledInstruction.accounts[2], 2); - EXPECT_EQ(compiledInstruction.accounts[3], 1); - EXPECT_EQ(compiledInstruction.accounts[4], 0); - ASSERT_EQ(compiledInstruction.data.size(), 4ul); -} - -TEST(SolanaSigner, CompiledInstructionFindAccount) { - Address address1 = Address(parse_hex("0102030405060708090a0102030405060708090a0102030405060708090a0101")); - Address address2 = Address(parse_hex("0102030405060708090a0102030405060708090a0102030405060708090a0102")); - Address address3 = Address(parse_hex("0102030405060708090a0102030405060708090a0102030405060708090a0103")); - Address programId("11111111111111111111111111111111"); - // clang-format off - Instruction instruction(programId, std::vector{ - AccountMeta(address1, true, false), - AccountMeta(address2, false, false), - }, Data{1, 2, 3, 4}); - // clang-format on - std::vector
addresses = { - address1, - address2, - programId, - }; - CompiledInstruction compiledInstruction = CompiledInstruction(instruction, addresses); - ASSERT_EQ(compiledInstruction.findAccount(address1), 0); - ASSERT_EQ(compiledInstruction.findAccount(address2), 1); - ASSERT_EQ(compiledInstruction.findAccount(programId), 2); - // negative case - try { - compiledInstruction.findAccount(address3); - FAIL() << "Missing expected exception"; - } catch (...) { - // ok - } -} - -TEST(SolanaSigner, SingleSignTransaction) { - const auto privateKey = - PrivateKey(Base58::decode("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr")); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - ASSERT_EQ(Data(publicKey.bytes.begin(), publicKey.bytes.end()), - Base58::decode("7v91N7iZ9mNicL8WfG6cgSCKyRXydQjLh6UYBWwm6y1Q")); - - const auto from = Address(publicKey); - auto to = Address("EN2sCsJ1WDV8UFqsiTXHcUPUxQ4juE71eCknHYYMifkd"); - auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); - auto transaction = VersionedTransaction(from, to, 42, recentBlockhash); - - std::vector signerKeys; - signerKeys.push_back(privateKey); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - auto expectedSignature = Base58::decode( - "5T6uZBHnHFd8uWErDBTFRVkbKuhbcm94K5MJ2beTYDruzqv4FjS7EMKvC94ZfxNAiWUXZ6bZxS3WXUbhJwYNPWn"); - expectedSignatures.push_back(expectedSignature); - ASSERT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = - "3p2kzZ1DvquqC6LApPuxpTg5CCDVPqJFokGSnGhnBHrta4uq7S2EyehV1XNUVXp51D69GxGzQZU" - "jikfDzbWBG2aFtG3gHT1QfLzyFKHM4HQtMQMNXqay1NAeiiYZjNhx9UvMX4uAQZ4Q6rx6m2AYfQ" - "7aoMUrejq298q1wBFdtS9XVB5QTiStnzC7zs97FUEK2T4XapjF1519EyFBViTfHpGpnf5bfizDz" - "sW9kYUtRDW1UC2LgHr7npgq5W9TBmHf9hSmRgM9XXucjXLqubNWE7HUMhbKjuBqkirRM"; - ASSERT_EQ(transaction.serialize(), expectedString); - - const auto additionalPrivateKey = - PrivateKey(Base58::decode("96PKHuMPtniu1T74RvUNkbDPXPPRZ8Mg1zXwciCAyaDq")); - signerKeys.push_back(additionalPrivateKey); - try { - Signer::sign(signerKeys, transaction); - FAIL() << "publicKey not found in message.accountKeys"; - } catch (std::invalid_argument const& err) { - EXPECT_EQ(err.what(), std::string("publicKey not found in message.accountKeys")); - } -} - -TEST(SolanaSigner, SignTransactionToSelf) { - const auto privateKey = - PrivateKey(Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746")); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - ASSERT_EQ(Data(publicKey.bytes.begin(), publicKey.bytes.end()), - Base58::decode("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu")); - - const auto from = Address(publicKey); - auto to = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); - auto transaction = VersionedTransaction(from, to, 42, recentBlockhash); - - std::vector signerKeys; - signerKeys.push_back(privateKey); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - auto expectedSignature = Base58::decode( - "3CFWDEK51noPJP4v2t8JZ3qj7kC7kLKyws9akfHMyuJnQ35EtzBptHqvaHfeswiLsvUSxzMVNoj4CuRxWtDD9zB1"); - expectedSignatures.push_back(expectedSignature); - ASSERT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = - "EKUmihvvUPKVN4GSCFwZRtz8WiyAuPvthW69Smo19SCjcPLQ6T7EVZd1HU71WAoe1bfgmPNS5JhU7ZLA9XKG3qbZqe" - "EFJ1xmRwW9ZKw8SKMAL6VRWxp87oLu7PSmf5b8R34vCaww3XLKtZkoP49a7TUK31DqPN5xJCceMB3BZJyaojQaKU8n" - "UkzSGf89LY6abZXp9krKAebvc6bSMzTP8SHSvbmZbf3VtejmpQeN9X6e7WVDn6oDa2bGT"; - ASSERT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, MultipleSignTransaction) { - const auto privateKey0 = - PrivateKey(Base58::decode("96PKHuMPtniu1T74RvUNkbDPXPPRZ8Mg1zXwciCAyaDq")); - const auto publicKey0 = privateKey0.getPublicKey(TWPublicKeyTypeED25519); - const auto address0 = Address(publicKey0); - ASSERT_EQ(Data(publicKey0.bytes.begin(), publicKey0.bytes.end()), - Base58::decode("GymAh18wHuFTytfSJWi8eYTA9x5S3sNb9CJSGBWoPRE3")); - const auto privateKey1 = - PrivateKey(Base58::decode("GvGmNPMQLZE2VNx3KG2GdiC4ndS8uCqd7PjioPgm9Qhi")); - const auto publicKey1 = privateKey1.getPublicKey(TWPublicKeyTypeED25519); - const auto address1 = Address(publicKey1); - ASSERT_EQ(Data(publicKey1.bytes.begin(), publicKey1.bytes.end()), - Base58::decode("2oKoYSAHgveX91917v4DUEuN8BNKXDg8KJWpaGyEay9V")); - - Data data = {0, 0, 0, 0}; - Address programId("11111111111111111111111111111111"); - std::vector instrAddresses = { - AccountMeta(address0, true, false), - AccountMeta(address1, false, false), - }; - Instruction instruction(programId, instrAddresses, data); - std::vector instructions = {instruction}; - - MessageHeader header = {2, 0, 1}; - std::vector
accountKeys = {address0, address1, programId}; - auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); - LegacyMessage message; - message.header = header; - message.accountKeys = accountKeys; - message.mRecentBlockHash = recentBlockhash; - message.instructions = instructions; - message.compileInstructions(); - - auto transaction = VersionedTransaction(VersionedMessage(message)); - - std::vector signerKeys; - // Sign order should not matter - signerKeys.push_back(privateKey1); - signerKeys.push_back(privateKey0); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - auto expectedSignature0 = Base58::decode( - "37beWPhNMfWUz75Tb24TX3PCS89FZscbCgwwLpFnzVfZYPqDpAWruvqzc9eeQYft35H23Vm9Tv1dPwEKWT3vAVPb"); - expectedSignatures.push_back(expectedSignature0); - auto expectedSignature1 = Base58::decode( - "5NxQshVaAXtQ8YVdcBtCanT62KbxnRfhubjGndFvetgn9AiaoLVZvRGutR5D7FJebRxq8bd6nQXn59LFzavEUrdQ"); - expectedSignatures.push_back(expectedSignature1); - ASSERT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = - "oL2CmkcP9xf2DiU7eo6hh3JdHnX3NGjunheXYo6SjVchzc8LtFJpPs4jccWUd7oPZUPQNTcR7Ee" - "Hn259ror9A7aXgJdP4djhntoD8irF1kuBZCj7pubtoWfiAKzagSL4hChQsTSe7e9jaGtoXu58mP" - "HCMKTz55TLjhdmCj7ixoWRowWEzkrF49MxXnurb4yf6ASru1XdHPFn3DdzkRHgypYwvRM6ci8p2" - "7trQvXFukhWX6qG6JkxqsWYSzACcAAGGWfAxSi63Yx1RxkxGUzyxy5f2thQhWZ6Nx6pR1im65yV" - "YMYPXj94kgtHxXw9h5V4p7xSAwRpmhw4jewYyQVX4jmnfro3gFNdX9AqpqMs4uGHA4rZM"; - ASSERT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, SignUpdateBlockhash) { - const auto privateKey = - PrivateKey(Base58::decode("G4VSzrknPBWZ1z2YwUnWTxD1td7wmqR5jMPEJRN6wm8S")); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - ASSERT_EQ(Data(publicKey.bytes.begin(), publicKey.bytes.end()), - Base58::decode("41a5jYky56M6EWDsFfLaZRxoRtgAJSRWxJnxaJNJELn5")); - - const auto from = Address(publicKey); - auto to = Address("4iSnyfDKaejniaPc2pBBckwQqV3mDS93go15NdxWJq2y"); - auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); - auto transaction = VersionedTransaction(from, to, 42, recentBlockhash); - - std::vector signerKeys; - signerKeys.push_back(privateKey); - Signer::sign(signerKeys, transaction); - - auto newBlockhash = Base58::decode("GgBaCs3NCBuZN12kCJgAW63ydqohFkHEdfdEXBPzLHq"); - Signer::signUpdateBlockhash(signerKeys, transaction, newBlockhash); - - std::vector expectedSignatures; - auto expectedSignature = Base58::decode( - "5AFhXjvGdENXCAe9MPvUA2qjoL4XtZwZKG7kK2HmZf1ibpxjx5kzogHZjN39uYB9J33UFJN15KhSggBZhzyNQmta"); - expectedSignatures.push_back(expectedSignature); - ASSERT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = - "62ABadDCoPfGGRnhLoBhfcPekMHyN5ee8DgTY8wD4iwKDjyFAsNbsaahTcqMWxmwa61q9iAGCQB" - "v1bETcYzWsTwLKMVGLoEpwqA84mPjqHyr5sQD5dcghyQiQ1ckYNub9K7s8FspVwwowK8gJG69xe" - "DEaqi7G1zrChBVbQYTmVUwJETyDmP1Vs8QU3CaxBs8qwcxoziU52KWLBpRj9o38QVBdxJtJ7hig" - "hgPKJubfqUfTWdN94PzqEfyPqwoCpFD39nvBn8C5xe1caPKivicg6U7Lzm9s8RYTLCEB"; - ASSERT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, SignRawMessage) { - const auto privateKey = - PrivateKey(Base58::decode("GjXseuD8JavBjKMdd6GEsPYZPV7tMMa46GS2JRS5tHRq")); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - ASSERT_EQ(Data(publicKey.bytes.begin(), publicKey.bytes.end()), - Base58::decode("3BocAWPm1oNXN5qkAV4QeDUmAPpkTcN1rrmCMWAfsXJY")); - - auto rawMessageData = - "01000203207be13c43c4528592eaf3fd34e064c641c5be3cb6691877d7ade94dff36734108eaea30723c33b525" - "07bc54024910612f885e4c80c10b99a047fd42c0acbace00000000000000000000000000000000000000000000" - "000000000000000000000404040404040404040404040404040404040404040404040404040404040404010202" - "00010c020000002a00000000000000"; - - std::vector signerKeys; - signerKeys.push_back(privateKey); - Data rawTransaction = Signer::signRawMessage(signerKeys, parse_hex(rawMessageData)); - - auto expectedHex = - "016e7f8349977b482bccf0bfc202ad917295803831e59ccb865b97d657464791ebfe3336879b84b9f165e464a3" - "4751fe30d54b01f3c9f33f969aafe1e85951b10901000203207be13c43c4528592eaf3fd34e064c641c5be3cb6" - "691877d7ade94dff36734108eaea30723c33b52507bc54024910612f885e4c80c10b99a047fd42c0acbace0000" - "000000000000000000000000000000000000000000000000000000000000040404040404040404040404040404" - "040404040404040404040404040404040401020200010c020000002a00000000000000"; - ASSERT_EQ(hex(rawTransaction), expectedHex); -} - -TEST(SolanaSigner, SignDelegateStakeV2) { - const auto privateKeySigner = - PrivateKey(Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746")); - const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); - auto signer = Address(publicKeySigner); - ASSERT_EQ(signer.string(), "zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - - auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); - auto programId = Address("Stake11111111111111111111111111111111111111"); - auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); - auto stakeAddress = StakeProgram::addressFromRecentBlockhash(signer, recentBlockhash, programId); - - auto message = LegacyMessage::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); - auto transaction = VersionedTransaction(VersionedMessage(message)); - - std::vector signerKeys; - signerKeys.push_back(privateKeySigner); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - auto expectedSignature = Base58::decode("58iogHzSJZmvTxi71W8k2yZXSPVfGAgtgqrk1RaBtfVFewU9yiJCkvSF1Hhjyax5DuexzR7ryWZDAWKQ73pyqvMs"); - expectedSignatures.push_back(expectedSignature); - EXPECT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = "j24mVM9Zgu5vDZhPLGGuCRXQnP9djNtxdHh4txN3S7dwJsNNL5fbhzGpPgSUAcLGoMVCfF9TuqTYfpfJnb4sJFe1ahM8yPL5HwuKL6py5AZJFi8SWx9fvaVB699dCPo1GT3JoEBLPCZ9o2jQtnwzLkzTYJnKv2axqhKWFE2sz6TBA5J39eZcjMFUYgyxz6Q5S4MWqYQCb8UET2NAEZoKcfy7j8N25WXL6Gj4j3hBZjpHQQNaGaNEprEqyma3ZuVhpGiCALSsuzVLX3wZVo4icXwe952deMFA4tH3BK1jcSQCgfmcKDJ9nd7bdrnUUs4BoMdF1uDZB5LxE2UH8QiqtYvaUcorF4SJ3gPxM5ykbyPsNK1cSYZF9NMpW2GofyC17eELwnHQTQB2kqphxJZu7BahvkwiDPPeeydiXAkBspJ3nc3PCBujv6WJw22ZHw5j6zAP8ZGnCW44pqtWD5qifF9tTKhySKdANNiWifs3tSCCPQqjfJXu14drNinR6VG8rJxS1qgmRYiRQUa7m1vtoaZFRN5qKUeAfoFKkAVaNnMdwgsNqNH4dqBodTCJFs1LkYwhgRZdZGbwXTn1j7vpR3DSnv4g72i2H556srzK53jdUmdv6yfxt516XDSshqZtHnKZ1tudxKjBXwsqT3imDiZFVka9wKWUAYMCi4XZ79CY6Xpsd9c18U2e9TCngQmgkTATFgrqysfraokNffgqWxvsPMugksbvbPjJs3iCzByvphkC9p7hCf6LwbeF8XnVB91EAgRDA4VLE1f9wkcq5zjy879YWJ4r516h3PQszTz1EaJXNAXdbk5Em7eyuuabGP1Q3nijFTL2yhMDsXpgrjAuEAABNxFMd4J1JRMaic615mHrhwociksrsfQK"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, SignDelegateStakeV1) { - const auto privateKeySigner = - PrivateKey(Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746")); - const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); - auto signer = Address(publicKeySigner); - ASSERT_EQ(signer.string(), "zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - - auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); - auto programId = Address("Stake11111111111111111111111111111111111111"); - auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); - auto stakeAddress = StakeProgram::addressFromValidatorSeed(signer, voteAddress, programId); - - auto message = LegacyMessage::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); - auto transaction = VersionedTransaction(VersionedMessage(message)); - - std::vector signerKeys; - signerKeys.push_back(privateKeySigner); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - auto expectedSignature = Base58::decode("gDPbnakbktrASmnUwKGpmftvQRbcyAvxyAyVXq3oVLfAdTPDqY8hhLPHTgidEZGWcmiaXnEyKg2GQLkkAh3JYr3"); - expectedSignatures.push_back(expectedSignature); - EXPECT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = "TKPiN35HzeD3zdwxDFvnkgoqud7CZsda15JkBwM4nDpr623rM7MZsH6QvMMyKpiz7MeRNTrfyHkRLQSBT9Tbg2mgTdfrbhhqeF3Suu5ECphqn8DFYPoMnFzeg5u9gaqevfjhuizzeo2YDJF8aVGy1pez8gMbp5vHz1SuvQUgfcvFctggUMwNiJorSmmp3N6TzQSd38CZrA8ZLhaJjuwDwVMjmj18rGTV1gkX19L7byTFrus2vNvPeUa2AawwUnFpYMPgvCKkHTrpnjvypjoLof9yMUFQ5M1S3Ntv53KJyXwXq6ejJnBDtisnDcdMDNSZp3VeKz6XCr8XVM5xNVh3LX12V4kc3ueqkokYJLP1JmuhA3nNZA1G5KTNno93HUoBkEa1x5h3haoCSgmQC97LoJbJM6B6C2NbaDj2J6iiTaVQdin4He4Jpj575WDhNTqsLjzFUHPUHQF1CRnuss8UpVyMsa4kdVqCDQGeh5DKbkikgcB8GKPBuC91DRxGEqgoygNsu5nnQy4o3YAJnBBK6HsKxpdjbYD8wCUdLw8muhjpEqeBTPShEaogm9zfehidiCcnxbeoX3gmW8oH9gpWoX7GrkJgF6Wn7iWohmrzqzAjoBz8hpeY5nkkhHrf9iswVGMpakdLGy3YxkGJVpsW8KJACwEKXGLq8SVLtXSUHG8EP16zfYHxKjkCSs8PkdFsA5esxsxppPTVZivuEPqJ5og55aNmugdNDrAFYWdcH1Q4rm7BXN6oHECdz2yY4HFVWh9u592oqozt2gQKu3vmhcNFzzQe1xgs6zKSv38kSGTnipd7Hx2VL3qNAR6XBRiwAi226qSTzxi6R82p7cMB7TMy6fk5AZ3sXDSXFNJ9S5SSU1V63ruw75QMtVio"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, SignCreateTokenAccount) { - const auto privateKeySigner = - PrivateKey(Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5")); - const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); - auto signer = Address(publicKeySigner); - EXPECT_EQ(signer.string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - - auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - auto tokenAddress = Address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - auto recentBlockhash = Base58::decode("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); - - auto message = LegacyMessage::createTokenCreateAccount(signer, signer, token, tokenAddress, recentBlockhash); - auto transaction = VersionedTransaction(VersionedMessage(message)); - - std::vector signerKeys; - signerKeys.push_back(privateKeySigner); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - auto expectedSignature = Base58::decode("3doYbPs5rES3TeDSrntqUvMgXCDE2ViJX2SFhLtiptVNkqPuixXs1SwU5LUZ3KwHnCzDUth6BRr3vU3gqnuUgRvQ"); - expectedSignatures.push_back(expectedSignature); - EXPECT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = - // test data obtained from spl-token create-account - "CKzRLx3AQeVeLQ7T4hss2rdbUpuAHdbwXDazxtRnSKBuncCk3WnYgy7XTrEiya19MJviYHYdTxi9gmWJY8qnR2vHVnH2DbPiKA8g72rD3VvMnjosGUBBvCwbBLge6FeQdgczMyRo9n5PcHvg9yJBTJaEEvuewyBVHwCGyGQci7eYd26xtZtCjAjwcTq4gGr3NZbeRW6jZp6j6APuew7jys4MKYRV4xPodua1TZFCkyWZr1XKzmPh7KTavtN5VzPDA8rbsvoEjHnKzjB2Bszs6pDjcBFSHyQqGsHoF8XPD35BLfjDghNtBmf9cFqo5axa6oSjANAuYg6cMSP4Hy28waSj8isr6gQjE315hWi3W1swwwPcn322gYZx6aMAcmjczaxX9aktpHYgZxixF7cYWEHxJs5QUK9mJePu9Xc6yW75UB4Ynx6dUgaSTEUzoQthF2TN3xXwu1"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, SignCreateTokenAccountForOther_3E6UFV) { - const auto privateKeySigner = - PrivateKey(parse_hex("4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7")); - const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); - auto signer = Address(publicKeySigner); - EXPECT_EQ(signer.string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - - auto otherMainAddress = Address("3xJ3MoUVFPNFEHfWdtNFa8ajXUHsJPzXcBSWMKLd76ft"); - auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - auto tokenAddress = Address("67BrwFYt7qUnbAcYBVx7sQ4jeD2KWN1ohP6bMikmmQV3"); - auto recentBlockhash = Base58::decode("HmWyvrif3QfZJnDiRyrojmH9iLr7eMxxqiC9RJWFeunr"); - - auto message = LegacyMessage::createTokenCreateAccount(signer, otherMainAddress, token, tokenAddress, recentBlockhash); - auto transaction = VersionedTransaction(VersionedMessage(message)); - - std::vector signerKeys; - signerKeys.push_back(privateKeySigner); - Signer::sign(signerKeys, transaction); - - auto expectedString = - // https://explorer.solana.com/tx/3E6UFVamHCm6Bgk8gXdZex7R7tJAVxqJm6t9ephAKu1PjcfZrD7CJqMwKu6RrvWSUESbZFqzdUyLXuxAFaawPHvJ - "4BsrHedHuForcKDhLdnLYDXgtQgQEj3EQVDtEhqa7o6ukFjW3shpTWv6PeKQdMp6af4ASjD4xQeZvXxLK5WUjguVMUf3xdJn7RnFeM7hdDJ56RDBM5PRJbRJVHjz6FJ7SVNTvr9y3gVYQtWx7NfKRxiyEAfq9JG7nqxSWaW6raMr9t35aVcdAVuXE9iXj3rzhVfCS69vVzy5KcFEK3mvDYG6L12V2CfviCydmeCvPw5r3zBUrZSQv7Ti4XFNBrPbk28gcqQwsBknBqasHxHqD9VUyPmBTuUyXq75QN8rhqN55NjxKBUw37tEUS1jKVpWnTeLFq1eRAMdXvjftNuQ5Bmm8Zc12PGWj9vdorBaYyvZXexJST5xNjR4SCkXvXZoRScETck95chv3VBn54jP8DpB4GGUmATFKSxpdtnNV64i1SQXW13KJwswthJvAaDiqevQLKLkvrTEAdb4BxEfPkFjDVti6P58rTZCMg5CTVLqdmWwpTSW5V"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, SignTransferToken_3vZ67C) { - const auto privateKeySigner = - PrivateKey(Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5")); - const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); - auto signer = Address(publicKeySigner); - EXPECT_EQ(signer.string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - - auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - auto senderTokenAddress = Address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - auto recipientTokenAddress = Address("3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei"); - uint64_t amount = 4000; - uint8_t decimals = 6; - auto recentBlockhash = Base58::decode("CNaHfvqePgGYMvtYi9RuUdVxDYttr1zs4TWrTXYabxZi"); - - auto message = LegacyMessage::createTokenTransfer(signer, token, - senderTokenAddress, recipientTokenAddress, amount, decimals, recentBlockhash); - auto transaction = VersionedTransaction(VersionedMessage(message)); - - std::vector signerKeys; - signerKeys.push_back(privateKeySigner); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - auto expectedSignature = Base58::decode("3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg"); - expectedSignatures.push_back(expectedSignature); - EXPECT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = - // https://explorer.solana.com/tx/3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg - // test data obtained from spl-token transfer - "PGfKqEaH2zZXDMZLcU6LUKdBSzU1GJWJ1CJXtRYCxaCH7k8uok38WSadZfrZw3TGejiau7nSpan2GvbK26hQim24jRe2AupmcYJFrgsdaCt1Aqs5kpGjPqzgj9krgxTZwwob3xgC1NdHK5BcNwhxwRtrCphGEH7zUFpGFrFrHzgpf2KY8FvPiPELQyxzTBuyNtjLjMMreehSKShEjD9Xzp1QeC1pEF8JL6vUKzxMXuveoEYem8q8JiWszYzmTMfDk13JPgv7pXFGMqDV3yNGCLsWccBeSFKN4UKECre6x2QbUEiKGkHkMc4zQwwyD8tGmEMBAGm339qdANssEMNpDeJp2LxLDStSoWShHnotcrH7pUa94xCVvCPPaomF"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, SignCreateNonceAccount) { - const auto privateKey = - PrivateKey(parse_hex("044014463e2ee3cc9c67a6f191dbac82288eb1d5c1111d21245bdc6a855082a1")); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - ASSERT_EQ(Data(publicKey.bytes.begin(), publicKey.bytes.end()), - Base58::decode("sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH")); - const auto nonceAccountPrivateKey = - PrivateKey(parse_hex("2a9737aca3cde2dc0b4f3ae3487e3a90000490cb39fbc979da32b974ff5d7490")); - const auto nonceAccountPublicKey = nonceAccountPrivateKey.getPublicKey(TWPublicKeyTypeED25519); - ASSERT_EQ(Data(nonceAccountPublicKey.bytes.begin(), nonceAccountPublicKey.bytes.end()), - Base58::decode("6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ")); - - const auto from = Address(publicKey); - const auto nonceAccount = Address(nonceAccountPublicKey); - auto recentBlockhash = Base58::decode("mFmK2xFMhzJJaUN5cctfdCizE9dtgcSASSEDh1Yzmat"); - uint64_t rent = 10000000; - auto message = LegacyMessage::createNonceAccount( - /* owner */ from, - /* new nonce_account */ nonceAccount, - /* rent */ rent, - /* recent_blockhash */ recentBlockhash); - auto transaction = VersionedTransaction(VersionedMessage(message)); - // auto transaction = Transaction(from, to, 42, recentBlockhash); - - std::vector signerKeys; - signerKeys.push_back(privateKey); - signerKeys.push_back(nonceAccountPrivateKey); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - auto expectedSignature1 = Base58::decode( - "3dbiGHLsFqnwA1PXx7xmoikzv6v9g9BXvZts2126qyE163BypurkvgbDiF5RmrEZRiT2MG88v6xwyJTkhhDRuFc9"); - expectedSignatures.push_back(expectedSignature1); - auto expectedSignature2 = Base58::decode( - "jFq4PbbEM1fuPbq5CkUYgzs7a21g6rvFkfLJAUUGP5QMKYhHBE6nB1dqtwaJsABgyUvrR8QjT2Ej73cXNz7Vur1"); - expectedSignatures.push_back(expectedSignature2); - ASSERT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = - "3wu6xJSbb2NysVgi7pdfMgwVBT1knAdeCr9NR8EktJLoByzM4s9SMto2PPmrnbRqPtHwnpAKxXkC4vqyWY2dRBgdGG" - "CC1bep6qN5nSLVzpPYAWUSq5cd4gfYMAVriFYRRNHmYUnEq8vMn4vjiECmZoHrpabBj8HpXGqYBo87sbZa8ZPCxUcB" - "71hxXiHWZHj2rovx2kr75Uuv1buWXyW6M8uR4UNvQcPPvzVbwBG82RjDYTuancMSAxmrVNR8GLBQNhrCCYrZyte3EW" - "gEyMQxxfW8T3xNXqnbgdfvFJ3UjRBxXj3hrmv17xEivTjfs81aG2AAi24yiYrk8ep7eQqwDHVSArsrynnwVKVNUcCQ" - "CnSy7fuiuS7FweFX8DEN1K9BrfecHyWrF15fYzhkmWSs64aH6ZTYHWPv5znhFKYmAuopGwbsBEb2j5p8NS3iJZ2skb" - "2wi47n1rpLZfoCHWKxNiikkDUJTGQNcSDrGUMfeW5aGubJrCfecPKEo9Wo9kd36iSsxYPYSWNKrz2HTooa1rCRhqjX" - "D8dyX3bXGV8TK6W2sEgf4JkcDnNoWQLbindcP8XR"; - ASSERT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, SignWithdrawNonceAccount) { - const auto privateKey = - PrivateKey(parse_hex("044014463e2ee3cc9c67a6f191dbac82288eb1d5c1111d21245bdc6a855082a1")); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - ASSERT_EQ(Data(publicKey.bytes.begin(), publicKey.bytes.end()), - Base58::decode("sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH")); - - const auto from = Address(publicKey); - const auto nonceAccount = Address("6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ"); - auto recentBlockhash = Base58::decode("5ccb7sRth3CP8fghmarFycr6VQX3NcfyDJsMFtmdkdU8"); - auto to = Address("3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe"); - uint64_t value = 10000000; - auto message = - LegacyMessage::createWithdrawNonceAccount(from, nonceAccount, to, value, recentBlockhash); - auto transaction = VersionedTransaction(VersionedMessage(message)); - - std::vector signerKeys; - signerKeys.push_back(privateKey); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - auto expectedSignature = Base58::decode( - "MxbTCAUmBLiESDLK1NiK5ab41mL2SpAPKSbvGdYQQD5eKgAJRdFEJ8MV9HqBhDQHdsS2LG3QMQQVJp51ekGu6KM"); - expectedSignatures.push_back(expectedSignature); - ASSERT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = - "7gdEdDymvtfPfVgVvCTPzafmZc1Z8Zu4uXgJDLm8KGpLyPHysxFGjtFzimZDmGtNhQCh22Ygv3ZtPZmSbANbafikR3" - "S1tvujatHW9gMo35jveq7TxwcGoNSqc7tnH85hkEZwnDryVaiKRvtCeH3dgFE9YqPHxiBuZT5eChHJvVNb9iTTdMsJ" - "XMusRtzeRV45CvrLKUvsAH7SSWHYW6bGow5TbEJie4buuz2rnbeVG5cxaZ6vyG2nJWHNuDPWZJTRi1MFEwHoxst3a5" - "jQPv9UrG9rNZFCw4uZizVcG6HEqHWgQBu8gVpYpzFCX5SrhjGPZpbK3YmHhUEMEpJx3Fn7jX7Kt4t3hhhrieXppoqK" - "NuqjeNVjfEf3Q8dJRfuVMLdXYbmitCVTPQzYKWBR6ERqWLYoAVqjoAS2pRUw1nrqi1HR"; - ASSERT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, SignCreateTokenAccountAndTransfer) { - const auto privateKeySigner = - PrivateKey(Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5")); - const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); - auto signer = Address(publicKeySigner); - EXPECT_EQ(signer.string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - - auto token = Address("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); - auto senderTokenAddress = Address("5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf"); - auto recipientMainAddress = Address("3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei"); - auto recipientTokenAddress = Address("BwTAyrCEdkjNyGSGVNSSGh6phn8KQaLN638Evj7PVQfJ"); - uint64_t amount = 4000; - uint8_t decimals = 6; - auto recentBlockhash = Base58::decode("AfzzEC8NVXoxKoHdjXLDVzqwqvvZmgPuqyJqjuHiPY1D"); - - auto message = LegacyMessage::createTokenCreateAndTransfer(signer, recipientMainAddress, token, - recipientTokenAddress, senderTokenAddress, - amount, decimals, recentBlockhash); - auto transaction = VersionedTransaction(VersionedMessage(message)); - - std::vector signerKeys; - signerKeys.push_back(privateKeySigner); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - auto expectedSignature = Base58::decode( - "pL1m11UEDWn3jMkrNMqLeGwNpKzmhQzJiYaCocgPy7vXKA1tnvEjJbuVq9hTeM9kqMAmxhRpwRY157jDgkRdUZw"); - expectedSignatures.push_back(expectedSignature); - EXPECT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = - "2qkvFTcMk9kPaHtd7idJ1gJc4zTkuYDUJsC67kXvHjv3zwEyUx92QyhhSeBjL6h3Zaisj2nvTWid2UD1N9hbg9Ty7v" - "SHLc7mcFVvy3yJmN9tz99iLKsf15rEeKUk3crXWLtKZEpcXJurN7vrxKwjQJnVob2RjyxwVfho1oNZ72BHvqToRM1W" - "2KbcYhxK4d9zB4QY5tR2dzgCHWqAjf9Yov3y9mPBYCQBtw2GewrVMDbg5TK81E9BaWer3BcEafc3NCnRfcFEp7ZUXs" - "GAgJYx32uUoJPP8ByTqBsp2JWgHyZhoz1WUUYRqWKZthzotErVetjSik4h5GcXk9Nb6kzqEf4nKEZ22eyrP5dv3eZM" - "uGUUpMYUT9uF16T72s4TTwqiWDPFkidD33tACx74JKGoDraHEvEeAPrv6iUmC675kMuAV4EtVspVc5SnKXgRWRxb4d" - "cH3k7K4ckjSxYZwg8UhTXUgPxA936jBr2HeQuPLmNVn2muA1HfL2DnyrobUP9vHpbL3HHgM2fckeXy8LAcjnoE9TTa" - "AKX32wo5xoMj9wJmmtcU6YbXN4KgZ"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, SignAdvanceNonceAccount) { - const auto privateKeySigner = - PrivateKey(Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5")); - const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); - auto signer = Address(publicKeySigner); - EXPECT_EQ(signer.string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - - auto nonceAccountAddress = Address("6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ"); - auto recentBlockhash = Base58::decode("4KQLRUfd7GEVXxAeDqwtuGTdwKd9zMfPycyAG3wJsdck"); - - auto message = LegacyMessage::advanceNonceAccount(signer, nonceAccountAddress, recentBlockhash); - auto transaction = VersionedTransaction(VersionedMessage(message)); - - std::vector signerKeys; - signerKeys.push_back(privateKeySigner); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - auto expectedSignature = Base58::decode( - "2gwuvwJ3mdEsjA8Gid6FXYuSwa2AAyFY6Btw8ifwSc2SPsfKBnD859C5mX4tLy6zQFHhKxSMMsW49o3dbJNiXDMo"); - expectedSignatures.push_back(expectedSignature); - EXPECT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = - "7YPgNzjCnUd2zBb6ZC6bf1YaoLjhJPHixLUdTjqMjq1YdzADJCx2wsTTBFFrqDKSHXEL6ntRq8NVJTQMGzGH5AQRKw" - "tKtutehxesmtzkZCPY9ADZ4ijFyveLmTt7kjZXX7ZWVoUmKAqiaYsPTex728uMBSRJpV4zRw2yKGdQRHTKy2QFEb9a" - "cwLjmrbEgoyzPCarxjPhw21QZnNcy8RiYJB2mzZ9nvhrD5d2jB5TtdiroQPgTSdKFzkNEd7hJUKpqUppjDFcNHGK73" - "FE9pCP2dKxCLH8Wfaez8bLtopjmWun9cbikxo7LZsarYzMXvxwZmerRd1"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, SignTokenTransferWithExternalFeePayer) { - const auto privateKeySigner = - PrivateKey(Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5")); - const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); - const auto feePayerPrivateKeySigner = PrivateKey(Base58::decode("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c")); - const auto feePayerPublicKeySigner = feePayerPrivateKeySigner.getPublicKey(TWPublicKeyTypeED25519); - - auto signer = Address(publicKeySigner); - EXPECT_EQ(signer.string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - auto feePayerSigner = Address(feePayerPublicKeySigner); - EXPECT_EQ(feePayerSigner.string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - - auto token = Address("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); - auto senderTokenAddress = Address("5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf"); - auto recipientTokenAddress = Address("AZapcpAZtEL1gQuC87F2L1hSfAZnAvNy1hHtJ8DJzySN"); - auto recentBlockhash = Base58::decode("GwB5uixiTQUG2Pvo6fWAaNQmz41Jt4WMEPD7b83wvHkX"); - - auto message = LegacyMessage::createTokenTransfer(signer, token, senderTokenAddress, recipientTokenAddress, 4000, 6, recentBlockhash, "", {}, "", feePayerSigner.string()); - auto transaction = VersionedTransaction(VersionedMessage(message)); - - std::vector signerKeys; - signerKeys.push_back(feePayerPrivateKeySigner); - signerKeys.push_back(privateKeySigner); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - auto expectedSignature1 = Base58::decode( - "2LbovMDuKoR2LFcV5NbK9bCQZcTG99W6VE1urvdWFWvRhNg9ocDGhayyeBGisoqZgYZtcD3b6LJDTmPx9Gp3T6qd"); - expectedSignatures.push_back(expectedSignature1); - auto expectedSignature2 = Base58::decode( - "2hUHMS9rwbUbXrpC7sK7utL2M4soTyQ7EX3sBYvdee9wraJvYoPH2XjovHDn8eRFY8z5uCx9DCj2Zfjpmzfa81Db"); - expectedSignatures.push_back(expectedSignature2); - EXPECT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = - // https://explorer.solana.com/tx/2LbovMDuKoR2LFcV5NbK9bCQZcTG99W6VE1urvdWFWvRhNg9ocDGhayyeBGisoqZgYZtcD3b6LJDTmPx9Gp3T6qd?cluster=devnet - "qjgNVBmoPDHNTN2ENQfxNVE57jWXpqdmu5GQX4msA7iK8ZRAnKpvbusQagv8CZGyNYti23p9jBsjTSx75ZU26UW5vgC8D88pusW8W5dp1ERo5DSfurMSYJ6afgQHdcuzn7exb8znSm6uV4y1cWgBRcuAGdg3wRpVhP8HEB1EeKgzjYVWvMSy6yR7qVrSL6BxHG6eiAMyahLFbEt4qBqLEdxxY7Dt4DyydVYmG2ZVtheaMHD3ACwCjpyPLXj399wxSgGXQQFGtzEJQw9awVezmJ4wZk6W4dDpXQvdKYaqUvwTwRZsQB5o2iekPWZXR9xvHiMLjMVBPzYgcU14ZSaCbqSNVv2pAJxP1sMvxZMNMzZPttPxCsDDGq9biC7exXwzesXSnZ3rsgEYeZtkUiBHAxR4rYqBpA6VzLs1bPx8MPTvr9mhNi2ezMBbg2nEfHV6Fz7H7rEY2g3jDtRz35Vmgits8s9RKi3kb73WtGUieRiXjiqkNhpvKkST1oEYRQ9"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, SignCreateTokenAccountAndTransferWithExternalFeePayer) { - const auto privateKeySigner = - PrivateKey(Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5")); - const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); - const auto feePayerPrivateKeySigner = PrivateKey(Base58::decode("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c")); - const auto feePayerPublicKeySigner = feePayerPrivateKeySigner.getPublicKey(TWPublicKeyTypeED25519); - - auto signer = Address(publicKeySigner); - EXPECT_EQ(signer.string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - auto feePayerSigner = Address(feePayerPublicKeySigner); - EXPECT_EQ(feePayerSigner.string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - - auto token = Address("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); - auto senderTokenAddress = Address("5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf"); - auto recipientMainAddress = Address("E54BymfdhXQtzDSGtgiJayauEMdB2gJjPqoDjzfbCXwj"); - auto recipientTokenAddress = Address("Ay7G7Yb7ZCebCQdG39FxD1nFNDtqFWJCBgfd5Ek7DDcS"); - uint64_t amount = 4000; - uint8_t decimals = 6; - auto recentBlockhash = Base58::decode("EsnN3ksLV6RLBW7qqgrvm2diwVJNTzL9QCuzm6tqWsU8"); - - auto message = LegacyMessage::createTokenCreateAndTransfer(signer, recipientMainAddress, token, - recipientTokenAddress, senderTokenAddress, - amount, decimals, recentBlockhash, "", {}, "", feePayerSigner.string()); - auto transaction = VersionedTransaction(VersionedMessage(message)); - - std::vector signerKeys; - signerKeys.push_back(feePayerPrivateKeySigner); - signerKeys.push_back(privateKeySigner); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - auto expectedSignature1 = Base58::decode( - "7GZGFT2VA4dpJBBwjiMqj1o8yChDvoCsqnQ7xz4GxY513W3efxRqbB8y7g4tH2GEGQRx4vCkKipG1sMaDEen1A2"); - expectedSignatures.push_back(expectedSignature1); - auto expectedSignature2 = Base58::decode( - "3n7RHTCBAtnFVuDn5eRbyQB24h6AqajJi5nGMPrfnUVFUDh2Cb8AoaJ7mVtjnv73V4HaJCzSwCLAj3zcGEaFftWZ"); - expectedSignatures.push_back(expectedSignature2); - EXPECT_EQ(transaction.signatures, expectedSignatures); - - // https://explorer.solana.com/tx/7GZGFT2VA4dpJBBwjiMqj1o8yChDvoCsqnQ7xz4GxY513W3efxRqbB8y7g4tH2GEGQRx4vCkKipG1sMaDEen1A2?cluster=devnet - auto expectedString = - "5sxFkQYd2FvqRU64N79A6xjJKNkgUsEEg2wKgai2NiK7A7hF3q5GYEbjQsYBG9S2MejwTENbwHzvypaa3D3cEkxvVTg19aJFWdCtXQiz42QF5fN2MuAb6eJR4KHFnzCtxxnYGtN9swZ5B5cMSPCffCRZeUTe3kooRmbTYPvSaemU6reVSM7X2beoFKPd2svrLFa8XnvhBwL9EiFWQ9WhHB2cDV7KozCnJAW9kdNDR4RbfFQxboANGo3ZGE5ddcZ6YdomATKze1TtHj2qzJEJRwxsRr3iM3iNFb4Eav5Q2n71KUriRf73mo44GQUPbQ2LvpZKf4V6M2PzxJwzBo7FiFZurPmsanT3U5efEsKnnueddbiLHedc8JXc1d3Z53sFxVGJpsGA8RR6thse9wUvaEWqXVtPbNA6NMao9DFGD6Dudza9pJXSobPc7mDHZmVmookf5vi6Lb9Y1Q4EgcEPQmbaDnKGGB6uGfZe629i3iKXRzAd2dB7mKfffhDadZ8S1eYGT3dhddV3ExRxcqDP9BAGQT3rkRw1JpeSSi7ziYMQ3vn4t3okdgQSq6rrpbPDUNG8tLSHFMAq3ydnh4Cb4ECKkYoz9SFAnXACUu4mWETxijuKMK9kHrTqPGk9weHTzobzCC8q8fcPWV3TcyUyMxsbVxh5q1p5h5tWfD9td5TZJ2HEUbTop2dA53ZF"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -} // namespace TW::Solana::tests diff --git a/tests/chains/Solana/TWAnySignerTests.cpp b/tests/chains/Solana/TWAnySignerTests.cpp index 8e67cd22bab..8537199f0e6 100644 --- a/tests/chains/Solana/TWAnySignerTests.cpp +++ b/tests/chains/Solana/TWAnySignerTests.cpp @@ -3,12 +3,8 @@ // Copyright © 2017 Trust Wallet. #include "Base58.h" -#include "HexCoding.h" #include "PrivateKey.h" -#include "Solana/Address.h" -#include "Solana/Program.h" #include "proto/Solana.pb.h" -#include "PrivateKey.h" #include "TestUtilities.h" #include @@ -40,558 +36,4 @@ TEST(TWAnySignerSolana, SignTransfer) { ASSERT_EQ(output.unsigned_tx(), "87PYsiS4MUU1UqXrsDoCBmD5FcKsXhwEBD8hc4zbq78yePu7bLENmbnmjmVbsj4VvaxnZhy4bERndPFzjSRH5WpwKwMLSCKvn9eSDmPESNcdkqne2UdMfWiFoq8ZeQBnF9h98dP8GM9kfzWPjvLmhjwuwA1E2k5WCtfii7LKQ34v6AtmFQGZqgdKiNqygP7ZKusHWGT8ZkTZ"); } -TEST(TWAnySignerSolana, SignV0Transfer) { - // Successfully broadcasted: https://explorer.solana.com/tx/4ffBzXxLPYEEdCYpQGETkCTCCsH6iTdmKzwUZXZZgFemdhRpxQwboguFFoKCeGF3SsZPzuwwE7LbRwLgJbsyRqyP?cluster=testnet - auto privateKey = parse_hex("833a053c59e78138a3ed090459bc6743cca6a9cbc2809a7bf5dbc7939b8775c8"); - auto input = Proto::SigningInput(); - - auto& message = *input.mutable_transfer_transaction(); - message.set_recipient("6pEfiZjMycJY4VA2FtAbKgYvRwzXDpxY58Xp4b7FQCz9"); - message.set_value((uint64_t)5000L); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("HxKwWFTHixCu8aw35J1uxAX6yUhLHkFCdJJdK4y98Gyj"); - input.set_v0_msg(true); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - ASSERT_EQ(output.encoded(), "6NijVxwQoDjqt6A41HXCK9kXwNDp48uLgvRyE8uz6NY5dEzaEDLzjzuMnc5TGatHZZUXehKrzUGzbg9jPSdn6pVsMc9TXNH6JGe5RJLmHwWey3MC1p8Hs2zhjw5P439P57NToatraDX9ZwvBtK4EzZzRjWbyGdicheTPjeYKCzvPCLxDkTFtPCM9VZGGXSN2Bne92NLDvf6ntNm5pxsPkZGxPe4w9Eq26gkE83hZyrYXKaiDh8TbqbHatSkw"); -} - -TEST(TWAnySignerSolana, SignTransferToSelf) { - auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); - auto input = Proto::SigningInput(); - - auto& message = *input.mutable_transfer_transaction(); - message.set_recipient("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - message.set_value((uint64_t)42L); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - "EKUmihvvUPKVN4GSCFwZRtz8WiyAuPvthW69Smo19SCjcPLQ6T7EVZd1HU71WAoe1bfgmPNS5JhU7ZLA9XKG3qbZqe" - "EFJ1xmRwW9ZKw8SKMAL6VRWxp87oLu7PSmf5b8R34vCaww3XLKtZkoP49a7TUK31DqPN5xJCceMB3BZJyaojQaKU8n" - "UkzSGf89LY6abZXp9krKAebvc6bSMzTP8SHSvbmZbf3VtejmpQeN9X6e7WVDn6oDa2bGT"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignTransferWithMemoAndReference) { - const auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); - auto input = Solana::Proto::SigningInput(); - - auto& message = *input.mutable_transfer_transaction(); - message.set_recipient("71e8mDsh3PR6gN64zL1HjwuxyKpgRXrPDUJT7XXojsVd"); - message.set_value((uint64_t)10000000L); - message.set_memo("HelloSolanaMemo"); - message.add_references("CuieVDEDtLo7FypA9SbLM9saXFdb1dsshEkyErMqkRQq"); - message.add_references("tFpP7tZUt6zb7YZPpQ11kXNmsc5YzpMXmahGMvCHhqS"); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - const auto expectedString = "NfNH76sST3nJ4FmFGTZJBUpJou7DRuHM3YNprT1HeEau699CQF65xNf21Hoi491bbtVKUXfqCJyeZhfTCEnABuXNC1JrhGBeCv2AbQdaS9gpp9j4xHHomhCYdwYaBWFMcKkdMXrx9xHqL9Vkny4HezkwQfb3wGqcaE9XVRdkkNxsoJnVKddRnrQbjhsZGTcKdfmbTghoUeRECNPTm6nZTA1owWF1Dq6mfr6M3GZRh4ucqEquxKsQC2HQwNRrGZahsfyUvwspPWwMt78q5Jpjd9kHqkFDspZL6Pepv4dAA4uHhYDCHeP2bbDiFMBYxxWCVDDtRKSh3H92xUgh1GCSgNcjGdbVfQUhSDPX3k9xuuszPTsVZ2GnsavAsRp6Vf6fFEikBX6pVV9zjW1cx94EepQ2aGEBSsVu4RzX7rJjCLCq87h8cxxf1XnF8mvYGEK7wzF"; - EXPECT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignDelegateStakeTransaction_noStakeAccount) { - auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); - auto input = Solana::Proto::SigningInput(); - - auto& message = *input.mutable_delegate_stake_transaction(); - message.set_validator_pubkey("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); - message.set_value((uint64_t)42L); - message.set_stake_account(""); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = "j24mVM9Zgu5vDZhPLGGuCRXQnP9djNtxdHh4txN3S7dwJsNNL5fbhzGpPgSUAcLGoMVCfF9TuqTYfpfJnb4sJFe1ahM8yPL5HwuKL6py5AZJFi8SWx9fvaVB699dCPo1GT3JoEBLPCZ9o2jQtnwzLkzTYJnKv2axqhKWFE2sz6TBA5J39eZcjMFUYgyxz6Q5S4MWqYQCb8UET2NAEZoKcfy7j8N25WXL6Gj4j3hBZjpHQQNaGaNEprEqyma3ZuVhpGiCALSsuzVLX3wZVo4icXwe952deMFA4tH3BK1jcSQCgfmcKDJ9nd7bdrnUUs4BoMdF1uDZB5LxE2UH8QiqtYvaUcorF4SJ3gPxM5ykbyPsNK1cSYZF9NMpW2GofyC17eELwnHQTQB2kqphxJZu7BahvkwiDPPeeydiXAkBspJ3nc3PCBujv6WJw22ZHw5j6zAP8ZGnCW44pqtWD5qifF9tTKhySKdANNiWifs3tSCCPQqjfJXu14drNinR6VG8rJxS1qgmRYiRQUa7m1vtoaZFRN5qKUeAfoFKkAVaNnMdwgsNqNH4dqBodTCJFs1LkYwhgRZdZGbwXTn1j7vpR3DSnv4g72i2H556srzK53jdUmdv6yfxt516XDSshqZtHnKZ1tudxKjBXwsqT3imDiZFVka9wKWUAYMCi4XZ79CY6Xpsd9c18U2e9TCngQmgkTATFgrqysfraokNffgqWxvsPMugksbvbPjJs3iCzByvphkC9p7hCf6LwbeF8XnVB91EAgRDA4VLE1f9wkcq5zjy879YWJ4r516h3PQszTz1EaJXNAXdbk5Em7eyuuabGP1Q3nijFTL2yhMDsXpgrjAuEAABNxFMd4J1JRMaic615mHrhwociksrsfQK"; - EXPECT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignDelegateStakeTransaction_withAccount) { - auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); - auto input = Solana::Proto::SigningInput(); - - auto& message = *input.mutable_delegate_stake_transaction(); - message.set_validator_pubkey("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); - message.set_stake_account("6u9vJH9pRj66N5oJFCBADEbpMTrLxQATcL6q5p5MXwYv"); - message.set_value((uint64_t)42L); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = "TKPiN35HzeD3zdwxDFvnkgoqud7CZsda15JkBwM4nDpr623rM7MZsH6QvMMyKpiz7MeRNTrfyHkRLQSBT9Tbg2mgTdfrbhhqeF3Suu5ECphqn8DFYPoMnFzeg5u9gaqevfjhuizzeo2YDJF8aVGy1pez8gMbp5vHz1SuvQUgfcvFctggUMwNiJorSmmp3N6TzQSd38CZrA8ZLhaJjuwDwVMjmj18rGTV1gkX19L7byTFrus2vNvPeUa2AawwUnFpYMPgvCKkHTrpnjvypjoLof9yMUFQ5M1S3Ntv53KJyXwXq6ejJnBDtisnDcdMDNSZp3VeKz6XCr8XVM5xNVh3LX12V4kc3ueqkokYJLP1JmuhA3nNZA1G5KTNno93HUoBkEa1x5h3haoCSgmQC97LoJbJM6B6C2NbaDj2J6iiTaVQdin4He4Jpj575WDhNTqsLjzFUHPUHQF1CRnuss8UpVyMsa4kdVqCDQGeh5DKbkikgcB8GKPBuC91DRxGEqgoygNsu5nnQy4o3YAJnBBK6HsKxpdjbYD8wCUdLw8muhjpEqeBTPShEaogm9zfehidiCcnxbeoX3gmW8oH9gpWoX7GrkJgF6Wn7iWohmrzqzAjoBz8hpeY5nkkhHrf9iswVGMpakdLGy3YxkGJVpsW8KJACwEKXGLq8SVLtXSUHG8EP16zfYHxKjkCSs8PkdFsA5esxsxppPTVZivuEPqJ5og55aNmugdNDrAFYWdcH1Q4rm7BXN6oHECdz2yY4HFVWh9u592oqozt2gQKu3vmhcNFzzQe1xgs6zKSv38kSGTnipd7Hx2VL3qNAR6XBRiwAi226qSTzxi6R82p7cMB7TMy6fk5AZ3sXDSXFNJ9S5SSU1V63ruw75QMtVio"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignDeactivateStakeTransaction) { - auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); - auto input = Solana::Proto::SigningInput(); - - auto& message = *input.mutable_deactivate_stake_transaction(); - message.set_stake_account("6XMLCn47d5kPi3g4YcjqFvDuxWnpVADpN2tXpeRc4XUB"); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = "6x3fSstNz4GpPxmT5jHXwyD62uyJMKaPWeBDNNcwXZA9NJ3E7KavCXPNUd8ZYTX5VpkfHKGszkwzM6AdAp4giLD29jvWdNYjkV1Nvb42xFwGD6ryMPZzXkJijaRTrA7SvPTDSRU2haGVmorqkywAXLQUCw47NmBUfLTb5gDcKoBeaAsahckv1eCE746thJVTg2dQNvUTULKF6xckUg7kwFkcUuRe4HCcRgrKcNAUKLR2rEM3brVQkUyAaAtMMtc3gVDXxxpbtW5Fa9wGaEnh31FdRo4z5YBzAUaz7vcrvzF2j81KCPTVnYyTmeJzCzJafzCVCtw"; - EXPECT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignDeactivateAllStakeTransaction) { - auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); - auto input = Solana::Proto::SigningInput(); - - auto& message = *input.mutable_deactivate_all_stake_transaction(); - *message.add_stake_accounts() = "CJQStmfyoHbosX1GfVn64yWrNJAo214q2aqxwS6FGh4k"; - *message.add_stake_accounts() = "6XMLCn47d5kPi3g4YcjqFvDuxWnpVADpN2tXpeRc4XUB"; - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = "U9azMJWRfDhypoDeQLYWyBYFZCwRNZy8sbrVX9awKK84zNGbSQfYTTJ3ZyzjNUVbU5npbw2MsWfmZGHZRvpfN7G7o3sVePyFRXrmLxrGZzGycFv25Zff4zPxDarbsugbCBgzVGpgwu8x7MdkwBAVHVtNsgMcHgArEAjEmk7YEGpZ15rjo39bCRvmuprWLqSv2SK1RyTZPpTPXVevAbA4i9vvcY8eUbwW29SZCoyGaagLU5EBV9vckMjzGa7gq2yMR6rbq8tDdWaXapYs8RavU49WN94yg4wdE4fzYq8DjqXHq3MuUBLxeYDKJnvj84ioeM4eR1EwjBNrGyz5GHTRuhbNg1nc57SpKsSMVSZW5Ra3tUk84YZXYFHxzeQ9Tv4o"; - EXPECT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignWithdrawStakeTransaction) { - auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); - auto input = Solana::Proto::SigningInput(); - - auto& message = *input.mutable_withdraw_transaction(); - message.set_stake_account("6XMLCn47d5kPi3g4YcjqFvDuxWnpVADpN2tXpeRc4XUB"); - message.set_value((uint64_t)42L); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = "gxr4o1trVP8DGG8UC21AA964YqAPFA3rBCF9MwmBQpn5fDtcujM9wp1gzT466MxWGR8wMciS6dSL771q29eURrEEuvhJzRaFDGPLgVB3UL4gd4T2amPQkR4Dzq5drKEtPJRBR86KVVc2kjDsbWNpdL8S7pZqW3VUijAbm9TS8ezG8NExSCkhxExKhUjXWWguEL4qXra7s2JZfhtmvuJneWnEY3isUVfC9knWtGNwpNFvRvzbH2sgHzwtSsD7mkYrBJoazLCwT8r9yypxycHL41XcGtH425MA16kVSunvvBfzG9PzBTS65YJBs64tzttasCU9uEphkwgmfrmoEC8iKt8xD47Ra79RyXd95yURsaxvpb1tVAH8kMNtj8iV1Pfm"; - EXPECT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignWithdrawAllStakeTransaction) { - auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); - auto input = Solana::Proto::SigningInput(); - - auto& message = *input.mutable_withdraw_all_transaction(); - message.add_stake_accounts(); - message.add_stake_accounts(); - message.mutable_stake_accounts(0)->set_stake_account("CJQStmfyoHbosX1GfVn64yWrNJAo214q2aqxwS6FGh4k"); - message.mutable_stake_accounts(0)->set_value((uint64_t)42L); - message.mutable_stake_accounts(1)->set_stake_account("6XMLCn47d5kPi3g4YcjqFvDuxWnpVADpN2tXpeRc4XUB"); - message.mutable_stake_accounts(1)->set_value((uint64_t)67L); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = "cvBNusjtHkR74EfWsvFPEe2Mydcr7eoLeY2wJw2ZMZYViotbb63Adai7UD1PW9uLusoVHGLeJC5cPgVBC4F693P9tPAxLs9yiZj1ZJQ4DgnYbeXafqzjdWje1Ly5FgpDUJaaU2RnLCG51CcrmiTJ4KB5fwai6egZaNjbiqo1DEC1wJz4FgKug2aKQWLdeCiH9WhCuvqfhNV6mEE4qRCkU8uS2gfSqBd1AdrczvoDEbKQszosrwmawxqmvTE5EWaFzMb48x9nLqxvpQCvGQu1nX6FxZJjv2swekA7wGLEAA4uSdFLTHNrYSi8pn8hVYGwESEzth9oiPkJCvW7Y2KvGALeERUZn8knHiz2eqaaT72Ajp9UogMdZtiuFHufveLXpBLWUERchhB7eU1magYcPNHcZuEE4uQv5kZJhHAqYCGU6dyUFLVA9Edus7o6fTktYVCjoGb"; - EXPECT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignDeactivateStakeTransaction_1) { - auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); - auto input = Solana::Proto::SigningInput(); - - auto& message = *input.mutable_deactivate_stake_transaction(); - message.set_stake_account("6u9vJH9pRj66N5oJFCBADEbpMTrLxQATcL6q5p5MXwYv"); - - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - "AhfB77PTGTKBfbGPGuEz2khbBy8m8Kou1zqZST9dP7PLJNSeEze5NJuCh5qecPLa3S8xAQ6mTULmnAWiW81ib87nhy" - "wFtx5nKiUvmhdXsvKCSX6NNtNXdRz5yZi3UEop4obco85SY2czS6n4SJwmtDedHLtg9urqdZVth7AUM8KAtrRsksyv" - "ZRYXh64Z8QGyNY7ekj31ae11avGiSDNWYZZHqx7VPWRsKeatGyGk5zPmnRdL8ABMQgJ1Te7wAWwVnNn5QcoAxDuPw6" - "uDctP8Q5S4TieRVatCnukQFj5BTJisez3E2ZJPWhVrMh4K3wEFkPHA7dR"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignWithdrawStakeTransaction_1) { - auto privateKey = Base58::decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); - auto input = Solana::Proto::SigningInput(); - - auto& message = *input.mutable_withdraw_transaction(); - message.set_stake_account("6u9vJH9pRj66N5oJFCBADEbpMTrLxQATcL6q5p5MXwYv"); - message.set_value((uint64_t)42L); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = "NL7WgagucfLd6AkTtcKe1dqd47xxzF356Q7tEhPrz1LRzZiAmokAaUkpwJ7X71Pmz97zZf9gZQU5BNswdcdpqUL8n1jwn4CoZMaPJhX5LF43Sj817cgreSG14TEWfKertpVpTtc5zY7vkDM7t9wjYhkaqgYz76HQtqAqRHnHF2Qr9EEfLj4zYRerWtyfS3EVyVUaasPxJ5vkcaonEfpGc6uWecaFr2A3YbzEBQpWXjMaXLqmMDtNS8rTNZmwvToa71ddFZKDgaHDcc6Lkg8qriZ3aQbUqL1TbeYp2mk9dWTKY62L1YFE2DyZV5P2qz5feywcMZ9JW6X1wBmiHFCseC42QbnbTibr1VdqLbGx7UWn5tHWk5jCN2aatEPfbFDZ"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignCreateTokenAccount1) { - auto privateKeyData = Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); - ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - - auto input = Solana::Proto::SigningInput(); - auto& message = *input.mutable_create_token_account_transaction(); - message.set_main_address("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - message.set_token_address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - input.set_private_key(privateKeyData.data(), privateKeyData.size()); - input.set_recent_blockhash("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - "CKzRLx3AQeVeLQ7T4hss2rdbUpuAHdbwXDazxtRnSKBuncCk3WnYgy7XTrEiya19MJviYHYdTxi9gmWJY8qnR2vHVnH2DbPiKA8g72rD3VvMnjosGUBBvCwbBLge6FeQdgczMyRo9n5PcHvg9yJBTJaEEvuewyBVHwCGyGQci7eYd26xtZtCjAjwcTq4gGr3NZbeRW6jZp6j6APuew7jys4MKYRV4xPodua1TZFCkyWZr1XKzmPh7KTavtN5VzPDA8rbsvoEjHnKzjB2Bszs6pDjcBFSHyQqGsHoF8XPD35BLfjDghNtBmf9cFqo5axa6oSjANAuYg6cMSP4Hy28waSj8isr6gQjE315hWi3W1swwwPcn322gYZx6aMAcmjczaxX9aktpHYgZxixF7cYWEHxJs5QUK9mJePu9Xc6yW75UB4Ynx6dUgaSTEUzoQthF2TN3xXwu1"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignCreateTokenAccount2_5KtPn1) { - auto privateKeyData = parse_hex("4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7"); - ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - - auto input = Solana::Proto::SigningInput(); - auto& message = *input.mutable_create_token_account_transaction(); - message.set_main_address("Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - message.set_token_address("ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf"); - input.set_private_key(privateKeyData.data(), privateKeyData.size()); - input.set_recent_blockhash("HxaCmxrXgzkzXYvDFTToENtf9rVKk7cbiuSUqnqNheHq"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - // https://explorer.solana.com/tx/5KtPn1LGuxhFiwjxErkxTb7XxtLVYUBe6Cn33ej7ATNVyorrkk3UAFJWDBUmzP8CZjmkocCxiMAdYnvrKoGpMsJx - "EoJGDRFZdnjmx7rgwYSuDGTMTUdxCBeh8RggrQDzGht9bwzLPpCWkCrN4iQJqg3R6JxP7z2QZuf7dGCZcjMVBmmisYE8waRsohcvygRwmGr6nefbaujR5avm2x3EUvoTGyy8cMZJxX7URx45qQJyCgqFLNFCQzD1Kej3xCEPAJqCdGZgmqkryw2E2nkpGKXgRmbyEg2rFgd5kpvjG6jSLLYzGomxVnaKK2XyMQbcedkTMYJ8Ara71iWPRFUziWfgivZcA1qsQp92Fpao3FSsRprhoQz9u1VyAnh8zEM9jCKiE5s4dwCknqCJYeYsbMLn1be2vNP9bMQfu1jjGSHmbb9WR3E2vakTUEUByASXqSAJZuXYE5scopEzB28rC8nrC31ArLMZng5wWym3QbqEv2Syd6RHoEeoXR6vA5LPqvJKyvtH82p4hc4XbD18128aNrFG3GTD2P"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignCreateTokenAccountForOther_3E6UFV) { - auto privateKeyData = parse_hex("4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7"); - ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - - auto input = Solana::Proto::SigningInput(); - auto& message = *input.mutable_create_token_account_transaction(); - message.set_main_address("3xJ3MoUVFPNFEHfWdtNFa8ajXUHsJPzXcBSWMKLd76ft"); - message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - message.set_token_address("67BrwFYt7qUnbAcYBVx7sQ4jeD2KWN1ohP6bMikmmQV3"); - input.set_private_key(privateKeyData.data(), privateKeyData.size()); - input.set_recent_blockhash("HmWyvrif3QfZJnDiRyrojmH9iLr7eMxxqiC9RJWFeunr"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - // https://explorer.solana.com/tx/3E6UFVamHCm6Bgk8gXdZex7R7tJAVxqJm6t9ephAKu1PjcfZrD7CJqMwKu6RrvWSUESbZFqzdUyLXuxAFaawPHvJ - "4BsrHedHuForcKDhLdnLYDXgtQgQEj3EQVDtEhqa7o6ukFjW3shpTWv6PeKQdMp6af4ASjD4xQeZvXxLK5WUjguVMUf3xdJn7RnFeM7hdDJ56RDBM5PRJbRJVHjz6FJ7SVNTvr9y3gVYQtWx7NfKRxiyEAfq9JG7nqxSWaW6raMr9t35aVcdAVuXE9iXj3rzhVfCS69vVzy5KcFEK3mvDYG6L12V2CfviCydmeCvPw5r3zBUrZSQv7Ti4XFNBrPbk28gcqQwsBknBqasHxHqD9VUyPmBTuUyXq75QN8rhqN55NjxKBUw37tEUS1jKVpWnTeLFq1eRAMdXvjftNuQ5Bmm8Zc12PGWj9vdorBaYyvZXexJST5xNjR4SCkXvXZoRScETck95chv3VBn54jP8DpB4GGUmATFKSxpdtnNV64i1SQXW13KJwswthJvAaDiqevQLKLkvrTEAdb4BxEfPkFjDVti6P58rTZCMg5CTVLqdmWwpTSW5V"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignTokenTransfer1_3vZ67C) { - auto privateKeyData = Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); - ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - - auto input = Solana::Proto::SigningInput(); - auto& message = *input.mutable_token_transfer_transaction(); - message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - message.set_sender_token_address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - message.set_recipient_token_address("3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei"); - message.set_amount(4000); // 0.004 - message.set_decimals(6); - input.set_private_key(privateKeyData.data(), privateKeyData.size()); - input.set_recent_blockhash("CNaHfvqePgGYMvtYi9RuUdVxDYttr1zs4TWrTXYabxZi"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - // https://explorer.solana.com/tx/3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg - "PGfKqEaH2zZXDMZLcU6LUKdBSzU1GJWJ1CJXtRYCxaCH7k8uok38WSadZfrZw3TGejiau7nSpan2GvbK26hQim24jRe2AupmcYJFrgsdaCt1Aqs5kpGjPqzgj9krgxTZwwob3xgC1NdHK5BcNwhxwRtrCphGEH7zUFpGFrFrHzgpf2KY8FvPiPELQyxzTBuyNtjLjMMreehSKShEjD9Xzp1QeC1pEF8JL6vUKzxMXuveoEYem8q8JiWszYzmTMfDk13JPgv7pXFGMqDV3yNGCLsWccBeSFKN4UKECre6x2QbUEiKGkHkMc4zQwwyD8tGmEMBAGm339qdANssEMNpDeJp2LxLDStSoWShHnotcrH7pUa94xCVvCPPaomF"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignTokenTransfer2_2pMvzp) { - auto privateKeyData = Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); - ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - - auto input = Solana::Proto::SigningInput(); - auto& message = *input.mutable_token_transfer_transaction(); - message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - message.set_sender_token_address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - message.set_recipient_token_address("ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf"); - message.set_amount(6100); // 0.0061 - message.set_decimals(6); - input.set_private_key(privateKeyData.data(), privateKeyData.size()); - input.set_recent_blockhash("zMEbroNLJ4vfDTdQyA72rk35c7nPo4K38efHLujbSuz"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - // https://explorer.solana.com/tx/2pMvzparE16evNgNhiexBfj15eurQgqFJXemYkuGasWV8RfT5tQseadqXA2VXbgGZPM1MpLcGwfkKKqvYvrKTmnR - "LCtawaKHmvh9WEjYPFFMDQXsdKMQbVyK4Q3aRRfLCouqw6GE4p31PRPFoQqtazTziEj3ex3iLgnCspz1MN4SUE9d33g3HiiA6oCS6wGMvB2i3ojtmJzndCiLoDmuZgiuGouVSeS2MAEUoS3CRjdnbNKbRwgKn8YsDe1bZ57ueipfBLJfiE7xr8ji678uAv8FcMgo8Mq88SBGxVCUhjMS2VGQZhRUHHzDmvnzxhbbUzsLDfApzjHExkUm7ws3cQ2i1cSpQNCQWJd6rcDv1sYwDAavPS571Ny3CUq4cZxABh45Gj88LkRpzBMRdoebrh9hPy8ZRnu7PocBVjZytCgdF4CuhzdYNsmdcuU2WN5CEmv5zQ7pBrFdLZ8bBifP"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignCreateAndTransferToken_449VaY) { - auto privateKeyData = Base58::decode("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"); - ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - - auto input = Solana::Proto::SigningInput(); - auto& message = *input.mutable_create_and_transfer_token_transaction(); - message.set_recipient_main_address("71e8mDsh3PR6gN64zL1HjwuxyKpgRXrPDUJT7XXojsVd"); - message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - message.set_recipient_token_address("EF6L8yJT1SoRoDCkAZfSVmaweqMzfhxZiptKi7Tgj5XY"); - message.set_sender_token_address("ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf"); - message.set_amount(2900); - message.set_decimals(6); - input.set_private_key(privateKeyData.data(), privateKeyData.size()); - input.set_recent_blockhash("DMmDdJP41M9mw8Z4586VSvxqGCrqPy5uciF6HsKUVDja"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - // https://explorer.solana.com/tx/449VaYo48LrkMJF6XVKt9sJwVQN6Seqrmh9erDCLtiuj6BgFG3wpF5TwjNkxgJ7qzNa6NTj3TFsU3h9hKszfkA7w - "3Y2MVz2VVi7aEyC9q1awwdk1ModDBPHRSacKmTYnSgkmbbJeZ62Fub1bVPSHaTy4LUcQpzCQYhHAKtTKXUDYijEeLsMAUqPBEMAq1w8zCdqDpdXy6M4PuwNtYVV1WgqeiEsiMWpPp4BGWKfcziwFbmYueUGituacJq4wTnt92fho8mFi49XW64gEG4iNGScDtJkY7Geq8PKiLh1E9JMJoceiHxKbmxzCmmLTxEHdhySYHcDUSXnXWogZskeZNBMtR9dNjEMkCzEjrxRpBtJPtUNshciY45mDPNmw4j3xyLCBTRikyfFLc5g11r3UgyVD4YokoPRvrEXsgt6W3yjBshropBm6mY2eJYvfY2eZz4Yq8kLcUatCHVKtjcb1mP9Ww57KisJ9bRhipC8sodFaMYhZARMEa4a1u9eH4MyNUATRGNXarwQSBY46PWS3nKP6QBK7Dw7Ppp9MmYkdPcXKaLScbyLF3jKu6dHWMkHw3WdXSsM1wwXjXnWF9LxdwaEVcDmySWybj6aKD9QCWTU5kdncqJU56f7SYNRTN289WdUFGNDmSh56tj2v1"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignCreateAndTransferTokenWithMemoReferences) { - const auto privateKeyData = Base58::decode("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"); - EXPECT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - - auto input = Solana::Proto::SigningInput(); - auto& message = *input.mutable_create_and_transfer_token_transaction(); - message.set_recipient_main_address("71e8mDsh3PR6gN64zL1HjwuxyKpgRXrPDUJT7XXojsVd"); - message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - message.set_recipient_token_address("EF6L8yJT1SoRoDCkAZfSVmaweqMzfhxZiptKi7Tgj5XY"); - message.set_sender_token_address("ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf"); - message.set_amount(2900); - message.set_decimals(6); - message.set_memo("HelloSolanaMemo370"); - message.add_references("CuieVDEDtLo7FypA9SbLM9saXFdb1dsshEkyErMqkRQq"); - message.add_references("tFpP7tZUt6zb7YZPpQ11kXNmsc5YzpMXmahGMvCHhqS"); - input.set_private_key(privateKeyData.data(), privateKeyData.size()); - input.set_recent_blockhash("DMmDdJP41M9mw8Z4586VSvxqGCrqPy5uciF6HsKUVDja"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = "FuUw2MoEGPATE38roXAw9mGQhCfdsdpVDdhuf5h8LKc8iWj2HzNS3SteXqyUoZtQ7L1ufLvu7cTMwNzxT8snnVimcknsA52CeN7bgMz1Ad1hRTAr77zE5efzAi8B124kaQ1cBEb6nFMr5Zq4wwDRoJgBaiUaM1U9ZY6GofCKHGMQN7ZNqEFG4fFvPaMXB59dFtiqrtApBGzvDho3nGshyQWZVWfMY44hvVk45FqiGrXuqUwkiJqeRaDhooZdXiFR9ubwJLXo3Ux23ZyijWKXYNsx1Lm5zMFEgRz3kXhzxzb8uzHVSrFYNieXXCQEv1GtErMKeQWuAHcwS3zxC6avTnTWJhTz3kVSXfSTYEg4MF2MBWeGrzKZ7id88ZfbpG4ZwzsDsdUCSMV6YYRNmx9P3B6oC4DL7cbi2g8hwtBdeKojY4G6JMPeg629V9sPyg2KKeYxD3cjhMKAYtrsJEbixep4LZENtdQxmgZFouJVvGy9MVhiTzGEFVwm4G25p5FhWhiS9HxHWVRXpUFHi2K9K2ttoo4Ug39V9f8s9cG1Xb5A4bHhGSuKLeCCBcrBqPWEsuLdVhjxsKJrRBJhyrZ6mpxtDhUWivZa6skmEawTts9rN2aP3dXW3cNch3s3LTXZWXG9QPUARJJPy5QAYsBoR8GunF5FFgHVuEHVpjXAd8ku9f7aoF8RNiMnXAqQHxiM3ug6HZpLHLX8aGoUbJ7vVAnEDLH"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignJSON) { - auto json = STRING(R"({"recentBlockhash":"11111111111111111111111111111111","transferTransaction":{"recipient":"EN2sCsJ1WDV8UFqsiTXHcUPUxQ4juE71eCknHYYMifkd","value":"42"}})"); - Data keyData = Base58::decode("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr"); - EXPECT_EQ(hex(keyData), "8778cc93c6596387e751d2dc693bbd93e434bd233bc5b68a826c56131821cb63"); - auto key = WRAPD(TWDataCreateWithBytes(keyData.data(), keyData.size())); - - auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeSolana)); - - ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeSolana)); - assertStringsEqual(result, expectedString1); -} - -TEST(TWAnySignerSolana, SignCreateNonceAccount) { - const auto privateKeyData = parse_hex("044014463e2ee3cc9c67a6f191dbac82288eb1d5c1111d21245bdc6a855082a1"); - const auto privateKey = PrivateKey(privateKeyData); - EXPECT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH"); - const auto nonceAccountPrivateKeyData = parse_hex("2a9737aca3cde2dc0b4f3ae3487e3a90000490cb39fbc979da32b974ff5d7490"); - const auto nonceAccountPrivateKey = PrivateKey(nonceAccountPrivateKeyData); - EXPECT_EQ(Address(PrivateKey(nonceAccountPrivateKey).getPublicKey(TWPublicKeyTypeED25519)).string(), "6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ"); - - auto input = Solana::Proto::SigningInput(); - auto& message = *input.mutable_create_nonce_account(); - auto nonceAccount = std::string("6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ"); - auto sender = std::string("sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH"); - uint64_t rent = 10000000; - input.set_private_key(privateKeyData.data(), privateKeyData.size()); - input.set_recent_blockhash(std::string("mFmK2xFMhzJJaUN5cctfdCizE9dtgcSASSEDh1Yzmat")); - message.set_nonce_account_private_key(nonceAccountPrivateKeyData.data(), nonceAccountPrivateKeyData.size()); - message.set_rent(rent); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - "3wu6xJSbb2NysVgi7pdfMgwVBT1knAdeCr9NR8EktJLoByzM4s9SMto2PPmrnbRqPtHwnpAKxXkC4vqyWY2dRBgdGG" - "CC1bep6qN5nSLVzpPYAWUSq5cd4gfYMAVriFYRRNHmYUnEq8vMn4vjiECmZoHrpabBj8HpXGqYBo87sbZa8ZPCxUcB" - "71hxXiHWZHj2rovx2kr75Uuv1buWXyW6M8uR4UNvQcPPvzVbwBG82RjDYTuancMSAxmrVNR8GLBQNhrCCYrZyte3EW" - "gEyMQxxfW8T3xNXqnbgdfvFJ3UjRBxXj3hrmv17xEivTjfs81aG2AAi24yiYrk8ep7eQqwDHVSArsrynnwVKVNUcCQ" - "CnSy7fuiuS7FweFX8DEN1K9BrfecHyWrF15fYzhkmWSs64aH6ZTYHWPv5znhFKYmAuopGwbsBEb2j5p8NS3iJZ2skb" - "2wi47n1rpLZfoCHWKxNiikkDUJTGQNcSDrGUMfeW5aGubJrCfecPKEo9Wo9kd36iSsxYPYSWNKrz2HTooa1rCRhqjX" - "D8dyX3bXGV8TK6W2sEgf4JkcDnNoWQLbindcP8XR"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignWithdrawNonceAccount) { - const auto privateKeyData = parse_hex("044014463e2ee3cc9c67a6f191dbac82288eb1d5c1111d21245bdc6a855082a1"); - const auto privateKey = PrivateKey(privateKeyData); - EXPECT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH"); - - auto input = TW::Solana::Proto::SigningInput(); - auto& message = *input.mutable_withdraw_nonce_account(); - auto nonceAccount = std::string("6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ"); - auto recipient = std::string("3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe"); - uint64_t value = 10000000; - input.set_private_key(privateKeyData.data(), privateKeyData.size()); - input.set_recent_blockhash(std::string("5ccb7sRth3CP8fghmarFycr6VQX3NcfyDJsMFtmdkdU8")); - message.set_nonce_account(nonceAccount); - message.set_recipient(recipient); - message.set_value(value); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - "7gdEdDymvtfPfVgVvCTPzafmZc1Z8Zu4uXgJDLm8KGpLyPHysxFGjtFzimZDmGtNhQCh22Ygv3ZtPZmSbANbafikR3" - "S1tvujatHW9gMo35jveq7TxwcGoNSqc7tnH85hkEZwnDryVaiKRvtCeH3dgFE9YqPHxiBuZT5eChHJvVNb9iTTdMsJ" - "XMusRtzeRV45CvrLKUvsAH7SSWHYW6bGow5TbEJie4buuz2rnbeVG5cxaZ6vyG2nJWHNuDPWZJTRi1MFEwHoxst3a5" - "jQPv9UrG9rNZFCw4uZizVcG6HEqHWgQBu8gVpYpzFCX5SrhjGPZpbK3YmHhUEMEpJx3Fn7jX7Kt4t3hhhrieXppoqK" - "NuqjeNVjfEf3Q8dJRfuVMLdXYbmitCVTPQzYKWBR6ERqWLYoAVqjoAS2pRUw1nrqi1HR"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignCreateTokenAccountAndTransfer) { - const auto privateKeyData = Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); - const auto privateKey = PrivateKey(privateKeyData); - EXPECT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - - auto input = TW::Solana::Proto::SigningInput(); - auto& message = *input.mutable_create_and_transfer_token_transaction(); - auto signer = std::string("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - auto token = std::string("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); - auto senderTokenAddress = std::string("5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf"); - auto recipientMainAddress = std::string("3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei"); - auto recipientTokenAddress = std::string("BwTAyrCEdkjNyGSGVNSSGh6phn8KQaLN638Evj7PVQfJ"); - uint64_t amount = 4000; - uint8_t decimals = 6; - input.set_private_key(privateKeyData.data(), privateKeyData.size()); - input.set_recent_blockhash(std::string("AfzzEC8NVXoxKoHdjXLDVzqwqvvZmgPuqyJqjuHiPY1D")); - message.set_recipient_main_address(recipientMainAddress); - message.set_token_mint_address(token); - message.set_recipient_token_address(recipientTokenAddress); - message.set_sender_token_address(senderTokenAddress); - message.set_amount(amount); - message.set_decimals(decimals); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - "2qkvFTcMk9kPaHtd7idJ1gJc4zTkuYDUJsC67kXvHjv3zwEyUx92QyhhSeBjL6h3Zaisj2nvTWid2UD1N9hbg9Ty7v" - "SHLc7mcFVvy3yJmN9tz99iLKsf15rEeKUk3crXWLtKZEpcXJurN7vrxKwjQJnVob2RjyxwVfho1oNZ72BHvqToRM1W" - "2KbcYhxK4d9zB4QY5tR2dzgCHWqAjf9Yov3y9mPBYCQBtw2GewrVMDbg5TK81E9BaWer3BcEafc3NCnRfcFEp7ZUXs" - "GAgJYx32uUoJPP8ByTqBsp2JWgHyZhoz1WUUYRqWKZthzotErVetjSik4h5GcXk9Nb6kzqEf4nKEZ22eyrP5dv3eZM" - "uGUUpMYUT9uF16T72s4TTwqiWDPFkidD33tACx74JKGoDraHEvEeAPrv6iUmC675kMuAV4EtVspVc5SnKXgRWRxb4d" - "cH3k7K4ckjSxYZwg8UhTXUgPxA936jBr2HeQuPLmNVn2muA1HfL2DnyrobUP9vHpbL3HHgM2fckeXy8LAcjnoE9TTa" - "AKX32wo5xoMj9wJmmtcU6YbXN4KgZ"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignAdvanceNonceAccount) { - const auto privateKeyData = Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); - const auto privateKey = PrivateKey(privateKeyData); - EXPECT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - - auto input = TW::Solana::Proto::SigningInput(); - auto& message = *input.mutable_advance_nonce_account(); - auto nonceAccount = std::string("6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ"); - input.set_private_key(privateKeyData.data(), privateKeyData.size()); - input.set_recent_blockhash(std::string("4KQLRUfd7GEVXxAeDqwtuGTdwKd9zMfPycyAG3wJsdck")); - message.set_nonce_account(nonceAccount); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - "7YPgNzjCnUd2zBb6ZC6bf1YaoLjhJPHixLUdTjqMjq1YdzADJCx2wsTTBFFrqDKSHXEL6ntRq8NVJTQMGzGH5AQRKw" - "tKtutehxesmtzkZCPY9ADZ4ijFyveLmTt7kjZXX7ZWVoUmKAqiaYsPTex728uMBSRJpV4zRw2yKGdQRHTKy2QFEb9a" - "cwLjmrbEgoyzPCarxjPhw21QZnNcy8RiYJB2mzZ9nvhrD5d2jB5TtdiroQPgTSdKFzkNEd7hJUKpqUppjDFcNHGK73" - "FE9pCP2dKxCLH8Wfaez8bLtopjmWun9cbikxo7LZsarYzMXvxwZmerRd1"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignTokenTransferWithExternalFeePayer) { - const auto privateKeyData = Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); - ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - const auto feePayerPrivateKeyData = Base58::decode("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"); - ASSERT_EQ(Address(PrivateKey(feePayerPrivateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - - auto input = Solana::Proto::SigningInput(); - auto& message = *input.mutable_token_transfer_transaction(); - message.set_token_mint_address("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); - message.set_sender_token_address("5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf"); - message.set_recipient_token_address("AZapcpAZtEL1gQuC87F2L1hSfAZnAvNy1hHtJ8DJzySN"); - message.set_amount(4000); // 0.004 - message.set_decimals(6); - input.set_private_key(privateKeyData.data(), privateKeyData.size()); - input.set_recent_blockhash("H4gZ56AdmHfZj1F36oWrxDJMUJ8ph7XdTHtmsbtHZshG"); - input.set_fee_payer("Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - input.set_fee_payer_private_key(feePayerPrivateKeyData.data(), feePayerPrivateKeyData.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - // https://explorer.solana.com/tx/3KbvREZUat76wgWMtnJfWbJL74Vzh4U2eabVJa3Z3bb2fPtW8AREP5pbmRwUrxZCESbTomWpL41PeKDcPGbojsej?cluster=devnet - "ushDP6dNZWq32FASGqdnw7E8x14zFDAEZBViTyevUptV9yb4WSgpYzEGCxt9sXkrEtHVyww4F4GdcGZbPEaQTjAeyX5KGvHHDhWoPeFNnECzjUTuPj35dpF7zJ75Jx3ADfLQtUyzu5w7812fQvhwBwP8XDm3btqSG4VLWeQU5XuVqpg33Mq1L9zkGHQ8PZ4WkgNuSrC584EVnDcFE4rZsUtAv2jFTMjinQJB1qQEGTCHbjgdtJt8PzmXGXeczNyisPsEDrhZUw3g7RFYsgBDB1RFe1TxspzbWmxwr6CNPkGVsopmS6cbvSG9ejXY8xRYaswP7knAoPXwYk26yetoA824mzdv9vJ2RYpyK72EyCqFfFidm8MrJjFR49KwV3HRQKxZzYcvhYuJhR15GKBUWAQvYJTQQWArTi7pr7m84wNsV1mCUjrYsCVK47QtMAYvWU4toTxfgThngfF47awnpcSxfy8ggbwamq7qcaSH6cQVk1LPGo1iB5YxitSbaXP"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignCreateTokenAccountAndTransferWithExternalFeePayer) { - const auto privateKeyData = Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); - const auto privateKey = PrivateKey(privateKeyData); - EXPECT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - const auto feePayerPrivateKeyData = Base58::decode("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"); - EXPECT_EQ(Address(PrivateKey(feePayerPrivateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - - auto input = TW::Solana::Proto::SigningInput(); - auto& message = *input.mutable_create_and_transfer_token_transaction(); - auto signer = std::string("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - auto token = std::string("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); - auto senderTokenAddress = std::string("5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf"); - auto recipientMainAddress = std::string("E54BymfdhXQtzDSGtgiJayauEMdB2gJjPqoDjzfbCXwj"); - auto recipientTokenAddress = std::string("Ay7G7Yb7ZCebCQdG39FxD1nFNDtqFWJCBgfd5Ek7DDcS"); - uint64_t amount = 4000; - uint8_t decimals = 6; - input.set_private_key(privateKeyData.data(), privateKeyData.size()); - input.set_recent_blockhash(std::string("EsnN3ksLV6RLBW7qqgrvm2diwVJNTzL9QCuzm6tqWsU8")); - input.set_fee_payer("Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - input.set_fee_payer_private_key(feePayerPrivateKeyData.data(), feePayerPrivateKeyData.size()); - message.set_recipient_main_address(recipientMainAddress); - message.set_token_mint_address(token); - message.set_recipient_token_address(recipientTokenAddress); - message.set_sender_token_address(senderTokenAddress); - message.set_amount(amount); - message.set_decimals(decimals); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - // https://explorer.solana.com/tx/7GZGFT2VA4dpJBBwjiMqj1o8yChDvoCsqnQ7xz4GxY513W3efxRqbB8y7g4tH2GEGQRx4vCkKipG1sMaDEen1A2?cluster=devnet - auto expectedString = - "5sxFkQYd2FvqRU64N79A6xjJKNkgUsEEg2wKgai2NiK7A7hF3q5GYEbjQsYBG9S2MejwTENbwHzvypaa3D3cEkxvVTg19aJFWdCtXQiz42QF5fN2MuAb6eJR4KHFnzCtxxnYGtN9swZ5B5cMSPCffCRZeUTe3kooRmbTYPvSaemU6reVSM7X2beoFKPd2svrLFa8XnvhBwL9EiFWQ9WhHB2cDV7KozCnJAW9kdNDR4RbfFQxboANGo3ZGE5ddcZ6YdomATKze1TtHj2qzJEJRwxsRr3iM3iNFb4Eav5Q2n71KUriRf73mo44GQUPbQ2LvpZKf4V6M2PzxJwzBo7FiFZurPmsanT3U5efEsKnnueddbiLHedc8JXc1d3Z53sFxVGJpsGA8RR6thse9wUvaEWqXVtPbNA6NMao9DFGD6Dudza9pJXSobPc7mDHZmVmookf5vi6Lb9Y1Q4EgcEPQmbaDnKGGB6uGfZe629i3iKXRzAd2dB7mKfffhDadZ8S1eYGT3dhddV3ExRxcqDP9BAGQT3rkRw1JpeSSi7ziYMQ3vn4t3okdgQSq6rrpbPDUNG8tLSHFMAq3ydnh4Cb4ECKkYoz9SFAnXACUu4mWETxijuKMK9kHrTqPGk9weHTzobzCC8q8fcPWV3TcyUyMxsbVxh5q1p5h5tWfD9td5TZJ2HEUbTop2dA53ZF"; - ASSERT_EQ(output.encoded(), expectedString); -} } // namespace TW::Solana::tests diff --git a/tests/chains/Solana/TWSolanaAddressTests.cpp b/tests/chains/Solana/TWSolanaAddressTests.cpp index 877fbcc5dff..d18050b8417 100644 --- a/tests/chains/Solana/TWSolanaAddressTests.cpp +++ b/tests/chains/Solana/TWSolanaAddressTests.cpp @@ -36,3 +36,14 @@ TEST(TWSolanaProgram, defaultTokenAddress) { assertStringsEqual(tokenAddress, "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); assertStringsEqual(description, "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); } + +TEST(TWSolanaProgram, defaultTokenAddressError) { + const auto solAddress = STRING("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); + // Invalid token mint address. + const auto serumToken = STRING("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKW"); + + auto solanaAddress = WRAP(TWSolanaAddress, TWSolanaAddressCreateWithString(solAddress.get())); + auto description = WRAPS(TWSolanaAddressDescription(solanaAddress.get())); + + EXPECT_EQ(TWSolanaAddressDefaultTokenAddress(solanaAddress.get(), serumToken.get()), nullptr); +} diff --git a/tests/chains/Solana/TWSolanaTransaction.cpp b/tests/chains/Solana/TWSolanaTransaction.cpp index 54f579931e5..6850ce721c5 100644 --- a/tests/chains/Solana/TWSolanaTransaction.cpp +++ b/tests/chains/Solana/TWSolanaTransaction.cpp @@ -2,10 +2,13 @@ // // Copyright © 2017 Trust Wallet. -#include "PrivateKey.h" #include "TrustWalletCore/TWSolanaTransaction.h" +#include "TrustWalletCore/TWTransactionDecoder.h" +#include "TrustWalletCore/TWAnySigner.h" #include "proto/Solana.pb.h" #include "TestUtilities.h" +#include "Base64.h" +#include "Base58.h" #include @@ -33,4 +36,54 @@ TEST(TWSolanaTransaction, UpdateBlockhashAndSign) { EXPECT_EQ(output.encoded(), "Ajzc/Tke0CG8Cew5qFa6xZI/7Ya3DN0M8Ige6tKPsGzhg8Bw9DqL18KUrEZZ1F4YqZBo4Rv+FsDT8A7Nss7p4A6BNVZzzGprCJqYQeNg0EVIbmPc6mDitNniHXGeKgPZ6QZbM4FElw9O7IOFTpOBPvQFeqy0vZf/aayncL8EK/UEAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqbHiki6ThNH3auuyZPQpJntnN0mA//56nMpK/6HIuu8xAQUEAgQDAQoMoA8AAAAAAAAG"); } +TEST(TWSolanaTransaction, DecodeUpdateBlockhashAndSign) { + // base64 encoded + // https://explorer.solana.com/tx/3KbvREZUat76wgWMtnJfWbJL74Vzh4U2eabVJa3Z3bb2fPtW8AREP5pbmRwUrxZCESbTomWpL41PeKDcPGbojsej?cluster=devnet + auto encodedTx = Base64::decode("AnQTYwZpkm3fs4SdLxnV6gQj3hSLsyacpxDdLMALYWObm722f79IfYFTbZeFK9xHtMumiDOWAM2hHQP4r/GtbARpncaXgOVFv7OgbRLMbuCEJHO1qwcdCbtH72VzyzU8yw9sqqHIAaCUE8xaQTgT6Z5IyZfeyMe2QGJIfOjz65UPAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8Aqe6sdLXiXSDILEtzckCjkjchiSf6zVGpMYiAE5BE2IqHAQUEAgQDAQoMoA8AAAAAAAAG"); + auto newBlockhash = "CyPYVsYWrsJNfVpi8aazu7WsrswNFuDd385z6GNoBGUg"; + auto senderPrivateKey = Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); + auto feePayerPrivateKey = Base58::decode("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"); + + // Step 1: Decode the transaction. + + auto encodedTxPtr = WRAPD(TWDataCreateWithBytes(encodedTx.data(), encodedTx.size())); + auto decodeOutputPtr = WRAPD(TWTransactionDecoderDecode(TWCoinTypeSolana, encodedTxPtr.get())); + + Proto::DecodingTransactionOutput decodeOutput; + decodeOutput.ParseFromArray( + TWDataBytes(decodeOutputPtr.get()), + static_cast(TWDataSize(decodeOutputPtr.get())) + ); + + EXPECT_EQ(decodeOutput.error(), Common::Proto::SigningError::OK); + ASSERT_TRUE(decodeOutput.transaction().has_legacy()); + // Previous fee payer signature. + EXPECT_EQ( + decodeOutput.transaction().signatures(0).signature(), + "3KbvREZUat76wgWMtnJfWbJL74Vzh4U2eabVJa3Z3bb2fPtW8AREP5pbmRwUrxZCESbTomWpL41PeKDcPGbojsej" + ); + // Previous sender signature. + EXPECT_EQ( + decodeOutput.transaction().signatures(1).signature(), + "37UT8U6DdqaWqV6yQuHcRN3JpMefDgVna8LJJPmmDNKYMwmefEgvcreSg9AqSsT7cU2rMBNyDktEtDU19ZqqZvML" + ); + + // Step 2. Prepare a signing input and update the recent-blockhash. + + Proto::SigningInput signingInput; + *signingInput.mutable_raw_message() = decodeOutput.transaction(); + signingInput.mutable_raw_message()->mutable_legacy()->set_recent_blockhash(newBlockhash); + signingInput.set_private_key(senderPrivateKey.data(), senderPrivateKey.size()); + signingInput.set_fee_payer_private_key(feePayerPrivateKey.data(), feePayerPrivateKey.size()); + signingInput.set_tx_encoding(Proto::Encoding::Base64); + + // Step 3. Re-sign the updated transaction. + + Proto::SigningOutput output; + ANY_SIGN(signingInput, TWCoinTypeSolana); + + EXPECT_EQ(output.error(), Common::Proto::SigningError::OK); + EXPECT_EQ(output.encoded(), "Ajzc/Tke0CG8Cew5qFa6xZI/7Ya3DN0M8Ige6tKPsGzhg8Bw9DqL18KUrEZZ1F4YqZBo4Rv+FsDT8A7Nss7p4A6BNVZzzGprCJqYQeNg0EVIbmPc6mDitNniHXGeKgPZ6QZbM4FElw9O7IOFTpOBPvQFeqy0vZf/aayncL8EK/UEAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqbHiki6ThNH3auuyZPQpJntnN0mA//56nMpK/6HIuu8xAQUEAgQDAQoMoA8AAAAAAAAG"); +} + } // TW::Solana::tests diff --git a/tests/chains/Solana/TWWalletConnectSolana.cpp b/tests/chains/Solana/TWWalletConnectSolana.cpp new file mode 100644 index 00000000000..f8c23d0c80e --- /dev/null +++ b/tests/chains/Solana/TWWalletConnectSolana.cpp @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#include "Base58.h" +#include "proto/Solana.pb.h" +#include "proto/WalletConnect.pb.h" +#include "Coin.h" +#include +#include + +#include "TestUtilities.h" +#include + +namespace TW::Solana { + +TEST(TWWalletConnectSolana, Transfer) { + auto private_key = Base58::decode("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr"); + const auto payload = R"({"transaction":"AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEDZsL1CMnFVcrMn7JtiOiN1U4hC7WovOVof2DX51xM0H/GizyJTHgrBanCf8bGbrFNTn0x3pCGq30hKbywSTr6AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgIAAQwCAAAAKgAAAAAAAAA="})"; + + WalletConnect::Proto::ParseRequestInput parsingInput; + parsingInput.set_method(WalletConnect::Proto::Method::SolanaSignTransaction); + parsingInput.set_payload(payload); + + const auto parsinginputData = parsingInput.SerializeAsString(); + const auto parsingInputDataPtr = WRAPD(TWDataCreateWithBytes(reinterpret_cast(parsinginputData.c_str()), parsinginputData.size())); + + const auto outputDataPtr = WRAPD(TWWalletConnectRequestParse(TWCoinTypeSolana, parsingInputDataPtr.get())); + + WalletConnect::Proto::ParseRequestOutput parsingOutput; + parsingOutput.ParseFromArray( + TWDataBytes(outputDataPtr.get()), + static_cast(TWDataSize(outputDataPtr.get())) + ); + + EXPECT_EQ(parsingOutput.error(), Common::Proto::SigningError::OK); + + // Step 2: Set missing fields. + ASSERT_TRUE(parsingOutput.has_solana()); + Proto::SigningInput signingInput = parsingOutput.solana(); + + signingInput.set_private_key(private_key.data(), private_key.size()); + signingInput.set_tx_encoding(Proto::Encoding::Base64); + + Proto::SigningOutput output; + ANY_SIGN(signingInput, TWCoinTypeSolana); + + EXPECT_EQ(output.error(), Common::Proto::SigningError::OK); + EXPECT_EQ(output.encoded(), "AQPWaOi7dMdmQpXi8HyQQKwiqIftrg1igGQxGtZeT50ksn4wAnyH4DtDrkkuE0fqgx80LTp4LwNN9a440SrmoA8BAAEDZsL1CMnFVcrMn7JtiOiN1U4hC7WovOVof2DX51xM0H/GizyJTHgrBanCf8bGbrFNTn0x3pCGq30hKbywSTr6AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgIAAQwCAAAAKgAAAAAAAAA="); +} + +} // namespace TW::Binance diff --git a/tests/chains/Solana/TransactionCompilerTests.cpp b/tests/chains/Solana/TransactionCompilerTests.cpp index 08a199038ae..53e3567486e 100644 --- a/tests/chains/Solana/TransactionCompilerTests.cpp +++ b/tests/chains/Solana/TransactionCompilerTests.cpp @@ -62,7 +62,6 @@ TEST(SolanaCompiler, CompileTransferWithSignatures) { "QSUuHZdyyn7hK4pxzLPMgPG8fY1XvXdppWMaKMLmhriLkckzGKJMaE3pWBRFBKzigXY28714uUNndb7S9hVakxa59h" "rLph39CMgAkcj6b8KYvJEkb1YdYytHSZNGi4kVVTNqiicNgPdf1gmG6qz9zVtnqj9JtaD2efdS8qxsKnvNWSgb8Xxb" "T6dwyp7msUUi7d27cYaPTpK"; - EXPECT_EQ(outputData.size(), 296ul); { Solana::Proto::SigningOutput output; ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); @@ -84,466 +83,4 @@ TEST(SolanaCompiler, CompileTransferWithSignatures) { } } -TEST(SolanaCompiler, CompileCreateNonceAccountWithSignatures) { - const auto coin = TWCoinTypeSolana; - /// Step 1: Prepare transaction input (protobuf) - auto input = TW::Solana::Proto::SigningInput(); - auto& message = *input.mutable_create_nonce_account(); - auto nonceAccount = std::string("6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ"); - auto sender = std::string("sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH"); - uint64_t rent = 10000000; - input.set_sender(sender); - input.set_recent_blockhash(std::string("mFmK2xFMhzJJaUN5cctfdCizE9dtgcSASSEDh1Yzmat")); - message.set_nonce_account(nonceAccount); - message.set_rent(rent); - auto inputString = input.SerializeAsString(); - auto inputStrData = TW::Data(inputString.begin(), inputString.end()); - /// Step 2: Obtain preimage hash - const auto preImageHashesData = TransactionCompiler::preImageHashes(coin, inputStrData); - auto preSigningOutput = TW::Solana::Proto::PreSigningOutput(); - preSigningOutput.ParseFromArray(preImageHashesData.data(), (int)preImageHashesData.size()); - ASSERT_EQ(preSigningOutput.signers_size(), 2); - auto signer1 = preSigningOutput.signers(0); - EXPECT_EQ(signer1, sender); - auto signer2 = preSigningOutput.signers(1); - EXPECT_EQ(signer2, nonceAccount); - auto preImageHash = preSigningOutput.data(); - EXPECT_EQ( - hex(preImageHash), - "020003050d044a62d0a4dfe5a037a15b59fa4d4d0d3ab81103a2c10a6da08a4d058611c057f6ed937bb447a670" - "0c9684d2e182b1a6661838a86cca7d0aac18be2e098b2106a7d517192c568ee08a845f73d29788cf035c3145b2" - "1ab344d8062ea940000006a7d517192c5c51218cc94c3d4af17f58daee089ba1fd44e3dbd98a00000000000000" - "00000000000000000000000000000000000000000000000000000000000b563fd13b46e844f12f54fa8a0e78c4" - "4d95dbae4953368b7135f1e0de111cb50204020001340000000080969800000000005000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000040301020324060000000d044a62d0a4" - "dfe5a037a15b59fa4d4d0d3ab81103a2c10a6da08a4d058611c0"); - // Simulate signature, normally obtained from signature server - const Data publicKeyData = Base58::decode(sender); - const PublicKey publicKey = PublicKey(publicKeyData, TWPublicKeyTypeED25519); - const Data nonceAccountPublicKeyData = Base58::decode(nonceAccount); - const PublicKey nonceAccountPublicKey = - PublicKey(nonceAccountPublicKeyData, TWPublicKeyTypeED25519); - const auto signature = Base58::decode( - "3dbiGHLsFqnwA1PXx7xmoikzv6v9g9BXvZts2126qyE163BypurkvgbDiF5RmrEZRiT2MG88v6xwyJTkhhDRuFc9"); - const auto nonceAccountSignature = Base58::decode( - "jFq4PbbEM1fuPbq5CkUYgzs7a21g6rvFkfLJAUUGP5QMKYhHBE6nB1dqtwaJsABgyUvrR8QjT2Ej73cXNz7Vur1"); - // Verify signature (pubkey & hash & signature) - EXPECT_TRUE(publicKey.verify(signature, TW::data(preImageHash))); - EXPECT_TRUE(nonceAccountPublicKey.verify(nonceAccountSignature, TW::data(preImageHash))); - /// Step 3: Compile transaction info - auto outputData = TransactionCompiler::compileWithSignatures( - coin, inputStrData, {signature, nonceAccountSignature}, - {publicKeyData, nonceAccountPublicKeyData}); - const auto ExpectedTx = - "3wu6xJSbb2NysVgi7pdfMgwVBT1knAdeCr9NR8EktJLoByzM4s9SMto2PPmrnbRqPtHwnpAKxXkC4vqyWY2dRBgdGG" - "CC1bep6qN5nSLVzpPYAWUSq5cd4gfYMAVriFYRRNHmYUnEq8vMn4vjiECmZoHrpabBj8HpXGqYBo87sbZa8ZPCxUcB" - "71hxXiHWZHj2rovx2kr75Uuv1buWXyW6M8uR4UNvQcPPvzVbwBG82RjDYTuancMSAxmrVNR8GLBQNhrCCYrZyte3EW" - "gEyMQxxfW8T3xNXqnbgdfvFJ3UjRBxXj3hrmv17xEivTjfs81aG2AAi24yiYrk8ep7eQqwDHVSArsrynnwVKVNUcCQ" - "CnSy7fuiuS7FweFX8DEN1K9BrfecHyWrF15fYzhkmWSs64aH6ZTYHWPv5znhFKYmAuopGwbsBEb2j5p8NS3iJZ2skb" - "2wi47n1rpLZfoCHWKxNiikkDUJTGQNcSDrGUMfeW5aGubJrCfecPKEo9Wo9kd36iSsxYPYSWNKrz2HTooa1rCRhqjX" - "D8dyX3bXGV8TK6W2sEgf4JkcDnNoWQLbindcP8XR"; - - EXPECT_EQ(outputData.size(), 583ul); - - { - Solana::Proto::SigningOutput output; - ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); - - EXPECT_EQ(output.encoded(), ExpectedTx); - EXPECT_EQ(output.encoded().size(), 580ul); - } - - { // Double check: check if simple signature process gives the same result. Note that private - // keys were not used anywhere up to this point. - Solana::Proto::SigningInput signingInput; - ASSERT_TRUE(signingInput.ParseFromArray(inputStrData.data(), (int)inputStrData.size())); - auto key = parse_hex("044014463e2ee3cc9c67a6f191dbac82288eb1d5c1111d21245bdc6a855082a1"); - auto nonceAccountKey = - parse_hex("2a9737aca3cde2dc0b4f3ae3487e3a90000490cb39fbc979da32b974ff5d7490"); - signingInput.set_private_key(key.data(), key.size()); - auto& aMessage = *signingInput.mutable_create_nonce_account(); - aMessage.set_nonce_account_private_key(nonceAccountKey.data(), nonceAccountKey.size()); - aMessage.set_rent(rent); - - Solana::Proto::SigningOutput output; - ANY_SIGN(signingInput, coin); - - ASSERT_EQ(output.encoded(), ExpectedTx); - } -} - -TEST(SolanaCompiler, CompileWithdrawNonceAccountWithSignatures) { - const auto coin = TWCoinTypeSolana; - /// Step 1: Prepare transaction input (protobuf) - auto input = TW::Solana::Proto::SigningInput(); - auto& message = *input.mutable_withdraw_nonce_account(); - auto nonceAccount = std::string("6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ"); - auto sender = std::string("sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH"); - auto recipient = std::string("3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe"); - uint64_t value = 10000000; - input.set_sender(sender); - input.set_recent_blockhash(std::string("5ccb7sRth3CP8fghmarFycr6VQX3NcfyDJsMFtmdkdU8")); - message.set_nonce_account(nonceAccount); - message.set_recipient(recipient); - message.set_value(value); - auto inputString = input.SerializeAsString(); - auto inputStrData = TW::Data(inputString.begin(), inputString.end()); - /// Step 2: Obtain preimage hash - const auto preImageHashesData = TransactionCompiler::preImageHashes(coin, inputStrData); - auto preSigningOutput = TW::Solana::Proto::PreSigningOutput(); - preSigningOutput.ParseFromArray(preImageHashesData.data(), (int)preImageHashesData.size()); - ASSERT_EQ(preSigningOutput.signers_size(), 1); - auto signer = preSigningOutput.signers(0); - EXPECT_EQ(signer, sender); - auto preImageHash = preSigningOutput.data(); - EXPECT_EQ(hex(preImageHash), - "010003060d044a62d0a4dfe5a037a15b59fa4d4d0d3ab81103a2c10a6da08a4d058611c057f6ed937bb4" - "47a6700c9684d2e182b1a6661838a86cca7d0aac18be2e098b2124c255a8bc3e8496217a2cd2a1894b9b" - "9dcace04fcd9c0d599acdaaea40a1b6106a7d517192c568ee08a845f73d29788cf035c3145b21ab344d8" - "062ea940000006a7d517192c5c51218cc94c3d4af17f58daee089ba1fd44e3dbd98a0000000000000000" - "00000000000000000000000000000000000000000000000000000000448e50d73f42e3163f5926922aad" - "d2bca6bdd91f97b3eb7b750e2cecfd810f6d01050501020304000c050000008096980000000000"); - // Simulate signature, normally obtained from signature server - const Data publicKeyData = Base58::decode(sender); - const PublicKey publicKey = PublicKey(publicKeyData, TWPublicKeyTypeED25519); - const auto signature = Base58::decode( - "MxbTCAUmBLiESDLK1NiK5ab41mL2SpAPKSbvGdYQQD5eKgAJRdFEJ8MV9HqBhDQHdsS2LG3QMQQVJp51ekGu6KM"); - // Verify signature (pubkey & hash & signature) - EXPECT_TRUE(publicKey.verify(signature, TW::data(preImageHash))); - /// Step 3: Compile transaction info - const Data outputData = TransactionCompiler::compileWithSignatures( - coin, inputStrData, {signature}, - {publicKeyData}); - const auto ExpectedTx = - "7gdEdDymvtfPfVgVvCTPzafmZc1Z8Zu4uXgJDLm8KGpLyPHysxFGjtFzimZDmGtNhQCh22Ygv3ZtPZmSbANbafikR3" - "S1tvujatHW9gMo35jveq7TxwcGoNSqc7tnH85hkEZwnDryVaiKRvtCeH3dgFE9YqPHxiBuZT5eChHJvVNb9iTTdMsJ" - "XMusRtzeRV45CvrLKUvsAH7SSWHYW6bGow5TbEJie4buuz2rnbeVG5cxaZ6vyG2nJWHNuDPWZJTRi1MFEwHoxst3a5" - "jQPv9UrG9rNZFCw4uZizVcG6HEqHWgQBu8gVpYpzFCX5SrhjGPZpbK3YmHhUEMEpJx3Fn7jX7Kt4t3hhhrieXppoqK" - "NuqjeNVjfEf3Q8dJRfuVMLdXYbmitCVTPQzYKWBR6ERqWLYoAVqjoAS2pRUw1nrqi1HR"; - EXPECT_EQ(outputData.size(), 431ul); - { - Solana::Proto::SigningOutput output; - ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); - - EXPECT_EQ(output.encoded(), ExpectedTx); - EXPECT_EQ(output.encoded().size(), 428ul); - } - { // Double check: check if simple signature process gives the same result. Note that private - // keys were not used anywhere up to this point. - Solana::Proto::SigningInput signingInput; - ASSERT_TRUE(signingInput.ParseFromArray(inputStrData.data(), (int)inputStrData.size())); - auto key = parse_hex("044014463e2ee3cc9c67a6f191dbac82288eb1d5c1111d21245bdc6a855082a1"); - signingInput.set_private_key(key.data(), key.size()); - - Solana::Proto::SigningOutput output; - ANY_SIGN(signingInput, coin); - - ASSERT_EQ(output.encoded(), ExpectedTx); - } -} - -TEST(SolanaCompiler, CompileCreateTokenAccountAndTransferWithSignatures) { - const auto coin = TWCoinTypeSolana; - /// Step 1: Prepare transaction input (protobuf) - auto input = TW::Solana::Proto::SigningInput(); - auto& message = *input.mutable_create_and_transfer_token_transaction(); - auto sender = std::string("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - auto token = std::string("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); - auto senderTokenAddress = std::string("5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf"); - auto recipientMainAddress = std::string("3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei"); - auto recipientTokenAddress = std::string("BwTAyrCEdkjNyGSGVNSSGh6phn8KQaLN638Evj7PVQfJ"); - uint64_t amount = 4000; - uint8_t decimals = 6; - input.set_sender(sender); - input.set_recent_blockhash(std::string("AfzzEC8NVXoxKoHdjXLDVzqwqvvZmgPuqyJqjuHiPY1D")); - message.set_recipient_main_address(recipientMainAddress); - message.set_token_mint_address(token); - message.set_recipient_token_address(recipientTokenAddress); - message.set_sender_token_address(senderTokenAddress); - message.set_amount(amount); - message.set_decimals(decimals); - auto inputString = input.SerializeAsString(); - auto inputStrData = TW::Data(inputString.begin(), inputString.end()); - /// Step 2: Obtain preimage hash - const auto preImageHashesData = TransactionCompiler::preImageHashes(coin, inputStrData); - auto preSigningOutput = TW::Solana::Proto::PreSigningOutput(); - preSigningOutput.ParseFromArray(preImageHashesData.data(), (int)preImageHashesData.size()); - ASSERT_EQ(preSigningOutput.signers_size(), 1); - auto signer = preSigningOutput.signers(0); - EXPECT_EQ(signer, sender); - auto preImageHash = preSigningOutput.data(); - EXPECT_EQ( - hex(preImageHash), - "0100060994c3890fa8d4bc04ab2a676d2cafea5cdc899ecd95a9cbe593e9df258759685aa287d36838b4ef65c2" - "c460d1a52498453c259cd6a35ca91064aaead28187ca69485a24ffb4070461bb6d7f1c8b758c6b2dc90029d551" - "b5fd4eacd82d65e302202544558bb05c2698f88852a7925c5c0ee5ea8711ddb3fe1262150283eee811633b442c" - "b3912157f13a933d0134282d032b5ffecd01a2dbf1b7790608df002ea700000000000000000000000000000000" - "0000000000000000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857e" - "ff00a906a7d517192c5c51218cc94c3d4af17f58daee089ba1fd44e3dbd98a000000008c97258f4e2489f1bb3d" - "1029148e0d830b5a1399daff1084048e7bd8dbe9f8598fb6d19edbbae20ebbc767fba1da4d4b40a4b97479fe52" - "6a82325cba7cee506802080700010304050607000604020401000a0ca00f00000000000006"); - // Simulate signature, normally obtained from signature server - const Data publicKeyData = Base58::decode(sender); - const PublicKey publicKey = PublicKey(publicKeyData, TWPublicKeyTypeED25519); - const auto signature = Base58::decode( - "pL1m11UEDWn3jMkrNMqLeGwNpKzmhQzJiYaCocgPy7vXKA1tnvEjJbuVq9hTeM9kqMAmxhRpwRY157jDgkRdUZw"); - // Verify signature (pubkey & hash & signature) - EXPECT_TRUE(publicKey.verify(signature, TW::data(preImageHash))); - /// Step 3: Compile transaction info - auto outputData = - TransactionCompiler::compileWithSignatures(coin, inputStrData, {signature}, {publicKeyData}); - const auto ExpectedTx = - "2qkvFTcMk9kPaHtd7idJ1gJc4zTkuYDUJsC67kXvHjv3zwEyUx92QyhhSeBjL6h3Zaisj2nvTWid2UD1N9hbg9Ty7v" - "SHLc7mcFVvy3yJmN9tz99iLKsf15rEeKUk3crXWLtKZEpcXJurN7vrxKwjQJnVob2RjyxwVfho1oNZ72BHvqToRM1W" - "2KbcYhxK4d9zB4QY5tR2dzgCHWqAjf9Yov3y9mPBYCQBtw2GewrVMDbg5TK81E9BaWer3BcEafc3NCnRfcFEp7ZUXs" - "GAgJYx32uUoJPP8ByTqBsp2JWgHyZhoz1WUUYRqWKZthzotErVetjSik4h5GcXk9Nb6kzqEf4nKEZ22eyrP5dv3eZM" - "uGUUpMYUT9uF16T72s4TTwqiWDPFkidD33tACx74JKGoDraHEvEeAPrv6iUmC675kMuAV4EtVspVc5SnKXgRWRxb4d" - "cH3k7K4ckjSxYZwg8UhTXUgPxA936jBr2HeQuPLmNVn2muA1HfL2DnyrobUP9vHpbL3HHgM2fckeXy8LAcjnoE9TTa" - "AKX32wo5xoMj9wJmmtcU6YbXN4KgZ"; - EXPECT_EQ(outputData.size(), 572ul); - { - Solana::Proto::SigningOutput output; - ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); - - EXPECT_EQ(output.encoded(), ExpectedTx); - EXPECT_EQ(output.encoded().size(), 569ul); - } - { // Double check: check if simple signature process gives the same result. Note that private - // keys were not used anywhere up to this point. - Solana::Proto::SigningInput signingInput; - ASSERT_TRUE(signingInput.ParseFromArray(inputStrData.data(), (int)inputStrData.size())); - auto key = Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); - signingInput.set_private_key(key.data(), key.size()); - - Solana::Proto::SigningOutput output; - ANY_SIGN(signingInput, coin); - - ASSERT_EQ(output.encoded(), ExpectedTx); - } -} - -TEST(SolanaCompiler, SolanaCompileAdvanceNonceAccountWithSignatures) { - const auto coin = TWCoinTypeSolana; - /// Step 1: Prepare transaction input (protobuf) - auto sender = std::string("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - auto input = TW::Solana::Proto::SigningInput(); - auto& message = *input.mutable_advance_nonce_account(); - auto nonceAccount = std::string("6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ"); - input.set_sender(sender); - input.set_recent_blockhash(std::string("4KQLRUfd7GEVXxAeDqwtuGTdwKd9zMfPycyAG3wJsdck")); - message.set_nonce_account(nonceAccount); - auto inputString = input.SerializeAsString(); - auto inputStrData = TW::Data(inputString.begin(), inputString.end()); - /// Step 2: Obtain preimage hash - const auto preImageHashesData = TransactionCompiler::preImageHashes(coin, inputStrData); - auto preSigningOutput = TW::Solana::Proto::PreSigningOutput(); - preSigningOutput.ParseFromArray(preImageHashesData.data(), (int)preImageHashesData.size()); - ASSERT_EQ(preSigningOutput.signers_size(), 1); - auto signer = preSigningOutput.signers(0); - EXPECT_EQ(signer, sender); - auto preImageHash = preSigningOutput.data(); - EXPECT_EQ( - hex(preImageHash), - "0100020494c3890fa8d4bc04ab2a676d2cafea5cdc899ecd95a9cbe593e9df258759685a57f6ed937bb447a670" - "0c9684d2e182b1a6661838a86cca7d0aac18be2e098b2106a7d517192c568ee08a845f73d29788cf035c3145b2" - "1ab344d8062ea940000000000000000000000000000000000000000000000000000000000000000000003149e6" - "70959884ea98bb33bca21c9505f1fc17b1d51ca59555a5d58c93f0f9c90103030102000404000000"); - // Simulate signature, normally obtained from signature server - const Data publicKeyData = Base58::decode(sender); - const PublicKey publicKey = PublicKey(publicKeyData, TWPublicKeyTypeED25519); - const auto signature = Base58::decode( - "2gwuvwJ3mdEsjA8Gid6FXYuSwa2AAyFY6Btw8ifwSc2SPsfKBnD859C5mX4tLy6zQFHhKxSMMsW49o3dbJNiXDMo"); - // Verify signature (pubkey & hash & signature) - EXPECT_TRUE(publicKey.verify(signature, TW::data(preImageHash))); - /// Step 3: Compile transaction info - auto outputData = - TransactionCompiler::compileWithSignatures(coin, inputStrData, {signature}, {publicKeyData}); - const auto ExpectedTx = - "7YPgNzjCnUd2zBb6ZC6bf1YaoLjhJPHixLUdTjqMjq1YdzADJCx2wsTTBFFrqDKSHXEL6ntRq8NVJTQMGzGH5AQRKw" - "tKtutehxesmtzkZCPY9ADZ4ijFyveLmTt7kjZXX7ZWVoUmKAqiaYsPTex728uMBSRJpV4zRw2yKGdQRHTKy2QFEb9a" - "cwLjmrbEgoyzPCarxjPhw21QZnNcy8RiYJB2mzZ9nvhrD5d2jB5TtdiroQPgTSdKFzkNEd7hJUKpqUppjDFcNHGK73" - "FE9pCP2dKxCLH8Wfaez8bLtopjmWun9cbikxo7LZsarYzMXvxwZmerRd1"; - EXPECT_EQ(outputData.size(), 330ul); - { - - Solana::Proto::SigningOutput output; - ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); - - EXPECT_EQ(output.encoded(), ExpectedTx); - EXPECT_EQ(output.encoded().size(), 327ul); - } - - { // Double check: check if simple signature process gives the same result. Note that private - // keys were not used anywhere up to this point. - Solana::Proto::SigningInput signingInput; - ASSERT_TRUE(signingInput.ParseFromArray(inputStrData.data(), (int)inputStrData.size())); - auto key = Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); - signingInput.set_private_key(key.data(), key.size()); - - Solana::Proto::SigningOutput output; - ANY_SIGN(signingInput, coin); - - ASSERT_EQ(output.encoded(), ExpectedTx); - } -} - -TEST(SolanaCompiler, CompileCreateTokenAccountAndTransferWithExternalFeePayerWithSignatures) { - const auto coin = TWCoinTypeSolana; - /// Step 1: Prepare transaction input (protobuf) - auto input = TW::Solana::Proto::SigningInput(); - auto& message = *input.mutable_create_and_transfer_token_transaction(); - auto sender = std::string("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - auto token = std::string("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); - auto senderTokenAddress = std::string("5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf"); - auto recipientMainAddress = std::string("E54BymfdhXQtzDSGtgiJayauEMdB2gJjPqoDjzfbCXwj"); - auto recipientTokenAddress = std::string("Ay7G7Yb7ZCebCQdG39FxD1nFNDtqFWJCBgfd5Ek7DDcS"); - auto feePayer = std::string("Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - uint64_t amount = 4000; - uint8_t decimals = 6; - input.set_sender(sender); - input.set_recent_blockhash(std::string("EsnN3ksLV6RLBW7qqgrvm2diwVJNTzL9QCuzm6tqWsU8")); - input.set_fee_payer(feePayer); - message.set_recipient_main_address(recipientMainAddress); - message.set_token_mint_address(token); - message.set_recipient_token_address(recipientTokenAddress); - message.set_sender_token_address(senderTokenAddress); - message.set_amount(amount); - message.set_decimals(decimals); - auto inputString = input.SerializeAsString(); - auto inputStrData = TW::Data(inputString.begin(), inputString.end()); - /// Step 2: Obtain preimage hash - const auto preImageHashesData = TransactionCompiler::preImageHashes(coin, inputStrData); - auto preSigningOutput = TW::Solana::Proto::PreSigningOutput(); - preSigningOutput.ParseFromArray(preImageHashesData.data(), (int)preImageHashesData.size()); - ASSERT_EQ(preSigningOutput.signers_size(), 2); - auto feepayerSigner = preSigningOutput.signers(0); - EXPECT_EQ(feepayerSigner, feePayer); - auto signer = preSigningOutput.signers(1); - EXPECT_EQ(signer, sender); - auto preImageHash = preSigningOutput.data(); - EXPECT_EQ( - hex(preImageHash), - "0200060acb2af089b56a557737bc1718e0cbf232cf5b02e14ee0aa7c6675233f5f6f9b5794c3890fa8d4bc04ab2a676d2cafea5cdc899ecd95a9cbe593e9df258759685a9418c9576a9c00c6bd8fc223f471573f7172488de10aa84dbf63c53a20bae717485a24ffb4070461bb6d7f1c8b758c6b2dc90029d551b5fd4eacd82d65e30220c231dc02f482980f7d9915c1ecf53374091d38c060b49487f9c5d932e077ed763b442cb3912157f13a933d0134282d032b5ffecd01a2dbf1b7790608df002ea7000000000000000000000000000000000000000000000000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a906a7d517192c5c51218cc94c3d4af17f58daee089ba1fd44e3dbd98a000000008c97258f4e2489f1bb3d1029148e0d830b5a1399daff1084048e7bd8dbe9f859ce2a4331bce3670e6ea8bedff5908c6d91f833a31a7fdeac16978c261a1801d502090700020405060708000704030502010a0ca00f00000000000006"); - // Simulate signature, normally obtained from signature server - // Verify signature (pubkey & hash & signature) - const Data feePayerPublicKeyData = Base58::decode(feePayer); - const PublicKey feePayerPublicKey = PublicKey(feePayerPublicKeyData, TWPublicKeyTypeED25519); - const auto signature1 = Base58::decode( - "7GZGFT2VA4dpJBBwjiMqj1o8yChDvoCsqnQ7xz4GxY513W3efxRqbB8y7g4tH2GEGQRx4vCkKipG1sMaDEen1A2"); - EXPECT_TRUE(feePayerPublicKey.verify(signature1, TW::data(preImageHash))); - - const Data publicKeyData = Base58::decode(sender); - const PublicKey publicKey = PublicKey(publicKeyData, TWPublicKeyTypeED25519); - const auto signature2 = Base58::decode( - "3n7RHTCBAtnFVuDn5eRbyQB24h6AqajJi5nGMPrfnUVFUDh2Cb8AoaJ7mVtjnv73V4HaJCzSwCLAj3zcGEaFftWZ"); - EXPECT_TRUE(publicKey.verify(signature2, TW::data(preImageHash))); - - /// Step 3: Compile transaction info - auto outputData = - TransactionCompiler::compileWithSignatures(coin, inputStrData, {signature1, signature2}, {feePayerPublicKeyData, publicKeyData}); - const auto ExpectedTx = - "5sxFkQYd2FvqRU64N79A6xjJKNkgUsEEg2wKgai2NiK7A7hF3q5GYEbjQsYBG9S2MejwTENbwHzvypaa3D3cEkxvVTg19aJFWdCtXQiz42QF5fN2MuAb6eJR4KHFnzCtxxnYGtN9swZ5B5cMSPCffCRZeUTe3kooRmbTYPvSaemU6reVSM7X2beoFKPd2svrLFa8XnvhBwL9EiFWQ9WhHB2cDV7KozCnJAW9kdNDR4RbfFQxboANGo3ZGE5ddcZ6YdomATKze1TtHj2qzJEJRwxsRr3iM3iNFb4Eav5Q2n71KUriRf73mo44GQUPbQ2LvpZKf4V6M2PzxJwzBo7FiFZurPmsanT3U5efEsKnnueddbiLHedc8JXc1d3Z53sFxVGJpsGA8RR6thse9wUvaEWqXVtPbNA6NMao9DFGD6Dudza9pJXSobPc7mDHZmVmookf5vi6Lb9Y1Q4EgcEPQmbaDnKGGB6uGfZe629i3iKXRzAd2dB7mKfffhDadZ8S1eYGT3dhddV3ExRxcqDP9BAGQT3rkRw1JpeSSi7ziYMQ3vn4t3okdgQSq6rrpbPDUNG8tLSHFMAq3ydnh4Cb4ECKkYoz9SFAnXACUu4mWETxijuKMK9kHrTqPGk9weHTzobzCC8q8fcPWV3TcyUyMxsbVxh5q1p5h5tWfD9td5TZJ2HEUbTop2dA53ZF"; - EXPECT_EQ(outputData.size(), 703ul); - { - Solana::Proto::SigningOutput output; - ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); - - EXPECT_EQ(output.encoded(), ExpectedTx); - EXPECT_EQ(output.encoded().size(), 700ul); - } - { // Double check: check if simple signature process gives the same result. Note that private - // keys were not used anywhere up to this point. - Solana::Proto::SigningInput signingInput; - ASSERT_TRUE(signingInput.ParseFromArray(inputStrData.data(), (int)inputStrData.size())); - auto key = Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); - signingInput.set_private_key(key.data(), key.size()); - auto feePayerKey = Base58::decode("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"); - signingInput.set_fee_payer_private_key(feePayerKey.data(), feePayerKey.size()); - - Solana::Proto::SigningOutput output; - ANY_SIGN(signingInput, coin); - - ASSERT_EQ(output.encoded(), ExpectedTx); - } -} - -TEST(SolanaCompiler, CompileTokenTransferWithExternalFeePayerWithSignatures) { - const auto coin = TWCoinTypeSolana; - /// Step 1: Prepare transaction input (protobuf) - auto input = TW::Solana::Proto::SigningInput(); - auto& message = *input.mutable_token_transfer_transaction(); - auto sender = std::string("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - auto token = std::string("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); - auto senderTokenAddress = std::string("5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf"); - // auto recipientMainAddress = std::string("E54BymfdhXQtzDSGtgiJayauEMdB2gJjPqoDjzfbCXwj"); - auto recipientTokenAddress = std::string("AZapcpAZtEL1gQuC87F2L1hSfAZnAvNy1hHtJ8DJzySN"); - auto feePayer = std::string("Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - uint64_t amount = 4000; - uint8_t decimals = 6; - input.set_sender(sender); - input.set_recent_blockhash(std::string("GwB5uixiTQUG2Pvo6fWAaNQmz41Jt4WMEPD7b83wvHkX")); - input.set_fee_payer(feePayer); - message.set_token_mint_address(token); - message.set_recipient_token_address(recipientTokenAddress); - message.set_sender_token_address(senderTokenAddress); - message.set_amount(amount); - message.set_decimals(decimals); - auto inputString = input.SerializeAsString(); - auto inputStrData = TW::Data(inputString.begin(), inputString.end()); - /// Step 2: Obtain preimage hash - const auto preImageHashesData = TransactionCompiler::preImageHashes(coin, inputStrData); - auto preSigningOutput = TW::Solana::Proto::PreSigningOutput(); - preSigningOutput.ParseFromArray(preImageHashesData.data(), (int)preImageHashesData.size()); - ASSERT_EQ(preSigningOutput.signers_size(), 2); - auto feepayerSigner = preSigningOutput.signers(0); - EXPECT_EQ(feepayerSigner, feePayer); - auto signer = preSigningOutput.signers(1); - EXPECT_EQ(signer, sender); - auto preImageHash = preSigningOutput.data(); - EXPECT_EQ( - hex(preImageHash), - "02000206cb2af089b56a557737bc1718e0cbf232cf5b02e14ee0aa7c6675233f5f6f9b5794c3890fa8d4bc04ab2a676d2cafea5cdc899ecd95a9cbe593e9df258759685a485a24ffb4070461bb6d7f1c8b758c6b2dc90029d551b5fd4eacd82d65e302208e12027e9261a6a276b5ff00ddecfda567ff3ae510a5b47045086ad1d50cab573b442cb3912157f13a933d0134282d032b5ffecd01a2dbf1b7790608df002ea706ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a9ecc01200a43c87ad04ab8b382c0934a54e585e7dcd9cdef6d1cacd52718981c4010504020403010a0ca00f00000000000006"); - // Simulate signature, normally obtained from signature server - // Verify signature (pubkey & hash & signature) - const Data feePayerPublicKeyData = Base58::decode(feePayer); - const PublicKey feePayerPublicKey = PublicKey(feePayerPublicKeyData, TWPublicKeyTypeED25519); - const auto signature1 = Base58::decode( - "2LbovMDuKoR2LFcV5NbK9bCQZcTG99W6VE1urvdWFWvRhNg9ocDGhayyeBGisoqZgYZtcD3b6LJDTmPx9Gp3T6qd"); - EXPECT_TRUE(feePayerPublicKey.verify(signature1, TW::data(preImageHash))); - - const Data publicKeyData = Base58::decode(sender); - const PublicKey publicKey = PublicKey(publicKeyData, TWPublicKeyTypeED25519); - const auto signature2 = Base58::decode( - "2hUHMS9rwbUbXrpC7sK7utL2M4soTyQ7EX3sBYvdee9wraJvYoPH2XjovHDn8eRFY8z5uCx9DCj2Zfjpmzfa81Db"); - EXPECT_TRUE(publicKey.verify(signature2, TW::data(preImageHash))); - - /// Step 3: Compile transaction info - auto outputData = - TransactionCompiler::compileWithSignatures(coin, inputStrData, {signature1, signature2}, {feePayerPublicKeyData, publicKeyData}); - const auto ExpectedTx = - "qjgNVBmoPDHNTN2ENQfxNVE57jWXpqdmu5GQX4msA7iK8ZRAnKpvbusQagv8CZGyNYti23p9jBsjTSx75ZU26UW5vgC8D88pusW8W5dp1ERo5DSfurMSYJ6afgQHdcuzn7exb8znSm6uV4y1cWgBRcuAGdg3wRpVhP8HEB1EeKgzjYVWvMSy6yR7qVrSL6BxHG6eiAMyahLFbEt4qBqLEdxxY7Dt4DyydVYmG2ZVtheaMHD3ACwCjpyPLXj399wxSgGXQQFGtzEJQw9awVezmJ4wZk6W4dDpXQvdKYaqUvwTwRZsQB5o2iekPWZXR9xvHiMLjMVBPzYgcU14ZSaCbqSNVv2pAJxP1sMvxZMNMzZPttPxCsDDGq9biC7exXwzesXSnZ3rsgEYeZtkUiBHAxR4rYqBpA6VzLs1bPx8MPTvr9mhNi2ezMBbg2nEfHV6Fz7H7rEY2g3jDtRz35Vmgits8s9RKi3kb73WtGUieRiXjiqkNhpvKkST1oEYRQ9"; - EXPECT_EQ(outputData.size(), 514ul); - { - Solana::Proto::SigningOutput output; - ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); - - EXPECT_EQ(output.encoded(), ExpectedTx); - EXPECT_EQ(output.encoded().size(), 511ul); - } - { // Double check: check if simple signature process gives the same result. Note that private - // keys were not used anywhere up to this point. - Solana::Proto::SigningInput signingInput; - ASSERT_TRUE(signingInput.ParseFromArray(inputStrData.data(), (int)inputStrData.size())); - auto key = Base58::decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); - signingInput.set_private_key(key.data(), key.size()); - auto feePayerKey = Base58::decode("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"); - signingInput.set_fee_payer_private_key(feePayerKey.data(), feePayerKey.size()); - - Solana::Proto::SigningOutput output; - ANY_SIGN(signingInput, coin); - - ASSERT_EQ(output.encoded(), ExpectedTx); - } -} - } // namespace TW::Solana::tests diff --git a/tests/chains/Solana/TransactionTests.cpp b/tests/chains/Solana/TransactionTests.cpp deleted file mode 100644 index 7fcefceb521..00000000000 --- a/tests/chains/Solana/TransactionTests.cpp +++ /dev/null @@ -1,607 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "Solana/Address.h" -#include "Solana/Program.h" -#include "Solana/Transaction.h" - -#include "Base58.h" -#include "BinaryCoding.h" -#include "HexCoding.h" - -#include - -namespace TW::Solana { - -TEST(SolanaTransaction, TransferMessageData) { - auto from = Address("6eoo7i1khGhVm8tLBMAdq4ax2FxkKP4G7mCcfHyr3STN"); - auto to = Address("56B334QvCDMSirsmtEJGfanZm8GqeQarrSjdAb2MbeNM"); - auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); - auto transaction = Transaction(from, to, 42, recentBlockhash); - - auto expectedHex = - "0100010353f9d600fe925083bb399907ea648d23a6a081fc7e9059202fd725f7edd281dd3cc1ff9ba3c7a876c8" - "082df2f8a36ea9342ce3819dd4b6fa72d4a18e04a5363a00000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000010202" - "00010c020000002a00000000000000"; - ASSERT_EQ(hex(transaction.messageData()), expectedHex); -} - -TEST(SolanaTransaction, TransferSerializeTransaction) { - auto from = Address("41a5jYky56M6EWDsFfLaZRxoRtgAJSRWxJnxaJNJELn5"); - auto to = Address("4iSnyfDKaejniaPc2pBBckwQqV3mDS93go15NdxWJq2y"); - auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); - auto transaction = Transaction(from, to, 42, recentBlockhash); - auto signature = Base58::decode("46SRiQGvtPb1iivDfnuC3dW1GzXkfQPTjdUyvFqF2sdPvFrsfx94fys2xpNKR6UiAj7RgKWdJG6mEfe85up6i1JT"); - transaction.signatures.clear(); - transaction.signatures.push_back(signature); - - auto expectedString = - "5SiHeYyuDgjHxWHbYXSSPfmYc8s7EYZ8bdZ7j15z9Bj1yyZA3Bia9uWkRdXVkuqifXiiQj6fVKy" - "7UkCL5kvv6iKrfjWTZ3szMVssTFxgJ7p8UJ7Mgg2uhHejVJvbzbiHHLbNVuJFs6kBxddnJ2yjWU" - "Cp2dYJgjmphfA8hRHHdPH4Rv6znxEhD8q9XY4nByRPL7oMCo32oxeJn5rGbUZdCkapRUXG7zU9w" - "hv6KjBktcUQZRCahhowGJT4UM5yCNCsUcqY9yan7UxqPyJgaFPuq4duqWJtQ39bTQ36X"; - ASSERT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, TransferTransactionPayToSelf) { - auto from = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - auto to = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); - auto transaction = Transaction(from, to, 42, recentBlockhash); - auto signature = Base58::decode( - "3CFWDEK51noPJP4v2t8JZ3qj7kC7kLKyws9akfHMyuJnQ35EtzBptHqvaHfeswiLsvUSxzMVNoj4CuRxWtDD9zB1"); - transaction.signatures.clear(); - transaction.signatures.push_back(signature); - - auto expectedString = - "EKUmihvvUPKVN4GSCFwZRtz8WiyAuPvthW69Smo19SCjcPLQ6T7EVZd1HU71WAoe1bfgmPNS5JhU7ZLA9XKG3qbZqe" - "EFJ1xmRwW9ZKw8SKMAL6VRWxp87oLu7PSmf5b8R34vCaww3XLKtZkoP49a7TUK31DqPN5xJCceMB3BZJyaojQaKU8n" - "UkzSGf89LY6abZXp9krKAebvc6bSMzTP8SHSvbmZbf3VtejmpQeN9X6e7WVDn6oDa2bGT"; - ASSERT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, TransferWithMemoAndReferenceTransaction) { - const auto from = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - const auto to = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); - const auto memo = "HelloSolana73"; - std::vector
references = {Address("GaeTAQZyhVEocTC7iY8GztSyY5cBAJTkAUUA1kLFLMV")}; - auto transaction = Transaction(from, to, 42, recentBlockhash, memo, references); - auto signature = Base58::decode("3CFWDEK51noPJP4v2t8JZ3qj7kC7kLKyws9akfHMyuJnQ35EtzBptHqvaHfeswiLsvUSxzMVNoj4CuRxWtDD9zB1"); - transaction.signatures.clear(); - transaction.signatures.push_back(signature); - - auto expectedString = - "3pzQEdU38uMQgegTyRsRLi23NK4YokgZeSVLXYzFB7HShqZZH8FdBLqj6CeA2d2L8oR9KF2UaJPWbE8YBFmSdaafeg" - "oSXJtyj7ciwTjk5ieSXnPXtqH1TEcnMntZATg7gKpeFg6iehqdSUtZuQD1PGmHA1TrzzqLpRSRrc1sqPz8EpSJcQr1" - "Y41B1XCEAfSJDfcuNKrfFrnQaVtRz6tseQfd9uXNYNuR1NQSepWdav5wQiohLUMDiZtxuwb7FQkQ68WE1FDsHmd4Jp" - "bWKmDEjz7HFyQY37vf6NBJyX5qWJpFMSg5qGKWvhNCDM32yM4A7HhPeoTWEywE5CXcNmQqdbRt4BzF1A11uqv4etW" - "j"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, StakeSerializeTransactionV2) { - auto signer = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); - auto programId = Address("Stake11111111111111111111111111111111111111"); - auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); - auto stakeAddress = StakeProgram::addressFromRecentBlockhash(signer, recentBlockhash, programId); - auto message = LegacyMessage::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); - auto transaction = Transaction(message); - auto signature = Base58::decode( - "2GXRrZMMWTaY8ycwFTLFojAVZ1EepFqnVGW7b5bBuuKPiVrpaPXMAwyYsSmYc2okCa1MuJjNguu1emSJRtZxVdwt"); - transaction.signatures.clear(); - transaction.signatures.push_back(signature); - - auto expectedString = - "W1EAswaWK7mF4r9eZ2hHBZnfPnqLuNPiYkEMzFbwQsgSQu6XbSTL9AN92iyMbAMxPoRpt9ipUyztrmszAnm688N3k7" - "uhiKn2osm9nxi6YkGLfu31jHTSu7mn3RtmenV3qopfPDAM92cXrzUhKGWtQ6cATeQh8i8ZfHpmjyuik7Eg3SQ4sa25" - "43CmcozzjmTTWThThuLdvFCZJzBeRBFWLujqjbs5mA66XVtiDwsEqYByznoo4BN45XUHxnZebmPfo4hi5sf27UkhzP" - "Hik371BGxbVDexQp4y5nCEHy8ybfNCvMPLr2SEBiWSifwPkmwYN3hGCkBpqLoHCCiRcyJuRHW8hSDFR4JPQ3Xe3FGf" - "pgbayaawZigUnFuPGSpoGrURZRoLCzc6V4ApqcJbmzFhg5zJz2yTX5GvQSYWLFnTKbPYcgBNpdyMLJTivonrKtgkfd" - "ymZVKjDwnHUApC7WD4L9mqzTf1dzR61Fxhu3Rdh8ECiVEDgB1wkWZWkTKEdANmtaYLKCMUs3n4VhuZbSFLEiTg7yRW" - "M2pjBgiBB4qywbF7SE75UtzSFCaDnn27mKkxRBqZEGEgfpEoK2AxjsiCZEZxfLeyZFbwWe7xasmNiXr6CnAQhwsmxJ" - "k79h7SYmaje76JLxHVX5gbQmLfn5bc1xthS3YhteSovQ8xYq1jiHCfsXRwbxKrNA4kVMiSa6spoU9AhFL8cDAZjAqo" - "U4YRwBihZVhXSFCRnYAK8FabzEv1M44EeHX1sfMG8T1U7y3DEjom7jv6rqZfLumWpbXDTqanB7zTbTjGyDcBBf21ed" - "jpZzBZ7osS5fTVYJ5mZBSvjjhuGkUgZZWgYozAKvdyyrJH6UdcPvNm2XgMRYJxqyCin1zhCeQ25vK1H8Jj"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, StakeSerializeTransactionV1) { - auto signer = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); - auto programId = Address("Stake11111111111111111111111111111111111111"); - auto recentBlockhash = Base58::decode("11111111111111111111111111111111"); - auto stakeAddress = StakeProgram::addressFromValidatorSeed(signer, voteAddress, programId); - auto message = LegacyMessage::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); - auto transaction = Transaction(message); - auto signature = Base58::decode( - "2GXRrZMMWTaY8ycwFTLFojAVZ1EepFqnVGW7b5bBuuKPiVrpaPXMAwyYsSmYc2okCa1MuJjNguu1emSJRtZxVdwt"); - transaction.signatures.clear(); - transaction.signatures.push_back(signature); - - auto expectedString = - "W1EAswaWK7mF4r9eZ2hHBZnfPnqLuNPiYkEMzFbwQsgSQu6XbSTL9AN92iyMbAMxPoRpt9ipUyztrmszAnm688N3k7" - "uhiKn2osm9nxi6YkGLfu31jHTSu7mn3RtmenV3qopfPDAM7jtGoYQFb7eFVbujUb6tbeQ9UqLJq1sJ7uMZ4wqecmQP" - "ouDmJnpmJk4CHMzLnPNTwyGmGio6sYAS3xKZ7DFXvjwGPuD8PyYHSfdPro1p3jy9igPZNAbQ6fgK7LL3sERKCUdvPy" - "7k14xgHbtsVy2mu54LY5c8F9sFst2uzQiTsXRTdjPFAyCVwB5pccNVotCrJ6Q2aKSC2D2knVH7LgWzSBMSreJG75xy" - "ATneu922wSzz7QJDieqhDtdePtSbPtoCdtPNmDfdaeDbHxVAxMios9F7RSRmH2dq86NfWDvF8TuEbYY7gPnygz6jGv" - "wfqSSoSnY8TnUhhceC7wJSMc8Hcf1kyfi8dqKm7rF57YjnrQoMmL5bWqJLKoJtdfFu24ceQN21k38U2tUMWJaBASWu" - "kgTJUbNSCemNPZt4P3cNbeB3L1wBj4GEYXVTbTFYKME5JscU5RsnkMJZZ1PgxSi63HT4hwQLok4c18UdJgzMFu1njp" - "Zj3Sw76mwV3ea7ruHnP4yyM3YhUGbNjpx5fAcnvdLcXChdsgeUpJhutME6V86Rk2EEskoJeD3qNWi3hvfQx172hZRH" - "yKyr29Ts1uLQxcMJq7oeQUxvTfXxSe6cBuPJUDFkAET3qpS7rWM7rvQQ8rDLQF5QvcJnrYTq12pVgw28WXdgi45811" - "a7DWHGuwHRj5FJdLQAHkKe4EXVeTCdbYHREVwuyTJgAvb8SXjRE5a9n3qpRDr7iEd5UDZKB5HgvMsMYWh5"; - ASSERT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, CreateTokenAccountTransaction) { - auto signer = Address("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - auto tokenAddress = Address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - auto recentBlockhash = Base58::decode("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); - auto message = LegacyMessage::createTokenCreateAccount(signer, signer, token, tokenAddress, recentBlockhash); - EXPECT_EQ(message.header.numRequiredSignatures, 1); - EXPECT_EQ(message.header.numReadOnlySignedAccounts, 0); - EXPECT_EQ(message.header.numReadOnlyUnsignedAccounts, 5); - ASSERT_EQ(message.accountKeys.size(), 7ul); - EXPECT_EQ(message.accountKeys[0].string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - EXPECT_EQ(message.accountKeys[1].string(), "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - EXPECT_EQ(message.accountKeys[2].string(), "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - EXPECT_EQ(message.accountKeys[3].string(), "11111111111111111111111111111111"); - EXPECT_EQ(message.accountKeys[4].string(), "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); - EXPECT_EQ(message.accountKeys[5].string(), "SysvarRent111111111111111111111111111111111"); - EXPECT_EQ(message.accountKeys[6].string(), "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); - EXPECT_EQ(Base58::encode(message.mRecentBlockHash), "9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); - ASSERT_EQ(message.instructions.size(), 1ul); - EXPECT_EQ(message.instructions[0].programId.string(), "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); - ASSERT_EQ(message.instructions[0].accounts.size(), 7ul); - EXPECT_EQ(message.instructions[0].accounts[0].account.string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - EXPECT_EQ(message.instructions[0].accounts[1].account.string(), "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - EXPECT_EQ(message.instructions[0].accounts[2].account.string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - EXPECT_EQ(message.instructions[0].accounts[3].account.string(), "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - EXPECT_EQ(message.instructions[0].accounts[4].account.string(), "11111111111111111111111111111111"); - EXPECT_EQ(message.instructions[0].accounts[5].account.string(), "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); - EXPECT_EQ(message.instructions[0].accounts[6].account.string(), "SysvarRent111111111111111111111111111111111"); - auto transaction = Transaction(message); - transaction.signatures.clear(); - auto signature = Base58::decode("3doYbPs5rES3TeDSrntqUvMgXCDE2ViJX2SFhLtiptVNkqPuixXs1SwU5LUZ3KwHnCzDUth6BRr3vU3gqnuUgRvQ"); - transaction.signatures.push_back(signature); - - auto expectedString = - // test data obtained from spl-token create-account - "CKzRLx3AQeVeLQ7T4hss2rdbUpuAHdbwXDazxtRnSKBuncCk3WnYgy7XTrEiya19MJviYHYdTxi9gmWJY8qnR2vHVn" - "H2DbPiKA8g72rD3VvMnjosGUBBvCwbBLge6FeQdgczMyRo9n5PcHvg9yJBTJaEEvuewyBVHwCGyGQci7eYd26xtZtC" - "jAjwcTq4gGr3NZbeRW6jZp6j6APuew7jys4MKYRV4xPodua1TZFCkyWZr1XKzmPh7KTavtN5VzPDA8rbsvoEjHnKzj" - "B2Bszs6pDjcBFSHyQqGsHoF8XPD35BLfjDghNtBmf9cFqo5axa6oSjANAuYg6cMSP4Hy28waSj8isr6gQjE315hWi3" - "W1swwwPcn322gYZx6aMAcmjczaxX9aktpHYgZxixF7cYWEHxJs5QUK9mJePu9Xc6yW75UB4Ynx6dUgaSTEUzoQthF2" - "TN3xXwu1"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, TransferTokenTransaction_3vZ67C) { - auto signer = Address("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - auto senderTokenAddress = Address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - auto recipientTokenAddress = Address("3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei"); - uint64_t amount = 4000; - uint8_t decimals = 6; - auto recentBlockhash = Base58::decode("CNaHfvqePgGYMvtYi9RuUdVxDYttr1zs4TWrTXYabxZi"); - auto message = LegacyMessage::createTokenTransfer(signer, token, senderTokenAddress, recipientTokenAddress, amount, decimals, recentBlockhash); - EXPECT_EQ(message.header.numRequiredSignatures, 1); - EXPECT_EQ(message.header.numReadOnlySignedAccounts, 0); - EXPECT_EQ(message.header.numReadOnlyUnsignedAccounts, 2); - ASSERT_EQ(message.accountKeys.size(), 5ul); - ASSERT_EQ(message.instructions.size(), 1ul); - EXPECT_EQ(message.instructions[0].programId.string(), "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); - ASSERT_EQ(message.instructions[0].accounts.size(), 4ul); - auto transaction = Transaction(message); - transaction.signatures.clear(); - auto signature = Base58::decode("3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg"); - transaction.signatures.push_back(signature); - - auto expectedString = - // https://explorer.solana.com/tx/3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg - // test data obtained from spl-token transfer - "PGfKqEaH2zZXDMZLcU6LUKdBSzU1GJWJ1CJXtRYCxaCH7k8uok38WSadZfrZw3TGejiau7nSpan2GvbK26hQim24jR" - "e2AupmcYJFrgsdaCt1Aqs5kpGjPqzgj9krgxTZwwob3xgC1NdHK5BcNwhxwRtrCphGEH7zUFpGFrFrHzgpf2KY8FvP" - "iPELQyxzTBuyNtjLjMMreehSKShEjD9Xzp1QeC1pEF8JL6vUKzxMXuveoEYem8q8JiWszYzmTMfDk13JPgv7pXFGMq" - "DV3yNGCLsWccBeSFKN4UKECre6x2QbUEiKGkHkMc4zQwwyD8tGmEMBAGm339qdANssEMNpDeJp2LxLDStSoWShHnot" - "crH7pUa94xCVvCPPaomF"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, CreateNonceAccountTransaction) { - auto sender = Address("sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH"); - auto nonceAccount = Address("6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ"); - uint64_t rent = 10000000; - auto recentBlockhash = Base58::decode("mFmK2xFMhzJJaUN5cctfdCizE9dtgcSASSEDh1Yzmat"); - auto message = LegacyMessage::createNonceAccount(sender, nonceAccount, rent, recentBlockhash); - EXPECT_EQ(message.header.numRequiredSignatures, 2); - EXPECT_EQ(message.header.numReadOnlySignedAccounts, 0); - EXPECT_EQ(message.header.numReadOnlyUnsignedAccounts, 3); - ASSERT_EQ(message.accountKeys.size(), 5ul); - ASSERT_EQ(message.instructions.size(), 2ul); - EXPECT_EQ(message.instructions[0].programId.string(), "11111111111111111111111111111111"); - ASSERT_EQ(message.instructions[0].accounts.size(), 2ul); - EXPECT_EQ(message.instructions[1].programId.string(), "11111111111111111111111111111111"); - ASSERT_EQ(message.instructions[1].accounts.size(), 3ul); - auto transaction = Transaction(message); - transaction.signatures.clear(); - transaction.signatures.push_back(Base58::decode("3dbiGHLsFqnwA1PXx7xmoikzv6v9g9BXvZts2126qyE163Bypur" - "kvgbDiF5RmrEZRiT2MG88v6xwyJTkhhDRuFc9")); - transaction.signatures.push_back(Base58::decode( - "jFq4PbbEM1fuPbq5CkUYgzs7a21g6rvFkfLJAUUGP5QMKYhHBE6nB1dqtwaJsABgyUvrR8QjT2Ej73cXNz7Vur1")); - auto expectedString = - "3wu6xJSbb2NysVgi7pdfMgwVBT1knAdeCr9NR8EktJLoByzM4s9SMto2PPmrnbRqPtHwnpAKxXkC4vqyWY2dRBgdGG" - "CC1bep6qN5nSLVzpPYAWUSq5cd4gfYMAVriFYRRNHmYUnEq8vMn4vjiECmZoHrpabBj8HpXGqYBo87sbZa8ZPCxUcB" - "71hxXiHWZHj2rovx2kr75Uuv1buWXyW6M8uR4UNvQcPPvzVbwBG82RjDYTuancMSAxmrVNR8GLBQNhrCCYrZyte3EW" - "gEyMQxxfW8T3xNXqnbgdfvFJ3UjRBxXj3hrmv17xEivTjfs81aG2AAi24yiYrk8ep7eQqwDHVSArsrynnwVKVNUcCQ" - "CnSy7fuiuS7FweFX8DEN1K9BrfecHyWrF15fYzhkmWSs64aH6ZTYHWPv5znhFKYmAuopGwbsBEb2j5p8NS3iJZ2skb" - "2wi47n1rpLZfoCHWKxNiikkDUJTGQNcSDrGUMfeW5aGubJrCfecPKEo9Wo9kd36iSsxYPYSWNKrz2HTooa1rCRhqjX" - "D8dyX3bXGV8TK6W2sEgf4JkcDnNoWQLbindcP8XR"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, CreateWithdrawNonceAccountToSelf) { - auto authorizer = Address("sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH"); - auto nonceAccount = Address("6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ"); - uint64_t value = 10000000; - auto recentBlockhash = Base58::decode("5W1cvn4AtWsm2NJfJ52DoXdG36pdS3wHY3Gmkut9sENH"); - auto message = LegacyMessage::createWithdrawNonceAccount(authorizer, nonceAccount, authorizer, value, - recentBlockhash); - EXPECT_EQ(message.header.numRequiredSignatures, 1); - EXPECT_EQ(message.header.numReadOnlySignedAccounts, 0); - EXPECT_EQ(message.header.numReadOnlyUnsignedAccounts, 3); - ASSERT_EQ(message.accountKeys.size(), 5ul); - ASSERT_EQ(message.instructions.size(), 1ul); - EXPECT_EQ(message.instructions[0].programId.string(), "11111111111111111111111111111111"); - ASSERT_EQ(message.instructions[0].accounts.size(), 5ul); - auto transaction = Transaction(message); - transaction.signatures.clear(); - transaction.signatures.push_back(Base58::decode("3JtHs3Ra2LyYCf7a3HFri3ZDa3D4c2G6x3EiyiCDpWjb11QPYfH" - "nU6gqkQaHURdD2aKUzVSBa3JHfzsbiUPi4iUb")); - auto expectedString = - "XVVzGHC7YUTfBwbaQ8y8S7ypc8WCgjpnLd8Yxcp2Vg3SAgtYWsfiFBfrwS6CbwSQa5QBLQ997ohS9wHSX8wyzQdkN6" - "k8ZidSMHdjkHSdFnHGoJBijCv3gURRti2XiaY7aQoKQUPKvwQn31TEKRBgGUQDMtLn9yFqVZ7UMGXtvwvwG7h55VCp" - "Sb32Saus6rSVfNTAfc4UAmwvkbo2gk9rTF5eT9U81uzG2XsBZYSmyqYJQxqaf5ySQD77mwnJo9TpwCX6KfFXnjqyav" - "LFMZb8FeEuXcq17MQKf55w6gqiSssXvQov62GtrUVaVqGg5KYvSTa98Kmu2XHPQcJpgCepft32F6wy2bfDUHjooLqL" - "pp6ZjYqnnWfvVEuSqL6UFjiF"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, CreateWithdrawNonceAccount) { - auto authorizer = Address("sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH"); - auto nonceAccount = Address("6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ"); - auto to = Address("3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe"); - uint64_t value = 10000000; - auto recentBlockhash = Base58::decode("5ccb7sRth3CP8fghmarFycr6VQX3NcfyDJsMFtmdkdU8"); - auto message = - LegacyMessage::createWithdrawNonceAccount(authorizer, nonceAccount, to, value, recentBlockhash); - EXPECT_EQ(message.header.numRequiredSignatures, 1); - EXPECT_EQ(message.header.numReadOnlySignedAccounts, 0); - EXPECT_EQ(message.header.numReadOnlyUnsignedAccounts, 3); - ASSERT_EQ(message.accountKeys.size(), 6ul); - ASSERT_EQ(message.instructions.size(), 1ul); - EXPECT_EQ(message.instructions[0].programId.string(), "11111111111111111111111111111111"); - ASSERT_EQ(message.instructions[0].accounts.size(), 5ul); - auto transaction = Transaction(message); - transaction.signatures.clear(); - transaction.signatures.push_back(Base58::decode( - "MxbTCAUmBLiESDLK1NiK5ab41mL2SpAPKSbvGdYQQD5eKgAJRdFEJ8MV9HqBhDQHdsS2LG3QMQQVJp51ekGu6KM")); - auto expectedString = - "7gdEdDymvtfPfVgVvCTPzafmZc1Z8Zu4uXgJDLm8KGpLyPHysxFGjtFzimZDmGtNhQCh22Ygv3ZtPZmSbANbafikR3" - "S1tvujatHW9gMo35jveq7TxwcGoNSqc7tnH85hkEZwnDryVaiKRvtCeH3dgFE9YqPHxiBuZT5eChHJvVNb9iTTdMsJ" - "XMusRtzeRV45CvrLKUvsAH7SSWHYW6bGow5TbEJie4buuz2rnbeVG5cxaZ6vyG2nJWHNuDPWZJTRi1MFEwHoxst3a5" - "jQPv9UrG9rNZFCw4uZizVcG6HEqHWgQBu8gVpYpzFCX5SrhjGPZpbK3YmHhUEMEpJx3Fn7jX7Kt4t3hhhrieXppoqK" - "NuqjeNVjfEf3Q8dJRfuVMLdXYbmitCVTPQzYKWBR6ERqWLYoAVqjoAS2pRUw1nrqi1HR"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, TransferWithDurableNonce) { - auto from = Address("sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH"); - auto to = Address("3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe"); - auto nonceAccount = "ALAaqqt4Cc8hWH22GT2L16xKNAn6gv7XCTF7JkbfWsc"; - uint64_t value = 1000; - auto recentBlockhash = Base58::decode("5ycoKxPRpW2GdD4byZuMptHU3VU5MgUCh6NLGQ2U8VE5"); - auto message = LegacyMessage::createTransfer(from, to, value, recentBlockhash, "", {}, nonceAccount); - EXPECT_EQ(message.header.numRequiredSignatures, 1); - EXPECT_EQ(message.header.numReadOnlySignedAccounts, 0); - EXPECT_EQ(message.header.numReadOnlyUnsignedAccounts, 2); - ASSERT_EQ(message.accountKeys.size(), 5ul); - ASSERT_EQ(message.instructions.size(), 2ul); - EXPECT_EQ(message.instructions[0].programId.string(), "11111111111111111111111111111111"); - ASSERT_EQ(message.instructions[0].accounts.size(), 3ul); - EXPECT_EQ(message.instructions[1].programId.string(), "11111111111111111111111111111111"); - ASSERT_EQ(message.instructions[1].accounts.size(), 2ul); - auto transaction = Transaction(message); - transaction.signatures.clear(); - transaction.signatures.push_back(Base58::decode("4c4LhPFsDQdwZnVqKtgDC1GVR63GU5REFvonAW3RF7Quo4b1YbU" - "rsXMmwYCfD3LGrUawThPaAixCUKnoGdMMQ7BQ")); - - auto expectedString = - "6zRqmNP5waeyartbf8GuQrWxdSy4SCYBTEmGhiXfYNxQTuUrvrBjia18YoCM367AQZWZ5yTjcN6FaXuaPWju7aVZNF" - "jyqpuMZLNEbpm8ZNmKP4Na2VzR59iAdSPEZGTPuesZEniNMAD7ZSux6fayxgwrEwMWjeiskFQEwdvFzKNHfNLbjoVp" - "dSTxhKiqfbwxnFBpBxNE4nqMj3bUR37cYJAFoDFokxy23HGpV93V9mbGG89aLBNQnd9LKTjpYFv49VMd48mptUd7uy" - "rRwZLMneew2Bxq3PLsj9SaJyCWbsnqYj6bBahhsErz67PJTJepx4BEhqRxHGUSbpeNiL7qyERri1GZsXhN8fgU3nPi" - "Yr7tMMxuLAoUFRMJ79HCex7vxhf7SapvcP"; - ASSERT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, CreateNonceAccountWithDurableNonce) { - auto from = Address("sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH"); - auto newNonceAccount = Address("6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ"); - auto nonceAccount = "ALAaqqt4Cc8hWH22GT2L16xKNAn6gv7XCTF7JkbfWsc"; - uint64_t rent = 10000000; - auto recentBlockhash = Base58::decode("E6hvnoXU9QmfWaibMk9NuT6QRZdfzbs96WGc2hhttqXQ"); - auto message = - LegacyMessage::createNonceAccount(from, newNonceAccount, rent, recentBlockhash, nonceAccount); - EXPECT_EQ(message.header.numRequiredSignatures, 2); - EXPECT_EQ(message.header.numReadOnlySignedAccounts, 0); - EXPECT_EQ(message.header.numReadOnlyUnsignedAccounts, 3); - ASSERT_EQ(message.accountKeys.size(), 6ul); - ASSERT_EQ(message.instructions.size(), 3ul); - EXPECT_EQ(message.instructions[0].programId.string(), "11111111111111111111111111111111"); - ASSERT_EQ(message.instructions[0].accounts.size(), 3ul); - EXPECT_EQ(message.instructions[1].programId.string(), "11111111111111111111111111111111"); - ASSERT_EQ(message.instructions[1].accounts.size(), 2ul); - EXPECT_EQ(message.instructions[2].programId.string(), "11111111111111111111111111111111"); - ASSERT_EQ(message.instructions[2].accounts.size(), 3ul); - auto transaction = Transaction(message); - transaction.signatures.clear(); - transaction.signatures.push_back(Base58::decode("5wTTTpoXKTzyhL3XbToCt1y12Cf5pcD8xH5NPmGW2y8Dcshgvs2" - "rY2QQ2b23DvXbheBBr3UTixpT4vYyStgDFPdq")); - transaction.signatures.push_back(Base58::decode("31nF9z8GG8ZkgUPSHmEqikMKhweo4LwVJpJACoLCEah8mJk469m" - "tFA4DxEeiyJBnZSumgbR1aFS7p4kqX1F42brK")); - - auto expectedString = - "Fr8FzXoH7h6Xo2La6SE49BEPzRX6f93Qn1cFA5E8n6z2GJtZdTU2BfyYGr1zv21Zkq7h68Z3Q96VnFyUVVd1hTWeq6" - "tHDamF1JK5L23yEeUXpEWv1KziWvG9XbxfseHUyWETQck7SY2HbsT4KSjRX9suDaBh68Bu8c96CVN7KtgYPhUrKP62" - "dAMHsf5qo7MESFN8wKJto94ANNCbQMzPmhig9nfiAfvfz9CqV4nbnSiqBGwo2XoyPknDK8RJ1UmA5ptfZ6w6Fy4UmJ" - "bQZWuZwpUrkEkfgLMNJ36McHkGAnjpyzq9gMtzb33xSjx1BqnbWXkKJdi8HyQAHTtvtqPz7DMsW9qx5fu3TNz6iC8Y" - "HG2HiynFCRjTtc2aH1rpJ9TLdFQEK8WrhdMFr3yW27cg6NB3JUFopUkDg2k5FwtzFyCdfifwebD7eswVNnqjoZxW59" - "fHgY3BrBH8uNst8YAQWvRH77y5L6imVmFhezU5JUb5sF58gR1D8eAQhUcHueakZb5FkFCaMeioTpKrVGgcSNe9zkBM" - "uquoUR3t4MVTiUSLa815qKoBCRmdexQDBt5RQbdQhYyVWn3ovjdhkwDGBU2zywRvottGCcEStQrUrSQDg1tMVKxX5G" - "3sBtxYf"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, WithdrawNonceAccountToSelfWithDurableNonce) { - auto authorizer = Address("sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH"); - auto withdrawNonceAccount = Address("6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ"); - auto nonceAccount = "ALAaqqt4Cc8hWH22GT2L16xKNAn6gv7XCTF7JkbfWsc"; - uint64_t value = 10000000; - auto recentBlockhash = Base58::decode("5EtRPR4sTWRSwNUE5a5SnKB46ZqTJH8vgF1qZFTKGHvw"); - auto message = LegacyMessage::createWithdrawNonceAccount(authorizer, withdrawNonceAccount, authorizer, - value, recentBlockhash, nonceAccount); - EXPECT_EQ(message.header.numRequiredSignatures, 1); - EXPECT_EQ(message.header.numReadOnlySignedAccounts, 0); - EXPECT_EQ(message.header.numReadOnlyUnsignedAccounts, 3); - ASSERT_EQ(message.accountKeys.size(), 6ul); - ASSERT_EQ(message.instructions.size(), 2ul); - EXPECT_EQ(message.instructions[0].programId.string(), "11111111111111111111111111111111"); - ASSERT_EQ(message.instructions[0].accounts.size(), 3ul); - EXPECT_EQ(message.instructions[1].programId.string(), "11111111111111111111111111111111"); - ASSERT_EQ(message.instructions[1].accounts.size(), 5ul); - auto transaction = Transaction(message); - transaction.signatures.clear(); - transaction.signatures.push_back(Base58::decode("5LsWV8w5CZT4uUDY63rxwcBgGczoVeNtYTPrchP7KZwPpJD7qB2" - "jFEmWVUVtnJGUWn36TGM1bGpnoYGgGWNYZGhA")); - auto expectedString = - "3rxbwm6dSX4SbFa7yitVQnoUGWkmRuQtg3V13a2jEAPfZZACCXZX2UFgWFpPqE7KfZSYhd5QE9TLzyikCwcmSBHhKX" - "jMp4oktQXwRT66YaCK8rJdNzBUuS1D9tgHMkLUWKAR7ZRyWd3XvtQhe7nWD6YF6TRGoKPSuwsZAArBxogA7YddmEUK" - "Psr2qjSKbjg7X5BbNceFwjEFAiafuizdSt7eGJHB5m9zJeYct8LCanTwJwyEVu1T9HTsgjW9hqHehqhCiHP46KGo63" - "o7WAoappZvM4EJZemu4GfM6F6H48bPXF2z1QJz17wE6BYeMXfXuGkCRt5jYxrjdKuqvTDYV34X1HjZYUdrkW6mQotW" - "DY3bS6zyAt784Vwzk2uiA8ytmWMbC24coUVwPSPGwZ92WJ6BpVCCtGDxLzp4CkahRu78UNWzdcEwPG6AUf"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, WithdrawNonceAccountWithDurableNonce) { - auto authorizer = Address("sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH"); - auto withdrawNonceAccount = Address("6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ"); - auto nonceAccount = "ALAaqqt4Cc8hWH22GT2L16xKNAn6gv7XCTF7JkbfWsc"; - auto to = Address("3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe"); - uint64_t value = 10000000; - auto recentBlockhash = Base58::decode("55F6jiKRh9Wyq7EZuur7nqTYwewNF6LcucuCHoK1N4HV"); - auto message = LegacyMessage::createWithdrawNonceAccount(authorizer, withdrawNonceAccount, to, value, - recentBlockhash, nonceAccount); - EXPECT_EQ(message.header.numRequiredSignatures, 1); - EXPECT_EQ(message.header.numReadOnlySignedAccounts, 0); - EXPECT_EQ(message.header.numReadOnlyUnsignedAccounts, 3); - ASSERT_EQ(message.accountKeys.size(), 7ul); - ASSERT_EQ(message.instructions.size(), 2ul); - EXPECT_EQ(message.instructions[0].programId.string(), "11111111111111111111111111111111"); - ASSERT_EQ(message.instructions[0].accounts.size(), 3ul); - EXPECT_EQ(message.instructions[1].programId.string(), "11111111111111111111111111111111"); - ASSERT_EQ(message.instructions[1].accounts.size(), 5ul); - auto transaction = Transaction(message); - transaction.signatures.clear(); - transaction.signatures.push_back(Base58::decode("2vzszn8rJbYAusA8F3aUo2x1pNeM8Us2uZm6ibeXiHA4XmZA557" - "yafbA3YPJ991Usd8Jbw1ov4rNYNw1VRrVjDnz")); - auto expectedString = - "djXotMMJaCX6HXkAshsTVDJfiG8JgryAVzWtPcamqaZGnnZztXbRrf4SvTtyu15ohTDTVMYak6BvwaoQcsGepzNs6k" - "QE68cgc1LngdtDJLaB5JceX2PmVC1WBK9nLYukdoCgmDrBFiJmkjSsouoFsyXqF7Cgswpq5PqdFqTboTvon6bbQt28" - "ST5cZ87G832kFMmrLtFXzrQC5MaqKxFYFuiy6rKDKWHh9Xyc1aqTmiWiwPkuoCGsxYmWyAjGmG8mSyjRwuLRLfAmjB" - "zAgSxFh36qKk5bZiwNwcktg4Ndsg6fdn39XdusfJYKvhWBjatmofk5PJ6eWjFaZbscmpdxcq15yNGRxKaD55e89JSa" - "2n3pe4wwaJ9SyMaY1trvu1YXx6NxpfhPDEBniMnSE32Ko3tpoQhmxLoMVXbz8pngCYNZUwTbPYmT9K9DXysJYsfnk9" - "FRoxVKJugskV91cyjKX1TeW4iosedQbiXdy"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, CreateTokenAccountAndTransfer) { - auto signer = Address("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - auto token = Address("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); - auto senderTokenAddress = Address("5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf"); - auto recipientMainAddress = Address("3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei"); - auto recipientTokenAddress = Address("BwTAyrCEdkjNyGSGVNSSGh6phn8KQaLN638Evj7PVQfJ"); - uint64_t amount = 4000; - uint8_t decimals = 6; - auto recentBlockhash = Base58::decode("AfzzEC8NVXoxKoHdjXLDVzqwqvvZmgPuqyJqjuHiPY1D"); - auto message = LegacyMessage::createTokenCreateAndTransfer(signer, recipientMainAddress, token, - recipientTokenAddress, senderTokenAddress, - amount, decimals, recentBlockhash); - EXPECT_EQ(message.header.numRequiredSignatures, 1); - EXPECT_EQ(message.header.numReadOnlySignedAccounts, 0); - EXPECT_EQ(message.header.numReadOnlyUnsignedAccounts, 6); - ASSERT_EQ(message.accountKeys.size(), 9ul); - ASSERT_EQ(message.instructions.size(), 2ul); - EXPECT_EQ(message.instructions[0].programId.string(), - "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); - ASSERT_EQ(message.instructions[0].accounts.size(), 7ul); - EXPECT_EQ(message.instructions[1].programId.string(), - "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); - ASSERT_EQ(message.instructions[1].accounts.size(), 4ul); - auto transaction = Transaction(message); - transaction.signatures.clear(); - transaction.signatures.push_back(Base58::decode( - "pL1m11UEDWn3jMkrNMqLeGwNpKzmhQzJiYaCocgPy7vXKA1tnvEjJbuVq9hTeM9kqMAmxhRpwRY157jDgkRdUZw")); - auto expectedString = - "2qkvFTcMk9kPaHtd7idJ1gJc4zTkuYDUJsC67kXvHjv3zwEyUx92QyhhSeBjL6h3Zaisj2nvTWid2UD1N9hbg9Ty7v" - "SHLc7mcFVvy3yJmN9tz99iLKsf15rEeKUk3crXWLtKZEpcXJurN7vrxKwjQJnVob2RjyxwVfho1oNZ72BHvqToRM1W" - "2KbcYhxK4d9zB4QY5tR2dzgCHWqAjf9Yov3y9mPBYCQBtw2GewrVMDbg5TK81E9BaWer3BcEafc3NCnRfcFEp7ZUXs" - "GAgJYx32uUoJPP8ByTqBsp2JWgHyZhoz1WUUYRqWKZthzotErVetjSik4h5GcXk9Nb6kzqEf4nKEZ22eyrP5dv3eZM" - "uGUUpMYUT9uF16T72s4TTwqiWDPFkidD33tACx74JKGoDraHEvEeAPrv6iUmC675kMuAV4EtVspVc5SnKXgRWRxb4d" - "cH3k7K4ckjSxYZwg8UhTXUgPxA936jBr2HeQuPLmNVn2muA1HfL2DnyrobUP9vHpbL3HHgM2fckeXy8LAcjnoE9TTa" - "AKX32wo5xoMj9wJmmtcU6YbXN4KgZ"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, CreateTokenAccountAndTransferWithDurableNonce) { - auto signer = Address("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - auto token = Address("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); - auto senderTokenAddress = Address("5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf"); - auto recipientMainAddress = Address("3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe"); - auto recipientTokenAddress = Address("93hbN3brRjZqRQTT9Xx6rAHVDFZFWD9ragFDXvDbTEjr"); - auto nonceAccount = "6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ"; - uint64_t amount = 4000; - uint8_t decimals = 6; - auto recentBlockhash = Base58::decode("AaYfEmGQpfJWypZ8MNmBHTep1dwCHVYDRHuZ3gVFiJpY"); - auto message = LegacyMessage::createTokenCreateAndTransfer( - signer, recipientMainAddress, token, recipientTokenAddress, senderTokenAddress, amount, - decimals, recentBlockhash, "", {}, nonceAccount); - EXPECT_EQ(message.header.numRequiredSignatures, 1); - EXPECT_EQ(message.header.numReadOnlySignedAccounts, 0); - EXPECT_EQ(message.header.numReadOnlyUnsignedAccounts, 7); - ASSERT_EQ(message.accountKeys.size(), 11ul); - ASSERT_EQ(message.instructions.size(), 3ul); - EXPECT_EQ(message.instructions[0].programId.string(), "11111111111111111111111111111111"); - ASSERT_EQ(message.instructions[0].accounts.size(), 3ul); - EXPECT_EQ(message.instructions[1].programId.string(), - "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); - ASSERT_EQ(message.instructions[1].accounts.size(), 7ul); - EXPECT_EQ(message.instructions[2].programId.string(), - "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); - ASSERT_EQ(message.instructions[2].accounts.size(), 4ul); - auto transaction = Transaction(message); - transaction.signatures.clear(); - transaction.signatures.push_back(Base58::decode( - "Gw1REivWi3j3cm17evPbxcdoJ1YY1jC7xcQERcmAWCtLx8WKsFoDDWTun4WYfUqSzEfuKjxiYKPvv2eAJWs7N1b")); - auto expectedString = - "388uZws6GfA9aiH1LPsYBijGBEfLEgqe6q5NWVYhsmjXjrgZB4cScGuvja6nBL3i6qg6HA4a8ptW6aHsNKVdcBWKhj" - "ZjaTPH5heEThzwEsMDfnH2PWAUbqfiFgMZQRCkhyCj57hGUR7hBFPELfz3DBw5qMz1tnP9gU6KTqHUomu5UaadLHb2" - "v5mbgTRcsMm3yDp2tzMwrp53VqvFNmHSau4ot4kdNL1jqEJC68Fj4ku6fMQaFSPyAeLQRF45ofYsFCa65fmtb4gBpq" - "WUdqWLv5Dy6xQUQUDsin8qpEVds6unXw5f63UjZeD7XQdC6Vz5aq3e6P9ug8L41xc1rbuRU3Kp4arUKyqTsHMQ2dxM" - "hPwEJLkHd4mFqqUWpYFTdfLFaNGU22hEkvP1esHUzaaGDmzAozbS96oaFw2jbHRRJtL8VjoA1aokGFFThM6M6mExuy" - "8GhUXdGjxDFU83Dan1URmHMGBRC4J9RMZip9s1sktJw9Rj5Std9KVT8T7m4MxTVTx4QoBw6KAf6PgNHyHPtZSc7kzo" - "CxDYNo2Myxvy8D95zk9YMp1MxeZXTDQ2aJuhWvfHhhrwgcQasAxRzbnJ9oehebVUNEcZEFsfnCgYuUmxWUemoKZnE1" - "bNMCvERVkT5fKQ36e1rt5vTC2iES9jzr3hDC1Pk1"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, AdvanceNonceAccount) { - auto authorizer = Address("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - auto nonceAccountAddress = Address("6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ"); - auto recentBlockhash = Base58::decode("4KQLRUfd7GEVXxAeDqwtuGTdwKd9zMfPycyAG3wJsdck"); - auto message = LegacyMessage::advanceNonceAccount(authorizer, nonceAccountAddress, recentBlockhash); - EXPECT_EQ(message.header.numRequiredSignatures, 1); - EXPECT_EQ(message.header.numReadOnlySignedAccounts, 0); - EXPECT_EQ(message.header.numReadOnlyUnsignedAccounts, 2); - ASSERT_EQ(message.accountKeys.size(), 4ul); - ASSERT_EQ(message.instructions.size(), 1ul); - EXPECT_EQ(message.instructions[0].programId.string(), "11111111111111111111111111111111"); - ASSERT_EQ(message.instructions[0].accounts.size(), 3ul); - auto transaction = Transaction(message); - transaction.signatures.clear(); - transaction.signatures.push_back(Base58::decode("2gwuvwJ3mdEsjA8Gid6FXYuSwa2AAyFY6Btw8ifwSc2SPsfKBnD" - "859C5mX4tLy6zQFHhKxSMMsW49o3dbJNiXDMo")); - auto expectedString = - "7YPgNzjCnUd2zBb6ZC6bf1YaoLjhJPHixLUdTjqMjq1YdzADJCx2wsTTBFFrqDKSHXEL6ntRq8NVJTQMGzGH5AQRKw" - "tKtutehxesmtzkZCPY9ADZ4ijFyveLmTt7kjZXX7ZWVoUmKAqiaYsPTex728uMBSRJpV4zRw2yKGdQRHTKy2QFEb9a" - "cwLjmrbEgoyzPCarxjPhw21QZnNcy8RiYJB2mzZ9nvhrD5d2jB5TtdiroQPgTSdKFzkNEd7hJUKpqUppjDFcNHGK73" - "FE9pCP2dKxCLH8Wfaez8bLtopjmWun9cbikxo7LZsarYzMXvxwZmerRd1"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, TransferTokenTransactionWithExternalPayer) { - auto signer = Address("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - auto feePayer = Address("Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - auto token = Address("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); - auto senderTokenAddress = Address("5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf"); - auto recipientTokenAddress = Address("AZapcpAZtEL1gQuC87F2L1hSfAZnAvNy1hHtJ8DJzySN"); - uint64_t amount = 4000; - uint8_t decimals = 6; - auto recentBlockhash = Base58::decode("GwB5uixiTQUG2Pvo6fWAaNQmz41Jt4WMEPD7b83wvHkX"); - auto message = - LegacyMessage::createTokenTransfer(signer, token, senderTokenAddress, recipientTokenAddress, - amount, decimals, recentBlockhash, "", {}, "", feePayer.string()); - EXPECT_EQ(message.header.numRequiredSignatures, 2); - EXPECT_EQ(message.header.numReadOnlySignedAccounts, 0); - EXPECT_EQ(message.header.numReadOnlyUnsignedAccounts, 2); - ASSERT_EQ(message.accountKeys.size(), 6ul); - ASSERT_EQ(message.instructions.size(), 1ul); - EXPECT_EQ(message.instructions[0].programId.string(), "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); - ASSERT_EQ(message.instructions[0].accounts.size(), 4ul); - auto transaction = Transaction(message); - transaction.signatures.clear(); - auto signature1 = Base58::decode( - "2LbovMDuKoR2LFcV5NbK9bCQZcTG99W6VE1urvdWFWvRhNg9ocDGhayyeBGisoqZgYZtcD3b6LJDTmPx9Gp3T6qd"); - auto signature2 = Base58::decode( - "2hUHMS9rwbUbXrpC7sK7utL2M4soTyQ7EX3sBYvdee9wraJvYoPH2XjovHDn8eRFY8z5uCx9DCj2Zfjpmzfa81Db"); - transaction.signatures.push_back(signature1); - transaction.signatures.push_back(signature2); - - auto expectedString = - // https://explorer.solana.com/tx/2LbovMDuKoR2LFcV5NbK9bCQZcTG99W6VE1urvdWFWvRhNg9ocDGhayyeBGisoqZgYZtcD3b6LJDTmPx9Gp3T6qd?cluster=devnet - "qjgNVBmoPDHNTN2ENQfxNVE57jWXpqdmu5GQX4msA7iK8ZRAnKpvbusQagv8CZGyNYti23p9jBsjTSx75ZU26UW5vgC8D88pusW8W5dp1ERo5DSfurMSYJ6afgQHdcuzn7exb8znSm6uV4y1cWgBRcuAGdg3wRpVhP8HEB1EeKgzjYVWvMSy6yR7qVrSL6BxHG6eiAMyahLFbEt4qBqLEdxxY7Dt4DyydVYmG2ZVtheaMHD3ACwCjpyPLXj399wxSgGXQQFGtzEJQw9awVezmJ4wZk6W4dDpXQvdKYaqUvwTwRZsQB5o2iekPWZXR9xvHiMLjMVBPzYgcU14ZSaCbqSNVv2pAJxP1sMvxZMNMzZPttPxCsDDGq9biC7exXwzesXSnZ3rsgEYeZtkUiBHAxR4rYqBpA6VzLs1bPx8MPTvr9mhNi2ezMBbg2nEfHV6Fz7H7rEY2g3jDtRz35Vmgits8s9RKi3kb73WtGUieRiXjiqkNhpvKkST1oEYRQ9"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, CreateTokenAccountAndTransferWithExternalFeePayer) { - auto signer = Address("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - auto feePayer = Address("Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - auto token = Address("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); - auto senderTokenAddress = Address("5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf"); - auto recipientMainAddress = Address("E54BymfdhXQtzDSGtgiJayauEMdB2gJjPqoDjzfbCXwj"); - auto recipientTokenAddress = Address("Ay7G7Yb7ZCebCQdG39FxD1nFNDtqFWJCBgfd5Ek7DDcS"); - uint64_t amount = 4000; - uint8_t decimals = 6; - auto recentBlockhash = Base58::decode("EsnN3ksLV6RLBW7qqgrvm2diwVJNTzL9QCuzm6tqWsU8"); - auto message = LegacyMessage::createTokenCreateAndTransfer( - signer, recipientMainAddress, token, recipientTokenAddress, senderTokenAddress, amount, - decimals, recentBlockhash, "", {}, "", feePayer.string()); - EXPECT_EQ(message.header.numRequiredSignatures, 2); - EXPECT_EQ(message.header.numReadOnlySignedAccounts, 0); - EXPECT_EQ(message.header.numReadOnlyUnsignedAccounts, 6); - ASSERT_EQ(message.accountKeys.size(), 10ul); - ASSERT_EQ(message.instructions.size(), 2ul); - EXPECT_EQ(message.instructions[0].programId.string(), - "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); - ASSERT_EQ(message.instructions[0].accounts.size(), 7ul); - EXPECT_EQ(message.instructions[1].programId.string(), - "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); - ASSERT_EQ(message.instructions[1].accounts.size(), 4ul); - auto transaction = Transaction(message); - transaction.signatures.clear(); - transaction.signatures.push_back(Base58::decode( - "7GZGFT2VA4dpJBBwjiMqj1o8yChDvoCsqnQ7xz4GxY513W3efxRqbB8y7g4tH2GEGQRx4vCkKipG1sMaDEen1A2")); - transaction.signatures.push_back(Base58::decode( - "3n7RHTCBAtnFVuDn5eRbyQB24h6AqajJi5nGMPrfnUVFUDh2Cb8AoaJ7mVtjnv73V4HaJCzSwCLAj3zcGEaFftWZ")); - - // https://explorer.solana.com/tx/7GZGFT2VA4dpJBBwjiMqj1o8yChDvoCsqnQ7xz4GxY513W3efxRqbB8y7g4tH2GEGQRx4vCkKipG1sMaDEen1A2?cluster=devnet - auto expectedString = - "5sxFkQYd2FvqRU64N79A6xjJKNkgUsEEg2wKgai2NiK7A7hF3q5GYEbjQsYBG9S2MejwTENbwHzvypaa3D3cEkxvVTg19aJFWdCtXQiz42QF5fN2MuAb6eJR4KHFnzCtxxnYGtN9swZ5B5cMSPCffCRZeUTe3kooRmbTYPvSaemU6reVSM7X2beoFKPd2svrLFa8XnvhBwL9EiFWQ9WhHB2cDV7KozCnJAW9kdNDR4RbfFQxboANGo3ZGE5ddcZ6YdomATKze1TtHj2qzJEJRwxsRr3iM3iNFb4Eav5Q2n71KUriRf73mo44GQUPbQ2LvpZKf4V6M2PzxJwzBo7FiFZurPmsanT3U5efEsKnnueddbiLHedc8JXc1d3Z53sFxVGJpsGA8RR6thse9wUvaEWqXVtPbNA6NMao9DFGD6Dudza9pJXSobPc7mDHZmVmookf5vi6Lb9Y1Q4EgcEPQmbaDnKGGB6uGfZe629i3iKXRzAd2dB7mKfffhDadZ8S1eYGT3dhddV3ExRxcqDP9BAGQT3rkRw1JpeSSi7ziYMQ3vn4t3okdgQSq6rrpbPDUNG8tLSHFMAq3ydnh4Cb4ECKkYoz9SFAnXACUu4mWETxijuKMK9kHrTqPGk9weHTzobzCC8q8fcPWV3TcyUyMxsbVxh5q1p5h5tWfD9td5TZJ2HEUbTop2dA53ZF"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -} // namespace TW::Solana diff --git a/tests/interface/TWTransactionCompilerTests.cpp b/tests/interface/TWTransactionCompilerTests.cpp index 74302d6c1aa..806de4959da 100644 --- a/tests/interface/TWTransactionCompilerTests.cpp +++ b/tests/interface/TWTransactionCompilerTests.cpp @@ -502,7 +502,6 @@ TEST(TWTransactionCompiler, ExternalSignatureSignSolana) { "rLph39CMgAkcj6b8KYvJEkb1YdYytHSZNGi4kVVTNqiicNgPdf1gmG6qz9zVtnqj9JtaD2efdS8qxsKnvNWSgb8Xxb" "T6dwyp7msUUi7d27cYaPTpK"; { - EXPECT_EQ(TWDataSize(outputData.get()), 296ul); Solana::Proto::SigningOutput output; ASSERT_TRUE(output.ParseFromArray(TWDataBytes(outputData.get()), (int)TWDataSize(outputData.get()))); From 13cdf838f334a1101774c849ab82983b2e319701 Mon Sep 17 00:00:00 2001 From: wh Date: Fri, 8 Mar 2024 19:19:06 +0800 Subject: [PATCH 057/128] [Solana]: Fix advance nonce not work with priority fee instruction (#3717) * [Solana]: Fix advance nonce not work with priority fee instruction * Convert indent to spaces * Adjust format --- .../tw_solana/src/modules/message_builder.rs | 26 +++++----- .../Solana/TransactionCompilerTests.cpp | 48 +++++++++++++++++++ 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/rust/chains/tw_solana/src/modules/message_builder.rs b/rust/chains/tw_solana/src/modules/message_builder.rs index b9e43cde4e5..7b8f6eaee04 100644 --- a/rust/chains/tw_solana/src/modules/message_builder.rs +++ b/rust/chains/tw_solana/src/modules/message_builder.rs @@ -168,9 +168,9 @@ impl<'a> MessageBuilder<'a> { let mut builder = InstructionBuilder::default(); builder + .maybe_advance_nonce(self.nonce_account()?, from) .maybe_priority_fee_price(self.priority_fee_price()) .maybe_priority_fee_limit(self.priority_fee_limit()) - .maybe_advance_nonce(self.nonce_account()?, from) .maybe_memo(transfer.memo.as_ref()) .add_instruction(transfer_ix); Ok(builder.output()) @@ -201,9 +201,9 @@ impl<'a> MessageBuilder<'a> { let mut builder = InstructionBuilder::default(); builder + .maybe_advance_nonce(self.nonce_account()?, sender) .maybe_priority_fee_price(self.priority_fee_price()) .maybe_priority_fee_limit(self.priority_fee_limit()) - .maybe_advance_nonce(self.nonce_account()?, sender) .add_instructions(deposit_ixs); Ok(builder.output()) } @@ -218,9 +218,9 @@ impl<'a> MessageBuilder<'a> { let mut builder = InstructionBuilder::default(); builder + .maybe_advance_nonce(self.nonce_account()?, sender) .maybe_priority_fee_price(self.priority_fee_price()) .maybe_priority_fee_limit(self.priority_fee_limit()) - .maybe_advance_nonce(self.nonce_account()?, sender) .add_instruction(deactivate_ix); Ok(builder.output()) } @@ -241,9 +241,9 @@ impl<'a> MessageBuilder<'a> { let mut builder = InstructionBuilder::default(); builder + .maybe_advance_nonce(self.nonce_account()?, sender) .maybe_priority_fee_price(self.priority_fee_price()) .maybe_priority_fee_limit(self.priority_fee_limit()) - .maybe_advance_nonce(self.nonce_account()?, sender) .add_instructions(deactivate_ixs); Ok(builder.output()) } @@ -266,9 +266,9 @@ impl<'a> MessageBuilder<'a> { let mut builder = InstructionBuilder::default(); builder + .maybe_advance_nonce(self.nonce_account()?, sender) .maybe_priority_fee_price(self.priority_fee_price()) .maybe_priority_fee_limit(self.priority_fee_limit()) - .maybe_advance_nonce(self.nonce_account()?, sender) .add_instruction(withdraw_ix); Ok(builder.output()) } @@ -297,9 +297,9 @@ impl<'a> MessageBuilder<'a> { let mut builder = InstructionBuilder::default(); builder + .maybe_advance_nonce(self.nonce_account()?, sender) .maybe_priority_fee_price(self.priority_fee_price()) .maybe_priority_fee_limit(self.priority_fee_limit()) - .maybe_advance_nonce(self.nonce_account()?, sender) .add_instructions(withdraw_ixs); Ok(builder.output()) } @@ -322,9 +322,9 @@ impl<'a> MessageBuilder<'a> { ); let mut builder = InstructionBuilder::default(); builder + .maybe_advance_nonce(self.nonce_account()?, funding_account) .maybe_priority_fee_price(self.priority_fee_price()) .maybe_priority_fee_limit(self.priority_fee_limit()) - .maybe_advance_nonce(self.nonce_account()?, funding_account) .add_instruction(instruction); Ok(builder.output()) } @@ -359,9 +359,9 @@ impl<'a> MessageBuilder<'a> { let mut builder = InstructionBuilder::default(); builder + .maybe_advance_nonce(self.nonce_account()?, signer) .maybe_priority_fee_price(self.priority_fee_price()) .maybe_priority_fee_limit(self.priority_fee_limit()) - .maybe_advance_nonce(self.nonce_account()?, signer) .maybe_memo(token_transfer.memo.as_ref()) .add_instruction(transfer_instruction); Ok(builder.output()) @@ -407,9 +407,9 @@ impl<'a> MessageBuilder<'a> { let mut builder = InstructionBuilder::default(); builder + .maybe_advance_nonce(self.nonce_account()?, signer) .maybe_priority_fee_price(self.priority_fee_price()) .maybe_priority_fee_limit(self.priority_fee_limit()) - .maybe_advance_nonce(self.nonce_account()?, signer) .add_instruction(create_account_instruction) // Optional memo. Order: before transfer, as per documentation. .maybe_memo(create_and_transfer.memo.as_ref()) @@ -435,9 +435,9 @@ impl<'a> MessageBuilder<'a> { let mut builder = InstructionBuilder::default(); builder + .maybe_advance_nonce(prev_nonce_account, signer) .maybe_priority_fee_price(self.priority_fee_price()) .maybe_priority_fee_limit(self.priority_fee_limit()) - .maybe_advance_nonce(prev_nonce_account, signer) .add_instructions(SystemInstructionBuilder::create_nonce_account( signer, new_nonce_account, @@ -457,9 +457,9 @@ impl<'a> MessageBuilder<'a> { let mut builder = InstructionBuilder::default(); builder + .maybe_advance_nonce(self.nonce_account()?, signer) .maybe_priority_fee_price(self.priority_fee_price()) .maybe_priority_fee_limit(self.priority_fee_limit()) - .maybe_advance_nonce(self.nonce_account()?, signer) .add_instruction(SystemInstructionBuilder::withdraw_nonce_account( withdraw_from_nonce, signer, @@ -478,9 +478,9 @@ impl<'a> MessageBuilder<'a> { let mut builder = InstructionBuilder::default(); builder + .maybe_advance_nonce(Some(nonce_account), signer) .maybe_priority_fee_price(self.priority_fee_price()) - .maybe_priority_fee_limit(self.priority_fee_limit()) - .maybe_advance_nonce(Some(nonce_account), signer); + .maybe_priority_fee_limit(self.priority_fee_limit()); Ok(builder.output()) } diff --git a/tests/chains/Solana/TransactionCompilerTests.cpp b/tests/chains/Solana/TransactionCompilerTests.cpp index 53e3567486e..7130966e3b8 100644 --- a/tests/chains/Solana/TransactionCompilerTests.cpp +++ b/tests/chains/Solana/TransactionCompilerTests.cpp @@ -83,4 +83,52 @@ TEST(SolanaCompiler, CompileTransferWithSignatures) { } } +TEST(SolanaCompiler, CompileTransferWithPriorityFee) { + // tx on mainnet + // https://explorer.solana.com/tx/5asW13PSGvbZAeiGe8YFo7jt3UTqb8KUfFhXh5DXpDVfpVup1ZP41tp7PmBJH43gK5xT9U4VDVChDynmC7PJp9fa + + const auto coin = TWCoinTypeSolana; + /// Step 1: Prepare transaction input (protobuf) + auto input = TW::Solana::Proto::SigningInput(); + auto& message = *input.mutable_transfer_transaction(); + auto recipient = std::string("6NdFCrugyZVRhFbHvJT3dFBrGE9ZYFbfc8dBS2q4d2a9"); + auto sender = std::string("EQ37VYUVcqUSzYgvaguDB4yVRg5m3Xg7qQoKF8zFiJxe"); + input.set_sender(sender); + input.set_recent_blockhash(std::string("8oFmGuWUpsy8h8WP8AXTp3yhcLt4gQJRG5GxNcK7jfhX")); + input.set_nonce_account("ubKTCz9avQt3twiC9TbRjfoCiJnggH1abjgj9FjZJJm"); + input.mutable_priority_fee_price()->set_price(40000); + input.mutable_priority_fee_limit()->set_limit(480000); + message.set_recipient(recipient); + message.set_value((uint64_t)10000); + auto inputString = input.SerializeAsString(); + auto inputStrData = TW::Data(inputString.begin(), inputString.end()); + + /// Step 2: Obtain preimage hash + const auto preImageHashesData = TransactionCompiler::preImageHashes(coin, inputStrData); + auto preSigningOutput = TW::Solana::Proto::PreSigningOutput(); + preSigningOutput.ParseFromArray(preImageHashesData.data(), (int)preImageHashesData.size()); + ASSERT_EQ(preSigningOutput.signers_size(), 1); + auto signer = preSigningOutput.signers(0); + EXPECT_EQ(signer, sender); + auto preImageHash = preSigningOutput.data(); + EXPECT_EQ(hex(preImageHash), "01000306c70ead37125b5e142838eb59a6883ef915474f9b0a52494f698a4d23f4827ca70d79017647148829ddee52e687a840b42ce55a808367ff3cb0dc67444f5361ca4fd49d2664e7ac3016160e0b943a9222a21bee6510248dc2ae341f58195a2a3606a7d517192c568ee08a845f73d29788cf035c3145b21ab344d8062ea940000000000000000000000000000000000000000000000000000000000000000000000306466fe5211732ffecadba72c39be7bc8ce5bbc5f7126b2c439b3a4000000073db37fafe5d370e4605395dfc4661d886170c751ecfd7d76a744ea43c9f16f6040403010300040400000005000903409c0000000000000500050200530700040200020c020000001027000000000000"); + + // Simulate signature, normally obtained from signature server + const Data publicKeyData = parse_hex("c70ead37125b5e142838eb59a6883ef915474f9b0a52494f698a4d23f4827ca7"); + const PublicKey publicKey = PublicKey(publicKeyData, TWPublicKeyTypeED25519); + const auto signature = parse_hex("e546dbc2b896ff53abe5d3f090abdea84fb3862c6dcab4a6878e4d0dc803a53a2b077ef14014737e049815ff4df5daa92dc3e11b55770466d5feab6bdfccf005"); + // Verify signature (pubkey & hash & signature) + EXPECT_TRUE(publicKey.verify(signature, TW::data(preImageHash))); + + /// Step 3: Compile transaction info + auto outputData = TransactionCompiler::compileWithSignatures(coin, inputStrData, {signature}, {publicKeyData}); + const auto ExpectedTx = "84RnGAbza5DstiCPgBwtyuGnB2TPYsRGFr4L9tvzc71tBjyPS5aK9xGfMPdKA16gm8dJSxdAwMQX22zF3bQAHtgNHSApaL9Hs2B4Rz1HjazPmbNYLJKkSZ4gq2MWbY6DSKkg3NUf4L9HpZVFbrUw7TNgFYbEYiA1wTJ4aVwVAh9NQLDaQBgANnMvjFTYy2rDjgHL8nZU6omjK2uvDaLdqp2L4hXjGTKQ1mMFVRLXnrMUX8dijBPty3NDcgJ3G442ccGguez7DwYooirYuZ5ajiJwkKtqu8tW4namRdvAC7YmL1tCqZpWXyDBhqMapoyf1bVCvUbnuz64RZwhd7nj6DyULtiMXCUXFayeShe2nvJGTkzWZxEEeHPyvLtTmSNrmRWqE2ZEDCFGH3bopBKG2QJVeEomh7rKFSZ6WTDmG2V7L6zPFexAhuen9ynEBu8JJWRM3nx5dj4BSDeQf"; + { + Solana::Proto::SigningOutput output; + ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); + + EXPECT_EQ(output.encoded(), ExpectedTx); + } +} + } // namespace TW::Solana::tests From 9dfb27b0bc6ccdf5e66489b146456004e8dae8ff Mon Sep 17 00:00:00 2001 From: wh Date: Tue, 12 Mar 2024 17:45:02 +0800 Subject: [PATCH 058/128] [Firo]: Support exchange address (#3712) --- include/TrustWalletCore/TWAnyAddress.h | 9 ++ include/TrustWalletCore/TWFiroAddressType.h | 18 +++ src/Bitcoin/Entry.cpp | 23 ++- src/Bitcoin/ExchangeAddress.h | 37 +++++ src/Bitcoin/OpCodes.h | 3 + src/Bitcoin/Script.cpp | 42 ++++- src/Bitcoin/Script.h | 8 + src/Bitcoin/SignatureBuilder.cpp | 4 +- src/CoinEntry.h | 5 +- src/interface/TWAnyAddress.cpp | 8 + ...ddressTests.cpp => TWFiroAddressTests.cpp} | 24 +++ .../chains/Firo/TransactionCompilerTests.cpp | 148 ++++++++++++++++++ 12 files changed, 322 insertions(+), 7 deletions(-) create mode 100644 include/TrustWalletCore/TWFiroAddressType.h create mode 100644 src/Bitcoin/ExchangeAddress.h rename tests/chains/Firo/{TWZCoinAddressTests.cpp => TWFiroAddressTests.cpp} (68%) create mode 100644 tests/chains/Firo/TransactionCompilerTests.cpp diff --git a/include/TrustWalletCore/TWAnyAddress.h b/include/TrustWalletCore/TWAnyAddress.h index 0256ac89070..49b604aa9fc 100644 --- a/include/TrustWalletCore/TWAnyAddress.h +++ b/include/TrustWalletCore/TWAnyAddress.h @@ -8,6 +8,7 @@ #include "TWCoinType.h" #include "TWData.h" #include "TWFilecoinAddressType.h" +#include "TWFiroAddressType.h" #include "TWString.h" TW_EXTERN_C_BEGIN @@ -122,6 +123,14 @@ struct TWAnyAddress* _Nonnull TWAnyAddressCreateSS58WithPublicKey(struct TWPubli TW_EXPORT_STATIC_METHOD struct TWAnyAddress* _Nonnull TWAnyAddressCreateWithPublicKeyFilecoinAddressType(struct TWPublicKey* _Nonnull publicKey, enum TWFilecoinAddressType filecoinAddressType); +/// Creates a Firo address from a public key and a given address type. +/// +/// \param publicKey derivates the address from the public key. +/// \param firoAddressType Firo address type. +/// \return TWAnyAddress pointer or nullptr if public key is invalid. +TW_EXPORT_STATIC_METHOD +struct TWAnyAddress* _Nonnull TWAnyAddressCreateWithPublicKeyFiroAddressType(struct TWPublicKey* _Nonnull publicKey, enum TWFiroAddressType firoAddressType); + /// Deletes an address. /// /// \param address address to delete. diff --git a/include/TrustWalletCore/TWFiroAddressType.h b/include/TrustWalletCore/TWFiroAddressType.h new file mode 100644 index 00000000000..55fa3a84259 --- /dev/null +++ b/include/TrustWalletCore/TWFiroAddressType.h @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#pragma once + +#include "TWBase.h" + +TW_EXTERN_C_BEGIN + +/// Firo address type. +TW_EXPORT_ENUM(uint32_t) +enum TWFiroAddressType { + TWFiroAddressTypeDefault = 0, // default + TWFiroAddressTypeExchange = 1, +}; + +TW_EXTERN_C_END diff --git a/src/Bitcoin/Entry.cpp b/src/Bitcoin/Entry.cpp index e599f93ffef..504ea50f796 100644 --- a/src/Bitcoin/Entry.cpp +++ b/src/Bitcoin/Entry.cpp @@ -6,6 +6,7 @@ #include "Address.h" #include "CashAddress.h" +#include "ExchangeAddress.h" #include "SegwitAddress.h" #include "Signer.h" @@ -32,11 +33,12 @@ bool Entry::validateAddress(TWCoinType coin, const std::string& address, const P return base58Prefix ? isValidBase58 : BitcoinCashAddress::isValid(address); case TWCoinTypeECash: return base58Prefix ? isValidBase58 : ECashAddress::isValid(address); + case TWCoinTypeFiro: + return isValidBase58 || ExchangeAddress::isValid(address); case TWCoinTypeDash: case TWCoinTypeDogecoin: case TWCoinTypePivx: case TWCoinTypeRavencoin: - case TWCoinTypeFiro: default: return isValidBase58; } @@ -96,13 +98,18 @@ std::string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW case TWCoinTypeECash: return ECashAddress(publicKey).string(); + case TWCoinTypeFiro: + if (std::get_if(&addressPrefix)) { + return ExchangeAddress(publicKey).string(); + } + return Address(publicKey, p2pkh).string(); + case TWCoinTypeDash: case TWCoinTypeDogecoin: case TWCoinTypeMonacoin: case TWCoinTypePivx: case TWCoinTypeQtum: case TWCoinTypeRavencoin: - case TWCoinTypeFiro: default: return Address(publicKey, p2pkh).string(); } @@ -121,6 +128,18 @@ Data Entry::addressToData(TWCoinType coin, const std::string& address) const { case TWCoinTypeECash: return cashAddressToData(ECashAddress(address)); + case TWCoinTypeFiro: { + // check if it is a legacy address + if (Address::isValid(address)) { + const auto addr = Address(address); + return {addr.bytes.begin() + 1, addr.bytes.end()}; + } else if (ExchangeAddress::isValid(address)) { + const auto addr = ExchangeAddress(address); + return {addr.bytes.begin() + 3, addr.bytes.end()}; + } + return {}; + } + default: { const auto decoded = SegwitAddress::decode(address); if (!std::get<2>(decoded)) { diff --git a/src/Bitcoin/ExchangeAddress.h b/src/Bitcoin/ExchangeAddress.h new file mode 100644 index 00000000000..dbdaa0bd765 --- /dev/null +++ b/src/Bitcoin/ExchangeAddress.h @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#pragma once + +#include "../Base58Address.h" +#include "Data.h" +#include "../PublicKey.h" + +#include + +namespace TW::Bitcoin { + +// see: https://github.com/firoorg/firo/blob/8bd4abdea223e22f15c36e7d2d42618dc843e2ef/src/chainparams.cpp#L357 +static const size_t kExchangeAddressSize = 23; +static const Data kPrefix = {0x01, 0xb9, 0xbb}; + +/// Class for firo exchange addresses +class ExchangeAddress : public TW::Base58Address { + public: + /// Initializes an address with a string representation. + explicit ExchangeAddress(const std::string& string) : TW::Base58Address(string) {} + + /// Initializes an address with a collection of bytes. + explicit ExchangeAddress(const Data& data) : TW::Base58Address(data) {} + + /// Initializes an address with a public key and prefix. + ExchangeAddress(const PublicKey& publicKey) : TW::Base58Address(publicKey, kPrefix) {} + + /// Determines whether a string makes a valid Firo exchange address. + static bool isValid(const std::string& string) { + return TW::Base58Address::isValid(string, {kPrefix}); + } +}; + +} // namespace TW::Bitcoin diff --git a/src/Bitcoin/OpCodes.h b/src/Bitcoin/OpCodes.h index c4d79a6e929..c76b653949f 100644 --- a/src/Bitcoin/OpCodes.h +++ b/src/Bitcoin/OpCodes.h @@ -141,6 +141,9 @@ enum OpCode { OP_NOP9 [[maybe_unused]] = 0xb8, OP_NOP10 [[maybe_unused]] = 0xb9, + // firo, see: https://github.com/firoorg/firo/blob/8bd4abdea223e22f15c36e7d2d42618dc843e2ef/src/script/script.h#L212 + OP_EXCHANGEADDR = 0xe0, + OP_INVALIDOPCODE [[maybe_unused]] = 0xff, }; diff --git a/src/Bitcoin/Script.cpp b/src/Bitcoin/Script.cpp index 341055e74ad..4128d621921 100644 --- a/src/Bitcoin/Script.cpp +++ b/src/Bitcoin/Script.cpp @@ -4,6 +4,7 @@ #include "Address.h" #include "CashAddress.h" +#include "ExchangeAddress.h" #include "OpCodes.h" #include "Script.h" #include "SegwitAddress.h" @@ -87,6 +88,17 @@ bool Script::matchPayToPublicKeyHash(Data& result) const { return false; } +// see: https://github.com/firoorg/firo/blob/8bd4abdea223e22f15c36e7d2d42618dc843e2ef/src/script/standard.cpp#L355 +bool Script::matchPayToExchangePublicKeyHash(Data& result) const { + if (bytes.size() == 26 && bytes[0] == OP_EXCHANGEADDR && bytes[1] == OP_DUP && bytes[2] == OP_HASH160 && bytes[3] == 20 && + bytes[24] == OP_EQUALVERIFY && bytes[25] == OP_CHECKSIG) { + result.clear(); + std::copy(std::begin(bytes) + 4, std::begin(bytes) + 4 + 20, std::back_inserter(result)); + return true; + } + return false; +} + bool Script::matchPayToPublicKeyHashReplay(Data& result) const { if (bytes.size() == 63 && bytes[0] == OP_DUP && bytes[1] == OP_HASH160 && bytes[2] == 20 && bytes[23] == OP_EQUALVERIFY && bytes[24] == OP_CHECKSIG && bytes[25] == 32 && @@ -246,6 +258,20 @@ Script Script::buildPayToPublicKeyHash(const Data& hash) { return script; } +// see: https://github.com/firoorg/firo/blob/8bd4abdea223e22f15c36e7d2d42618dc843e2ef/src/script/standard.cpp#L355 +Script Script::buildPayToExchangePublicKeyHash(const Data& hash) { + assert(hash.size() == 20); + Script script; + script.bytes.push_back(OP_EXCHANGEADDR); + script.bytes.push_back(OP_DUP); + script.bytes.push_back(OP_HASH160); + script.bytes.push_back(20); + append(script.bytes, hash); + script.bytes.push_back(OP_EQUALVERIFY); + script.bytes.push_back(OP_CHECKSIG); + return script; +} + Script Script::buildPayToPublicKeyHashReplay(const Data& hash, const Data& blockHash, int64_t blockHeight) { assert(hash.size() == 20); assert(blockHash.size() == 32); @@ -475,11 +501,21 @@ Script Script::lockScriptForAddress(const std::string& string, enum TWCoinType c } return {}; + case TWCoinTypeFiro: + if (ExchangeAddress::isValid(string)) { + auto address = ExchangeAddress(string); + auto data = Data(); + data.reserve(ExchangeAddress::size - 3); + std::copy(address.bytes.begin() + 3, address.bytes.end(), std::back_inserter(data)); + return buildPayToExchangePublicKeyHash(data); + } + return {}; + case TWCoinTypeGroestlcoin: if (Groestlcoin::Address::isValid(string)) { auto address = Groestlcoin::Address(string); auto data = Data(); - data.reserve(Address::size - 1); + data.reserve(Groestlcoin::Address::size - 1); std::copy(address.bytes.begin() + 1, address.bytes.end(), std::back_inserter(data)); if (address.bytes[0] == TW::p2pkhPrefix(TWCoinTypeGroestlcoin)) { return buildPayToPublicKeyHash(data); @@ -495,7 +531,7 @@ Script Script::lockScriptForAddress(const std::string& string, enum TWCoinType c if (Zcash::TAddress::isValid(string)) { auto address = Zcash::TAddress(string); auto data = Data(); - data.reserve(Address::size - 2); + data.reserve(Zcash::TAddress::size - 2); std::copy(address.bytes.begin() + 2, address.bytes.end(), std::back_inserter(data)); if (address.bytes[1] == TW::p2pkhPrefix(TWCoinTypeZcash)) { return buildPayToPublicKeyHash(data); @@ -514,7 +550,7 @@ Script Script::lockScriptForAddress(const std::string& string, enum TWCoinType c if (Zen::Address::isValid(string)) { auto address = Zen::Address(string); auto data = Data(); - data.reserve(Address::size - 2); + data.reserve(Zen::Address::size - 2); std::copy(address.bytes.begin() + 2, address.bytes.end(), std::back_inserter(data)); if (address.bytes[1] == TW::p2pkhPrefix(TWCoinTypeZen)) { return buildPayToPublicKeyHashReplay(data, blockHash, blockHeight); diff --git a/src/Bitcoin/Script.h b/src/Bitcoin/Script.h index 56ad6f9e570..6901e4c60ab 100644 --- a/src/Bitcoin/Script.h +++ b/src/Bitcoin/Script.h @@ -64,6 +64,10 @@ class Script { /// Matches the script to a pay-to-public-key-hash (P2PKH). bool matchPayToPublicKeyHash(Data& keyHash) const; + /// Matches the script to a pay-to-exchange-public-key-hash (P2PKH). + /// Only apply for firo + bool matchPayToExchangePublicKeyHash(Data& keyHash) const; + /// Matches the script to a pay-to-public-key-hash-replay (P2PKH). /// Only apply for zen bool matchPayToPublicKeyHashReplay(Data& keyHash) const; @@ -90,6 +94,10 @@ class Script { /// Builds a pay-to-public-key-hash (P2PKH) script from a public key hash. static Script buildPayToPublicKeyHash(const Data& hash); + /// Builds a pay-to-exchange-public-key-hash script from a public key hash. + /// This will apply for firo. + static Script buildPayToExchangePublicKeyHash(const Data& hash); + /// Builds a pay-to-public-key-hash-replay (P2PKH) script from a public key hash. /// This will apply for zen static Script buildPayToPublicKeyHashReplay(const Data& hash, const Data& blockHash, int64_t blockHeight); diff --git a/src/Bitcoin/SignatureBuilder.cpp b/src/Bitcoin/SignatureBuilder.cpp index 7dada6c3201..0e1f621a8d0 100644 --- a/src/Bitcoin/SignatureBuilder.cpp +++ b/src/Bitcoin/SignatureBuilder.cpp @@ -191,7 +191,9 @@ Result, Common::Proto::SigningError> SignatureBuilder, Common::Proto::SigningError>::success({signature}); } - if (script.matchPayToPublicKeyHash(data) || script.matchPayToPublicKeyHashReplay(data)) { + if (script.matchPayToPublicKeyHash(data) + || script.matchPayToPublicKeyHashReplay(data) + || script.matchPayToExchangePublicKeyHash(data)) { // obtain public key auto pair = keyPairForPubKeyHash(data); Data pubkey; diff --git a/src/CoinEntry.h b/src/CoinEntry.h index d9a450a52d6..569593dac84 100644 --- a/src/CoinEntry.h +++ b/src/CoinEntry.h @@ -34,7 +34,10 @@ using SS58Prefix = uint32_t; /// Declare a dummy prefix to notify the entry to derive a delegated address. struct DelegatedPrefix {}; -using PrefixVariant = std::variant; +/// Declare a dummy prefix to notify the entry to derive a firo exchange address. +struct ExchangePrefix {}; + +using PrefixVariant = std::variant; /// Interface for coin-specific entry, used to dispatch calls to coins /// Implement this for all coins. diff --git a/src/interface/TWAnyAddress.cpp b/src/interface/TWAnyAddress.cpp index 5c596625cfe..b91bb724f1d 100644 --- a/src/interface/TWAnyAddress.cpp +++ b/src/interface/TWAnyAddress.cpp @@ -90,6 +90,14 @@ struct TWAnyAddress* TWAnyAddressCreateWithPublicKeyFilecoinAddressType(struct T return new TWAnyAddress{TW::AnyAddress::createAddress(publicKey->impl, TWCoinTypeFilecoin, TWDerivationDefault, prefix)}; } +struct TWAnyAddress* TWAnyAddressCreateWithPublicKeyFiroAddressType(struct TWPublicKey* _Nonnull publicKey, enum TWFiroAddressType firoAddressType) { + TW::PrefixVariant prefix = std::monostate(); + if (firoAddressType == TWFiroAddressTypeExchange) { + prefix = TW::ExchangePrefix(); + } + return new TWAnyAddress{TW::AnyAddress::createAddress(publicKey->impl, TWCoinTypeFiro, TWDerivationDefault, prefix)}; +} + void TWAnyAddressDelete(struct TWAnyAddress* _Nonnull address) { delete address->impl; delete address; diff --git a/tests/chains/Firo/TWZCoinAddressTests.cpp b/tests/chains/Firo/TWFiroAddressTests.cpp similarity index 68% rename from tests/chains/Firo/TWZCoinAddressTests.cpp rename to tests/chains/Firo/TWFiroAddressTests.cpp index 4889637d0fe..9054b6100c5 100644 --- a/tests/chains/Firo/TWZCoinAddressTests.cpp +++ b/tests/chains/Firo/TWFiroAddressTests.cpp @@ -4,6 +4,9 @@ #include "TestUtilities.h" +#include +#include +#include #include #include #include @@ -22,6 +25,27 @@ TEST(TWZCoin, Address) { assertStringsEqual(addressString, "aAbqxogrjdy2YHVcnQxFHMzqpt2fhjCTVT"); } +TEST(TWZCoin, ExchangeAddress_CreateWithString) { + auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(STRING("aJtPAs49k2RYonsUoY9SGgmpzv4awdPfVP").get(), TWCoinTypeFiro)); + auto addressData = WRAPD(TWAnyAddressData(address.get())); + assertHexEqual(addressData, "c7529bf17541410428c7b23b402761acb83fdfba"); + + auto exchangeAddress = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(STRING("EXXYdhSMM9Em5Z3kzdUWeUm2vFMNyXFSAEE9").get(), TWCoinTypeFiro)); + auto exchangeAddressData = WRAPD(TWAnyAddressData(exchangeAddress.get())); + assertHexEqual(exchangeAddressData, "c7529bf17541410428c7b23b402761acb83fdfba"); +} + +TEST(TWZCoin, ExchangeAddress_DeriveFromPublicKey) { + auto publicKey = WRAP(TWPublicKey, TWPublicKeyCreateWithData(DATA("034cc1963365aa67d35643f419d6601eca6ef7f62e46bf7f8b6ffa64e2f44fd0bf").get(), TWPublicKeyTypeSECP256k1)); + auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKeyFiroAddressType(publicKey.get(), TWFiroAddressTypeExchange)); + auto addressDesc = WRAPS(TWAnyAddressDescription(address.get())); + assertStringsEqual(addressDesc, "EXXWKhUtcaFKVW1NeRFuqPq33zAJMtQJwR4y"); + + auto defaultAddress = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKeyFiroAddressType(publicKey.get(), TWFiroAddressTypeDefault)); + auto defaultAddressDesc = WRAPS(TWAnyAddressDescription(defaultAddress.get())); + assertStringsEqual(defaultAddressDesc, "aGaPDQKakaqVmQXGawLMLguZoqSx6CnSfK"); +} + TEST(TWZCoin, ExtendedKeys) { auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic( STRING("ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal").get(), diff --git a/tests/chains/Firo/TransactionCompilerTests.cpp b/tests/chains/Firo/TransactionCompilerTests.cpp new file mode 100644 index 00000000000..5851ae6ce4f --- /dev/null +++ b/tests/chains/Firo/TransactionCompilerTests.cpp @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#include "Coin.h" +#include "HexCoding.h" +#include "PublicKey.h" +#include "TransactionCompiler.h" + +#include "proto/Bitcoin.pb.h" +#include "proto/TransactionCompiler.pb.h" + +#include + +#include "Bitcoin/Script.h" +#include "Bitcoin/TransactionPlan.h" + +#include "TestUtilities.h" +#include + +using namespace TW; + +TEST(FiroCompiler, FiroCompileWithSignatures) { + // tx on mainnet + // https://explorer.firo.org/tx/f1e9a418eb8d2bc96856ac221e9112ee061805af35d52be261caf7a7c9c48756 + + const auto coin = TWCoinTypeFiro; + const int64_t amount = 9999741; + const int64_t fee = 259; + const std::string toAddress = "EXXQe1Xhay75BzoFFhXgpqNTtLomdBKSfyMZ"; + + auto toScript = Bitcoin::Script::lockScriptForAddress(toAddress, coin); + ASSERT_EQ(hex(toScript.bytes), "e076a9146fa0b49c4fe011eeeeba6abb9ea6832d15acda1488ac"); + + auto input = Bitcoin::Proto::SigningInput(); + input.set_hash_type(TWBitcoinSigHashTypeAll); + input.set_amount(amount); + input.set_byte_fee(1); + input.set_to_address(toAddress); + input.set_change_address("EXXWKhUtcaFKVW1NeRFuqPq33zAJMtQJwR4y"); + input.set_coin_type(coin); + input.set_lock_time(824147); + + auto txHash0 = parse_hex("7d46af1b51ac6d55554e4748f08d87727214da7c6148da037cb71dc893b6297f"); + std::reverse(txHash0.begin(), txHash0.end()); + + auto utxo0 = input.add_utxo(); + utxo0->mutable_out_point()->set_hash(txHash0.data(), txHash0.size()); + utxo0->mutable_out_point()->set_index(1); + utxo0->mutable_out_point()->set_sequence(UINT32_MAX - 1); + utxo0->set_amount(10000000); + + auto utxoAddr0 = "EXXWKhUtcaFKVW1NeRFuqPq33zAJMtQJwR4y"; + auto script0 = Bitcoin::Script::lockScriptForAddress(utxoAddr0, coin); + ASSERT_EQ(hex(script0.bytes), "e076a914adfae82521fb6bba65fecc265fe67e5ee476b5df88ac"); + utxo0->set_script(script0.bytes.data(), script0.bytes.size()); + EXPECT_EQ(input.utxo_size(), 1); + + // Plan + Bitcoin::Proto::TransactionPlan plan; + ANY_PLAN(input, plan, coin); + + plan.set_amount(amount); + plan.set_fee(fee); + plan.set_change(0); + + // Extend input with accepted plan + *input.mutable_plan() = plan; + + // Serialize input + const auto txInputData = data(input.SerializeAsString()); + EXPECT_GT((int)txInputData.size(), 0); + + /// Step 2: Obtain preimage hashes + const auto preImageHashes = TransactionCompiler::preImageHashes(coin, txInputData); + TW::Bitcoin::Proto::PreSigningOutput preSigningOutput; + ASSERT_TRUE(preSigningOutput.ParseFromArray(preImageHashes.data(), (int)preImageHashes.size())); + + ASSERT_EQ(preSigningOutput.error(), Common::Proto::OK); + EXPECT_EQ(hex(preSigningOutput.hash_public_keys()[0].data_hash()), + "c4841429065d36ec089c0d27b6f803b8fb1b2fb22d25629f38dcb40e2afff80d"); + + EXPECT_EQ(hex(preSigningOutput.hash_public_keys()[0].public_key_hash()), "adfae82521fb6bba65fecc265fe67e5ee476b5df"); + + auto publicKeyHex = "034cc1963365aa67d35643f419d6601eca6ef7f62e46bf7f8b6ffa64e2f44fd0bf"; + auto publicKey = PublicKey(parse_hex(publicKeyHex), TWPublicKeyTypeSECP256k1); + auto preImageHash = preSigningOutput.hash_public_keys()[0].data_hash(); + auto signature = parse_hex("304402206c5135f0ebfe329b1f1ba3b53730b2e1d02a6afca9c7c9ce007b8b956f9a235a0220482e76d74375b097bcd6275ab30d0c7a716263e744ecbbc33c651f83c15c4d99"); + + // Verify signature (pubkey & hash & signature) + EXPECT_TRUE( + publicKey.verifyAsDER(signature, TW::Data(preImageHash.begin(), preImageHash.end()))); + + // Simulate signatures, normally obtained from signature server. + std::vector signatureVec; + std::vector pubkeyVec; + signatureVec.push_back(signature); + pubkeyVec.push_back(publicKey.bytes); + + /// Step 3: Compile transaction info + auto outputData = + TransactionCompiler::compileWithSignatures(coin, txInputData, signatureVec, pubkeyVec); + + const auto ExpectedTx = + "01000000017f29b693c81db77c03da48617cda147272878df048474e55556dac511baf467d010000006a47304402206c5135f0ebfe329b1f1ba3b53730b2e1d02a6afca9c7c9ce007b8b956f9a235a0220482e76d74375b097bcd6275ab30d0c7a716263e744ecbbc33c651f83c15c4d990121034cc1963365aa67d35643f419d6601eca6ef7f62e46bf7f8b6ffa64e2f44fd0bffeffffff017d959800000000001ae076a9146fa0b49c4fe011eeeeba6abb9ea6832d15acda1488ac53930c00"; + { + Bitcoin::Proto::SigningOutput output; + ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); + EXPECT_EQ(hex(output.encoded()), ExpectedTx); + } + + { // Negative: not enough signatures + outputData = TransactionCompiler::compileWithSignatures( + coin, txInputData, {signature, signature}, pubkeyVec); + Bitcoin::Proto::SigningOutput output; + ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); + EXPECT_EQ(output.encoded().size(), 0ul); + EXPECT_EQ(output.error(), Common::Proto::Error_invalid_params); + } + + { // Negative: empty signatures + outputData = TransactionCompiler::compileWithSignatures( + coin, txInputData, {}, {}); + Bitcoin::Proto::SigningOutput output; + ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); + EXPECT_EQ(output.encoded().size(), 0ul); + EXPECT_EQ(output.error(), Common::Proto::Error_invalid_params); + } + { // Negative: invalid public key + const auto publicKeyBlake = + parse_hex("b689ab808542e13f3d2ec56fe1efe43a1660dcadc73ce489fde7df98dd8ce5d9"); + EXPECT_EXCEPTION( + TransactionCompiler::compileWithSignatures( + coin, txInputData, signatureVec, {publicKeyBlake}), + "Invalid public key"); + } + { // Negative: wrong signature (formally valid) + outputData = TransactionCompiler::compileWithSignatures( + coin, txInputData, + {parse_hex("415502201857bc6e6e48b46046a4bd204136fc77e24c240943fb5a1f0e86387aae59b349022" + "00a7f31478784e51c49f46ef072745a4f263d7efdbc9c6784aa2571ff4f6f3b51")}, + pubkeyVec); + Bitcoin::Proto::SigningOutput output; + ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); + EXPECT_EQ(output.encoded().size(), 0ul); + EXPECT_EQ(output.error(), Common::Proto::Error_signing); + } +} From 81ffb17100b71cc58a0e3894f3584b93150361fc Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Tue, 12 Mar 2024 10:45:31 +0100 Subject: [PATCH 059/128] [Rust]: Optimise Rust binary sizes (#3715) --- .github/workflows/linux-ci-rust.yml | 85 ++++- rust/.config/nextest.toml | 2 - rust/Cargo.lock | 263 +++++---------- rust/Cargo.toml | 5 + .../chains/tw_solana/src/modules/tx_signer.rs | 2 +- .../src/modules/legacy/build_and_sign.rs | 4 +- .../src/modules/transactions/brc20.rs | 6 +- rust/tw_evm/src/abi/decode.rs | 2 +- rust/tw_evm/src/abi/function.rs | 2 +- .../src/message/eip712/eip712_message.rs | 2 +- rust/tw_evm/src/modules/tx_builder.rs | 2 +- rust/tw_keypair/Cargo.toml | 8 +- rust/tw_keypair/src/ed25519/mod.rs | 8 +- rust/tw_keypair/src/ed25519/public.rs | 3 +- rust/tw_keypair/src/ed25519/secret.rs | 6 +- rust/tw_keypair/src/ed25519/signature.rs | 3 +- rust/tw_number/Cargo.toml | 2 +- rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs | 2 +- samples/kmp/shared/build.gradle.kts | 2 +- samples/rust/Cargo.lock | 307 ++++++------------ tools/install-rust-dependencies | 22 +- tools/install-sys-dependencies-mac | 2 + tools/release-size | 76 +++++ tools/rust-bindgen | 9 +- 24 files changed, 375 insertions(+), 450 deletions(-) delete mode 100644 rust/.config/nextest.toml create mode 100755 tools/release-size diff --git a/.github/workflows/linux-ci-rust.yml b/.github/workflows/linux-ci-rust.yml index 02e1b7955b3..9de4326f918 100644 --- a/.github/workflows/linux-ci-rust.yml +++ b/.github/workflows/linux-ci-rust.yml @@ -34,7 +34,6 @@ jobs: - name: Cache Rust uses: Swatinem/rust-cache@v2 with: - key: "build-and-test" workspaces: | rust @@ -54,19 +53,9 @@ jobs: - name: Run tests run: | - cargo llvm-cov nextest --profile ci --no-fail-fast --lcov --output-path coverage.info + cargo llvm-cov test --no-fail-fast --lcov --output-path coverage.info working-directory: rust - - name: Rust Test Report - uses: dorny/test-reporter@v1 - if: success() || failure() - continue-on-error: true - with: - name: Rust Tests - path: | - rust/target/nextest/ci/junit.xml - reporter: java-junit - - name: Gather and check Rust code coverage run: | tools/check-coverage rust/coverage.stats rust/coverage.info @@ -87,7 +76,6 @@ jobs: - name: Cache Rust uses: Swatinem/rust-cache@v2 with: - key: "test-wasm" workspaces: | rust @@ -100,3 +88,74 @@ jobs: - name: Run tests in WASM run: tools/rust-test wasm + + check-binary-sizes: + permissions: + contents: read + pull-requests: write + runs-on: macos-latest-xlarge + if: github.event.pull_request.draft == false + steps: + - uses: actions/checkout@v3 + - name: Install system dependencies + run: | + tools/install-sys-dependencies-mac + + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.3 + + - name: Cache Rust + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + rust + + - name: Install Rust dependencies + run: tools/install-rust-dependencies + + - name: Install emsdk + run: tools/install-wasm-dependencies + + - name: Compile release binaries + run: | + mkdir -p build/local/lib + source emsdk/emsdk_env.sh + tools/rust-bindgen + + - name: Generate release report + run: | + ./tools/release-size measure-rust > release-report.json + + - name: Upload release report + uses: actions/upload-artifact@v2 + with: + name: release_report + path: release-report.json + + # Download previous release report, compare the release binary sizes, and post/update a comment at the Pull Request. + - name: Download previous release report + if: github.event_name == 'pull_request' + uses: dawidd6/action-download-artifact@v3 + with: + commit: ${{github.event.pull_request.base.sha}} + path: previous + if_no_artifact_found: warn + # Same artifact name as at the "Upload release report" step. + name: release_report + # Ignore status or conclusion in the search. + workflow_conclusion: "" + + - name: Craft Comment Body + if: github.event_name == 'pull_request' + run: | + # Please note `previous/release-report.json` may not exist if the previous report was not found. + ./tools/release-size compare --before previous/release-report.json --current release-report.json > report-diff.md + + - name: Create or Update Comment + uses: edumserrano/find-create-or-update-comment@v2 + with: + issue-number: ${{ github.event.pull_request.number }} + body-includes: "Binary size comparison" + comment-author: 'github-actions[bot]' + edit-mode: replace + body-path: 'report-diff.md' diff --git a/rust/.config/nextest.toml b/rust/.config/nextest.toml deleted file mode 100644 index 76fd74b5d7f..00000000000 --- a/rust/.config/nextest.toml +++ /dev/null @@ -1,2 +0,0 @@ -[profile.ci.junit] -path = "junit.xml" diff --git a/rust/Cargo.lock b/rust/Cargo.lock index fe167147af0..22843571589 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -26,6 +26,12 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "arbitrary" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db55d72333851e17d572bec876e390cd3b11eb1ef53ae821dd9f3b653d2b4569" + [[package]] name = "arbitrary" version = "1.3.0" @@ -211,22 +217,10 @@ version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" dependencies = [ - "funty 1.1.0", - "radium 0.6.2", + "funty", + "radium", "tap", - "wyz 0.2.0", -] - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty 2.0.0", - "radium 0.7.0", - "tap", - "wyz 0.5.1", + "wyz", ] [[package]] @@ -242,13 +236,11 @@ dependencies = [ [[package]] name = "blake2" -version = "0.9.2" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4e37d16930f5459780f5621038b6382b9bb37c19016f39fb6b5808d831f174" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "crypto-mac", - "digest 0.9.0", - "opaque-debug", + "digest 0.10.6", ] [[package]] @@ -397,9 +389,9 @@ checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -417,7 +409,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c2538c4e68e52548bacb3e83ac549f903d44f011ac9d5abb5e132e67d0808f7" dependencies = [ "generic-array", - "rand_core 0.6.4", + "rand_core", "subtle", "zeroize", ] @@ -433,26 +425,31 @@ dependencies = [ ] [[package]] -name = "crypto-mac" -version = "0.8.0" +name = "curve25519-dalek" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" dependencies = [ - "generic-array", + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.6", + "fiat-crypto", + "platforms", + "rustc_version", "subtle", + "zeroize", ] [[package]] -name = "curve25519-dalek" -version = "3.2.0" +name = "curve25519-dalek-derive" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle", - "zeroize", + "proc-macro2", + "quote", + "syn 2.0.37", ] [[package]] @@ -547,7 +544,7 @@ dependencies = [ "group", "hkdf", "pkcs8", - "rand_core 0.6.4", + "rand_core", "sec1", "subtle", "zeroize", @@ -584,29 +581,23 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ - "rand_core 0.6.4", + "rand_core", "subtle", ] [[package]] -name = "fixed-hash" -version = "0.7.0" +name = "fiat-crypto" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" -dependencies = [ - "byteorder", - "rand", - "rustc-hex", - "static_assertions", -] +checksum = "1676f435fc1dadde4d03e43f5d62b259e1ce5f40bd4ffb21db2b42ebe59c1382" [[package]] name = "fixed-hash" -version = "0.8.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" dependencies = [ - "arbitrary", + "arbitrary 0.4.7", "byteorder", "rand", "rustc-hex", @@ -619,12 +610,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - [[package]] name = "generic-array" version = "0.14.6" @@ -636,17 +621,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.9" @@ -656,7 +630,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] @@ -676,7 +650,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core 0.6.4", + "rand_core", "subtle", ] @@ -752,16 +726,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" dependencies = [ - "parity-scale-codec 2.3.1", -] - -[[package]] -name = "impl-codec" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" -dependencies = [ - "parity-scale-codec 3.5.0", + "parity-scale-codec", ] [[package]] @@ -828,7 +793,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", - "sha2 0.10.6", + "sha2", "signature", ] @@ -849,9 +814,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.141" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "log" @@ -885,7 +850,7 @@ dependencies = [ "hex", "num", "once_cell", - "primitive-types 0.10.1", + "primitive-types", "rand", "ref-cast", "serde", @@ -985,12 +950,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - [[package]] name = "p256" version = "0.13.2" @@ -1000,7 +959,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2 0.10.6", + "sha2", ] [[package]] @@ -1010,24 +969,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" dependencies = [ "arrayvec", - "bitvec 0.20.4", + "bitvec", "byte-slice-cast", "impl-trait-for-tuples", - "parity-scale-codec-derive 2.3.1", - "serde", -] - -[[package]] -name = "parity-scale-codec" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ddb756ca205bd108aee3c62c6d3c994e1df84a59b9d6d4a5ea42ee1fd5a9a28" -dependencies = [ - "arrayvec", - "bitvec 1.0.1", - "byte-slice-cast", - "impl-trait-for-tuples", - "parity-scale-codec-derive 3.1.4", + "parity-scale-codec-derive", "serde", ] @@ -1043,18 +988,6 @@ dependencies = [ "syn 1.0.107", ] -[[package]] -name = "parity-scale-codec-derive" -version = "3.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.107", -] - [[package]] name = "paste" version = "1.0.11" @@ -1083,6 +1016,12 @@ dependencies = [ "spki", ] +[[package]] +name = "platforms" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1104,23 +1043,12 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" dependencies = [ - "fixed-hash 0.7.0", - "impl-codec 0.5.1", + "fixed-hash", + "impl-codec", "impl-serde", "uint", ] -[[package]] -name = "primitive-types" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" -dependencies = [ - "fixed-hash 0.8.0", - "impl-codec 0.6.0", - "uint", -] - [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -1202,12 +1130,6 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - [[package]] name = "rand" version = "0.8.5" @@ -1216,7 +1138,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -1226,16 +1148,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] @@ -1244,7 +1157,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.9", + "getrandom", ] [[package]] @@ -1457,19 +1370,6 @@ dependencies = [ "digest 0.10.6", ] -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "sha2" version = "0.10.6" @@ -1498,7 +1398,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" dependencies = [ "digest 0.10.6", - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -1530,7 +1430,7 @@ dependencies = [ "num-integer", "num-traits", "rfc6979", - "sha2 0.10.6", + "sha2", "starknet-crypto-codegen", "starknet-curve", "starknet-ff", @@ -1566,7 +1466,7 @@ dependencies = [ "ark-ff", "bigdecimal", "crypto-bigint", - "getrandom 0.2.9", + "getrandom", "hex", "serde", ] @@ -1891,7 +1791,7 @@ dependencies = [ name = "tw_encoding" version = "0.1.0" dependencies = [ - "arbitrary", + "arbitrary 1.3.0", "bcs", "bech32", "bs58", @@ -1956,7 +1856,7 @@ dependencies = [ name = "tw_hash" version = "0.1.0" dependencies = [ - "arbitrary", + "arbitrary 1.3.0", "blake-hash", "blake2b-ref", "digest 0.10.6", @@ -1966,7 +1866,7 @@ dependencies = [ "serde", "serde_json", "sha1", - "sha2 0.10.6", + "sha2", "sha3", "tw_encoding", "tw_memory", @@ -1992,11 +1892,11 @@ dependencies = [ name = "tw_keypair" version = "0.1.0" dependencies = [ - "arbitrary", + "arbitrary 1.3.0", "blake2", "curve25519-dalek", "der", - "digest 0.9.0", + "digest 0.10.6", "ecdsa", "k256", "lazy_static", @@ -2006,7 +1906,7 @@ dependencies = [ "ring", "serde", "serde_json", - "sha2 0.9.9", + "sha2", "starknet-crypto", "starknet-ff", "tw_encoding", @@ -2057,9 +1957,9 @@ dependencies = [ name = "tw_number" version = "0.1.0" dependencies = [ - "arbitrary", + "arbitrary 1.3.0", "lazy_static", - "primitive-types 0.12.1", + "primitive-types", "serde", "tw_encoding", "tw_hash", @@ -2070,7 +1970,7 @@ dependencies = [ name = "tw_proto" version = "0.1.0" dependencies = [ - "arbitrary", + "arbitrary 1.3.0", "pb-rs", "quick-protobuf", "tw_encoding", @@ -2143,7 +2043,7 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ - "arbitrary", + "arbitrary 1.3.0", "byteorder", "crunchy", "hex", @@ -2207,12 +2107,6 @@ dependencies = [ "tw_solana", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2338,15 +2232,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - [[package]] name = "zeroize" version = "1.6.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index a98ff23da18..bcd400d3e1f 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -29,6 +29,11 @@ members = [ "wallet_core_rs", ] +[profile.release] +strip = true +codegen-units = 1 +panic = "abort" + [profile.wasm-test] inherits = "release" # Fixes an incredibly slow compilation of `curve25519-dalek` package. diff --git a/rust/chains/tw_solana/src/modules/tx_signer.rs b/rust/chains/tw_solana/src/modules/tx_signer.rs index ee190af6063..f1bb7c2a0fd 100644 --- a/rust/chains/tw_solana/src/modules/tx_signer.rs +++ b/rust/chains/tw_solana/src/modules/tx_signer.rs @@ -24,7 +24,7 @@ impl TxSigner { let message_encoded = Self::preimage_versioned(&unsigned_msg)?; // Add external signatures first, so they can be overriden if corresponding private keys are specified. - key_signs.extend(external_signatures.clone().into_iter()); + key_signs.extend(external_signatures.clone()); // Sign the message with all given private keys. for private_key in keys { diff --git a/rust/tw_bitcoin/src/modules/legacy/build_and_sign.rs b/rust/tw_bitcoin/src/modules/legacy/build_and_sign.rs index 17dbaa03638..e488b1b76a7 100644 --- a/rust/tw_bitcoin/src/modules/legacy/build_and_sign.rs +++ b/rust/tw_bitcoin/src/modules/legacy/build_and_sign.rs @@ -44,7 +44,7 @@ pub fn taproot_build_and_sign_transaction( } else { legacy .private_key - .get(0) + .first() .ok_or_else(|| Error::from(Proto::Error::Error_legacy_no_private_key))? }; @@ -79,7 +79,7 @@ pub fn taproot_build_and_sign_transaction( version: 2, private_key: legacy .private_key - .get(0) + .first() .map(|pk| pk.to_vec().into()) .unwrap_or_default(), lock_time: Some(lock_time), diff --git a/rust/tw_bitcoin/src/modules/transactions/brc20.rs b/rust/tw_bitcoin/src/modules/transactions/brc20.rs index e67b81f24bb..e7e8816a15a 100644 --- a/rust/tw_bitcoin/src/modules/transactions/brc20.rs +++ b/rust/tw_bitcoin/src/modules/transactions/brc20.rs @@ -31,12 +31,12 @@ struct BRC20TransferPayload { } impl BRC20TransferPayload { - const PROTOCOL_ID: &str = "brc-20"; - const MIME: &[u8] = b"text/plain;charset=utf-8"; + const PROTOCOL_ID: &'static str = "brc-20"; + const MIME: &'static [u8] = b"text/plain;charset=utf-8"; } impl BRC20TransferPayload { - const OPERATION: &str = "transfer"; + const OPERATION: &'static str = "transfer"; fn new(ticker: Brc20Ticker, amount: String) -> Self { BRC20TransferPayload { diff --git a/rust/tw_evm/src/abi/decode.rs b/rust/tw_evm/src/abi/decode.rs index fc1489433d4..0b13d192076 100644 --- a/rust/tw_evm/src/abi/decode.rs +++ b/rust/tw_evm/src/abi/decode.rs @@ -27,7 +27,7 @@ pub fn decode_params(params: &[Param], data: &[u8]) -> AbiResult let named_tokens: Vec<_> = params .iter() - .zip(decoded_tokens.into_iter()) + .zip(decoded_tokens) .map(|(param, value)| NamedToken::with_param_and_token(param, value)) .collect(); Ok(named_tokens) diff --git a/rust/tw_evm/src/abi/function.rs b/rust/tw_evm/src/abi/function.rs index d4359e378b4..93a4d3d0441 100644 --- a/rust/tw_evm/src/abi/function.rs +++ b/rust/tw_evm/src/abi/function.rs @@ -60,6 +60,6 @@ impl Function { let signed = short_signature(&self.name, &input_param_types); let encoded = encode_tokens(tokens); - Ok(signed.into_iter().chain(encoded.into_iter()).collect()) + Ok(signed.into_iter().chain(encoded).collect()) } } diff --git a/rust/tw_evm/src/message/eip712/eip712_message.rs b/rust/tw_evm/src/message/eip712/eip712_message.rs index ceb3815d328..ccb3f1bea91 100644 --- a/rust/tw_evm/src/message/eip712/eip712_message.rs +++ b/rust/tw_evm/src/message/eip712/eip712_message.rs @@ -188,7 +188,7 @@ fn encode_array( // Check if the type definition actually matches the length of items to be encoded. if expected_len.is_some() && Some(elements.len()) != expected_len { - return Err(MessageSigningError::TypeValueMismatch)?; + return Err(MessageSigningError::TypeValueMismatch); } let mut encoded_items = vec![]; diff --git a/rust/tw_evm/src/modules/tx_builder.rs b/rust/tw_evm/src/modules/tx_builder.rs index a6cfc382528..5d23f9afa20 100644 --- a/rust/tw_evm/src/modules/tx_builder.rs +++ b/rust/tw_evm/src/modules/tx_builder.rs @@ -190,7 +190,7 @@ impl TxBuilder { erc4337_payload: Data, ) -> SigningResult { let Some(ref user_op) = input.user_operation else { - return Err(SigningError(CommonError::Error_invalid_params)) + return Err(SigningError(CommonError::Error_invalid_params)); }; let nonce = U256::from_big_endian_slice(&input.nonce)?; diff --git a/rust/tw_keypair/Cargo.toml b/rust/tw_keypair/Cargo.toml index d29ab7b89b3..b332c1e4aa4 100644 --- a/rust/tw_keypair/Cargo.toml +++ b/rust/tw_keypair/Cargo.toml @@ -25,10 +25,10 @@ p256 = { version = "0.13.0", features = ["ecdsa", "std"], default-features = fal pkcs8 = "0.10.2" rfc6979 = "0.4.0" # ED25519 specific: -blake2 = "0.9" -curve25519-dalek = "3" -digest = "0.9.0" -sha2 = "0.9" +blake2 = "0.10.6" +curve25519-dalek = { version = "4.1", features = ["digest", "legacy_compatibility"] } +digest = "0.10" +sha2 = "0.10.6" [dev-dependencies] serde_json = "1.0" diff --git a/rust/tw_keypair/src/ed25519/mod.rs b/rust/tw_keypair/src/ed25519/mod.rs index c12af906a36..3c34d4ff6ec 100644 --- a/rust/tw_keypair/src/ed25519/mod.rs +++ b/rust/tw_keypair/src/ed25519/mod.rs @@ -26,11 +26,11 @@ pub mod sha512 { /// `ed25519` implementation using `BLAKE2B` hash function. pub mod blake2b { - use blake2::Blake2b; + use blake2::Blake2b512; - pub type KeyPair = crate::ed25519::keypair::KeyPair; - pub type PrivateKey = crate::ed25519::private::PrivateKey; - pub type PublicKey = crate::ed25519::public::PublicKey; + pub type KeyPair = crate::ed25519::keypair::KeyPair; + pub type PrivateKey = crate::ed25519::private::PrivateKey; + pub type PublicKey = crate::ed25519::public::PublicKey; } /// A hash function that returns 64 length output. diff --git a/rust/tw_keypair/src/ed25519/public.rs b/rust/tw_keypair/src/ed25519/public.rs index c0de54f0c12..edee119dd0e 100644 --- a/rust/tw_keypair/src/ed25519/public.rs +++ b/rust/tw_keypair/src/ed25519/public.rs @@ -98,7 +98,8 @@ impl PublicKey { /// /// Source: https://github.com/dalek-cryptography/ed25519-dalek/blob/1.0.1/src/public.rs#L157-L160 fn multiply_by_basepoint_to_produce_public_key(bits: [u8; 32]) -> PublicKey { - let point = &Scalar::from_bits(bits) * &constants::ED25519_BASEPOINT_TABLE; + #[allow(deprecated)] + let point = &Scalar::from_bits(bits) * constants::ED25519_BASEPOINT_TABLE; PublicKey::with_edwards_point(point) } } diff --git a/rust/tw_keypair/src/ed25519/secret.rs b/rust/tw_keypair/src/ed25519/secret.rs index ca45153f84c..c1419e9fe92 100644 --- a/rust/tw_keypair/src/ed25519/secret.rs +++ b/rust/tw_keypair/src/ed25519/secret.rs @@ -39,8 +39,10 @@ impl ExpandedSecretKey { let (mut lower, upper): (H256, H256) = hash.split(); mangle_scalar(lower.deref_mut()); + #[allow(deprecated)] + let key = Scalar::from_bits(lower.take()); ExpandedSecretKey { - key: Scalar::from_bits(lower.take()), + key, nonce: upper, _phantom: PhantomData, } @@ -79,7 +81,7 @@ impl ExpandedSecretKey { h.update(message); let r = Scalar::from_hash(h); - let R = (&r * &constants::ED25519_BASEPOINT_TABLE).compress(); + let R = (&r * constants::ED25519_BASEPOINT_TABLE).compress(); h = H::new(); h.update(R.as_bytes()); diff --git a/rust/tw_keypair/src/ed25519/signature.rs b/rust/tw_keypair/src/ed25519/signature.rs index 85374bb2ff6..43051c4325e 100644 --- a/rust/tw_keypair/src/ed25519/signature.rs +++ b/rust/tw_keypair/src/ed25519/signature.rs @@ -84,10 +84,11 @@ fn get_scalar(bytes: H256) -> KeyPairResult { // This succeed-fast trick should succeed for roughly half of all scalars. let last_byte = bytes.last().expect("H256 is exactly 32 length"); if last_byte & SIGNIFICANT_BITS_MASK == 0 { + #[allow(deprecated)] return Ok(Scalar::from_bits(bytes.take())); } - match Scalar::from_canonical_bytes(bytes.take()) { + match Scalar::from_canonical_bytes(bytes.take()).into() { Some(x) => Ok(x), None => Err(KeyPairError::InvalidSignature), } diff --git a/rust/tw_number/Cargo.toml b/rust/tw_number/Cargo.toml index d76f8b6ed6d..6f32da2d9b1 100644 --- a/rust/tw_number/Cargo.toml +++ b/rust/tw_number/Cargo.toml @@ -11,7 +11,7 @@ helpers = [] [dependencies] arbitrary = { version = "1", features = ["derive"], optional = true } lazy_static = "1.4.0" -primitive-types = "0.12.1" +primitive-types = "0.10.1" serde = { version = "1.0", features = ["derive"], optional = true } tw_hash = { path = "../tw_hash" } tw_memory = { path = "../tw_memory" } diff --git a/rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs b/rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs index 3b94034dae9..0997ed88857 100644 --- a/rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs +++ b/rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs @@ -303,7 +303,7 @@ pub unsafe extern "C" fn tw_bitcoin_legacy_taproot_build_and_sign_transaction( }; let serialized = tw_proto::serialize(&error).expect("failed to serialize error message"); - return CByteArray::from(serialized) + return CByteArray::from(serialized); }; // Serialize SigningOutput and return. diff --git a/samples/kmp/shared/build.gradle.kts b/samples/kmp/shared/build.gradle.kts index 461c09d9882..89fb86c4634 100644 --- a/samples/kmp/shared/build.gradle.kts +++ b/samples/kmp/shared/build.gradle.kts @@ -45,7 +45,7 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation("com.trustwallet:wallet-core-kotlin:4.0.24") + implementation("com.trustwallet:wallet-core-kotlin:4.0.27") } } val commonTest by getting { diff --git a/samples/rust/Cargo.lock b/samples/rust/Cargo.lock index b77fefe671f..117be2dda3c 100644 --- a/samples/rust/Cargo.lock +++ b/samples/rust/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.19" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.65" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" [[package]] name = "autocfg" @@ -25,17 +25,18 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" -version = "1.3.2" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -46,9 +47,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "either" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "errno" @@ -57,17 +58,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "fastrand" -version = "1.8.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" -dependencies = [ - "instant", -] +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "hashbrown" @@ -75,12 +73,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - [[package]] name = "hex" version = "0.4.3" @@ -88,97 +80,83 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "indexmap" -version = "1.9.1" +name = "home" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "autocfg", - "hashbrown", + "windows-sys", ] [[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.9" +name = "indexmap" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.45.0", + "autocfg", + "hashbrown", ] [[package]] name = "jobserver" -version = "0.1.25" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" dependencies = [ "libc", ] [[package]] name = "libc" -version = "0.2.135" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "log" -version = "0.4.17" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "once_cell" -version = "1.15.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "protobuf" -version = "3.2.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e" +checksum = "58678a64de2fced2bdec6bca052a6716a0efe692d6e3f53d1bda6a1def64cfc0" dependencies = [ "once_cell", "protobuf-support", @@ -187,9 +165,9 @@ dependencies = [ [[package]] name = "protobuf-codegen" -version = "3.2.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd418ac3c91caa4032d37cb80ff0d44e2ebe637b2fb243b6234bf89cdac4901" +checksum = "32777b0b3f6538d9d2e012b3fad85c7e4b9244b5958d04a6415f4333782b7a77" dependencies = [ "anyhow", "once_cell", @@ -202,9 +180,9 @@ dependencies = [ [[package]] name = "protobuf-parse" -version = "3.2.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d39b14605eaa1f6a340aec7f320b34064feb26c93aec35d6a9a2272a8ddfa49" +checksum = "96cb37955261126624a25b5e6bda40ae34cf3989d52a783087ca6091b29b5642" dependencies = [ "anyhow", "indexmap", @@ -218,36 +196,39 @@ dependencies = [ [[package]] name = "protobuf-support" -version = "3.2.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372" +checksum = "e1ed294a835b0f30810e13616b1cd34943c6d1e84a8f3b0dcfe466d256c3e7e7" dependencies = [ "thiserror", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] -name = "redox_syscall" -version = "0.2.16" +name = "regex" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ - "bitflags", + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", ] [[package]] -name = "regex" -version = "1.6.0" +name = "regex-automata" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -256,22 +237,21 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "rustix" -version = "0.36.17" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305efbd14fde4139eb501df5f136994bb520b033fa9fbdce287507dc23b8c7ed" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ "bitflags", "errno", - "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -288,9 +268,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.102" +version = "2.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" dependencies = [ "proc-macro2", "quote", @@ -299,31 +279,30 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.4.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", "rustix", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", @@ -332,43 +311,20 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "which" -version = "4.3.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "libc", + "home", "once_cell", -] - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", + "rustix", ] [[package]] @@ -377,119 +333,62 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.42.2" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows-targets" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" -dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" diff --git a/tools/install-rust-dependencies b/tools/install-rust-dependencies index 32fe675512d..632c2290f1c 100755 --- a/tools/install-rust-dependencies +++ b/tools/install-rust-dependencies @@ -2,19 +2,16 @@ set -e -STABLE="stable-2023-06-01" -NIGHTLY="nightly-2023-06-27" +NIGHTLY="nightly-2024-02-09" -if [[ `uname` == "Darwin" || "$1" == "dev" ]]; then - rustup toolchain install $NIGHTLY - rustup default $NIGHTLY - rustup toolchain install $NIGHTLY-x86_64-apple-darwin --force-non-host - rustup toolchain install $NIGHTLY-aarch64-apple-darwin --force-non-host - rustup component add rust-src --toolchain $NIGHTLY-aarch64-apple-darwin - rustup component add rust-src --toolchain $NIGHTLY-x86_64-apple-darwin -else - rustup toolchain install $STABLE - rustup default $STABLE +rustup toolchain install $NIGHTLY +rustup default $NIGHTLY +rustup toolchain install $NIGHTLY-x86_64-apple-darwin --force-non-host +rustup toolchain install $NIGHTLY-aarch64-apple-darwin --force-non-host +rustup component add rust-src --toolchain $NIGHTLY-aarch64-apple-darwin +rustup component add rust-src --toolchain $NIGHTLY-x86_64-apple-darwin +if [[ `uname` == "Linux" ]]; then + rustup component add rust-src --toolchain $NIGHTLY-x86_64-unknown-linux-gnu fi # Android @@ -31,5 +28,4 @@ cargo install cbindgen --locked if [[ "$1" == "dev" ]]; then rustup component add llvm-tools-preview clippy rustfmt cargo install cargo-llvm-cov --locked - cargo install cargo-nextest --locked fi diff --git a/tools/install-sys-dependencies-mac b/tools/install-sys-dependencies-mac index 9ff7f1a41fd..c01bd2cde28 100755 --- a/tools/install-sys-dependencies-mac +++ b/tools/install-sys-dependencies-mac @@ -2,6 +2,8 @@ set -e +# A workaround for "The `brew link` step did not complete successfully" error. +brew install python@3 || brew link --overwrite python@3 brew install boost ninja xcodegen xcbeautify if command -v rustup &> /dev/null diff --git a/tools/release-size b/tools/release-size new file mode 100755 index 00000000000..bcd4076f9e8 --- /dev/null +++ b/tools/release-size @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 + +import argparse +import json +import os + +RUST_TARGETS = [ + "aarch64-apple-ios", + "aarch64-apple-ios-sim", + "aarch64-linux-android", + "armv7-linux-androideabi", + "wasm32-unknown-emscripten", +] +LIB_NAME = "libwallet_core_rs.a" + + +def display_size(size_kb: int) -> str: + if size_kb >= 10000: + size_mb = float(size_kb) / 1024 + return f'{size_mb:+.2f} MB' + else: + return f'{size_kb} KB' + + +def measure_rust(_args): + result = {} + + for target in RUST_TARGETS: + path = f'rust/target/{target}/release/{LIB_NAME}' + file_stats = os.stat(path) + file_size_kb = file_stats.st_size / 1024 + result[target] = int(file_size_kb) + + print(json.dumps(result)) + + +def compare_sizes(args): + def display_target(target: str, before_kb: int, current_kb: int): + diff_kb = current_kb - before_kb + print(f'➡️ **{target}**') + print("```diff") + if before_kb == current_kb: + print(f' Size {display_size(before_kb)}') + else: + print(f'- Size {display_size(before_kb)}') + print(f'+ Size {display_size(current_kb)} \t {display_size(diff_kb)}') + print("```") + + current_json = json.load(open(args.current, 'r')) + before_json = {} + if os.path.isfile(args.before): + before_json = json.load(open(args.before, 'r')) + + print("## Binary size comparison") + print() + for target, current_kb in current_json.items(): + before_kb = before_json.get(target, 0) + display_target(target, before_kb, current_kb) + print() + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="GitHub CI helper functions") + subparsers = parser.add_subparsers() + + measure_parser = subparsers.add_parser('measure-rust', help="Measures Rust release binaries'") + measure_parser.set_defaults(func=measure_rust) + + compare_parser = subparsers.add_parser('compare', + help="Compares binary sizes. Takes 'before' and 'current' file names") + compare_parser.add_argument('--before', type=str) + compare_parser.add_argument('--current', type=str) + compare_parser.set_defaults(func=compare_sizes) + + args = parser.parse_args() + args.func(args) diff --git a/tools/rust-bindgen b/tools/rust-bindgen index 0c61c54f467..1b0afa3a6a5 100755 --- a/tools/rust-bindgen +++ b/tools/rust-bindgen @@ -52,9 +52,11 @@ if [[ "$NATIVE" == "true" ]]; then cargo build --release fi +export RUSTFLAGS="-Zlocation-detail=none" + if [[ "$WASM" == "true" ]]; then echo "Generating WASM target" - cargo build --target wasm32-unknown-emscripten --release + cargo build -Z build-std=std,panic_abort --target wasm32-unknown-emscripten --release fi if [[ "$ANDROID" == "true" ]]; then @@ -67,13 +69,12 @@ if [[ "$ANDROID" == "true" ]]; then export CC_armv7_linux_androideabi="$NDK_BIN_PATH/armv7a-linux-androideabi$NDK_API_LEVEL-clang" echo "Generating Android targets" - cargo build --target aarch64-linux-android --target armv7-linux-androideabi --target x86_64-linux-android --target i686-linux-android --release + cargo build -Z build-std=std,panic_abort --target aarch64-linux-android --target armv7-linux-androideabi --target x86_64-linux-android --target i686-linux-android --release fi if [[ "$IOS" == "true" ]]; then echo "Generating iOS targets" - cargo build --target aarch64-apple-ios --target aarch64-apple-ios-sim --target x86_64-apple-ios --target aarch64-apple-darwin --target x86_64-apple-darwin --release & - cargo build -Z build-std --target aarch64-apple-ios-macabi --target x86_64-apple-ios-macabi --release & + cargo build -Z build-std=std,panic_abort --target aarch64-apple-ios --target aarch64-apple-ios-sim --target x86_64-apple-ios --target aarch64-apple-darwin --target x86_64-apple-darwin --target aarch64-apple-ios-macabi --target x86_64-apple-ios-macabi --release & wait lipo $BUILD_FOLDER/x86_64-apple-ios/release/$TARGET_NAME $BUILD_FOLDER/aarch64-apple-ios-sim/release/$TARGET_NAME -create -output $BUILD_FOLDER/$TARGET_NAME mkdir -p $BUILD_FOLDER/darwin_universal From b0508750cb7486554bc8e8751d94c4e784defd89 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Tue, 12 Mar 2024 13:22:14 +0100 Subject: [PATCH 060/128] [Solana]: Improve `TransactionDecoder` API (#3723) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Solana]: Do not require to provide a sender address if `RawMessage` specified * [CI] Trigger CI * [CI]: Fix Docker and check-binary-sizes * [CI]: Fix Docker build * [CI]: Fix Docker build №2 --- .github/workflows/linux-ci-rust.yml | 1 + Dockerfile | 1 + rust/chains/tw_solana/src/compiler.rs | 6 ++---- .../tw_solana/src/modules/message_builder.rs | 16 ---------------- .../tw_solana/src/transaction/versioned.rs | 10 ++++++++++ .../tests/chains/solana/solana_transaction.rs | 2 -- tests/chains/Firo/TransactionCompilerTests.cpp | 4 ++-- tools/release-size | 2 +- 8 files changed, 17 insertions(+), 25 deletions(-) diff --git a/.github/workflows/linux-ci-rust.yml b/.github/workflows/linux-ci-rust.yml index 9de4326f918..258c8f2d916 100644 --- a/.github/workflows/linux-ci-rust.yml +++ b/.github/workflows/linux-ci-rust.yml @@ -152,6 +152,7 @@ jobs: ./tools/release-size compare --before previous/release-report.json --current release-report.json > report-diff.md - name: Create or Update Comment + if: github.event_name == 'pull_request' uses: edumserrano/find-create-or-update-comment@v2 with: issue-number: ${{ github.event.pull_request.number }} diff --git a/Dockerfile b/Dockerfile index 7db09d1bd5b..179fa0d8973 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,6 +38,7 @@ ENV CXX=/usr/bin/clang++-14 RUN wget "https://sh.rustup.rs" -O rustup.sh \ && sh rustup.sh -y ENV PATH="/root/.cargo/bin:${PATH}" +RUN rustup default nightly-2024-02-09 RUN cargo install --force cbindgen \ && rustup target add wasm32-unknown-emscripten diff --git a/rust/chains/tw_solana/src/compiler.rs b/rust/chains/tw_solana/src/compiler.rs index 5b3c9f89df8..bd5a8af0297 100644 --- a/rust/chains/tw_solana/src/compiler.rs +++ b/rust/chains/tw_solana/src/compiler.rs @@ -35,13 +35,11 @@ impl SolanaCompiler { input: Proto::SigningInput<'_>, ) -> SigningResult> { let builder = MessageBuilder::new(input); - let signers = builder.signers()?; let unsigned_msg = builder.build()?; - let data_to_sign = TxSigner::preimage_versioned(&unsigned_msg)?; - let signers: Vec<_> = signers - .iter() + let signers: Vec<_> = unsigned_msg + .signers() .map(|addr| Cow::from(addr.to_string().into_bytes())) .collect(); diff --git a/rust/chains/tw_solana/src/modules/message_builder.rs b/rust/chains/tw_solana/src/modules/message_builder.rs index 7b8f6eaee04..59cc70fc739 100644 --- a/rust/chains/tw_solana/src/modules/message_builder.rs +++ b/rust/chains/tw_solana/src/modules/message_builder.rs @@ -57,22 +57,6 @@ impl<'a> MessageBuilder<'a> { Ok(signing_keys) } - pub fn signers(&self) -> SigningResult> { - let mut signers = Vec::default(); - if !self.input.fee_payer.is_empty() { - signers.push(SolanaAddress::from_str(self.input.fee_payer.as_ref())?); - } - - signers.push(self.signer_address()?); - - // Consider matching other transaction types if they may contain other private keys. - if let ProtoTransactionType::create_nonce_account(ref nonce) = self.input.transaction_type { - signers.push(SolanaAddress::from_str(nonce.nonce_account.as_ref())?); - } - - Ok(signers) - } - pub fn external_signatures(&self) -> SigningResult { match self.input.raw_message { Some(ref raw_message) => RawMessageBuilder::external_signatures(raw_message), diff --git a/rust/chains/tw_solana/src/transaction/versioned.rs b/rust/chains/tw_solana/src/transaction/versioned.rs index 71282340222..ade097c4b7d 100644 --- a/rust/chains/tw_solana/src/transaction/versioned.rs +++ b/rust/chains/tw_solana/src/transaction/versioned.rs @@ -65,6 +65,16 @@ impl VersionedMessage { } } + pub fn signers(&self) -> impl Iterator { + let signatures_count = self.num_required_signatures(); + match self { + VersionedMessage::Legacy(legacy) => &legacy.account_keys, + VersionedMessage::V0(v0) => &v0.account_keys, + } + .iter() + .take(signatures_count) + } + pub fn recent_blockhash(&self) -> Blockhash { match self { VersionedMessage::Legacy(legacy) => Blockhash::with_bytes(legacy.recent_blockhash), diff --git a/rust/tw_any_coin/tests/chains/solana/solana_transaction.rs b/rust/tw_any_coin/tests/chains/solana/solana_transaction.rs index 6d00329d02f..afb51dc08d8 100644 --- a/rust/tw_any_coin/tests/chains/solana/solana_transaction.rs +++ b/rust/tw_any_coin/tests/chains/solana/solana_transaction.rs @@ -302,8 +302,6 @@ fn test_solana_decode_transaction_update_blockhash_preimage_hash_and_compile() { // Step 3. Pre-image hash the transaction. let input = Proto::SigningInput { - // There is no matching pubkey in the transaction account keys. - sender: SENDER_PUBLIC_KEY.into(), raw_message: Some(decoded_tx), tx_encoding: Proto::Encoding::Base64, ..Proto::SigningInput::default() diff --git a/tests/chains/Firo/TransactionCompilerTests.cpp b/tests/chains/Firo/TransactionCompilerTests.cpp index 5851ae6ce4f..c5265971f52 100644 --- a/tests/chains/Firo/TransactionCompilerTests.cpp +++ b/tests/chains/Firo/TransactionCompilerTests.cpp @@ -2,7 +2,6 @@ // // Copyright © 2017 Trust Wallet. -#include "Coin.h" #include "HexCoding.h" #include "PublicKey.h" #include "TransactionCompiler.h" @@ -10,10 +9,11 @@ #include "proto/Bitcoin.pb.h" #include "proto/TransactionCompiler.pb.h" +#include +#include #include #include "Bitcoin/Script.h" -#include "Bitcoin/TransactionPlan.h" #include "TestUtilities.h" #include diff --git a/tools/release-size b/tools/release-size index bcd4076f9e8..3d73f4f50f6 100755 --- a/tools/release-size +++ b/tools/release-size @@ -54,7 +54,7 @@ def compare_sizes(args): print("## Binary size comparison") print() for target, current_kb in current_json.items(): - before_kb = before_json.get(target, 0) + before_kb = before_json.get(target, current_kb) display_target(target, before_kb, current_kb) print() From fcef924cbdc68dcce456930e8aca8060bb684fcb Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 13 Mar 2024 09:41:07 +0100 Subject: [PATCH 061/128] [CI]: Fix codegen-v2 CI (#3726) * [CI]: Fix codegen-v2 compilation issue after updating toolchain * Run codegen-v2.yml on each Pull Request * [KMP]: Bump WC version * [CI]: Refactor release sizes report --- .github/workflows/codegen-v2.yml | 4 - codegen-v2/Cargo.lock | 156 ++++++++++++---------------- samples/kmp/shared/build.gradle.kts | 2 +- tools/release-size | 22 ++-- 4 files changed, 82 insertions(+), 102 deletions(-) diff --git a/.github/workflows/codegen-v2.yml b/.github/workflows/codegen-v2.yml index aaa4ca76447..50446778240 100644 --- a/.github/workflows/codegen-v2.yml +++ b/.github/workflows/codegen-v2.yml @@ -3,12 +3,8 @@ name: Codegen-v2 Tests on: push: branches: [ dev, master ] - paths: - - 'codegen-v2/**' pull_request: branches: [ dev, master ] - paths: - - 'codegen-v2/**' env: SCCACHE_GHA_ENABLED: "true" diff --git a/codegen-v2/Cargo.lock b/codegen-v2/Cargo.lock index d52a97822f6..6ed11b1c48a 100644 --- a/codegen-v2/Cargo.lock +++ b/codegen-v2/Cargo.lock @@ -11,12 +11,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - [[package]] name = "block-buffer" version = "0.10.4" @@ -58,9 +52,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -77,9 +71,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", @@ -103,9 +97,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "4.3.6" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "035ef95d03713f2c347a72547b7cd38cbc9af7cd51e6099fb62d586d4a6dee3a" +checksum = "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225" dependencies = [ "log", "pest", @@ -117,15 +111,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.3" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" @@ -135,56 +123,43 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "indexmap" -version = "2.1.0" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", - "hashbrown 0.14.2", + "hashbrown", ] [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "libc" -version = "0.2.142" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "log" -version = "0.4.17" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "once_cell" -version = "1.17.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "pathdiff" @@ -194,19 +169,20 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "pest" -version = "2.5.7" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1403e8401ad5dedea73c626b99758535b342502f8d1e361f4a2dd952749122" +checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" dependencies = [ + "memchr", "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.5.7" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be99c4c1d2fc2769b1d00239431d711d08f6efedcecb8b6e30707160aee99c15" +checksum = "b0d24f72393fd16ab6ac5738bc33cdb6a9aa73f8b902e8fe29cf4e67d7dd1026" dependencies = [ "pest", "pest_generator", @@ -214,9 +190,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.7" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56094789873daa36164de2e822b3888c6ae4b4f9da555a1103587658c805b1e" +checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80" dependencies = [ "pest", "pest_meta", @@ -227,9 +203,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.5.7" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6733073c7cff3d8459fda0e42f13a047870242aed8b509fe98000928975f359e" +checksum = "934cd7631c050f4674352a6e835d5f6711ffbfb9345c2fc0107155ac495ae293" dependencies = [ "once_cell", "pest", @@ -238,42 +214,42 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "serde" -version = "1.0.160" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", @@ -282,9 +258,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.95" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -293,11 +269,11 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.21" +version = "0.9.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c" +checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f" dependencies = [ - "indexmap 1.9.3", + "indexmap", "itoa", "ryu", "serde", @@ -306,9 +282,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -317,9 +293,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.14" +version = "2.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" dependencies = [ "proc-macro2", "quote", @@ -328,18 +304,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", @@ -354,38 +330,38 @@ checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.1.0", + "indexmap", "toml_datetime", "winnow", ] [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unsafe-libyaml" @@ -401,9 +377,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "winnow" -version = "0.5.19" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] diff --git a/samples/kmp/shared/build.gradle.kts b/samples/kmp/shared/build.gradle.kts index 89fb86c4634..93e4a9173b7 100644 --- a/samples/kmp/shared/build.gradle.kts +++ b/samples/kmp/shared/build.gradle.kts @@ -45,7 +45,7 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation("com.trustwallet:wallet-core-kotlin:4.0.27") + implementation("com.trustwallet:wallet-core-kotlin:4.0.28") } } val commonTest by getting { diff --git a/tools/release-size b/tools/release-size index 3d73f4f50f6..c80c71ee873 100755 --- a/tools/release-size +++ b/tools/release-size @@ -17,11 +17,19 @@ LIB_NAME = "libwallet_core_rs.a" def display_size(size_kb: int) -> str: if size_kb >= 10000: size_mb = float(size_kb) / 1024 - return f'{size_mb:+.2f} MB' + return f'{size_mb:.2f} MB' else: return f'{size_kb} KB' +def display_diff(diff_kb: int) -> str: + if abs(diff_kb) >= 10000: + diff_mb = float(diff_kb) / 1024 + return f'{diff_mb:+.2f} MB' + else: + return f'{diff_kb:+} KB' + + def measure_rust(_args): result = {} @@ -37,14 +45,14 @@ def measure_rust(_args): def compare_sizes(args): def display_target(target: str, before_kb: int, current_kb: int): diff_kb = current_kb - before_kb - print(f'➡️ **{target}**') - print("```diff") if before_kb == current_kb: - print(f' Size {display_size(before_kb)}') + print(f'➡️ **{target}**: {display_size(before_kb)}') else: - print(f'- Size {display_size(before_kb)}') - print(f'+ Size {display_size(current_kb)} \t {display_size(diff_kb)}') - print("```") + print(f'➡️ **{target}**:') + print("```diff") + print(f'- {display_size(before_kb)}') + print(f'+ {display_size(current_kb)} \t {display_diff(diff_kb)}') + print("```") current_json = json.load(open(args.current, 'r')) before_json = {} From 35378aa5f420cf70319cf58cbadcc8ccc52ce8aa Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 13 Mar 2024 17:32:08 +0100 Subject: [PATCH 062/128] [Solana]: Fix Solana delegate stake transaction (#3732) * [Solana]: Fix Solana delegate stake transaction * [CI]: Disable check-binary-sizes PR step if opened from forks --- .github/workflows/linux-ci-rust.yml | 6 ++-- .../instruction_builder/stake_instruction.rs | 4 +-- .../tw_solana/src/program/stake_program.rs | 16 ++++++++-- .../tests/get_default_token_address.rs | 29 +++++++++++++++++++ .../tests/chains/solana/solana_sign.rs | 27 +++++++++++++++++ 5 files changed, 74 insertions(+), 8 deletions(-) diff --git a/.github/workflows/linux-ci-rust.yml b/.github/workflows/linux-ci-rust.yml index 258c8f2d916..cd6302e6aa0 100644 --- a/.github/workflows/linux-ci-rust.yml +++ b/.github/workflows/linux-ci-rust.yml @@ -134,7 +134,7 @@ jobs: # Download previous release report, compare the release binary sizes, and post/update a comment at the Pull Request. - name: Download previous release report - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false uses: dawidd6/action-download-artifact@v3 with: commit: ${{github.event.pull_request.base.sha}} @@ -146,13 +146,13 @@ jobs: workflow_conclusion: "" - name: Craft Comment Body - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false run: | # Please note `previous/release-report.json` may not exist if the previous report was not found. ./tools/release-size compare --before previous/release-report.json --current release-report.json > report-diff.md - name: Create or Update Comment - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false uses: edumserrano/find-create-or-update-comment@v2 with: issue-number: ${{ github.event.pull_request.number }} diff --git a/rust/chains/tw_solana/src/modules/instruction_builder/stake_instruction.rs b/rust/chains/tw_solana/src/modules/instruction_builder/stake_instruction.rs index be6ba463aba..a0dfd9139fb 100644 --- a/rust/chains/tw_solana/src/modules/instruction_builder/stake_instruction.rs +++ b/rust/chains/tw_solana/src/modules/instruction_builder/stake_instruction.rs @@ -376,9 +376,9 @@ impl StakeInstructionBuilder { pub fn deposit_stake(args: DepositStakeArgs) -> SigningResult> { let stake_addr = args.stake_account.unwrap_or_else(|| { // no stake address specified, generate a new unique - StakeProgram::address_from_recent_blockhash(args.sender, args.recent_blockhash) + StakeProgram::address_from_recent_blockhash(&args.sender, &args.recent_blockhash) }); - let seed = args.recent_blockhash.to_string(); + let seed = StakeProgram::recent_blockhash_as_seed(&args.recent_blockhash); let authorized = Authorized { staker: args.sender, diff --git a/rust/chains/tw_solana/src/program/stake_program.rs b/rust/chains/tw_solana/src/program/stake_program.rs index 5f2ca1886dc..5161edec832 100644 --- a/rust/chains/tw_solana/src/program/stake_program.rs +++ b/rust/chains/tw_solana/src/program/stake_program.rs @@ -9,15 +9,25 @@ use tw_coin_entry::error::{AddressError, AddressResult}; use tw_hash::sha2::sha256; use tw_hash::H256; +pub const MAX_SEED_LEN: usize = 32; + pub struct StakeProgram; impl StakeProgram { + pub fn recent_blockhash_as_seed(recent_blockhash: &Blockhash) -> String { + recent_blockhash + .to_string() + .chars() + .take(MAX_SEED_LEN) + .collect() + } + pub fn address_from_recent_blockhash( - from: SolanaAddress, - recent_blockhash: Blockhash, + from: &SolanaAddress, + recent_blockhash: &Blockhash, ) -> SolanaAddress { let mut seed = from.bytes().to_vec(); - seed.extend_from_slice(recent_blockhash.to_string().as_bytes()); + seed.extend_from_slice(Self::recent_blockhash_as_seed(recent_blockhash).as_bytes()); seed.extend_from_slice(STAKE_PROGRAM_ID_ADDRESS.bytes().as_slice()); let bytes = H256::try_from(sha256(&seed).as_slice()).expect("sha256 expected to return 32 bytes"); diff --git a/rust/chains/tw_solana/tests/get_default_token_address.rs b/rust/chains/tw_solana/tests/get_default_token_address.rs index 94c3358ed5a..d2b4690f9dc 100644 --- a/rust/chains/tw_solana/tests/get_default_token_address.rs +++ b/rust/chains/tw_solana/tests/get_default_token_address.rs @@ -4,6 +4,7 @@ use std::str::FromStr; use tw_solana::address::SolanaAddress; +use tw_solana::blockhash::Blockhash; use tw_solana::program::stake_program::StakeProgram; fn test_get_default_token_address_impl( @@ -48,3 +49,31 @@ fn test_get_default_token_address() { "ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf", ); } + +fn test_address_from_recent_blockhash_impl(main_address: &str, blockhash: &str, expected: &str) { + let main_address = SolanaAddress::from_str(main_address).unwrap(); + let expected = SolanaAddress::from_str(expected).unwrap(); + let blockhash = Blockhash::from_str(blockhash).unwrap(); + + let actual = StakeProgram::address_from_recent_blockhash(&main_address, &blockhash); + assert_eq!(actual, expected); +} + +#[test] +fn test_address_from_recent_blockhash() { + test_address_from_recent_blockhash_impl( + "zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu", + "11111111111111111111111111111111", + "GQDDc5EVGJZFC7AvpEJ8eoCQ75Yy4gr7eu17frCjvQRQ", + ); + test_address_from_recent_blockhash_impl( + "zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu", + "9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K", + "2Kos1xJRBq3Ae1GnVNBx7HgJhq8KvdUe2bXE4QGdNaXb", + ); + test_address_from_recent_blockhash_impl( + "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V", + "AfzzEC8NVXoxKoHdjXLDVzqwqvvZmgPuqyJqjuHiPY1D", + "Fxhhm82PZVuXEwycT28vGqknUEnVeoHh4UWEnJNQUDbv", + ); +} diff --git a/rust/tw_any_coin/tests/chains/solana/solana_sign.rs b/rust/tw_any_coin/tests/chains/solana/solana_sign.rs index 3f53846e94b..186105c5fda 100644 --- a/rust/tw_any_coin/tests/chains/solana/solana_sign.rs +++ b/rust/tw_any_coin/tests/chains/solana/solana_sign.rs @@ -219,6 +219,33 @@ fn test_solana_sign_delegate_stake_no_stake_account() { assert_eq!(output.encoded, "j24mVM9Zgu5vDZhPLGGuCRXQnP9djNtxdHh4txN3S7dwJsNNL5fbhzGpPgSUAcLGoMVCfF9TuqTYfpfJnb4sJFe1ahM8yPL5HwuKL6py5AZJFi8SWx9fvaVB699dCPo1GT3JoEBLPCZ9o2jQtnwzLkzTYJnKv2axqhKWFE2sz6TBA5J39eZcjMFUYgyxz6Q5S4MWqYQCb8UET2NAEZoKcfy7j8N25WXL6Gj4j3hBZjpHQQNaGaNEprEqyma3ZuVhpGiCALSsuzVLX3wZVo4icXwe952deMFA4tH3BK1jcSQCgfmcKDJ9nd7bdrnUUs4BoMdF1uDZB5LxE2UH8QiqtYvaUcorF4SJ3gPxM5ykbyPsNK1cSYZF9NMpW2GofyC17eELwnHQTQB2kqphxJZu7BahvkwiDPPeeydiXAkBspJ3nc3PCBujv6WJw22ZHw5j6zAP8ZGnCW44pqtWD5qifF9tTKhySKdANNiWifs3tSCCPQqjfJXu14drNinR6VG8rJxS1qgmRYiRQUa7m1vtoaZFRN5qKUeAfoFKkAVaNnMdwgsNqNH4dqBodTCJFs1LkYwhgRZdZGbwXTn1j7vpR3DSnv4g72i2H556srzK53jdUmdv6yfxt516XDSshqZtHnKZ1tudxKjBXwsqT3imDiZFVka9wKWUAYMCi4XZ79CY6Xpsd9c18U2e9TCngQmgkTATFgrqysfraokNffgqWxvsPMugksbvbPjJs3iCzByvphkC9p7hCf6LwbeF8XnVB91EAgRDA4VLE1f9wkcq5zjy879YWJ4r516h3PQszTz1EaJXNAXdbk5Em7eyuuabGP1Q3nijFTL2yhMDsXpgrjAuEAABNxFMd4J1JRMaic615mHrhwociksrsfQK"); } +#[test] +fn test_solana_sign_delegate_stake_no_stake_account_5zrqgk1() { + // Corresponding Solana address: GcQQ1qx822KK14zyfTkMLQMLEZ4a9d88HnKepaA2XbmW. + let private_key = "507479b94db463a33cdc413db98437b4aa8c74b6053aea936d1830c27027b1e9" + .decode_hex() + .unwrap(); + let delegate = Proto::DelegateStake { + validator_pubkey: "BWkvytz3MAiLkUbMuYK5yV1VYThbBYYQYG3gdef8NLw5".into(), + value: 10000000, + ..Proto::DelegateStake::default() + }; + let input = Proto::SigningInput { + private_key: private_key.into(), + recent_blockhash: "6ocSmEiuSHkN5yKDUaQrJHCzWCYXuFk3g3jB4rN1uDzv".into(), + transaction_type: TransactionType::delegate_stake_transaction(delegate), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + // Successfully broadcasted: + // https://solscan.io/tx/5zRQgK1oc1oHLhsM68AM2pSUUqif6VMMnuUKjyZgUsdySaNRufvzoEu8D8SF99GThw8iZiwRbPhnsDePpnDKKmRq + assert_eq!(output.encoded, "nuo7rnbTG4QLqpjYoPPPhDusRjpEVDyzos8YvdX67sDaw2XwfTJNm7eXcdxqjkgLqzFiHbD7kvm1i7f1caGjxZwTEzWqNkxK28YXJXU4S2NbQFa7HwSdamF38PLgKwNKmnUkh1quaZDFK3X5swL49QPxFhtSwFcFo9TphPsfeFjMbqqsLR8vjzLkePTrWKUSMmbwMG8U9TDEeWrsWbKaFeWsRQxzYP7ubDokPLVxGJopaEUobtUGyRA5K2A9H4dbuN7pZJSLwL926hGJQphjiXuJhD9PqY5TCRAq9PhyS6jtYP6ToQUiPfvigm5aHwb5rtEZKu5gM393G7LKfedMi263FVSnJyAb41i4xZXTMD3HdKiDBF5UomqCQEyPQhM4X8mYzPDPofRZfQjz2xS1fDesxY3BWMxwKQ6bgtazJDprBWWNJ7ULpHFEJ8wXtHAR3CfTWowQJ23zsKNUPbb6Ft4Lng3DJrRkMcTvvecLx9nnNqyuwjkqcBysWqJ4pJdvLC4QAnwoZzKAn8uzTKUSA85gSeE96RUmRVE4CgtPkZ7jYgmsSS5XgcC7g4XegHHJFQ3iMG4Uuxft635txXHAbkh6x9Ra9JnPFUq6Zdgc9UyCWrhhaKDvfmFgC2K7tykqHLXdDninmR2s7zmc1wsoGYeGvy2NbLTU3mRpyVwQPFaKPDPSisJYKYY4HnBQfG89wRLh4jkRsrH9snvxYwhgRLcsaMniwGNPpQEU4tLAkxHwco32rnxyFwycaPqEYsYpTR5vc5RUrnyy4nhEx1V79hqBb7NScxAbBsaBXvYpYitgdfX1h19uBr7A7uqZ3mqptKqA2Q3VjZthnLLGjJNVuR4BNJEa7VzPnR8SLCckjVycSHkbG26cUmB985K5"); +} + #[test] fn test_solana_sign_delegate_stake_with_account() { let delegate = Proto::DelegateStake { From b6cb1be7c33d2567a3acd1cc5ddc129ba2eea2ff Mon Sep 17 00:00:00 2001 From: yeahyear <138094847+yetyear@users.noreply.github.com> Date: Thu, 14 Mar 2024 01:16:17 +0800 Subject: [PATCH 063/128] chore: fix some comments (#3728) Signed-off-by: yetyear Co-authored-by: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> --- rust/tw_bitcoin/tests/p2tr_script_path.rs | 2 +- rust/tw_utxo/src/compiler.rs | 2 +- trezor-crypto/crypto/bignum.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/tw_bitcoin/tests/p2tr_script_path.rs b/rust/tw_bitcoin/tests/p2tr_script_path.rs index aeb0e901be3..7f70c76b772 100644 --- a/rust/tw_bitcoin/tests/p2tr_script_path.rs +++ b/rust/tw_bitcoin/tests/p2tr_script_path.rs @@ -106,7 +106,7 @@ fn coin_entry_custom_script_path() { .control_block(&(payload.to_owned(), LeafVersion::TapScript)) .unwrap(); - // Provide the the payload and control block directly to the builder. + // Provide the payload and control block directly to the builder. let tx1 = Proto::Input { txid: txid.as_slice().into(), vout: 0, diff --git a/rust/tw_utxo/src/compiler.rs b/rust/tw_utxo/src/compiler.rs index 822cd8a3542..cdb2625e21e 100644 --- a/rust/tw_utxo/src/compiler.rs +++ b/rust/tw_utxo/src/compiler.rs @@ -182,7 +182,7 @@ impl Compiler { let weight_estimate = tx.weight().to_wu() + total_input_weight; let fee_estimate = (weight_estimate + 3) / 4 * proto.weight_base; - // Check if there are enough inputs to cover the the full output and fee estimate. + // Check if there are enough inputs to cover the full output and fee estimate. if total_input_amount < total_output_amount + fee_estimate { return Err(Error::from(Proto::Error::Error_insufficient_inputs)); } diff --git a/trezor-crypto/crypto/bignum.c b/trezor-crypto/crypto/bignum.c index 85b8b0172e7..5791448d100 100644 --- a/trezor-crypto/crypto/bignum.c +++ b/trezor-crypto/crypto/bignum.c @@ -46,7 +46,7 @@ A limb of a bignum256 is *normalized* iff it's less than 2**29. A bignum256 is *normalized* iff every its limb is normalized. A number is *fully reduced modulo p* iff it is less than p. - A number is *partly reduced modulo p* iff is is less than 2*p. + A number is *partly reduced modulo p* iff it is less than 2*p. The number p is usually a prime number such that 2^256 - 2^224 <= p <= 2^256. All functions except bn_fast_mod expect that all their bignum256 inputs are From b92d1de562c05114e57da521da629d65f116c414 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Fri, 15 Mar 2024 09:20:38 +0100 Subject: [PATCH 064/128] [ZetaChain]: Deposit BTC and call a smart contract in zEVM (#3739) * feat(zetachain): Add an optional `output_op_return_index` to `SigningInput` and `Plan` * feat(zetachain): Add a unit test --- src/Bitcoin/SigningInput.cpp | 3 + src/Bitcoin/SigningInput.h | 4 + src/Bitcoin/TransactionBuilder.cpp | 1 + src/Bitcoin/TransactionBuilder.h | 15 +++- src/Bitcoin/TransactionPlan.h | 17 ++++- src/Zen/TransactionBuilder.h | 17 +++-- src/proto/Bitcoin.proto | 13 ++++ .../chains/Bitcoin/TWBitcoinSigningTests.cpp | 76 +++++++++++++++++++ tests/chains/Bitcoin/TransactionPlanTests.cpp | 1 + 9 files changed, 137 insertions(+), 10 deletions(-) diff --git a/src/Bitcoin/SigningInput.cpp b/src/Bitcoin/SigningInput.cpp index 3367652dd4a..bd7c86f7378 100644 --- a/src/Bitcoin/SigningInput.cpp +++ b/src/Bitcoin/SigningInput.cpp @@ -33,6 +33,9 @@ SigningInput::SigningInput(const Proto::SigningInput& input) { plan = TransactionPlan(input.plan()); } outputOpReturn = data(input.output_op_return()); + if (input.has_output_op_return_index()) { + outputOpReturnIndex = input.output_op_return_index().index(); + } lockTime = input.lock_time(); time = input.time(); diff --git a/src/Bitcoin/SigningInput.h b/src/Bitcoin/SigningInput.h index a2b40419323..86ced0f1122 100644 --- a/src/Bitcoin/SigningInput.h +++ b/src/Bitcoin/SigningInput.h @@ -64,6 +64,10 @@ class SigningInput { Data outputOpReturn; + // Optional index of the OP_RETURN output in the transaction. + // If not set, OP_RETURN output will be pushed as the latest output. + MaybeIndex outputOpReturnIndex; + uint32_t lockTime = 0; uint32_t time = 0; diff --git a/src/Bitcoin/TransactionBuilder.cpp b/src/Bitcoin/TransactionBuilder.cpp index b343b2bd924..beb0a7d682f 100644 --- a/src/Bitcoin/TransactionBuilder.cpp +++ b/src/Bitcoin/TransactionBuilder.cpp @@ -85,6 +85,7 @@ TransactionPlan TransactionBuilder::plan(const SigningInput& input) { if (input.outputOpReturn.size() > 0) { plan.outputOpReturn = input.outputOpReturn; } + plan.outputOpReturnIndex = input.outputOpReturnIndex; bool maxAmount = input.useMaxAmount; Amount totalAmount = input.amount + input.extraOutputsAmount; diff --git a/src/Bitcoin/TransactionBuilder.h b/src/Bitcoin/TransactionBuilder.h index 06e876c2e22..667fbe190c7 100644 --- a/src/Bitcoin/TransactionBuilder.h +++ b/src/Bitcoin/TransactionBuilder.h @@ -48,15 +48,22 @@ class TransactionBuilder { } // Optional OP_RETURN output - if (plan.outputOpReturn.size() > 0) { + if (!plan.outputOpReturn.empty()) { auto lockingScriptOpReturn = Script::buildOpReturnScript(plan.outputOpReturn); - if (lockingScriptOpReturn.bytes.size() == 0) { + if (lockingScriptOpReturn.bytes.empty()) { return Result::failure(Common::Proto::Error_invalid_memo); } - tx.outputs.emplace_back(0, lockingScriptOpReturn); + + auto emplace_at = tx.outputs.end(); + if (plan.outputOpReturnIndex.has_value()) { + emplace_at = tx.outputs.begin(); + std::advance(emplace_at, plan.outputOpReturnIndex.value()); + } + int64_t amount = 0; + tx.outputs.emplace(emplace_at, amount, lockingScriptOpReturn); } - // extra outputs + // extra outputs (always in the end of the outputs list) for (auto& o : input.extraOutputs) { auto output = prepareOutputWithScript(o.first, o.second, input.coinType); if (!output.has_value()) { diff --git a/src/Bitcoin/TransactionPlan.h b/src/Bitcoin/TransactionPlan.h index ddfc2dc4810..5124a301ab1 100644 --- a/src/Bitcoin/TransactionPlan.h +++ b/src/Bitcoin/TransactionPlan.h @@ -9,8 +9,12 @@ #include "Data.h" #include "../proto/Bitcoin.pb.h" +#include + namespace TW::Bitcoin { +using MaybeIndex = std::optional; + /// Describes a preliminary transaction plan. struct TransactionPlan { /// Amount to be received at the other end. @@ -39,6 +43,10 @@ struct TransactionPlan { Data outputOpReturn; + // Optional index of the OP_RETURN output in the transaction. + // If not set, OP_RETURN output will be pushed as the latest output. + MaybeIndex outputOpReturnIndex; + Common::Proto::SigningError error = Common::Proto::SigningError::OK; TransactionPlan() = default; @@ -54,7 +62,11 @@ struct TransactionPlan { , preBlockHeight(plan.preblockheight()) , outputOpReturn(plan.output_op_return().begin(), plan.output_op_return().end()) , error(plan.error()) - {} + { + if (plan.has_output_op_return_index()) { + outputOpReturnIndex = plan.output_op_return_index().index(); + } + } Proto::TransactionPlan proto() const { auto plan = Proto::TransactionPlan(); @@ -69,6 +81,9 @@ struct TransactionPlan { plan.set_preblockhash(preBlockHash.data(), preBlockHash.size()); plan.set_preblockheight(preBlockHeight); plan.set_output_op_return(outputOpReturn.data(), outputOpReturn.size()); + if (outputOpReturnIndex.has_value()) { + plan.mutable_output_op_return_index()->set_index(static_cast(outputOpReturnIndex.value())); + } plan.set_error(error); return plan; } diff --git a/src/Zen/TransactionBuilder.h b/src/Zen/TransactionBuilder.h index b3a657f2e6c..7abd9d51cec 100644 --- a/src/Zen/TransactionBuilder.h +++ b/src/Zen/TransactionBuilder.h @@ -55,15 +55,22 @@ struct TransactionBuilder { } // Optional OP_RETURN output - if (plan.outputOpReturn.size() > 0) { + if (!plan.outputOpReturn.empty()) { auto lockingScriptOpReturn = Bitcoin::Script::buildOpReturnScript(plan.outputOpReturn); - if (lockingScriptOpReturn.bytes.size() == 0) { + if (lockingScriptOpReturn.bytes.empty()) { return Result::failure(Common::Proto::Error_invalid_memo); } - tx.outputs.push_back(Bitcoin::TransactionOutput(0, lockingScriptOpReturn)); + + auto emplace_at = tx.outputs.end(); + if (plan.outputOpReturnIndex.has_value()) { + emplace_at = tx.outputs.begin(); + std::advance(emplace_at, plan.outputOpReturnIndex.value()); + } + const int64_t amount = 0; + tx.outputs.emplace(emplace_at, amount, lockingScriptOpReturn); } - // extra outputs + // extra outputs (always in the end of the outputs list) for (auto& o : input.extraOutputs) { auto output = prepareOutputWithScript(o.first, o.second, input.coinType, blockHash, blockHeight); if (!output.has_value()) { @@ -71,7 +78,7 @@ struct TransactionBuilder { } tx.outputs.push_back(output.value()); } - + return Result(tx); } diff --git a/src/proto/Bitcoin.proto b/src/proto/Bitcoin.proto index 0add08247a7..7e525a0a880 100644 --- a/src/proto/Bitcoin.proto +++ b/src/proto/Bitcoin.proto @@ -95,6 +95,11 @@ message OutputAddress { int64 amount = 2; } +// Optional index of a corresponding output in the transaction. +message OutputIndex { + uint32 index = 1; +} + // Input data necessary to create a signed transaction. message SigningInput { // Hash type to use when signing. @@ -142,6 +147,10 @@ message SigningInput { // Optional zero-amount, OP_RETURN output bytes output_op_return = 13; + // Optional index of the OP_RETURN output in the transaction. + // If not set, OP_RETURN output will be pushed as the latest output. + OutputIndex output_op_return_index = 26; + // Optional additional destination addresses, additional to first to_address output repeated OutputAddress extra_outputs = 14; @@ -199,6 +208,10 @@ message TransactionPlan { // Optional zero-amount, OP_RETURN output bytes output_op_return = 8; + // Optional index of the OP_RETURN output in the transaction. + // If not set, OP_RETURN output will be pushed as the latest output. + OutputIndex output_op_return_index = 14; + // zen & bitcoin diamond preblockhash bytes preblockhash = 9; diff --git a/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp b/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp index d3e64144553..fa2fb84e5b2 100644 --- a/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp +++ b/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp @@ -885,6 +885,82 @@ TEST(BitcoinSigning, SignPlanTransactionNotSufficientAfterDustFiltering) { EXPECT_EQ(output.error(), Common::Proto::Error_not_enough_utxos); } +// Deposit 0.0001 BTC from bc1q2sphzvc2uqmxqte2w9dd4gzy4sy9vvfv0me9ke to 0xa8491D40d4F71A752cA41DA0516AEd80c33a1B56 on ZETA mainnet. +// https://www.zetachain.com/docs/developers/omnichain/bitcoin/#example-1-deposit-btc-into-an-account-in-zevm +TEST(BitcoinSigning, SignDepositBtcToZetaChain) { + const auto myPrivateKey = PrivateKey(parse_hex("428d66be0b5a620f126a00fa67637222ce3dc9badfe5c605189520760810cfac")); + auto myPublicKey = myPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + auto utxoPubkeyHash = Hash::ripemd(Hash::sha256(myPublicKey.bytes)); + auto redeemScript = Script::buildPayToWitnessPublicKeyHash(utxoPubkeyHash); + + const auto ownAddress = "bc1q2sphzvc2uqmxqte2w9dd4gzy4sy9vvfv0me9ke"; + const auto ownZetaEvmAddress = parse_hex("a8491D40d4F71A752cA41DA0516AEd80c33a1B56"); + // https://www.zetachain.com/docs/reference/glossary/#tss + const auto zetaTssAddress = "bc1qm24wp577nk8aacckv8np465z3dvmu7ry45el6y"; + + auto utxoHash0 = + parse_hex("17a6adb5db1e33c87467a58aa31cddbb3800052315015cf3cf1c2b0119310e20"); + std::reverse(utxoHash0.begin(), utxoHash0.end()); + auto utxoAmount0 = 20000; + auto utxoOutputIndex0 = 0; + + const auto sendAmount = 10000; + const auto dustAmount = 546; + + Proto::SigningInput signingInput; + signingInput.set_coin_type(TWCoinTypeBitcoin); + signingInput.set_hash_type(TWBitcoinSigHashTypeAll); + signingInput.set_byte_fee(15); + signingInput.set_amount(sendAmount); + signingInput.set_to_address(zetaTssAddress); + signingInput.set_change_address(ownAddress); + signingInput.set_fixed_dust_threshold(dustAmount); + signingInput.set_output_op_return(ownZetaEvmAddress.data(), ownZetaEvmAddress.size()); + // OP_RETURN must be the second output before the change. + signingInput.mutable_output_op_return_index()->set_index(1); + + signingInput.add_private_key(myPrivateKey.bytes.data(), myPrivateKey.bytes.size()); + + // Add UTXO 0 + auto utxo0 = signingInput.add_utxo(); + utxo0->set_script(redeemScript.bytes.data(), redeemScript.bytes.size()); + utxo0->set_amount(utxoAmount0); + utxo0->mutable_out_point()->set_hash( + std::string(utxoHash0.begin(), utxoHash0.end())); + utxo0->mutable_out_point()->set_index(utxoOutputIndex0); + utxo0->mutable_out_point()->set_sequence(UINT32_MAX); + + Proto::SigningOutput output; + ANY_SIGN(signingInput, TWCoinTypeBitcoin); + EXPECT_EQ(output.error(), Common::Proto::SigningError::OK); + + EXPECT_EQ(output.transaction().outputs_size(), 3); + // P2WPKH to the TSS address. + EXPECT_EQ(output.transaction().outputs(0).value(), sendAmount); + // OP_RETURN + EXPECT_EQ(output.transaction().outputs(1).value(), 0); + // Transaction fee. + EXPECT_EQ(output.transaction().outputs(2).value(), 7420); + + // Successfully broadcasted: + // https://mempool.space/tx/2b871b6c1112ad0a777f6db1f7a7709154c4d9af8e771ba4eca148915f830e9d + // https://explorer.zetachain.com/cc/tx/0x269e319478f8849247abb28b33a7b8e0a849dab4551bab328bf58bf67b02a807 + const auto expectedTx = "01000000000101200e3119012b1ccff35c011523050038bbdd1ca38aa56774c8331edbb5ada6170000000000ffffffff031027000000000000160014daaae0d3de9d8fdee31661e61aea828b59be78640000000000000000166a14a8491d40d4f71a752ca41da0516aed80c33a1b56fc1c000000000000160014540371330ae036602f2a715adaa044ac0856312c02483045022100e29731f7474f9103c6df3434c8c62a540a21ad0e10e23df343b1e81e4b26110602202d37fb4fee5341a41f9e4e65ba2d3e0d2309425ea9806d94eb268efe6f21007001210369cdaf80b4a5fdad91e9face90e848225512884ec2e3ed572ca11dc68e75054700000000"; + + EXPECT_EQ(hex(output.encoded()), expectedTx); + + Proto::TransactionPlan plan; + ANY_PLAN(signingInput, plan, TWCoinTypeBitcoin); + EXPECT_EQ(plan.error(), Common::Proto::SigningError::OK); + EXPECT_TRUE(plan.has_output_op_return_index()); + EXPECT_EQ(plan.output_op_return_index().index(), 1); + + *signingInput.mutable_plan() = plan; + ANY_SIGN(signingInput, TWCoinTypeBitcoin); + // The result has to be the same as signing without transaction planning. + EXPECT_EQ(hex(output.encoded()), expectedTx); +} + TEST(BitcoinSigning, SignP2PKH) { auto input = buildInputP2PKH(); diff --git a/tests/chains/Bitcoin/TransactionPlanTests.cpp b/tests/chains/Bitcoin/TransactionPlanTests.cpp index 5a4d6597f74..357d4784979 100644 --- a/tests/chains/Bitcoin/TransactionPlanTests.cpp +++ b/tests/chains/Bitcoin/TransactionPlanTests.cpp @@ -715,6 +715,7 @@ TEST(TransactionPlan, OpReturn) { EXPECT_TRUE(verifyPlan(txPlan, {342101}, 300000, 205 * byteFee)); EXPECT_EQ(txPlan.outputOpReturn.size(), 59ul); EXPECT_EQ(hex(txPlan.outputOpReturn), "535741503a54484f522e52554e453a74686f72317470657263616d6b6b7865633071306a6b366c74646e6c7176737732396775617038776d636c3a"); + EXPECT_FALSE(txPlan.outputOpReturnIndex.has_value()); auto& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); EXPECT_EQ(feeCalculator.calculate(1, 2, byteFee), 174 * byteFee); From 102fa2b6c5a29e8554eed3eb4a4e7d1a4662d5a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 09:58:32 +0100 Subject: [PATCH 065/128] Bump google.golang.org/protobuf from 1.27.1 to 1.33.0 in /samples/go (#3737) Bumps google.golang.org/protobuf from 1.27.1 to 1.33.0. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> --- samples/go/go.mod | 2 +- samples/go/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/go/go.mod b/samples/go/go.mod index 2835844a409..a089a1d470f 100644 --- a/samples/go/go.mod +++ b/samples/go/go.mod @@ -2,4 +2,4 @@ module tw go 1.16 -require google.golang.org/protobuf v1.27.1 +require google.golang.org/protobuf v1.33.0 diff --git a/samples/go/go.sum b/samples/go/go.sum index 03b1917b5a4..3575a38aca0 100644 --- a/samples/go/go.sum +++ b/samples/go/go.sum @@ -4,5 +4,5 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= From c4ffde3d170821fd0c5c50512505222c59e47db7 Mon Sep 17 00:00:00 2001 From: 10gic Date: Fri, 15 Mar 2024 20:19:21 +0800 Subject: [PATCH 066/128] Fix issue 3724: The generated files of EVM-compatible chain cannot pass test (#3725) Co-authored-by: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> --- codegen-v2/src/codegen/template_generator.rs | 2 +- codegen-v2/src/registry.rs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/codegen-v2/src/codegen/template_generator.rs b/codegen-v2/src/codegen/template_generator.rs index 724066d47ac..7b725ba74e7 100644 --- a/codegen-v2/src/codegen/template_generator.rs +++ b/codegen-v2/src/codegen/template_generator.rs @@ -38,7 +38,7 @@ impl TemplateGenerator { .add_pattern("{TW_CRATE_NAME}", coin.id.to_tw_crate_name()) .add_pattern("{COIN_ID}", coin.id.as_str()) .add_pattern("{COIN_TYPE}", coin.coin_type()) - .add_pattern("{COIN_NAME}", &coin.name) + .add_pattern("{COIN_NAME}", if coin.display_name.len() > 0 { &coin.display_name } else { &coin.name }) .add_pattern("{SYMBOL}", &coin.symbol) .add_pattern("{DECIMALS}", coin.decimals) .add_pattern("{P2PKH_PREFIX}", coin.p2pkh_prefix) diff --git a/codegen-v2/src/registry.rs b/codegen-v2/src/registry.rs index 853f0d2249e..c83ef174dd0 100644 --- a/codegen-v2/src/registry.rs +++ b/codegen-v2/src/registry.rs @@ -33,6 +33,9 @@ pub struct CoinExplorer { pub struct CoinItem { pub id: CoinId, pub name: String, + #[serde(rename = "displayName")] + #[serde(default)] + pub display_name: String, #[serde(rename = "coinId")] pub coin_id_number: u32, pub symbol: String, From adb20c0577bc8c5dce5e6bb6d865c3727a4d9e96 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Thu, 21 Mar 2024 18:03:15 +0100 Subject: [PATCH 067/128] feat(merlin): Add support for Merlin EVM chain (#3752) --- .../blockchains/CoinAddressDerivationTests.kt | 2 +- docs/registry.md | 1 + include/TrustWalletCore/TWCoinType.h | 1 + .../core/test/CoinAddressDerivationTests.kt | 2 +- registry.json | 30 +++++++++++++++++++ .../tests/coin_address_derivation_test.rs | 1 + swift/Tests/CoinAddressDerivationTests.swift | 3 +- tests/chains/Merlin/TWCoinTypeTests.cpp | 29 ++++++++++++++++++ tests/common/CoinAddressDerivationTests.cpp | 1 + 9 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 tests/chains/Merlin/TWCoinTypeTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index 0d0e8f18103..5e167705863 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -47,7 +47,7 @@ class CoinAddressDerivationTests { FANTOM, CELO, CRONOSCHAIN, SMARTBITCOINCASH, KUCOINCOMMUNITYCHAIN, BOBA, METIS, AURORA, EVMOS, MOONRIVER, MOONBEAM, KAVAEVM, KLAYTN, METER, OKXCHAIN, POLYGONZKEVM, SCROLL, CONFLUXESPACE, ACALAEVM, OPBNB, NEON, BASE, LINEA, GREENFIELD, MANTLE, ZENEON, MANTAPACIFIC, - ZETAEVM, + ZETAEVM, MERLIN, -> assertEquals("0x8f348F300873Fd5DA36950B2aC75a26584584feE", address) RONIN -> assertEquals("ronin:8f348F300873Fd5DA36950B2aC75a26584584feE", address) diff --git a/docs/registry.md b/docs/registry.md index a2023da41f1..8d053718316 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -87,6 +87,7 @@ This list is generated from [./registry.json](../registry.json) | 2301 | Qtum | QTUM | | | | 2718 | Nebulas | NAS | | | | 3030 | Hedera | HBAR | | | +| 4200 | Merlin | ETH | | | | 5000 | Mantle | MNT | | | | 5600 | BNB Greenfield | BNB | | | | 6060 | GoChain | GO | | | diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index e65dc4878e7..7722e0bea4c 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -181,6 +181,7 @@ enum TWCoinType { TWCoinTypeNativeZetaChain = 10007000, TWCoinTypeZetaEVM = 20007000, TWCoinTypeDydx = 22000118, + TWCoinTypeMerlin = 4200, // end_of_tw_coin_type_marker_do_not_modify }; diff --git a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt index a586146c54d..8c8046199ad 100644 --- a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt +++ b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt @@ -40,7 +40,7 @@ class CoinAddressDerivationTests { Fantom, Celo, CronosChain, SmartBitcoinCash, KuCoinCommunityChain, Boba, Metis, Aurora, Evmos, Moonriver, Moonbeam, KavaEvm, Klaytn, Meter, OKXChain, PolygonzkEVM, Scroll, ConfluxeSpace, AcalaEVM, OpBNB, Neon, Base, Linea, Greenfield, Mantle, ZenEON, MantaPacific, - ZetaEVM, + ZetaEVM, Merlin, -> "0x8f348F300873Fd5DA36950B2aC75a26584584feE" Ronin -> "ronin:8f348F300873Fd5DA36950B2aC75a26584584feE" diff --git a/registry.json b/registry.json index 941c1312d0a..15717d5b5b4 100644 --- a/registry.json +++ b/registry.json @@ -4628,5 +4628,35 @@ "rpc": "https://pacific-rpc.manta.network/http", "documentation": "https://docs.manta.network/docs/Introduction" } + }, + { + "id": "merlin", + "name": "Merlin", + "coinId": 4200, + "symbol": "ETH", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "4200", + "addressHasher": "keccak256", + "explorer": { + "url": "https://scan.merlinchain.io", + "txPath": "/tx/", + "accountPath": "/address/", + "sampleTx": "0xca6f2891959b669237738dd0bc2c1051d966778c9de90b94165032ce58364212", + "sampleAccount": "0xf7e017b3f61bD3410A3b03D7DAD7699FD6780584" + }, + "info": { + "url": "https://merlinchain.io", + "source": "https://merlinchain.io", + "rpc": "https://rpc.merlinchain.io", + "documentation": "https://docs.merlinchain.io/merlin-docs" + } } ] diff --git a/rust/tw_any_coin/tests/coin_address_derivation_test.rs b/rust/tw_any_coin/tests/coin_address_derivation_test.rs index b7abadbb806..6225dc7304a 100644 --- a/rust/tw_any_coin/tests/coin_address_derivation_test.rs +++ b/rust/tw_any_coin/tests/coin_address_derivation_test.rs @@ -86,6 +86,7 @@ fn test_coin_address_derivation() { | CoinType::ZenEON | CoinType::MantaPacific | CoinType::ZetaEVM + | CoinType::Merlin // end_of_evm_address_derivation_tests_marker_do_not_modify => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", CoinType::Bitcoin diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 339b2ccde19..e27974f5040 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -114,7 +114,8 @@ class CoinAddressDerivationTests: XCTestCase { .mantle, .zenEON, .mantaPacific, - .zetaEVM: + .zetaEVM, + .merlin: let expectedResult = "0x8f348F300873Fd5DA36950B2aC75a26584584feE" assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .ronin: diff --git a/tests/chains/Merlin/TWCoinTypeTests.cpp b/tests/chains/Merlin/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..4f045742865 --- /dev/null +++ b/tests/chains/Merlin/TWCoinTypeTests.cpp @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#include "TestUtilities.h" +#include +#include + +TEST(TWMerlinCoinType, TWCoinType) { + const auto coin = TWCoinTypeMerlin; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xca6f2891959b669237738dd0bc2c1051d966778c9de90b94165032ce58364212")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xf7e017b3f61bD3410A3b03D7DAD7699FD6780584")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "merlin"); + assertStringsEqual(name, "Merlin"); + assertStringsEqual(symbol, "ETH"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainEthereum); + ASSERT_EQ(TWCoinTypeP2pkhPrefix(coin), 0); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0); + assertStringsEqual(txUrl, "https://scan.merlinchain.io/tx/0xca6f2891959b669237738dd0bc2c1051d966778c9de90b94165032ce58364212"); + assertStringsEqual(accUrl, "https://scan.merlinchain.io/address/0xf7e017b3f61bD3410A3b03D7DAD7699FD6780584"); +} diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index 93411f2e8cf..398db624bfd 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -84,6 +84,7 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeZenEON: case TWCoinTypeMantaPacific: case TWCoinTypeZetaEVM: + case TWCoinTypeMerlin: // end_of_evm_address_derivation_tests_marker_do_not_modify EXPECT_EQ(address, "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); break; From 2c877a324a21f3a005022e10cb17dfcebed44a98 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Thu, 21 Mar 2024 18:28:52 +0100 Subject: [PATCH 068/128] [Boost]: Reduce dependency on Boost library (#3751) * [Misc]: Do not use boost/algorithm/string.hpp * [Misc]: Add string.cpp tests * [Misc]: Replace * TODO make sure if the changed test covers all cases * [Boost]: Replace with a Rust FFI * [Boost]: Minimize the use of * [Misc]: Refactor `wallet_core_rs` crate * Remove `tw_proto` test FFIs * [Boost]: Refactor includes * [Boost]: Refactor includes * [Boost]: Minor changes --- rust/Cargo.lock | 13 ++- rust/tw_proto/Cargo.toml | 2 - rust/tw_proto/src/ffi.rs | 100 ------------------ rust/tw_proto/src/lib.rs | 2 - rust/tw_proto/tests/proto_ffi_tests.rs | 24 ----- rust/wallet_core_rs/Cargo.toml | 52 +++++---- rust/wallet_core_rs/cbindgen.toml | 10 -- rust/wallet_core_rs/src/ffi/bitcoin/mod.rs | 1 - rust/wallet_core_rs/src/ffi/ethereum/mod.rs | 2 - rust/wallet_core_rs/src/ffi/mod.rs | 5 + rust/wallet_core_rs/src/ffi/solana/mod.rs | 2 - rust/wallet_core_rs/src/ffi/utils/mod.rs | 5 + rust/wallet_core_rs/src/ffi/utils/uuid_ffi.rs | 16 +++ rust/wallet_core_rs/src/lib.rs | 10 +- rust/wallet_core_rs/tests/uuid.rs | 40 +++++++ src/Aion/RLP.h | 4 +- src/Aion/Signer.cpp | 4 - src/Aion/Transaction.cpp | 1 - src/Aion/Transaction.h | 4 +- src/EOS/Asset.cpp | 13 ++- src/EOS/Name.cpp | 4 +- src/Everscale/CommonTON/Cell.cpp | 10 +- src/Everscale/CommonTON/Cell.h | 2 - src/Everscale/CommonTON/CellBuilder.h | 3 +- src/Everscale/CommonTON/Messages.h | 2 - src/Everscale/Wallet.h | 1 - src/FullSS58Address.h | 1 - src/Harmony/Signer.h | 1 - src/Harmony/Staking.h | 1 - src/IOST/Signer.cpp | 3 - src/Icon/Signer.cpp | 1 - src/Keystore/StoredKey.cpp | 11 +- src/Nano/Signer.cpp | 6 +- src/Polkadot/ScaleCodec.h | 11 +- src/Tezos/MessageSigner.cpp | 3 - src/VeChain/Signer.h | 1 - src/WebAuthn.cpp | 1 - src/algorithm/string.hpp | 29 ++++- src/uint256.h | 15 +-- tests/chains/Everscale/CellTests.cpp | 2 +- tests/chains/Ontology/TransactionTests.cpp | 1 - tests/common/Uint256Tests.cpp | 6 -- tests/common/algorithm/string.cpp | 12 +++ .../common/rust/bindgen/WalletCoreRsTests.cpp | 76 ------------- 44 files changed, 181 insertions(+), 332 deletions(-) delete mode 100644 rust/tw_proto/src/ffi.rs delete mode 100644 rust/tw_proto/tests/proto_ffi_tests.rs create mode 100644 rust/wallet_core_rs/src/ffi/utils/mod.rs create mode 100644 rust/wallet_core_rs/src/ffi/utils/uuid_ffi.rs create mode 100644 rust/wallet_core_rs/tests/uuid.rs delete mode 100644 tests/common/rust/bindgen/WalletCoreRsTests.cpp diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 22843571589..231e9ff182b 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1973,8 +1973,6 @@ dependencies = [ "arbitrary 1.3.0", "pb-rs", "quick-protobuf", - "tw_encoding", - "tw_memory", ] [[package]] @@ -2074,6 +2072,15 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "uuid" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +dependencies = [ + "getrandom", +] + [[package]] name = "vec_map" version = "0.8.2" @@ -2092,7 +2099,6 @@ version = "0.1.0" dependencies = [ "serde_json", "tw_any_coin", - "tw_aptos", "tw_bitcoin", "tw_coin_entry", "tw_coin_registry", @@ -2105,6 +2111,7 @@ dependencies = [ "tw_number", "tw_proto", "tw_solana", + "uuid", ] [[package]] diff --git a/rust/tw_proto/Cargo.toml b/rust/tw_proto/Cargo.toml index 73be2eecd76..3e80de2c895 100644 --- a/rust/tw_proto/Cargo.toml +++ b/rust/tw_proto/Cargo.toml @@ -10,8 +10,6 @@ fuzz = ["arbitrary"] # Enable in fuzz tests only! arbitrary = { version = "1", features = ["derive"], optional = true } quick-protobuf = "0.8.1" -tw_encoding = { path = "../tw_encoding" } -tw_memory = { path = "../tw_memory" } [build-dependencies] pb-rs = "0.10.0" diff --git a/rust/tw_proto/src/ffi.rs b/rust/tw_proto/src/ffi.rs deleted file mode 100644 index 5eb33e557c7..00000000000 --- a/rust/tw_proto/src/ffi.rs +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#![allow(clippy::missing_safety_doc)] - -use std::borrow::Cow; -use tw_memory::ffi::c_byte_array::{CByteArray, CByteArrayResult}; -use tw_memory::ffi::c_byte_array_ref::CByteArrayRef; -use tw_memory::ffi::c_result::ErrorCode; - -#[repr(C)] -pub enum CProtoError { - Ok = 0, - Internal = 1, - ErrorDeserializingMsg = 2, - ErrorSerializingMsg = 3, -} - -impl From for ErrorCode { - fn from(e: CProtoError) -> Self { - e as ErrorCode - } -} - -/// Takes a serialized `Ethereum::Proto::SigningInput` message, deserializes it and returns a serialized message back. -/// This FFI is used within integration tests. -/// \param input *non-null* byte array. -/// \param input_len length of the input byte array. -/// \return C-compatible result with a C-compatible byte array. -#[no_mangle] -pub unsafe extern "C" fn pass_eth_signing_msg_through( - input: *const u8, - input_len: usize, -) -> CByteArrayResult { - let data = CByteArrayRef::new(input, input_len) - .to_vec() - .unwrap_or_default(); - - let msg: crate::Ethereum::Proto::SigningInput = match crate::deserialize(&data) { - Ok(msg) => msg, - Err(_) => return CByteArrayResult::error(CProtoError::ErrorDeserializingMsg), - }; - crate::serialize(&msg) - .map(CByteArray::from) - .map_err(|_| CProtoError::ErrorSerializingMsg) - .into() -} - -/// Returns a serialized `Polkadot::Proto::SigningInput` message. -/// This FFI is used within integration tests. -/// \return C-compatible result with a C-compatible byte array. -#[no_mangle] -pub unsafe extern "C" fn polkadot_test_signing_input() -> CByteArrayResult { - use crate::Polkadot::Proto::{ - self as proto, mod_Balance::OneOfmessage_oneof as BalanceEnum, - mod_SigningInput::OneOfmessage_oneof as SigningMsgEnum, - }; - - let block_hash = "0x343a3f4258fd92f5ca6ca5abdf473d86a78b0bcd0dc09c568ca594245cc8c642"; - let genesis_hash = "91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"; - let to_address = "14E5nqKAp3oAJcmzgZhUD2RcptBeUBScxKHgJKU4HPNcKVf3"; - let privkey = [ - 171, 248, 229, 189, 190, 48, 198, 86, 86, 192, 163, 203, 209, 129, 255, 138, 86, 41, 74, - 105, 223, 237, 210, 121, 130, 170, 206, 74, 118, 144, 145, 21, - ]; - let value = [48, 57]; // 12345 - - let block_hash = tw_encoding::hex::decode(block_hash).expect("Expect valid hash"); - let genesis_hash = tw_encoding::hex::decode(genesis_hash).expect("Expect valid hash"); - - let balance = BalanceEnum::transfer(proto::mod_Balance::Transfer { - to_address: Cow::from(to_address), - value: Cow::Borrowed(&value), - memo: Cow::default(), - call_indices: None, - }); - let signing_input = proto::SigningInput { - block_hash: Cow::Owned(block_hash), - genesis_hash: Cow::Owned(genesis_hash), - nonce: 0, - spec_version: 17, - transaction_version: 3, - era: Some(proto::Era { - block_number: 927699, - period: 8, - }), - private_key: Cow::Borrowed(&privkey), - network: 0, - message_oneof: SigningMsgEnum::balance_call(proto::Balance { - message_oneof: balance, - }), - ..proto::SigningInput::default() - }; - - crate::serialize(&signing_input) - .map(CByteArray::from) - .map_err(|_| CProtoError::ErrorSerializingMsg) - .into() -} diff --git a/rust/tw_proto/src/lib.rs b/rust/tw_proto/src/lib.rs index fd222ddbb4e..aa7a3345b54 100644 --- a/rust/tw_proto/src/lib.rs +++ b/rust/tw_proto/src/lib.rs @@ -21,8 +21,6 @@ pub use quick_protobuf::{ Error as ProtoError, MessageRead, MessageWrite, Result as ProtoResult, }; -pub mod ffi; - /// Serializes a Protobuf message without the length prefix. /// Please note that [`quick_protobuf::serialize_into_vec`] appends a `varint32` length prefix. pub fn serialize(message: &T) -> ProtoResult> { diff --git a/rust/tw_proto/tests/proto_ffi_tests.rs b/rust/tw_proto/tests/proto_ffi_tests.rs deleted file mode 100644 index 8c8bb0e9441..00000000000 --- a/rust/tw_proto/tests/proto_ffi_tests.rs +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -use tw_memory::ffi::c_byte_array::CByteArray; - -/// Test `pass_eth_signing_msg_through` to avoid dropping code coverage. -#[test] -fn test_pass_eth_signing_msg_through() { - let serialized = tw_encoding::hex::decode("0a0101120100220509c76524002a030130b9422a3078366231373534373465383930393463343464613938623935346565646561633439353237316430664a20608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151523812360a2a30783533323262333463383865643036393139373162663532613730343734343866306634656663383412081bc16d674ec80000").unwrap(); - let actual = unsafe { - let array = CByteArray::from(serialized.clone()); - tw_proto::ffi::pass_eth_signing_msg_through(array.data(), array.size()) - .unwrap() - .into_vec() - }; - assert_eq!(actual, serialized); -} - -/// Test `polkadot_test_signing_input` to avoid dropping code coverage. -#[test] -fn test_polkadot_test_signing_input() { - unsafe { tw_proto::ffi::polkadot_test_signing_input().unwrap() }; -} diff --git a/rust/wallet_core_rs/Cargo.toml b/rust/wallet_core_rs/Cargo.toml index effc5c0ad81..d0c43170fdf 100644 --- a/rust/wallet_core_rs/Cargo.toml +++ b/rust/wallet_core_rs/Cargo.toml @@ -8,30 +8,46 @@ name = "wallet_core_rs" crate-type = ["staticlib", "rlib"] # Creates static lib [features] -default = ["bitcoin-legacy", "ethereum-abi", "ethereum-rlp", "solana-address", "solana-transaction"] -bitcoin-legacy = [] -ethereum-abi = [] -ethereum-rlp = [] -solana-address = [] -solana-transaction = [] +default = [ + "any-coin", + "bitcoin", + "ethereum", + "keypair", + "solana", + "utils", +] +any-coin = ["tw_any_coin"] +bitcoin = ["tw_bitcoin"] +ethereum = ["tw_ethereum", "tw_coin_registry"] +keypair = ["tw_keypair"] +solana = ["tw_solana"] +utils = [ + "tw_encoding", + "tw_hash", + "tw_memory", + "tw_number", + "tw_proto", + "uuid" +] [dependencies] -tw_any_coin = { path = "../tw_any_coin" } -tw_aptos = { path = "../tw_aptos" } -tw_bitcoin = { path = "../tw_bitcoin" } -tw_coin_entry = { path = "../tw_coin_entry", features = ["test-utils"] } -tw_coin_registry = { path = "../tw_coin_registry" } -tw_encoding = { path = "../tw_encoding" } -tw_ethereum = { path = "../tw_ethereum" } -tw_hash = { path = "../tw_hash" } -tw_keypair = { path = "../tw_keypair" } -tw_memory = { path = "../tw_memory" } +tw_any_coin = { path = "../tw_any_coin", optional = true } +tw_bitcoin = { path = "../tw_bitcoin", optional = true } +tw_coin_registry = { path = "../tw_coin_registry", optional = true } +tw_encoding = { path = "../tw_encoding", optional = true } +tw_ethereum = { path = "../tw_ethereum", optional = true } +tw_hash = { path = "../tw_hash", optional = true } +tw_keypair = { path = "../tw_keypair", optional = true } +tw_memory = { path = "../tw_memory", optional = true } +tw_number = { path = "../tw_number", optional = true } tw_misc = { path = "../tw_misc" } -tw_proto = { path = "../tw_proto" } -tw_solana = { path = "../chains/tw_solana" } +tw_proto = { path = "../tw_proto", optional = true } +tw_solana = { path = "../chains/tw_solana", optional = true } +uuid = { version = "1.7", features = ["v4"], optional = true } [dev-dependencies] serde_json = "1.0" tw_any_coin = { path = "../tw_any_coin", features = ["test-utils"] } +tw_coin_entry = { path = "../tw_coin_entry", features = ["test-utils"] } tw_memory = { path = "../tw_memory", features = ["test-utils"] } tw_number = { path = "../tw_number", features = ["helpers"] } diff --git a/rust/wallet_core_rs/cbindgen.toml b/rust/wallet_core_rs/cbindgen.toml index fcab0505411..6697f798a56 100644 --- a/rust/wallet_core_rs/cbindgen.toml +++ b/rust/wallet_core_rs/cbindgen.toml @@ -8,25 +8,15 @@ namespaces = ["TW", "Rust"] parse_deps = true extra_bindings = [ "tw_any_coin", - "tw_bitcoin", - "tw_coin_registry", "tw_encoding", - "tw_ethereum", "tw_hash", "tw_keypair", "tw_memory", - "tw_move_parser", - "tw_proto" ] include = [ "tw_any_coin", - "tw_bitcoin", - "tw_coin_registry", "tw_encoding", - "tw_ethereum", "tw_hash", "tw_keypair", "tw_memory", - "tw_move_parser", - "tw_proto" ] diff --git a/rust/wallet_core_rs/src/ffi/bitcoin/mod.rs b/rust/wallet_core_rs/src/ffi/bitcoin/mod.rs index 84b48e76e1e..4a2477e69f2 100644 --- a/rust/wallet_core_rs/src/ffi/bitcoin/mod.rs +++ b/rust/wallet_core_rs/src/ffi/bitcoin/mod.rs @@ -2,5 +2,4 @@ // // Copyright © 2017 Trust Wallet. -#[cfg(feature = "bitcoin-legacy")] pub mod legacy; diff --git a/rust/wallet_core_rs/src/ffi/ethereum/mod.rs b/rust/wallet_core_rs/src/ffi/ethereum/mod.rs index c3de348c12c..3817d406809 100644 --- a/rust/wallet_core_rs/src/ffi/ethereum/mod.rs +++ b/rust/wallet_core_rs/src/ffi/ethereum/mod.rs @@ -2,7 +2,5 @@ // // Copyright © 2017 Trust Wallet. -#[cfg(feature = "ethereum-abi")] pub mod abi; -#[cfg(feature = "ethereum-rlp")] pub mod rlp; diff --git a/rust/wallet_core_rs/src/ffi/mod.rs b/rust/wallet_core_rs/src/ffi/mod.rs index 4b64452c2e7..8b0bc21ca38 100644 --- a/rust/wallet_core_rs/src/ffi/mod.rs +++ b/rust/wallet_core_rs/src/ffi/mod.rs @@ -2,6 +2,11 @@ // // Copyright © 2017 Trust Wallet. +#[cfg(feature = "bitcoin")] pub mod bitcoin; +#[cfg(feature = "ethereum")] pub mod ethereum; +#[cfg(feature = "solana")] pub mod solana; +#[cfg(feature = "utils")] +pub mod utils; diff --git a/rust/wallet_core_rs/src/ffi/solana/mod.rs b/rust/wallet_core_rs/src/ffi/solana/mod.rs index 19e39015399..19d3bb454a1 100644 --- a/rust/wallet_core_rs/src/ffi/solana/mod.rs +++ b/rust/wallet_core_rs/src/ffi/solana/mod.rs @@ -2,7 +2,5 @@ // // Copyright © 2017 Trust Wallet. -#[cfg(feature = "solana-address")] pub mod address; -#[cfg(feature = "solana-transaction")] pub mod transaction; diff --git a/rust/wallet_core_rs/src/ffi/utils/mod.rs b/rust/wallet_core_rs/src/ffi/utils/mod.rs new file mode 100644 index 00000000000..f233b194cb3 --- /dev/null +++ b/rust/wallet_core_rs/src/ffi/utils/mod.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +pub mod uuid_ffi; diff --git a/rust/wallet_core_rs/src/ffi/utils/uuid_ffi.rs b/rust/wallet_core_rs/src/ffi/utils/uuid_ffi.rs new file mode 100644 index 00000000000..159a4a079de --- /dev/null +++ b/rust/wallet_core_rs/src/ffi/utils/uuid_ffi.rs @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#![allow(clippy::missing_safety_doc)] + +use std::ffi::{c_char, CString}; + +/// Creates a random UUID. +/// This uses the [`getrandom`] crate to utilise the operating system's RNG +/// as the source of random numbers. +#[no_mangle] +pub unsafe extern "C" fn tw_uuid_random() -> *const c_char { + let res = uuid::Uuid::new_v4(); + CString::new(res.to_string()).unwrap().into_raw() +} diff --git a/rust/wallet_core_rs/src/lib.rs b/rust/wallet_core_rs/src/lib.rs index fa6c23b6bb6..f26f8e9364e 100644 --- a/rust/wallet_core_rs/src/lib.rs +++ b/rust/wallet_core_rs/src/lib.rs @@ -2,15 +2,17 @@ // // Copyright © 2017 Trust Wallet. +#[cfg(feature = "any-coin")] pub extern crate tw_any_coin; -pub extern crate tw_aptos; +#[cfg(feature = "bitcoin")] pub extern crate tw_bitcoin; -pub extern crate tw_coin_registry; +#[cfg(feature = "utils")] pub extern crate tw_encoding; -pub extern crate tw_ethereum; +#[cfg(feature = "utils")] pub extern crate tw_hash; +#[cfg(feature = "keypair")] pub extern crate tw_keypair; +#[cfg(feature = "utils")] pub extern crate tw_memory; -pub extern crate tw_proto; pub mod ffi; diff --git a/rust/wallet_core_rs/tests/uuid.rs b/rust/wallet_core_rs/tests/uuid.rs new file mode 100644 index 00000000000..a5c9aad34e4 --- /dev/null +++ b/rust/wallet_core_rs/tests/uuid.rs @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use std::collections::HashSet; +use std::ffi::CStr; +use wallet_core_rs::ffi::utils::uuid_ffi::tw_uuid_random; + +/// Example of the valid UUID: 3cbbcce1-db89-4ea2-be24-88a686be461c +#[test] +fn test_tw_uuid_random_is_valid() { + let uuid = unsafe { CStr::from_ptr(tw_uuid_random()) } + .to_str() + .unwrap(); + + let tokens: Vec<_> = uuid.split("-").collect(); + assert_eq!(tokens.len(), 5); + assert_eq!(tokens[0].len(), 8); + assert_eq!(tokens[1].len(), 4); + assert_eq!(tokens[2].len(), 4); + assert_eq!(tokens[3].len(), 4); + assert_eq!(tokens[4].len(), 12); +} + +#[test] +fn test_tw_uuid_random_do_not_repeat() { + const ITERATIONS: usize = 10000; + + // Use `Vec` instead of `HashSet` here to make each iteration as fast as possible. + let mut uuids = Vec::with_capacity(ITERATIONS); + for _ in 0..ITERATIONS { + let uuid = unsafe { CStr::from_ptr(tw_uuid_random()) } + .to_str() + .unwrap(); + uuids.push(uuid); + } + + let unique_uuids: HashSet<&str> = uuids.into_iter().collect(); + assert_eq!(unique_uuids.len(), ITERATIONS); +} diff --git a/src/Aion/RLP.h b/src/Aion/RLP.h index 6330bc278de..643c7289d16 100644 --- a/src/Aion/RLP.h +++ b/src/Aion/RLP.h @@ -7,16 +7,14 @@ #include "Transaction.h" #include "Ethereum/RLP.h" +#include "uint256.h" -#include #include #include #include namespace TW::Aion { -using boost::multiprecision::uint128_t; - /// Aion's RLP encoding for long numbers /// https://github.com/aionnetwork/aion/issues/680 struct RLP { diff --git a/src/Aion/Signer.cpp b/src/Aion/Signer.cpp index b3cacc038e4..8fe86f3904f 100644 --- a/src/Aion/Signer.cpp +++ b/src/Aion/Signer.cpp @@ -3,8 +3,6 @@ // Copyright © 2017 Trust Wallet. #include "Signer.h" -#include "../uint256.h" -#include using namespace TW; @@ -59,8 +57,6 @@ Proto::SigningOutput Signer::compile(const Data& signature, const PublicKey& pub } Transaction Signer::buildTransaction(const Proto::SigningInput& input) noexcept { - using boost::multiprecision::uint128_t; - auto transaction = Transaction( /* nonce: */ static_cast(load(input.nonce())), /* gasPrice: */ static_cast(load(input.gas_price())), diff --git a/src/Aion/Transaction.cpp b/src/Aion/Transaction.cpp index c4681472340..c2c6eee46aa 100644 --- a/src/Aion/Transaction.cpp +++ b/src/Aion/Transaction.cpp @@ -10,7 +10,6 @@ #include "uint256.h" using namespace TW; -using boost::multiprecision::uint128_t; namespace TW::Aion { diff --git a/src/Aion/Transaction.h b/src/Aion/Transaction.h index d3805859df3..257ddb68736 100644 --- a/src/Aion/Transaction.h +++ b/src/Aion/Transaction.h @@ -6,14 +6,12 @@ #include "Address.h" #include "Data.h" - -#include +#include "uint256.h" namespace TW::Aion { class Transaction { public: - using uint128_t = boost::multiprecision::uint128_t; uint128_t nonce; uint128_t gasPrice; diff --git a/src/EOS/Asset.cpp b/src/EOS/Asset.cpp index 86a53e38a89..7dbb5a87d11 100644 --- a/src/EOS/Asset.cpp +++ b/src/EOS/Asset.cpp @@ -3,9 +3,8 @@ // Copyright © 2017 Trust Wallet. #include "Asset.h" +#include "algorithm/string.hpp" -#include -#include #include namespace TW::EOS { @@ -38,7 +37,7 @@ Asset::Asset(int64_t amount, uint8_t decimals, const std::string& symbol) { Asset Asset::fromString(std::string assetString) { using namespace std; - boost::algorithm::trim(assetString); + trim(assetString); // Find space in order to split amount and symbol auto spacePosition = assetString.find(' '); @@ -46,7 +45,7 @@ Asset Asset::fromString(std::string assetString) { throw std::invalid_argument("Asset's amount and symbol should be separated with space"); } - auto symbolString = boost::algorithm::trim_copy(assetString.substr(spacePosition + 1)); + auto symbolString = trim_copy(assetString.substr(spacePosition + 1)); auto amountString = assetString.substr(0, spacePosition); // Ensure that if decimal point is used (.), decimal fraction is specified @@ -65,13 +64,13 @@ Asset Asset::fromString(std::string assetString) { // Parse amount int64_t intPart, fractPart = 0; if (dotPosition != string::npos) { - intPart = boost::lexical_cast(amountString.data(), dotPosition); - fractPart = boost::lexical_cast(amountString.data() + dotPosition + 1, decimals); + intPart = std::stoll(amountString.substr(0, dotPosition)); + fractPart = std::stoll(amountString.substr(dotPosition + 1, decimals)); if (amountString[0] == '-') { fractPart *= -1; } } else { - intPart = boost::lexical_cast(amountString); + intPart = std::stoll(amountString); } int64_t amount = intPart; diff --git a/src/EOS/Name.cpp b/src/EOS/Name.cpp index 9825a0f5b5b..6ce388d800f 100644 --- a/src/EOS/Name.cpp +++ b/src/EOS/Name.cpp @@ -4,8 +4,8 @@ #include "../BinaryCoding.h" #include "Name.h" +#include "algorithm/string.hpp" -#include #include namespace TW::EOS { @@ -50,7 +50,7 @@ std::string Name::string() const noexcept { tmp >>= 5; } - boost::algorithm::trim_right_if(str, [](char c) { return c == '.'; }); + trim_right(str, "."); return str; } diff --git a/src/Everscale/CommonTON/Cell.cpp b/src/Everscale/CommonTON/Cell.cpp index b148168fa00..c7488285d3f 100644 --- a/src/Everscale/CommonTON/Cell.cpp +++ b/src/Everscale/CommonTON/Cell.cpp @@ -9,11 +9,9 @@ #include #include +#include "Base64.h" #include "BinaryCoding.h" -#include -#include - using namespace TW; namespace TW::CommonTON { @@ -87,11 +85,7 @@ struct Reader { }; std::shared_ptr Cell::fromBase64(const std::string& encoded) { - // `Hash::base64` trims \0 bytes from the end of the _decoded_ data, so - // raw transform is used here - using namespace boost::archive::iterators; - using It = transform_width, 8, 6>; - Data boc(It(begin(encoded)), It(end(encoded))); + auto boc = Base64::decode(encoded); return Cell::deserialize(boc.data(), boc.size()); } diff --git a/src/Everscale/CommonTON/Cell.h b/src/Everscale/CommonTON/Cell.h index 90a024d0eb0..aaa03d67a9d 100644 --- a/src/Everscale/CommonTON/Cell.h +++ b/src/Everscale/CommonTON/Cell.h @@ -8,8 +8,6 @@ #include #include -#include - #include "Data.h" #include "Hash.h" diff --git a/src/Everscale/CommonTON/CellBuilder.h b/src/Everscale/CommonTON/CellBuilder.h index 1a22e9bc65e..6054e8d23be 100644 --- a/src/Everscale/CommonTON/CellBuilder.h +++ b/src/Everscale/CommonTON/CellBuilder.h @@ -11,6 +11,7 @@ #include "Cell.h" #include "CellSlice.h" #include "RawAddress.h" +#include "uint256.h" namespace TW::CommonTON { @@ -20,8 +21,6 @@ class CellBuilder { std::vector references{}; public: - using uint128_t = boost::multiprecision::uint128_t; - CellBuilder() = default; CellBuilder(Data& appendedData, uint16_t bits); diff --git a/src/Everscale/CommonTON/Messages.h b/src/Everscale/CommonTON/Messages.h index 4103a2f2feb..232f92664d0 100644 --- a/src/Everscale/CommonTON/Messages.h +++ b/src/Everscale/CommonTON/Messages.h @@ -16,8 +16,6 @@ namespace TW::CommonTON { -using uint128_t = CellBuilder::uint128_t; - class CommonMsgInfo { public: virtual void writeTo(CellBuilder& builder) const = 0; diff --git a/src/Everscale/Wallet.h b/src/Everscale/Wallet.h index a32a8fb5c1d..7efd68b7778 100644 --- a/src/Everscale/Wallet.h +++ b/src/Everscale/Wallet.h @@ -4,7 +4,6 @@ #pragma once -#include #include #include "PublicKey.h" diff --git a/src/FullSS58Address.h b/src/FullSS58Address.h index a9616186af2..04644660204 100644 --- a/src/FullSS58Address.h +++ b/src/FullSS58Address.h @@ -10,7 +10,6 @@ #include #include -#include const std::string FullSS58Prefix = "SS58PRE"; diff --git a/src/Harmony/Signer.h b/src/Harmony/Signer.h index f45fdb9b90a..0c8b073a196 100644 --- a/src/Harmony/Signer.h +++ b/src/Harmony/Signer.h @@ -12,7 +12,6 @@ #include "../proto/Harmony.pb.h" #include "../proto/EthereumRlp.pb.h" -#include #include #include #include diff --git a/src/Harmony/Staking.h b/src/Harmony/Staking.h index ef778a01a19..eff3ba8b37f 100644 --- a/src/Harmony/Staking.h +++ b/src/Harmony/Staking.h @@ -4,7 +4,6 @@ #pragma once -#include #include #include #include diff --git a/src/IOST/Signer.cpp b/src/IOST/Signer.cpp index 5b6f39f34d5..645be9f610f 100644 --- a/src/IOST/Signer.cpp +++ b/src/IOST/Signer.cpp @@ -5,10 +5,7 @@ #include "Signer.h" #include "Account.h" #include "../BinaryCoding.h" -#include "../Hash.h" -#include "../PrivateKey.h" #include -#include #include #include diff --git a/src/Icon/Signer.cpp b/src/Icon/Signer.cpp index 8a54194df2b..e537a72740d 100644 --- a/src/Icon/Signer.cpp +++ b/src/Icon/Signer.cpp @@ -9,7 +9,6 @@ #include "../HexCoding.h" #include "../PrivateKey.h" -#include #include #include diff --git a/src/Keystore/StoredKey.cpp b/src/Keystore/StoredKey.cpp index 8742e2760af..fb0d8701f00 100644 --- a/src/Keystore/StoredKey.cpp +++ b/src/Keystore/StoredKey.cpp @@ -9,11 +9,6 @@ #include "Mnemonic.h" #include "PrivateKey.h" -#define BOOST_UUID_RANDOM_PROVIDER_FORCE_POSIX 1 - -#include -#include -#include #include #include @@ -76,8 +71,10 @@ StoredKey::StoredKey(StoredKeyType type, std::string name, const Data& password, : type(type), id(), name(std::move(name)), accounts() { const auto encryptionParams = EncryptionParameters::getPreset(encryptionLevel, encryption); payload = EncryptedPayload(password, data, encryptionParams); - boost::uuids::random_generator gen; - id = boost::lexical_cast(gen()); + + const char* uuid_ptr = Rust::tw_uuid_random(); + id = std::make_optional(uuid_ptr); + Rust::free_string(uuid_ptr); } const HDWallet<> StoredKey::wallet(const Data& password) const { diff --git a/src/Nano/Signer.cpp b/src/Nano/Signer.cpp index c18eb036eee..bfac78f141d 100644 --- a/src/Nano/Signer.cpp +++ b/src/Nano/Signer.cpp @@ -5,16 +5,14 @@ #include "Signer.h" #include "../BinaryCoding.h" -#include "../Hash.h" #include "../HexCoding.h" +#include "../uint256.h" #include -#include #include using namespace TW; -using uint128_t = boost::multiprecision::uint128_t; using json = nlohmann::json; namespace TW::Nano { @@ -27,8 +25,6 @@ const std::array kBlockHashPreamble{ }; std::array store(const uint128_t& value) { - using boost::multiprecision::cpp_int; - Data buf; buf.reserve(16); export_bits(value, std::back_inserter(buf), 8); diff --git a/src/Polkadot/ScaleCodec.h b/src/Polkadot/ScaleCodec.h index 47a0dbdf99d..eb7dbadab12 100644 --- a/src/Polkadot/ScaleCodec.h +++ b/src/Polkadot/ScaleCodec.h @@ -4,18 +4,17 @@ #pragma once -#include "../BinaryCoding.h" +#include "BinaryCoding.h" #include "Data.h" -#include "../PublicKey.h" +#include "PublicKey.h" #include "SS58Address.h" -#include +#include "uint256.h" + #include #include #include - -/// Reference https://github.com/soramitsu/kagome/blob/master/core/scale/scale_encoder_stream.cpp -using CompactInteger = boost::multiprecision::cpp_int; +using CompactInteger = TW::uint256_t; namespace TW::Polkadot { diff --git a/src/Tezos/MessageSigner.cpp b/src/Tezos/MessageSigner.cpp index b3ed0c797c2..077564019dc 100644 --- a/src/Tezos/MessageSigner.cpp +++ b/src/Tezos/MessageSigner.cpp @@ -2,11 +2,8 @@ // // Copyright © 2017 Trust Wallet. -#include #include #include -#include -#include #include "Base58.h" #include "HexCoding.h" diff --git a/src/VeChain/Signer.h b/src/VeChain/Signer.h index 2e3422a3f9f..aec2d1746b9 100644 --- a/src/VeChain/Signer.h +++ b/src/VeChain/Signer.h @@ -10,7 +10,6 @@ #include "../Hash.h" #include "../PrivateKey.h" -#include #include #include #include diff --git a/src/WebAuthn.cpp b/src/WebAuthn.cpp index 83fa598cea5..780b0c0b4f8 100644 --- a/src/WebAuthn.cpp +++ b/src/WebAuthn.cpp @@ -6,7 +6,6 @@ #include "Cbor.h" #include "PublicKey.h" -#include #include #include #include diff --git a/src/algorithm/string.hpp b/src/algorithm/string.hpp index 8d5fc8145a4..806b14b9899 100644 --- a/src/algorithm/string.hpp +++ b/src/algorithm/string.hpp @@ -10,7 +10,34 @@ namespace TW { -static std::vector ssplit(const std::string& input, char delimiter) { +constexpr std::string_view kWitespaces = " \t\n\r\f\v"; + +// trim from end of string (right) +inline void trim_right(std::string& s, std::string_view t = kWitespaces) +{ + s.erase(s.find_last_not_of(t) + 1); +} + +// trim from beginning of string (left) +inline void trim_left(std::string& s, std::string_view t = kWitespaces) +{ + s.erase(0, s.find_first_not_of(t)); +} + +// trim from both ends of string (right then left) +inline void trim(std::string& s, std::string_view t = kWitespaces) +{ + trim_left(s, t); + trim_right(s, t); +} + +// trim from both ends of string (right then left) +inline std::string trim_copy(std::string s, std::string_view t = kWitespaces) { + trim(s, t); + return s; +} + +inline std::vector ssplit(const std::string& input, char delimiter) { std::istringstream iss(input); std::vector tokens; std::string token; diff --git a/src/uint256.h b/src/uint256.h index 9d5e4f01171..43c6999a21a 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -11,6 +11,7 @@ namespace TW { +using uint128_t = boost::multiprecision::uint128_t; using int256_t = boost::multiprecision::int256_t; using uint256_t = boost::multiprecision::uint256_t; @@ -26,20 +27,6 @@ inline uint256_t load(const Data& data) { return result; } -/// Loads a `uint256_t` from a collection of bytes. -/// The leftmost offset bytes are skipped, and the next 32 bytes are taken. At least 32 (+offset) -/// bytes are needed. -inline uint256_t loadWithOffset(const Data& data, size_t offset) { - using boost::multiprecision::cpp_int; - if (data.empty() || (data.size() < (256 / 8 + offset))) { - // not enough bytes in data - return uint256_t(0); - } - uint256_t result; - import_bits(result, data.begin() + offset, data.begin() + offset + 256 / 8); - return result; -} - /// Loads a `uint256_t` from Protobuf bytes (which are wrongly represented as /// std::string). inline uint256_t load(const std::string& data) { diff --git a/tests/chains/Everscale/CellTests.cpp b/tests/chains/Everscale/CellTests.cpp index 8fb9c913cd5..139878ec6c1 100644 --- a/tests/chains/Everscale/CellTests.cpp +++ b/tests/chains/Everscale/CellTests.cpp @@ -64,7 +64,7 @@ TEST(EverscaleCell, EmptyCell) { ASSERT_EQ(hex(cell3->hash), EMPTY_CELL); // With `storeHashes` (provided hash should be skipped) - const auto cell4 = Cell::fromBase64("te6ccgEBAQEAJAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + const auto cell4 = Cell::fromBase64("te6ccgEBAQEAJAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); ASSERT_EQ(hex(cell4->hash), EMPTY_CELL); } diff --git a/tests/chains/Ontology/TransactionTests.cpp b/tests/chains/Ontology/TransactionTests.cpp index 154245ff49b..43def521309 100644 --- a/tests/chains/Ontology/TransactionTests.cpp +++ b/tests/chains/Ontology/TransactionTests.cpp @@ -11,7 +11,6 @@ #include -#include #include namespace TW::Ontology::tests { diff --git a/tests/common/Uint256Tests.cpp b/tests/common/Uint256Tests.cpp index 9d084e62b02..5b01de5a3c5 100644 --- a/tests/common/Uint256Tests.cpp +++ b/tests/common/Uint256Tests.cpp @@ -97,12 +97,6 @@ TEST(Uint256, LoadEmpty) { EXPECT_EQ(load(parse_hex("0000")), uint256_t(0)); } -TEST(Uint256, LoadWithOffset) { - EXPECT_EQ(loadWithOffset(parse_hex("0000000000000000000000000000000000000000000000000000000000000003"), 0), uint256_t(3)); - EXPECT_EQ(loadWithOffset(parse_hex("abcdef0000000000000000000000000000000000000000000000000000000000000003"), 3), uint256_t(3)); - EXPECT_EQ(loadWithOffset(parse_hex("0000000000000000000000000000000000000000000000000000000000000003"), 1), uint256_t(0)); // not enough bytes -} - TEST(Uint256, loadStringProtobuf) { const Data data = parse_hex("03"); const std::string str = std::string(reinterpret_cast(data.data()), data.size()); diff --git a/tests/common/algorithm/string.cpp b/tests/common/algorithm/string.cpp index 18f51c95e9c..1371ddcf8b2 100644 --- a/tests/common/algorithm/string.cpp +++ b/tests/common/algorithm/string.cpp @@ -14,4 +14,16 @@ namespace TW::tests { ASSERT_EQ(splitted[1], "0"); ASSERT_EQ(splitted[2], "1"); } + + TEST(Algorithm, StringTrim) { + std::string str = " \t \n Hello, \n World \t \n"; + trim(str); + ASSERT_EQ(str, "Hello, \n World"); + } + + TEST(Algorithm, StringTrimSpecificSymbols) { + std::string str = ".\n Hello. World ..."; + trim(str, "."); + ASSERT_EQ(str, "\n Hello. World "); + } } diff --git a/tests/common/rust/bindgen/WalletCoreRsTests.cpp b/tests/common/rust/bindgen/WalletCoreRsTests.cpp deleted file mode 100644 index f2f1eb7f1f5..00000000000 --- a/tests/common/rust/bindgen/WalletCoreRsTests.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "HexCoding.h" -#include "Polkadot/Signer.h" -#include "TestUtilities.h" -#include "rust/bindgen/WalletCoreRSBindgen.h" -#include "gtest/gtest.h" -#include "proto/Ethereum.pb.h" -#include "proto/Polkadot.pb.h" -#include "uint256.h" - -TEST(RustBindgen, EthSigningMessageProto) { - using namespace TW; - - auto chainId = store(uint256_t(1)); - auto nonce = store(uint256_t(0)); - auto gasPrice = store(uint256_t(42000000000ul)); // 0x09c7652400 - auto gasLimit = store(uint256_t(78009ul)); // 130B9 - auto toAddress = "0x5322b34c88ed0691971bf52a7047448f0f4efc84"; - auto token = "0x6b175474e89094c44da98b954eedeac495271d0f"; // DAI - auto amount = uint256_t(2000000000000000000); - auto amountData = store(amount); - auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); - - Ethereum::Proto::SigningInput input; - input.set_chain_id(chainId.data(), chainId.size()); - input.set_nonce(nonce.data(), nonce.size()); - // tx_mode not set, Legacy is the default - input.set_gas_price(gasPrice.data(), gasPrice.size()); - input.set_gas_limit(gasLimit.data(), gasLimit.size()); - input.set_to_address(token); - input.set_private_key(key.data(), key.size()); - auto& erc20 = *input.mutable_transaction()->mutable_erc20_transfer(); - erc20.set_to(toAddress); - erc20.set_amount(amountData.data(), amountData.size()); - - Data serialized = data(input.SerializeAsString()); - Rust::CByteArrayResultWrapper res = Rust::pass_eth_signing_msg_through(serialized.data(), serialized.size()); - ASSERT_TRUE(res.isOk()); - ASSERT_EQ(res.unwrap().data, serialized); -} - -TEST(RustBindgen, PolkadotSignTxProto) { - using namespace TW; - - auto blockHash = parse_hex("0x343a3f4258fd92f5ca6ca5abdf473d86a78b0bcd0dc09c568ca594245cc8c642"); - auto genesisHash = parse_hex("91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"); - auto toAddress = "14E5nqKAp3oAJcmzgZhUD2RcptBeUBScxKHgJKU4HPNcKVf3"; - auto privateKey = parse_hex("0xabf8e5bdbe30c65656c0a3cbd181ff8a56294a69dfedd27982aace4a76909115"); - auto expectedEncoded = "29028488dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee003d91a06263956d8ce3ce5c55455baefff299d9cb2bb3f76866b6828ee4083770b6c03b05d7b6eb510ac78d047002c1fe5c6ee4b37c9c5a8b09ea07677f12e50d3200000005008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48e5c0"; - - Rust::CByteArrayResultWrapper res = Rust::polkadot_test_signing_input(); - ASSERT_TRUE(res.isOk()); - auto serialized = std::move(res.unwrap().data); - - Polkadot::Proto::SigningInput input; - input.ParseFromArray(serialized.data(), static_cast(serialized.size())); - - ASSERT_EQ(input.nonce(), 0ul); - ASSERT_EQ(input.spec_version(), 17ul); - ASSERT_EQ(data(input.private_key()), privateKey); - ASSERT_EQ(input.network(), 0ul); - ASSERT_EQ(input.transaction_version(), 3ul); - - ASSERT_EQ(input.era().block_number(), 927699ul); - ASSERT_EQ(input.era().period(), 8ul); - - auto transfer = input.balance_call().transfer(); - ASSERT_EQ(data(transfer.value()), store(uint256_t(12345ul))); - ASSERT_EQ(transfer.to_address(), toAddress); - - auto output = Polkadot::Signer::sign(input); - ASSERT_EQ(hex(output.encoded()), expectedEncoded); -} From 2de1a628f8483351f83626ae2740698988d12192 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Fri, 22 Mar 2024 17:22:07 +0100 Subject: [PATCH 069/128] [Misc]: Fix C++ includes on Linux (#3755) * [Misc]: Fix C++ includes on Linux * [Misc]: Fix C++ includes on Linux * [Misc]: Remove `iostream` header * [KMP]: Bump wallet-core-kotlin version to 4.0.30 * [KMP]: Decrease wallet-core-kotlin version to 4.0.29 * [KMP]: Test using the latest wallet-core-kotlin dependency * [KMP]: Debug gradlew assemble * [KMP]: Remove debug --- samples/kmp/shared/build.gradle.kts | 2 +- src/Everscale/CommonTON/Cell.cpp | 1 + src/Tezos/MessageSigner.cpp | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/samples/kmp/shared/build.gradle.kts b/samples/kmp/shared/build.gradle.kts index 93e4a9173b7..c27215519a7 100644 --- a/samples/kmp/shared/build.gradle.kts +++ b/samples/kmp/shared/build.gradle.kts @@ -45,7 +45,7 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation("com.trustwallet:wallet-core-kotlin:4.0.28") + implementation("com.trustwallet:wallet-core-kotlin:+") } } val commonTest by getting { diff --git a/src/Everscale/CommonTON/Cell.cpp b/src/Everscale/CommonTON/Cell.cpp index c7488285d3f..8329c299713 100644 --- a/src/Everscale/CommonTON/Cell.cpp +++ b/src/Everscale/CommonTON/Cell.cpp @@ -5,6 +5,7 @@ #include "Cell.h" #include +#include #include #include #include diff --git a/src/Tezos/MessageSigner.cpp b/src/Tezos/MessageSigner.cpp index 077564019dc..25234f170ed 100644 --- a/src/Tezos/MessageSigner.cpp +++ b/src/Tezos/MessageSigner.cpp @@ -2,8 +2,10 @@ // // Copyright © 2017 Trust Wallet. +#include #include #include +#include #include "Base58.h" #include "HexCoding.h" From e80c198ed72e8ec301dc8e78d7ebdac0f8819161 Mon Sep 17 00:00:00 2001 From: damian-zhao <158460257+damian-zhao@users.noreply.github.com> Date: Mon, 25 Mar 2024 12:42:55 +0400 Subject: [PATCH 070/128] Skip sig-verify for all-zero signature in Solana compiler. (#3754) * Skip sig-verify for all-zero signature in Solana compiler. * Implement Hash.is_zero() to optimize. * Reformat rust code. --------- Co-authored-by: damian-z Co-authored-by: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> --- rust/chains/tw_solana/src/compiler.rs | 4 +- .../tests/chains/solana/solana_compile.rs | 78 +++++++++++++++++++ rust/tw_hash/src/hash_array.rs | 4 + 3 files changed, 85 insertions(+), 1 deletion(-) diff --git a/rust/chains/tw_solana/src/compiler.rs b/rust/chains/tw_solana/src/compiler.rs index bd5a8af0297..ca50e214776 100644 --- a/rust/chains/tw_solana/src/compiler.rs +++ b/rust/chains/tw_solana/src/compiler.rs @@ -86,7 +86,9 @@ impl SolanaCompiler { let signature = ed25519::Signature::try_from(sign.as_slice())?; let pubkey = ed25519::sha512::PublicKey::try_from(pubkey.as_slice())?; - if !pubkey.verify(signature.clone(), data_to_sign.clone()) { + if !pubkey.verify(signature.clone(), data_to_sign.clone()) + && !signature.to_bytes().is_zero() + { return Err(SigningError(SigningErrorType::Error_signing)); } diff --git a/rust/tw_any_coin/tests/chains/solana/solana_compile.rs b/rust/tw_any_coin/tests/chains/solana/solana_compile.rs index a2f6b0cfadf..186d6bf1ee2 100644 --- a/rust/tw_any_coin/tests/chains/solana/solana_compile.rs +++ b/rust/tw_any_coin/tests/chains/solana/solana_compile.rs @@ -343,3 +343,81 @@ fn test_solana_compile_token_transfer_with_external_fee_payer() { assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded, "qjgNVBmoPDHNTN2ENQfxNVE57jWXpqdmu5GQX4msA7iK8ZRAnKpvbusQagv8CZGyNYti23p9jBsjTSx75ZU26UW5vgC8D88pusW8W5dp1ERo5DSfurMSYJ6afgQHdcuzn7exb8znSm6uV4y1cWgBRcuAGdg3wRpVhP8HEB1EeKgzjYVWvMSy6yR7qVrSL6BxHG6eiAMyahLFbEt4qBqLEdxxY7Dt4DyydVYmG2ZVtheaMHD3ACwCjpyPLXj399wxSgGXQQFGtzEJQw9awVezmJ4wZk6W4dDpXQvdKYaqUvwTwRZsQB5o2iekPWZXR9xvHiMLjMVBPzYgcU14ZSaCbqSNVv2pAJxP1sMvxZMNMzZPttPxCsDDGq9biC7exXwzesXSnZ3rsgEYeZtkUiBHAxR4rYqBpA6VzLs1bPx8MPTvr9mhNi2ezMBbg2nEfHV6Fz7H7rEY2g3jDtRz35Vmgits8s9RKi3kb73WtGUieRiXjiqkNhpvKkST1oEYRQ9"); } + +#[test] +fn test_solana_compile_transfer_with_zero_signature() { + let transfer = Proto::Transfer { + recipient: "3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe".into(), + value: 1000, + ..Proto::Transfer::default() + }; + let input = Proto::SigningInput { + sender: "sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH".into(), + recent_blockhash: "TPJFTN4CjBn12HiBfAbGUhpD9zGvRSm2RcheFRA4Fyv".into(), + transaction_type: TransactionType::transfer_transaction(transfer), + ..Proto::SigningInput::default() + }; + + // Step 2: Obtain preimage hash + let mut pre_imager = PreImageHelper::::default(); + let preimage_output = pre_imager.pre_image_hashes(CoinType::Solana, &input); + + assert_eq!(preimage_output.error, SigningError::OK); + assert_eq!( + preimage_output.data.to_hex(), + "010001030d044a62d0a4dfe5a037a15b59fa4d4d0d3ab81103a2c10a6da08a4d058611c024c255a8bc3e8496217a2cd2a1894b9b9dcace04fcd9c0d599acdaaea40a1b61000000000000000000000000000000000000000000000000000000000000000006c25012cc11a599a45b3b2f7f8a7c65b0547fa0bb67170d7a0cd1eda4e2c9e501020200010c02000000e803000000000000" + ); + + // Step 3: Compile transaction info + let public_key = "0d044a62d0a4dfe5a037a15b59fa4d4d0d3ab81103a2c10a6da08a4d058611c0" + .decode_hex() + .unwrap(); + let mut compiler = CompilerHelper::::default(); + + // Compile with zero signature + let signature = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + .decode_hex() + .unwrap(); + let output = compiler.compile(CoinType::Solana, &input, vec![signature], vec![public_key]); + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "3md7BBV9wFjYGnMWcMNyAZcjca2HGfXWZkrU8vvho66z2sJMZFcx6HZdBiAddjo2kzgBv3uZoac3domBRjJJSXkbBvokxPZ8Lfa4BipQ5HdcJ2Fij2h7NHywnKFjDrgqfZ3YfUTEFreBy3MebYxQvgCaVhxGkxDwYc3Pvsoz4yQNMXTDrmgaqbhJzhV5WUXmqJeFMTyY8NguMLS6A9uP4iWe9RnMs2X5dFnhMPQzfCzL6wjVKJ99gRaqqDundDhdoYUdvgC17jY4pB9tMhL9bNBs9VwWu66dSYzQX"); + assert_eq!(output.unsigned_tx, "87PYr2vKPjNPfwNmqTvhgkThhohTqFNKYJgCHcrUCeayX6daQs9AFvMA698MG9TknbSnUxNXaNaReatkevLDgiTG5FqcBgVHG5PLPrq3PCdKPLjAN9RMQJXM5i6KaVMDzGJGMfgSFMS4ecEjqumZX4nux9rhG4jpYaQbe5sgyYUetwMmemoNiCgW2qCFsGnTYR9rWSU7S9zF"); +} + +#[test] +fn test_solana_compile_transfer_with_fake_signature() { + let transfer = Proto::Transfer { + recipient: "3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe".into(), + value: 1000, + ..Proto::Transfer::default() + }; + let input = Proto::SigningInput { + sender: "sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH".into(), + recent_blockhash: "TPJFTN4CjBn12HiBfAbGUhpD9zGvRSm2RcheFRA4Fyv".into(), + transaction_type: TransactionType::transfer_transaction(transfer), + ..Proto::SigningInput::default() + }; + + // Step 2: Obtain preimage hash + let mut pre_imager = PreImageHelper::::default(); + let preimage_output = pre_imager.pre_image_hashes(CoinType::Solana, &input); + + assert_eq!(preimage_output.error, SigningError::OK); + assert_eq!( + preimage_output.data.to_hex(), + "010001030d044a62d0a4dfe5a037a15b59fa4d4d0d3ab81103a2c10a6da08a4d058611c024c255a8bc3e8496217a2cd2a1894b9b9dcace04fcd9c0d599acdaaea40a1b61000000000000000000000000000000000000000000000000000000000000000006c25012cc11a599a45b3b2f7f8a7c65b0547fa0bb67170d7a0cd1eda4e2c9e501020200010c02000000e803000000000000" + ); + + // Step 3: Compile transaction info + let public_key = "0d044a62d0a4dfe5a037a15b59fa4d4d0d3ab81103a2c10a6da08a4d058611c0" + .decode_hex() + .unwrap(); + let mut compiler = CompilerHelper::::default(); + + // Compile with other fake signature + let signature = "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + .decode_hex() + .unwrap(); + let output = compiler.compile(CoinType::Solana, &input, vec![signature], vec![public_key]); + assert_eq!(output.error, SigningError::Error_signing); +} diff --git a/rust/tw_hash/src/hash_array.rs b/rust/tw_hash/src/hash_array.rs index b740b2b9735..5732f56f8ff 100644 --- a/rust/tw_hash/src/hash_array.rs +++ b/rust/tw_hash/src/hash_array.rs @@ -80,6 +80,10 @@ impl Hash { pub const fn len() -> usize { N } + + pub fn is_zero(&self) -> bool { + self.0.iter().all(|byte| *byte == 0) + } } /// This is a [`Hash::split`] helper that ensures that `L + R == N` at compile time. From e56488fba9f3e1c6cd7aa5fbca64f56f3a8a90fa Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 27 Mar 2024 20:11:40 +0100 Subject: [PATCH 071/128] [README]: Update the GitHub Issue policy at README.md (#3757) --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c425811aa56..55c2878bbdd 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,8 @@ There are a few community-maintained projects that extend Wallet Core to some ad # Contributing -The best way to submit feedback and report bugs is to [open a GitHub issue](https://github.com/trustwallet/wallet-core/issues/new). +The best way to submit feedback and report bugs related to WalletCore is to [open a GitHub issue](https://github.com/trustwallet/wallet-core/issues/new). +If the bug is not related to WalletCore but to the TrustWallet app, please [create a Customer Support ticket](https://support.trustwallet.com/en/support/tickets/new). If you want to contribute code please see [Contributing](https://developer.trustwallet.com/wallet-core/contributing). If you want to add support for a new blockchain also see [Adding Support for a New Blockchain](https://developer.trustwallet.com/wallet-core/newblockchain), make sure you have read the [requirements](https://developer.trustwallet.com/wallet-core/newblockchain#requirements) section. From e82fa08bf3e52b096af05ccd4f0647caacbfbd0a Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Tue, 2 Apr 2024 10:40:58 +0200 Subject: [PATCH 072/128] [registry]: Set "OP Mainnet" for Optimism, "BTC" for Merlin display names (#3763) * feat(optimism): Change "Optimism Ethereum" display name to "OP Mainnet" * feat(merlin): Change symbol from ETH to BTC * feat(merlin): Change symbol from ETH to BTC * Revert changes for Ethereum mainnet * feat(merlin): Fix WalletConsole tests * Update registry.md --- docs/registry.md | 4 +-- registry.json | 4 +-- tests/chains/Merlin/TWCoinTypeTests.cpp | 2 +- tests/chains/Optimism/TWCoinTypeTests.cpp | 2 +- tests/common/WalletConsoleTests.cpp | 38 +++++++++++------------ 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/docs/registry.md b/docs/registry.md index 8d053718316..4079dd81817 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -87,7 +87,7 @@ This list is generated from [./registry.json](../registry.json) | 2301 | Qtum | QTUM | | | | 2718 | Nebulas | NAS | | | | 3030 | Hedera | HBAR | | | -| 4200 | Merlin | ETH | | | +| 4200 | Merlin | BTC | | | | 5000 | Mantle | MNT | | | | 5600 | BNB Greenfield | BNB | | | | 6060 | GoChain | GO | | | @@ -105,7 +105,7 @@ This list is generated from [./registry.json](../registry.json) | 5741564 | Waves | WAVES | | | | 10000025 | Cronos Chain | CRO | | | | 10000060 | Native Injective | INJ | | | -| 10000070 | Optimism Ethereum | ETH | | | +| 10000070 | OP Mainnet | ETH | | | | 10000100 | Gnosis Chain | xDAI | | | | 10000118 | Osmosis | OSMO | | | | 10000145 | Smart Bitcoin Cash | BCH | | | diff --git a/registry.json b/registry.json index 15717d5b5b4..2f058c965a0 100644 --- a/registry.json +++ b/registry.json @@ -3198,7 +3198,7 @@ { "id": "optimism", "name": "Optimism", - "displayName": "Optimism Ethereum", + "displayName": "OP Mainnet", "coinId": 10000070, "slip44": 60, "symbol": "ETH", @@ -4633,7 +4633,7 @@ "id": "merlin", "name": "Merlin", "coinId": 4200, - "symbol": "ETH", + "symbol": "BTC", "decimals": 18, "blockchain": "Ethereum", "derivation": [ diff --git a/tests/chains/Merlin/TWCoinTypeTests.cpp b/tests/chains/Merlin/TWCoinTypeTests.cpp index 4f045742865..c50d7b185bb 100644 --- a/tests/chains/Merlin/TWCoinTypeTests.cpp +++ b/tests/chains/Merlin/TWCoinTypeTests.cpp @@ -18,7 +18,7 @@ TEST(TWMerlinCoinType, TWCoinType) { assertStringsEqual(id, "merlin"); assertStringsEqual(name, "Merlin"); - assertStringsEqual(symbol, "ETH"); + assertStringsEqual(symbol, "BTC"); ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainEthereum); ASSERT_EQ(TWCoinTypeP2pkhPrefix(coin), 0); diff --git a/tests/chains/Optimism/TWCoinTypeTests.cpp b/tests/chains/Optimism/TWCoinTypeTests.cpp index 9e51cffa575..97be122198d 100644 --- a/tests/chains/Optimism/TWCoinTypeTests.cpp +++ b/tests/chains/Optimism/TWCoinTypeTests.cpp @@ -26,5 +26,5 @@ TEST(TWOptimismCoinType, TWCoinType) { assertStringsEqual(txUrl, "https://optimistic.etherscan.io/tx/0x6fd99288be9bf71eb002bb31da10a4fb0fbbb3c45ae73693b212f49c9db7df8f"); assertStringsEqual(accUrl, "https://optimistic.etherscan.io/address/0x1f932361e31d206b4f6b2478123a9d0f8c761031"); assertStringsEqual(id, "optimism"); - assertStringsEqual(name, "Optimism Ethereum"); + assertStringsEqual(name, "OP Mainnet"); } diff --git a/tests/common/WalletConsoleTests.cpp b/tests/common/WalletConsoleTests.cpp index 7ac70b0c654..3ad84cb5f2c 100644 --- a/tests/common/WalletConsoleTests.cpp +++ b/tests/common/WalletConsoleTests.cpp @@ -84,27 +84,27 @@ TEST(WalletConsole, coins) { TEST(WalletConsole, coin) { { auto pos = outputss.str().length(); - cmd.executeLine("coin btc"); + cmd.executeLine("coin atom"); string res = outputss.str().substr(pos); - EXPECT_TRUE(res.find("Set active coin to: bitcoin") != string::npos); + EXPECT_TRUE(res.find("Set active coin to: cosmos") != string::npos); } { auto pos = outputss.str().length(); cmd.executeLine("coin ethereum"); string res = outputss.str().substr(pos); - EXPECT_TRUE(res.find("Set active coin to: ethereum") != string::npos); + EXPECT_TRUE(res.find("Set active coin to: ethereum") != string::npos) << res; } { auto pos = outputss.str().length(); cmd.executeLine("coin bitcoin"); string res = outputss.str().substr(pos); - EXPECT_TRUE(res.find("Set active coin to: bitcoin") != string::npos); + EXPECT_TRUE(res.find("Set active coin to: bitcoin") != string::npos) << res; } { auto pos = outputss.str().length(); cmd.executeLine("coin no_such_coin_exists"); string res = outputss.str().substr(pos); - EXPECT_TRUE(res.find("Error: No such coin") != string::npos); + EXPECT_TRUE(res.find("Error: No such coin") != string::npos) << res; } } @@ -127,7 +127,7 @@ TEST(WalletConsole, newkey1) { } TEST(WalletConsole, pubPri1) { - cmd.executeLine("coin btc"); + cmd.executeLine("coin bitcoin"); auto pos1 = outputss.str().length(); cmd.executeLine("pubPri 7d40d6a74e98543f545852989d54712834f9c86eddee89303a2083219749e38c"); string res1 = outputss.str().substr(pos1); @@ -135,7 +135,7 @@ TEST(WalletConsole, pubPri1) { } TEST(WalletConsole, pubPriInvalid) { - cmd.executeLine("coin btc"); + cmd.executeLine("coin bitcoin"); auto pos1 = outputss.str().length(); cmd.executeLine("pubPri Hello!_This_is_an_invalid_private_key"); string res1 = outputss.str().substr(pos1); @@ -143,7 +143,7 @@ TEST(WalletConsole, pubPriInvalid) { } TEST(WalletConsole, priPub) { - cmd.executeLine("coin btc"); + cmd.executeLine("coin bitcoin"); auto pos1 = outputss.str().length(); cmd.executeLine("priPub 0200266ab7dc3efec040cc8b9714ff49cc8339d2f30d9bab8a4b11043e1bdfee37"); string res1 = outputss.str().substr(pos1); @@ -151,7 +151,7 @@ TEST(WalletConsole, priPub) { } TEST(WalletConsole, addrPubBtc1) { - cmd.executeLine("coin btc"); + cmd.executeLine("coin bitcoin"); auto pos0 = outputss.str().length(); cmd.executeLine("addrPub 0200266ab7dc3efec040cc8b9714ff49cc8339d2f30d9bab8a4b11043e1bdfee37"); string res = outputss.str().substr(pos0); @@ -160,7 +160,7 @@ TEST(WalletConsole, addrPubBtc1) { } TEST(WalletConsole, addrPubInvalid) { - cmd.executeLine("coin btc"); + cmd.executeLine("coin bitcoin"); auto pos0 = outputss.str().length(); cmd.executeLine("addrPub Hello!"); string res = outputss.str().substr(pos0); @@ -168,7 +168,7 @@ TEST(WalletConsole, addrPubInvalid) { } TEST(WalletConsole, addrPri1) { - cmd.executeLine("coin btc"); + cmd.executeLine("coin bitcoin"); auto pos1 = outputss.str().length(); cmd.executeLine("addrPri 7d40d6a74e98543f545852989d54712834f9c86eddee89303a2083219749e38c"); string res1 = outputss.str().substr(pos1); @@ -177,7 +177,7 @@ TEST(WalletConsole, addrPri1) { } TEST(WalletConsole, addrPriInvalid) { - cmd.executeLine("coin btc"); + cmd.executeLine("coin bitcoin"); auto pos1 = outputss.str().length(); cmd.executeLine("addrPri Hello!"); string res1 = outputss.str().substr(pos1); @@ -185,7 +185,7 @@ TEST(WalletConsole, addrPriInvalid) { } TEST(WalletConsole, addrInvalid) { - cmd.executeLine("coin btc"); + cmd.executeLine("coin bitcoin"); auto pos1 = outputss.str().length(); cmd.executeLine("addr Hello_This_is_an_Invalid_BTC_Address!_"); string res1 = outputss.str().substr(pos1); @@ -194,7 +194,7 @@ TEST(WalletConsole, addrInvalid) { TEST(WalletConsole, addrDP1) { cmd.executeLine("setMnemonic " + mnemonic1); - cmd.executeLine("coin btc"); + cmd.executeLine("coin bitcoin"); // default DP auto pos1 = outputss.str().length(); @@ -263,7 +263,7 @@ TEST(WalletConsole, newMnemonic) { TEST(WalletConsole, dumpdp) { { - cmd.executeLine("coin btc"); + cmd.executeLine("coin bitcoin"); auto pos1 = outputss.str().length(); cmd.executeLine("dumpDP"); string res1 = outputss.str().substr(pos1); @@ -286,7 +286,7 @@ TEST(WalletConsole, dumpdp) { } TEST(WalletConsole, dumpXpub) { - cmd.executeLine("coin btc"); + cmd.executeLine("coin bitcoin"); auto pos1 = outputss.str().length(); cmd.executeLine("setMnemonic " + mnemonic1); string res1 = outputss.str().substr(pos1); @@ -301,7 +301,7 @@ TEST(WalletConsole, dumpXpub) { TEST(WalletConsole, derive) { // Step-by-step derivation, mnemo -> pri -> pub -> addr cmd.executeLine("setMnemonic " + mnemonic1); - cmd.executeLine("coin btc"); + cmd.executeLine("coin bitcoin"); { auto pos1 = outputss.str().length(); cmd.executeLine("priDP m/84'/0'/0'/0/1"); @@ -334,7 +334,7 @@ TEST(WalletConsole, derive) { TEST(WalletConsole, addrDefault) { { cmd.executeLine("setMnemonic " + mnemonic1); - cmd.executeLine("coin btc"); + cmd.executeLine("coin bitcoin"); auto pos1 = outputss.str().length(); cmd.executeLine("addrDefault"); string res1 = outputss.str().substr(pos1); @@ -357,7 +357,7 @@ TEST(WalletConsole, addrDefault) { } TEST(WalletConsole, addrXpub) { - cmd.executeLine("coin btc"); + cmd.executeLine("coin bitcoin"); // no need to set mnemonic here auto pos1 = outputss.str().length(); cmd.executeLine("addrXpub zpub6qvN3x2m4Q96SJJ8Q3ZRbCTm4mGdTny6u2hY8tTiyWznnjwc3rRYpHDb1gN9AAypB5m2x1WR954CLNqpLcAxkxt9x7LX9hKDGp9sGtZca7o 0"); From 7cd532cb9706d268f0841f169d740dbfb7a3526d Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Sat, 6 Apr 2024 12:10:16 +0200 Subject: [PATCH 073/128] [ios]: Update Package.swift to 4.0.33 (#3775) --- Package.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Package.swift b/Package.swift index 90ec9e38022..38e74510f2f 100644 --- a/Package.swift +++ b/Package.swift @@ -12,13 +12,13 @@ let package = Package( targets: [ .binaryTarget( name: "WalletCore", - url: "https://github.com/trustwallet/wallet-core/releases/download/3.2.1/WalletCore.xcframework.zip", - checksum: "b7f9fe6b5aa1fd216d43f676400c9db6999c6494e0120db73b2afb49dcc1f013" + url: "https://github.com/trustwallet/wallet-core/releases/download/4.0.33/WalletCore.xcframework.zip", + checksum: "2fb8b833047b9697bba6ade66a9bdeede622b2fe0fb7a9b90cb9edb4651ec866" ), .binaryTarget( name: "SwiftProtobuf", - url: "https://github.com/trustwallet/wallet-core/releases/download/3.2.1/SwiftProtobuf.xcframework.zip", - checksum: "12bbc92dc2225c661e301cc2826e034fcb69b2a144f8c4ff2e51ce6ccf122124" + url: "https://github.com/trustwallet/wallet-core/releases/download/4.0.33/SwiftProtobuf.xcframework.zip", + checksum: "05557735dd607c5a369dc378eb3f299504b880614ef13f136a028ecd320b0e4d" ) ] ) From 2ddfe52591e8e4a62d5b205d8a81f2d0d366480f Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Tue, 9 Apr 2024 18:34:51 +0400 Subject: [PATCH 074/128] Update CODEOWNERS (#3781) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0b82e075fc0..001dca8da7e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,5 +3,5 @@ # @global-owner1 and @global-owner2 will be requested for # review when someone opens a pull request. -* @milerius @satoshiotomakan @lamafab +* @milerius @satoshiotomakan @lamafab @ar-g kotlin/ @ar-g @JaimeToca @rkokhatskyi From df60b69b53757c45688a351a56da4849e4c840ae Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 10 Apr 2024 09:50:44 +0200 Subject: [PATCH 075/128] [ICP]: Update derivation path to be compatible with other wallets (#3771) * feat(ICP): Update derivation path to be compatible with other wallets * feat(icp): Fix Android, Kotlin, Swift tests --------- Co-authored-by: Andrii Rakhimov --- .../core/app/blockchains/CoinAddressDerivationTests.kt | 2 +- .../com/trustwallet/core/test/CoinAddressDerivationTests.kt | 2 +- registry.json | 2 +- swift/Tests/CoinAddressDerivationTests.swift | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index 5e167705863..da03cbf9021 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -150,7 +150,7 @@ class CoinAddressDerivationTests { NOBLE -> assertEquals("noble142j9u5eaduzd7faumygud6ruhdwme98qc8l3wa", address) ROOTSTOCK -> assertEquals("0xA2D7065F94F838a3aB9C04D67B312056846424Df", address) SEI -> assertEquals("sei142j9u5eaduzd7faumygud6ruhdwme98qagm0sj", address) - INTERNETCOMPUTER -> assertEquals("b9a13d974ee9db036d5abc5b66ace23e513cb5676f3996626c7717c339a3ee87", address) + INTERNETCOMPUTER -> assertEquals("6f8e568160a3c8362789848dc0fa52891964473c045cc25208a305fb35b7c4ab", address) TIA -> assertEquals("celestia142j9u5eaduzd7faumygud6ruhdwme98qpwmfv7", address) NATIVEZETACHAIN -> assertEquals("zeta13u6g7vqgw074mgmf2ze2cadzvkz9snlwywj304", address) DYDX -> assertEquals("dydx142j9u5eaduzd7faumygud6ruhdwme98qeayaky", address) diff --git a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt index 8c8046199ad..b075a460195 100644 --- a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt +++ b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt @@ -143,7 +143,7 @@ class CoinAddressDerivationTests { Noble -> "noble142j9u5eaduzd7faumygud6ruhdwme98qc8l3wa" Rootstock -> "0xA2D7065F94F838a3aB9C04D67B312056846424Df" Sei -> "sei142j9u5eaduzd7faumygud6ruhdwme98qagm0sj" - InternetComputer -> "b9a13d974ee9db036d5abc5b66ace23e513cb5676f3996626c7717c339a3ee87" + InternetComputer -> "6f8e568160a3c8362789848dc0fa52891964473c045cc25208a305fb35b7c4ab" Tia -> "celestia142j9u5eaduzd7faumygud6ruhdwme98qpwmfv7" NativeZetaChain -> "zeta13u6g7vqgw074mgmf2ze2cadzvkz9snlwywj304" Dydx -> "dydx142j9u5eaduzd7faumygud6ruhdwme98qeayaky" diff --git a/registry.json b/registry.json index 2f058c965a0..acb68493aea 100644 --- a/registry.json +++ b/registry.json @@ -4578,7 +4578,7 @@ "blockchain": "InternetComputer", "derivation": [ { - "path": "m/44'/223'/1'/0/0", + "path": "m/44'/223'/0'/0/0", "xpub": "xpub", "xpriv": "xpriv" } diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index e27974f5040..25e187439f3 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -385,7 +385,7 @@ class CoinAddressDerivationTests: XCTestCase { let expectedResult = "sei142j9u5eaduzd7faumygud6ruhdwme98qagm0sj" assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .internetComputer: - let expectedResult = "b9a13d974ee9db036d5abc5b66ace23e513cb5676f3996626c7717c339a3ee87" + let expectedResult = "6f8e568160a3c8362789848dc0fa52891964473c045cc25208a305fb35b7c4ab" assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .tia: let expectedResult = "celestia142j9u5eaduzd7faumygud6ruhdwme98qpwmfv7" From 70f86360a5e527450fd176790ba09dc7f831c724 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:48:18 +0200 Subject: [PATCH 076/128] [Binance]: Add `cosmos-sdk/MsgSideChainStakeMigration` message type (#3783) * feat(binance): Add `cosmos-sdk/MsgSideChainStakeMigration` * feat(binance): Fix rustfmt --- .../tw_binance/src/transaction/message/mod.rs | 9 +++ .../message/side_chain_delegate.rs | 69 +++++++++++++++++++ .../tests/chains/binance/binance_sign.rs | 50 +++++++++++++- rust/tw_any_coin/tests/chains/binance/mod.rs | 3 + rust/tw_evm/src/address.rs | 9 +++ src/proto/Binance.proto | 11 +++ 6 files changed, 150 insertions(+), 1 deletion(-) diff --git a/rust/chains/tw_binance/src/transaction/message/mod.rs b/rust/chains/tw_binance/src/transaction/message/mod.rs index bb7bcf8c74c..5974ba50400 100644 --- a/rust/chains/tw_binance/src/transaction/message/mod.rs +++ b/rust/chains/tw_binance/src/transaction/message/mod.rs @@ -41,6 +41,7 @@ pub enum BinanceMessageEnum { SideDelegateOrder(side_chain_delegate::SideDelegateOrder), SideRedelegateOrder(side_chain_delegate::SideRedelegateOrder), SideUndelegateOrder(side_chain_delegate::SideUndelegateOrder), + StakeMigrationOrder(side_chain_delegate::StakeMigrationOrder), TimeLockOrder(time_lock_order::TimeLockOrder), TimeRelockOrder(time_lock_order::TimeRelockOrder), TimeUnlockOrder(time_lock_order::TimeUnlockOrder), @@ -133,6 +134,10 @@ impl TWBinanceProto for BinanceMessageEnum { time_lock_order::TimeUnlockOrder::from_tw_proto(coin, order) .map(BinanceMessageEnum::TimeUnlockOrder) }, + BinanceMessageProto::side_stake_migration_order(ref order) => { + side_chain_delegate::StakeMigrationOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::StakeMigrationOrder) + }, BinanceMessageProto::None => Err(SigningError(SigningErrorType::Error_invalid_params)), } } @@ -159,6 +164,9 @@ impl TWBinanceProto for BinanceMessageEnum { BinanceMessageEnum::SideUndelegateOrder(m) => { BinanceMessageProto::side_undelegate_order(m.to_tw_proto()) }, + BinanceMessageEnum::StakeMigrationOrder(m) => { + BinanceMessageProto::side_stake_migration_order(m.to_tw_proto()) + }, BinanceMessageEnum::TimeLockOrder(m) => { BinanceMessageProto::time_lock_order(m.to_tw_proto()) }, @@ -207,6 +215,7 @@ impl<'a> AsRef for BinanceMessageEnum { BinanceMessageEnum::SideDelegateOrder(m) => m, BinanceMessageEnum::SideRedelegateOrder(m) => m, BinanceMessageEnum::SideUndelegateOrder(m) => m, + BinanceMessageEnum::StakeMigrationOrder(m) => m, BinanceMessageEnum::TimeLockOrder(m) => m, BinanceMessageEnum::TimeRelockOrder(m) => m, BinanceMessageEnum::TimeUnlockOrder(m) => m, diff --git a/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs b/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs index 19e4dce4a80..690d78def18 100644 --- a/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs +++ b/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs @@ -9,6 +9,7 @@ use serde::{Deserialize, Serialize}; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_evm::address::Address as EthereumAddress; use tw_memory::Data; use tw_misc::serde::Typed; use tw_proto::Binance::Proto; @@ -204,3 +205,71 @@ impl TWBinanceProto for SideUndelegateOrder { } } } + +pub type StakeMigrationOrder = Typed; + +/// https://github.com/bnb-chain/bnc-cosmos-sdk/blob/cf3ab19af300ccd6a6381287c3fae6bf6ac12f5e/x/stake/types/stake_migration.go#L29-L35 +#[derive(Deserialize, Serialize)] +pub struct StakeMigrationOrderValue { + #[serde(serialize_with = "Token::serialize_with_string_amount")] + pub amount: Token, + pub delegator_addr: EthereumAddress, + pub refund_addr: BinanceAddress, + pub validator_dst_addr: EthereumAddress, + pub validator_src_addr: BinanceAddress, +} + +impl StakeMigrationOrderValue { + /// cbindgen:ignore + /// https://github.com/bnb-chain/javascript-sdk/blob/442286ac2923fdfd7cb4fb2299f722ec263c714c/src/types/tx/stdTx.ts#L68 + pub const PREFIX: [u8; 4] = [0x38, 0x58, 0x91, 0x96]; + /// cbindgen:ignore + pub const MESSAGE_TYPE: &'static str = "cosmos-sdk/MsgSideChainStakeMigration"; +} + +impl BinanceMessage for StakeMigrationOrder { + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&StakeMigrationOrderValue::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) + } +} + +impl TWBinanceProto for StakeMigrationOrder { + type Proto<'a> = Proto::SideChainStakeMigration<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let delegator_addr = EthereumAddress::try_from(msg.delegator_addr.as_ref())?; + let refund_addr = BinanceAddress::from_key_hash_with_coin(coin, msg.refund_addr.to_vec())?; + let validator_dst_addr = EthereumAddress::try_from(msg.validator_dst_addr.as_ref())?; + let validator_src_addr = + BinanceAddress::new_validator_addr(msg.validator_src_addr.to_vec())?; + + let amount = msg + .amount + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + + let value = StakeMigrationOrderValue { + amount: Token::from_tw_proto(amount), + delegator_addr, + refund_addr, + validator_dst_addr, + validator_src_addr, + }; + Ok(Typed { + ty: StakeMigrationOrderValue::MESSAGE_TYPE.to_string(), + value, + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::SideChainStakeMigration { + delegator_addr: self.value.delegator_addr.data().into(), + validator_src_addr: self.value.validator_src_addr.data().into(), + validator_dst_addr: self.value.validator_dst_addr.data().into(), + refund_addr: self.value.refund_addr.data().into(), + amount: Some(self.value.amount.to_tw_proto()), + } + } +} diff --git a/rust/tw_any_coin/tests/chains/binance/binance_sign.rs b/rust/tw_any_coin/tests/chains/binance/binance_sign.rs index 906f6d5a5e4..d45b6a75902 100644 --- a/rust/tw_any_coin/tests/chains/binance/binance_sign.rs +++ b/rust/tw_any_coin/tests/chains/binance/binance_sign.rs @@ -4,7 +4,7 @@ use crate::chains::binance::{ make_token, ACCOUNT_12_PRIVATE_KEY, ACCOUNT_15_PRIVATE_KEY, ACCOUNT_16_PRIVATE_KEY, - ACCOUNT_19_PRIVATE_KEY, + ACCOUNT_19_PRIVATE_KEY, ACCOUNT_91147_PRIVATE_KEY, }; use tw_any_coin::test_utils::sign_utils::AnySignerHelper; use tw_coin_registry::coin_type::CoinType; @@ -681,6 +681,54 @@ fn test_binance_sign_side_chain_undelegate_order() { assert_eq!(output.signature_json, expected_signature_json); } +#[test] +fn test_binance_sign_side_chain_stake_migration_order() { + // Current staking delegator: + // tbnb1rr74uvz8rcvl5dqn43jkwdufx5aksp4zwzszvs + let refund_addr_key_hash = "18fd5e30471e19fa3413ac65673789353b6806a2"; + // Where the staking amount will be re-delegated: + // 0xCAAc3DAf661b6cEFF18DB1C8fCC2C2fDA1B73893 + let delegator_key_hash = "CAAc3DAf661b6cEFF18DB1C8fCC2C2fDA1B73893"; + // BNB Beacon Chain - ARARAT validator has the following addresses: + // 1. bva1p7s26ervsmv3w83k5696glautc9sm5rchz5f5e (if using `bva` validator HRP); + // 2. tbnb1p7s26ervsmv3w83k5696glautc9sm5rcetua2v (if using `tbnb` testnet HRP). + let validator_src_key_hash = "0fa0ad646c86d9171e36a68ba47fbc5e0b0dd078"; + // Binance Smart Chain - ARARAT validator has the following address: + // 0x341e228f22D4ec16297DD05A9d6347C74c125F66. + let validator_dst_key_hash = "0x341e228f22D4ec16297DD05A9d6347C74c125F66"; + + let stake_migration = Proto::SideChainStakeMigration { + delegator_addr: delegator_key_hash.decode_hex().unwrap().into(), + validator_src_addr: validator_src_key_hash.decode_hex().unwrap().into(), + validator_dst_addr: validator_dst_key_hash.decode_hex().unwrap().into(), + refund_addr: refund_addr_key_hash.decode_hex().unwrap().into(), + // 3.21 BNB + amount: Some(make_token("BNB", 321000000)), + }; + + let input = Proto::SigningInput { + chain_id: "Binance-Chain-Ganges".into(), + account_number: 91147, + sequence: 11, + private_key: ACCOUNT_91147_PRIVATE_KEY.decode_hex().unwrap().into(), + order_oneof: OrderEnum::side_stake_migration_order(stake_migration), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::TBinance, input); + + assert_eq!(output.error, SigningError::OK); + // Successfully broadcasted tx hash: 94E2FC487D93F9744EB0C6BEED7AE1E5F5AF7D6B756B5EEE9AB1E6CB02C7D015 + // Note it's not indexed by the explorer for some reason. + assert_eq!(output.encoded.to_hex(), "e101f0625dee0a69385891960a140fa0ad646c86d9171e36a68ba47fbc5e0b0dd0781214341e228f22d4ec16297dd05a9d6347c74c125f661a14caac3daf661b6ceff18db1c8fcc2c2fda1b73893221418fd5e30471e19fa3413ac65673789353b6806a22a0b0a03424e4210c0a488990112700a26eb5ae987210243107052476885baddb4fad4df3fa07f4df807eac64daf6035e25232180f8c6a124065a45c4001488121e8199c3d87bc3dbcf1612ba690f0c474cc94f9a060a9e1bc302943dca5cd9240bba1c78a1691700e23d538efad79cee856a34595247249b6188bc805200b"); + + let expected_signature = "65a45c4001488121e8199c3d87bc3dbcf1612ba690f0c474cc94f9a060a9e1bc302943dca5cd9240bba1c78a1691700e23d538efad79cee856a34595247249b6"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AkMQcFJHaIW63bT61N8/oH9N+Afqxk2vYDXiUjIYD4xq"},"signature":"ZaRcQAFIgSHoGZw9h7w9vPFhK6aQ8MR0zJT5oGCp4bwwKUPcpc2SQLuhx4oWkXAOI9U47615zuhWo0WVJHJJtg=="}"#; + assert_eq!(output.signature_json, expected_signature_json); +} + #[test] fn test_binance_sign_time_lock_order() { let from_key_hash = "08c7c918f6b72c3c0c21b7d08eb6fc66509998e1"; diff --git a/rust/tw_any_coin/tests/chains/binance/mod.rs b/rust/tw_any_coin/tests/chains/binance/mod.rs index a4f3860818a..e86e4ed1056 100644 --- a/rust/tw_any_coin/tests/chains/binance/mod.rs +++ b/rust/tw_any_coin/tests/chains/binance/mod.rs @@ -17,6 +17,9 @@ const ACCOUNT_15_PRIVATE_KEY: &str = "eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d"; const ACCOUNT_16_PRIVATE_KEY: &str = "851fab89c14f4bbec0cc06f5e445ec065efc641068d78b308c67217d9bd5c88a"; +/// tbnb1rr74uvz8rcvl5dqn43jkwdufx5aksp4zwzszvs +const ACCOUNT_91147_PRIVATE_KEY: &str = + "56b1253d944956c7f5b7668892509a44290f1fd149edecbe4fd44f69ba04b84c"; fn make_token(denom: &str, amount: i64) -> Proto::mod_SendOrder::Token { Proto::mod_SendOrder::Token { diff --git a/rust/tw_evm/src/address.rs b/rust/tw_evm/src/address.rs index 81fb72e0222..6da55393fde 100644 --- a/rust/tw_evm/src/address.rs +++ b/rust/tw_evm/src/address.rs @@ -110,6 +110,15 @@ impl FromStr for Address { } } +impl<'a> TryFrom<&'a [u8]> for Address { + type Error = AddressError; + + fn try_from(bytes: &'a [u8]) -> Result { + let bytes = H160::try_from(bytes).map_err(|_| AddressError::InvalidInput)?; + Ok(Address { bytes }) + } +} + impl EvmAddress for Address {} /// Implement `str` -> `PrivateKey` conversion for test purposes. diff --git a/src/proto/Binance.proto b/src/proto/Binance.proto index 01aa30a3698..7fe597ee92e 100644 --- a/src/proto/Binance.proto +++ b/src/proto/Binance.proto @@ -282,6 +282,16 @@ message SideChainUndelegate { string chain_id = 4; } +// Message for BNB Beacon Chain -> BSC Stake Migration. +// https://github.com/bnb-chain/javascript-sdk/blob/26f6db8b67326e6214e74203ff90c89777b592a1/src/types/msg/stake/stakeMigrationMsg.ts#L13-L18 +message SideChainStakeMigration { + bytes validator_src_addr = 1; + bytes validator_dst_addr = 2; + bytes delegator_addr = 3; + bytes refund_addr = 4; + SendOrder.Token amount = 5; +} + // Message for TimeLock order message TimeLockOrder { // owner address @@ -369,6 +379,7 @@ message SigningInput { TimeLockOrder time_lock_order = 24; TimeRelockOrder time_relock_order = 25; TimeUnlockOrder time_unlock_order = 26; + SideChainStakeMigration side_stake_migration_order = 27; } } From 7d03d04c8e420bbdac250332246936b8d405232f Mon Sep 17 00:00:00 2001 From: Andrii Rakhimov Date: Thu, 11 Apr 2024 11:50:50 +0100 Subject: [PATCH 077/128] [ICP]: Update ingress processing timeout (#3782) * [ICP]: Update ingress processing timeout * [ICP]: Update ingress processing timeout * Format * Format --------- Co-authored-by: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> --- .../fuzz_targets/tw_internet_computer_transfer.rs | 1 + rust/tw_internet_computer/src/protocol/mod.rs | 15 +++++++++------ rust/tw_internet_computer/src/transactions/mod.rs | 5 +++++ .../src/transactions/transfer.rs | 5 ++++- src/proto/InternetComputer.proto | 1 + 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/rust/tw_internet_computer/fuzz/fuzz_targets/tw_internet_computer_transfer.rs b/rust/tw_internet_computer/fuzz/fuzz_targets/tw_internet_computer_transfer.rs index cfd4b494762..568b15610b2 100644 --- a/rust/tw_internet_computer/fuzz/fuzz_targets/tw_internet_computer_transfer.rs +++ b/rust/tw_internet_computer/fuzz/fuzz_targets/tw_internet_computer_transfer.rs @@ -16,6 +16,7 @@ struct ArbitraryTransferArgs { #[arbitrary(with = arbitrary_to_field)] to: String, current_timestamp_nanos: u64, + permitted_drift: Option, } fn arbitrary_to_field(u: &mut arbitrary::Unstructured) -> arbitrary::Result { diff --git a/rust/tw_internet_computer/src/protocol/mod.rs b/rust/tw_internet_computer/src/protocol/mod.rs index 19b13630cc2..5d1f0380c38 100644 --- a/rust/tw_internet_computer/src/protocol/mod.rs +++ b/rust/tw_internet_computer/src/protocol/mod.rs @@ -20,14 +20,17 @@ use std::time::Duration; /// is maintained by the IC before it is deleted from the ingress history. const MAX_INGRESS_TTL: Duration = Duration::from_secs(5 * 60); -/// Duration subtracted from `MAX_INGRESS_TTL` by -/// `expiry_time_from_now()` when creating an ingress message. -const PERMITTED_DRIFT: Duration = Duration::from_secs(60); - /// An upper limit on the validity of the request, expressed in nanoseconds since 1970-01-01. -pub fn get_ingress_expiry(current_timestamp_duration: Duration) -> u64 { +pub fn get_ingress_expiry( + current_timestamp_duration: Duration, + permitted_drift_in_seconds: Option, +) -> u64 { + let permitted_drift = permitted_drift_in_seconds + .map(Duration::from_secs) + .unwrap_or(Duration::from_secs(60)); + current_timestamp_duration .saturating_add(MAX_INGRESS_TTL) - .saturating_sub(PERMITTED_DRIFT) + .saturating_sub(permitted_drift) .as_nanos() as u64 } diff --git a/rust/tw_internet_computer/src/transactions/mod.rs b/rust/tw_internet_computer/src/transactions/mod.rs index d15fa9be777..e0c21697c1d 100644 --- a/rust/tw_internet_computer/src/transactions/mod.rs +++ b/rust/tw_internet_computer/src/transactions/mod.rs @@ -38,6 +38,11 @@ pub fn sign_transaction( max_fee: None, to: transfer_args.to_account_identifier.to_string(), current_timestamp_nanos: transfer_args.current_timestamp_nanos, + permitted_drift: if transfer_args.permitted_drift > 0 { + Some(transfer_args.permitted_drift) + } else { + None + }, }, ), Tx::None => Err(SignTransactionError::InvalidArguments), diff --git a/rust/tw_internet_computer/src/transactions/transfer.rs b/rust/tw_internet_computer/src/transactions/transfer.rs index 78708226468..7cc846f3b53 100644 --- a/rust/tw_internet_computer/src/transactions/transfer.rs +++ b/rust/tw_internet_computer/src/transactions/transfer.rs @@ -39,6 +39,8 @@ pub struct TransferArgs { pub to: String, /// The current timestamp in nanoseconds. pub current_timestamp_nanos: u64, + /// The duration to tune up ingress expiry in seconds. + pub permitted_drift: Option, } impl TryFrom for SendRequest { @@ -82,7 +84,7 @@ pub fn transfer( } let current_timestamp_duration = Duration::from_nanos(args.current_timestamp_nanos); - let ingress_expiry = get_ingress_expiry(current_timestamp_duration); + let ingress_expiry = get_ingress_expiry(current_timestamp_duration, args.permitted_drift); let identity = Identity::new(private_key); // Encode the arguments for the ledger `send_pb` endpoint. @@ -189,6 +191,7 @@ mod test { max_fee: None, to: to_account_identifier.to_hex(), current_timestamp_nanos, + permitted_drift: None, } } diff --git a/src/proto/InternetComputer.proto b/src/proto/InternetComputer.proto index f76380f21da..a74c86107d3 100644 --- a/src/proto/InternetComputer.proto +++ b/src/proto/InternetComputer.proto @@ -18,6 +18,7 @@ message Transaction { uint64 amount = 2; uint64 memo = 3; uint64 current_timestamp_nanos = 4; + uint64 permitted_drift = 5; } // Payload transfer From 8daa8f9e66e2a8315ca0a3294d670a16bea794dc Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:51:31 +0200 Subject: [PATCH 078/128] [Lightlink]: Add Lightlink Phoenix EVM chain (#3784) * feat(lightlink): Add Lightlink Phoenix EVM chain * feat(lightlink): Fix iOS test --- .../blockchains/CoinAddressDerivationTests.kt | 2 +- docs/registry.md | 1 + include/TrustWalletCore/TWCoinType.h | 1 + .../core/test/CoinAddressDerivationTests.kt | 2 +- registry.json | 31 +++++++++++++++++++ .../tests/coin_address_derivation_test.rs | 1 + swift/Tests/CoinAddressDerivationTests.swift | 3 +- tests/chains/Lightlink/TWCoinTypeTests.cpp | 29 +++++++++++++++++ tests/common/CoinAddressDerivationTests.cpp | 1 + 9 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 tests/chains/Lightlink/TWCoinTypeTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index da03cbf9021..aec21fe9d39 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -47,7 +47,7 @@ class CoinAddressDerivationTests { FANTOM, CELO, CRONOSCHAIN, SMARTBITCOINCASH, KUCOINCOMMUNITYCHAIN, BOBA, METIS, AURORA, EVMOS, MOONRIVER, MOONBEAM, KAVAEVM, KLAYTN, METER, OKXCHAIN, POLYGONZKEVM, SCROLL, CONFLUXESPACE, ACALAEVM, OPBNB, NEON, BASE, LINEA, GREENFIELD, MANTLE, ZENEON, MANTAPACIFIC, - ZETAEVM, MERLIN, + ZETAEVM, MERLIN, LIGHTLINK, -> assertEquals("0x8f348F300873Fd5DA36950B2aC75a26584584feE", address) RONIN -> assertEquals("ronin:8f348F300873Fd5DA36950B2aC75a26584584feE", address) diff --git a/docs/registry.md b/docs/registry.md index 4079dd81817..1b07f45a003 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -84,6 +84,7 @@ This list is generated from [./registry.json](../registry.json) | 1030 | Conflux eSpace | CFX | | | | 1729 | Tezos | XTZ | | | | 1815 | Cardano | ADA | | | +| 1890 | Lightlink Phoenix | ETH | | | | 2301 | Qtum | QTUM | | | | 2718 | Nebulas | NAS | | | | 3030 | Hedera | HBAR | | | diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 7722e0bea4c..e953cccc197 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -182,6 +182,7 @@ enum TWCoinType { TWCoinTypeZetaEVM = 20007000, TWCoinTypeDydx = 22000118, TWCoinTypeMerlin = 4200, + TWCoinTypeLightlink = 1890, // end_of_tw_coin_type_marker_do_not_modify }; diff --git a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt index b075a460195..0f50a958c6a 100644 --- a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt +++ b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt @@ -40,7 +40,7 @@ class CoinAddressDerivationTests { Fantom, Celo, CronosChain, SmartBitcoinCash, KuCoinCommunityChain, Boba, Metis, Aurora, Evmos, Moonriver, Moonbeam, KavaEvm, Klaytn, Meter, OKXChain, PolygonzkEVM, Scroll, ConfluxeSpace, AcalaEVM, OpBNB, Neon, Base, Linea, Greenfield, Mantle, ZenEON, MantaPacific, - ZetaEVM, Merlin, + ZetaEVM, Merlin, Lightlink, -> "0x8f348F300873Fd5DA36950B2aC75a26584584feE" Ronin -> "ronin:8f348F300873Fd5DA36950B2aC75a26584584feE" diff --git a/registry.json b/registry.json index acb68493aea..06b15a770e9 100644 --- a/registry.json +++ b/registry.json @@ -4658,5 +4658,36 @@ "rpc": "https://rpc.merlinchain.io", "documentation": "https://docs.merlinchain.io/merlin-docs" } + }, + { + "id": "lightlink", + "name": "Lightlink", + "displayName": "Lightlink Phoenix", + "coinId": 1890, + "symbol": "ETH", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "1890", + "addressHasher": "keccak256", + "explorer": { + "url": "https://phoenix.lightlink.io", + "txPath": "/tx/", + "accountPath": "/address/", + "sampleTx": "0xc65f82445aefc883fd4ffe09149c8ce48f61b670c0734528a49d4a8bd8309bb0", + "sampleAccount": "0x4566ED6c7a7fFc90E2C7cfF7eB9156262afD2fDe" + }, + "info": { + "url": "https://lightlink.io", + "source": "https://github.com/lightlink-network", + "rpc": "https://endpoints.omniatech.io/v1/lightlink/phoenix/public", + "documentation": "https://docs.lightlink.io/lightlink-protocol" + } } ] diff --git a/rust/tw_any_coin/tests/coin_address_derivation_test.rs b/rust/tw_any_coin/tests/coin_address_derivation_test.rs index 6225dc7304a..7a4189138a2 100644 --- a/rust/tw_any_coin/tests/coin_address_derivation_test.rs +++ b/rust/tw_any_coin/tests/coin_address_derivation_test.rs @@ -87,6 +87,7 @@ fn test_coin_address_derivation() { | CoinType::MantaPacific | CoinType::ZetaEVM | CoinType::Merlin + | CoinType::Lightlink // end_of_evm_address_derivation_tests_marker_do_not_modify => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", CoinType::Bitcoin diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 25e187439f3..b4bb9d10cbb 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -115,7 +115,8 @@ class CoinAddressDerivationTests: XCTestCase { .zenEON, .mantaPacific, .zetaEVM, - .merlin: + .merlin, + .lightlink: let expectedResult = "0x8f348F300873Fd5DA36950B2aC75a26584584feE" assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .ronin: diff --git a/tests/chains/Lightlink/TWCoinTypeTests.cpp b/tests/chains/Lightlink/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..1b41b162d87 --- /dev/null +++ b/tests/chains/Lightlink/TWCoinTypeTests.cpp @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#include "TestUtilities.h" +#include +#include + +TEST(TWLightlinkCoinType, TWCoinType) { + const auto coin = TWCoinTypeLightlink; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xc65f82445aefc883fd4ffe09149c8ce48f61b670c0734528a49d4a8bd8309bb0")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x4566ED6c7a7fFc90E2C7cfF7eB9156262afD2fDe")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "lightlink"); + assertStringsEqual(name, "Lightlink Phoenix"); + assertStringsEqual(symbol, "ETH"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainEthereum); + ASSERT_EQ(TWCoinTypeP2pkhPrefix(coin), 0); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0); + assertStringsEqual(txUrl, "https://phoenix.lightlink.io/tx/0xc65f82445aefc883fd4ffe09149c8ce48f61b670c0734528a49d4a8bd8309bb0"); + assertStringsEqual(accUrl, "https://phoenix.lightlink.io/address/0x4566ED6c7a7fFc90E2C7cfF7eB9156262afD2fDe"); +} diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index 398db624bfd..1ecef1da434 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -85,6 +85,7 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeMantaPacific: case TWCoinTypeZetaEVM: case TWCoinTypeMerlin: + case TWCoinTypeLightlink: // end_of_evm_address_derivation_tests_marker_do_not_modify EXPECT_EQ(address, "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); break; From 41bd37302f1485303a9cbe4182049f57d225a33e Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Thu, 11 Apr 2024 13:25:29 +0200 Subject: [PATCH 079/128] feat(Blast): Add Blast EVM chain (#3785) * feat(lightlink): Add Lightlink Phoenix EVM chain * feat(lightlink): Fix iOS test * feat(blast): Add Blast EVM chain --- .../blockchains/CoinAddressDerivationTests.kt | 2 +- docs/registry.md | 1 + include/TrustWalletCore/TWCoinType.h | 1 + .../core/test/CoinAddressDerivationTests.kt | 2 +- registry.json | 30 +++++++++++++++++++ .../tests/coin_address_derivation_test.rs | 1 + swift/Tests/CoinAddressDerivationTests.swift | 3 +- tests/chains/Blast/TWCoinTypeTests.cpp | 29 ++++++++++++++++++ tests/common/CoinAddressDerivationTests.cpp | 1 + 9 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 tests/chains/Blast/TWCoinTypeTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index aec21fe9d39..07108b4c9d6 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -47,7 +47,7 @@ class CoinAddressDerivationTests { FANTOM, CELO, CRONOSCHAIN, SMARTBITCOINCASH, KUCOINCOMMUNITYCHAIN, BOBA, METIS, AURORA, EVMOS, MOONRIVER, MOONBEAM, KAVAEVM, KLAYTN, METER, OKXCHAIN, POLYGONZKEVM, SCROLL, CONFLUXESPACE, ACALAEVM, OPBNB, NEON, BASE, LINEA, GREENFIELD, MANTLE, ZENEON, MANTAPACIFIC, - ZETAEVM, MERLIN, LIGHTLINK, + ZETAEVM, MERLIN, LIGHTLINK, BLAST, -> assertEquals("0x8f348F300873Fd5DA36950B2aC75a26584584feE", address) RONIN -> assertEquals("ronin:8f348F300873Fd5DA36950B2aC75a26584584feE", address) diff --git a/docs/registry.md b/docs/registry.md index 1b07f45a003..46c7671fa3f 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -100,6 +100,7 @@ This list is generated from [./registry.json](../registry.json) | 19167 | Flux | FLUX | | | | 52752 | Celo | CELO | | | | 59144 | Linea | ETH | | | +| 81457 | Blast | ETH | | | | 105105 | Stratis | STRAX | | | | 534352 | Scroll | ETH | | | | 5718350 | Wanchain | WAN | | | diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index e953cccc197..3af38bb8a6c 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -183,6 +183,7 @@ enum TWCoinType { TWCoinTypeDydx = 22000118, TWCoinTypeMerlin = 4200, TWCoinTypeLightlink = 1890, + TWCoinTypeBlast = 81457, // end_of_tw_coin_type_marker_do_not_modify }; diff --git a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt index 0f50a958c6a..3c1277ce712 100644 --- a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt +++ b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt @@ -40,7 +40,7 @@ class CoinAddressDerivationTests { Fantom, Celo, CronosChain, SmartBitcoinCash, KuCoinCommunityChain, Boba, Metis, Aurora, Evmos, Moonriver, Moonbeam, KavaEvm, Klaytn, Meter, OKXChain, PolygonzkEVM, Scroll, ConfluxeSpace, AcalaEVM, OpBNB, Neon, Base, Linea, Greenfield, Mantle, ZenEON, MantaPacific, - ZetaEVM, Merlin, Lightlink, + ZetaEVM, Merlin, Lightlink, Blast, -> "0x8f348F300873Fd5DA36950B2aC75a26584584feE" Ronin -> "ronin:8f348F300873Fd5DA36950B2aC75a26584584feE" diff --git a/registry.json b/registry.json index 06b15a770e9..8044e408aae 100644 --- a/registry.json +++ b/registry.json @@ -4689,5 +4689,35 @@ "rpc": "https://endpoints.omniatech.io/v1/lightlink/phoenix/public", "documentation": "https://docs.lightlink.io/lightlink-protocol" } + }, + { + "id": "blast", + "name": "Blast", + "coinId": 81457, + "symbol": "ETH", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "81457", + "addressHasher": "keccak256", + "explorer": { + "url": "https://blastscan.io", + "txPath": "/tx/", + "accountPath": "/address/", + "sampleTx": "0x511fc00e8329343b9e953bf1f75e9b0a7b3cc2eb3a8f049d5be41adf4fbd6cac", + "sampleAccount": "0x0d11f2f0ff55c4fcfc3ff86bdc8e78ffa7df99fd" + }, + "info": { + "url": "https://blast.io", + "source": "https://github.com/blast-io", + "rpc": "https://rpc.blast.io", + "documentation": "https://docs.blast.io" + } } ] diff --git a/rust/tw_any_coin/tests/coin_address_derivation_test.rs b/rust/tw_any_coin/tests/coin_address_derivation_test.rs index 7a4189138a2..a0929cbec72 100644 --- a/rust/tw_any_coin/tests/coin_address_derivation_test.rs +++ b/rust/tw_any_coin/tests/coin_address_derivation_test.rs @@ -88,6 +88,7 @@ fn test_coin_address_derivation() { | CoinType::ZetaEVM | CoinType::Merlin | CoinType::Lightlink + | CoinType::Blast // end_of_evm_address_derivation_tests_marker_do_not_modify => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", CoinType::Bitcoin diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index b4bb9d10cbb..9c04c72e16d 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -116,7 +116,8 @@ class CoinAddressDerivationTests: XCTestCase { .mantaPacific, .zetaEVM, .merlin, - .lightlink: + .lightlink, + .blast: let expectedResult = "0x8f348F300873Fd5DA36950B2aC75a26584584feE" assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .ronin: diff --git a/tests/chains/Blast/TWCoinTypeTests.cpp b/tests/chains/Blast/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..6136c26626d --- /dev/null +++ b/tests/chains/Blast/TWCoinTypeTests.cpp @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#include "TestUtilities.h" +#include +#include + +TEST(TWBlastCoinType, TWCoinType) { + const auto coin = TWCoinTypeBlast; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x511fc00e8329343b9e953bf1f75e9b0a7b3cc2eb3a8f049d5be41adf4fbd6cac")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x0d11f2f0ff55c4fcfc3ff86bdc8e78ffa7df99fd")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "blast"); + assertStringsEqual(name, "Blast"); + assertStringsEqual(symbol, "ETH"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainEthereum); + ASSERT_EQ(TWCoinTypeP2pkhPrefix(coin), 0); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0); + assertStringsEqual(txUrl, "https://blastscan.io/tx/0x511fc00e8329343b9e953bf1f75e9b0a7b3cc2eb3a8f049d5be41adf4fbd6cac"); + assertStringsEqual(accUrl, "https://blastscan.io/address/0x0d11f2f0ff55c4fcfc3ff86bdc8e78ffa7df99fd"); +} diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index 1ecef1da434..04cb56c4fb4 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -86,6 +86,7 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeZetaEVM: case TWCoinTypeMerlin: case TWCoinTypeLightlink: + case TWCoinTypeBlast: // end_of_evm_address_derivation_tests_marker_do_not_modify EXPECT_EQ(address, "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); break; From 49a36fcb01533d6805a63fdf321b0636b082bd90 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Thu, 11 Apr 2024 13:25:51 +0200 Subject: [PATCH 080/128] [SUI]: Move SUI blockchain to Rust (#3769) * feat(Sui): Add Sui Rust skeleton files * feat(ronin): reorganize tw_aptos, tw_ethereum, tw_ronin, tw_internet_computer * feat(ronin): Implement `SuiAddress` * feat(sui): Implement pay, paySui, payAllSui, request_addStake, request_withdrawStake * feat(sui): Add Protobuf transaction types * feat(sui): Remove unnecessary derives * feat(sui): Implement direct signing * feat(merlin): Change symbol from ETH to BTC * feat(sui): Add direct sign tests * feat(solana): Add delegate stake with priority fee test * feat(sui): Add pay_sui, pay_all_sui, pay, request_add_stake, request_withdraw_stake * feat(sui): Implement transaction preimage and compile functions * feat(sui): Add `pay` and `pay_sui` tests * feat(sui): Add split, merge sui and token tests * feat(sui): Add pay_all_sui test * feat(sui): Add request_addStake test * feat(sui): Fix direct signing to support legacy tests * feat(sui): Add compile test * feat(sui): Add compile direct test * feat(sui): Replace C++ implementation with Rust FFI * feat(sui): Add `test_sui_sign_undelegate_sui` test * feat(sui): Add Android, iOS tests * feat(sui): Fix tests * Add fuzz tests * feat(sui): Add TransferObject transaction type * Add fuzz tests * feat(sui): Comment TransferObject Protobuf message * feat(sui): Fix C++ includes --- .../core/app/blockchains/sui/TestSuiSigner.kt | 35 +- rust/Cargo.lock | 17 + rust/Cargo.toml | 9 +- rust/chains/tw_aptos/Cargo.toml | 22 + rust/{ => chains}/tw_aptos/fuzz/.gitignore | 0 rust/{ => chains}/tw_aptos/fuzz/Cargo.toml | 0 .../tw_aptos/fuzz/fuzz_targets/sign.rs | 0 rust/{ => chains}/tw_aptos/src/address.rs | 19 +- .../tw_aptos/src/aptos_move_packages.rs | 0 .../{ => chains}/tw_aptos/src/bcs_encoding.rs | 0 rust/{ => chains}/tw_aptos/src/compiler.rs | 0 rust/{ => chains}/tw_aptos/src/constants.rs | 0 rust/{ => chains}/tw_aptos/src/entry.rs | 0 rust/{ => chains}/tw_aptos/src/lib.rs | 0 .../tw_aptos/src/liquid_staking.rs | 0 rust/{ => chains}/tw_aptos/src/nft.rs | 0 .../tw_aptos/src/serde_helper/mod.rs | 0 .../tw_aptos/src/serde_helper/vec_bytes.rs | 0 rust/{ => chains}/tw_aptos/src/signer.rs | 0 rust/{ => chains}/tw_aptos/src/transaction.rs | 0 .../tw_aptos/src/transaction_builder.rs | 0 .../tw_aptos/src/transaction_payload.rs | 0 rust/{ => chains}/tw_aptos/tests/signer.rs | 0 .../src/transaction/message/htlt_order.rs | 10 +- rust/chains/tw_ethereum/Cargo.toml | 15 + rust/{ => chains}/tw_ethereum/src/entry.rs | 0 rust/{ => chains}/tw_ethereum/src/lib.rs | 0 .../tw_ethereum/tests/compiler.rs | 0 rust/{ => chains}/tw_ethereum/tests/signer.rs | 0 rust/chains/tw_internet_computer/Cargo.toml | 17 + .../tw_internet_computer/build.rs | 0 .../tw_internet_computer/fuzz/.gitignore | 0 .../tw_internet_computer/fuzz/Cargo.lock | 0 .../tw_internet_computer/fuzz/Cargo.toml | 0 .../tw_internet_computer_transfer.rs | 0 .../tw_internet_computer/src/address.rs | 0 .../tw_internet_computer/src/context.rs | 0 .../tw_internet_computer/src/entry.rs | 0 .../tw_internet_computer/src/lib.rs | 0 .../src/protocol/envelope.rs | 0 .../src/protocol/identity.rs | 0 .../tw_internet_computer/src/protocol/mod.rs | 0 .../src/protocol/principal.rs | 0 .../src/protocol/request_id.rs | 0 .../src/protocol/rosetta.rs | 0 .../tw_internet_computer/src/signer.rs | 0 .../src/transactions/mod.rs | 0 .../src/transactions/proto/ledger.proto | 0 .../src/transactions/proto/types.proto | 0 .../src/transactions/transfer.rs | 0 rust/chains/tw_ronin/Cargo.toml | 16 + rust/{ => chains}/tw_ronin/src/address.rs | 0 rust/{ => chains}/tw_ronin/src/entry.rs | 0 rust/{ => chains}/tw_ronin/src/lib.rs | 0 .../tw_ronin/src/ronin_context.rs | 0 rust/{ => chains}/tw_ronin/tests/address.rs | 0 rust/{ => chains}/tw_ronin/tests/compiler.rs | 0 rust/{ => chains}/tw_ronin/tests/rlp.rs | 0 rust/{ => chains}/tw_ronin/tests/signer.rs | 0 rust/chains/tw_sui/Cargo.toml | 16 + rust/chains/tw_sui/fuzz/.gitignore | 5 + rust/chains/tw_sui/fuzz/Cargo.toml | 30 ++ rust/chains/tw_sui/fuzz/fuzz_targets/sign.rs | 15 + rust/chains/tw_sui/src/address.rs | 89 ++++ rust/chains/tw_sui/src/compiler.rs | 99 ++++ rust/chains/tw_sui/src/constants.rs | 30 ++ rust/chains/tw_sui/src/entry.rs | 96 ++++ rust/chains/tw_sui/src/lib.rs | 12 + rust/chains/tw_sui/src/modules/mod.rs | 6 + rust/chains/tw_sui/src/modules/tx_builder.rs | 211 ++++++++ rust/chains/tw_sui/src/modules/tx_signer.rs | 138 ++++++ rust/chains/tw_sui/src/signature.rs | 46 ++ rust/chains/tw_sui/src/signer.rs | 46 ++ rust/chains/tw_sui/src/transaction/command.rs | 83 ++++ rust/chains/tw_sui/src/transaction/mod.rs | 9 + .../transaction/programmable_transaction.rs | 239 +++++++++ .../tw_sui/src/transaction/sui_types.rs | 102 ++++ .../src/transaction/transaction_builder.rs | 185 +++++++ .../src/transaction/transaction_data.rs | 225 +++++++++ .../chains/tw_sui/tests/decode_transaction.rs | 66 +++ rust/coverage.stats | 2 +- rust/tw_any_coin/tests/chains/mod.rs | 1 + .../tests/chains/solana/solana_sign.rs | 30 ++ rust/tw_any_coin/tests/chains/sui/mod.rs | 18 + .../tests/chains/sui/sui_address.rs | 55 ++ .../tests/chains/sui/sui_compile.rs | 92 ++++ rust/tw_any_coin/tests/chains/sui/sui_sign.rs | 468 ++++++++++++++++++ .../tests/chains/sui/test_cases.rs | 44 ++ .../tests/coin_address_derivation_test.rs | 1 + rust/tw_aptos/Cargo.toml | 22 - rust/tw_coin_registry/Cargo.toml | 9 +- rust/tw_coin_registry/src/blockchain_type.rs | 1 + rust/tw_coin_registry/src/dispatcher.rs | 3 + rust/tw_encoding/src/bcs.rs | 8 + rust/tw_encoding/src/hex.rs | 33 +- rust/tw_ethereum/Cargo.toml | 15 - rust/tw_evm/src/modules/abi_encoder.rs | 8 +- rust/tw_hash/src/hash_array.rs | 92 ++-- rust/tw_hash/src/lib.rs | 2 +- rust/tw_internet_computer/Cargo.toml | 17 - rust/tw_keypair/src/ed25519/signature.rs | 3 + .../tw_keypair/tests/ed25519_blake2b_tests.rs | 4 +- .../tests/ed25519_extended_cardano_tests.rs | 3 +- rust/tw_keypair/tests/ed25519_tests.rs | 6 +- rust/tw_keypair/tests/ed25519_waves_tests.rs | 6 +- rust/tw_keypair/tests/nist256p1_tests.rs | 6 + rust/tw_keypair/tests/secp256k1_tests.rs | 4 + rust/tw_ronin/Cargo.toml | 16 - rust/wallet_core_rs/Cargo.toml | 2 +- src/Sui/Address.cpp | 22 - src/Sui/Address.h | 37 -- src/Sui/Entry.cpp | 43 -- src/Sui/Entry.h | 10 +- src/Sui/Signer.cpp | 69 --- src/Sui/Signer.h | 31 -- src/proto/Sui.proto | 117 ++++- swift/Tests/Blockchains/SuiTests.swift | 33 +- tests/chains/Sui/AddressTests.cpp | 52 -- tests/chains/Sui/CompilerTests.cpp | 3 +- tests/chains/Sui/SignerTests.cpp | 46 +- tests/common/HDWallet/HDWalletTests.cpp | 13 - tests/common/TestUtilities.h | 2 + 122 files changed, 2890 insertions(+), 467 deletions(-) create mode 100644 rust/chains/tw_aptos/Cargo.toml rename rust/{ => chains}/tw_aptos/fuzz/.gitignore (100%) rename rust/{ => chains}/tw_aptos/fuzz/Cargo.toml (100%) rename rust/{ => chains}/tw_aptos/fuzz/fuzz_targets/sign.rs (100%) rename rust/{ => chains}/tw_aptos/src/address.rs (87%) rename rust/{ => chains}/tw_aptos/src/aptos_move_packages.rs (100%) rename rust/{ => chains}/tw_aptos/src/bcs_encoding.rs (100%) rename rust/{ => chains}/tw_aptos/src/compiler.rs (100%) rename rust/{ => chains}/tw_aptos/src/constants.rs (100%) rename rust/{ => chains}/tw_aptos/src/entry.rs (100%) rename rust/{ => chains}/tw_aptos/src/lib.rs (100%) rename rust/{ => chains}/tw_aptos/src/liquid_staking.rs (100%) rename rust/{ => chains}/tw_aptos/src/nft.rs (100%) rename rust/{ => chains}/tw_aptos/src/serde_helper/mod.rs (100%) rename rust/{ => chains}/tw_aptos/src/serde_helper/vec_bytes.rs (100%) rename rust/{ => chains}/tw_aptos/src/signer.rs (100%) rename rust/{ => chains}/tw_aptos/src/transaction.rs (100%) rename rust/{ => chains}/tw_aptos/src/transaction_builder.rs (100%) rename rust/{ => chains}/tw_aptos/src/transaction_payload.rs (100%) rename rust/{ => chains}/tw_aptos/tests/signer.rs (100%) create mode 100644 rust/chains/tw_ethereum/Cargo.toml rename rust/{ => chains}/tw_ethereum/src/entry.rs (100%) rename rust/{ => chains}/tw_ethereum/src/lib.rs (100%) rename rust/{ => chains}/tw_ethereum/tests/compiler.rs (100%) rename rust/{ => chains}/tw_ethereum/tests/signer.rs (100%) create mode 100644 rust/chains/tw_internet_computer/Cargo.toml rename rust/{ => chains}/tw_internet_computer/build.rs (100%) rename rust/{ => chains}/tw_internet_computer/fuzz/.gitignore (100%) rename rust/{ => chains}/tw_internet_computer/fuzz/Cargo.lock (100%) rename rust/{ => chains}/tw_internet_computer/fuzz/Cargo.toml (100%) rename rust/{ => chains}/tw_internet_computer/fuzz/fuzz_targets/tw_internet_computer_transfer.rs (100%) rename rust/{ => chains}/tw_internet_computer/src/address.rs (100%) rename rust/{ => chains}/tw_internet_computer/src/context.rs (100%) rename rust/{ => chains}/tw_internet_computer/src/entry.rs (100%) rename rust/{ => chains}/tw_internet_computer/src/lib.rs (100%) rename rust/{ => chains}/tw_internet_computer/src/protocol/envelope.rs (100%) rename rust/{ => chains}/tw_internet_computer/src/protocol/identity.rs (100%) rename rust/{ => chains}/tw_internet_computer/src/protocol/mod.rs (100%) rename rust/{ => chains}/tw_internet_computer/src/protocol/principal.rs (100%) rename rust/{ => chains}/tw_internet_computer/src/protocol/request_id.rs (100%) rename rust/{ => chains}/tw_internet_computer/src/protocol/rosetta.rs (100%) rename rust/{ => chains}/tw_internet_computer/src/signer.rs (100%) rename rust/{ => chains}/tw_internet_computer/src/transactions/mod.rs (100%) rename rust/{ => chains}/tw_internet_computer/src/transactions/proto/ledger.proto (100%) rename rust/{ => chains}/tw_internet_computer/src/transactions/proto/types.proto (100%) rename rust/{ => chains}/tw_internet_computer/src/transactions/transfer.rs (100%) create mode 100644 rust/chains/tw_ronin/Cargo.toml rename rust/{ => chains}/tw_ronin/src/address.rs (100%) rename rust/{ => chains}/tw_ronin/src/entry.rs (100%) rename rust/{ => chains}/tw_ronin/src/lib.rs (100%) rename rust/{ => chains}/tw_ronin/src/ronin_context.rs (100%) rename rust/{ => chains}/tw_ronin/tests/address.rs (100%) rename rust/{ => chains}/tw_ronin/tests/compiler.rs (100%) rename rust/{ => chains}/tw_ronin/tests/rlp.rs (100%) rename rust/{ => chains}/tw_ronin/tests/signer.rs (100%) create mode 100644 rust/chains/tw_sui/Cargo.toml create mode 100644 rust/chains/tw_sui/fuzz/.gitignore create mode 100644 rust/chains/tw_sui/fuzz/Cargo.toml create mode 100644 rust/chains/tw_sui/fuzz/fuzz_targets/sign.rs create mode 100644 rust/chains/tw_sui/src/address.rs create mode 100644 rust/chains/tw_sui/src/compiler.rs create mode 100644 rust/chains/tw_sui/src/constants.rs create mode 100644 rust/chains/tw_sui/src/entry.rs create mode 100644 rust/chains/tw_sui/src/lib.rs create mode 100644 rust/chains/tw_sui/src/modules/mod.rs create mode 100644 rust/chains/tw_sui/src/modules/tx_builder.rs create mode 100644 rust/chains/tw_sui/src/modules/tx_signer.rs create mode 100644 rust/chains/tw_sui/src/signature.rs create mode 100644 rust/chains/tw_sui/src/signer.rs create mode 100644 rust/chains/tw_sui/src/transaction/command.rs create mode 100644 rust/chains/tw_sui/src/transaction/mod.rs create mode 100644 rust/chains/tw_sui/src/transaction/programmable_transaction.rs create mode 100644 rust/chains/tw_sui/src/transaction/sui_types.rs create mode 100644 rust/chains/tw_sui/src/transaction/transaction_builder.rs create mode 100644 rust/chains/tw_sui/src/transaction/transaction_data.rs create mode 100644 rust/chains/tw_sui/tests/decode_transaction.rs create mode 100644 rust/tw_any_coin/tests/chains/sui/mod.rs create mode 100644 rust/tw_any_coin/tests/chains/sui/sui_address.rs create mode 100644 rust/tw_any_coin/tests/chains/sui/sui_compile.rs create mode 100644 rust/tw_any_coin/tests/chains/sui/sui_sign.rs create mode 100644 rust/tw_any_coin/tests/chains/sui/test_cases.rs delete mode 100644 rust/tw_aptos/Cargo.toml delete mode 100644 rust/tw_ethereum/Cargo.toml delete mode 100644 rust/tw_internet_computer/Cargo.toml delete mode 100644 rust/tw_ronin/Cargo.toml delete mode 100644 src/Sui/Address.cpp delete mode 100644 src/Sui/Address.h delete mode 100644 src/Sui/Entry.cpp delete mode 100644 src/Sui/Signer.cpp delete mode 100644 src/Sui/Signer.h delete mode 100644 tests/chains/Sui/AddressTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiSigner.kt index 0a37390cdd5..84ab0914391 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiSigner.kt @@ -22,7 +22,7 @@ class TestSuiSigner { } @Test - fun SuiTransactionSigning() { + fun testSuiDirectSigning() { // Successfully broadcasted https://explorer.sui.io/txblock/HkPo6rYPyDY53x1MBszvSZVZyixVN7CHvCJGX381czAh?network=devnet val txBytes = """ AAACAAgQJwAAAAAAAAAgJZ/4B0q0Jcu0ifI24Y4I8D8aeFa998eih3vWT3OLUBUCAgABAQAAAQEDAAAAAAEBANV1rX8Y6UhGKlz2mPVk7zlKdSpx/sYkk6+KBVwBLA1QAQbywsjB2JZN8QGdZhbpcFcZvrq9kx2idVy5SM635olk7AIAAAAAAAAgYEVuxmf1zRBGdoDr+VDtMpIFF12s2Ua7I2ru1XyGF8/Vda1/GOlIRipc9pj1ZO85SnUqcf7GJJOvigVcASwNUAEAAAAAAAAA0AcAAAAAAAAA @@ -37,4 +37,37 @@ class TestSuiSigner { assertEquals(result.unsignedTx, txBytes); assertEquals(result.signature, expectedSignature) } + + @Test + fun testSuiTransfer() { + // Successfully broadcasted: https://suiscan.xyz/mainnet/tx/D4Ay9TdBJjXkGmrZSstZakpEWskEQHaWURP6xWPRXbAm + val txBytes = """ + AAAEAAjoAwAAAAAAAAAIUMMAAAAAAAAAIKcXWr3V7ZLr4605DbNmxqcGR4zfUXzebPmGMAZc2jd6ACBU6A1215DCd/WkTzzpL1PSb1iUiSvzld7mN1mIh2vmsgMCAAIBAAABAQABAQMAAAAAAQIAAQEDAAABAAEDAFToDXbXkMJ39aRPPOkvU9JvWJSJK/OV3uY3WYiHa+ayAWNgILOn3HsRw6pvQZsX+KnBLn95ox0b3S3mcLTt1jAFeHEaBQAAAAAgGGuNnxrqusosgjP3gQ3jBjnhapGNBlcU0yTaupXpa0BU6A1215DCd/WkTzzpL1PSb1iUiSvzld7mN1mIh2vmsu4CAAAAAAAAwMYtAAAAAAAA + """.trimIndent() + val key = + "7e6682f7bf479ef0f627823cffd4e1a940a7af33e5fb39d9e0f631d2ecc5daff".toHexBytesInByteString() + + val paySui = Sui.PaySui.newBuilder() + .addInputCoins(Sui.ObjectRef.newBuilder().apply { + objectId = "0x636020b3a7dc7b11c3aa6f419b17f8a9c12e7f79a31d1bdd2de670b4edd63005" + version = 85619064 + objectDigest = "2eKuWbZSVfpFVfg8FXY9wP6W5AFXnTchSoUdp7obyYZ5" + }) + .addRecipients("0xa7175abdd5ed92ebe3ad390db366c6a706478cdf517cde6cf98630065cda377a") + .addRecipients("0x54e80d76d790c277f5a44f3ce92f53d26f5894892bf395dee6375988876be6b2") + .addAmounts(1000) + .addAmounts(50000) + + val signingInput = Sui.SigningInput.newBuilder() + .setPaySui(paySui) + .setPrivateKey(key) + .setGasBudget(3000000) + .setReferenceGasPrice(750) + .build() + + val result = AnySigner.sign(signingInput, CoinType.SUI, Sui.SigningOutput.parser()) + val expectedSignature = "AEh44B7iGArEHF1wOLAQJMLNgGnaIwn3gKPC92vtDJqITDETAM5z9plaxio1xomt6/cZReQ5FZaQsMC6l7E0BwmF69FEH+T5VPvl3GB3vwCOEZpeJpKXxvcIPQAdKsh2/g==" + assertEquals(result.unsignedTx, txBytes); + assertEquals(result.signature, expectedSignature) + } } diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 231e9ff182b..3d7c0c29603 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1754,6 +1754,7 @@ dependencies = [ "tw_native_injective", "tw_ronin", "tw_solana", + "tw_sui", "tw_thorchain", ] @@ -2005,6 +2006,22 @@ dependencies = [ "tw_proto", ] +[[package]] +name = "tw_sui" +version = "0.1.0" +dependencies = [ + "indexmap", + "move-core-types", + "serde", + "serde_repr", + "tw_coin_entry", + "tw_encoding", + "tw_hash", + "tw_keypair", + "tw_memory", + "tw_proto", +] + [[package]] name = "tw_thorchain" version = "0.1.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index bcd400d3e1f..183f29e20a6 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,30 +1,31 @@ [workspace] members = [ + "chains/tw_aptos", "chains/tw_binance", "chains/tw_cosmos", + "chains/tw_ethereum", + "chains/tw_internet_computer", "chains/tw_greenfield", "chains/tw_native_evmos", "chains/tw_native_injective", + "chains/tw_ronin", "chains/tw_solana", + "chains/tw_sui", "chains/tw_thorchain", "tw_any_coin", - "tw_aptos", "tw_bech32_address", "tw_bitcoin", "tw_coin_entry", "tw_coin_registry", "tw_cosmos_sdk", "tw_encoding", - "tw_ethereum", "tw_evm", "tw_hash", - "tw_internet_computer", "tw_keypair", "tw_memory", "tw_misc", "tw_number", "tw_proto", - "tw_ronin", "tw_utxo", "wallet_core_rs", ] diff --git a/rust/chains/tw_aptos/Cargo.toml b/rust/chains/tw_aptos/Cargo.toml new file mode 100644 index 00000000000..d78b0e3c644 --- /dev/null +++ b/rust/chains/tw_aptos/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "tw_aptos" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde_json = "1.0" +tw_coin_entry = { path = "../../tw_coin_entry" } +tw_encoding = { path = "../../tw_encoding" } +tw_keypair = { path = "../../tw_keypair" } +tw_proto = { path = "../../tw_proto" } +tw_number = { path = "../../tw_number" } +tw_hash = { path = "../../tw_hash" } +tw_memory = { path = "../../tw_memory" } +move-core-types = { git = "https://github.com/move-language/move", rev = "ea70797099baea64f05194a918cebd69ed02b285", features = ["address32"] } +serde = { version = "1.0", features = ["derive"] } +serde_bytes = "0.11.12" + +[dev-dependencies] +tw_coin_entry = { path = "../../tw_coin_entry", features = ["test-utils"] } +tw_encoding = { path = "../../tw_encoding" } +tw_number = { path = "../../tw_number", features = ["helpers"] } diff --git a/rust/tw_aptos/fuzz/.gitignore b/rust/chains/tw_aptos/fuzz/.gitignore similarity index 100% rename from rust/tw_aptos/fuzz/.gitignore rename to rust/chains/tw_aptos/fuzz/.gitignore diff --git a/rust/tw_aptos/fuzz/Cargo.toml b/rust/chains/tw_aptos/fuzz/Cargo.toml similarity index 100% rename from rust/tw_aptos/fuzz/Cargo.toml rename to rust/chains/tw_aptos/fuzz/Cargo.toml diff --git a/rust/tw_aptos/fuzz/fuzz_targets/sign.rs b/rust/chains/tw_aptos/fuzz/fuzz_targets/sign.rs similarity index 100% rename from rust/tw_aptos/fuzz/fuzz_targets/sign.rs rename to rust/chains/tw_aptos/fuzz/fuzz_targets/sign.rs diff --git a/rust/tw_aptos/src/address.rs b/rust/chains/tw_aptos/src/address.rs similarity index 87% rename from rust/tw_aptos/src/address.rs rename to rust/chains/tw_aptos/src/address.rs index 619ae3d4317..520166c9798 100644 --- a/rust/tw_aptos/src/address.rs +++ b/rust/chains/tw_aptos/src/address.rs @@ -6,26 +6,11 @@ use move_core_types::account_address::{AccountAddress, AccountAddressParseError} use std::fmt::{Display, Formatter}; use std::str::FromStr; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::AddressError; use tw_hash::sha3::sha3_256; use tw_keypair::ed25519; use tw_memory::Data; -pub trait AptosAddress: FromStr + Into
{ - /// Tries to parse an address from the string representation. - /// Returns `Ok(None)` if the given `s` string is empty. - #[inline] - fn from_str_optional(s: &str) -> AddressResult> { - if s.is_empty() { - return Ok(None); - } - - Self::from_str(s).map(Some) - } -} - -impl AptosAddress for Address {} - #[repr(u8)] pub enum Scheme { Ed25519 = 0, @@ -38,8 +23,8 @@ pub struct Address { impl Address { pub const LENGTH: usize = AccountAddress::LENGTH; - /// Initializes an address with a `ed25519` public key. + /// Initializes an address with a `ed25519` public key. pub fn with_ed25519_pubkey( pubkey: &ed25519::sha512::PublicKey, ) -> Result { diff --git a/rust/tw_aptos/src/aptos_move_packages.rs b/rust/chains/tw_aptos/src/aptos_move_packages.rs similarity index 100% rename from rust/tw_aptos/src/aptos_move_packages.rs rename to rust/chains/tw_aptos/src/aptos_move_packages.rs diff --git a/rust/tw_aptos/src/bcs_encoding.rs b/rust/chains/tw_aptos/src/bcs_encoding.rs similarity index 100% rename from rust/tw_aptos/src/bcs_encoding.rs rename to rust/chains/tw_aptos/src/bcs_encoding.rs diff --git a/rust/tw_aptos/src/compiler.rs b/rust/chains/tw_aptos/src/compiler.rs similarity index 100% rename from rust/tw_aptos/src/compiler.rs rename to rust/chains/tw_aptos/src/compiler.rs diff --git a/rust/tw_aptos/src/constants.rs b/rust/chains/tw_aptos/src/constants.rs similarity index 100% rename from rust/tw_aptos/src/constants.rs rename to rust/chains/tw_aptos/src/constants.rs diff --git a/rust/tw_aptos/src/entry.rs b/rust/chains/tw_aptos/src/entry.rs similarity index 100% rename from rust/tw_aptos/src/entry.rs rename to rust/chains/tw_aptos/src/entry.rs diff --git a/rust/tw_aptos/src/lib.rs b/rust/chains/tw_aptos/src/lib.rs similarity index 100% rename from rust/tw_aptos/src/lib.rs rename to rust/chains/tw_aptos/src/lib.rs diff --git a/rust/tw_aptos/src/liquid_staking.rs b/rust/chains/tw_aptos/src/liquid_staking.rs similarity index 100% rename from rust/tw_aptos/src/liquid_staking.rs rename to rust/chains/tw_aptos/src/liquid_staking.rs diff --git a/rust/tw_aptos/src/nft.rs b/rust/chains/tw_aptos/src/nft.rs similarity index 100% rename from rust/tw_aptos/src/nft.rs rename to rust/chains/tw_aptos/src/nft.rs diff --git a/rust/tw_aptos/src/serde_helper/mod.rs b/rust/chains/tw_aptos/src/serde_helper/mod.rs similarity index 100% rename from rust/tw_aptos/src/serde_helper/mod.rs rename to rust/chains/tw_aptos/src/serde_helper/mod.rs diff --git a/rust/tw_aptos/src/serde_helper/vec_bytes.rs b/rust/chains/tw_aptos/src/serde_helper/vec_bytes.rs similarity index 100% rename from rust/tw_aptos/src/serde_helper/vec_bytes.rs rename to rust/chains/tw_aptos/src/serde_helper/vec_bytes.rs diff --git a/rust/tw_aptos/src/signer.rs b/rust/chains/tw_aptos/src/signer.rs similarity index 100% rename from rust/tw_aptos/src/signer.rs rename to rust/chains/tw_aptos/src/signer.rs diff --git a/rust/tw_aptos/src/transaction.rs b/rust/chains/tw_aptos/src/transaction.rs similarity index 100% rename from rust/tw_aptos/src/transaction.rs rename to rust/chains/tw_aptos/src/transaction.rs diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/chains/tw_aptos/src/transaction_builder.rs similarity index 100% rename from rust/tw_aptos/src/transaction_builder.rs rename to rust/chains/tw_aptos/src/transaction_builder.rs diff --git a/rust/tw_aptos/src/transaction_payload.rs b/rust/chains/tw_aptos/src/transaction_payload.rs similarity index 100% rename from rust/tw_aptos/src/transaction_payload.rs rename to rust/chains/tw_aptos/src/transaction_payload.rs diff --git a/rust/tw_aptos/tests/signer.rs b/rust/chains/tw_aptos/tests/signer.rs similarity index 100% rename from rust/tw_aptos/tests/signer.rs rename to rust/chains/tw_aptos/tests/signer.rs diff --git a/rust/chains/tw_binance/src/transaction/message/htlt_order.rs b/rust/chains/tw_binance/src/transaction/message/htlt_order.rs index f677a0dfc7e..e55e3e4b3e5 100644 --- a/rust/chains/tw_binance/src/transaction/message/htlt_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/htlt_order.rs @@ -20,7 +20,7 @@ pub struct HTLTOrder { pub expected_income: String, pub from: BinanceAddress, pub height_span: i64, - #[serde(serialize_with = "as_hex")] + #[serde(with = "as_hex")] pub random_number_hash: Data, pub recipient_other_chain: String, pub sender_other_chain: String, @@ -84,7 +84,7 @@ impl TWBinanceProto for HTLTOrder { pub struct DepositHTLTOrder { pub amount: Vec, pub from: BinanceAddress, - #[serde(serialize_with = "as_hex")] + #[serde(with = "as_hex")] pub swap_id: Data, } @@ -127,9 +127,9 @@ impl TWBinanceProto for DepositHTLTOrder { #[derive(Deserialize, Serialize)] pub struct ClaimHTLTOrder { pub from: BinanceAddress, - #[serde(serialize_with = "as_hex")] + #[serde(with = "as_hex")] pub random_number: Data, - #[serde(serialize_with = "as_hex")] + #[serde(with = "as_hex")] pub swap_id: Data, } @@ -171,7 +171,7 @@ impl TWBinanceProto for ClaimHTLTOrder { #[derive(Deserialize, Serialize)] pub struct RefundHTLTOrder { pub from: BinanceAddress, - #[serde(serialize_with = "as_hex")] + #[serde(with = "as_hex")] pub swap_id: Data, } diff --git a/rust/chains/tw_ethereum/Cargo.toml b/rust/chains/tw_ethereum/Cargo.toml new file mode 100644 index 00000000000..6a3cbe0ebd8 --- /dev/null +++ b/rust/chains/tw_ethereum/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "tw_ethereum" +version = "0.1.0" +edition = "2021" + +[dependencies] +tw_coin_entry = { path = "../../tw_coin_entry" } +tw_evm = { path = "../../tw_evm" } +tw_keypair = { path = "../../tw_keypair" } +tw_proto = { path = "../../tw_proto" } + +[dev-dependencies] +tw_coin_entry = { path = "../../tw_coin_entry", features = ["test-utils"] } +tw_encoding = { path = "../../tw_encoding" } +tw_number = { path = "../../tw_number", features = ["helpers"] } diff --git a/rust/tw_ethereum/src/entry.rs b/rust/chains/tw_ethereum/src/entry.rs similarity index 100% rename from rust/tw_ethereum/src/entry.rs rename to rust/chains/tw_ethereum/src/entry.rs diff --git a/rust/tw_ethereum/src/lib.rs b/rust/chains/tw_ethereum/src/lib.rs similarity index 100% rename from rust/tw_ethereum/src/lib.rs rename to rust/chains/tw_ethereum/src/lib.rs diff --git a/rust/tw_ethereum/tests/compiler.rs b/rust/chains/tw_ethereum/tests/compiler.rs similarity index 100% rename from rust/tw_ethereum/tests/compiler.rs rename to rust/chains/tw_ethereum/tests/compiler.rs diff --git a/rust/tw_ethereum/tests/signer.rs b/rust/chains/tw_ethereum/tests/signer.rs similarity index 100% rename from rust/tw_ethereum/tests/signer.rs rename to rust/chains/tw_ethereum/tests/signer.rs diff --git a/rust/chains/tw_internet_computer/Cargo.toml b/rust/chains/tw_internet_computer/Cargo.toml new file mode 100644 index 00000000000..fda55e71f69 --- /dev/null +++ b/rust/chains/tw_internet_computer/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "tw_internet_computer" +version = "0.1.0" +edition = "2021" + +[dependencies] +quick-protobuf = "0.8.1" +serde = { version = "1.0", features = ["derive"] } +tw_coin_entry = { path = "../../tw_coin_entry" } +tw_encoding = { path = "../../tw_encoding" } +tw_hash = { path = "../../tw_hash" } +tw_keypair = { path = "../../tw_keypair" } +tw_memory = { path = "../../tw_memory" } +tw_proto = { path = "../../tw_proto" } + +[build-dependencies] +pb-rs = "0.10.0" diff --git a/rust/tw_internet_computer/build.rs b/rust/chains/tw_internet_computer/build.rs similarity index 100% rename from rust/tw_internet_computer/build.rs rename to rust/chains/tw_internet_computer/build.rs diff --git a/rust/tw_internet_computer/fuzz/.gitignore b/rust/chains/tw_internet_computer/fuzz/.gitignore similarity index 100% rename from rust/tw_internet_computer/fuzz/.gitignore rename to rust/chains/tw_internet_computer/fuzz/.gitignore diff --git a/rust/tw_internet_computer/fuzz/Cargo.lock b/rust/chains/tw_internet_computer/fuzz/Cargo.lock similarity index 100% rename from rust/tw_internet_computer/fuzz/Cargo.lock rename to rust/chains/tw_internet_computer/fuzz/Cargo.lock diff --git a/rust/tw_internet_computer/fuzz/Cargo.toml b/rust/chains/tw_internet_computer/fuzz/Cargo.toml similarity index 100% rename from rust/tw_internet_computer/fuzz/Cargo.toml rename to rust/chains/tw_internet_computer/fuzz/Cargo.toml diff --git a/rust/tw_internet_computer/fuzz/fuzz_targets/tw_internet_computer_transfer.rs b/rust/chains/tw_internet_computer/fuzz/fuzz_targets/tw_internet_computer_transfer.rs similarity index 100% rename from rust/tw_internet_computer/fuzz/fuzz_targets/tw_internet_computer_transfer.rs rename to rust/chains/tw_internet_computer/fuzz/fuzz_targets/tw_internet_computer_transfer.rs diff --git a/rust/tw_internet_computer/src/address.rs b/rust/chains/tw_internet_computer/src/address.rs similarity index 100% rename from rust/tw_internet_computer/src/address.rs rename to rust/chains/tw_internet_computer/src/address.rs diff --git a/rust/tw_internet_computer/src/context.rs b/rust/chains/tw_internet_computer/src/context.rs similarity index 100% rename from rust/tw_internet_computer/src/context.rs rename to rust/chains/tw_internet_computer/src/context.rs diff --git a/rust/tw_internet_computer/src/entry.rs b/rust/chains/tw_internet_computer/src/entry.rs similarity index 100% rename from rust/tw_internet_computer/src/entry.rs rename to rust/chains/tw_internet_computer/src/entry.rs diff --git a/rust/tw_internet_computer/src/lib.rs b/rust/chains/tw_internet_computer/src/lib.rs similarity index 100% rename from rust/tw_internet_computer/src/lib.rs rename to rust/chains/tw_internet_computer/src/lib.rs diff --git a/rust/tw_internet_computer/src/protocol/envelope.rs b/rust/chains/tw_internet_computer/src/protocol/envelope.rs similarity index 100% rename from rust/tw_internet_computer/src/protocol/envelope.rs rename to rust/chains/tw_internet_computer/src/protocol/envelope.rs diff --git a/rust/tw_internet_computer/src/protocol/identity.rs b/rust/chains/tw_internet_computer/src/protocol/identity.rs similarity index 100% rename from rust/tw_internet_computer/src/protocol/identity.rs rename to rust/chains/tw_internet_computer/src/protocol/identity.rs diff --git a/rust/tw_internet_computer/src/protocol/mod.rs b/rust/chains/tw_internet_computer/src/protocol/mod.rs similarity index 100% rename from rust/tw_internet_computer/src/protocol/mod.rs rename to rust/chains/tw_internet_computer/src/protocol/mod.rs diff --git a/rust/tw_internet_computer/src/protocol/principal.rs b/rust/chains/tw_internet_computer/src/protocol/principal.rs similarity index 100% rename from rust/tw_internet_computer/src/protocol/principal.rs rename to rust/chains/tw_internet_computer/src/protocol/principal.rs diff --git a/rust/tw_internet_computer/src/protocol/request_id.rs b/rust/chains/tw_internet_computer/src/protocol/request_id.rs similarity index 100% rename from rust/tw_internet_computer/src/protocol/request_id.rs rename to rust/chains/tw_internet_computer/src/protocol/request_id.rs diff --git a/rust/tw_internet_computer/src/protocol/rosetta.rs b/rust/chains/tw_internet_computer/src/protocol/rosetta.rs similarity index 100% rename from rust/tw_internet_computer/src/protocol/rosetta.rs rename to rust/chains/tw_internet_computer/src/protocol/rosetta.rs diff --git a/rust/tw_internet_computer/src/signer.rs b/rust/chains/tw_internet_computer/src/signer.rs similarity index 100% rename from rust/tw_internet_computer/src/signer.rs rename to rust/chains/tw_internet_computer/src/signer.rs diff --git a/rust/tw_internet_computer/src/transactions/mod.rs b/rust/chains/tw_internet_computer/src/transactions/mod.rs similarity index 100% rename from rust/tw_internet_computer/src/transactions/mod.rs rename to rust/chains/tw_internet_computer/src/transactions/mod.rs diff --git a/rust/tw_internet_computer/src/transactions/proto/ledger.proto b/rust/chains/tw_internet_computer/src/transactions/proto/ledger.proto similarity index 100% rename from rust/tw_internet_computer/src/transactions/proto/ledger.proto rename to rust/chains/tw_internet_computer/src/transactions/proto/ledger.proto diff --git a/rust/tw_internet_computer/src/transactions/proto/types.proto b/rust/chains/tw_internet_computer/src/transactions/proto/types.proto similarity index 100% rename from rust/tw_internet_computer/src/transactions/proto/types.proto rename to rust/chains/tw_internet_computer/src/transactions/proto/types.proto diff --git a/rust/tw_internet_computer/src/transactions/transfer.rs b/rust/chains/tw_internet_computer/src/transactions/transfer.rs similarity index 100% rename from rust/tw_internet_computer/src/transactions/transfer.rs rename to rust/chains/tw_internet_computer/src/transactions/transfer.rs diff --git a/rust/chains/tw_ronin/Cargo.toml b/rust/chains/tw_ronin/Cargo.toml new file mode 100644 index 00000000000..fadcf48ebab --- /dev/null +++ b/rust/chains/tw_ronin/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "tw_ronin" +version = "0.1.0" +edition = "2021" + +[dependencies] +tw_coin_entry = { path = "../../tw_coin_entry" } +tw_evm = { path = "../../tw_evm" } +tw_keypair = { path = "../../tw_keypair" } +tw_memory = { path = "../../tw_memory" } +tw_proto = { path = "../../tw_proto" } + +[dev-dependencies] +tw_coin_entry = { path = "../../tw_coin_entry", features = ["test-utils"] } +tw_encoding = { path = "../../tw_encoding" } +tw_number = { path = "../../tw_number", features = ["helpers"] } diff --git a/rust/tw_ronin/src/address.rs b/rust/chains/tw_ronin/src/address.rs similarity index 100% rename from rust/tw_ronin/src/address.rs rename to rust/chains/tw_ronin/src/address.rs diff --git a/rust/tw_ronin/src/entry.rs b/rust/chains/tw_ronin/src/entry.rs similarity index 100% rename from rust/tw_ronin/src/entry.rs rename to rust/chains/tw_ronin/src/entry.rs diff --git a/rust/tw_ronin/src/lib.rs b/rust/chains/tw_ronin/src/lib.rs similarity index 100% rename from rust/tw_ronin/src/lib.rs rename to rust/chains/tw_ronin/src/lib.rs diff --git a/rust/tw_ronin/src/ronin_context.rs b/rust/chains/tw_ronin/src/ronin_context.rs similarity index 100% rename from rust/tw_ronin/src/ronin_context.rs rename to rust/chains/tw_ronin/src/ronin_context.rs diff --git a/rust/tw_ronin/tests/address.rs b/rust/chains/tw_ronin/tests/address.rs similarity index 100% rename from rust/tw_ronin/tests/address.rs rename to rust/chains/tw_ronin/tests/address.rs diff --git a/rust/tw_ronin/tests/compiler.rs b/rust/chains/tw_ronin/tests/compiler.rs similarity index 100% rename from rust/tw_ronin/tests/compiler.rs rename to rust/chains/tw_ronin/tests/compiler.rs diff --git a/rust/tw_ronin/tests/rlp.rs b/rust/chains/tw_ronin/tests/rlp.rs similarity index 100% rename from rust/tw_ronin/tests/rlp.rs rename to rust/chains/tw_ronin/tests/rlp.rs diff --git a/rust/tw_ronin/tests/signer.rs b/rust/chains/tw_ronin/tests/signer.rs similarity index 100% rename from rust/tw_ronin/tests/signer.rs rename to rust/chains/tw_ronin/tests/signer.rs diff --git a/rust/chains/tw_sui/Cargo.toml b/rust/chains/tw_sui/Cargo.toml new file mode 100644 index 00000000000..1aa3d7cabb3 --- /dev/null +++ b/rust/chains/tw_sui/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "tw_sui" +version = "0.1.0" +edition = "2021" + +[dependencies] +indexmap = "2.0" +move-core-types = { git = "https://github.com/move-language/move", rev = "ea70797099baea64f05194a918cebd69ed02b285", features = ["address32"] } +serde = { version = "1.0", features = ["derive"] } +serde_repr = "0.1" +tw_coin_entry = { path = "../../tw_coin_entry" } +tw_encoding = { path = "../../tw_encoding" } +tw_hash = { path = "../../tw_hash" } +tw_keypair = { path = "../../tw_keypair" } +tw_memory = { path = "../../tw_memory" } +tw_proto = { path = "../../tw_proto" } diff --git a/rust/chains/tw_sui/fuzz/.gitignore b/rust/chains/tw_sui/fuzz/.gitignore new file mode 100644 index 00000000000..5c404b9583f --- /dev/null +++ b/rust/chains/tw_sui/fuzz/.gitignore @@ -0,0 +1,5 @@ +target +corpus +artifacts +coverage +Cargo.lock diff --git a/rust/chains/tw_sui/fuzz/Cargo.toml b/rust/chains/tw_sui/fuzz/Cargo.toml new file mode 100644 index 00000000000..32ce151dfaa --- /dev/null +++ b/rust/chains/tw_sui/fuzz/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "tw_sui-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" +tw_any_coin = { path = "../../../tw_any_coin", features = ["test-utils"] } +tw_coin_registry = { path = "../../../tw_coin_registry" } +tw_proto = { path = "../../../tw_proto", features = ["fuzz"] } + +[dependencies.tw_sui] +path = ".." + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[profile.release] +debug = 1 + +[[bin]] +name = "sign" +path = "fuzz_targets/sign.rs" +test = false +doc = false diff --git a/rust/chains/tw_sui/fuzz/fuzz_targets/sign.rs b/rust/chains/tw_sui/fuzz/fuzz_targets/sign.rs new file mode 100644 index 00000000000..bf0158d275f --- /dev/null +++ b/rust/chains/tw_sui/fuzz/fuzz_targets/sign.rs @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#![no_main] + +use libfuzzer_sys::fuzz_target; +use tw_any_coin::test_utils::sign_utils::AnySignerHelper; +use tw_coin_registry::coin_type::CoinType; +use tw_proto::Sui::Proto; + +fuzz_target!(|input: Proto::SigningInput<'_>| { + let mut signer = AnySignerHelper::::default(); + let _ = signer.sign(CoinType::Sui, input); +}); diff --git a/rust/chains/tw_sui/src/address.rs b/rust/chains/tw_sui/src/address.rs new file mode 100644 index 00000000000..76c359561ec --- /dev/null +++ b/rust/chains/tw_sui/src/address.rs @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use move_core_types::account_address::AccountAddress; +use serde::{Deserialize, Serialize}; +use std::fmt; +use std::str::FromStr; +use tw_coin_entry::coin_entry::CoinAddress; +use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_encoding::hex; +use tw_hash::blake2::blake2_b; +use tw_keypair::ed25519; +use tw_memory::Data; + +#[repr(u8)] +pub enum Scheme { + Ed25519 = 0, +} + +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct SuiAddress(AccountAddress); + +impl SuiAddress { + pub const LENGTH: usize = AccountAddress::LENGTH; + + /// Initializes an address with a `ed25519` public key. + pub fn with_ed25519_pubkey(pubkey: &ed25519::sha512::PublicKey) -> AddressResult { + const CAPACITY: usize = ed25519::sha512::PublicKey::LEN + 1; + + let mut to_hash = Vec::with_capacity(CAPACITY); + to_hash.push(Scheme::Ed25519 as u8); + to_hash.extend_from_slice(pubkey.as_slice()); + let hashed = + blake2_b(to_hash.as_slice(), SuiAddress::LENGTH).map_err(|_| AddressError::Internal)?; + + AccountAddress::from_bytes(hashed) + .map(SuiAddress) + .map_err(|_| AddressError::Internal) + } + + pub fn into_inner(self) -> AccountAddress { + self.0 + } +} + +impl FromStr for SuiAddress { + type Err = AddressError; + + fn from_str(s: &str) -> Result { + let bytes = hex::decode(s).map_err(|_| AddressError::FromHexError)?; + AccountAddress::from_bytes(bytes) + .map(SuiAddress) + .map_err(|_| AddressError::InvalidInput) + } +} + +impl fmt::Display for SuiAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0.to_hex_literal()) + } +} + +impl CoinAddress for SuiAddress { + #[inline] + fn data(&self) -> Data { + self.0.to_vec() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tw_keypair::ed25519::sha512::PrivateKey; + + #[test] + fn test_from_public_key() { + let private = PrivateKey::try_from( + "088baa019f081d6eab8dff5c447f9ce2f83c1babf3d03686299eaf6a1e89156e", + ) + .unwrap(); + let public = private.public(); + let addr = SuiAddress::with_ed25519_pubkey(&public).unwrap(); + assert_eq!( + addr.to_string(), + "0x259ff8074ab425cbb489f236e18e08f03f1a7856bdf7c7a2877bd64f738b5015" + ); + } +} diff --git a/rust/chains/tw_sui/src/compiler.rs b/rust/chains/tw_sui/src/compiler.rs new file mode 100644 index 00000000000..adff5bd13d6 --- /dev/null +++ b/rust/chains/tw_sui/src/compiler.rs @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::modules::tx_builder::{TWTransaction, TWTransactionBuilder}; +use crate::modules::tx_signer::{TransactionPreimage, TxSigner}; +use crate::signature::SuiSignatureInfo; +use std::borrow::Cow; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::common::compile_input::SingleSignaturePubkey; +use tw_coin_entry::error::SigningResult; +use tw_coin_entry::signing_output_error; +use tw_encoding::base64; +use tw_keypair::ed25519; +use tw_proto::Sui::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct SuiCompiler; + +impl SuiCompiler { + #[inline] + pub fn preimage_hashes( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> CompilerProto::PreSigningOutput<'static> { + Self::preimage_hashes_impl(coin, input) + .unwrap_or_else(|e| signing_output_error!(CompilerProto::PreSigningOutput, e)) + } + + fn preimage_hashes_impl( + _coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> SigningResult> { + let builder = TWTransactionBuilder::new(input); + let tx_to_sign = builder.build()?; + + let TransactionPreimage { + tx_data_to_sign, + tx_hash_to_sign, + .. + } = match tx_to_sign { + TWTransaction::Transaction(tx) => TxSigner::preimage(&tx)?, + TWTransaction::SignDirect(tx_data) => TxSigner::preimage_direct(tx_data)?, + }; + + Ok(CompilerProto::PreSigningOutput { + data: Cow::from(tx_data_to_sign), + data_hash: Cow::from(tx_hash_to_sign.to_vec()), + ..CompilerProto::PreSigningOutput::default() + }) + } + + #[inline] + pub fn compile( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Proto::SigningOutput<'static> { + Self::compile_impl(coin, input, signatures, public_keys) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn compile_impl( + _coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> SigningResult> { + let builder = TWTransactionBuilder::new(input); + let tx_to_sign = builder.build()?; + + let TransactionPreimage { + unsigned_tx_data, .. + } = match tx_to_sign { + TWTransaction::Transaction(tx) => TxSigner::preimage(&tx), + TWTransaction::SignDirect(tx_data) => TxSigner::preimage_direct(tx_data), + }?; + + let SingleSignaturePubkey { + signature: raw_signature, + public_key: public_key_bytes, + } = SingleSignaturePubkey::from_sign_pubkey_list(signatures, public_keys)?; + + let signature = ed25519::Signature::try_from(raw_signature.as_slice())?; + let public_key = ed25519::sha512::PublicKey::try_from(public_key_bytes.as_slice())?; + + let signature_info = SuiSignatureInfo::ed25519(&signature, &public_key); + + let is_url = false; + let unsigned_tx = base64::encode(&unsigned_tx_data, is_url); + Ok(Proto::SigningOutput { + unsigned_tx: Cow::from(unsigned_tx), + signature: Cow::from(signature_info.to_base64()), + ..Proto::SigningOutput::default() + }) + } +} diff --git a/rust/chains/tw_sui/src/constants.rs b/rust/chains/tw_sui/src/constants.rs new file mode 100644 index 00000000000..35c5d525f3e --- /dev/null +++ b/rust/chains/tw_sui/src/constants.rs @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::transaction::sui_types::{ObjectID, SequenceNumber}; +use move_core_types::account_address::AccountAddress; +use move_core_types::ident_str; +use move_core_types::identifier::IdentStr; + +pub const OBJECT_START_VERSION: SequenceNumber = SequenceNumber(1); + +/// 0x5: hardcoded object ID for the singleton sui system state object. +pub const SUI_SYSTEM_STATE_ADDRESS: AccountAddress = address_from_single_byte(5); +pub const SUI_SYSTEM_STATE_OBJECT_ID: ObjectID = ObjectID(SUI_SYSTEM_STATE_ADDRESS); +pub const SUI_SYSTEM_STATE_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION; + +/// 0x3-- account address where sui system modules are stored +/// Same as the ObjectID +pub const SUI_SYSTEM_ADDRESS: AccountAddress = address_from_single_byte(3); +pub const SUI_SYSTEM_PACKAGE_ID: ObjectID = ObjectID(SUI_SYSTEM_ADDRESS); + +pub const SUI_SYSTEM_MODULE_NAME: &IdentStr = ident_str!("sui_system"); +pub const ADD_STAKE_MUL_COIN_FUN_NAME: &IdentStr = ident_str!("request_add_stake_mul_coin"); +pub const WITHDRAW_STAKE_FUN_NAME: &IdentStr = ident_str!("request_withdraw_stake"); + +const fn address_from_single_byte(b: u8) -> AccountAddress { + let mut addr = [0u8; AccountAddress::LENGTH]; + addr[AccountAddress::LENGTH - 1] = b; + AccountAddress::new(addr) +} diff --git a/rust/chains/tw_sui/src/entry.rs b/rust/chains/tw_sui/src/entry.rs new file mode 100644 index 00000000000..70682041a43 --- /dev/null +++ b/rust/chains/tw_sui/src/entry.rs @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::address::SuiAddress; +use crate::compiler::SuiCompiler; +use crate::signer::SuiSigner; +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::derivation::Derivation; +use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::modules::json_signer::NoJsonSigner; +use tw_coin_entry::modules::message_signer::NoMessageSigner; +use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; +use tw_coin_entry::modules::wallet_connector::NoWalletConnector; +use tw_coin_entry::prefix::NoPrefix; +use tw_keypair::tw::PublicKey; +use tw_proto::Sui::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct SuiEntry; + +impl CoinEntry for SuiEntry { + type AddressPrefix = NoPrefix; + type Address = SuiAddress; + type SigningInput<'a> = Proto::SigningInput<'a>; + type SigningOutput = Proto::SigningOutput<'static>; + type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + + // Optional modules: + type JsonSigner = NoJsonSigner; + type PlanBuilder = NoPlanBuilder; + type MessageSigner = NoMessageSigner; + type WalletConnector = NoWalletConnector; + type TransactionDecoder = NoTransactionDecoder; + + #[inline] + fn parse_address( + &self, + _coin: &dyn CoinContext, + address: &str, + _prefix: Option, + ) -> AddressResult { + SuiAddress::from_str(address) + } + + #[inline] + fn parse_address_unchecked( + &self, + _coin: &dyn CoinContext, + address: &str, + ) -> AddressResult { + SuiAddress::from_str(address) + } + + #[inline] + fn derive_address( + &self, + _coin: &dyn CoinContext, + public_key: PublicKey, + _derivation: Derivation, + _prefix: Option, + ) -> AddressResult { + let ed25519_public = public_key + .to_ed25519() + .ok_or(AddressError::PublicKeyTypeMismatch)?; + SuiAddress::with_ed25519_pubkey(ed25519_public) + } + + #[inline] + fn sign(&self, coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput { + SuiSigner::sign(coin, input) + } + + #[inline] + fn preimage_hashes( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + ) -> Self::PreSigningOutput { + SuiCompiler::preimage_hashes(coin, input) + } + + #[inline] + fn compile( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Self::SigningOutput { + SuiCompiler::compile(coin, input, signatures, public_keys) + } +} diff --git a/rust/chains/tw_sui/src/lib.rs b/rust/chains/tw_sui/src/lib.rs new file mode 100644 index 00000000000..9a2411b8058 --- /dev/null +++ b/rust/chains/tw_sui/src/lib.rs @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +pub mod address; +pub mod compiler; +pub mod constants; +pub mod entry; +pub mod modules; +pub mod signature; +pub mod signer; +pub mod transaction; diff --git a/rust/chains/tw_sui/src/modules/mod.rs b/rust/chains/tw_sui/src/modules/mod.rs new file mode 100644 index 00000000000..7398efd0e9b --- /dev/null +++ b/rust/chains/tw_sui/src/modules/mod.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +pub mod tx_builder; +pub mod tx_signer; diff --git a/rust/chains/tw_sui/src/modules/tx_builder.rs b/rust/chains/tw_sui/src/modules/tx_builder.rs new file mode 100644 index 00000000000..58ec6dff986 --- /dev/null +++ b/rust/chains/tw_sui/src/modules/tx_builder.rs @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::address::SuiAddress; +use crate::transaction::sui_types::{ObjectDigest, ObjectID, ObjectRef, SequenceNumber}; +use crate::transaction::transaction_builder::TransactionBuilder; +use crate::transaction::transaction_data::TransactionData; +use std::borrow::Cow; +use std::str::FromStr; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_encoding::base64; +use tw_keypair::ed25519; +use tw_keypair::traits::KeyPairTrait; +use tw_memory::Data; +use tw_proto::Sui::Proto; +use tw_proto::Sui::Proto::mod_SigningInput::OneOftransaction_payload as TransactionType; + +pub enum TWTransaction { + Transaction(TransactionData), + SignDirect(Data), +} + +pub struct TWTransactionBuilder<'a> { + input: Proto::SigningInput<'a>, +} + +impl<'a> TWTransactionBuilder<'a> { + pub fn new(input: Proto::SigningInput<'a>) -> Self { + TWTransactionBuilder { input } + } + + pub fn signer_key(&self) -> SigningResult { + ed25519::sha512::KeyPair::try_from(self.input.private_key.as_ref()) + .map_err(SigningError::from) + } + + pub fn build(self) -> SigningResult { + let tx_data = match self.input.transaction_payload { + TransactionType::sign_direct_message(ref direct) => { + let raw_data = self.sign_direct_from_proto(direct)?; + return Ok(TWTransaction::SignDirect(raw_data)); + }, + TransactionType::pay_sui(ref pay_sui) => self.pay_sui_from_proto(pay_sui), + TransactionType::pay_all_sui(ref pay_all_sui) => { + self.pay_all_sui_from_proto(pay_all_sui) + }, + TransactionType::pay(ref pay) => self.pay_from_proto(pay), + TransactionType::request_add_stake(ref stake) => self.stake_from_proto(stake), + TransactionType::request_withdraw_stake(ref withdraw) => { + self.withdraw_from_proto(withdraw) + }, + TransactionType::transfer_object(ref transfer_obj) => { + self.transfer_object_from_proto(transfer_obj) + }, + TransactionType::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + }?; + Ok(TWTransaction::Transaction(tx_data)) + } + + fn sign_direct_from_proto(&self, sign_direct: &Proto::SignDirect<'_>) -> SigningResult { + let url = false; + base64::decode(&sign_direct.unsigned_tx_msg, url) + .map_err(|_| SigningError(SigningErrorType::Error_input_parse)) + } + + fn pay_sui_from_proto(&self, pay_sui: &Proto::PaySui<'_>) -> SigningResult { + let signer = self.signer_address()?; + let input_coins = Self::build_coins(&pay_sui.input_coins)?; + let recipients = Self::parse_addresses(&pay_sui.recipients)?; + + TransactionBuilder::pay_sui( + signer, + input_coins, + recipients, + pay_sui.amounts.clone(), + self.input.gas_budget, + self.input.reference_gas_price, + ) + } + + fn pay_all_sui_from_proto( + &self, + pay_all_sui: &Proto::PayAllSui<'_>, + ) -> SigningResult { + let signer = self.signer_address()?; + let input_coins = Self::build_coins(&pay_all_sui.input_coins)?; + let recipient = SuiAddress::from_str(&pay_all_sui.recipient)?; + + TransactionBuilder::pay_all_sui( + signer, + input_coins, + recipient, + self.input.gas_budget, + self.input.reference_gas_price, + ) + } + + fn pay_from_proto(&self, pay: &Proto::Pay<'_>) -> SigningResult { + let signer = self.signer_address()?; + let input_coins = Self::build_coins(&pay.input_coins)?; + let recipients = Self::parse_addresses(&pay.recipients)?; + let gas = Self::require_coin(&pay.gas)?; + + TransactionBuilder::pay( + signer, + input_coins, + recipients, + pay.amounts.clone(), + gas, + self.input.gas_budget, + self.input.reference_gas_price, + ) + } + + fn stake_from_proto( + &self, + stake: &Proto::RequestAddStake<'_>, + ) -> SigningResult { + let signer = self.signer_address()?; + + let input_coins = Self::build_coins(&stake.coins)?; + let amount = stake.amount.as_ref().map(|a| a.amount); + let validator = SuiAddress::from_str(stake.validator.as_ref())?; + let gas = Self::require_coin(&stake.gas)?; + + TransactionBuilder::request_add_stake( + signer, + input_coins, + amount, + validator, + gas, + self.input.gas_budget, + self.input.reference_gas_price, + ) + } + + fn withdraw_from_proto( + &self, + withdraw: &Proto::RequestWithdrawStake<'_>, + ) -> SigningResult { + let signer = self.signer_address()?; + + let staked_sui = Self::require_coin(&withdraw.staked_sui)?; + let gas = Self::require_coin(&withdraw.gas)?; + + TransactionBuilder::request_withdraw_stake( + signer, + staked_sui, + gas, + self.input.gas_budget, + self.input.reference_gas_price, + ) + } + + fn transfer_object_from_proto( + &self, + transfer_obj: &Proto::TransferObject<'_>, + ) -> SigningResult { + let signer = self.signer_address()?; + + let recipient = SuiAddress::from_str(&transfer_obj.recipient)?; + let object = Self::require_coin(&transfer_obj.object)?; + let gas = Self::require_coin(&transfer_obj.gas)?; + + TransactionBuilder::transfer_object( + signer, + object, + recipient, + gas, + self.input.gas_budget, + self.input.reference_gas_price, + ) + } + + fn signer_address(&self) -> SigningResult { + if self.input.private_key.is_empty() { + SuiAddress::from_str(&self.input.signer).map_err(SigningError::from) + } else { + let keypair = self.signer_key()?; + SuiAddress::with_ed25519_pubkey(keypair.public()).map_err(SigningError::from) + } + } + + fn build_coins(coins: &[Proto::ObjectRef]) -> SigningResult> { + coins.iter().map(Self::build_coin).collect() + } + + fn require_coin(maybe_coin: &Option) -> SigningResult { + let coin = maybe_coin + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + Self::build_coin(coin) + } + + fn build_coin(coin: &Proto::ObjectRef) -> SigningResult { + let object_id = ObjectID::from_str(coin.object_id.as_ref())?; + let version = SequenceNumber(coin.version); + let object_digest = ObjectDigest::from_str(coin.object_digest.as_ref())?; + + Ok((object_id, version, object_digest)) + } + + fn parse_addresses(addresses: &[Cow<'_, str>]) -> SigningResult> { + let mut res = Vec::with_capacity(addresses.len()); + for addr in addresses { + res.push(SuiAddress::from_str(addr.as_ref())?); + } + Ok(res) + } +} diff --git a/rust/chains/tw_sui/src/modules/tx_signer.rs b/rust/chains/tw_sui/src/modules/tx_signer.rs new file mode 100644 index 00000000000..e7364feac56 --- /dev/null +++ b/rust/chains/tw_sui/src/modules/tx_signer.rs @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::address::SuiAddress; +use crate::signature::SuiSignatureInfo; +use crate::transaction::transaction_data::TransactionData; +use serde::Serialize; +use serde_repr::Serialize_repr; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_encoding::bcs; +use tw_hash::blake2::blake2_b; +use tw_hash::H256; +use tw_keypair::ed25519; +use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; +use tw_memory::Data; + +/// This enums specifies the intent scope. +#[derive(Serialize_repr)] +#[repr(u8)] +pub enum IntentScope { + /// Used for a user signature on a transaction data. + TransactionData = 0, +} + +/// The version here is to distinguish between signing different versions of the struct +/// or enum. Serialized output between two different versions of the same struct/enum +/// might accidentally (or maliciously on purpose) match. +#[derive(Serialize_repr)] +#[repr(u8)] +pub enum IntentVersion { + V0 = 0, +} + +/// This enums specifies the application ID. Two intents in two different applications +/// (i.e., Narwhal, Sui, Ethereum etc) should never collide, so that even when a signing +/// key is reused, nobody can take a signature designated for app_1 and present it as a +/// valid signature for an (any) intent in app_2. +#[derive(Serialize_repr)] +#[repr(u8)] +pub enum AppId { + Sui = 0, +} + +/// An intent is a compact struct serves as the domain separator for a message that a signature commits to. +/// It consists of three parts: [enum IntentScope] (what the type of the message is), +/// [enum IntentVersion], [enum AppId] (what application that the signature refers to). +/// It is used to construct [struct IntentMessage] that what a signature commits to. +/// +/// The serialization of an Intent is a 3-byte array where each field is represented by a byte. +#[derive(Serialize)] +pub struct Intent { + pub scope: IntentScope, + pub version: IntentVersion, + pub app_id: AppId, +} + +/// Intent Message is a wrapper around a message with its intent. The message can +/// be any type that implements [trait Serialize]. *ALL* signatures in Sui must commits +/// to the intent message, not the message itself. This guarantees any intent +/// message signed in the system cannot collide with another since they are domain +/// separated by intent. +/// +/// The serialization of an IntentMessage is compact: it only appends three bytes +/// to the message itself. +#[derive(Serialize)] +pub struct IntentMessage { + pub intent: Intent, + pub value: T, +} + +pub struct TransactionPreimage { + /// Transaction `bcs` encoded representation. + pub unsigned_tx_data: Data, + /// [`TransactionPreimage::unsigned_tx_data`] extended with the `IntentMessage`. + pub tx_data_to_sign: Data, + /// Hash of the [`TransactionPreimage::tx_data_to_sign`]. + pub tx_hash_to_sign: H256, +} + +pub struct TxSigner; + +impl TxSigner { + pub fn sign( + tx: &TransactionData, + signer_key: &ed25519::sha512::KeyPair, + ) -> SigningResult<(TransactionPreimage, SuiSignatureInfo)> { + let public_key = signer_key.public(); + let signer_address = SuiAddress::with_ed25519_pubkey(public_key)?; + if signer_address != tx.sender() { + return Err(SigningError(SigningErrorType::Error_missing_private_key)); + } + + let unsigned_tx_data = + bcs::encode(tx).map_err(|_| SigningError(SigningErrorType::Error_internal))?; + Self::sign_direct(unsigned_tx_data, signer_key) + } + + pub fn sign_direct( + unsigned_tx_data: Data, + signer_key: &ed25519::sha512::KeyPair, + ) -> SigningResult<(TransactionPreimage, SuiSignatureInfo)> { + let preimage = Self::preimage_direct(unsigned_tx_data)?; + let signature = signer_key.sign(preimage.tx_hash_to_sign.into_vec())?; + let signature_info = SuiSignatureInfo::ed25519(&signature, signer_key.public()); + Ok((preimage, signature_info)) + } + + pub fn preimage(tx: &TransactionData) -> SigningResult { + let unsigned_tx_data = + bcs::encode(tx).map_err(|_| SigningError(SigningErrorType::Error_internal))?; + Self::preimage_direct(unsigned_tx_data) + } + + pub fn preimage_direct(unsigned_tx_data: Data) -> SigningResult { + let intent = Intent { + scope: IntentScope::TransactionData, + version: IntentVersion::V0, + app_id: AppId::Sui, + }; + let intent_data = + bcs::encode(&intent).map_err(|_| SigningError(SigningErrorType::Error_internal))?; + + let tx_data_to_sign: Data = intent_data + .into_iter() + .chain(unsigned_tx_data.iter().copied()) + .collect(); + let tx_hash_to_sign = blake2_b(&tx_data_to_sign, H256::LEN) + .and_then(|hash| H256::try_from(hash.as_slice())) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + + Ok(TransactionPreimage { + unsigned_tx_data, + tx_data_to_sign, + tx_hash_to_sign, + }) + } +} diff --git a/rust/chains/tw_sui/src/signature.rs b/rust/chains/tw_sui/src/signature.rs new file mode 100644 index 00000000000..4a35bf808d0 --- /dev/null +++ b/rust/chains/tw_sui/src/signature.rs @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_encoding::base64; +use tw_hash::{H256, H512}; +use tw_keypair::ed25519; +use tw_memory::Data; + +#[derive(Clone, Copy)] +#[repr(u8)] +pub enum SignatureScheme { + ED25519 = 0, +} + +pub struct SuiSignatureInfo { + scheme: SignatureScheme, + signature: H512, + public_key: H256, +} + +impl SuiSignatureInfo { + pub fn ed25519( + signature: &ed25519::Signature, + public_key: &ed25519::sha512::PublicKey, + ) -> SuiSignatureInfo { + SuiSignatureInfo { + scheme: SignatureScheme::ED25519, + signature: signature.to_bytes(), + public_key: public_key.to_bytes(), + } + } + + pub fn to_vec(&self) -> Data { + let mut scheme: Data = Vec::with_capacity(H512::LEN + H256::LEN + 1); + scheme.push(self.scheme as u8); + scheme.extend_from_slice(self.signature.as_slice()); + scheme.extend_from_slice(self.public_key.as_slice()); + scheme + } + + pub fn to_base64(&self) -> String { + let is_url = false; + base64::encode(&self.to_vec(), is_url) + } +} diff --git a/rust/chains/tw_sui/src/signer.rs b/rust/chains/tw_sui/src/signer.rs new file mode 100644 index 00000000000..bfb59285678 --- /dev/null +++ b/rust/chains/tw_sui/src/signer.rs @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::modules::tx_builder::{TWTransaction, TWTransactionBuilder}; +use crate::modules::tx_signer::TxSigner; +use std::borrow::Cow; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::SigningResult; +use tw_coin_entry::signing_output_error; +use tw_encoding::base64; +use tw_proto::Sui::Proto; + +pub struct SuiSigner; + +impl SuiSigner { + pub fn sign( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> Proto::SigningOutput<'static> { + Self::sign_impl(coin, input) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn sign_impl( + _coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> SigningResult> { + let builder = TWTransactionBuilder::new(input); + let signer_key = builder.signer_key()?; + let tx_to_sign = builder.build()?; + + let (preimage, signature) = match tx_to_sign { + TWTransaction::Transaction(tx) => TxSigner::sign(&tx, &signer_key)?, + TWTransaction::SignDirect(tx_data) => TxSigner::sign_direct(tx_data, &signer_key)?, + }; + + let is_url = false; + let unsigned_tx = base64::encode(&preimage.unsigned_tx_data, is_url); + Ok(Proto::SigningOutput { + unsigned_tx: Cow::from(unsigned_tx), + signature: Cow::from(signature.to_base64()), + ..Proto::SigningOutput::default() + }) + } +} diff --git a/rust/chains/tw_sui/src/transaction/command.rs b/rust/chains/tw_sui/src/transaction/command.rs new file mode 100644 index 00000000000..533c0be3df2 --- /dev/null +++ b/rust/chains/tw_sui/src/transaction/command.rs @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::transaction::sui_types::ObjectID; +use move_core_types::identifier::Identifier; +use move_core_types::language_storage::TypeTag; +use serde::{Deserialize, Serialize}; + +/// A single command in a programmable transaction. +#[derive(Debug, Deserialize, Serialize)] +pub enum Command { + /// A call to either an entry or a public Move function + MoveCall(Box), + /// `(Vec, address)` + /// It sends n-objects to the specified address. These objects must have store + /// (public transfer) and either the previous owner must be an address or the object must + /// be newly created. + TransferObjects(Vec, Argument), + /// `(&mut Coin, Vec)` -> `Vec>` + /// It splits off some amounts into a new coins with those amounts + SplitCoins(Argument, Vec), + /// `(&mut Coin, Vec>)` + /// It merges n-coins into the first coin + MergeCoins(Argument, Vec), + /// Publishes a Move package. It takes the package bytes and a list of the package's transitive + /// dependencies to link against on-chain. + Publish(Vec>, Vec), + /// `forall T: Vec -> vector` + /// Given n-values of the same type, it constructs a vector. For non objects or an empty vector, + /// the type tag must be specified. + MakeMoveVec(Option, Vec), +} + +impl Command { + pub fn move_call( + package: ObjectID, + module: Identifier, + function: Identifier, + type_arguments: Vec, + arguments: Vec, + ) -> Self { + Command::MoveCall(Box::new(ProgrammableMoveCall { + package, + module, + function, + type_arguments, + arguments, + })) + } +} + +/// The command for calling a Move function, either an entry function or a public +/// function (which cannot return references). +#[derive(Debug, Deserialize, Serialize)] +pub struct ProgrammableMoveCall { + /// The package containing the module and function. + pub package: ObjectID, + /// The specific module in the package containing the function. + pub module: Identifier, + /// The function to be called. + pub function: Identifier, + /// The type arguments to the function. + pub type_arguments: Vec, + /// The arguments to the function. + pub arguments: Vec, +} + +/// An argument to a programmable transaction command +#[derive(Clone, Copy, Debug, Deserialize, Serialize)] +pub enum Argument { + /// The gas coin. The gas coin can only be used by-ref, except for with + /// `TransferObjects`, which can use it by-value. + GasCoin, + /// One of the input objects or primitive values (from + /// `ProgrammableTransaction` inputs) + Input(u16), + /// The result of another command (from `ProgrammableTransaction` commands) + Result(u16), + /// Like a `Result` but it accesses a nested result. Currently, the only usage + /// of this is to access a value from a Move call with multiple return values. + NestedResult(u16, u16), +} diff --git a/rust/chains/tw_sui/src/transaction/mod.rs b/rust/chains/tw_sui/src/transaction/mod.rs new file mode 100644 index 00000000000..46b7c4a5cf0 --- /dev/null +++ b/rust/chains/tw_sui/src/transaction/mod.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +pub mod command; +pub mod programmable_transaction; +pub mod sui_types; +pub mod transaction_builder; +pub mod transaction_data; diff --git a/rust/chains/tw_sui/src/transaction/programmable_transaction.rs b/rust/chains/tw_sui/src/transaction/programmable_transaction.rs new file mode 100644 index 00000000000..6a3529299b5 --- /dev/null +++ b/rust/chains/tw_sui/src/transaction/programmable_transaction.rs @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::address::SuiAddress; +use crate::transaction::command::{Argument, Command}; +use crate::transaction::sui_types::{CallArg, ObjectArg, ObjectID, ObjectRef}; +use indexmap::IndexMap; +use move_core_types::identifier::Identifier; +use move_core_types::language_storage::TypeTag; +use serde::{Deserialize, Serialize}; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_encoding::bcs; + +/// A series of commands where the results of one command can be used in future +/// commands +#[derive(Debug, Deserialize, Serialize)] +pub struct ProgrammableTransaction { + /// Input objects or primitive values + pub inputs: Vec, + /// The commands to be executed sequentially. A failure in any command will + /// result in the failure of the entire transaction. + pub commands: Vec, +} + +#[derive(Eq, Hash, PartialEq)] +enum BuilderArg { + Object(ObjectID), + Pure(Vec), + ForcedNonUniquePure(usize), +} + +#[derive(Default)] +pub struct ProgrammableTransactionBuilder { + inputs: IndexMap, + commands: Vec, +} + +impl ProgrammableTransactionBuilder { + pub fn finish(self) -> ProgrammableTransaction { + let Self { inputs, commands } = self; + let inputs = inputs.into_values().collect(); + ProgrammableTransaction { inputs, commands } + } + + /// Will fail to generate if recipients and amounts do not have the same lengths. + /// Or if coins is empty + pub fn pay( + &mut self, + coins: Vec, + recipients: Vec, + amounts: Vec, + ) -> SigningResult<()> { + let mut coins = coins.into_iter(); + let Some(coin) = coins.next() else { + // coins vector is empty + return Err(SigningError(SigningErrorType::Error_invalid_params)); + }; + let coin_arg = self.obj(ObjectArg::ImmOrOwnedObject(coin))?; + let merge_args: Vec<_> = coins + .map(|c| self.obj(ObjectArg::ImmOrOwnedObject(c))) + .collect::>()?; + if !merge_args.is_empty() { + self.command(Command::MergeCoins(coin_arg, merge_args)); + } + self.pay_impl(recipients, amounts, coin_arg) + } + + /// Will fail to generate if recipients and amounts do not have the same lengths + pub fn pay_sui(&mut self, recipients: Vec, amounts: Vec) -> SigningResult<()> { + self.pay_impl(recipients, amounts, Argument::GasCoin) + } + + pub fn pay_all_sui(&mut self, recipient: SuiAddress) { + let rec_arg = self.pure(recipient).unwrap(); + self.command(Command::TransferObjects(vec![Argument::GasCoin], rec_arg)); + } + + pub fn transfer_object( + &mut self, + recipient: SuiAddress, + object_ref: ObjectRef, + ) -> SigningResult<()> { + let rec_arg = self.pure(recipient).unwrap(); + let obj_arg = self.obj(ObjectArg::ImmOrOwnedObject(object_ref)); + self.commands + .push(Command::TransferObjects(vec![obj_arg?], rec_arg)); + Ok(()) + } + + /// Will fail to generate if given an empty ObjVec + pub fn move_call( + &mut self, + package: ObjectID, + module: Identifier, + function: Identifier, + type_arguments: Vec, + call_args: Vec, + ) -> SigningResult<()> { + let arguments = call_args + .into_iter() + .map(|a| self.input(a)) + .collect::>()?; + self.command(Command::move_call( + package, + module, + function, + type_arguments, + arguments, + )); + Ok(()) + } + + pub fn input(&mut self, call_arg: CallArg) -> SigningResult { + match call_arg { + CallArg::Pure(bytes) => { + let force_separate = false; + Ok(self.pure_bytes(bytes, force_separate)) + }, + CallArg::Object(obj) => self.obj(obj), + } + } + + pub fn pure_bytes(&mut self, bytes: Vec, force_separate: bool) -> Argument { + let arg = if force_separate { + BuilderArg::ForcedNonUniquePure(self.inputs.len()) + } else { + BuilderArg::Pure(bytes.clone()) + }; + let (i, _) = self.inputs.insert_full(arg, CallArg::Pure(bytes)); + Argument::Input(i as u16) + } + + pub fn pure(&mut self, value: T) -> SigningResult { + let force_separate = false; + Ok(self.pure_bytes(bcs::encode(&value)?, force_separate)) + } + + pub fn obj(&mut self, obj_arg: ObjectArg) -> SigningResult { + let id = obj_arg.id(); + let obj_arg = if let Some(old_value) = self.inputs.get(&BuilderArg::Object(id)) { + let old_obj_arg = match old_value { + CallArg::Pure(_) => return Err(SigningError(SigningErrorType::Error_internal)), + CallArg::Object(arg) => arg, + }; + match (old_obj_arg, obj_arg) { + ( + ObjectArg::SharedObject { + id: id1, + initial_shared_version: v1, + mutable: mut1, + }, + ObjectArg::SharedObject { + id: id2, + initial_shared_version: v2, + mutable: mut2, + }, + ) if v1 == &v2 => { + if id1 != &id2 || id != id2 { + // "invariant violation! object has id does not match call arg" + return Err(SigningError(SigningErrorType::Error_internal)); + } + ObjectArg::SharedObject { + id, + initial_shared_version: v2, + mutable: *mut1 || mut2, + } + }, + (old_obj_arg, obj_arg) => { + if old_obj_arg != &obj_arg { + // "Mismatched Object argument kind for object {id}. {old_value:?} is not compatible with {obj_arg:?}" + return Err(SigningError(SigningErrorType::Error_internal)); + } + obj_arg + }, + } + } else { + obj_arg + }; + let (i, _) = self + .inputs + .insert_full(BuilderArg::Object(id), CallArg::Object(obj_arg)); + Ok(Argument::Input(i as u16)) + } + + pub fn make_obj_vec( + &mut self, + objs: impl IntoIterator, + ) -> SigningResult { + let make_vec_args = objs + .into_iter() + .map(|obj| self.obj(obj)) + .collect::>()?; + Ok(self.command(Command::MakeMoveVec(None, make_vec_args))) + } + + pub fn command(&mut self, command: Command) -> Argument { + let i = self.commands.len(); + self.commands.push(command); + Argument::Result(i as u16) + } + + fn pay_impl( + &mut self, + recipients: Vec, + amounts: Vec, + coin: Argument, + ) -> SigningResult<()> { + if recipients.len() != amounts.len() { + // "Recipients and amounts mismatch. Got {} recipients but {} amounts" + return Err(SigningError(SigningErrorType::Error_invalid_params)); + } + if amounts.is_empty() { + return Ok(()); + } + + // collect recipients in the case where they are non-unique in order + // to minimize the number of transfers that must be performed + let mut recipient_map: IndexMap> = IndexMap::new(); + let mut amt_args = vec![]; + for (i, (recipient, amount)) in recipients.into_iter().zip(amounts).enumerate() { + recipient_map.entry(recipient).or_default().push(i); + amt_args.push(self.pure(amount)?); + } + let Argument::Result(split_primary) = self.command(Command::SplitCoins(coin, amt_args)) + else { + panic!("self.command should always give a Argument::Result") + }; + for (recipient, split_secondaries) in recipient_map { + let rec_arg = self.pure(recipient).unwrap(); + let coins = split_secondaries + .into_iter() + .map(|j| Argument::NestedResult(split_primary, j as u16)) + .collect(); + self.command(Command::TransferObjects(coins, rec_arg)); + } + Ok(()) + } +} diff --git a/rust/chains/tw_sui/src/transaction/sui_types.rs b/rust/chains/tw_sui/src/transaction/sui_types.rs new file mode 100644 index 00000000000..b35df1b26d8 --- /dev/null +++ b/rust/chains/tw_sui/src/transaction/sui_types.rs @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::address::SuiAddress; +use crate::constants::{SUI_SYSTEM_STATE_OBJECT_ID, SUI_SYSTEM_STATE_OBJECT_SHARED_VERSION}; +use move_core_types::account_address::AccountAddress; +use serde::{Deserialize, Serialize}; +use std::str::FromStr; +use tw_coin_entry::error::{AddressError, SigningError, SigningErrorType}; +use tw_encoding::base58::{self, Alphabet}; +use tw_hash::{as_bytes, H256}; +use tw_memory::Data; + +pub type ObjectRef = (ObjectID, SequenceNumber, ObjectDigest); +pub type EpochId = u64; + +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] +pub struct SequenceNumber(pub u64); + +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct ObjectID(pub AccountAddress); + +impl FromStr for ObjectID { + type Err = AddressError; + + fn from_str(s: &str) -> Result { + let addr = SuiAddress::from_str(s)?; + Ok(ObjectID(addr.into_inner())) + } +} + +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct ObjectDigest(#[serde(with = "as_bytes")] pub H256); + +impl FromStr for ObjectDigest { + type Err = SigningError; + + fn from_str(s: &str) -> Result { + let bytes = base58::decode(s, Alphabet::BITCOIN) + .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?; + H256::try_from(bytes.as_slice()) + .map(ObjectDigest) + .map_err(|_| SigningError(SigningErrorType::Error_invalid_params)) + } +} + +#[derive(Debug, Deserialize, Serialize)] +pub enum CallArg { + // contains no structs or objects + Pure(Data), + // an object + Object(ObjectArg), +} + +impl CallArg { + pub const SUI_SYSTEM_MUT: Self = Self::Object(ObjectArg::SUI_SYSTEM_MUT); +} + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +pub enum ObjectArg { + // A Move object, either immutable, or owned mutable. + ImmOrOwnedObject(ObjectRef), + // A Move object that's shared. + // SharedObject::mutable controls whether caller asks for a mutable reference to shared object. + SharedObject { + id: ObjectID, + initial_shared_version: SequenceNumber, + mutable: bool, + }, +} + +impl ObjectArg { + pub const SUI_SYSTEM_MUT: Self = Self::SharedObject { + id: SUI_SYSTEM_STATE_OBJECT_ID, + initial_shared_version: SUI_SYSTEM_STATE_OBJECT_SHARED_VERSION, + mutable: true, + }; + + pub fn id(&self) -> ObjectID { + match self { + ObjectArg::ImmOrOwnedObject((id, _, _)) | ObjectArg::SharedObject { id, .. } => *id, + } + } +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct GasData { + pub payment: Vec, + pub owner: SuiAddress, + pub price: u64, + pub budget: u64, +} + +#[derive(Debug, Deserialize, Serialize)] +pub enum TransactionExpiration { + /// The transaction has no expiration + None, + /// Validators wont sign a transaction unless the expiration Epoch + /// is greater than or equal to the current epoch + Epoch(EpochId), +} diff --git a/rust/chains/tw_sui/src/transaction/transaction_builder.rs b/rust/chains/tw_sui/src/transaction/transaction_builder.rs new file mode 100644 index 00000000000..93ad29060f0 --- /dev/null +++ b/rust/chains/tw_sui/src/transaction/transaction_builder.rs @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::address::SuiAddress; +use crate::constants::{ + ADD_STAKE_MUL_COIN_FUN_NAME, SUI_SYSTEM_MODULE_NAME, SUI_SYSTEM_PACKAGE_ID, + WITHDRAW_STAKE_FUN_NAME, +}; +use crate::transaction::command::Command; +use crate::transaction::programmable_transaction::ProgrammableTransactionBuilder; +use crate::transaction::sui_types::{CallArg, ObjectArg, ObjectRef}; +use crate::transaction::transaction_data::{TransactionData, TransactionKind}; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_encoding::bcs; + +pub struct TransactionBuilder; + +impl TransactionBuilder { + pub fn request_add_stake( + signer: SuiAddress, + coins: Vec, + amount: Option, + validator: SuiAddress, + gas: ObjectRef, + gas_budget: u64, + gas_price: u64, + ) -> SigningResult { + let obj_vec: Vec<_> = coins.into_iter().map(ObjectArg::ImmOrOwnedObject).collect(); + + let pt = { + let mut builder = ProgrammableTransactionBuilder::default(); + let arguments = vec![ + builder.input(CallArg::SUI_SYSTEM_MUT).unwrap(), + builder.make_obj_vec(obj_vec)?, + builder.input(CallArg::Pure(bcs::encode(&amount)?)).unwrap(), + builder + .input(CallArg::Pure(bcs::encode(&validator)?)) + .unwrap(), + ]; + builder.command(Command::move_call( + SUI_SYSTEM_PACKAGE_ID, + SUI_SYSTEM_MODULE_NAME.to_owned(), + ADD_STAKE_MUL_COIN_FUN_NAME.to_owned(), + vec![], + arguments, + )); + builder.finish() + }; + Ok(TransactionData::new_programmable( + signer, + vec![gas], + pt, + gas_budget, + gas_price, + )) + } + + pub fn request_withdraw_stake( + signer: SuiAddress, + staked_sui: ObjectRef, + gas: ObjectRef, + gas_budget: u64, + gas_price: u64, + ) -> SigningResult { + TransactionData::new_move_call( + signer, + SUI_SYSTEM_PACKAGE_ID, + SUI_SYSTEM_MODULE_NAME.to_owned(), + WITHDRAW_STAKE_FUN_NAME.to_owned(), + vec![], + gas, + vec![ + CallArg::SUI_SYSTEM_MUT, + CallArg::Object(ObjectArg::ImmOrOwnedObject(staked_sui)), + ], + gas_budget, + gas_price, + ) + } + + /// Send `Coin` to a list of addresses, where T can be any coin type, following a list of amounts. + /// The object specified in the gas field will be used to pay the gas fee for the transaction. + /// The gas object can not appear in input_coins. + /// https://docs.sui.io/sui-api-ref#unsafe_pay + #[allow(clippy::too_many_arguments)] + pub fn pay( + signer: SuiAddress, + input_coins: Vec, + recipients: Vec, + amounts: Vec, + gas: ObjectRef, + gas_budget: u64, + gas_price: u64, + ) -> SigningResult { + if input_coins.iter().any(|coin| coin.0 == gas.0) { + // Gas coin is in input coins of Pay transaction, use PaySui transaction instead!. + return Err(SigningError(SigningErrorType::Error_invalid_params)); + } + + TransactionData::new_pay( + signer, + input_coins, + recipients, + amounts, + gas, + gas_budget, + gas_price, + ) + } + + /// Send SUI coins to a list of addresses, following a list of amounts. + /// This is for SUI coin only and does not require a separate gas coin object. + /// https://docs.sui.io/sui-api-ref#unsafe_paysui + pub fn pay_sui( + signer: SuiAddress, + mut input_coins: Vec, + recipients: Vec, + amounts: Vec, + gas_budget: u64, + gas_price: u64, + ) -> SigningResult { + if input_coins.is_empty() { + // "Empty input coins for Pay related transaction" + return Err(SigningError(SigningErrorType::Error_invalid_params)); + } + + let gas_object_ref = input_coins.remove(0); + TransactionData::new_pay_sui( + signer, + input_coins, + recipients, + amounts, + gas_object_ref, + gas_budget, + gas_price, + ) + } + + /// Send all SUI coins to one recipient. + /// This is for SUI coin only and does not require a separate gas coin object. + /// https://docs.sui.io/sui-api-ref#unsafe_payallsui + pub fn pay_all_sui( + signer: SuiAddress, + mut input_coins: Vec, + recipient: SuiAddress, + gas_budget: u64, + gas_price: u64, + ) -> SigningResult { + if input_coins.is_empty() { + // "Empty input coins for Pay related transaction" + return Err(SigningError(SigningErrorType::Error_invalid_params)); + } + + let gas_object_ref = input_coins.remove(0); + Ok(TransactionData::new_pay_all_sui( + signer, + input_coins, + recipient, + gas_object_ref, + gas_budget, + gas_price, + )) + } + + pub fn transfer_object( + signer: SuiAddress, + object: ObjectRef, + recipient: SuiAddress, + gas: ObjectRef, + gas_budget: u64, + gas_price: u64, + ) -> SigningResult { + let mut builder = ProgrammableTransactionBuilder::default(); + builder.transfer_object(recipient, object)?; + + Ok(TransactionData::new( + TransactionKind::ProgrammableTransaction(builder.finish()), + signer, + gas, + gas_budget, + gas_price, + )) + } +} diff --git a/rust/chains/tw_sui/src/transaction/transaction_data.rs b/rust/chains/tw_sui/src/transaction/transaction_data.rs new file mode 100644 index 00000000000..6f3329cc419 --- /dev/null +++ b/rust/chains/tw_sui/src/transaction/transaction_data.rs @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::address::SuiAddress; +use crate::transaction::programmable_transaction::{ + ProgrammableTransaction, ProgrammableTransactionBuilder, +}; +use crate::transaction::sui_types::{CallArg, GasData, ObjectID, ObjectRef, TransactionExpiration}; +use move_core_types::identifier::Identifier; +use move_core_types::language_storage::TypeTag; +use serde::{Deserialize, Serialize}; +use tw_coin_entry::error::SigningResult; + +#[derive(Debug, Deserialize, Serialize)] +pub enum TransactionData { + V1(TransactionDataV1), +} + +impl TransactionData { + #[inline] + pub fn new( + kind: TransactionKind, + sender: SuiAddress, + gas_payment: ObjectRef, + gas_budget: u64, + gas_price: u64, + ) -> Self { + TransactionData::V1(TransactionDataV1 { + kind, + sender, + gas_data: GasData { + price: gas_price, + owner: sender, + payment: vec![gas_payment], + budget: gas_budget, + }, + expiration: TransactionExpiration::None, + }) + } + + #[inline] + pub fn new_programmable( + sender: SuiAddress, + gas_payment: Vec, + pt: ProgrammableTransaction, + gas_budget: u64, + gas_price: u64, + ) -> Self { + Self::new_programmable_allow_sponsor(sender, gas_payment, pt, gas_budget, gas_price, sender) + } + + #[inline] + pub fn new_programmable_allow_sponsor( + sender: SuiAddress, + gas_payment: Vec, + pt: ProgrammableTransaction, + gas_budget: u64, + gas_price: u64, + sponsor: SuiAddress, + ) -> Self { + let kind = TransactionKind::ProgrammableTransaction(pt); + Self::new_with_gas_coins_allow_sponsor( + kind, + sender, + gas_payment, + gas_budget, + gas_price, + sponsor, + ) + } + + #[inline] + pub fn new_with_gas_coins_allow_sponsor( + kind: TransactionKind, + sender: SuiAddress, + gas_payment: Vec, + gas_budget: u64, + gas_price: u64, + gas_sponsor: SuiAddress, + ) -> Self { + TransactionData::V1(TransactionDataV1 { + kind, + sender, + gas_data: GasData { + price: gas_price, + owner: gas_sponsor, + payment: gas_payment, + budget: gas_budget, + }, + expiration: TransactionExpiration::None, + }) + } + + pub fn new_pay( + sender: SuiAddress, + coins: Vec, + recipients: Vec, + amounts: Vec, + gas_payment: ObjectRef, + gas_budget: u64, + gas_price: u64, + ) -> SigningResult { + let pt = { + let mut builder = ProgrammableTransactionBuilder::default(); + builder.pay(coins, recipients, amounts)?; + builder.finish() + }; + Ok(Self::new_programmable( + sender, + vec![gas_payment], + pt, + gas_budget, + gas_price, + )) + } + + pub fn new_pay_sui( + sender: SuiAddress, + mut coins: Vec, + recipients: Vec, + amounts: Vec, + gas_payment: ObjectRef, + gas_budget: u64, + gas_price: u64, + ) -> SigningResult { + coins.insert(0, gas_payment); + let pt = { + let mut builder = ProgrammableTransactionBuilder::default(); + builder.pay_sui(recipients, amounts)?; + builder.finish() + }; + Ok(Self::new_programmable( + sender, coins, pt, gas_budget, gas_price, + )) + } + + pub fn new_pay_all_sui( + sender: SuiAddress, + mut coins: Vec, + recipient: SuiAddress, + gas_payment: ObjectRef, + gas_budget: u64, + gas_price: u64, + ) -> Self { + coins.insert(0, gas_payment); + let pt = { + let mut builder = ProgrammableTransactionBuilder::default(); + builder.pay_all_sui(recipient); + builder.finish() + }; + Self::new_programmable(sender, coins, pt, gas_budget, gas_price) + } + + #[allow(clippy::too_many_arguments)] + pub fn new_move_call( + sender: SuiAddress, + package: ObjectID, + module: Identifier, + function: Identifier, + type_arguments: Vec, + gas_payment: ObjectRef, + arguments: Vec, + gas_budget: u64, + gas_price: u64, + ) -> SigningResult { + Self::new_move_call_with_gas_coins( + sender, + package, + module, + function, + type_arguments, + vec![gas_payment], + arguments, + gas_budget, + gas_price, + ) + } + + #[allow(clippy::too_many_arguments)] + pub fn new_move_call_with_gas_coins( + sender: SuiAddress, + package: ObjectID, + module: Identifier, + function: Identifier, + type_arguments: Vec, + gas_payment: Vec, + arguments: Vec, + gas_budget: u64, + gas_price: u64, + ) -> SigningResult { + let pt = { + let mut builder = ProgrammableTransactionBuilder::default(); + builder.move_call(package, module, function, type_arguments, arguments)?; + builder.finish() + }; + Ok(Self::new_programmable( + sender, + gas_payment, + pt, + gas_budget, + gas_price, + )) + } + + pub fn sender(&self) -> SuiAddress { + match self { + TransactionData::V1(v1) => v1.sender, + } + } +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct TransactionDataV1 { + pub kind: TransactionKind, + pub sender: SuiAddress, + pub gas_data: GasData, + pub expiration: TransactionExpiration, +} + +#[derive(Debug, Deserialize, Serialize)] +pub enum TransactionKind { + /// A transaction that allows the interleaving of native commands and Move calls + ProgrammableTransaction(ProgrammableTransaction), +} diff --git a/rust/chains/tw_sui/tests/decode_transaction.rs b/rust/chains/tw_sui/tests/decode_transaction.rs new file mode 100644 index 00000000000..746516874b1 --- /dev/null +++ b/rust/chains/tw_sui/tests/decode_transaction.rs @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use std::str::FromStr; +use tw_encoding::hex::DecodeHex; +use tw_encoding::{base64, bcs}; +use tw_sui::address::SuiAddress; +use tw_sui::transaction::command::{Argument, Command}; +use tw_sui::transaction::programmable_transaction::ProgrammableTransaction; +use tw_sui::transaction::sui_types::{ + CallArg, GasData, ObjectDigest, ObjectID, SequenceNumber, TransactionExpiration, +}; +use tw_sui::transaction::transaction_data::{TransactionData, TransactionDataV1, TransactionKind}; + +#[test] +fn test_decode_transfer_tx() { + let programmable = ProgrammableTransaction { + inputs: vec![ + CallArg::Pure("1027000000000000".decode_hex().unwrap()), + CallArg::Pure( + "259ff8074ab425cbb489f236e18e08f03f1a7856bdf7c7a2877bd64f738b5015" + .decode_hex() + .unwrap(), + ), + ], + commands: vec![ + Command::SplitCoins(Argument::GasCoin, vec![Argument::Input(0)]), + Command::TransferObjects(vec![Argument::NestedResult(0, 0)], Argument::Input(1)), + ], + }; + + let v1 = TransactionDataV1 { + kind: TransactionKind::ProgrammableTransaction(programmable), + sender: SuiAddress::from_str( + "0xd575ad7f18e948462a5cf698f564ef394a752a71fec62493af8a055c012c0d50", + ) + .unwrap(), + gas_data: GasData { + payment: vec![( + ObjectID( + SuiAddress::from_str( + "0x06f2c2c8c1d8964df1019d6616e9705719bebabd931da2755cb948ceb7e68964", + ) + .unwrap() + .into_inner(), + ), + SequenceNumber(748), + ObjectDigest::from_str("7UoYeVzREVT17ZyYbRTsKzRCec5xJWm6FMh8AKaDPdDx").unwrap(), + )], + owner: SuiAddress::from_str( + "0xd575ad7f18e948462a5cf698f564ef394a752a71fec62493af8a055c012c0d50", + ) + .unwrap(), + price: 1, + budget: 2000, + }, + expiration: TransactionExpiration::None, + }; + let data = TransactionData::V1(v1); + + let is_url = false; + let bytes = base64::encode(&bcs::encode(&data).unwrap(), is_url); + // Successfully broadcasted https://explorer.sui.io/txblock/HkPo6rYPyDY53x1MBszvSZVZyixVN7CHvCJGX381czAh?network=devnet + assert_eq!(bytes, "AAACAAgQJwAAAAAAAAAgJZ/4B0q0Jcu0ifI24Y4I8D8aeFa998eih3vWT3OLUBUCAgABAQAAAQEDAAAAAAEBANV1rX8Y6UhGKlz2mPVk7zlKdSpx/sYkk6+KBVwBLA1QAQbywsjB2JZN8QGdZhbpcFcZvrq9kx2idVy5SM635olk7AIAAAAAAAAgYEVuxmf1zRBGdoDr+VDtMpIFF12s2Ua7I2ru1XyGF8/Vda1/GOlIRipc9pj1ZO85SnUqcf7GJJOvigVcASwNUAEAAAAAAAAA0AcAAAAAAAAA"); +} diff --git a/rust/coverage.stats b/rust/coverage.stats index f2511044fdd..fd6ae261616 100644 --- a/rust/coverage.stats +++ b/rust/coverage.stats @@ -1 +1 @@ -93.0 \ No newline at end of file +94.0 \ No newline at end of file diff --git a/rust/tw_any_coin/tests/chains/mod.rs b/rust/tw_any_coin/tests/chains/mod.rs index cd580c22dde..c4e5020a1dd 100644 --- a/rust/tw_any_coin/tests/chains/mod.rs +++ b/rust/tw_any_coin/tests/chains/mod.rs @@ -13,6 +13,7 @@ mod internet_computer; mod native_evmos; mod native_injective; mod solana; +mod sui; mod tbinance; mod thorchain; mod zetachain; diff --git a/rust/tw_any_coin/tests/chains/solana/solana_sign.rs b/rust/tw_any_coin/tests/chains/solana/solana_sign.rs index 186105c5fda..b667a3c588c 100644 --- a/rust/tw_any_coin/tests/chains/solana/solana_sign.rs +++ b/rust/tw_any_coin/tests/chains/solana/solana_sign.rs @@ -219,6 +219,36 @@ fn test_solana_sign_delegate_stake_no_stake_account() { assert_eq!(output.encoded, "j24mVM9Zgu5vDZhPLGGuCRXQnP9djNtxdHh4txN3S7dwJsNNL5fbhzGpPgSUAcLGoMVCfF9TuqTYfpfJnb4sJFe1ahM8yPL5HwuKL6py5AZJFi8SWx9fvaVB699dCPo1GT3JoEBLPCZ9o2jQtnwzLkzTYJnKv2axqhKWFE2sz6TBA5J39eZcjMFUYgyxz6Q5S4MWqYQCb8UET2NAEZoKcfy7j8N25WXL6Gj4j3hBZjpHQQNaGaNEprEqyma3ZuVhpGiCALSsuzVLX3wZVo4icXwe952deMFA4tH3BK1jcSQCgfmcKDJ9nd7bdrnUUs4BoMdF1uDZB5LxE2UH8QiqtYvaUcorF4SJ3gPxM5ykbyPsNK1cSYZF9NMpW2GofyC17eELwnHQTQB2kqphxJZu7BahvkwiDPPeeydiXAkBspJ3nc3PCBujv6WJw22ZHw5j6zAP8ZGnCW44pqtWD5qifF9tTKhySKdANNiWifs3tSCCPQqjfJXu14drNinR6VG8rJxS1qgmRYiRQUa7m1vtoaZFRN5qKUeAfoFKkAVaNnMdwgsNqNH4dqBodTCJFs1LkYwhgRZdZGbwXTn1j7vpR3DSnv4g72i2H556srzK53jdUmdv6yfxt516XDSshqZtHnKZ1tudxKjBXwsqT3imDiZFVka9wKWUAYMCi4XZ79CY6Xpsd9c18U2e9TCngQmgkTATFgrqysfraokNffgqWxvsPMugksbvbPjJs3iCzByvphkC9p7hCf6LwbeF8XnVB91EAgRDA4VLE1f9wkcq5zjy879YWJ4r516h3PQszTz1EaJXNAXdbk5Em7eyuuabGP1Q3nijFTL2yhMDsXpgrjAuEAABNxFMd4J1JRMaic615mHrhwociksrsfQK"); } +#[test] +fn test_solana_sign_delegate_stake_with_priority_fee() { + let delegate = Proto::DelegateStake { + validator_pubkey: "BWkvytz3MAiLkUbMuYK5yV1VYThbBYYQYG3gdef8NLw5".into(), + // 0.01 + value: 10000000, + ..Proto::DelegateStake::default() + }; + + let input = Proto::SigningInput { + // Corresponding Solana address: GEjr7dsDVHZMzTvnz1AgDW76LJ2GC7DATnHHMUVoF4p6. + private_key: "43c0aeb43038c582b1abac52e5bdc2c20c83a1995fbfde5936ccd2e6392e9d8b" + .decode_hex() + .unwrap() + .into(), + recent_blockhash: "7uKCh5WRoLdib5LfcX4NETFcJroQPjHVsekdXniyaoFZ".into(), + transaction_type: TransactionType::delegate_stake_transaction(delegate), + priority_fee_price: Some(Proto::PriorityFeePrice { price: 1000 }), + priority_fee_limit: Some(Proto::PriorityFeeLimit { limit: 10_000 }), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + // // https://solscan.io/tx/2DRUt4Q3BxYPB1Whd4MyekByh4pXWgrfQAybZ3obeNReR96sKYX1pZgZV12NUkLXJRb1c6ffAcEx1Eu7kYmA6zqH + assert_eq!(output.encoded, "XUMBKTDMfK4i8N3RBJtzzDsA3KnKZ4R4HexjXxiwsKr3j4DhENHcpUFWA41xwU94dE4av4hqP9Z5nvES9FU7GuECsDE5CuRagt5b1EEH2SPW8AjdeGbTs7JfT4nm3nd6gh2uJHXVMCjTYEhTKSKgLehgCa2JqwodiKEoPUdPwRoq7YoznzSBzvwvmSZrySC2eA8pp1PaBNDCG9rMAg1mDAzAu4VNmH7Nhh9bssGPcxgKZfoWmrhpvNcx6bDV4xBJBtvsYoGasRRSTXwF82VjA1L5aaQWJjAbBBZgsiws2Z5CqeWWdt6XF7sUHovAZihUdxXzJXPjRpEgX4HqHdriqTnPuaNwmVMEcNKzmZWJ543LTz4yUhyLRbYbN9hxWMtpiKBnjZe5KX783NPiuSHioyxAvmfkdKbDyKUf6a9G9HocHZMjKT3Yz1yeanHo69V2mAfKGZCPT7hRhRae6QoXu16QjHDHNBDLUtX8Db49vNJLdV3cYWPuyZ5eHLfDbw6r9jzCF34nmXJ4kFZHJHatp3wGKGbTtyeoxZnJVxLFmv7byoKVZ1hQdxGYg3q2jfjc2NsSj2Expmz6qLGZWMyVy6cVYjfqcQz9bwT89zC8823mAnFEPKRs8whZevCxifGiUfT6H99zZzNKfuJAhwJ14rP5VpZHCiDSuDRHzjydzwFxHDKamnAQXYuadEWJB89gJWA6wPoBQE1q4kRFTrYjU32mmnfwCs6Ji18uzNfrP2W6xPMs93vz2Pejpgzyru6anf7cYhKWVtou6bM9uNdVV5Qj5zr3eY4FqbNXXMz4WkiWmKQb5nUeyvp4e424WS1QUkwNYVW5vmehM2AuzMM9VEA5F2VMyrDihE74ZFA9yctaYZ4ovtH2TvSVgCGsS7ujqHoETVtDiyrjmgJvm68NtjcTioSKpAPgEvMciwxVwsUmYC6NGC6iCP2oVc2S6QCzixF"); +} + #[test] fn test_solana_sign_delegate_stake_no_stake_account_5zrqgk1() { // Corresponding Solana address: GcQQ1qx822KK14zyfTkMLQMLEZ4a9d88HnKepaA2XbmW. diff --git a/rust/tw_any_coin/tests/chains/sui/mod.rs b/rust/tw_any_coin/tests/chains/sui/mod.rs new file mode 100644 index 00000000000..53876af861f --- /dev/null +++ b/rust/tw_any_coin/tests/chains/sui/mod.rs @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_proto::Sui::Proto; + +mod sui_address; +mod sui_compile; +mod sui_sign; +mod test_cases; + +fn object_ref(id: &'static str, version: u64, digest: &'static str) -> Proto::ObjectRef<'static> { + Proto::ObjectRef { + object_id: id.into(), + version, + object_digest: digest.into(), + } +} diff --git a/rust/tw_any_coin/tests/chains/sui/sui_address.rs b/rust/tw_any_coin/tests/chains/sui/sui_address.rs new file mode 100644 index 00000000000..c2a484ed332 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/sui/sui_address.rs @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_any_coin::test_utils::address_utils::{ + test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, +}; +use tw_coin_registry::coin_type::CoinType; + +#[test] +fn test_sui_address_normalization() { + test_address_normalization( + CoinType::Sui, + "259ff8074ab425cbb489f236e18e08f03f1a7856bdf7c7a2877bd64f738b5015", + "0x259ff8074ab425cbb489f236e18e08f03f1a7856bdf7c7a2877bd64f738b5015", + ); +} + +#[test] +fn test_sui_address_is_valid() { + test_address_valid( + CoinType::Sui, + "0x259ff8074ab425cbb489f236e18e08f03f1a7856bdf7c7a2877bd64f738b5015", + ); +} + +#[test] +fn test_sui_address_invalid() { + // Address 20 are invalid in SUI. + test_address_invalid(CoinType::Sui, "0xb1dc06bd64d4e179a482b97bb68243f6c02c1b92"); + test_address_invalid(CoinType::Sui, "b1dc06bd64d4e179a482b97bb68243f6c02c1b92"); + // Too long. + test_address_invalid( + CoinType::Sui, + "d575ad7f18e948462a5cf698f564ef394a752a71fec62493af8a055c012c0d502", + ); + // Too short. + test_address_invalid(CoinType::Sui, "b1dc06bd64d4e179a482b97bb68243f6c02c1b9"); + // Invalid short address. + test_address_invalid(CoinType::Sui, "0x11"); + // Invalid Hex + test_address_invalid( + CoinType::Sui, + "0xS59ff8074ab425cbb489f236e18e08f03f1a7856bdf7c7a2877bd64f738b5015", + ); +} + +#[test] +fn test_sui_address_get_data() { + test_address_get_data( + CoinType::Sui, + "0x259ff8074ab425cbb489f236e18e08f03f1a7856bdf7c7a2877bd64f738b5015", + "259ff8074ab425cbb489f236e18e08f03f1a7856bdf7c7a2877bd64f738b5015", + ); +} diff --git a/rust/tw_any_coin/tests/chains/sui/sui_compile.rs b/rust/tw_any_coin/tests/chains/sui/sui_compile.rs new file mode 100644 index 00000000000..67f84472525 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/sui/sui_compile.rs @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::chains::sui::test_cases::{transfer_d4ay9tdb, PRIVATE_KEY_54E80D76, SENDER_54E80D76}; +use tw_any_coin::test_utils::sign_utils::{CompilerHelper, PreImageHelper}; +use tw_coin_registry::coin_type::CoinType; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_keypair::ed25519; +use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; +use tw_misc::traits::ToBytesVec; +use tw_proto::Common::Proto::SigningError; +use tw_proto::Sui::Proto::{self, mod_SigningInput::OneOftransaction_payload as TransactionType}; +use tw_proto::TxCompiler::Proto as CompilerProto; + +struct SuiCompileArgs<'a> { + input: Proto::SigningInput<'a>, + private_key: &'a str, + tx_hash: &'a str, + unsigned_tx_data: &'a str, + signature: &'a str, +} + +fn test_sui_compile_impl(args: SuiCompileArgs) { + // Step 2: Obtain preimage hash + let mut pre_imager = PreImageHelper::::default(); + let preimage_output = pre_imager.pre_image_hashes(CoinType::Sui, &args.input); + + assert_eq!(preimage_output.error, SigningError::OK); + assert_eq!(preimage_output.data_hash.to_hex(), args.tx_hash); + + // Step 3: Compile transaction info + + // Simulate external signing. + let key_pair = ed25519::sha512::KeyPair::try_from(args.private_key).unwrap(); + let public_key_bytes = key_pair.public().to_vec(); + let signature_bytes = key_pair + .sign(preimage_output.data_hash.to_vec()) + .unwrap() + .to_vec(); + + let mut compiler = CompilerHelper::::default(); + let output = compiler.compile( + CoinType::Sui, + &args.input, + vec![signature_bytes], + vec![public_key_bytes], + ); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.unsigned_tx, args.unsigned_tx_data); + assert_eq!(output.signature, args.signature); +} + +#[test] +fn test_sui_compile_transfer() { + let input = Proto::SigningInput { + signer: SENDER_54E80D76.into(), + ..transfer_d4ay9tdb::sui_transfer_input() + }; + + // Successfully broadcasted: https://suiscan.xyz/mainnet/tx/D4Ay9TdBJjXkGmrZSstZakpEWskEQHaWURP6xWPRXbAm + test_sui_compile_impl(SuiCompileArgs { + input, + private_key: PRIVATE_KEY_54E80D76, + tx_hash: transfer_d4ay9tdb::TX_HASH, + unsigned_tx_data: transfer_d4ay9tdb::UNSIGNED_TX, + signature: transfer_d4ay9tdb::SIGNATURE, + }); +} + +#[test] +fn test_sui_compile_direct_transfer() { + let direct = Proto::SignDirect { + unsigned_tx_msg: transfer_d4ay9tdb::UNSIGNED_TX.into(), + }; + + let input = Proto::SigningInput { + transaction_payload: TransactionType::sign_direct_message(direct), + private_key: PRIVATE_KEY_54E80D76.decode_hex().unwrap().into(), + ..Proto::SigningInput::default() + }; + + // Successfully broadcasted: https://suiscan.xyz/mainnet/tx/D4Ay9TdBJjXkGmrZSstZakpEWskEQHaWURP6xWPRXbAm + test_sui_compile_impl(SuiCompileArgs { + input, + private_key: PRIVATE_KEY_54E80D76, + tx_hash: transfer_d4ay9tdb::TX_HASH, + unsigned_tx_data: transfer_d4ay9tdb::UNSIGNED_TX, + signature: transfer_d4ay9tdb::SIGNATURE, + }); +} diff --git a/rust/tw_any_coin/tests/chains/sui/sui_sign.rs b/rust/tw_any_coin/tests/chains/sui/sui_sign.rs new file mode 100644 index 00000000000..bbc3f297e34 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/sui/sui_sign.rs @@ -0,0 +1,468 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::chains::sui::object_ref; +use crate::chains::sui::test_cases::{transfer_d4ay9tdb, PRIVATE_KEY_54E80D76, SENDER_54E80D76}; +use tw_any_coin::test_utils::sign_utils::AnySignerHelper; +use tw_coin_registry::coin_type::CoinType; +use tw_encoding::hex::DecodeHex; +use tw_proto::Common::Proto::SigningError; +use tw_proto::Sui::Proto::{self, mod_SigningInput::OneOftransaction_payload as TransactionType}; + +fn test_sign_direct_impl(unsigned_tx: &str, private_key: &str, expected_signature: &str) { + let direct = Proto::SignDirect { + unsigned_tx_msg: unsigned_tx.into(), + }; + + let input = Proto::SigningInput { + transaction_payload: TransactionType::sign_direct_message(direct), + private_key: private_key.decode_hex().unwrap().into(), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Sui, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.unsigned_tx, unsigned_tx); + assert_eq!(output.signature, expected_signature); +} + +#[test] +fn test_sui_sign_direct_transfer() { + let unsigned_tx = "AAACAAgQJwAAAAAAAAAgJZ/4B0q0Jcu0ifI24Y4I8D8aeFa998eih3vWT3OLUBUCAgABAQAAAQEDAAAAAAEBANV1rX8Y6UhGKlz2mPVk7zlKdSpx/sYkk6+KBVwBLA1QAQbywsjB2JZN8QGdZhbpcFcZvrq9kx2idVy5SM635olk7AIAAAAAAAAgYEVuxmf1zRBGdoDr+VDtMpIFF12s2Ua7I2ru1XyGF8/Vda1/GOlIRipc9pj1ZO85SnUqcf7GJJOvigVcASwNUAEAAAAAAAAA0AcAAAAAAAAA"; + let private_key = "3823dce5288ab55dd1c00d97e91933c613417fdb282a0b8b01a7f5f5a533b266"; + let expected_signature = "APxPduNVvHj2CcRcHOtiP2aBR9qP3vO2Cb0g12PI64QofDB6ks33oqe/i/iCTLcop2rBrkczwrayZuJOdi7gvwNqfN7sFqdcD/Z4e8I1YQlGkDMCK7EOgmydRDqfH8C9jg=="; + + test_sign_direct_impl(unsigned_tx, private_key, expected_signature); +} + +#[test] +fn test_sui_sign_direct_transfer_nft() { + let unsigned_tx = "AAAv0f6HrJCZ/1cuDVuxh1BL12XMeHxkKeZ7Js9grhcB0u8xtTvoOepOHAAAAAAAAAAgJvcpOSvKhM+tHPgGAnp5Pmc8l3wjZhVxK4/BrLu4YAgttQCskZzd41GsNuNxHYMsbbl2aSEnoKw8oAGf/LobCM7RxGurtPZtHAAAAAAAAAAgwk74iUAH9S+cGVXQxAydItvltZ3UK2L0vg1TYgDMPfABAAAAAAAAAOgDAAAAAAAA"; + let private_key = "3823dce5288ab55dd1c00d97e91933c613417fdb282a0b8b01a7f5f5a533b266"; + let expected_signature = "AI+KRy820ucibONQXbaVm53ixNWqRcqp16/aG0hvX7Mt3dOMqTDKRYoRBRvbMDsyPFmpS+n5iYvs5vuGdqjUvgBqfN7sFqdcD/Z4e8I1YQlGkDMCK7EOgmydRDqfH8C9jg=="; + + test_sign_direct_impl(unsigned_tx, private_key, expected_signature); +} + +#[test] +fn test_sui_sign_direct_move_call() { + let unsigned_tx = "AAIAAAAAAAAAAAAAAAAAAAAAAAAAAgEAAAAAAAAAINaXMihjlCd4CQVFRPjcNb7QfYP4wGgQyl1xbplvEKUCA3N1aQh0cmFuc2ZlcgACAQCdB6Mav5rHiXD0rAWTCxS+ENwxMBsAAAAAAAAAINqDfrJUZebPjUi7xcyR3QcQSA9tOLwxThgYaZ1vMfgfABQU2gJ3ToaOYd1F/R6mXryOZdvpRi21AKyRnN3jUaw243EdgyxtuXZppM+mSjYYEQWDcV/7hFRrAE0VtRwbAAAAAAAAACC5nJxYaYJfa9rfbxSikaEFVmHGuXyCIZoZbMpxMwLebAEAAAAAAAAA0AcAAAAAAAA="; + let private_key = "3823dce5288ab55dd1c00d97e91933c613417fdb282a0b8b01a7f5f5a533b266"; + let expected_signature = "AHoX1/mzUS8WQ+tNr0gXtfI7KFXjSbDlUxbGG2gkEh6L8FngU2KrsXsR1N8MzCXyJIz7+YvTfl5+Dh6AWSZC5wVqfN7sFqdcD/Z4e8I1YQlGkDMCK7EOgmydRDqfH8C9jg=="; + + test_sign_direct_impl(unsigned_tx, private_key, expected_signature); +} + +#[test] +fn test_sui_sign_direct_add_delegation() { + let unsigned_tx = "AAIAAAAAAAAAAAAAAAAAAAAAAAAAAgEAAAAAAAAAIEt/p6rXSTjdKP6wJOXyx0c2xsgJ4MJtfxe7qHC34u4UCnN1aV9zeXN0ZW0fcmVxdWVzdF9hZGRfZGVsZWdhdGlvbl9tdWxfY29pbgAEAQEAAAAAAAAAAAAAAAAAAAAAAAAABQEAAAAAAAAAAgIAGSkMV9AFc419O9dL1kez9tzVIOiXzAEAAAAAACAfIePlHHP/+iv++FWQW9ofkVm4S2sFwupGikSq8bNjYwAH1A26NKDn7pJfn9zWaDi1nbntMJfMAQAAAAAAIKfMZAZktdmw36jwg/jcK1TDmrHmSZ/fdkeInO3BSjfWAAkB0AcAAAAAAAAAFAej1I8mhjmcpQTQtiX2J2HZ7y2xLbUArJGc3eNRrDbjcR2DLG25dmlY2RBfTU/P+GL1qpxE8NQrwiLw/JfMAQAAAAAAICs2NJlowCmCnpJ+hja2VwZE5K6yGM/qw0MSRnn9tW2cbgAAAAAAAACghgEAAAAAAA=="; + let private_key = "3823dce5288ab55dd1c00d97e91933c613417fdb282a0b8b01a7f5f5a533b266"; + let expected_signature = "AMn4XpOcE9pX/VWCcue/tMkk+TxRQprGas53TT9W4beLkj6XuQdSNLSdjp9AmbqQPHKh0yJZ9i7Q2i6aax8NdQZqfN7sFqdcD/Z4e8I1YQlGkDMCK7EOgmydRDqfH8C9jg=="; + + test_sign_direct_impl(unsigned_tx, private_key, expected_signature); +} + +#[test] +fn test_sui_sign_transfer_sui() { + let input = Proto::SigningInput { + private_key: PRIVATE_KEY_54E80D76.decode_hex().unwrap().into(), + ..transfer_d4ay9tdb::sui_transfer_input() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Sui, input); + + assert_eq!(output.error, SigningError::OK); + // Successfully broadcasted: https://suiscan.xyz/mainnet/tx/D4Ay9TdBJjXkGmrZSstZakpEWskEQHaWURP6xWPRXbAm + assert_eq!(output.unsigned_tx, transfer_d4ay9tdb::UNSIGNED_TX); + assert_eq!(output.signature, transfer_d4ay9tdb::SIGNATURE); +} + +#[test] +fn test_sui_sign_split_sui() { + let pay_sui = Proto::PaySui { + input_coins: vec![object_ref( + "0x636020b3a7dc7b11c3aa6f419b17f8a9c12e7f79a31d1bdd2de670b4edd63005", + 85887685, + "GnzkqXxoowwtz1W33JrjwaW63FpnXmVo8DoVVWUwARyx", + )], + recipients: vec![ + SENDER_54E80D76.into(), + SENDER_54E80D76.into(), + SENDER_54E80D76.into(), + ], + amounts: vec![150_000, 200_000, 100_000], + }; + + let input = Proto::SigningInput { + transaction_payload: TransactionType::pay_sui(pay_sui), + private_key: PRIVATE_KEY_54E80D76.decode_hex().unwrap().into(), + // 0.007 SUI + gas_budget: 7000000, + reference_gas_price: 750, + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Sui, input); + + assert_eq!(output.error, SigningError::OK); + // Successfully broadcasted: https://suiscan.xyz/mainnet/tx/GNoQj54Ra8qGbzbvD25KXEYTsRDKTH5SSjLtHftGNwBM + assert_eq!(output.unsigned_tx, "AAAEAAjwSQIAAAAAAAAIQA0DAAAAAAAACKCGAQAAAAAAACBU6A1215DCd/WkTzzpL1PSb1iUiSvzld7mN1mIh2vmsgICAAMBAAABAQABAgABAwMAAAAAAwAAAQADAAACAAEDAFToDXbXkMJ39aRPPOkvU9JvWJSJK/OV3uY3WYiHa+ayAWNgILOn3HsRw6pvQZsX+KnBLn95ox0b3S3mcLTt1jAFxYoeBQAAAAAg6qe+uHxDnn7q4cupb3Z1reQK3m4sh6efYtcz8fWA6C9U6A1215DCd/WkTzzpL1PSb1iUiSvzld7mN1mIh2vmsu4CAAAAAAAAwM9qAAAAAAAA"); + assert_eq!(output.signature, "AAN/lP/bRRsgdDS/QCSl45D5gHdKv4Aow0Hmkcot6w+84vd2X+nvOgxyYo2BMInBIbsCqlOtnn8t9zo2+dNSegGF69FEH+T5VPvl3GB3vwCOEZpeJpKXxvcIPQAdKsh2/g=="); +} + +#[test] +fn test_sui_sign_merge_sui() { + // Coin type: `0x2::sui::SUI`. + let primary_coin = object_ref( + "0x102054b7676a46b1bae724134dc962db729f3389acf79d3d6f3c27ba018a0404", + 85887686, + "J3ZVMi8NUj2cbwB94dYFxTLoDJiBbLj156D7DmBUnVb2", + ); + let primary_coin_balance = 150000; + + // Coin type: `0x2::sui::SUI`. + let coin_to_merge = object_ref( + "0xf3b55a88fe631fdc778621c334941dd82453736fa02c7f6effd4441c38a805cc", + 85887686, + "AT7MeU611cvSpM7B2cQFd53Uiw7WtVwuoQcUcZjWbxou", + ); + let coin_to_merge_balance = 100000; + + let pay = Proto::Pay { + input_coins: vec![primary_coin, coin_to_merge], + recipients: vec![SENDER_54E80D76.into()], + gas: Some(object_ref( + "0x636020b3a7dc7b11c3aa6f419b17f8a9c12e7f79a31d1bdd2de670b4edd63005", + 85887691, + "JAdejLaC6f59Ko7SugLSuwVn55wVKYE2ukQae4nRJcm5", + )), + amounts: vec![primary_coin_balance + coin_to_merge_balance], + }; + + let input = Proto::SigningInput { + transaction_payload: TransactionType::pay(pay), + private_key: PRIVATE_KEY_54E80D76.decode_hex().unwrap().into(), + // 0.004 SUI + gas_budget: 4000000, + reference_gas_price: 750, + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Sui, input); + + assert_eq!(output.error, SigningError::OK); + + // Successfully broadcasted: https://suiscan.xyz/mainnet/tx/68wBKsZyYXmCUydDmabQ71kTcFWTfDG7tFmTLk1HgNdN + assert_eq!(output.unsigned_tx, "AAAEAQAQIFS3Z2pGsbrnJBNNyWLbcp8ziaz3nT1vPCe6AYoEBMaKHgUAAAAAIP0+kx97Pe9YDREgUkz6oiMWshB9Lmh378kj8zPFQKydAQDztVqI/mMf3HeGIcM0lB3YJFNzb6Asf27/1EQcOKgFzMaKHgUAAAAAIIxpeFeM16YQBcGe5g1g/yPrBg49nG7O3ONFnBMQpao8AAiQ0AMAAAAAAAAgVOgNdteQwnf1pE886S9T0m9YlIkr85Xe5jdZiIdr5rIDAwEAAAEBAQACAQAAAQECAAEBAwEAAAABAwBU6A1215DCd/WkTzzpL1PSb1iUiSvzld7mN1mIh2vmsgFjYCCzp9x7EcOqb0GbF/ipwS5/eaMdG90t5nC07dYwBcuKHgUAAAAAIP8OWIzz7zyhJZG6luM+fwC+wc/3IWtHtGWeD/6h5YNwVOgNdteQwnf1pE886S9T0m9YlIkr85Xe5jdZiIdr5rLuAgAAAAAAAAAJPQAAAAAAAA=="); + assert_eq!(output.signature, "AAjKOQKQuLYdWN798F50O0dtLtRWsAa6bl/C4xJHnJaEIpRbYdhlxRXXfcSDpB6/YI14YU5P+auk6KsFGOBZmg2F69FEH+T5VPvl3GB3vwCOEZpeJpKXxvcIPQAdKsh2/g=="); +} + +#[test] +fn test_sui_sign_transfer_all_sui() { + // Coin type: `0x2::sui::SUI`. + let pay_all_sui = Proto::PayAllSui { + input_coins: vec![ + object_ref( + "0x102054b7676a46b1bae724134dc962db729f3389acf79d3d6f3c27ba018a0404", + 85887692, + "AHRJoaqPDuBd9fUcJApoiGF94LAKWzXeUd4M6R3qQdCh", + ), + object_ref( + "0x636020b3a7dc7b11c3aa6f419b17f8a9c12e7f79a31d1bdd2de670b4edd63005", + 85887692, + "8Q8GsKYTwhUDVMEAuv98P52Wj6HMoQdZdrZNfgsVPStU", + ), + object_ref( + "0xf424e836c9158c53e7361d24ed81875ef06b988465cb75561e035f8f42f00d33", + 85887692, + "74iZSGGCqC61JtERAkXFbPAvWLDwT2fHSQFJbebMtcEJ", + ), + ], + recipient: "0xf887e7077017554511e736d43424363da946d8aa748225f6b054630a0b1c0ae5".into(), + }; + + let input = Proto::SigningInput { + transaction_payload: TransactionType::pay_all_sui(pay_all_sui), + private_key: PRIVATE_KEY_54E80D76.decode_hex().unwrap().into(), + // 0.004 SUI + gas_budget: 5000000, + reference_gas_price: 750, + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Sui, input); + + assert_eq!(output.error, SigningError::OK); + + // Successfully broadcasted: https://suiscan.xyz/mainnet/tx/3yNCCsiEFMyoNcsCniCcSQ9AFZY2WVoQWGa56fcd1nvh + assert_eq!(output.unsigned_tx, "AAABACD4h+cHcBdVRRHnNtQ0JDY9qUbYqnSCJfawVGMKCxwK5QEBAQABAABU6A1215DCd/WkTzzpL1PSb1iUiSvzld7mN1mIh2vmsgMQIFS3Z2pGsbrnJBNNyWLbcp8ziaz3nT1vPCe6AYoEBMyKHgUAAAAAIInt9ZC5H/D+LXQVrm5FMVRbXYYja9DzMY7xtj9fmTgOY2Ags6fcexHDqm9Bmxf4qcEuf3mjHRvdLeZwtO3WMAXMih4FAAAAACBt7mQv2i7T+meqMktoDf8lCK0rhyCHnv7MB+dzIwEWXfQk6DbJFYxT5zYdJO2Bh17wa5iEZct1Vh4DX49C8A0zzIoeBQAAAAAgWhna69UsKB/zNdrxzcL1x4N3cD4QnQllvgrYmNa0dqdU6A1215DCd/WkTzzpL1PSb1iUiSvzld7mN1mIh2vmsu4CAAAAAAAAQEtMAAAAAAAA"); + assert_eq!(output.signature, "AC+cq5DVVb97CpvtgbPer5tC1TpyItXPuvZsC7mQySyrVks/eymaovfZL62zCjtyjM2gpVGt2Hy8xDLIIb5YiAaF69FEH+T5VPvl3GB3vwCOEZpeJpKXxvcIPQAdKsh2/g=="); +} + +#[test] +fn test_sui_sign_transfer_token() { + let pay = Proto::Pay { + input_coins: vec![ + // Coin type: `0x1d58e26e85fbf9ee8596872686da75544342487f95b1773be3c9a49ab1061b19::suia_token::SUIA_TOKEN` + object_ref( + "0x69e007e40d4b64528c3d9e519e3d1f3e5c9c962870748171f7b94c168c868161", + 85619063, + "HwJaXWsUXZnd1G8cQVtRmWvBvPKGAZMoTryoJqazNoNK", + ), + ], + recipients: vec![ + "0xa7175abdd5ed92ebe3ad390db366c6a706478cdf517cde6cf98630065cda377a".into(), + ], + amounts: vec![123000], + // Coin type: `0x2::sui::SUI`. + gas: Some(object_ref( + "0x636020b3a7dc7b11c3aa6f419b17f8a9c12e7f79a31d1bdd2de670b4edd63005", + 85619065, + "9zB7kRVtKQcxCfi47z22u6vQGcUtnYFBXeiuaDfHUudr", + )), + }; + + let input = Proto::SigningInput { + transaction_payload: TransactionType::pay(pay), + private_key: PRIVATE_KEY_54E80D76.decode_hex().unwrap().into(), + // 0.003 SUI + gas_budget: 4000000, + reference_gas_price: 750, + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Sui, input); + + assert_eq!(output.error, SigningError::OK); + // Successfully broadcasted: https://suiscan.xyz/mainnet/tx/DYRXibkwsy84d9qdKktePE3gj9zN4yFpVx2ehv1uYMCo + assert_eq!(output.unsigned_tx, "AAADAQBp4AfkDUtkUow9nlGePR8+XJyWKHB0gXH3uUwWjIaBYXdxGgUAAAAAIPukOuVHEHFrj96jMXgsE8RmAU/Es2ejPAhD41bNXslEAAh44AEAAAAAAAAgpxdavdXtkuvjrTkNs2bGpwZHjN9RfN5s+YYwBlzaN3oCAgEAAAEBAQABAQMAAAAAAQIAVOgNdteQwnf1pE886S9T0m9YlIkr85Xe5jdZiIdr5rIBY2Ags6fcexHDqm9Bmxf4qcEuf3mjHRvdLeZwtO3WMAV5cRoFAAAAACCFgwpF3YPy9Px5R/K+WLDjMSiO7AsEa/4cNWn5P/nkIVToDXbXkMJ39aRPPOkvU9JvWJSJK/OV3uY3WYiHa+ay7gIAAAAAAAAACT0AAAAAAAA="); + assert_eq!(output.signature, "AAXK0so7TwO285ZhWKKRYs2MyFumsFSlOe4boampQKmrqhIZKhNnJKCTFEkarJTq5lIIvyTtgIu5S93gOsxN4guF69FEH+T5VPvl3GB3vwCOEZpeJpKXxvcIPQAdKsh2/g=="); +} + +#[test] +fn test_sui_sign_split_tokens() { + let pay = Proto::Pay { + input_coins: vec![ + // Coin type: `0xce7ff77a83ea0cb6fd39bd8748e2ec89a3f41e8efdc3f4eb123e0ca37b184db2::buck::BUCK` + object_ref( + "0xcc94319baba4c4a2f1988805952f1cf0edf9690a150976396491ba72f7ea06f4", + 85887684, + "5ErzJWYsjecyvjBSYg2CXPB76oqqJHCRarkYxsSYEf7c", + ), + ], + recipients: vec![ + SENDER_54E80D76.into(), + SENDER_54E80D76.into(), + SENDER_54E80D76.into(), + ], + amounts: vec![10000000, 7000000, 123], + // Coin type: `0x2::sui::SUI`. + gas: Some(object_ref( + "0x636020b3a7dc7b11c3aa6f419b17f8a9c12e7f79a31d1bdd2de670b4edd63005", + 85887686, + "HE58PPRBBwksCbB7DMG6RRUcHkR4uJEh5yq5Eax5g546", + )), + }; + + let input = Proto::SigningInput { + transaction_payload: TransactionType::pay(pay), + private_key: PRIVATE_KEY_54E80D76.decode_hex().unwrap().into(), + // 0.007 SUI + gas_budget: 7000000, + reference_gas_price: 750, + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Sui, input); + + assert_eq!(output.error, SigningError::OK); + // Successfully broadcasted: https://suiscan.xyz/mainnet/tx/6yp2AfESB3od1AMmS7Q6KDbLPJgjNrcTBYR4YSx9nYTR + assert_eq!(output.unsigned_tx, "AAAFAQDMlDGbq6TEovGYiAWVLxzw7flpChUJdjlkkbpy9+oG9MSKHgUAAAAAID770d4E8lYGVpgqgH7V6wveP0upByVv6GPKEis8+F1vAAiAlpgAAAAAAAAIwM9qAAAAAAAACHsAAAAAAAAAACBU6A1215DCd/WkTzzpL1PSb1iUiSvzld7mN1mIh2vmsgICAQAAAwEBAAECAAEDAAEDAwAAAAADAAABAAMAAAIAAQQAVOgNdteQwnf1pE886S9T0m9YlIkr85Xe5jdZiIdr5rIBY2Ags6fcexHDqm9Bmxf4qcEuf3mjHRvdLeZwtO3WMAXGih4FAAAAACDxFDVI2G12qWGuVXlDH+ENYODgVgpT6lVcFhrw27vlu1ToDXbXkMJ39aRPPOkvU9JvWJSJK/OV3uY3WYiHa+ay7gIAAAAAAADAz2oAAAAAAAA="); + assert_eq!(output.signature, "AGrbddwztZ+spZCG39obT6Qp+Yv35hXfIPOExVNXzkUpdr7NDMjEMS19BLT6rc811EFiMMVi65G4RmetXGOeUgWF69FEH+T5VPvl3GB3vwCOEZpeJpKXxvcIPQAdKsh2/g=="); +} + +/// Merge `primary_coin_id`, `coin_to_merge1` and `coin_to_merge2` coins into one coin. +/// Read the migration guide: https://blog.sui.io/sui-payment-transaction-types/ +#[test] +fn test_sui_sign_merge_tokens() { + // Coin type: `0xce7ff77a83ea0cb6fd39bd8748e2ec89a3f41e8efdc3f4eb123e0ca37b184db2::buck::BUCK` + let primary_coin = object_ref( + "0x7c91902ea14bc1e1a27358d7aa44f7ab9f10890642ae97d03b4e8a4c804662cd", + 85887687, + "DWJeDBNn5Uyb69E6xxoMZL2wupH9VaGxY3Pf7asJfRCQ", + ); + let primary_coin_balance = 10000000; + + // Coin type: `0xce7ff77a83ea0cb6fd39bd8748e2ec89a3f41e8efdc3f4eb123e0ca37b184db2::buck::BUCK` + let coin_to_merge1 = object_ref( + "0x3a5edd52deb7535dadb6cf92b9ed5e0d0eb959a6ce19ea075a3f7e1a8fe29070", + 85887687, + "79CmNvfmneL651e4ND2Kqje13ZJ2sbpGk6oXsa87TkQv", + ); + let coin_to_merge_balance1 = 7000000; + + // Coin type: `0xce7ff77a83ea0cb6fd39bd8748e2ec89a3f41e8efdc3f4eb123e0ca37b184db2::buck::BUCK` + let coin_to_merge2 = object_ref( + "0xcc94319baba4c4a2f1988805952f1cf0edf9690a150976396491ba72f7ea06f4", + 85887687, + "CZbcgfFMyUFmTErb9jewVVcThKPeLqEHk5SvoRtnihHD", + ); + let coin_to_merge_balance2 = 135116; + + let pay = Proto::Pay { + input_coins: vec![primary_coin, coin_to_merge1, coin_to_merge2], + recipients: vec![SENDER_54E80D76.into()], + amounts: vec![primary_coin_balance + coin_to_merge_balance1 + coin_to_merge_balance2], + // Coin type: `0x2::sui::SUI`. + gas: Some(object_ref( + "0x636020b3a7dc7b11c3aa6f419b17f8a9c12e7f79a31d1bdd2de670b4edd63005", + 85887687, + "GvBAtzvLkhaj2mpC4LnmTH7796zYfHLof4U5dpRStSbn", + )), + }; + + let input = Proto::SigningInput { + transaction_payload: TransactionType::pay(pay), + private_key: PRIVATE_KEY_54E80D76.decode_hex().unwrap().into(), + // 0.007 SUI + gas_budget: 7000000, + reference_gas_price: 750, + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Sui, input); + + assert_eq!(output.error, SigningError::OK); + // Successfully broadcasted: https://suiscan.xyz/mainnet/tx/EadSFJmRbfcJXjWsTwDZ1jUDonN6uWfjEgs79SuG2NCj + assert_eq!(output.unsigned_tx, "AAAFAQB8kZAuoUvB4aJzWNeqRPernxCJBkKul9A7TopMgEZizceKHgUAAAAAILnOCLChQW+Ka6TYqDCKxTKXk7bbxxfROgRAn9cAqdZ1AQA6Xt1S3rdTXa22z5K57V4NDrlZps4Z6gdaP34aj+KQcMeKHgUAAAAAIFtAEid8uKSWbaEakB3Qld75NYy9NE0uUtVG7z2Oj+mHAQDMlDGbq6TEovGYiAWVLxzw7flpChUJdjlkkbpy9+oG9MeKHgUAAAAAIKvKSBp+aB45dA6ogOr30c+9zJ0SI3c+/OczUGBIHJxkAAgMdgUBAAAAAAAgVOgNdteQwnf1pE886S9T0m9YlIkr85Xe5jdZiIdr5rIDAwEAAAIBAQABAgACAQAAAQEDAAEBAwEAAAABBABU6A1215DCd/WkTzzpL1PSb1iUiSvzld7mN1mIh2vmsgFjYCCzp9x7EcOqb0GbF/ipwS5/eaMdG90t5nC07dYwBceKHgUAAAAAIOx+ljTbUQkwMHYczKT1iN+DkOpvQYNfaLWlTcnznhGtVOgNdteQwnf1pE886S9T0m9YlIkr85Xe5jdZiIdr5rLuAgAAAAAAAMDPagAAAAAAAA=="); + assert_eq!(output.signature, "AIPDD+jY6aYK0bc2XhtvdSyygk9HKha9WdZjTcRxasDkAi6vq1/a43s9jTV7WuZ7otAXk21eu8zevJ+HB0MABwWF69FEH+T5VPvl3GB3vwCOEZpeJpKXxvcIPQAdKsh2/g=="); +} + +#[test] +fn test_sui_sign_delegate_sui() { + let add_stake = Proto::RequestAddStake { + // Coin type: `0x2::sui::SUI`. + coins: vec![ + object_ref( + "0xff1af62d35654956964437882b33d3256aad20214f18a234c62b5e258ca163ee", + 83160977, + "FAugxdfWPQrMu57mMc9FmgNSjkt613pixR6V5M9nashw", + ), + object_ref( + "0x5ef77d20c7d6745d3d9b5f69e7825aae733fa5c8a3f82f7192749e3169791c8c", + 85887695, + "F3JgSqdQJgzBsNnzJiYkr2XkjTEXmq7NEybixjEYrSf4", + ), + ], + // Do not specify the amount. + amount: Some(Proto::Amount { + // 1.00095 + amount: 1_000_000_000 + 1_000_000 - 50_000, + }), + // https://suiscan.xyz/mainnet/validator/0x61953ea72709eed72f4441dd944eec49a11b4acabfc8e04015e89c63be81b6ab/delegators + validator: "0x61953ea72709eed72f4441dd944eec49a11b4acabfc8e04015e89c63be81b6ab".into(), + // Coin type: `0x2::sui::SUI`. + gas: Some(object_ref( + "0x102054b7676a46b1bae724134dc962db729f3389acf79d3d6f3c27ba018a0404", + 85989207, + "GXGhEVNJGNBsvaTiLi85bGask5PbVXTZUKyN6CLR3N7D", + )), + }; + + let input = Proto::SigningInput { + transaction_payload: TransactionType::request_add_stake(add_stake), + private_key: PRIVATE_KEY_54E80D76.decode_hex().unwrap().into(), + // 0.009 SUI + gas_budget: 9000000, + reference_gas_price: 750, + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Sui, input); + + assert_eq!(output.error, SigningError::OK); + // Successfully broadcasted: https://suiscan.xyz/mainnet/tx/9CHdn8h68pnC7pKxFN7ABCCiufFkYQQ6EwFEQEPiz6bp + assert_eq!(output.unsigned_tx, "AAAFAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQEAAAAAAAAAAQEA/xr2LTVlSVaWRDeIKzPTJWqtICFPGKI0xiteJYyhY+6R7/QEAAAAACDSjWt6fM4gT8LU9OmUKUD0oeVAN3195wXyRgLAAkj/RgEAXvd9IMfWdF09m19p54JarnM/pcij+C9xknSeMWl5HIzPih4FAAAAACDQmsUAK2qhMxauQja6zUchci2O+VpXNpKHQPa5uzG92wAJAfBIqTsAAAAAACBhlT6nJwnu1y9EQd2UTuxJoRtKyr/I4EAV6JxjvoG2qwIFAAIBAQABAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMKc3VpX3N5c3RlbRpyZXF1ZXN0X2FkZF9zdGFrZV9tdWxfY29pbgAEAQAAAgAAAQMAAQQAVOgNdteQwnf1pE886S9T0m9YlIkr85Xe5jdZiIdr5rIBECBUt2dqRrG65yQTTcli23KfM4ms9509bzwnugGKBARXFyAFAAAAACDmoHkZ4Q2u0tMpkkJOmnK9WHxAXfwVxtKnoGoU3ZecTFToDXbXkMJ39aRPPOkvU9JvWJSJK/OV3uY3WYiHa+ay7gIAAAAAAABAVIkAAAAAAAA="); + assert_eq!(output.signature, "AF7oDeTkRQT23xGuW1WsILvm2FQIycaP6bvbTA8oQ8QJU75VQcJDTgEscfxfg8GAN60uzSLKVAJKXKOu8O6vugmF69FEH+T5VPvl3GB3vwCOEZpeJpKXxvcIPQAdKsh2/g=="); +} + +#[test] +fn test_sui_sign_undelegate_sui() { + let add_stake = Proto::RequestWithdrawStake { + // Coin type: `0x2::sui::SUI`. + staked_sui: Some(object_ref( + "0x4d3211b1569a24226d672b7de4edb08fa9e19b4f02dab89c9236e4fc8c4ab12d", + 86012336, + "559WJM2RXnQvPyLzRJpUj2bH9ZuZNxd48YJKwNXxtxMn", + )), + // Coin type: `0x2::sui::SUI`. + gas: Some(object_ref( + "0x102054b7676a46b1bae724134dc962db729f3389acf79d3d6f3c27ba018a0404", + 86012337, + "AhhZeVzEF8uGG8rRKv4h4J2MyeUXZvvDYEb1C64pyip7", + )), + }; + + let input = Proto::SigningInput { + transaction_payload: TransactionType::request_withdraw_stake(add_stake), + private_key: PRIVATE_KEY_54E80D76.decode_hex().unwrap().into(), + // 0.009 SUI + gas_budget: 9000000, + reference_gas_price: 750, + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Sui, input); + + assert_eq!(output.error, SigningError::OK); + // Successfully broadcasted: AwZgU1EoWoo2Zn72U119KRvdjkvUz8QXN3fedLnGXa4n + assert_eq!(output.unsigned_tx, "AAACAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQEAAAAAAAAAAQEATTIRsVaaJCJtZyt95O2wj6nhm08C2rickjbk/IxKsS2wcSAFAAAAACA8frAQitBlYHSw54BYKrEOpjPNXZtUQcp8CBCgeteO2QEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMKc3VpX3N5c3RlbRZyZXF1ZXN0X3dpdGhkcmF3X3N0YWtlAAIBAAABAQBU6A1215DCd/WkTzzpL1PSb1iUiSvzld7mN1mIh2vmsgEQIFS3Z2pGsbrnJBNNyWLbcp8ziaz3nT1vPCe6AYoEBLFxIAUAAAAAIJAmR388UsDK20u66hpL0Yo017timzGO1w9bTx3rP9fAVOgNdteQwnf1pE886S9T0m9YlIkr85Xe5jdZiIdr5rLuAgAAAAAAAEBUiQAAAAAAAA=="); + assert_eq!(output.signature, "ADbHKQ6TSvyrjll8YwIl+0/BnPR3YedjbLllydFcYL2gCt5AdX2wXZxbwfFgpLCMUPe2tGXzj09MkWsjxEM3XwqF69FEH+T5VPvl3GB3vwCOEZpeJpKXxvcIPQAdKsh2/g=="); +} + +#[test] +fn test_sui_sign_transfer_nft() { + let transfer_obj = Proto::TransferObject { + // NFT https://suiscan.xyz/mainnet/object/0x2e2355a7e5f857a67c237d27e5a2184f9c683f4275d54bf90dcc70f6117f4a03 + object: Some(object_ref( + "0x2e2355a7e5f857a67c237d27e5a2184f9c683f4275d54bf90dcc70f6117f4a03", + 86012337, + "2XTKVJGNZm7i6ZYGQ6ikZowFu855TpTUTip8JJ1jf1ch", + )), + recipient: "0xf887e7077017554511e736d43424363da946d8aa748225f6b054630a0b1c0ae5".into(), + gas: Some(object_ref( + "0x102054b7676a46b1bae724134dc962db729f3389acf79d3d6f3c27ba018a0404", + 87121030, + "5eWAHWYnidUinZFf3CWNCLSxsUr3c56VVVVofAgaP6bu", + )), + }; + + let input = Proto::SigningInput { + transaction_payload: TransactionType::transfer_object(transfer_obj), + private_key: PRIVATE_KEY_54E80D76.decode_hex().unwrap().into(), + // 0.004 SUI + gas_budget: 4000000, + reference_gas_price: 750, + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Sui, input); + + assert_eq!(output.error, SigningError::OK); + // Successfully broadcasted: https://suiscan.xyz/mainnet/tx/zJdcR77RiaMTzq1rURePQdcEFLEKBMUXiiUG2PyGWzR + assert_eq!(output.unsigned_tx, "AAACACD4h+cHcBdVRRHnNtQ0JDY9qUbYqnSCJfawVGMKCxwK5QEALiNVp+X4V6Z8I30n5aIYT5xoP0J11Uv5Dcxw9hF/SgOxcSAFAAAAACAWqN6yiNss1A1yjjz0hYuYwWdS3Dui2QSHjdKsQz08ZgEBAQEBAAEAAFToDXbXkMJ39aRPPOkvU9JvWJSJK/OV3uY3WYiHa+ayARAgVLdnakaxuuckE03JYttynzOJrPedPW88J7oBigQEhlwxBQAAAAAgRQo1hDoAiMbl2lgicyjy67PmKIWT5wccUlQMAfu84LxU6A1215DCd/WkTzzpL1PSb1iUiSvzld7mN1mIh2vmsu4CAAAAAAAAAAk9AAAAAAAA"); + assert_eq!(output.signature, "AIbNoo74XJ9EvfVCBVwM2YMht5qsPHSu4Cb61uzKq6g2tgh4dlhKpY9Shhw/hHjlNGcg590+PvXm4nlj/IWy6wGF69FEH+T5VPvl3GB3vwCOEZpeJpKXxvcIPQAdKsh2/g=="); +} diff --git a/rust/tw_any_coin/tests/chains/sui/test_cases.rs b/rust/tw_any_coin/tests/chains/sui/test_cases.rs new file mode 100644 index 00000000000..04b824f3261 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/sui/test_cases.rs @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::chains::sui::object_ref; +use tw_proto::Sui::Proto::{self, mod_SigningInput::OneOftransaction_payload as TransactionType}; + +pub(super) const PRIVATE_KEY_54E80D76: &str = + "7e6682f7bf479ef0f627823cffd4e1a940a7af33e5fb39d9e0f631d2ecc5daff"; +pub(super) const SENDER_54E80D76: &str = + "0x54e80d76d790c277f5a44f3ce92f53d26f5894892bf395dee6375988876be6b2"; + +/// Successfully broadcasted: https://suiscan.xyz/mainnet/tx/D4Ay9TdBJjXkGmrZSstZakpEWskEQHaWURP6xWPRXbAm +pub(super) mod transfer_d4ay9tdb { + use super::*; + + pub const UNSIGNED_TX: &str = "AAAEAAjoAwAAAAAAAAAIUMMAAAAAAAAAIKcXWr3V7ZLr4605DbNmxqcGR4zfUXzebPmGMAZc2jd6ACBU6A1215DCd/WkTzzpL1PSb1iUiSvzld7mN1mIh2vmsgMCAAIBAAABAQABAQMAAAAAAQIAAQEDAAABAAEDAFToDXbXkMJ39aRPPOkvU9JvWJSJK/OV3uY3WYiHa+ayAWNgILOn3HsRw6pvQZsX+KnBLn95ox0b3S3mcLTt1jAFeHEaBQAAAAAgGGuNnxrqusosgjP3gQ3jBjnhapGNBlcU0yTaupXpa0BU6A1215DCd/WkTzzpL1PSb1iUiSvzld7mN1mIh2vmsu4CAAAAAAAAwMYtAAAAAAAA"; + pub const SIGNATURE: &str = "AEh44B7iGArEHF1wOLAQJMLNgGnaIwn3gKPC92vtDJqITDETAM5z9plaxio1xomt6/cZReQ5FZaQsMC6l7E0BwmF69FEH+T5VPvl3GB3vwCOEZpeJpKXxvcIPQAdKsh2/g=="; + pub const TX_HASH: &str = "4c171457873befef70077461909ae40ac67bdad476b832b9c09b589cd698578f"; + + pub fn sui_transfer_input() -> Proto::SigningInput<'static> { + let pay_sui = Proto::PaySui { + input_coins: vec![object_ref( + "0x636020b3a7dc7b11c3aa6f419b17f8a9c12e7f79a31d1bdd2de670b4edd63005", + 85619064, + "2eKuWbZSVfpFVfg8FXY9wP6W5AFXnTchSoUdp7obyYZ5", + )], + recipients: vec![ + "0xa7175abdd5ed92ebe3ad390db366c6a706478cdf517cde6cf98630065cda377a".into(), + // Send some amount to self. + SENDER_54E80D76.into(), + ], + amounts: vec![1000, 50_000], + }; + + Proto::SigningInput { + transaction_payload: TransactionType::pay_sui(pay_sui), + // 0.003 SUI + gas_budget: 3000000, + reference_gas_price: 750, + ..Proto::SigningInput::default() + } + } +} diff --git a/rust/tw_any_coin/tests/coin_address_derivation_test.rs b/rust/tw_any_coin/tests/coin_address_derivation_test.rs index a0929cbec72..4eeb9ed7f01 100644 --- a/rust/tw_any_coin/tests/coin_address_derivation_test.rs +++ b/rust/tw_any_coin/tests/coin_address_derivation_test.rs @@ -152,6 +152,7 @@ fn test_coin_address_derivation() { CoinType::NativeZetaChain => "zeta14s0vgnj0pjnazu4hsqlksdk7slah9vcfcwctsr", CoinType::Dydx => "dydx1ten42eesehw0ktddcp0fws7d3ycsqez3kaamq3", CoinType::Solana => "5sn9QYhDaq61jLXJ8Li5BKqGL4DDMJQvU1rdN8XgVuwC", + CoinType::Sui => "0x1a5c6c1b74cec4fbd12b3e17252b83448136065afcdf24954dc3a9c26df4905", // end_of_coin_address_derivation_tests_marker_do_not_modify _ => panic!("{:?} must be covered", coin), }; diff --git a/rust/tw_aptos/Cargo.toml b/rust/tw_aptos/Cargo.toml deleted file mode 100644 index 38b0270b8f1..00000000000 --- a/rust/tw_aptos/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "tw_aptos" -version = "0.1.0" -edition = "2021" - -[dependencies] -serde_json = "1.0" -tw_coin_entry = { path = "../tw_coin_entry" } -tw_encoding = { path = "../tw_encoding" } -tw_keypair = { path = "../tw_keypair" } -tw_proto = { path = "../tw_proto" } -tw_number = { path = "../tw_number" } -tw_hash = { path = "../tw_hash" } -tw_memory = { path = "../tw_memory" } -move-core-types = { git = "https://github.com/move-language/move", rev = "ea70797099baea64f05194a918cebd69ed02b285", features = ["address32"] } -serde = { version = "1.0", features = ["derive"] } -serde_bytes = "0.11.12" - -[dev-dependencies] -tw_coin_entry = { path = "../tw_coin_entry", features = ["test-utils"] } -tw_encoding = { path = "../tw_encoding" } -tw_number = { path = "../tw_number", features = ["helpers"] } diff --git a/rust/tw_coin_registry/Cargo.toml b/rust/tw_coin_registry/Cargo.toml index 2359e4d6329..dfbbc4b7b0d 100644 --- a/rust/tw_coin_registry/Cargo.toml +++ b/rust/tw_coin_registry/Cargo.toml @@ -9,23 +9,24 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" strum = "0.25" strum_macros = "0.25" -tw_aptos = { path = "../tw_aptos" } +tw_aptos = { path = "../chains/tw_aptos" } tw_binance = { path = "../chains/tw_binance" } tw_bitcoin = { path = "../tw_bitcoin" } tw_coin_entry = { path = "../tw_coin_entry" } tw_cosmos = { path = "../chains/tw_cosmos" } -tw_ethereum = { path = "../tw_ethereum" } +tw_ethereum = { path = "../chains/tw_ethereum" } tw_evm = { path = "../tw_evm" } tw_greenfield = { path = "../chains/tw_greenfield" } tw_hash = { path = "../tw_hash" } -tw_internet_computer = { path = "../tw_internet_computer" } +tw_internet_computer = { path = "../chains/tw_internet_computer" } tw_keypair = { path = "../tw_keypair" } tw_memory = { path = "../tw_memory" } tw_misc = { path = "../tw_misc" } tw_native_evmos = { path = "../chains/tw_native_evmos" } tw_native_injective = { path = "../chains/tw_native_injective" } -tw_ronin = { path = "../tw_ronin" } +tw_ronin = { path = "../chains/tw_ronin" } tw_solana = { path = "../chains/tw_solana" } +tw_sui = { path = "../chains/tw_sui" } tw_thorchain = { path = "../chains/tw_thorchain" } [build-dependencies] diff --git a/rust/tw_coin_registry/src/blockchain_type.rs b/rust/tw_coin_registry/src/blockchain_type.rs index 19b7198ed90..3aff7a6d250 100644 --- a/rust/tw_coin_registry/src/blockchain_type.rs +++ b/rust/tw_coin_registry/src/blockchain_type.rs @@ -20,6 +20,7 @@ pub enum BlockchainType { NativeInjective, Ronin, Solana, + Sui, Thorchain, // end_of_blockchain_type - USED TO GENERATE CODE #[serde(other)] diff --git a/rust/tw_coin_registry/src/dispatcher.rs b/rust/tw_coin_registry/src/dispatcher.rs index c978e17f5c0..5a335fa5c3b 100644 --- a/rust/tw_coin_registry/src/dispatcher.rs +++ b/rust/tw_coin_registry/src/dispatcher.rs @@ -20,6 +20,7 @@ use tw_native_evmos::entry::NativeEvmosEntry; use tw_native_injective::entry::NativeInjectiveEntry; use tw_ronin::entry::RoninEntry; use tw_solana::entry::SolanaEntry; +use tw_sui::entry::SuiEntry; use tw_thorchain::entry::ThorchainEntry; pub type CoinEntryExtStaticRef = &'static dyn CoinEntryExt; @@ -37,6 +38,7 @@ const NATIVE_EVMOS: NativeEvmosEntry = NativeEvmosEntry; const NATIVE_INJECTIVE: NativeInjectiveEntry = NativeInjectiveEntry; const RONIN: RoninEntry = RoninEntry; const SOLANA: SolanaEntry = SolanaEntry; +const SUI: SuiEntry = SuiEntry; const THORCHAIN: ThorchainEntry = ThorchainEntry; // end_of_blockchain_entries - USED TO GENERATE CODE @@ -54,6 +56,7 @@ pub fn blockchain_dispatcher(blockchain: BlockchainType) -> RegistryResult Ok(&NATIVE_INJECTIVE), BlockchainType::Ronin => Ok(&RONIN), BlockchainType::Solana => Ok(&SOLANA), + BlockchainType::Sui => Ok(&SUI), BlockchainType::Thorchain => Ok(&THORCHAIN), // end_of_blockchain_dispatcher - USED TO GENERATE CODE BlockchainType::Unsupported => Err(RegistryError::Unsupported), diff --git a/rust/tw_encoding/src/bcs.rs b/rust/tw_encoding/src/bcs.rs index 5f15345bd66..8ed8257e43c 100644 --- a/rust/tw_encoding/src/bcs.rs +++ b/rust/tw_encoding/src/bcs.rs @@ -3,6 +3,7 @@ // Copyright © 2017 Trust Wallet. use crate::{EncodingError, EncodingResult}; +use serde::de::DeserializeOwned; use serde::Serialize; use tw_memory::Data; @@ -12,3 +13,10 @@ where { bcs::to_bytes(value).map_err(|_| EncodingError::InvalidInput) } + +pub fn decode(bytes: &[u8]) -> EncodingResult +where + T: DeserializeOwned, +{ + bcs::from_bytes(bytes).map_err(|_| EncodingError::InvalidInput) +} diff --git a/rust/tw_encoding/src/hex.rs b/rust/tw_encoding/src/hex.rs index aeed9dd751e..5db6e8038a3 100644 --- a/rust/tw_encoding/src/hex.rs +++ b/rust/tw_encoding/src/hex.rs @@ -3,7 +3,6 @@ // Copyright © 2017 Trust Wallet. pub use hex::FromHexError; -use serde::{Serialize, Serializer}; use tw_memory::Data; pub type FromHexResult = Result; @@ -64,13 +63,31 @@ pub fn encode>(data: T, prefixed: bool) -> String { encoded } -/// Serializes the `value` as a hex. -pub fn as_hex(value: &T, serializer: S) -> Result -where - T: ToHex, - S: Serializer, -{ - value.to_hex().serialize(serializer) +pub mod as_hex { + use super::*; + use serde::de::Error; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use std::fmt; + + /// Serializes the `value` as a hex. + pub fn serialize(value: &T, serializer: S) -> Result + where + T: ToHex, + S: Serializer, + { + value.to_hex().serialize(serializer) + } + + pub fn deserialize<'de, D, T, E>(deserializer: D) -> Result + where + D: Deserializer<'de>, + T: for<'a> TryFrom<&'a [u8], Error = E>, + E: fmt::Debug, + { + let s = String::deserialize(deserializer)?; + let data = decode(&s).map_err(|e| Error::custom(format!("{e:?}")))?; + T::try_from(&data).map_err(|e| Error::custom(format!("Error parsing from bytes: {e:?}"))) + } } #[cfg(test)] diff --git a/rust/tw_ethereum/Cargo.toml b/rust/tw_ethereum/Cargo.toml deleted file mode 100644 index 2b8f6554a62..00000000000 --- a/rust/tw_ethereum/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "tw_ethereum" -version = "0.1.0" -edition = "2021" - -[dependencies] -tw_coin_entry = { path = "../tw_coin_entry" } -tw_evm = { path = "../tw_evm" } -tw_keypair = { path = "../tw_keypair" } -tw_proto = { path = "../tw_proto" } - -[dev-dependencies] -tw_coin_entry = { path = "../tw_coin_entry", features = ["test-utils"] } -tw_encoding = { path = "../tw_encoding" } -tw_number = { path = "../tw_number", features = ["helpers"] } diff --git a/rust/tw_evm/src/modules/abi_encoder.rs b/rust/tw_evm/src/modules/abi_encoder.rs index 1e2b746897d..de70367d618 100644 --- a/rust/tw_evm/src/modules/abi_encoder.rs +++ b/rust/tw_evm/src/modules/abi_encoder.rs @@ -18,6 +18,7 @@ use std::borrow::Cow; use std::collections::HashMap; use std::marker::PhantomData; use std::str::FromStr; +use tw_encoding::hex::as_hex; use tw_hash::H32; use tw_misc::traits::ToBytesVec; use tw_number::{I256, U256}; @@ -88,7 +89,7 @@ impl AbiEncoder { let function = abi_json .map - .get_mut(&short_signature) + .get_mut(&ContractCallSignature(short_signature)) .ok_or(AbiError(AbiErrorKind::Error_abi_mismatch))?; let decoded_tokens = function.decode_input(encoded_data)?; @@ -451,9 +452,12 @@ impl AbiEncoder { #[derive(Deserialize)] struct SmartContractCallAbiJson { #[serde(flatten)] - map: HashMap, + map: HashMap, } +#[derive(Eq, Deserialize, Hash, PartialEq, Serialize)] +struct ContractCallSignature(#[serde(with = "as_hex")] H32); + #[derive(Serialize)] struct SmartContractCallDecodedInputJson<'a> { function: String, diff --git a/rust/tw_hash/src/hash_array.rs b/rust/tw_hash/src/hash_array.rs index 5732f56f8ff..80e2c1877e3 100644 --- a/rust/tw_hash/src/hash_array.rs +++ b/rust/tw_hash/src/hash_array.rs @@ -177,28 +177,25 @@ impl fmt::Display for Hash { } #[cfg(feature = "serde")] -mod impl_serde { +pub mod as_bytes { use super::Hash; use serde::de::Error; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use serde::{Deserialize, Deserializer, Serializer}; + use tw_memory::Data; - impl<'de, const N: usize> Deserialize<'de> for Hash { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let hex = String::deserialize(deserializer)?; - hex.parse().map_err(|e| Error::custom(format!("{e:?}"))) - } + pub fn deserialize<'de, const N: usize, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let bytes = Data::deserialize(deserializer)?; + Hash::::try_from(bytes.as_slice()).map_err(|e| Error::custom(format!("{e:?}"))) } - impl Serialize for Hash { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - self.to_string().serialize(serializer) - } + pub fn serialize(hash: &Hash, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_bytes(&hash.0) } } @@ -335,7 +332,9 @@ mod tests { #[cfg(all(test, feature = "serde"))] mod serde_tests { use super::*; + use serde::{Deserialize, Serialize}; use serde_json::json; + use tw_encoding::hex::as_hex; const BYTES_32: [u8; 32] = [ 175u8, 238, 252, 167, 77, 154, 50, 92, 241, 214, 182, 145, 29, 97, 166, 92, 50, 175, 168, @@ -343,27 +342,60 @@ mod serde_tests { ]; const HEX_32: &str = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"; + #[derive(Debug, Deserialize, Serialize)] + struct TestAsHex { + #[serde(with = "as_hex")] + data: Hash<32>, + } + + #[derive(Debug, Deserialize, Serialize)] + struct TestAsByteSequence { + #[serde(with = "as_byte_sequence")] + data: Hash<32>, + } + + #[test] + fn test_hash_deserialize_as_byte_sequence() { + let res: TestAsByteSequence = serde_json::from_value(json!({"data": BYTES_32})).unwrap(); + assert_eq!(res.data.0, BYTES_32); + } + #[test] - fn test_hash_deserialize() { - let unprefixed: Hash<32> = serde_json::from_value(json!(HEX_32)).unwrap(); - assert_eq!(unprefixed.0, BYTES_32); + fn test_hash_serialize_as_byte_sequence() { + let res = TestAsByteSequence { + data: Hash::<32>::from(BYTES_32), + }; + assert_eq!( + serde_json::to_value(&res).unwrap(), + json!({"data": BYTES_32}) + ); + } + + #[test] + fn test_hash_deserialize_as_hex() { + let unprefixed: TestAsHex = serde_json::from_value(json!({"data": HEX_32})).unwrap(); + + assert_eq!(unprefixed.data.0, BYTES_32); - let prefixed: Hash<32> = serde_json::from_value(json!(HEX_32)).unwrap(); - assert_eq!(prefixed.0, BYTES_32); + let prefixed: TestAsHex = + serde_json::from_value(json!({"data": format!("0x{HEX_32}")})).unwrap(); + assert_eq!(prefixed.data.0, BYTES_32); } #[test] - fn test_hash_deserialize_error() { - serde_json::from_value::>(json!( - "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45" - )) + fn test_hash_deserialize_as_hex_error() { + serde_json::from_value::( + json!({"data": "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45"}), + ) .unwrap_err(); } #[test] - fn test_hash_serialize() { - let hash = Hash::<32>::from(HEX_32); - let actual = serde_json::to_value(&hash).unwrap(); - assert_eq!(actual, json!(HEX_32)); + fn test_hash_serialize_as_hex() { + let test = TestAsHex { + data: Hash::<32>::from(HEX_32), + }; + let actual = serde_json::to_value(&test).unwrap(); + assert_eq!(actual, json!({"data": HEX_32})); } } diff --git a/rust/tw_hash/src/lib.rs b/rust/tw_hash/src/lib.rs index 48838e77569..0e83ce463b6 100644 --- a/rust/tw_hash/src/lib.rs +++ b/rust/tw_hash/src/lib.rs @@ -17,7 +17,7 @@ pub mod sha3; mod hash_array; mod hash_wrapper; -pub use hash_array::{as_byte_sequence, concat, Hash, H160, H256, H264, H32, H512, H520}; +pub use hash_array::{as_byte_sequence, as_bytes, concat, Hash, H160, H256, H264, H32, H512, H520}; use tw_encoding::hex::FromHexError; diff --git a/rust/tw_internet_computer/Cargo.toml b/rust/tw_internet_computer/Cargo.toml deleted file mode 100644 index e40261494e7..00000000000 --- a/rust/tw_internet_computer/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "tw_internet_computer" -version = "0.1.0" -edition = "2021" - -[dependencies] -quick-protobuf = "0.8.1" -serde = { version = "1.0", features = ["derive"] } -tw_coin_entry = { path = "../tw_coin_entry" } -tw_encoding = { path = "../tw_encoding" } -tw_hash = { path = "../tw_hash" } -tw_keypair = { path = "../tw_keypair" } -tw_memory = { path = "../tw_memory" } -tw_proto = { path = "../tw_proto" } - -[build-dependencies] -pb-rs = "0.10.0" diff --git a/rust/tw_keypair/src/ed25519/signature.rs b/rust/tw_keypair/src/ed25519/signature.rs index 43051c4325e..55592e50a6a 100644 --- a/rust/tw_keypair/src/ed25519/signature.rs +++ b/rust/tw_keypair/src/ed25519/signature.rs @@ -37,6 +37,9 @@ pub struct Signature { } impl Signature { + /// cbindgen:ignore + pub const LEN: usize = H512::LEN; + /// Returns the signature data (64 bytes). pub fn to_bytes(&self) -> H512 { let left = H256::from(self.R.to_bytes()); diff --git a/rust/tw_keypair/tests/ed25519_blake2b_tests.rs b/rust/tw_keypair/tests/ed25519_blake2b_tests.rs index 53e8e718d84..a8f4cacb02e 100644 --- a/rust/tw_keypair/tests/ed25519_blake2b_tests.rs +++ b/rust/tw_keypair/tests/ed25519_blake2b_tests.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use serde::Deserialize; -use tw_encoding::hex; +use tw_encoding::hex::{self, as_hex}; use tw_hash::{H256, H512}; use tw_keypair::ed25519::blake2b::KeyPair; use tw_keypair::traits::{SigningKeyTrait, VerifyingKeyTrait}; @@ -13,8 +13,10 @@ const ED25519_BLAKE2B_SIGN: &str = include_str!("ed25519_blake2b_sign.json"); #[derive(Deserialize)] struct Ed255191SignTest { + #[serde(with = "as_hex")] secret: H256, msg: String, + #[serde(with = "as_hex")] signature: H512, } diff --git a/rust/tw_keypair/tests/ed25519_extended_cardano_tests.rs b/rust/tw_keypair/tests/ed25519_extended_cardano_tests.rs index 42f2b17bca7..ed2e021b961 100644 --- a/rust/tw_keypair/tests/ed25519_extended_cardano_tests.rs +++ b/rust/tw_keypair/tests/ed25519_extended_cardano_tests.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use serde::Deserialize; -use tw_encoding::hex; +use tw_encoding::hex::{self, as_hex}; use tw_hash::H512; use tw_keypair::ed25519::cardano::ExtendedKeyPair; use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait, VerifyingKeyTrait}; @@ -18,6 +18,7 @@ const ED25519_EXTENDED_CARDANO_PRIV_TO_PUB: &str = struct Ed255191ExtendedCardanoSignTest { secret: String, msg: String, + #[serde(with = "as_hex")] signature: H512, } diff --git a/rust/tw_keypair/tests/ed25519_tests.rs b/rust/tw_keypair/tests/ed25519_tests.rs index 4b76afb5613..57dbe81c2e4 100644 --- a/rust/tw_keypair/tests/ed25519_tests.rs +++ b/rust/tw_keypair/tests/ed25519_tests.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use serde::Deserialize; -use tw_encoding::hex; +use tw_encoding::hex::{self, as_hex}; use tw_hash::{H256, H512}; use tw_keypair::ed25519::sha512::KeyPair; use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait, VerifyingKeyTrait}; @@ -15,14 +15,18 @@ const ED25519_PRIV_TO_PUB: &str = include_str!("ed25519_priv_to_pub.json"); #[derive(Deserialize)] struct Ed255191SignTest { + #[serde(with = "as_hex")] secret: H256, msg: String, + #[serde(with = "as_hex")] signature: H512, } #[derive(Deserialize)] struct Ed255191PrivToPubTest { + #[serde(with = "as_hex")] secret: H256, + #[serde(with = "as_hex")] public: H256, } diff --git a/rust/tw_keypair/tests/ed25519_waves_tests.rs b/rust/tw_keypair/tests/ed25519_waves_tests.rs index 7b39d226383..c231ee362b1 100644 --- a/rust/tw_keypair/tests/ed25519_waves_tests.rs +++ b/rust/tw_keypair/tests/ed25519_waves_tests.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use serde::Deserialize; -use tw_encoding::hex; +use tw_encoding::hex::{self, as_hex}; use tw_hash::{H256, H512}; use tw_keypair::ed25519::waves::KeyPair; use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait, VerifyingKeyTrait}; @@ -15,14 +15,18 @@ const ED25519_WAVES_PRIV_TO_PUB: &str = include_str!("ed25519_waves_priv_to_pub. #[derive(Deserialize)] struct Ed255191WavesSignTest { + #[serde(with = "as_hex")] secret: H256, msg: String, + #[serde(with = "as_hex")] signature: H512, } #[derive(Deserialize)] struct Ed255191WavesPrivToPubTest { + #[serde(with = "as_hex")] secret: H256, + #[serde(with = "as_hex")] public: H256, } diff --git a/rust/tw_keypair/tests/nist256p1_tests.rs b/rust/tw_keypair/tests/nist256p1_tests.rs index 8e60832de80..a3a603f9bd3 100644 --- a/rust/tw_keypair/tests/nist256p1_tests.rs +++ b/rust/tw_keypair/tests/nist256p1_tests.rs @@ -3,6 +3,7 @@ // Copyright © 2017 Trust Wallet. use serde::Deserialize; +use tw_encoding::hex::as_hex; use tw_hash::{H256, H264, H520}; use tw_keypair::ecdsa::nist256p1::{PrivateKey, PublicKey, VerifySignature}; use tw_keypair::traits::VerifyingKeyTrait; @@ -14,14 +15,19 @@ const NIST256P1_PRIV_TO_PUB_COMPRESSED: &str = #[derive(Deserialize)] struct Nist256p1VerifyTest { + #[serde(with = "as_hex")] public: H264, + #[serde(with = "as_hex")] msg: H256, + #[serde(with = "as_hex")] signature: H520, } #[derive(Deserialize)] struct Nist256p1PrivToPubCompressedTest { + #[serde(with = "as_hex")] secret: H256, + #[serde(with = "as_hex")] public: H264, } diff --git a/rust/tw_keypair/tests/secp256k1_tests.rs b/rust/tw_keypair/tests/secp256k1_tests.rs index f320cdcc32c..9e56c024862 100644 --- a/rust/tw_keypair/tests/secp256k1_tests.rs +++ b/rust/tw_keypair/tests/secp256k1_tests.rs @@ -3,6 +3,7 @@ // Copyright © 2017 Trust Wallet. use serde::Deserialize; +use tw_encoding::hex::as_hex; use tw_hash::{H256, H520}; use tw_keypair::ecdsa::secp256k1::{KeyPair, VerifySignature}; use tw_keypair::traits::{SigningKeyTrait, VerifyingKeyTrait}; @@ -12,8 +13,11 @@ const SECP256K1_SIGN: &str = include_str!("secp256k1_sign.json"); #[derive(Deserialize)] struct Secp256k1SignTest { + #[serde(with = "as_hex")] secret: H256, + #[serde(with = "as_hex")] hash: H256, + #[serde(with = "as_hex")] signature: H520, } diff --git a/rust/tw_ronin/Cargo.toml b/rust/tw_ronin/Cargo.toml deleted file mode 100644 index 98a13847890..00000000000 --- a/rust/tw_ronin/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "tw_ronin" -version = "0.1.0" -edition = "2021" - -[dependencies] -tw_coin_entry = { path = "../tw_coin_entry" } -tw_evm = { path = "../tw_evm" } -tw_keypair = { path = "../tw_keypair" } -tw_memory = { path = "../tw_memory" } -tw_proto = { path = "../tw_proto" } - -[dev-dependencies] -tw_coin_entry = { path = "../tw_coin_entry", features = ["test-utils"] } -tw_encoding = { path = "../tw_encoding" } -tw_number = { path = "../tw_number", features = ["helpers"] } diff --git a/rust/wallet_core_rs/Cargo.toml b/rust/wallet_core_rs/Cargo.toml index d0c43170fdf..eb148ecab18 100644 --- a/rust/wallet_core_rs/Cargo.toml +++ b/rust/wallet_core_rs/Cargo.toml @@ -35,7 +35,7 @@ tw_any_coin = { path = "../tw_any_coin", optional = true } tw_bitcoin = { path = "../tw_bitcoin", optional = true } tw_coin_registry = { path = "../tw_coin_registry", optional = true } tw_encoding = { path = "../tw_encoding", optional = true } -tw_ethereum = { path = "../tw_ethereum", optional = true } +tw_ethereum = { path = "../chains/tw_ethereum", optional = true } tw_hash = { path = "../tw_hash", optional = true } tw_keypair = { path = "../tw_keypair", optional = true } tw_memory = { path = "../tw_memory", optional = true } diff --git a/src/Sui/Address.cpp b/src/Sui/Address.cpp deleted file mode 100644 index bc432805e94..00000000000 --- a/src/Sui/Address.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "Address.h" -#include "HexCoding.h" - -namespace TW::Sui { - -Address::Address(const std::string& string) : Address::SuiAddress(string) { -} - -Address::Address(const PublicKey& publicKey): Address::SuiAddress(publicKey, TW::Hash::HasherBlake2b) { -} - -Data Address::getDigest(const PublicKey& publicKey) { - auto key_data = Data{0x00}; - append(key_data, publicKey.bytes); - return key_data; -} - -} // namespace TW::Sui diff --git a/src/Sui/Address.h b/src/Sui/Address.h deleted file mode 100644 index 74d5fd6ad27..00000000000 --- a/src/Sui/Address.h +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "Data.h" -#include "PublicKey.h" -#include "Move/Address.h" - -#include - -namespace TW::Sui { - -class Address : public Move::Address { -public: - using SuiAddress = Move::Address; - using SuiAddress::size; - using SuiAddress::bytes; - - /// Initializes an Sui address with a string representation. - explicit Address(const std::string& string); - - /// Initializes an Sui address with a public key. - explicit Address(const PublicKey& publicKey); - - /// Constructor that allow factory programming; - Address() noexcept = default; - - Data getDigest(const PublicKey& publicKey); -}; - -constexpr inline bool operator==(const Address& lhs, const Address& rhs) noexcept { - return lhs.bytes == rhs.bytes; -} - -} // namespace TW::Sui diff --git a/src/Sui/Entry.cpp b/src/Sui/Entry.cpp deleted file mode 100644 index cc537615924..00000000000 --- a/src/Sui/Entry.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "Entry.h" - -#include "Address.h" -#include "Signer.h" - -#include "proto/TransactionCompiler.pb.h" - -namespace TW::Sui { - -bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - return Address::isValid(address); -} - -std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { - return Address(publicKey).string(); -} - -void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { - signTemplate(dataIn, dataOut); -} - -Data Entry::preImageHashes([[maybe_unused]] TWCoinType coin, const Data& txInputData) const { - return txCompilerTemplate( - txInputData, [](const auto& input, auto& output) { - output = Signer::preImageHashes(input); - }); -} - -void Entry::compile([[maybe_unused]] TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const { - dataOut = txCompilerSingleTemplate( - txInputData, signatures, publicKeys, - [](const auto& input, auto& output, const auto& signature, const auto& publicKey) { - auto txSignatureScheme = Signer::signatureScheme(signature, publicKey); - output.set_unsigned_tx(input.sign_direct_message().unsigned_tx_msg()); - output.set_signature(txSignatureScheme); - }); -} - -} // namespace TW::Sui diff --git a/src/Sui/Entry.h b/src/Sui/Entry.h index 160520b6db7..86106198b80 100644 --- a/src/Sui/Entry.h +++ b/src/Sui/Entry.h @@ -4,17 +4,11 @@ #pragma once -#include "CoinEntry.h" +#include "rust/RustCoinEntry.h" namespace TW::Sui { -class Entry final : public CoinEntry { -public: - bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const override; - std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const override; - void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const override; - Data preImageHashes(TWCoinType coin, const Data& txInputData) const override; - void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const override; +class Entry final : public Rust::RustCoinEntry { }; } // namespace TW::Sui diff --git a/src/Sui/Signer.cpp b/src/Sui/Signer.cpp deleted file mode 100644 index 29984779b65..00000000000 --- a/src/Sui/Signer.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "Signer.h" -#include "Address.h" -#include "Base64.h" -#include "PublicKey.h" - -namespace { - -enum IntentScope : int { - TransactionData = 0, -}; - -enum IntentVersion : int { - V0 = 0, -}; - -enum IntentAppId { - Sui = 0 -}; - -} // namespace - -namespace TW::Sui { - -Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) { - auto protoOutput = Proto::SigningOutput(); - auto privateKey = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); - - auto toSign = transactionPreimage(input); - auto signature = privateKey.sign(TW::Hash::blake2b(toSign, 32), TWCurveED25519); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - auto txSignatureScheme = signatureScheme(signature, publicKey); - - auto unsignedTx = input.sign_direct_message().unsigned_tx_msg(); - protoOutput.set_unsigned_tx(unsignedTx); - protoOutput.set_signature(txSignatureScheme); - return protoOutput; -} - -TxCompiler::Proto::PreSigningOutput Signer::preImageHashes(const Proto::SigningInput& input) { - TxCompiler::Proto::PreSigningOutput output; - auto preImage = Signer::transactionPreimage(input); - auto preImageHash = TW::Hash::blake2b(preImage, 32); - output.set_data(preImage.data(), preImage.size()); - output.set_data_hash(preImageHash.data(), preImageHash.size()); - return output; -} - -Data Signer::transactionPreimage(const Proto::SigningInput& input) { - auto unsignedTx = input.sign_direct_message().unsigned_tx_msg(); - auto unsignedTxData = TW::Base64::decode(unsignedTx); - Data toSign{TransactionData, V0, IntentAppId::Sui}; - append(toSign, unsignedTxData); - return toSign; -} - -std::string Signer::signatureScheme(const Data& signature, const PublicKey& publicKey) { - Data signatureScheme{0x00}; - append(signatureScheme, signature); - append(signatureScheme, publicKey.bytes); - return TW::Base64::encode(signatureScheme); -} - -// Data - -} // namespace TW::Sui diff --git a/src/Sui/Signer.h b/src/Sui/Signer.h deleted file mode 100644 index a23f59454f9..00000000000 --- a/src/Sui/Signer.h +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "Data.h" -#include "PrivateKey.h" -#include "proto/Sui.pb.h" -#include "proto/TransactionCompiler.pb.h" - -namespace TW::Sui { - -/// Helper class that performs Sui transaction signing. -class Signer { -public: - /// Hide default constructor - Signer() = delete; - - /// Signs a Proto::SigningInput transaction - static Proto::SigningOutput sign(const Proto::SigningInput& input); - - static TxCompiler::Proto::PreSigningOutput preImageHashes(const Proto::SigningInput& input); - - /// Get transaction data to be signed (with a type tag). - static Data transactionPreimage(const Proto::SigningInput& input); - - static std::string signatureScheme(const Data& signature, const PublicKey& publicKey); -}; - -} // namespace TW::Sui diff --git a/src/proto/Sui.proto b/src/proto/Sui.proto index 5b11812db41..1efb8798b8c 100644 --- a/src/proto/Sui.proto +++ b/src/proto/Sui.proto @@ -9,20 +9,133 @@ option java_package = "wallet.core.jni.proto"; import "Common.proto"; +// Object info (including Coins). +message ObjectRef { + // Hex string representing the object ID. + string object_id = 1; + // Object version. + uint64 version = 2; + // Base58 string representing the object digest. + string object_digest = 3; +} + +// Optional amount. +message Amount { + uint64 amount = 1; +} + // Base64 encoded msg to sign (string) message SignDirect { // Obtain by calling any write RpcJson on SUI string unsigned_tx_msg = 1; } +// Send `Coin` to a list of addresses, where T can be any coin type, following a list of amounts. +// The object specified in the gas field will be used to pay the gas fee for the transaction. +// The gas object can not appear in input_coins. +// https://docs.sui.io/sui-api-ref#unsafe_pay +message Pay { + // The Sui coins to be used in this transaction, including the coin for gas payment. + repeated ObjectRef input_coins = 1; + + // The recipients' addresses, the length of this vector must be the same as amounts. + repeated string recipients = 2; + + // The amounts to be transferred to recipients, following the same order. + repeated uint64 amounts = 3; + + // Gas object to be used in this transaction. + ObjectRef gas = 4; +} + +// Send SUI coins to a list of addresses, following a list of amounts. +// This is for SUI coin only and does not require a separate gas coin object. +// https://docs.sui.io/sui-api-ref#unsafe_paysui +message PaySui { + // The Sui coins to be used in this transaction, including the coin for gas payment. + repeated ObjectRef input_coins = 1; + + // The recipients' addresses, the length of this vector must be the same as amounts. + repeated string recipients = 2; + + // The amounts to be transferred to recipients, following the same order. + repeated uint64 amounts = 3; +} + +// Send all SUI coins to one recipient. +// This is for SUI coin only and does not require a separate gas coin object. +// https://docs.sui.io/sui-api-ref#unsafe_payallsui +message PayAllSui { + // The Sui coins to be used in this transaction, including the coin for gas payment. + repeated ObjectRef input_coins = 1; + + // The recipient address. + string recipient = 2; +} + +// Add stake to a validator's staking pool using multiple coins and amount. +// https://docs.sui.io/sui-api-ref#unsafe_requestaddstake +message RequestAddStake { + // Coin objects to stake. + repeated ObjectRef coins = 1; + + // Optional stake amount. + Amount amount = 2; + + // The validator's Sui address. + string validator = 3; + + // Gas object to be used in this transaction. + ObjectRef gas = 4; +} + +// Withdraw stake from a validator's staking pool. +// https://docs.sui.io/sui-api-ref#unsafe_requestwithdrawstake +message RequestWithdrawStake { + // StakedSui object ID. + ObjectRef staked_sui = 1; + + // Gas object to be used in this transaction. + ObjectRef gas = 2; +} + +/// Transfer an object from one address to another. The object's type must allow public transfers. +/// https://docs.sui.io/sui-api-ref#unsafe_transferobject +message TransferObject { + // Object ID to be transferred. + ObjectRef object = 1; + + // The recipient address. + string recipient = 2; + + // Gas object to be used in this transaction. + ObjectRef gas = 3; +} + // Input data necessary to create a signed transaction. message SigningInput { - // Private key to sign the transaction (bytes) + // Private key to sign the transaction (bytes). bytes private_key = 1; + // Optional transaction signer. + // Needs to be set if no private key provided at `TransactionCompiler` module. + string signer = 2; + oneof transaction_payload { - SignDirect sign_direct_message = 2; + SignDirect sign_direct_message = 3; + Pay pay = 4; + PaySui pay_sui = 5; + PayAllSui pay_all_sui = 6; + RequestAddStake request_add_stake = 7; + RequestWithdrawStake request_withdraw_stake = 8; + TransferObject transfer_object = 9; } + + // The gas budget, the transaction will fail if the gas cost exceed the budget. + uint64 gas_budget = 12; + + // Reference gas price. + uint64 reference_gas_price = 13; } // Transaction signing output. diff --git a/swift/Tests/Blockchains/SuiTests.swift b/swift/Tests/Blockchains/SuiTests.swift index 7e8092cd805..868cb6947ec 100644 --- a/swift/Tests/Blockchains/SuiTests.swift +++ b/swift/Tests/Blockchains/SuiTests.swift @@ -18,7 +18,38 @@ class SuiTests: XCTestCase { XCTAssertFalse(AnyAddress.isValid(string: invalid, coin: .sui)) } - func testSign() { + func testSignDirect() { + // Successfully broadcasted: https://suiscan.xyz/mainnet/tx/D4Ay9TdBJjXkGmrZSstZakpEWskEQHaWURP6xWPRXbAm + let privateKeyData = Data(hexString: "7e6682f7bf479ef0f627823cffd4e1a940a7af33e5fb39d9e0f631d2ecc5daff")! + let txBytes = """ +AAAEAAjoAwAAAAAAAAAIUMMAAAAAAAAAIKcXWr3V7ZLr4605DbNmxqcGR4zfUXzebPmGMAZc2jd6ACBU6A1215DCd/WkTzzpL1PSb1iUiSvzld7mN1mIh2vmsgMCAAIBAAABAQABAQMAAAAAAQIAAQEDAAABAAEDAFToDXbXkMJ39aRPPOkvU9JvWJSJK/OV3uY3WYiHa+ayAWNgILOn3HsRw6pvQZsX+KnBLn95ox0b3S3mcLTt1jAFeHEaBQAAAAAgGGuNnxrqusosgjP3gQ3jBjnhapGNBlcU0yTaupXpa0BU6A1215DCd/WkTzzpL1PSb1iUiSvzld7mN1mIh2vmsu4CAAAAAAAAwMYtAAAAAAAA +""" + + let input = SuiSigningInput.with { + $0.paySui = SuiPaySui.with { + $0.inputCoins = [SuiObjectRef.with { + $0.objectID = "0x636020b3a7dc7b11c3aa6f419b17f8a9c12e7f79a31d1bdd2de670b4edd63005" + $0.version = 85619064 + $0.objectDigest = "2eKuWbZSVfpFVfg8FXY9wP6W5AFXnTchSoUdp7obyYZ5" + }] + $0.recipients = [ + "0xa7175abdd5ed92ebe3ad390db366c6a706478cdf517cde6cf98630065cda377a", + "0x54e80d76d790c277f5a44f3ce92f53d26f5894892bf395dee6375988876be6b2" + ] + $0.amounts = [1000, 50000] + } + $0.privateKey = privateKeyData + // 0.003 SUI + $0.gasBudget = 3000000 + $0.referenceGasPrice = 750 + } + let output: SuiSigningOutput = AnySigner.sign(input: input, coin: .sui) + XCTAssertEqual(output.unsignedTx, txBytes) + let expectedSignature = "AEh44B7iGArEHF1wOLAQJMLNgGnaIwn3gKPC92vtDJqITDETAM5z9plaxio1xomt6/cZReQ5FZaQsMC6l7E0BwmF69FEH+T5VPvl3GB3vwCOEZpeJpKXxvcIPQAdKsh2/g==" + XCTAssertEqual(output.signature, expectedSignature) + } + + func testTransferSui() { // Successfully broadcasted https://explorer.sui.io/txblock/HkPo6rYPyDY53x1MBszvSZVZyixVN7CHvCJGX381czAh?network=devnet let privateKeyData = Data(hexString: "3823dce5288ab55dd1c00d97e91933c613417fdb282a0b8b01a7f5f5a533b266")! let txBytes = """ diff --git a/tests/chains/Sui/AddressTests.cpp b/tests/chains/Sui/AddressTests.cpp deleted file mode 100644 index e42f6a5d55d..00000000000 --- a/tests/chains/Sui/AddressTests.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "HexCoding.h" -#include "Sui/Address.h" -#include "PublicKey.h" -#include "PrivateKey.h" -#include -#include - -namespace TW::Sui::tests { - -TEST(SuiAddress, Valid) { - ASSERT_TRUE(Address::isValid("0x1")); - // Address 32 are valid in SUI - ASSERT_TRUE(Address::isValid("0x259ff8074ab425cbb489f236e18e08f03f1a7856bdf7c7a2877bd64f738b5015")); - ASSERT_TRUE(Address::isValid("259ff8074ab425cbb489f236e18e08f03f1a7856bdf7c7a2877bd64f738b5015")); -} - -TEST(SuiAddress, Invalid) { - // Address 20 are invalid in SUI - ASSERT_FALSE(Address::isValid("0xb1dc06bd64d4e179a482b97bb68243f6c02c1b92")); - ASSERT_FALSE(Address::isValid("b1dc06bd64d4e179a482b97bb68243f6c02c1b92")); - // Too long - ASSERT_FALSE(Address::isValid("d575ad7f18e948462a5cf698f564ef394a752a71fec62493af8a055c012c0d502")); - // Too short - ASSERT_FALSE(Address::isValid("b1dc06bd64d4e179a482b97bb68243f6c02c1b9")); - // Invalid short address - ASSERT_FALSE(Address::isValid("0x11")); - // Invalid Hex - ASSERT_FALSE(Address::isValid("0xS59ff8074ab425cbb489f236e18e08f03f1a7856bdf7c7a2877bd64f738b5015")); -} - -TEST(SuiAddress, FromString) { - auto address = Address("259ff8074ab425cbb489f236e18e08f03f1a7856bdf7c7a2877bd64f738b5015"); - ASSERT_EQ(address.string(), "0x259ff8074ab425cbb489f236e18e08f03f1a7856bdf7c7a2877bd64f738b5015"); -} - -TEST(SuiAddress, FromPrivateKey) { - auto privateKey = PrivateKey(parse_hex("088baa019f081d6eab8dff5c447f9ce2f83c1babf3d03686299eaf6a1e89156e")); - auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeED25519)); - ASSERT_EQ(address.string(), "0x259ff8074ab425cbb489f236e18e08f03f1a7856bdf7c7a2877bd64f738b5015"); -} - -TEST(SuiAddress, FromPublicKey) { - auto publicKey = PublicKey(parse_hex("ad0e293a56c9fc648d1872a00521d97e6b65724519a2676c2c47cb95d131cf5a"), TWPublicKeyTypeED25519); - auto address = Address(publicKey); - ASSERT_EQ(address.string(), "0x259ff8074ab425cbb489f236e18e08f03f1a7856bdf7c7a2877bd64f738b5015"); -} - -} // namespace TW::Sui::tests diff --git a/tests/chains/Sui/CompilerTests.cpp b/tests/chains/Sui/CompilerTests.cpp index cbe887f78d5..b496546ff43 100644 --- a/tests/chains/Sui/CompilerTests.cpp +++ b/tests/chains/Sui/CompilerTests.cpp @@ -2,9 +2,10 @@ // // Copyright © 2017 Trust Wallet. -#include "Sui/Signer.h" #include "HexCoding.h" #include "PrivateKey.h" +#include "proto/Sui.pb.h" +#include "proto/TransactionCompiler.pb.h" #include "PublicKey.h" #include "TransactionCompiler.h" diff --git a/tests/chains/Sui/SignerTests.cpp b/tests/chains/Sui/SignerTests.cpp index eb61ce26ef0..ce801ca9113 100644 --- a/tests/chains/Sui/SignerTests.cpp +++ b/tests/chains/Sui/SignerTests.cpp @@ -2,11 +2,11 @@ // // Copyright © 2017 Trust Wallet. -#include "Sui/Signer.h" -#include "Sui/Address.h" #include "HexCoding.h" #include "PrivateKey.h" +#include "proto/Sui.pb.h" #include "PublicKey.h" +#include "TestUtilities.h" #include @@ -19,45 +19,11 @@ TEST(SuiSigner, Transfer) { input.mutable_sign_direct_message()->set_unsigned_tx_msg(txMsg); auto privateKey = PrivateKey(parse_hex("3823dce5288ab55dd1c00d97e91933c613417fdb282a0b8b01a7f5f5a533b266")); input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(result.unsigned_tx(), "AAACAAgQJwAAAAAAAAAgJZ/4B0q0Jcu0ifI24Y4I8D8aeFa998eih3vWT3OLUBUCAgABAQAAAQEDAAAAAAEBANV1rX8Y6UhGKlz2mPVk7zlKdSpx/sYkk6+KBVwBLA1QAQbywsjB2JZN8QGdZhbpcFcZvrq9kx2idVy5SM635olk7AIAAAAAAAAgYEVuxmf1zRBGdoDr+VDtMpIFF12s2Ua7I2ru1XyGF8/Vda1/GOlIRipc9pj1ZO85SnUqcf7GJJOvigVcASwNUAEAAAAAAAAA0AcAAAAAAAAA"); - ASSERT_EQ(result.signature(), "APxPduNVvHj2CcRcHOtiP2aBR9qP3vO2Cb0g12PI64QofDB6ks33oqe/i/iCTLcop2rBrkczwrayZuJOdi7gvwNqfN7sFqdcD/Z4e8I1YQlGkDMCK7EOgmydRDqfH8C9jg=="); -} - -TEST(SuiSigner, TransferNFT) { - // Successfully broadcasted https://explorer.sui.io/transaction/EmnhP9swuoijxYwHMywnXDGCXfFs1QxErsYoyWy9Y15J - Proto::SigningInput input; - std::string unsigned_tx = R"(AAAv0f6HrJCZ/1cuDVuxh1BL12XMeHxkKeZ7Js9grhcB0u8xtTvoOepOHAAAAAAAAAAgJvcpOSvKhM+tHPgGAnp5Pmc8l3wjZhVxK4/BrLu4YAgttQCskZzd41GsNuNxHYMsbbl2aSEnoKw8oAGf/LobCM7RxGurtPZtHAAAAAAAAAAgwk74iUAH9S+cGVXQxAydItvltZ3UK2L0vg1TYgDMPfABAAAAAAAAAOgDAAAAAAAA)"; - input.mutable_sign_direct_message()->set_unsigned_tx_msg(unsigned_tx); - auto privateKey = PrivateKey(parse_hex("3823dce5288ab55dd1c00d97e91933c613417fdb282a0b8b01a7f5f5a533b266")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(result.unsigned_tx(), unsigned_tx); - ASSERT_EQ(result.signature(), "AI+KRy820ucibONQXbaVm53ixNWqRcqp16/aG0hvX7Mt3dOMqTDKRYoRBRvbMDsyPFmpS+n5iYvs5vuGdqjUvgBqfN7sFqdcD/Z4e8I1YQlGkDMCK7EOgmydRDqfH8C9jg=="); -} - -TEST(SuiSigner, MoveCall) { - // Successfully broadcasted on: https://explorer.sui.io/transaction/3Gg8AcEfokDnA8m7W58ANmeCr8vkSaPWjXMp9sLMScTj - Proto::SigningInput input; - std::string unsigned_tx = R"(AAIAAAAAAAAAAAAAAAAAAAAAAAAAAgEAAAAAAAAAINaXMihjlCd4CQVFRPjcNb7QfYP4wGgQyl1xbplvEKUCA3N1aQh0cmFuc2ZlcgACAQCdB6Mav5rHiXD0rAWTCxS+ENwxMBsAAAAAAAAAINqDfrJUZebPjUi7xcyR3QcQSA9tOLwxThgYaZ1vMfgfABQU2gJ3ToaOYd1F/R6mXryOZdvpRi21AKyRnN3jUaw243EdgyxtuXZppM+mSjYYEQWDcV/7hFRrAE0VtRwbAAAAAAAAACC5nJxYaYJfa9rfbxSikaEFVmHGuXyCIZoZbMpxMwLebAEAAAAAAAAA0AcAAAAAAAA=)"; - input.mutable_sign_direct_message()->set_unsigned_tx_msg(unsigned_tx); - auto privateKey = PrivateKey(parse_hex("3823dce5288ab55dd1c00d97e91933c613417fdb282a0b8b01a7f5f5a533b266")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(result.unsigned_tx(), unsigned_tx); - ASSERT_EQ(result.signature(), "AHoX1/mzUS8WQ+tNr0gXtfI7KFXjSbDlUxbGG2gkEh6L8FngU2KrsXsR1N8MzCXyJIz7+YvTfl5+Dh6AWSZC5wVqfN7sFqdcD/Z4e8I1YQlGkDMCK7EOgmydRDqfH8C9jg=="); -} -TEST(SuiSigner, AddDelegation) { - // Successfully broadcasted on: https://explorer.sui.io/transaction/3Gg8AcEfokDnA8m7W58ANmeCr8vkSaPWjXMp9sLMScTj - Proto::SigningInput input; - std::string unsigned_tx = R"(AAIAAAAAAAAAAAAAAAAAAAAAAAAAAgEAAAAAAAAAIEt/p6rXSTjdKP6wJOXyx0c2xsgJ4MJtfxe7qHC34u4UCnN1aV9zeXN0ZW0fcmVxdWVzdF9hZGRfZGVsZWdhdGlvbl9tdWxfY29pbgAEAQEAAAAAAAAAAAAAAAAAAAAAAAAABQEAAAAAAAAAAgIAGSkMV9AFc419O9dL1kez9tzVIOiXzAEAAAAAACAfIePlHHP/+iv++FWQW9ofkVm4S2sFwupGikSq8bNjYwAH1A26NKDn7pJfn9zWaDi1nbntMJfMAQAAAAAAIKfMZAZktdmw36jwg/jcK1TDmrHmSZ/fdkeInO3BSjfWAAkB0AcAAAAAAAAAFAej1I8mhjmcpQTQtiX2J2HZ7y2xLbUArJGc3eNRrDbjcR2DLG25dmlY2RBfTU/P+GL1qpxE8NQrwiLw/JfMAQAAAAAAICs2NJlowCmCnpJ+hja2VwZE5K6yGM/qw0MSRnn9tW2cbgAAAAAAAACghgEAAAAAAA==)"; - input.mutable_sign_direct_message()->set_unsigned_tx_msg(unsigned_tx); - auto privateKey = PrivateKey(parse_hex("3823dce5288ab55dd1c00d97e91933c613417fdb282a0b8b01a7f5f5a533b266")); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto result = Signer::sign(input); - ASSERT_EQ(result.unsigned_tx(), unsigned_tx); - ASSERT_EQ(result.signature(), "AMn4XpOcE9pX/VWCcue/tMkk+TxRQprGas53TT9W4beLkj6XuQdSNLSdjp9AmbqQPHKh0yJZ9i7Q2i6aax8NdQZqfN7sFqdcD/Z4e8I1YQlGkDMCK7EOgmydRDqfH8C9jg=="); + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSui); + ASSERT_EQ(output.unsigned_tx(), "AAACAAgQJwAAAAAAAAAgJZ/4B0q0Jcu0ifI24Y4I8D8aeFa998eih3vWT3OLUBUCAgABAQAAAQEDAAAAAAEBANV1rX8Y6UhGKlz2mPVk7zlKdSpx/sYkk6+KBVwBLA1QAQbywsjB2JZN8QGdZhbpcFcZvrq9kx2idVy5SM635olk7AIAAAAAAAAgYEVuxmf1zRBGdoDr+VDtMpIFF12s2Ua7I2ru1XyGF8/Vda1/GOlIRipc9pj1ZO85SnUqcf7GJJOvigVcASwNUAEAAAAAAAAA0AcAAAAAAAAA"); + ASSERT_EQ(output.signature(), "APxPduNVvHj2CcRcHOtiP2aBR9qP3vO2Cb0g12PI64QofDB6ks33oqe/i/iCTLcop2rBrkczwrayZuJOdi7gvwNqfN7sFqdcD/Z4e8I1YQlGkDMCK7EOgmydRDqfH8C9jg=="); } } // namespace TW::Sui::tests diff --git a/tests/common/HDWallet/HDWalletTests.cpp b/tests/common/HDWallet/HDWalletTests.cpp index 32d68f6b006..355036071c6 100644 --- a/tests/common/HDWallet/HDWalletTests.cpp +++ b/tests/common/HDWallet/HDWalletTests.cpp @@ -8,7 +8,6 @@ #include "Bitcoin/SegwitAddress.h" #include "IoTeX/Address.h" #include "Cosmos/Address.h" -#include "Sui/Address.h" #include "Coin.h" #include "Ethereum/Address.h" #include "Ethereum/EIP2645.h" @@ -440,18 +439,6 @@ TEST(HDWallet, AptosKey) { } } -TEST(HDWallet, SuiKey) { - const auto derivPath = "m/44'/784'/0'/0'/0'"; - HDWallet wallet = HDWallet("cost add execute system fault long raccoon stone paddle column ketchup smile debate wood marble please jar can goddess magnet axis celery rough gold", ""); - { - const auto privateKey = wallet.getKey(TWCoinTypeSui, DerivationPath(derivPath)); - EXPECT_EQ(hex(privateKey.bytes), "3823dce5288ab55dd1c00d97e91933c613417fdb282a0b8b01a7f5f5a533b266"); - auto pubkey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - EXPECT_EQ(hex(pubkey.bytes), "6a7cdeec16a75c0ff6787bc2356109469033022bb10e826c9d443a9f1fc0bd8e"); - EXPECT_EQ(TW::Sui::Address(pubkey).string(), "0xd575ad7f18e948462a5cf698f564ef394a752a71fec62493af8a055c012c0d50"); - } -} - TEST(HDWallet, HederaKey) { // https://github.com/hashgraph/hedera-sdk-js/blob/e0cd39c84ab189d59a6bcedcf16e4102d7bb8beb/packages/cryptography/test/unit/Mnemonic.js#L47 { diff --git a/tests/common/TestUtilities.h b/tests/common/TestUtilities.h index 35949c7166d..28ccc30584d 100644 --- a/tests/common/TestUtilities.h +++ b/tests/common/TestUtilities.h @@ -4,6 +4,8 @@ #pragma once +#include +#include #include #include From 7a74dcb76c3d55510b643b436706933029a37578 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 17 Apr 2024 09:56:06 +0200 Subject: [PATCH 081/128] feat(FIO): Add support for "remaddress" action type (#3795) * [FIO]: Add support for "remaddress" action * [FIO]: Add `SigningOutput::action_name` parameter --- src/FIO/Action.cpp | 2 +- src/FIO/Action.h | 9 +- src/FIO/Signer.cpp | 4 +- src/FIO/TransactionBuilder.cpp | 90 ++++++++++++++----- src/FIO/TransactionBuilder.h | 30 ++++++- src/proto/FIO.proto | 17 ++++ tests/chains/FIO/SignerTests.cpp | 1 + tests/chains/FIO/TWFIOTests.cpp | 32 +++++++ tests/chains/FIO/TransactionBuilderTests.cpp | 6 +- tests/chains/FIO/TransactionCompilerTests.cpp | 3 +- 10 files changed, 165 insertions(+), 29 deletions(-) diff --git a/src/FIO/Action.cpp b/src/FIO/Action.cpp index 15b0b21906b..5be60a17866 100644 --- a/src/FIO/Action.cpp +++ b/src/FIO/Action.cpp @@ -41,7 +41,7 @@ void Action::serialize(Data& out) const { append(out, 0); // 00 } -void AddPubAddressData::serialize(Data& out) const { +void PubAddressActionData::serialize(Data& out) const { encodeString(fioAddress, out); addresses.serialize(out); encode64LE(fee, out); diff --git a/src/FIO/Action.h b/src/FIO/Action.h index 7e9fd4e56ea..1ebdc3eff91 100644 --- a/src/FIO/Action.h +++ b/src/FIO/Action.h @@ -81,8 +81,11 @@ class Action { void serialize(Data& out) const; }; -/// AddPubAddress action data part. -class AddPubAddressData { +/// A public address action data part. +/// Can be used for `addaddress`, `remaddress` actions. +/// https://dev.fio.net/reference/add_pub_address +/// https://dev.fio.net/reference/remove_pub_address +class PubAddressActionData { public: std::string fioAddress; PublicAddresses addresses; @@ -90,7 +93,7 @@ class AddPubAddressData { std::string tpid; std::string actor; - AddPubAddressData(const std::string& fioAddress, const std::vector& addresses, + PubAddressActionData(const std::string& fioAddress, const std::vector& addresses, uint64_t fee, const std::string& tpid, const std::string& actor) : fioAddress(fioAddress), addresses(addresses), fee(fee), tpid(tpid), actor(actor) {} diff --git a/src/FIO/Signer.cpp b/src/FIO/Signer.cpp index d4e5361c318..440e0c5535a 100644 --- a/src/FIO/Signer.cpp +++ b/src/FIO/Signer.cpp @@ -23,9 +23,11 @@ using namespace std; Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { FIO::Proto::SigningOutput output; - try { + try { + const string actionName = TransactionBuilder::actionName(input); const string json = TransactionBuilder::sign(input); output.set_json(json); + output.set_action_name(actionName); } catch(const std::exception& e) { output.set_error(Common::Proto::Error_internal); } diff --git a/src/FIO/TransactionBuilder.cpp b/src/FIO/TransactionBuilder.cpp index e9881d1eb71..bf5ca8a8302 100644 --- a/src/FIO/TransactionBuilder.cpp +++ b/src/FIO/TransactionBuilder.cpp @@ -21,6 +21,12 @@ using namespace TW; using namespace std; using json = nlohmann::json; +static constexpr auto gRegisterFioAddress = "regaddress"; +static constexpr auto gAddPubAddress = "addaddress"; +static constexpr auto gRemoveAddress = "remaddress"; +static constexpr auto gTransferFIOPubkey = "trnsfiopubky"; +static constexpr auto gRenewFIOAddress = "renewaddress"; +static constexpr auto gNewFundsRequest = "newfundsreq"; /// Internal helper ChainParams getChainParams(const Proto::SigningInput& input) { @@ -38,6 +44,25 @@ bool TransactionBuilder::expirySetDefaultIfNeeded(uint32_t& expiryTime) { return true; } +string TransactionBuilder::actionName(const Proto::SigningInput& input) { + switch (input.action().message_oneof_case()) { + case Proto::Action::MessageOneofCase::kRegisterFioAddressMessage: + return gRegisterFioAddress; + case Proto::Action::MessageOneofCase::kAddPubAddressMessage: + return gAddPubAddress; + case Proto::Action::MessageOneofCase::kTransferMessage: + return gTransferFIOPubkey; + case Proto::Action::MessageOneofCase::kRenewFioAddressMessage: + return gRenewFIOAddress; + case Proto::Action::MessageOneofCase::kNewFundsRequestMessage: + return gNewFundsRequest; + case Proto::Action::MessageOneofCase::kRemovePubAddressMessage: + return gRemoveAddress; + default: + return {}; + } +} + string TransactionBuilder::sign(Proto::SigningInput in) { PrivateKey privateKey(in.private_key()); PublicKey publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); @@ -76,6 +101,16 @@ string TransactionBuilder::sign(Proto::SigningInput in) { action.payer_fio_name(), action.payer_fio_address(), action.payee_fio_name(), content.payee_public_address(), content.amount(), content.coin_symbol(), content.memo(), content.hash(), content.offline_url(), getChainParams(in), action.fee(), in.tpid(), in.expiry(), Data()); + } else if (in.action().has_remove_pub_address_message()) { + const auto action = in.action().remove_pub_address_message(); + // process addresses + std::vector> addresses; + for (int i = 0; i < action.public_addresses_size(); ++i) { + addresses.emplace_back(std::make_pair(action.public_addresses(i).coin_symbol(), action.public_addresses(i).address())); + } + json = TransactionBuilder::createRemovePubAddress(owner, privateKey, + action.fio_address(), addresses, + getChainParams(in), action.fee(), in.tpid(), in.expiry()); } return json; } @@ -96,7 +131,19 @@ string TransactionBuilder::createAddPubAddress(const Address& address, const Pri const vector>& pubAddresses, const ChainParams& chainParams, uint64_t fee, const string& walletTpId, uint32_t expiryTime) { - Transaction transaction = TransactionBuilder::buildUnsignedAddPubAddress(address, fioName, pubAddresses, chainParams, fee, walletTpId, expiryTime); + Transaction transaction = TransactionBuilder::buildUnsignedPubAddressAction(gAddPubAddress, address, fioName, pubAddresses, chainParams, fee, walletTpId, expiryTime); + + Data serTx; + transaction.serialize(serTx); + + return signAndBuildTx(chainParams.chainId, serTx, privateKey); +} + +string TransactionBuilder::createRemovePubAddress(const Address& address, const PrivateKey& privateKey, const string& fioName, + const vector>& pubAddresses, + const ChainParams& chainParams, uint64_t fee, const string& walletTpId, uint32_t expiryTime) { + + Transaction transaction = TransactionBuilder::buildUnsignedPubAddressAction(gRemoveAddress, address, fioName, pubAddresses, chainParams, fee, walletTpId, expiryTime); Data serTx; transaction.serialize(serTx); @@ -134,8 +181,6 @@ string TransactionBuilder::createNewFundsRequest(const Address& address, const P const ChainParams& chainParams, uint64_t fee, const string& walletTpId, uint32_t expiryTime, const Data& iv) { - const auto* const apiName = "newfundsreq"; - // use coinSymbol for chainCode as well NewFundsContent newFundsContent { payeePublicAddress, amount, coinSymbol, coinSymbol, memo, hash, offlineUrl }; // serialize and encrypt @@ -154,7 +199,7 @@ string TransactionBuilder::createNewFundsRequest(const Address& address, const P Action action; action.account = ContractPayRequest; - action.name = apiName; + action.name = gNewFundsRequest; action.actionDataSer = serData; action.auth.authArray.push_back(Authorization{actor, AuthrizationActive}); @@ -207,7 +252,7 @@ Data TransactionBuilder::buildUnsignedTxBytes(const Proto::SigningInput& in) { for (int i = 0; i < action.public_addresses_size(); ++i) { addresses.emplace_back(std::make_pair(action.public_addresses(i).coin_symbol(), action.public_addresses(i).address())); } - transaction = TransactionBuilder::buildUnsignedAddPubAddress(owner, action.fio_address(), addresses, + transaction = TransactionBuilder::buildUnsignedPubAddressAction(gAddPubAddress, owner, action.fio_address(), addresses, getChainParams(in), action.fee(), in.tpid(), in.expiry()); } else if (in.action().has_transfer_message()) { const auto action = in.action().transfer_message(); @@ -217,7 +262,16 @@ Data TransactionBuilder::buildUnsignedTxBytes(const Proto::SigningInput& in) { const auto action = in.action().renew_fio_address_message(); transaction = TransactionBuilder::buildUnsignedRenewFioAddress(owner, action.fio_address(), getChainParams(in), action.fee(), in.tpid(), in.expiry()); - } + } else if (in.action().has_remove_pub_address_message()) { + const auto action = in.action().remove_pub_address_message(); + // process addresses + std::vector> addresses; + for (int i = 0; i < action.public_addresses_size(); ++i) { + addresses.emplace_back(std::make_pair(action.public_addresses(i).coin_symbol(), action.public_addresses(i).address())); + } + transaction = TransactionBuilder::buildUnsignedPubAddressAction(gRemoveAddress, owner, action.fio_address(), addresses, + getChainParams(in), action.fee(), in.tpid(), in.expiry()); + } Data serTx; transaction.serialize(serTx); @@ -239,12 +293,11 @@ Proto::SigningOutput TransactionBuilder::buildSigningOutput(const Proto::Signing }; output.set_json(tx.dump()); + output.set_action_name(actionName(input)); return output; } Transaction TransactionBuilder::buildUnsignedRegisterFioAddress(const Address& address, const std::string& fioName, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime){ - const auto* const apiName = "regaddress"; - string actor = Actor::actor(address); RegisterFioAddressData raData(fioName, address.string(), fee, walletTpId, actor); Data serData; @@ -252,7 +305,7 @@ Transaction TransactionBuilder::buildUnsignedRegisterFioAddress(const Address& a Action action; action.account = ContractAddress; - action.name = apiName; + action.name = gRegisterFioAddress; action.actionDataSer = serData; action.auth.authArray.push_back(Authorization{actor, AuthrizationActive}); @@ -263,8 +316,9 @@ Transaction TransactionBuilder::buildUnsignedRegisterFioAddress(const Address& a return tx; } -Transaction TransactionBuilder::buildUnsignedAddPubAddress(const Address& address, const std::string& fioName, const std::vector>& pubAddresses, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) { - const auto* const apiName = "addaddress"; +Transaction TransactionBuilder::buildUnsignedPubAddressAction(const std::string& apiName, const Address& address, + const std::string& fioName, const std::vector>& pubAddresses, + const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) { string actor = Actor::actor(address); // convert addresses to add chainCode -- set it equal to coinSymbol @@ -272,10 +326,10 @@ Transaction TransactionBuilder::buildUnsignedAddPubAddress(const Address& addres for (const auto& a: pubAddresses) { pubAddresses2.push_back(PublicAddress{a.first, a.first, a.second}); } - AddPubAddressData aaData(fioName, pubAddresses2, fee, walletTpId, actor); + PubAddressActionData actionData(fioName, pubAddresses2, fee, walletTpId, actor); Data serData; - aaData.serialize(serData); - + actionData.serialize(serData); + Action action; action.account = ContractAddress; action.name = apiName; @@ -290,8 +344,6 @@ Transaction TransactionBuilder::buildUnsignedAddPubAddress(const Address& addres } Transaction TransactionBuilder::buildUnsignedTransfer(const Address& address, const std::string& payeePublicKey, uint64_t amount, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) { - const auto* const apiName = "trnsfiopubky"; - string actor = Actor::actor(address); TransferData ttData(payeePublicKey, amount, fee, walletTpId, actor); Data serData; @@ -299,7 +351,7 @@ Transaction TransactionBuilder::buildUnsignedTransfer(const Address& address, co Action action; action.account = ContractToken; - action.name = apiName; + action.name = gTransferFIOPubkey; action.actionDataSer = serData; action.auth.authArray.push_back(Authorization{actor, AuthrizationActive}); @@ -311,8 +363,6 @@ Transaction TransactionBuilder::buildUnsignedTransfer(const Address& address, co } Transaction TransactionBuilder::buildUnsignedRenewFioAddress(const Address& address, const std::string& fioName, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) { - const auto* const apiName = "renewaddress"; - string actor = Actor::actor(address); RenewFioAddressData raData(fioName, fee, walletTpId, actor); Data serData; @@ -320,7 +370,7 @@ Transaction TransactionBuilder::buildUnsignedRenewFioAddress(const Address& addr Action action; action.account = ContractAddress; - action.name = apiName; + action.name = gRenewFIOAddress; action.actionDataSer = serData; action.auth.authArray.push_back(Authorization{actor, AuthrizationActive}); diff --git a/src/FIO/TransactionBuilder.h b/src/FIO/TransactionBuilder.h index 8bd7061db72..735bf38d834 100644 --- a/src/FIO/TransactionBuilder.h +++ b/src/FIO/TransactionBuilder.h @@ -38,6 +38,9 @@ class TransactionBuilder { /// Generic transaction signer: Build a signed transaction, in Json, from the specific SigningInput messages. static std::string sign(Proto::SigningInput in); + /// Returns an action name according to the given signing input. + static std::string actionName(const Proto::SigningInput& input); + /// Create a signed RegisterFioAddress transaction, returned as json string (double quote delimited), suitable for register_fio_address RPC call /// @address The owners' FIO address. Ex.: "FIO6m1fMdTpRkRBnedvYshXCxLFiC5suRU8KDfx8xxtXp2hntxpnf" /// @privateKey The private key matching the address, needed for signing. @@ -64,6 +67,20 @@ class TransactionBuilder { const std::vector>& pubAddresses, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); + /// Create a signed `remaddress` transaction, returned as json string (double quote delimited), suitable for remove_pub_address RPC call + /// @address The owners' FIO address + /// @privateKey The private key matching the address, needed for signing. + /// @fioName The FIO name already registered to the owner. Ex.: "dmitry@trust" + /// @addressess List of public addresses to be registered, ex. {{"BTC", "bc1qv...7v"},{"BNB", "bnb1ts3...9s"}} + /// @chainParams Current parameters from the FIO chain, must be obtained recently using get_info and get_block calls. + /// @fee Max fee to spend, can be obtained using get_fee API. + /// @walletTpId The FIO name of the originating wallet (project-wide constant) + /// @expiryTime Expiry for this message, can be 0, then it is taken from current time with default expiry + /// Note: fee is usually 0 for remove_pub_address. + static std::string createRemovePubAddress(const Address& address, const PrivateKey& privateKey, const std::string& fioName, + const std::vector>& pubAddresses, + const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); + /// Create a signed TransferTokens transaction, returned as json string (double quote delimited), suitable for transfer_tokens_pub_key RPC call /// @address The owners' FIO address /// @privateKey The private key matching the address, needed for signing. @@ -131,8 +148,17 @@ class TransactionBuilder { static Transaction buildUnsignedRegisterFioAddress(const Address& address, const std::string& fioName, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); - static Transaction buildUnsignedAddPubAddress(const Address& address, const std::string& fioName, - const std::vector>& pubAddresses, + /// Builds an unsigned transaction to perform an action over public addresses, e.g. adding or removing public addresses. + /// @apiName The action API name, ex. "addaddress", "remaddress". + /// @address The owners' FIO address. + /// @fioName The FIO name already registered to the owner. Ex.: "dmitry@trust" + /// @pubAddresses List of public addresses to be registered, ex. {{"BTC", "bc1qv...7v"},{"BNB", "bnb1ts3...9s"}} + /// @chainParams Current parameters from the FIO chain, must be obtained recently using get_info and get_block calls. + /// @fee Max fee to spend, can be obtained using get_fee API. + /// @walletTpId The FIO name of the originating wallet (project-wide constant) + /// @expiryTime Expiry for this message, can be 0, then it is taken from current time with default expiry + static Transaction buildUnsignedPubAddressAction(const std::string& apiName, const Address& address, + const std::string& fioName, const std::vector>& pubAddresses, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); static Transaction buildUnsignedTransfer(const Address& address, const std::string& payeePublicKey, uint64_t amount, diff --git a/src/proto/FIO.proto b/src/proto/FIO.proto index 9af884ddf9b..9fdb61b4c70 100644 --- a/src/proto/FIO.proto +++ b/src/proto/FIO.proto @@ -64,6 +64,19 @@ message Action { uint64 fee = 3; } + // Action for removing public chain addresses from a FIO name; remove_pub_address + // Note: actor is not needed, computed from private key + message RemovePubAddress { + // The FIO name already registered to the owner. Ex.: "alice@trust" + string fio_address = 1; + + // List of public addresses to be unregistered, ex. {{"BTC", "bc1qv...7v"},{"BNB", "bnb1ts3...9s"}} + repeated PublicAddress public_addresses = 2; + + // Max fee to spend, can be obtained using get_fee API. + uint64 fee = 3; + } + // Action for transferring FIO coins; transfer_tokens_pub_key // Note: actor is not needed, computed from private key message Transfer { @@ -116,6 +129,7 @@ message Action { Transfer transfer_message = 3; RenewFioAddress renew_fio_address_message = 4; NewFundsRequest new_funds_request_message = 5; + RemovePubAddress remove_pub_address_message = 6; } } @@ -162,4 +176,7 @@ message SigningOutput { // error code description string error_message = 3; + + // Performed action name, ex. "addaddress", "remaddress", "trnsfiopubky" etc. + string action_name = 4; } diff --git a/tests/chains/FIO/SignerTests.cpp b/tests/chains/FIO/SignerTests.cpp index db663c9cf0a..a3104e15afc 100644 --- a/tests/chains/FIO/SignerTests.cpp +++ b/tests/chains/FIO/SignerTests.cpp @@ -103,6 +103,7 @@ TEST(FIOSigner, compile) { Proto::SigningOutput result = Signer::compile(input, signature); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"b0ae295e50c3400a6dee00000000010000980ad20ca85be0e1d195ba85e7cd01102b2f46fca756b200000000a8ed32325d3546494f37754d5a6f6565693548745841443234433479436b70575762663234626a597472524e6a57646d474358485a63637775694500ca9a3b0000000080b2e60e00000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K9VRCnvaTYN7vgcoVKVXgyJTdKUGV8hLXgFLoEbvqAcFxy7DXQ1rSnAfEuabi4ATkgmvnpaSBdVFN7TBtM1wrbZYqeJQw9"]})" , result.json()); + EXPECT_EQ(result.action_name(), "trnsfiopubky"); } } // namespace TW::FIO::tests diff --git a/tests/chains/FIO/TWFIOTests.cpp b/tests/chains/FIO/TWFIOTests.cpp index 77b6acdffaf..6cf1fd3c7d7 100644 --- a/tests/chains/FIO/TWFIOTests.cpp +++ b/tests/chains/FIO/TWFIOTests.cpp @@ -37,6 +37,7 @@ TEST(TWFIO, Address) { } const Data gChainId = parse_hex("4e46572250454b796d7296eec9e8896327ea82dd40f2cd74cf1b1d8ba90bcd77"); +const Data gChainIdMainnet = parse_hex("21dcae42c0182200e93f954a074011f9048a7624c6fe81d3c9541a614a88bd1c"); // 5KEDWtAUJcFX6Vz38WXsAQAv2geNqT7UaZC8gYu9kTuryr3qkri FIO6m1fMdTpRkRBnedvYshXCxLFiC5suRU8KDfx8xxtXp2hntxpnf const PrivateKey privKeyBA = PrivateKey(parse_hex("ba0828d5734b65e3bcc2c51c93dfc26dd71bd666cc0273adee77d73d9a322035")); const PublicKey pubKey6M = privKeyBA.getPublicKey(TWPublicKeyTypeSECP256k1); @@ -58,6 +59,7 @@ TEST(TWFIO, RegisterFioAddress) { ANY_SIGN(input, TWCoinTypeFIO); EXPECT_EQ(Common::Proto::OK, output.error()); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"3f99295ec99b904215ff0000000001003056372503a85b0000c6eaa66498ba01102b2f46fca756b200000000a8ed3232650f6164616d4066696f746573746e65743546494f366d31664d645470526b52426e6564765973685843784c4669433573755255384b44667838787874587032686e7478706e6600f2052a01000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K19ugLriG3ApYgjJCRDsy21p9xgsjbDtqBuZrmAEix9XYzndR1kNbJ6fXCngMJMAhxUHfwHAsPnh58otXiJZkazaM1EkS5"]})", output.json()); + EXPECT_EQ(output.action_name(), "regaddress"); } TEST(TWFIO, AddPubAddress) { @@ -85,6 +87,33 @@ TEST(TWFIO, AddPubAddress) { ANY_SIGN(input, TWCoinTypeFIO); EXPECT_EQ(Common::Proto::OK, output.error()); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"15c2285e2d2d23622eff0000000001003056372503a85b0000c6eaa664523201102b2f46fca756b200000000a8ed3232c9010f6164616d4066696f746573746e65740303425443034254432a626331717679343037347267676b647232707a773576706e6e3632656730736d7a6c787770373064377603455448034554482a30786365356342366339324461333762624261393142643430443443394434443732344133613846353103424e4203424e422a626e6231747333646735346170776c76723968757076326e306a366534367135347a6e6e75736a6b39730000000000000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K3zimaMKU8cBhVRPw46KM2u7uQWaAKXrnoeYZ7MEbp6sVJcDQmQR2RtdavpUPwkAnYUkd8NqLun8H48tcxZBcTUgkiPGVJ"]})", output.json()); + EXPECT_EQ(output.action_name(), "addaddress"); +} + +TEST(TWFIO, RemovePubAddress) { + auto privateKey = parse_hex("93083dc4d9e8f613a57e3a862a1fa5d665c5e90141a8428990c945d1c2b56491"); + + Proto::SigningInput input; + input.set_expiry(1713269931); + input.mutable_chain_params()->set_chain_id(string(gChainIdMainnet.begin(), gChainIdMainnet.end())); + input.mutable_chain_params()->set_head_block_number(256054093); + input.mutable_chain_params()->set_ref_block_prefix(2438027034); + input.set_private_key(string(privateKey.begin(), privateKey.end())); + input.set_tpid("trust@fiomembers"); + auto action = input.mutable_action()->mutable_remove_pub_address_message(); + action->set_fio_address("sergeitrust@wallet"); + action->add_public_addresses(); + action->mutable_public_addresses(0)->set_coin_symbol("BTC"); + action->mutable_public_addresses(0)->set_address("bc1q68caps3gqt2c9qxtnkhmzf3whxenrs9cav4wlm"); + action->set_fee(4878336459); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeFIO); + EXPECT_EQ(Common::Proto::OK, output.error()); + std::cout << output.json() << std::endl; + // Successfully broadcasted: https://fio.bloks.io/transaction/0bb6da24a3ea9e3ee57906de1cfa8bad18709acd64bf30908713dd61c54cfaea + EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"ab6c1e664d131a5751910000000001003056372503a85b0000c6eaa664a4ba01b038b9d6c13372f700000000a8ed3232681273657267656974727573744077616c6c65740103425443034254432a62633171363863617073336771743263397178746e6b686d7a6633776878656e72733963617634776c6dcb81c52201000000b038b9d6c13372f71074727573744066696f6d656d6265727300","signatures":["SIG_K1_K3cKHXCFYYB9aLFc9qk2idmWgEA4Q9192fECc3cF7MYHXkw9kZamdeHv3qbVoifG9oS8h6nVAJwJvj5YcnhHmnd3u89ND7"]})", output.json()); + EXPECT_EQ(output.action_name(), "remaddress"); } TEST(TWFIO, Transfer) { @@ -103,6 +132,7 @@ TEST(TWFIO, Transfer) { ANY_SIGN(input, TWCoinTypeFIO); EXPECT_EQ(Common::Proto::OK, output.error()); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"b0ae295e50c3400a6dee00000000010000980ad20ca85be0e1d195ba85e7cd01102b2f46fca756b200000000a8ed32325d3546494f37754d5a6f6565693548745841443234433479436b70575762663234626a597472524e6a57646d474358485a63637775694500ca9a3b0000000080b2e60e00000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K9VRCnvaTYN7vgcoVKVXgyJTdKUGV8hLXgFLoEbvqAcFxy7DXQ1rSnAfEuabi4ATkgmvnpaSBdVFN7TBtM1wrbZYqeJQw9"]})", output.json()); + EXPECT_EQ(output.action_name(), "trnsfiopubky"); } TEST(TWFIO, RenewFioAddress) { @@ -121,6 +151,7 @@ TEST(TWFIO, RenewFioAddress) { ANY_SIGN(input, TWCoinTypeFIO); EXPECT_EQ(Common::Proto::OK, output.error()); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"289b295ec99b904215ff0000000001003056372503a85b80b1ba2919aea6ba01102b2f46fca756b200000000a8ed32322f0f6e69636b4066696f746573746e6574005ed0b200000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_Jxz7oCJ7Z4ECsxqb2utqBcyP3zPQCeQCBws9wWQjyptUKoWVk2AyCVEqtdMHJwqtLniio5Z7npMnaZB8E4pa2G75P9uGkb"]})", output.json()); + EXPECT_EQ(output.action_name(), "renewaddress"); } TEST(TWFIO, NewFundsRequest) { @@ -149,6 +180,7 @@ TEST(TWFIO, NewFundsRequest) { EXPECT_EQ( R"({"compression":"none","packed_context_free_data":"","packed_trx":"289b295ec99b904215ff000000000100403ed4aa0ba85b00acba384dbdb89a01102b2f46fca756b200000000a8ed32328802106d6172696f4066696f746573746)", output.json().substr(0, 195)); + EXPECT_EQ(output.action_name(), "newfundsreq"); } } // namespace TW::FIO::TWFIOTests diff --git a/tests/chains/FIO/TransactionBuilderTests.cpp b/tests/chains/FIO/TransactionBuilderTests.cpp index 097d587b341..16f16f92b06 100644 --- a/tests/chains/FIO/TransactionBuilderTests.cpp +++ b/tests/chains/FIO/TransactionBuilderTests.cpp @@ -145,7 +145,7 @@ TEST(FIOTransaction, ActionRegisterFioAddressInternal) { } TEST(FIOTransaction, ActionAddPubAddressInternal) { - AddPubAddressData aadata("adam@fiotestnet", {{"BTC", "BTC", "bc1qvy4074rggkdr2pzw5vpnn62eg0smzlxwp70d7v"}, {"ETH", "ETH", "0xce5cB6c92Da37bbBa91Bd40D4C9D4D724A3a8F51"}, {"BNB", "BNB", "bnb1ts3dg54apwlvr9hupv2n0j6e46q54znnusjk9s"}}, + PubAddressActionData aadata("adam@fiotestnet", {{"BTC", "BTC", "bc1qvy4074rggkdr2pzw5vpnn62eg0smzlxwp70d7v"}, {"ETH", "ETH", "0xce5cB6c92Da37bbBa91Bd40D4C9D4D724A3a8F51"}, {"BNB", "BNB", "bnb1ts3dg54apwlvr9hupv2n0j6e46q54znnusjk9s"}}, 0, "rewards@wallet", "qdfejz2a5wpl"); Data ser1; aadata.serialize(ser1); @@ -468,6 +468,7 @@ TEST(FIOTransactionBuilder, buildSigningOutput) { EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"3f99295ec99b904215ff0000000001003056372503a85b0000c6eaa66498ba01102b2f46fca756b200000000a8ed3232650f6164616d4066696f746573746e65743546494f366d31664d645470526b52426e6564765973685843784c4669433573755255384b44667838787874587032686e7478706e6600f2052a01000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K19ugLriG3ApYgjJCRDsy21p9xgsjbDtqBuZrmAEix9XYzndR1kNbJ6fXCngMJMAhxUHfwHAsPnh58otXiJZkazaM1EkS5"]})" , result.json()); + EXPECT_EQ(result.action_name(), "regaddress"); } { @@ -506,6 +507,7 @@ TEST(FIOTransactionBuilder, buildSigningOutput) { Proto::SigningOutput result = TransactionBuilder::buildSigningOutput(input, signature); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"15c2285e2d2d23622eff0000000001003056372503a85b0000c6eaa664523201102b2f46fca756b200000000a8ed3232c9010f6164616d4066696f746573746e65740303425443034254432a626331717679343037347267676b647232707a773576706e6e3632656730736d7a6c787770373064377603455448034554482a30786365356342366339324461333762624261393142643430443443394434443732344133613846353103424e4203424e422a626e6231747333646735346170776c76723968757076326e306a366534367135347a6e6e75736a6b39730000000000000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K3zimaMKU8cBhVRPw46KM2u7uQWaAKXrnoeYZ7MEbp6sVJcDQmQR2RtdavpUPwkAnYUkd8NqLun8H48tcxZBcTUgkiPGVJ"]})" , result.json()); + EXPECT_EQ(result.action_name(), "addaddress"); } { // Test transfer_message @@ -530,6 +532,7 @@ TEST(FIOTransactionBuilder, buildSigningOutput) { Proto::SigningOutput result = TransactionBuilder::buildSigningOutput(input, signature); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"b0ae295e50c3400a6dee00000000010000980ad20ca85be0e1d195ba85e7cd01102b2f46fca756b200000000a8ed32325d3546494f37754d5a6f6565693548745841443234433479436b70575762663234626a597472524e6a57646d474358485a63637775694500ca9a3b0000000080b2e60e00000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K9VRCnvaTYN7vgcoVKVXgyJTdKUGV8hLXgFLoEbvqAcFxy7DXQ1rSnAfEuabi4ATkgmvnpaSBdVFN7TBtM1wrbZYqeJQw9"]})" , result.json()); + EXPECT_EQ(result.action_name(), "trnsfiopubky"); } { // Test renew_fio_address_message @@ -553,6 +556,7 @@ TEST(FIOTransactionBuilder, buildSigningOutput) { Proto::SigningOutput result = TransactionBuilder::buildSigningOutput(input, signature); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"289b295ec99b904215ff0000000001003056372503a85b80b1ba2919aea6ba01102b2f46fca756b200000000a8ed32322f0f6e69636b4066696f746573746e6574005ed0b200000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_Jxz7oCJ7Z4ECsxqb2utqBcyP3zPQCeQCBws9wWQjyptUKoWVk2AyCVEqtdMHJwqtLniio5Z7npMnaZB8E4pa2G75P9uGkb"]})" , result.json()); + EXPECT_EQ(result.action_name(), "renewaddress"); } } diff --git a/tests/chains/FIO/TransactionCompilerTests.cpp b/tests/chains/FIO/TransactionCompilerTests.cpp index cfdba2b1fee..f3eaecbc159 100644 --- a/tests/chains/FIO/TransactionCompilerTests.cpp +++ b/tests/chains/FIO/TransactionCompilerTests.cpp @@ -85,7 +85,8 @@ TEST(FIOCompiler, CompileWithSignatures) { TW::FIO::Proto::SigningOutput output; ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); - EXPECT_EQ(ExpectedTx, output.json()); + EXPECT_EQ(ExpectedTx, output.json()); + EXPECT_EQ(output.action_name(), "trnsfiopubky"); } { // Double check: check if simple signature process gives the same result. Note that private // keys were not used anywhere up to this point. From 51917c0a8d0a646680e4910024bf694d4e9de05a Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Thu, 18 Apr 2024 21:57:15 +0200 Subject: [PATCH 082/128] feat(fio): Add support for `addbundles` and `remalladdr` actions (#3802) --- src/FIO/Action.cpp | 15 ++++++ src/FIO/Action.h | 31 +++++++++++- src/FIO/TransactionBuilder.cpp | 86 +++++++++++++++++++++++++++++++++ src/FIO/TransactionBuilder.h | 30 ++++++++++++ src/proto/FIO.proto | 24 +++++++++ tests/chains/FIO/TWFIOTests.cpp | 45 +++++++++++++++++ 6 files changed, 230 insertions(+), 1 deletion(-) diff --git a/src/FIO/Action.cpp b/src/FIO/Action.cpp index 5be60a17866..34e1a8455cd 100644 --- a/src/FIO/Action.cpp +++ b/src/FIO/Action.cpp @@ -49,6 +49,13 @@ void PubAddressActionData::serialize(Data& out) const { encodeString(tpid, out); } +void RemoveAllPubAddressActionData::serialize(Data& out) const { + encodeString(fioAddress, out); + encode64LE(fee, out); + EOS::Name(actor).serialize(out); + encodeString(tpid, out); +} + void RegisterFioAddressData::serialize(Data& out) const { encodeString(fioAddress, out); encodeString(ownerPublicKey, out); @@ -81,4 +88,12 @@ void NewFundsRequestData::serialize(Data& out) const { encodeString(tpid, out); } +void AddBundledTransactionsActionData::serialize(Data& out) const { + encodeString(fioAddress, out); + encode64LE(bundledSets, out); + encode64LE(fee, out); + encodeString(tpid, out); + EOS::Name(actor).serialize(out); +} + } // namespace TW::FIO diff --git a/src/FIO/Action.h b/src/FIO/Action.h index 1ebdc3eff91..a5c325d3341 100644 --- a/src/FIO/Action.h +++ b/src/FIO/Action.h @@ -82,7 +82,7 @@ class Action { }; /// A public address action data part. -/// Can be used for `addaddress`, `remaddress` actions. +/// Can be used for `addaddress`, `remaddress`, `remalladdr` (addresses must be empty) actions. /// https://dev.fio.net/reference/add_pub_address /// https://dev.fio.net/reference/remove_pub_address class PubAddressActionData { @@ -100,6 +100,20 @@ class PubAddressActionData { void serialize(Data& out) const; }; +/// RemoveAllPubAddress action data part. +/// https://dev.fio.net/reference/remove_all_pub_address +class RemoveAllPubAddressActionData { +public: + std::string fioAddress; + uint64_t fee; + std::string tpid; + std::string actor; + + RemoveAllPubAddressActionData(const std::string& fioAddress, uint64_t fee, const std::string& tpid, const std::string& actor) : + fioAddress(fioAddress), fee(fee), tpid(tpid), actor(actor) {} + void serialize(Data& out) const; +}; + /// RegisterFioAddress action data part. class RegisterFioAddressData { public: @@ -158,4 +172,19 @@ class NewFundsRequestData { void serialize(Data& out) const; }; +/// AddBundledTransactions action data part. +/// https://dev.fio.net/reference/add_bundled_transactions +class AddBundledTransactionsActionData { +public: + std::string fioAddress; + uint64_t bundledSets; + uint64_t fee; + std::string tpid; + std::string actor; + + AddBundledTransactionsActionData(const std::string& fioAddress, uint64_t bundledSets, uint64_t fee, const std::string& tpid, const std::string& actor) : + fioAddress(fioAddress), bundledSets(bundledSets), fee(fee), tpid(tpid), actor(actor) {} + void serialize(Data& out) const; +}; + } // namespace TW::FIO diff --git a/src/FIO/TransactionBuilder.cpp b/src/FIO/TransactionBuilder.cpp index bf5ca8a8302..4a890d81705 100644 --- a/src/FIO/TransactionBuilder.cpp +++ b/src/FIO/TransactionBuilder.cpp @@ -24,9 +24,11 @@ using json = nlohmann::json; static constexpr auto gRegisterFioAddress = "regaddress"; static constexpr auto gAddPubAddress = "addaddress"; static constexpr auto gRemoveAddress = "remaddress"; +static constexpr auto gRemoveAllPubAddresses = "remalladdr"; static constexpr auto gTransferFIOPubkey = "trnsfiopubky"; static constexpr auto gRenewFIOAddress = "renewaddress"; static constexpr auto gNewFundsRequest = "newfundsreq"; +static constexpr auto gAddBundledTransactions = "addbundles"; /// Internal helper ChainParams getChainParams(const Proto::SigningInput& input) { @@ -58,6 +60,10 @@ string TransactionBuilder::actionName(const Proto::SigningInput& input) { return gNewFundsRequest; case Proto::Action::MessageOneofCase::kRemovePubAddressMessage: return gRemoveAddress; + case Proto::Action::MessageOneofCase::kRemoveAllPubAddressesMessage: + return gRemoveAllPubAddresses; + case Proto::Action::MessageOneofCase::kAddBundledTransactionsMessage: + return gAddBundledTransactions; default: return {}; } @@ -111,6 +117,14 @@ string TransactionBuilder::sign(Proto::SigningInput in) { json = TransactionBuilder::createRemovePubAddress(owner, privateKey, action.fio_address(), addresses, getChainParams(in), action.fee(), in.tpid(), in.expiry()); + } else if (in.action().has_remove_all_pub_addresses_message()) { + const auto action = in.action().remove_all_pub_addresses_message(); + json = TransactionBuilder::createRemoveAllPubAddresses(owner, privateKey, + action.fio_address(), getChainParams(in), action.fee(), in.tpid(), in.expiry()); + } else if (in.action().has_add_bundled_transactions_message()) { + const auto action = in.action().add_bundled_transactions_message(); + json = TransactionBuilder::createAddBundledTransactions(owner, privateKey, action.fio_address(), + action.bundle_sets(), getChainParams(in), action.fee(), in.tpid(), in.expiry()); } return json; } @@ -151,6 +165,28 @@ string TransactionBuilder::createRemovePubAddress(const Address& address, const return signAndBuildTx(chainParams.chainId, serTx, privateKey); } +std::string TransactionBuilder::createRemoveAllPubAddresses(const Address& address, const PrivateKey& privateKey, const std::string& fioName, + const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) { + + Transaction transaction = TransactionBuilder::buildUnsignedRemoveAllAddressesAction(address, fioName, chainParams, fee, walletTpId, expiryTime); + + Data serTx; + transaction.serialize(serTx); + + return signAndBuildTx(chainParams.chainId, serTx, privateKey); +} + +std::string TransactionBuilder::createAddBundledTransactions(const Address& address, const PrivateKey& privateKey, const std::string& fioName, + uint64_t bundleSets, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) { + + Transaction transaction = TransactionBuilder::buildUnsignedAddBundledTransactions(address, fioName, bundleSets, chainParams, fee, walletTpId, expiryTime); + + Data serTx; + transaction.serialize(serTx); + + return signAndBuildTx(chainParams.chainId, serTx, privateKey); +} + string TransactionBuilder::createTransfer(const Address& address, const PrivateKey& privateKey, const string& payeePublicKey, uint64_t amount, const ChainParams& chainParams, uint64_t fee, const string& walletTpId, uint32_t expiryTime) { @@ -271,6 +307,14 @@ Data TransactionBuilder::buildUnsignedTxBytes(const Proto::SigningInput& in) { } transaction = TransactionBuilder::buildUnsignedPubAddressAction(gRemoveAddress, owner, action.fio_address(), addresses, getChainParams(in), action.fee(), in.tpid(), in.expiry()); + } else if (in.action().has_remove_all_pub_addresses_message()) { + const auto action = in.action().remove_all_pub_addresses_message(); + transaction = TransactionBuilder::buildUnsignedRemoveAllAddressesAction(owner, action.fio_address(), + getChainParams(in), action.fee(), in.tpid(), in.expiry()); + } else if (in.action().has_add_bundled_transactions_message()) { + const auto action = in.action().add_bundled_transactions_message(); + transaction = TransactionBuilder::buildUnsignedAddBundledTransactions(owner, action.fio_address(), action.bundle_sets(), + getChainParams(in), action.fee(), in.tpid(), in.expiry()); } Data serTx; @@ -381,4 +425,46 @@ Transaction TransactionBuilder::buildUnsignedRenewFioAddress(const Address& addr return tx; } +Transaction TransactionBuilder::buildUnsignedRemoveAllAddressesAction(const Address& address, const std::string& fioName, + const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) { + + string actor = Actor::actor(address); + RemoveAllPubAddressActionData actionData(fioName, fee, walletTpId, actor); + Data serData; + actionData.serialize(serData); + + Action action; + action.account = ContractAddress; + action.name = gRemoveAllPubAddresses; + action.actionDataSer = serData; + action.auth.authArray.push_back(Authorization{actor, AuthrizationActive}); + + Transaction tx; + expirySetDefaultIfNeeded(expiryTime); + tx.set(expiryTime, chainParams); + tx.actions.push_back(action); + return tx; +} + +Transaction TransactionBuilder::buildUnsignedAddBundledTransactions(const Address& address, const std::string& fioName, + uint64_t bundleSets, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) { + + string actor = Actor::actor(address); + AddBundledTransactionsActionData actionData(fioName, bundleSets, fee, walletTpId, actor); + Data serData; + actionData.serialize(serData); + + Action action; + action.account = ContractAddress; + action.name = gAddBundledTransactions; + action.actionDataSer = serData; + action.auth.authArray.push_back(Authorization{actor, AuthrizationActive}); + + Transaction tx; + expirySetDefaultIfNeeded(expiryTime); + tx.set(expiryTime, chainParams); + tx.actions.push_back(action); + return tx; +} + } // namespace TW::FIO diff --git a/src/FIO/TransactionBuilder.h b/src/FIO/TransactionBuilder.h index 735bf38d834..844a58850b1 100644 --- a/src/FIO/TransactionBuilder.h +++ b/src/FIO/TransactionBuilder.h @@ -81,6 +81,18 @@ class TransactionBuilder { const std::vector>& pubAddresses, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); + /// Create a signed `remalladdr` transaction, returned as json string (double quote delimited), suitable for remove_all_pub_address RPC call + /// @address The owners' FIO address + /// @privateKey The private key matching the address, needed for signing. + /// @fioName The FIO name already registered to the owner. Ex.: "dmitry@trust" + /// @chainParams Current parameters from the FIO chain, must be obtained recently using get_info and get_block calls. + /// @fee Max fee to spend, can be obtained using get_fee API. + /// @walletTpId The FIO name of the originating wallet (project-wide constant) + /// @expiryTime Expiry for this message, can be 0, then it is taken from current time with default expiry + /// Note: fee is usually 0 for remove_all_pub_address. + static std::string createRemoveAllPubAddresses(const Address& address, const PrivateKey& privateKey, const std::string& fioName, + const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); + /// Create a signed TransferTokens transaction, returned as json string (double quote delimited), suitable for transfer_tokens_pub_key RPC call /// @address The owners' FIO address /// @privateKey The private key matching the address, needed for signing. @@ -130,6 +142,18 @@ class TransactionBuilder { const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime, const Data& iv); + /// Create a signed `addbundles` transaction, returned as json string (double quote delimited), suitable for add_bundled_transactions RPC call + /// @address The owners' FIO address + /// @privateKey The private key matching the address, needed for signing. + /// @fioName The FIO name already registered to the owner. Ex.: "dmitry@trust" + /// @bundleSets Number of bundled sets. One set is 100 bundled transactions. + /// @chainParams Current parameters from the FIO chain, must be obtained recently using get_info and get_block calls. + /// @fee Max fee to spend, can be obtained using get_fee API. + /// @walletTpId The FIO name of the originating wallet (project-wide constant) + /// @expiryTime Expiry for this message, can be 0, then it is taken from current time with default expiry + static std::string createAddBundledTransactions(const Address& address, const PrivateKey& privateKey, const std::string& fioName, + uint64_t bundleSets, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); + /// Used internally. Creates signatures and json with transaction. static std::string signAndBuildTx(const Data& chainId, const Data& packedTx, const PrivateKey& privateKey); @@ -166,6 +190,12 @@ class TransactionBuilder { static Transaction buildUnsignedRenewFioAddress(const Address& address, const std::string& fioName, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); + + static Transaction buildUnsignedRemoveAllAddressesAction(const Address& address, const std::string& fioName, + const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); + + static Transaction buildUnsignedAddBundledTransactions(const Address& address, const std::string& fioName, + uint64_t bundleSets, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); }; } // namespace TW::FIO diff --git a/src/proto/FIO.proto b/src/proto/FIO.proto index 9fdb61b4c70..664c041e5f0 100644 --- a/src/proto/FIO.proto +++ b/src/proto/FIO.proto @@ -77,6 +77,16 @@ message Action { uint64 fee = 3; } + // Action for removing public chain addresses from a FIO name; remove_pub_address + // Note: actor is not needed, computed from private key + message RemoveAllPubAddress { + // The FIO name already registered to the owner. Ex.: "alice@trust" + string fio_address = 1; + + // Max fee to spend, can be obtained using get_fee API. + uint64 fee = 3; + } + // Action for transferring FIO coins; transfer_tokens_pub_key // Note: actor is not needed, computed from private key message Transfer { @@ -122,6 +132,18 @@ message Action { uint64 fee = 5; } + // Action for adding `100 * bundle_sets` bundled transactions to the supplied FIO Handle. When bundles are purchased one or more sets of bundled transactions are added to the existing count. + message AddBundledTransactions { + // The FIO name already registered to the owner. Ex.: "alice@trust" + string fio_address = 1; + + // Number of bundled sets. One set is 100 bundled transactions. + uint64 bundle_sets = 2; + + // Max fee to spend, can be obtained using get_fee API. + uint64 fee = 3; + } + // Payload message oneof message_oneof { RegisterFioAddress register_fio_address_message = 1; @@ -130,6 +152,8 @@ message Action { RenewFioAddress renew_fio_address_message = 4; NewFundsRequest new_funds_request_message = 5; RemovePubAddress remove_pub_address_message = 6; + RemoveAllPubAddress remove_all_pub_addresses_message = 7; + AddBundledTransactions add_bundled_transactions_message = 8; } } diff --git a/tests/chains/FIO/TWFIOTests.cpp b/tests/chains/FIO/TWFIOTests.cpp index 6cf1fd3c7d7..1c4f35285b3 100644 --- a/tests/chains/FIO/TWFIOTests.cpp +++ b/tests/chains/FIO/TWFIOTests.cpp @@ -116,6 +116,28 @@ TEST(TWFIO, RemovePubAddress) { EXPECT_EQ(output.action_name(), "remaddress"); } +TEST(TWFIO, RemoveAllPubAddresses) { + auto privateKey = parse_hex("93083dc4d9e8f613a57e3a862a1fa5d665c5e90141a8428990c945d1c2b56491"); + + Proto::SigningInput input; + input.set_expiry(1713458993); + input.mutable_chain_params()->set_chain_id(string(gChainIdMainnet.begin(), gChainIdMainnet.end())); + input.mutable_chain_params()->set_head_block_number(256432311); + input.mutable_chain_params()->set_ref_block_prefix(2287536876); + input.set_private_key(string(privateKey.begin(), privateKey.end())); + input.set_tpid("trust@fiomembers"); + auto action = input.mutable_action()->mutable_remove_all_pub_addresses_message(); + action->set_fio_address("sergeitrust@wallet"); + action->set_fee(0); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeFIO); + EXPECT_EQ(Common::Proto::OK, output.error()); + // Successfully broadcasted: https://fio.bloks.io/transaction/f2facdebfcba1981377537424a6d7b7e7ebd8222c87ba4d25a480d1b968704b2 + EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"314f2166b7d8ec0a59880000000001003056372503a85b00c04dc9c468a4ba01b038b9d6c13372f700000000a8ed3232341273657267656974727573744077616c6c65740000000000000000b038b9d6c13372f71074727573744066696f6d656d6265727300","signatures":["SIG_K1_KXXtpz7NWhzCms7Dj54nSwwtCw6w4zLCyTLxs3tqqgLscrz91cMjcbN4yxcySvZ7t4MER8HPteeJZUnR16uLyDa1gFGzrx"]})", output.json()); + EXPECT_EQ(output.action_name(), "remalladdr"); +} + TEST(TWFIO, Transfer) { Proto::SigningInput input; input.set_expiry(1579790000); @@ -183,4 +205,27 @@ TEST(TWFIO, NewFundsRequest) { EXPECT_EQ(output.action_name(), "newfundsreq"); } +TEST(TWFIO, AddBundledTransactions) { + auto privateKey = parse_hex("93083dc4d9e8f613a57e3a862a1fa5d665c5e90141a8428990c945d1c2b56491"); + + Proto::SigningInput input; + input.set_expiry(1713458594); + input.mutable_chain_params()->set_chain_id(string(gChainIdMainnet.begin(), gChainIdMainnet.end())); + input.mutable_chain_params()->set_head_block_number(256431437); + input.mutable_chain_params()->set_ref_block_prefix(791306279); + input.set_private_key(string(privateKey.begin(), privateKey.end())); + input.set_tpid("trust@fiomembers"); + auto action = input.mutable_action()->mutable_add_bundled_transactions_message(); + action->set_fio_address("sergeitrust@wallet"); + action->set_bundle_sets(1); + action->set_fee(100000000000); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeFIO); + EXPECT_EQ(Common::Proto::OK, output.error()); + // Successfully broadcasted: https://fio.bloks.io/transaction/2c00f2051ca3738c4fe03ceddb82c48fefd9c534d8bb793dc7dce5d12f4f4f9c + EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"a24d21664dd527602a2f0000000001003056372503a85b000056314d7d523201b038b9d6c13372f700000000a8ed32323c1273657267656974727573744077616c6c6574010000000000000000e87648170000001074727573744066696f6d656d62657273b038b9d6c13372f700","signatures":["SIG_K1_KjWGZ4Yd48VJcTAgox3HYVQhXeLhpRCgz2WqiF5WHRFSnbHouKxPgLQmymoABHC8EX51G1jU4ocWg2RKU17UYm4L5kTXP6"]})", output.json()); + EXPECT_EQ(output.action_name(), "addbundles"); +} + } // namespace TW::FIO::TWFIOTests From cd212c9cb3a186f10ff2cc98fad01f6b9b514d33 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Fri, 19 Apr 2024 15:21:25 +0200 Subject: [PATCH 083/128] feat(bouncebit): Add support for `BounceBit` EVM (#3807) --- .../blockchains/CoinAddressDerivationTests.kt | 2 +- docs/registry.md | 1 + include/TrustWalletCore/TWCoinType.h | 1 + .../core/test/CoinAddressDerivationTests.kt | 2 +- registry.json | 30 +++++++++++++++++++ .../tests/coin_address_derivation_test.rs | 1 + swift/Tests/CoinAddressDerivationTests.swift | 3 +- tests/chains/BounceBit/TWCoinTypeTests.cpp | 29 ++++++++++++++++++ tests/common/CoinAddressDerivationTests.cpp | 1 + 9 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 tests/chains/BounceBit/TWCoinTypeTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index 07108b4c9d6..3fa855cc07b 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -47,7 +47,7 @@ class CoinAddressDerivationTests { FANTOM, CELO, CRONOSCHAIN, SMARTBITCOINCASH, KUCOINCOMMUNITYCHAIN, BOBA, METIS, AURORA, EVMOS, MOONRIVER, MOONBEAM, KAVAEVM, KLAYTN, METER, OKXCHAIN, POLYGONZKEVM, SCROLL, CONFLUXESPACE, ACALAEVM, OPBNB, NEON, BASE, LINEA, GREENFIELD, MANTLE, ZENEON, MANTAPACIFIC, - ZETAEVM, MERLIN, LIGHTLINK, BLAST, + ZETAEVM, MERLIN, LIGHTLINK, BLAST, BOUNCEBIT, -> assertEquals("0x8f348F300873Fd5DA36950B2aC75a26584584feE", address) RONIN -> assertEquals("ronin:8f348F300873Fd5DA36950B2aC75a26584584feE", address) diff --git a/docs/registry.md b/docs/registry.md index 46c7671fa3f..ad4c51171a7 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -91,6 +91,7 @@ This list is generated from [./registry.json](../registry.json) | 4200 | Merlin | BTC | | | | 5000 | Mantle | MNT | | | | 5600 | BNB Greenfield | BNB | | | +| 6001 | BounceBit | BB | | | | 6060 | GoChain | GO | | | | 7332 | Zen EON | ZEN | | | | 8453 | Base | ETH | | | diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 3af38bb8a6c..ce3f2aa0d20 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -184,6 +184,7 @@ enum TWCoinType { TWCoinTypeMerlin = 4200, TWCoinTypeLightlink = 1890, TWCoinTypeBlast = 81457, + TWCoinTypeBounceBit = 6001, // end_of_tw_coin_type_marker_do_not_modify }; diff --git a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt index 3c1277ce712..1714a611aba 100644 --- a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt +++ b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt @@ -40,7 +40,7 @@ class CoinAddressDerivationTests { Fantom, Celo, CronosChain, SmartBitcoinCash, KuCoinCommunityChain, Boba, Metis, Aurora, Evmos, Moonriver, Moonbeam, KavaEvm, Klaytn, Meter, OKXChain, PolygonzkEVM, Scroll, ConfluxeSpace, AcalaEVM, OpBNB, Neon, Base, Linea, Greenfield, Mantle, ZenEON, MantaPacific, - ZetaEVM, Merlin, Lightlink, Blast, + ZetaEVM, Merlin, Lightlink, Blast, BounceBit, -> "0x8f348F300873Fd5DA36950B2aC75a26584584feE" Ronin -> "ronin:8f348F300873Fd5DA36950B2aC75a26584584feE" diff --git a/registry.json b/registry.json index 8044e408aae..0a2f370590a 100644 --- a/registry.json +++ b/registry.json @@ -4719,5 +4719,35 @@ "rpc": "https://rpc.blast.io", "documentation": "https://docs.blast.io" } + }, + { + "id": "bouncebit", + "name": "BounceBit", + "coinId": 6001, + "symbol": "BB", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "6001", + "addressHasher": "keccak256", + "explorer": { + "url": "https://bbscan.io", + "txPath": "/tx/", + "accountPath": "/address/", + "sampleTx": "0x52558f4143d058d942e3b73414090266ae3ffce1fe8c25fe86896e2c8e5ef932", + "sampleAccount": "0xf4aa7349a9ccca4609943955b5ddc7bd9278c223" + }, + "info": { + "url": "https://bouncebit.io", + "source": "https://github.com/BounceBit-Labs", + "rpc": "https://fullnode-mainnet.bouncebitapi.com", + "documentation": "https://docs.bouncebit.io" + } } ] diff --git a/rust/tw_any_coin/tests/coin_address_derivation_test.rs b/rust/tw_any_coin/tests/coin_address_derivation_test.rs index 4eeb9ed7f01..6be2411064a 100644 --- a/rust/tw_any_coin/tests/coin_address_derivation_test.rs +++ b/rust/tw_any_coin/tests/coin_address_derivation_test.rs @@ -89,6 +89,7 @@ fn test_coin_address_derivation() { | CoinType::Merlin | CoinType::Lightlink | CoinType::Blast + | CoinType::BounceBit // end_of_evm_address_derivation_tests_marker_do_not_modify => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", CoinType::Bitcoin diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 9c04c72e16d..e6cda80b29a 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -117,7 +117,8 @@ class CoinAddressDerivationTests: XCTestCase { .zetaEVM, .merlin, .lightlink, - .blast: + .blast, + .bounceBit: let expectedResult = "0x8f348F300873Fd5DA36950B2aC75a26584584feE" assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .ronin: diff --git a/tests/chains/BounceBit/TWCoinTypeTests.cpp b/tests/chains/BounceBit/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..7dbc072273e --- /dev/null +++ b/tests/chains/BounceBit/TWCoinTypeTests.cpp @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#include "TestUtilities.h" +#include +#include + +TEST(TWBounceBitCoinType, TWCoinType) { + const auto coin = TWCoinTypeBounceBit; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x52558f4143d058d942e3b73414090266ae3ffce1fe8c25fe86896e2c8e5ef932")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xf4aa7349a9ccca4609943955b5ddc7bd9278c223")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "bouncebit"); + assertStringsEqual(name, "BounceBit"); + assertStringsEqual(symbol, "BB"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainEthereum); + ASSERT_EQ(TWCoinTypeP2pkhPrefix(coin), 0); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0); + assertStringsEqual(txUrl, "https://bbscan.io/tx/0x52558f4143d058d942e3b73414090266ae3ffce1fe8c25fe86896e2c8e5ef932"); + assertStringsEqual(accUrl, "https://bbscan.io/address/0xf4aa7349a9ccca4609943955b5ddc7bd9278c223"); +} diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index 04cb56c4fb4..351b5dd4954 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -87,6 +87,7 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeMerlin: case TWCoinTypeLightlink: case TWCoinTypeBlast: + case TWCoinTypeBounceBit: // end_of_evm_address_derivation_tests_marker_do_not_modify EXPECT_EQ(address, "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); break; From 107d71b4f916604c909d825151ccf1b8b3e32383 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 24 Apr 2024 09:56:39 +0200 Subject: [PATCH 084/128] [misc]: Update CODEOWNERS (#3812) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 001dca8da7e..1724b9a1b7e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,5 +3,5 @@ # @global-owner1 and @global-owner2 will be requested for # review when someone opens a pull request. -* @milerius @satoshiotomakan @lamafab @ar-g +* @milerius @satoshiotomakan @ar-g kotlin/ @ar-g @JaimeToca @rkokhatskyi From 47a249e7507bc9d0bc6a3a09720421b93590b5a7 Mon Sep 17 00:00:00 2001 From: momantech <166911910+momantech@users.noreply.github.com> Date: Wed, 24 Apr 2024 15:57:00 +0800 Subject: [PATCH 085/128] Fix some typos in comments (#3800) Signed-off-by: momantech Co-authored-by: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> --- trezor-crypto/crypto/aes/aescrypt.c | 2 +- trezor-crypto/include/TrezorCrypto/aes/aesopt.h | 4 ++-- trezor-crypto/include/TrezorCrypto/groestl.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/trezor-crypto/crypto/aes/aescrypt.c b/trezor-crypto/crypto/aes/aescrypt.c index aac3b571b6c..8a168d04efe 100644 --- a/trezor-crypto/crypto/aes/aescrypt.c +++ b/trezor-crypto/crypto/aes/aescrypt.c @@ -215,7 +215,7 @@ AES_RETURN aes_xi(encrypt)(const unsigned char *in, unsigned char *out, const ae #endif /* This code can work with the decryption key schedule in the */ -/* order that is used for encrytpion (where the 1st decryption */ +/* order that is used for encryption (where the 1st decryption */ /* round key is at the high end ot the schedule) or with a key */ /* schedule that has been reversed to put the 1st decryption */ /* round key at the low end of the schedule in memory (when */ diff --git a/trezor-crypto/include/TrezorCrypto/aes/aesopt.h b/trezor-crypto/include/TrezorCrypto/aes/aesopt.h index e8d9db4b4c0..5e8763fa545 100644 --- a/trezor-crypto/include/TrezorCrypto/aes/aesopt.h +++ b/trezor-crypto/include/TrezorCrypto/aes/aesopt.h @@ -363,10 +363,10 @@ Issue Date: 20/12/2007 /* 10. TABLE ALIGNMENT - On some sytsems speed will be improved by aligning the AES large lookup + On some systems speed will be improved by aligning the AES large lookup tables on particular boundaries. This define should be set to a power of two giving the desired alignment. It can be left undefined if alignment - is not needed. This option is specific to the Microsft VC++ compiler - + is not needed. This option is specific to the Microsoft VC++ compiler - it seems to sometimes cause trouble for the VC++ version 6 compiler. */ diff --git a/trezor-crypto/include/TrezorCrypto/groestl.h b/trezor-crypto/include/TrezorCrypto/groestl.h index e18f96910bb..8335abf46a1 100644 --- a/trezor-crypto/include/TrezorCrypto/groestl.h +++ b/trezor-crypto/include/TrezorCrypto/groestl.h @@ -86,7 +86,7 @@ void groestl512_Update(void *cc, const void *data, size_t len); /** * Terminate the current Groestl-512 computation and output the result into * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (64 bytes). The context is automatically + * accommodate the result (64 bytes). The context is automatically * reinitialized. * * @param cc the Groestl-512 context From bcd0422a012b8153e3edce5e468c5e6bc07d9d59 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:38:07 +0200 Subject: [PATCH 086/128] [CI]: Fix linux CI (#3813) * [CI]: Downgrade libc6=2.35-0ubuntu3.6 to libc6=2.35-0ubuntu3 * [CI]: Upgrade to libc6=2.35-0ubuntu3.7 --- .github/workflows/linux-ci.yml | 2 +- .github/workflows/linux-sampleapp-ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml index 27241508303..fed511e4054 100644 --- a/.github/workflows/linux-ci.yml +++ b/.github/workflows/linux-ci.yml @@ -21,7 +21,7 @@ jobs: run: | sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r-ubuntu-test-jammy.list sudo apt-get update - sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.6 libc6-dev=2.35-0ubuntu3.6 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 + sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.7 libc6-dev=2.35-0ubuntu3.7 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 - uses: actions/checkout@v3 - name: Install system dependencies run: | diff --git a/.github/workflows/linux-sampleapp-ci.yml b/.github/workflows/linux-sampleapp-ci.yml index 0e944049d54..580351d8ca8 100644 --- a/.github/workflows/linux-sampleapp-ci.yml +++ b/.github/workflows/linux-sampleapp-ci.yml @@ -21,7 +21,7 @@ jobs: run: | sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r-ubuntu-test-jammy.list sudo apt-get update - sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.6 libc6-dev=2.35-0ubuntu3.6 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 + sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.7 libc6-dev=2.35-0ubuntu3.7 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 - uses: actions/checkout@v3 - name: Install system dependencies run: | From 406abe4b0649a74279b2e7b77a2b10d1bb61520c Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 24 Apr 2024 17:22:43 +0200 Subject: [PATCH 087/128] [Misc]: Improve error handling by adding contexts (#3806) * [misc]: Add TWError, TWResult, TWErrorKind, and traits * Use `TWError` in `tw_coin_entry` * [misc]: Return SigningError, SigningResult back, make TWError generic * [misc]: Add `tw_aptos` error contexts * [misc]: Add `tw_sui` error contexts * [misc]: Simplify `TWError` casts, add tests * [misc]: Add contexts in `tw_cosmos_sdk` * [misc]: Add contexts in `tw_evm` * [misc]: Fix compilation of `tw_ethereum`, `tw_ronin`, `tw_bitcoin`, `tw_native_evmos`, `tw_native_injective`, `tw_thorchain` * [misc]: Add error contexts in `tw_solana` * [misc]: Fix `tw_bitcoin` compilation errors * [misc]: Add error contexts to `tw_greenfield` * [misc]: Fix other compilation errors * [CI] Trigger CI * [codegen-v2]: Fix Rust codegen templates * [misc]: Add `TWError` doc comments * [CI]: Apply a workaround * [CI]: Apply a work around https://github.com/actions/runner-images/issues/9733 * [CI]: Try to disable workaround * [CI]: Try to disable workaround * [CI]: Fix broken dependencies cache * [CI]: Apply the changes for linux-sampleapp-ci.yml --- .github/workflows/linux-ci.yml | 11 +- .github/workflows/linux-sampleapp-ci.yml | 11 +- .../templates/blockchain_crate/address.rs | 2 +- .../templates/blockchain_crate/compiler.rs | 2 +- .../rust/templates/blockchain_crate/entry.rs | 2 +- .../rust/templates/blockchain_crate/signer.rs | 2 +- rust/chains/tw_aptos/src/address.rs | 2 +- .../tw_aptos/src/aptos_move_packages.rs | 2 +- rust/chains/tw_aptos/src/bcs_encoding.rs | 0 rust/chains/tw_aptos/src/compiler.rs | 10 +- rust/chains/tw_aptos/src/entry.rs | 2 +- rust/chains/tw_aptos/src/liquid_staking.rs | 16 +- rust/chains/tw_aptos/src/nft.rs | 25 ++- rust/chains/tw_aptos/src/signer.rs | 6 +- rust/chains/tw_aptos/src/transaction.rs | 7 +- .../tw_aptos/src/transaction_builder.rs | 41 +++- .../tw_aptos/src/transaction_payload.rs | 9 +- rust/chains/tw_aptos/tests/signer.rs | 2 +- rust/chains/tw_binance/src/address.rs | 2 +- rust/chains/tw_binance/src/compiler.rs | 6 +- rust/chains/tw_binance/src/entry.rs | 2 +- .../tw_binance/src/modules/preimager.rs | 6 +- .../tw_binance/src/modules/serializer.rs | 4 +- .../tw_binance/src/modules/tx_builder.rs | 5 +- .../src/modules/wallet_connect/connector.rs | 8 +- rust/chains/tw_binance/src/signer.rs | 6 +- .../src/transaction/message/htlt_order.rs | 2 +- .../tw_binance/src/transaction/message/mod.rs | 4 +- .../src/transaction/message/send_order.rs | 2 +- .../message/side_chain_delegate.rs | 10 +- .../transaction/message/time_lock_order.rs | 2 +- .../src/transaction/message/token_order.rs | 2 +- .../src/transaction/message/trade_order.rs | 4 +- .../transaction/message/tranfer_out_order.rs | 8 +- rust/chains/tw_cosmos/src/entry.rs | 2 +- rust/chains/tw_ethereum/src/entry.rs | 11 +- rust/chains/tw_ethereum/tests/compiler.rs | 2 +- rust/chains/tw_greenfield/src/address.rs | 2 +- rust/chains/tw_greenfield/src/compiler.rs | 13 +- rust/chains/tw_greenfield/src/entry.rs | 2 +- .../src/modules/eip712_signer.rs | 16 +- .../tw_greenfield/src/modules/tx_builder.rs | 37 +++- rust/chains/tw_greenfield/src/signer.rs | 2 +- .../src/transaction/message/send_order.rs | 2 +- .../src/transaction/message/transfer_out.rs | 2 +- .../tw_internet_computer/src/address.rs | 5 +- .../tw_internet_computer/src/context.rs | 2 +- rust/chains/tw_internet_computer/src/entry.rs | 8 +- .../chains/tw_internet_computer/src/signer.rs | 22 +- rust/chains/tw_native_evmos/src/entry.rs | 2 +- rust/chains/tw_native_injective/src/entry.rs | 2 +- rust/chains/tw_ronin/src/address.rs | 2 +- rust/chains/tw_ronin/src/entry.rs | 11 +- rust/chains/tw_ronin/tests/compiler.rs | 2 +- rust/chains/tw_ronin/tests/rlp.rs | 2 +- rust/chains/tw_ronin/tests/signer.rs | 2 +- rust/chains/tw_solana/src/address.rs | 2 +- rust/chains/tw_solana/src/compiler.rs | 11 +- rust/chains/tw_solana/src/entry.rs | 2 +- .../src/modules/compiled_instructions.rs | 10 +- .../tw_solana/src/modules/compiled_keys.rs | 13 +- .../instruction_builder/stake_instruction.rs | 7 +- .../tw_solana/src/modules/message_builder.rs | 196 ++++++++++++----- .../tw_solana/src/modules/proto_builder.rs | 7 +- .../src/modules/transaction_decoder.rs | 7 +- .../chains/tw_solana/src/modules/tx_signer.rs | 21 +- rust/chains/tw_solana/src/modules/utils.rs | 12 +- .../src/modules/wallet_connect/connector.rs | 14 +- .../tw_solana/src/program/stake_program.rs | 2 +- rust/chains/tw_solana/src/signer.rs | 8 +- rust/chains/tw_solana/src/transaction/mod.rs | 8 +- .../tests/update_blockhash_and_sign.rs | 2 +- rust/chains/tw_sui/src/address.rs | 2 +- rust/chains/tw_sui/src/compiler.rs | 2 +- rust/chains/tw_sui/src/entry.rs | 2 +- rust/chains/tw_sui/src/modules/tx_builder.rs | 46 ++-- rust/chains/tw_sui/src/modules/tx_signer.rs | 22 +- rust/chains/tw_sui/src/signer.rs | 2 +- .../transaction/programmable_transaction.rs | 26 ++- .../tw_sui/src/transaction/sui_types.rs | 14 +- .../src/transaction/transaction_builder.rs | 15 +- .../src/transaction/transaction_data.rs | 2 +- rust/chains/tw_thorchain/src/entry.rs | 2 +- rust/tw_any_coin/src/any_address.rs | 2 +- rust/tw_any_coin/src/any_signer.rs | 2 +- rust/tw_any_coin/src/message_signer.rs | 2 +- rust/tw_any_coin/src/transaction_compiler.rs | 2 +- rust/tw_any_coin/src/transaction_decoder.rs | 2 +- .../tw_any_coin/src/wallet_connect_request.rs | 2 +- .../tests/chains/aptos/aptos_compile.rs | 2 +- .../tests/chains/aptos/aptos_sign.rs | 2 +- .../tests/chains/cosmos/cosmos_sign.rs | 2 +- .../tests/chains/ethereum/ethereum_compile.rs | 2 +- .../chains/ethereum/ethereum_message_sign.rs | 2 +- .../tests/chains/ethereum/ethereum_sign.rs | 2 +- .../chains/native_evmos/native_evmos_sign.rs | 2 +- .../native_injective_compile.rs | 2 +- .../native_injective/native_injective_sign.rs | 2 +- .../chains/thorchain/thorchain_compile.rs | 2 +- .../tests/chains/thorchain/thorchain_sign.rs | 2 +- .../tests/chains/zetachain/zetachain_sign.rs | 2 +- rust/tw_bech32_address/src/bech32_prefix.rs | 2 +- rust/tw_bech32_address/src/lib.rs | 2 +- rust/tw_bitcoin/src/entry.rs | 11 +- rust/tw_bitcoin/src/lib.rs | 14 ++ rust/tw_coin_entry/src/coin_entry.rs | 2 +- rust/tw_coin_entry/src/coin_entry_ext.rs | 17 +- .../tw_coin_entry/src/common/compile_input.rs | 17 +- rust/tw_coin_entry/src/error.rs | 134 ------------ rust/tw_coin_entry/src/error/address_error.rs | 23 ++ rust/tw_coin_entry/src/error/impl_from.rs | 59 +++++ rust/tw_coin_entry/src/error/mod.rs | 81 +++++++ rust/tw_coin_entry/src/error/tw_error.rs | 93 ++++++++ rust/tw_coin_entry/src/error/tw_result.rs | 81 +++++++ rust/tw_coin_entry/src/modules/json_signer.rs | 2 +- rust/tw_coin_entry/src/prefix.rs | 2 +- rust/tw_coin_registry/src/error.rs | 10 +- rust/tw_cosmos_sdk/src/address.rs | 2 +- .../src/modules/broadcast_msg.rs | 7 +- .../src/modules/compiler/json_preimager.rs | 5 +- .../modules/compiler/protobuf_preimager.rs | 2 +- .../src/modules/compiler/tw_compiler.rs | 54 +++-- .../src/modules/serializer/json_serializer.rs | 2 +- .../modules/serializer/protobuf_serializer.rs | 2 +- .../src/modules/signer/tw_signer.rs | 17 +- rust/tw_cosmos_sdk/src/modules/tx_builder.rs | 205 +++++++++++++----- rust/tw_cosmos_sdk/src/private_key/mod.rs | 2 +- .../src/private_key/secp256k1.rs | 2 +- .../src/test_utils/sign_utils.rs | 2 +- .../message/cosmos_auth_message.rs | 2 +- .../message/cosmos_bank_message.rs | 2 +- .../message/cosmos_generic_message.rs | 2 +- .../transaction/message/cosmos_gov_message.rs | 2 +- .../message/cosmos_staking_message.rs | 2 +- .../src/transaction/message/ibc_message.rs | 2 +- .../src/transaction/message/mod.rs | 13 +- .../src/transaction/message/stride_message.rs | 2 +- .../transaction/message/terra_wasm_message.rs | 2 +- .../transaction/message/thorchain_message.rs | 2 +- .../src/transaction/message/wasm_message.rs | 5 +- rust/tw_evm/src/abi/contract.rs | 6 +- rust/tw_evm/src/abi/decode.rs | 119 ++++++---- rust/tw_evm/src/abi/function.rs | 12 +- rust/tw_evm/src/abi/mod.rs | 26 +-- rust/tw_evm/src/abi/non_empty_array.rs | 5 +- rust/tw_evm/src/abi/param_type/constructor.rs | 6 +- rust/tw_evm/src/abi/param_type/reader.rs | 34 ++- rust/tw_evm/src/abi/uint.rs | 4 +- rust/tw_evm/src/address.rs | 2 +- .../src/message/eip712/eip712_message.rs | 111 ++++++---- rust/tw_evm/src/message/eip712/property.rs | 8 +- rust/tw_evm/src/message/mod.rs | 25 +-- rust/tw_evm/src/modules/abi_encoder.rs | 67 ++++-- rust/tw_evm/src/modules/compiler.rs | 10 +- rust/tw_evm/src/modules/json_signer.rs | 28 --- rust/tw_evm/src/modules/message_signer.rs | 20 +- rust/tw_evm/src/modules/mod.rs | 1 - rust/tw_evm/src/modules/rlp_encoder.rs | 18 +- rust/tw_evm/src/modules/signer.rs | 6 +- rust/tw_evm/src/modules/tx_builder.rs | 202 ++++++++++++----- rust/tw_evm/src/transaction/mod.rs | 2 +- .../src/transaction/transaction_eip1559.rs | 2 +- .../src/transaction/transaction_non_typed.rs | 2 +- rust/tw_evm/src/transaction/user_operation.rs | 2 +- rust/tw_evm/tests/barz.rs | 2 +- rust/tw_evm/tests/message_signer.rs | 2 +- rust/tw_evm/tests/rlp.rs | 2 +- rust/tw_evm/tests/signer.rs | 2 +- rust/tw_proto/src/impls.rs | 60 +++++ rust/tw_proto/src/lib.rs | 1 + rust/wallet_core_rs/tests/ethereum_rlp.rs | 2 +- tools/install-sys-dependencies-linux | 22 +- 172 files changed, 1687 insertions(+), 881 deletions(-) delete mode 100644 rust/chains/tw_aptos/src/bcs_encoding.rs delete mode 100644 rust/tw_coin_entry/src/error.rs create mode 100644 rust/tw_coin_entry/src/error/address_error.rs create mode 100644 rust/tw_coin_entry/src/error/impl_from.rs create mode 100644 rust/tw_coin_entry/src/error/mod.rs create mode 100644 rust/tw_coin_entry/src/error/tw_error.rs create mode 100644 rust/tw_coin_entry/src/error/tw_result.rs delete mode 100644 rust/tw_evm/src/modules/json_signer.rs create mode 100644 rust/tw_proto/src/impls.rs diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml index fed511e4054..653fbf0c135 100644 --- a/.github/workflows/linux-ci.yml +++ b/.github/workflows/linux-ci.yml @@ -15,24 +15,17 @@ jobs: runs-on: ubuntu-latest if: github.event.pull_request.draft == false steps: - # Work around https://github.com/actions/runner-images/issues/8659 - - name: Remove GCC 13 from runner image - shell: bash - run: | - sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r-ubuntu-test-jammy.list - sudo apt-get update - sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.7 libc6-dev=2.35-0ubuntu3.7 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 - uses: actions/checkout@v3 - name: Install system dependencies run: | - tools/install-sys-dependencies-linux + tools/install-sys-dependencies-linux ci tools/install-rust-dependencies - name: Cache internal dependencies id: internal_cache uses: actions/cache@v3 with: path: build/local - key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-dependencies') }} + key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-sys-dependencies-linux') }}-${{ hashFiles('tools/install-dependencies') }} - name: Install internal dependencies run: | tools/install-dependencies diff --git a/.github/workflows/linux-sampleapp-ci.yml b/.github/workflows/linux-sampleapp-ci.yml index 580351d8ca8..da1aa11656b 100644 --- a/.github/workflows/linux-sampleapp-ci.yml +++ b/.github/workflows/linux-sampleapp-ci.yml @@ -15,24 +15,17 @@ jobs: runs-on: ubuntu-latest if: github.event.pull_request.draft == false steps: - # Work around https://github.com/actions/runner-images/issues/8659 - - name: Remove GCC 13 from runner image - shell: bash - run: | - sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r-ubuntu-test-jammy.list - sudo apt-get update - sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.7 libc6-dev=2.35-0ubuntu3.7 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 - uses: actions/checkout@v3 - name: Install system dependencies run: | - tools/install-sys-dependencies-linux + tools/install-sys-dependencies-linux ci tools/install-rust-dependencies - name: Cache internal dependencies id: internal_cache uses: actions/cache@v3 with: path: build/local - key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-dependencies') }} + key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-sys-dependencies-linux') }}-${{ hashFiles('tools/install-dependencies') }} - name: Install internal dependencies run: | tools/install-dependencies diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/address.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/address.rs index 4ca837b12fb..3ec4f8516b5 100644 --- a/codegen-v2/src/codegen/rust/templates/blockchain_crate/address.rs +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/address.rs @@ -5,7 +5,7 @@ use std::fmt; use std::str::FromStr; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::AddressError; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; pub struct {BLOCKCHAIN}Address { diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/compiler.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/compiler.rs index aff08a27680..63d51f1df50 100644 --- a/codegen-v2/src/codegen/rust/templates/blockchain_crate/compiler.rs +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/compiler.rs @@ -4,7 +4,7 @@ use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_proto::{BLOCKCHAIN}::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs index 0cb80268185..c76b1b25d69 100644 --- a/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs @@ -9,7 +9,7 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::AddressResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/signer.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/signer.rs index 343b819b63f..3a64018e599 100644 --- a/codegen-v2/src/codegen/rust/templates/blockchain_crate/signer.rs +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/signer.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_proto::{BLOCKCHAIN}::Proto; diff --git a/rust/chains/tw_aptos/src/address.rs b/rust/chains/tw_aptos/src/address.rs index 520166c9798..3915750c625 100644 --- a/rust/chains/tw_aptos/src/address.rs +++ b/rust/chains/tw_aptos/src/address.rs @@ -6,7 +6,7 @@ use move_core_types::account_address::{AccountAddress, AccountAddressParseError} use std::fmt::{Display, Formatter}; use std::str::FromStr; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::AddressError; +use tw_coin_entry::error::prelude::*; use tw_hash::sha3::sha3_256; use tw_keypair::ed25519; use tw_memory::Data; diff --git a/rust/chains/tw_aptos/src/aptos_move_packages.rs b/rust/chains/tw_aptos/src/aptos_move_packages.rs index 3d3d318cc0d..e90e6bf2a4b 100644 --- a/rust/chains/tw_aptos/src/aptos_move_packages.rs +++ b/rust/chains/tw_aptos/src/aptos_move_packages.rs @@ -7,7 +7,7 @@ use move_core_types::account_address::AccountAddress; use move_core_types::ident_str; use move_core_types::language_storage::{ModuleId, TypeTag}; use serde_json::json; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_encoding::bcs; pub fn aptos_account_transfer( diff --git a/rust/chains/tw_aptos/src/bcs_encoding.rs b/rust/chains/tw_aptos/src/bcs_encoding.rs deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/rust/chains/tw_aptos/src/compiler.rs b/rust/chains/tw_aptos/src/compiler.rs index 73954261f19..53b9df11bd4 100644 --- a/rust/chains/tw_aptos/src/compiler.rs +++ b/rust/chains/tw_aptos/src/compiler.rs @@ -2,7 +2,7 @@ use crate::address::Address; use crate::transaction_builder; use std::str::FromStr; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_proto::Aptos::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; @@ -22,7 +22,9 @@ impl Compiler { input: Proto::SigningInput<'_>, ) -> SigningResult> { let builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone())?; - let sender = Address::from_str(&input.sender)?; + let sender = Address::from_str(&input.sender) + .into_tw() + .context("Invalid sender address")?; let signed_tx = builder .sender(sender.inner()) .sequence_number(input.sequence_number as u64) @@ -53,10 +55,10 @@ impl Compiler { let sender = Address::from_str(&input.sender)?; let signature = signatures .first() - .ok_or(SigningError(SigningErrorType::Error_signatures_count))?; + .or_tw_err(SigningErrorType::Error_signatures_count)?; let public_key = public_keys .first() - .ok_or(SigningError(SigningErrorType::Error_signatures_count))?; + .or_tw_err(SigningErrorType::Error_signatures_count)?; let signed_tx = builder .sender(sender.inner()) diff --git a/rust/chains/tw_aptos/src/entry.rs b/rust/chains/tw_aptos/src/entry.rs index 741502bdf93..bee4fb47ddc 100644 --- a/rust/chains/tw_aptos/src/entry.rs +++ b/rust/chains/tw_aptos/src/entry.rs @@ -9,7 +9,7 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/rust/chains/tw_aptos/src/liquid_staking.rs b/rust/chains/tw_aptos/src/liquid_staking.rs index 4b1027abe9e..2ce311e4350 100644 --- a/rust/chains/tw_aptos/src/liquid_staking.rs +++ b/rust/chains/tw_aptos/src/liquid_staking.rs @@ -7,7 +7,7 @@ use crate::transaction_payload::{EntryFunction, TransactionPayload}; use move_core_types::{account_address::AccountAddress, ident_str, language_storage::ModuleId}; use serde_json::json; use std::str::FromStr; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::bcs; use tw_proto::{ Aptos::Proto::mod_LiquidStaking::OneOfliquid_stake_transaction_payload, @@ -91,7 +91,9 @@ impl TryFrom> for LiquidStakingOperation { OneOfliquid_stake_transaction_payload::stake(stake_msg) => { let smart_contract_address = AccountAddress::from_str(&value.smart_contract_address) - .map_err(from_account_error)?; + .map_err(from_account_error) + .into_tw() + .context("Invalid Smart Contract address")?; Ok(LiquidStakingOperation::Stake(Stake { amount: stake_msg.amount, smart_contract_address, @@ -100,7 +102,9 @@ impl TryFrom> for LiquidStakingOperation { OneOfliquid_stake_transaction_payload::unstake(unstake_msg) => { let smart_contract_address = AccountAddress::from_str(&value.smart_contract_address) - .map_err(from_account_error)?; + .map_err(from_account_error) + .into_tw() + .context("Invalid Smart Contract address")?; Ok(LiquidStakingOperation::Unstake(Unstake { amount: unstake_msg.amount, smart_contract_address, @@ -109,14 +113,16 @@ impl TryFrom> for LiquidStakingOperation { OneOfliquid_stake_transaction_payload::claim(claim) => { let smart_contract_address = AccountAddress::from_str(&value.smart_contract_address) - .map_err(from_account_error)?; + .map_err(from_account_error) + .into_tw() + .context("Invalid Smart Contract address")?; Ok(LiquidStakingOperation::Claim(Claim { idx: claim.idx, smart_contract_address, })) }, OneOfliquid_stake_transaction_payload::None => { - Err(SigningError(SigningErrorType::Error_invalid_params)) + SigningError::err(SigningErrorType::Error_invalid_params) }, } } diff --git a/rust/chains/tw_aptos/src/nft.rs b/rust/chains/tw_aptos/src/nft.rs index caf46b432bb..172308b4ee2 100644 --- a/rust/chains/tw_aptos/src/nft.rs +++ b/rust/chains/tw_aptos/src/nft.rs @@ -5,7 +5,7 @@ use crate::address::from_account_error; use move_core_types::account_address::AccountAddress; use std::str::FromStr; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_proto::Aptos::Proto::mod_NftMessage::OneOfnft_transaction_payload; use tw_proto::Aptos::Proto::{CancelOfferNftMessage, ClaimNftMessage, NftMessage, OfferNftMessage}; @@ -47,7 +47,8 @@ impl TryFrom> for NftOperation { Ok(NftOperation::Claim(Claim::try_from(msg)?)) }, OneOfnft_transaction_payload::None => { - Err(SigningError(SigningErrorType::Error_invalid_params)) + SigningError::err(SigningErrorType::Error_invalid_params) + .context("No transaction payload provided") }, } } @@ -106,8 +107,14 @@ impl TryFrom> for Offer { fn try_from(value: CancelOfferNftMessage) -> SigningResult { Ok(Offer { - receiver: AccountAddress::from_str(&value.receiver).map_err(from_account_error)?, - creator: AccountAddress::from_str(&value.creator).map_err(from_account_error)?, + receiver: AccountAddress::from_str(&value.receiver) + .map_err(from_account_error) + .into_tw() + .context("Invalid receiver address")?, + creator: AccountAddress::from_str(&value.creator) + .map_err(from_account_error) + .into_tw() + .context("Invalid creator address")?, collection: value.collectionName.as_bytes().to_vec(), name: value.name.as_bytes().to_vec(), property_version: value.property_version, @@ -137,8 +144,14 @@ impl TryFrom> for Claim { fn try_from(value: ClaimNftMessage) -> SigningResult { Ok(Claim { - sender: AccountAddress::from_str(&value.sender).map_err(from_account_error)?, - creator: AccountAddress::from_str(&value.creator).map_err(from_account_error)?, + sender: AccountAddress::from_str(&value.sender) + .map_err(from_account_error) + .into_tw() + .context("Invalid sender address")?, + creator: AccountAddress::from_str(&value.creator) + .map_err(from_account_error) + .into_tw() + .context("Invalid creator address")?, collection: value.collectionName.as_bytes().to_vec(), name: value.name.as_bytes().to_vec(), property_version: value.property_version, diff --git a/rust/chains/tw_aptos/src/signer.rs b/rust/chains/tw_aptos/src/signer.rs index 7249e7bec5e..8937e821081 100644 --- a/rust/chains/tw_aptos/src/signer.rs +++ b/rust/chains/tw_aptos/src/signer.rs @@ -5,7 +5,7 @@ use crate::address::Address; use crate::transaction_builder; use std::str::FromStr; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_keypair::ed25519; use tw_proto::Aptos::Proto; @@ -24,7 +24,9 @@ impl Signer { ) -> SigningResult> { let key_pair = ed25519::sha512::KeyPair::try_from(input.private_key.as_ref())?; let builder = transaction_builder::TransactionFactory::new_from_protobuf(input.clone())?; - let sender = Address::from_str(&input.sender)?; + let sender = Address::from_str(&input.sender) + .into_tw() + .context("Invalid sender address")?; let signed_tx = builder .sender(sender.inner()) .sequence_number(input.sequence_number as u64) diff --git a/rust/chains/tw_aptos/src/transaction.rs b/rust/chains/tw_aptos/src/transaction.rs index c4822285566..f4ca4a2c151 100644 --- a/rust/chains/tw_aptos/src/transaction.rs +++ b/rust/chains/tw_aptos/src/transaction.rs @@ -8,7 +8,7 @@ use move_core_types::account_address::AccountAddress; use serde::Serialize; use serde_json::{json, Value}; use std::borrow::Cow; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex::encode; use tw_encoding::{bcs, EncodingResult}; use tw_keypair::ed25519::sha512::KeyPair; @@ -128,7 +128,10 @@ impl RawTransaction { } fn msg_to_sign(&self) -> SigningResult { - let serialized = self.serialize()?; + let serialized = self + .serialize() + .into_tw() + .context("Error serializing RawTransaction")?; let mut preimage = tw_hash::sha3::sha3_256(APTOS_SALT); preimage.extend_from_slice(serialized.as_slice()); Ok(preimage) diff --git a/rust/chains/tw_aptos/src/transaction_builder.rs b/rust/chains/tw_aptos/src/transaction_builder.rs index 723121f9910..deae84c8008 100644 --- a/rust/chains/tw_aptos/src/transaction_builder.rs +++ b/rust/chains/tw_aptos/src/transaction_builder.rs @@ -21,7 +21,7 @@ use move_core_types::account_address::AccountAddress; use move_core_types::language_storage::TypeTag; use serde_json::Value; use std::str::FromStr; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_proto::Aptos::Proto::mod_SigningInput::OneOftransaction_payload; use tw_proto::Aptos::Proto::SigningInput; @@ -49,10 +49,12 @@ impl TransactionBuilder { pub fn build(self) -> SigningResult { let sender = self .sender - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("Invalid sender address")?; let sequence_number = self .sequence_number - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("Invalid sequence number")?; Ok(RawTransaction::new( sender, sequence_number, @@ -91,22 +93,31 @@ impl TransactionFactory { match input.transaction_payload { OneOftransaction_payload::transfer(transfer) => factory .implicitly_create_user_account_and_transfer( - AccountAddress::from_str(&transfer.to).map_err(from_account_error)?, + AccountAddress::from_str(&transfer.to) + .map_err(from_account_error) + .into_tw() + .context("Invalid destination address")?, transfer.amount, ), OneOftransaction_payload::token_transfer(token_transfer) => { let func = token_transfer .function - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("'TokenTransferMessage::function' is not set")?; factory.coins_transfer( - AccountAddress::from_str(&token_transfer.to).map_err(from_account_error)?, + AccountAddress::from_str(&token_transfer.to) + .map_err(from_account_error) + .into_tw() + .context("Invalid destination address")?, token_transfer.amount, convert_proto_struct_tag_to_type_tag(func)?, ) }, OneOftransaction_payload::create_account(create_account) => { let address = AccountAddress::from_str(&create_account.auth_key) - .map_err(from_account_error)?; + .map_err(from_account_error) + .into_tw() + .context("Invalid 'auth_key' address")?; factory.create_user_account(address) }, OneOftransaction_payload::nft_message(nft_message) => { @@ -115,7 +126,8 @@ impl TransactionFactory { OneOftransaction_payload::register_token(register_token) => { let function = register_token .function - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("'ManagedTokensRegisterMessage::function' is not set")?; Ok(factory.register_token(convert_proto_struct_tag_to_type_tag(function)?)) }, OneOftransaction_payload::liquid_staking_message(msg) => { @@ -124,22 +136,27 @@ impl TransactionFactory { OneOftransaction_payload::token_transfer_coins(token_transfer_coins) => { let func = token_transfer_coins .function - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("'TokenTransferCoinsMessage::function' is not set")?; factory.implicitly_create_user_and_coins_transfer( AccountAddress::from_str(&token_transfer_coins.to) - .map_err(from_account_error)?, + .map_err(from_account_error) + .into_tw() + .context("Invalid destination address")?, token_transfer_coins.amount, convert_proto_struct_tag_to_type_tag(func)?, ) }, OneOftransaction_payload::None => { let is_blind_sign = !input.any_encoded.is_empty(); - let v = serde_json::from_str::(&input.any_encoded)?; + let v = serde_json::from_str::(&input.any_encoded) + .into_tw() + .context("Error decoding 'SigningInput::any_encoded' as JSON")?; if is_blind_sign { let entry_function = EntryFunction::try_from(v)?; Ok(factory.payload(TransactionPayload::EntryFunction(entry_function))) } else { - Err(SigningError(SigningErrorType::Error_input_parse)) + SigningError::err(SigningErrorType::Error_input_parse) } }, } diff --git a/rust/chains/tw_aptos/src/transaction_payload.rs b/rust/chains/tw_aptos/src/transaction_payload.rs index a14666ad2b5..aa1c74d02da 100644 --- a/rust/chains/tw_aptos/src/transaction_payload.rs +++ b/rust/chains/tw_aptos/src/transaction_payload.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use std::default::Default; use std::str::FromStr; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::{bcs, EncodingError, EncodingResult}; use tw_memory::Data; use tw_proto::Aptos; @@ -36,8 +36,9 @@ impl From for EntryFunctionError { } impl From for SigningError { - fn from(_: EntryFunctionError) -> Self { - SigningError(SigningErrorType::Error_invalid_params) + fn from(e: EntryFunctionError) -> Self { + SigningError::new(SigningErrorType::Error_invalid_params) + .context(format!("Error decoding EntryFunction: {e:?}")) } } @@ -122,7 +123,7 @@ pub fn convert_proto_struct_tag_to_type_tag( "{}::{}::{}", struct_tag.account_address, struct_tag.module, struct_tag.name )) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_params)) + .tw_err(|_| SigningErrorType::Error_invalid_params) } pub fn convert_type_tag_to_struct_tag(type_tag: TypeTag) -> Aptos::Proto::StructTag<'static> { diff --git a/rust/chains/tw_aptos/tests/signer.rs b/rust/chains/tw_aptos/tests/signer.rs index 5b5268539d4..e69c6894839 100644 --- a/rust/chains/tw_aptos/tests/signer.rs +++ b/rust/chains/tw_aptos/tests/signer.rs @@ -10,7 +10,7 @@ use tw_aptos::liquid_staking::{LiquidStakingOperation, Stake, Unstake}; use tw_aptos::nft::{Claim, NftOperation, Offer}; use tw_aptos::signer::Signer; use tw_aptos::transaction_payload::convert_type_tag_to_struct_tag; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex; use tw_proto::Aptos::Proto; use tw_proto::Aptos::Proto::{SigningInput, SigningOutput}; diff --git a/rust/chains/tw_binance/src/address.rs b/rust/chains/tw_binance/src/address.rs index 3c718a97bd8..45b00e26bb4 100644 --- a/rust/chains/tw_binance/src/address.rs +++ b/rust/chains/tw_binance/src/address.rs @@ -9,7 +9,7 @@ use tw_bech32_address::bech32_prefix::Bech32Prefix; use tw_bech32_address::Bech32Address; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_cosmos_sdk::address::CosmosAddress; use tw_keypair::tw::PublicKey; use tw_memory::Data; diff --git a/rust/chains/tw_binance/src/compiler.rs b/rust/chains/tw_binance/src/compiler.rs index 754cdd854ed..400c37e36ec 100644 --- a/rust/chains/tw_binance/src/compiler.rs +++ b/rust/chains/tw_binance/src/compiler.rs @@ -11,7 +11,7 @@ use crate::transaction::SignerInfo; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; use tw_coin_entry::common::compile_input::SingleSignaturePubkey; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_cosmos_sdk::modules::serializer::json_serializer::JsonSerializer; use tw_cosmos_sdk::public_key::secp256k1::Secp256PublicKey; @@ -89,8 +89,8 @@ impl BinanceCompiler { let encoded_tx = BinanceAminoSerializer::serialize_signed_tx(&signed_tx)?; - let signature_json = serde_json::to_string(&signature_json) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let signature_json = + serde_json::to_string(&signature_json).tw_err(|_| SigningErrorType::Error_internal)?; Ok(Proto::SigningOutput { encoded: encoded_tx.into(), signature: signature_bytes.into(), diff --git a/rust/chains/tw_binance/src/entry.rs b/rust/chains/tw_binance/src/entry.rs index f7798bd0f39..cd552881c9d 100644 --- a/rust/chains/tw_binance/src/entry.rs +++ b/rust/chains/tw_binance/src/entry.rs @@ -11,7 +11,7 @@ use tw_bech32_address::bech32_prefix::Bech32Prefix; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::AddressResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/rust/chains/tw_binance/src/modules/preimager.rs b/rust/chains/tw_binance/src/modules/preimager.rs index ba881f8beae..45e2dacdf53 100644 --- a/rust/chains/tw_binance/src/modules/preimager.rs +++ b/rust/chains/tw_binance/src/modules/preimager.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use crate::transaction::UnsignedTransaction; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_hash::{sha2, H256}; pub struct JsonTxPreimage { @@ -15,8 +15,8 @@ pub struct JsonPreimager; impl JsonPreimager { pub fn preimage_hash(unsigned: &UnsignedTransaction) -> SigningResult { - let encoded_tx = serde_json::to_string(unsigned) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let encoded_tx = + serde_json::to_string(unsigned).tw_err(|_| SigningErrorType::Error_internal)?; let tx_hash = sha2::sha256(encoded_tx.as_bytes()); let tx_hash = H256::try_from(tx_hash.as_slice()).expect("sha256 must return 32 bytes"); Ok(JsonTxPreimage { diff --git a/rust/chains/tw_binance/src/modules/serializer.rs b/rust/chains/tw_binance/src/modules/serializer.rs index cb1ea5ff4d6..88ec0ad22f3 100644 --- a/rust/chains/tw_binance/src/modules/serializer.rs +++ b/rust/chains/tw_binance/src/modules/serializer.rs @@ -5,7 +5,7 @@ use crate::amino::AminoEncoder; use crate::transaction::SignedTransaction; use std::borrow::Cow; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_cosmos_sdk::public_key::CosmosPublicKey; use tw_memory::Data; use tw_misc::traits::ToBytesVec; @@ -58,6 +58,6 @@ impl BinanceAminoSerializer { sequence: signed.unsigned.sequence, }; // There is no need to use Amino encoding here as the prefix is empty. - serialize(&sign_msg).map_err(|_| SigningError(SigningErrorType::Error_internal)) + serialize(&sign_msg).tw_err(|_| SigningErrorType::Error_internal) } } diff --git a/rust/chains/tw_binance/src/modules/tx_builder.rs b/rust/chains/tw_binance/src/modules/tx_builder.rs index 4b6fb2ebbea..ce1340d63c0 100644 --- a/rust/chains/tw_binance/src/modules/tx_builder.rs +++ b/rust/chains/tw_binance/src/modules/tx_builder.rs @@ -6,7 +6,7 @@ use crate::transaction::message::{BinanceMessageEnum, TWBinanceProto}; use crate::transaction::UnsignedTransaction; use std::borrow::Cow; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_proto::Binance::Proto; pub struct TxBuilder; @@ -32,7 +32,8 @@ impl TxBuilder { unsigned: &UnsignedTransaction, ) -> SigningResult> { if unsigned.msgs.len() != 1 { - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("Expected exactly one Transaction Message"); } let msg = unsigned .msgs diff --git a/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs b/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs index a1e60b7da16..e66b4688a2f 100644 --- a/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs +++ b/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs @@ -5,7 +5,7 @@ use crate::modules::tx_builder::TxBuilder; use crate::modules::wallet_connect::types::SignAminoRequest; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::wallet_connector::WalletConnector; use tw_coin_entry::signing_output_error; use tw_proto::WalletConnect::Proto::{ @@ -32,7 +32,8 @@ impl BinanceWalletConnector { ) -> SigningResult> { match request.method { WCProto::Method::CosmosSignAmino => Self::parse_sign_amino_request(coin, request), - _ => Err(SigningError(SigningErrorType::Error_not_supported)), + _ => SigningError::err(SigningErrorType::Error_not_supported) + .context("Unknown WalletConnect method"), } } @@ -41,7 +42,8 @@ impl BinanceWalletConnector { request: WCProto::ParseRequestInput<'_>, ) -> SigningResult> { let amino_req: SignAminoRequest = serde_json::from_str(&request.payload) - .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; + .tw_err(|_| SigningErrorType::Error_input_parse) + .context("Error deserializing WalletConnect signAmino request as JSON")?; // Parse a `SigningInput` from the given `signDoc`. let signing_input = TxBuilder::unsigned_tx_to_proto(&amino_req.sign_doc)?; diff --git a/rust/chains/tw_binance/src/signer.rs b/rust/chains/tw_binance/src/signer.rs index b17553e067e..86cb5da5b4b 100644 --- a/rust/chains/tw_binance/src/signer.rs +++ b/rust/chains/tw_binance/src/signer.rs @@ -9,7 +9,7 @@ use crate::modules::tx_builder::TxBuilder; use crate::signature::BinanceSignature; use crate::transaction::SignerInfo; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_cosmos_sdk::modules::serializer::json_serializer::JsonSerializer; use tw_cosmos_sdk::public_key::secp256k1::Secp256PublicKey; @@ -54,8 +54,8 @@ impl BinanceSigner { }); let encoded_tx = BinanceAminoSerializer::serialize_signed_tx(&signed_tx)?; - let signature_json = serde_json::to_string(&signature_json) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let signature_json = + serde_json::to_string(&signature_json).tw_err(|_| SigningErrorType::Error_internal)?; Ok(Proto::SigningOutput { encoded: encoded_tx.into(), signature: signature_bytes.into(), diff --git a/rust/chains/tw_binance/src/transaction/message/htlt_order.rs b/rust/chains/tw_binance/src/transaction/message/htlt_order.rs index e55e3e4b3e5..902ae019e82 100644 --- a/rust/chains/tw_binance/src/transaction/message/htlt_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/htlt_order.rs @@ -8,7 +8,7 @@ use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; use serde::{Deserialize, Serialize}; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex::as_hex; use tw_memory::Data; use tw_proto::Binance::Proto; diff --git a/rust/chains/tw_binance/src/transaction/message/mod.rs b/rust/chains/tw_binance/src/transaction/message/mod.rs index 5974ba50400..0879bcf5b3e 100644 --- a/rust/chains/tw_binance/src/transaction/message/mod.rs +++ b/rust/chains/tw_binance/src/transaction/message/mod.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize, Serializer}; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; use tw_proto::Binance::Proto::{self, mod_SigningInput::OneOforder_oneof as BinanceMessageProto}; @@ -138,7 +138,7 @@ impl TWBinanceProto for BinanceMessageEnum { side_chain_delegate::StakeMigrationOrder::from_tw_proto(coin, order) .map(BinanceMessageEnum::StakeMigrationOrder) }, - BinanceMessageProto::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + BinanceMessageProto::None => SigningError::err(SigningErrorType::Error_invalid_params), } } diff --git a/rust/chains/tw_binance/src/transaction/message/send_order.rs b/rust/chains/tw_binance/src/transaction/message/send_order.rs index ef1b5107134..6ffd7d69be2 100644 --- a/rust/chains/tw_binance/src/transaction/message/send_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/send_order.rs @@ -8,7 +8,7 @@ use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; use serde::{Deserialize, Serialize}; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; use tw_proto::Binance::Proto; diff --git a/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs b/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs index 690d78def18..4684056cede 100644 --- a/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs +++ b/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs @@ -8,7 +8,7 @@ use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; use serde::{Deserialize, Serialize}; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_evm::address::Address as EthereumAddress; use tw_memory::Data; use tw_misc::serde::Typed; @@ -52,7 +52,7 @@ impl TWBinanceProto for SideDelegateOrder { let delegation = msg .delegation .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params)?; let value = SideDelegateOrderValue { delegator_addr, @@ -118,7 +118,7 @@ impl TWBinanceProto for SideRedelegateOrder { let amount = msg .amount .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params)?; let value = SideRedelegateOrderValue { delegator_addr, @@ -182,7 +182,7 @@ impl TWBinanceProto for SideUndelegateOrder { let amount = msg .amount .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params)?; let value = SideUndelegateOrderValue { delegator_addr, @@ -248,7 +248,7 @@ impl TWBinanceProto for StakeMigrationOrder { let amount = msg .amount .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params)?; let value = StakeMigrationOrderValue { amount: Token::from_tw_proto(amount), diff --git a/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs b/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs index 2647d6553cb..9e2e4e21e8c 100644 --- a/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs @@ -8,7 +8,7 @@ use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; use serde::{Deserialize, Serialize}; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; use tw_proto::Binance::Proto; diff --git a/rust/chains/tw_binance/src/transaction/message/token_order.rs b/rust/chains/tw_binance/src/transaction/message/token_order.rs index ecff5236773..ec88ba88e26 100644 --- a/rust/chains/tw_binance/src/transaction/message/token_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/token_order.rs @@ -8,7 +8,7 @@ use crate::transaction::message::{BinanceMessage, TWBinanceProto}; use serde::{Deserialize, Serialize}; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; use tw_proto::Binance::Proto; diff --git a/rust/chains/tw_binance/src/transaction/message/trade_order.rs b/rust/chains/tw_binance/src/transaction/message/trade_order.rs index 5aced178ea6..0add4dcd87b 100644 --- a/rust/chains/tw_binance/src/transaction/message/trade_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/trade_order.rs @@ -8,7 +8,7 @@ use crate::transaction::message::{BinanceMessage, TWBinanceProto}; use serde::{Deserialize, Serialize}; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; use tw_proto::Binance::Proto; @@ -61,7 +61,7 @@ impl TWBinanceProto for NewTradeOrder { fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { let order_type = OrderType::from_repr(msg.ordertype) - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params)?; let sender = BinanceAddress::from_key_hash_with_coin(coin, msg.sender.to_vec())?; Ok(NewTradeOrder { diff --git a/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs b/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs index ae8aabff1ca..30dfef73f56 100644 --- a/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs @@ -8,7 +8,7 @@ use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; use serde::{Deserialize, Serialize}; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_evm::address::Address as EthereumAddress; use tw_hash::H160; use tw_memory::Data; @@ -41,14 +41,14 @@ impl TWBinanceProto for TransferOutOrder { fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; - let to_bytes = H160::try_from(msg.to.as_ref()) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_address))?; + let to_bytes = + H160::try_from(msg.to.as_ref()).tw_err(|_| SigningErrorType::Error_invalid_address)?; let to = EthereumAddress::from_bytes(to_bytes); let amount_proto = msg .amount .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params)?; Ok(TransferOutOrder { from, diff --git a/rust/chains/tw_cosmos/src/entry.rs b/rust/chains/tw_cosmos/src/entry.rs index 1f8650a70cd..67a15bc2d1e 100644 --- a/rust/chains/tw_cosmos/src/entry.rs +++ b/rust/chains/tw_cosmos/src/entry.rs @@ -6,7 +6,7 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::AddressResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/rust/chains/tw_ethereum/src/entry.rs b/rust/chains/tw_ethereum/src/entry.rs index 86b351aad25..13f48c92701 100644 --- a/rust/chains/tw_ethereum/src/entry.rs +++ b/rust/chains/tw_ethereum/src/entry.rs @@ -6,7 +6,8 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; +use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_coin_entry::modules::wallet_connector::NoWalletConnector; @@ -15,7 +16,6 @@ use tw_evm::address::Address; use tw_evm::evm_context::StandardEvmContext; use tw_evm::evm_entry::EvmEntry; use tw_evm::modules::compiler::Compiler; -use tw_evm::modules::json_signer::EthJsonSigner; use tw_evm::modules::message_signer::EthMessageSigner; use tw_evm::modules::signer::Signer; use tw_keypair::tw::PublicKey; @@ -32,7 +32,7 @@ impl CoinEntry for EthereumEntry { type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; // Optional modules: - type JsonSigner = EthJsonSigner; + type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = EthMessageSigner; type WalletConnector = NoWalletConnector; @@ -96,11 +96,6 @@ impl CoinEntry for EthereumEntry { Compiler::::compile(input, signatures, public_keys) } - #[inline] - fn json_signer(&self) -> Option { - Some(EthJsonSigner::default()) - } - #[inline] fn message_signer(&self) -> Option { Some(EthMessageSigner) diff --git a/rust/chains/tw_ethereum/tests/compiler.rs b/rust/chains/tw_ethereum/tests/compiler.rs index bdf14b3e2a9..7d88e7279e2 100644 --- a/rust/chains/tw_ethereum/tests/compiler.rs +++ b/rust/chains/tw_ethereum/tests/compiler.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use tw_coin_entry::coin_entry_ext::CoinEntryExt; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_encoding::hex; use tw_ethereum::entry::EthereumEntry; diff --git a/rust/chains/tw_greenfield/src/address.rs b/rust/chains/tw_greenfield/src/address.rs index d0d4d794448..94b9565b17a 100644 --- a/rust/chains/tw_greenfield/src/address.rs +++ b/rust/chains/tw_greenfield/src/address.rs @@ -6,7 +6,7 @@ use serde::Serialize; use std::fmt; use std::str::FromStr; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::AddressError; +use tw_coin_entry::error::prelude::*; use tw_cosmos_sdk::address::CosmosAddress; use tw_evm::address::Address as EthereumAddress; use tw_keypair::ecdsa::secp256k1; diff --git a/rust/chains/tw_greenfield/src/compiler.rs b/rust/chains/tw_greenfield/src/compiler.rs index a37ac39a7f2..e9db4725199 100644 --- a/rust/chains/tw_greenfield/src/compiler.rs +++ b/rust/chains/tw_greenfield/src/compiler.rs @@ -11,7 +11,7 @@ use std::borrow::Cow; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; use tw_coin_entry::common::compile_input::SingleSignaturePubkey; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_cosmos_sdk::modules::broadcast_msg::{BroadcastMode, BroadcastMsg}; use tw_cosmos_sdk::modules::serializer::json_serializer::JsonSerializer; @@ -72,8 +72,12 @@ impl GreenfieldCompiler { } = SingleSignaturePubkey::from_sign_pubkey_list(signatures, public_keys)?; let public_key_params = None; - let public_key = GreenfieldPublicKey::from_bytes(coin, &public_key, public_key_params)?; - let signature = GreenfieldSignature::try_from(raw_signature.as_slice())?; + let public_key = GreenfieldPublicKey::from_bytes(coin, &public_key, public_key_params) + .into_tw() + .context("Invalid provided public key")?; + let signature = GreenfieldSignature::try_from(raw_signature.as_slice()) + .into_tw() + .context("Invalid provided signature")?; let signature_bytes = signature.to_vec(); // Set the public key. It will be used to construct a signer info. @@ -91,7 +95,8 @@ impl GreenfieldCompiler { signature_bytes.clone(), ); let signature_json = serde_json::to_string(&[signature_json]) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing signatures as JSON")?; Ok(Proto::SigningOutput { signature: Cow::from(signature_bytes), diff --git a/rust/chains/tw_greenfield/src/entry.rs b/rust/chains/tw_greenfield/src/entry.rs index 52ea07203f4..01ee90e50ae 100644 --- a/rust/chains/tw_greenfield/src/entry.rs +++ b/rust/chains/tw_greenfield/src/entry.rs @@ -9,7 +9,7 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/rust/chains/tw_greenfield/src/modules/eip712_signer.rs b/rust/chains/tw_greenfield/src/modules/eip712_signer.rs index 7b9fcb34f05..c5ead1f19de 100644 --- a/rust/chains/tw_greenfield/src/modules/eip712_signer.rs +++ b/rust/chains/tw_greenfield/src/modules/eip712_signer.rs @@ -7,10 +7,10 @@ use crate::eip712_types::{ }; use crate::transaction::GreenfieldUnsignedTransaction; use std::collections::BTreeMap; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_evm::message::eip712::eip712_message::Eip712Message; use tw_evm::message::eip712::message_types::MessageTypesBuilder; -use tw_evm::message::EthMessage; +use tw_evm::message::{to_signing, EthMessage}; use tw_hash::H256; use tw_number::U256; @@ -65,15 +65,17 @@ impl Eip712Signer { let msg_to_sign = Eip712Message { types: types_builder.build(), domain: serde_json::to_value(domain) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?, + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing EIP712Domain as JSON")?, primary_type: Eip712Transaction::TYPE_NAME.to_string(), message: serde_json::to_value(tx_to_sign) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?, + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing EIP712 message payload as JSON")?, }; - let tx_hash = msg_to_sign.hash()?; - let eip712_tx = serde_json::to_string(&msg_to_sign) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let tx_hash = msg_to_sign.hash().map_err(to_signing)?; + let eip712_tx = + serde_json::to_string(&msg_to_sign).tw_err(|_| SigningErrorType::Error_internal)?; Ok(Eip712TxPreimage { eip712_tx, tx_hash }) } diff --git a/rust/chains/tw_greenfield/src/modules/tx_builder.rs b/rust/chains/tw_greenfield/src/modules/tx_builder.rs index 2e0951c8cce..6a12bb2aa6e 100644 --- a/rust/chains/tw_greenfield/src/modules/tx_builder.rs +++ b/rust/chains/tw_greenfield/src/modules/tx_builder.rs @@ -11,7 +11,7 @@ use crate::transaction::{ }; use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_cosmos_sdk::public_key::CosmosPublicKey; use tw_cosmos_sdk::transaction::{Coin, SignerInfo}; use tw_misc::traits::OptionalEmpty; @@ -36,11 +36,13 @@ impl TxBuilder { let fee = input .fee .as_ref() - .ok_or(SigningError(SigningErrorType::Error_wrong_fee))?; + .or_tw_err(SigningErrorType::Error_wrong_fee) + .context("No 'fee' specified")?; let fee = Self::fee_from_proto(fee, &signer)?; let eth_chain_id = U256::from_str(&input.eth_chain_id) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?; + .tw_err(|_| SigningErrorType::Error_invalid_params) + .context("Invalid ETH chain ID")?; Ok(GreenfieldUnsignedTransaction { signer, @@ -89,7 +91,9 @@ impl TxBuilder { } fn coin_from_proto(input: &Proto::Amount<'_>) -> SigningResult { - let amount = U256::from_str(&input.amount)?; + let amount = U256::from_str(&input.amount) + .into_tw() + .context("Invalid amount: expected uint256 decimal-string")?; Ok(Coin { amount, denom: input.denom.to_string(), @@ -98,7 +102,8 @@ impl TxBuilder { fn tx_body_from_proto(input: &Proto::SigningInput<'_>) -> SigningResult { if input.messages.is_empty() { - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("No transaction messages provided"); } let messages = input @@ -122,7 +127,8 @@ impl TxBuilder { MessageEnum::bridge_transfer_out(ref transfer_out) => { Self::bridge_transfer_out_from_proto(transfer_out) }, - MessageEnum::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + MessageEnum::None => SigningError::err(SigningErrorType::Error_invalid_params) + .context("No message type provided"), } } @@ -139,8 +145,12 @@ impl TxBuilder { .collect::>()?; let msg = SendMessage { custom_type_prefix: send.type_prefix.to_string().empty_or_some(), - from_address: GreenfieldAddress::from_str(&send.from_address)?, - to_address: GreenfieldAddress::from_str(&send.to_address)?, + from_address: GreenfieldAddress::from_str(&send.from_address) + .into_tw() + .context("Invalid sender address")?, + to_address: GreenfieldAddress::from_str(&send.to_address) + .into_tw() + .context("Invalid recipient address")?, amount: amounts, }; Ok(Box::new(GreenfieldSendMessage(msg))) @@ -154,13 +164,18 @@ impl TxBuilder { let amount = transfer_out .amount .as_ref() - .ok_or(SigningError(SigningErrorType::Error_wrong_fee))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("No transfer amount specified")?; let msg = GreenfieldTransferOut { custom_type_prefix: transfer_out.type_prefix.to_string().empty_or_some(), amount: Self::coin_from_proto(amount)?, - from: GreenfieldAddress::from_str(&transfer_out.from_address)?, - to: GreenfieldAddress::from_str(&transfer_out.to_address)?, + from: GreenfieldAddress::from_str(&transfer_out.from_address) + .into_tw() + .context("Invalid sender address")?, + to: GreenfieldAddress::from_str(&transfer_out.to_address) + .into_tw() + .context("Invalid recipient address")?, }; Ok(Box::new(msg)) } diff --git a/rust/chains/tw_greenfield/src/signer.rs b/rust/chains/tw_greenfield/src/signer.rs index 6b0d2c88e28..afde1ed2747 100644 --- a/rust/chains/tw_greenfield/src/signer.rs +++ b/rust/chains/tw_greenfield/src/signer.rs @@ -7,7 +7,7 @@ use crate::modules::eip712_signer::{Eip712Signer, Eip712TxPreimage}; use crate::modules::tx_builder::TxBuilder; use std::borrow::Cow; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_keypair::ecdsa::secp256k1; use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; diff --git a/rust/chains/tw_greenfield/src/transaction/message/send_order.rs b/rust/chains/tw_greenfield/src/transaction/message/send_order.rs index 8d9ebc62ccf..7733629ba9d 100644 --- a/rust/chains/tw_greenfield/src/transaction/message/send_order.rs +++ b/rust/chains/tw_greenfield/src/transaction/message/send_order.rs @@ -5,7 +5,7 @@ use crate::address::GreenfieldAddress; use crate::transaction::message::type_msg_amount::TypeMsgAmount; use crate::transaction::message::GreenfieldMessage; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_cosmos_sdk::proto::cosmos as CosmosProto; use tw_cosmos_sdk::transaction::message::cosmos_bank_message::SendMessage; use tw_cosmos_sdk::transaction::message::{ diff --git a/rust/chains/tw_greenfield/src/transaction/message/transfer_out.rs b/rust/chains/tw_greenfield/src/transaction/message/transfer_out.rs index f626db7d5a7..00a01116db5 100644 --- a/rust/chains/tw_greenfield/src/transaction/message/transfer_out.rs +++ b/rust/chains/tw_greenfield/src/transaction/message/transfer_out.rs @@ -6,7 +6,7 @@ use crate::address::GreenfieldAddress; use crate::transaction::message::type_msg_amount::TypeMsgAmount; use crate::transaction::message::GreenfieldMessage; use serde::Serialize; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_cosmos_sdk::modules::serializer::protobuf_serializer::build_coin; use tw_cosmos_sdk::proto::greenfield as GreenfieldProto; use tw_cosmos_sdk::transaction::message::{ diff --git a/rust/chains/tw_internet_computer/src/address.rs b/rust/chains/tw_internet_computer/src/address.rs index 80821df6f70..a30ab2ef984 100644 --- a/rust/chains/tw_internet_computer/src/address.rs +++ b/rust/chains/tw_internet_computer/src/address.rs @@ -2,10 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::{ - coin_entry::CoinAddress, - error::{AddressError, AddressResult}, -}; +use tw_coin_entry::{coin_entry::CoinAddress, error::prelude::*}; use tw_encoding::hex; use tw_hash::{crc32::crc32, sha2::sha224, H256}; use tw_keypair::ecdsa::secp256k1::PublicKey; diff --git a/rust/chains/tw_internet_computer/src/context.rs b/rust/chains/tw_internet_computer/src/context.rs index f429d4bc5db..41cee32fb86 100644 --- a/rust/chains/tw_internet_computer/src/context.rs +++ b/rust/chains/tw_internet_computer/src/context.rs @@ -30,7 +30,7 @@ mod test { use std::marker::PhantomData; - use tw_coin_entry::error::AddressResult; + use tw_coin_entry::error::prelude::*; use super::*; diff --git a/rust/chains/tw_internet_computer/src/entry.rs b/rust/chains/tw_internet_computer/src/entry.rs index 446ed2367fd..a11d07ad9cc 100644 --- a/rust/chains/tw_internet_computer/src/entry.rs +++ b/rust/chains/tw_internet_computer/src/entry.rs @@ -8,7 +8,7 @@ use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_coin_entry::{ coin_context::CoinContext, coin_entry::CoinEntry, - error::{AddressError, AddressResult, SigningError}, + error::prelude::*, modules::{ json_signer::NoJsonSigner, message_signer::NoMessageSigner, plan_builder::NoPlanBuilder, wallet_connector::NoWalletConnector, @@ -66,7 +66,7 @@ impl CoinEntry for InternetComputerEntry { public_key: tw_keypair::tw::PublicKey, _derivation: tw_coin_entry::derivation::Derivation, _prefix: Option, - ) -> tw_coin_entry::error::AddressResult { + ) -> AddressResult { let secp256k1_public_key = public_key .to_secp256k1() .ok_or(AddressError::PublicKeyTypeMismatch)?; @@ -89,7 +89,7 @@ impl CoinEntry for InternetComputerEntry { ) -> Self::PreSigningOutput { signing_output_error!( CompilerProto::PreSigningOutput, - SigningError(CommonError::Error_not_supported) + SigningError::new(CommonError::Error_not_supported) ) } @@ -102,7 +102,7 @@ impl CoinEntry for InternetComputerEntry { ) -> Self::SigningOutput { signing_output_error!( Proto::SigningOutput, - SigningError(CommonError::Error_not_supported) + SigningError::new(CommonError::Error_not_supported) ) } } diff --git a/rust/chains/tw_internet_computer/src/signer.rs b/rust/chains/tw_internet_computer/src/signer.rs index 311fd587f44..ee4a39a76de 100644 --- a/rust/chains/tw_internet_computer/src/signer.rs +++ b/rust/chains/tw_internet_computer/src/signer.rs @@ -4,10 +4,7 @@ use std::marker::PhantomData; -use tw_coin_entry::{ - error::{SigningError, SigningResult}, - signing_output_error, -}; +use tw_coin_entry::{error::prelude::*, signing_output_error}; use tw_keypair::ecdsa::secp256k1; use tw_proto::{Common::Proto::SigningError as CommonError, InternetComputer::Proto}; @@ -21,20 +18,20 @@ impl From for SigningError { fn from(error: transactions::SignTransactionError) -> Self { match error { transactions::SignTransactionError::InvalidArguments => { - SigningError(CommonError::Error_invalid_params) + SigningError::new(CommonError::Error_invalid_params) }, transactions::SignTransactionError::Identity(identity_error) => match identity_error { - identity::SigningError::Failed(_) => SigningError(CommonError::Error_signing), + identity::SigningError::Failed(_) => SigningError::new(CommonError::Error_signing), }, transactions::SignTransactionError::InvalidEnvelopePair | transactions::SignTransactionError::EncodingArgsFailed => { - SigningError(CommonError::Error_internal) + SigningError::new(CommonError::Error_internal) }, transactions::SignTransactionError::InvalidToAccountIdentifier => { - SigningError(CommonError::Error_invalid_address) + SigningError::new(CommonError::Error_invalid_address) }, transactions::SignTransactionError::InvalidAmount => { - SigningError(CommonError::Error_invalid_requested_token_amount) + SigningError::new(CommonError::Error_invalid_requested_token_amount) }, } } @@ -57,16 +54,15 @@ impl Signer { let private_key = secp256k1::PrivateKey::try_from(input.private_key.as_ref())?; let Some(ref transaction) = input.transaction else { - return Err(SigningError(CommonError::Error_invalid_params)); + return SigningError::err(CommonError::Error_invalid_params); }; let canister_id = Context::get_canister_id(); let signed_transaction = - sign_transaction(private_key, canister_id, &transaction.transaction_oneof) - .map_err(SigningError::from)?; + sign_transaction(private_key, canister_id, &transaction.transaction_oneof)?; let cbor_encoded_signed_transaction = tw_encoding::cbor::encode(&signed_transaction) - .map_err(|_| SigningError(CommonError::Error_internal))?; + .tw_err(|_| CommonError::Error_internal)?; Ok(Proto::SigningOutput { signed_transaction: cbor_encoded_signed_transaction.into(), diff --git a/rust/chains/tw_native_evmos/src/entry.rs b/rust/chains/tw_native_evmos/src/entry.rs index 9586259983c..86a9e715b73 100644 --- a/rust/chains/tw_native_evmos/src/entry.rs +++ b/rust/chains/tw_native_evmos/src/entry.rs @@ -7,7 +7,7 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::AddressResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/rust/chains/tw_native_injective/src/entry.rs b/rust/chains/tw_native_injective/src/entry.rs index e65ab7fa0a8..3a16d84836c 100644 --- a/rust/chains/tw_native_injective/src/entry.rs +++ b/rust/chains/tw_native_injective/src/entry.rs @@ -7,7 +7,7 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::AddressResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/rust/chains/tw_ronin/src/address.rs b/rust/chains/tw_ronin/src/address.rs index ad4a4c076ea..73bdaf91624 100644 --- a/rust/chains/tw_ronin/src/address.rs +++ b/rust/chains/tw_ronin/src/address.rs @@ -5,7 +5,7 @@ use std::fmt; use std::str::FromStr; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::AddressError; +use tw_coin_entry::error::prelude::*; use tw_evm::address::{Address as EthAddress, EvmAddress}; use tw_keypair::ecdsa::secp256k1; use tw_memory::Data; diff --git a/rust/chains/tw_ronin/src/entry.rs b/rust/chains/tw_ronin/src/entry.rs index 2bf767d2af6..1c3a9ab674d 100644 --- a/rust/chains/tw_ronin/src/entry.rs +++ b/rust/chains/tw_ronin/src/entry.rs @@ -8,14 +8,14 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; +use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_coin_entry::prefix::NoPrefix; use tw_evm::evm_entry::EvmEntry; use tw_evm::modules::compiler::Compiler; -use tw_evm::modules::json_signer::EthJsonSigner; use tw_evm::modules::message_signer::EthMessageSigner; use tw_evm::modules::signer::Signer; use tw_keypair::tw::PublicKey; @@ -32,7 +32,7 @@ impl CoinEntry for RoninEntry { type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; // Optional modules: - type JsonSigner = EthJsonSigner; + type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = EthMessageSigner; type WalletConnector = NoWalletConnector; @@ -95,11 +95,6 @@ impl CoinEntry for RoninEntry { Compiler::::compile(input, signatures, public_keys) } - #[inline] - fn json_signer(&self) -> Option { - Some(EthJsonSigner::default()) - } - #[inline] fn message_signer(&self) -> Option { Some(EthMessageSigner) diff --git a/rust/chains/tw_ronin/tests/compiler.rs b/rust/chains/tw_ronin/tests/compiler.rs index f755ed12431..18d55a3fd92 100644 --- a/rust/chains/tw_ronin/tests/compiler.rs +++ b/rust/chains/tw_ronin/tests/compiler.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use tw_coin_entry::coin_entry_ext::CoinEntryExt; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_keypair::ecdsa::secp256k1; diff --git a/rust/chains/tw_ronin/tests/rlp.rs b/rust/chains/tw_ronin/tests/rlp.rs index 1bfce0fa625..d3d9548fd7b 100644 --- a/rust/chains/tw_ronin/tests/rlp.rs +++ b/rust/chains/tw_ronin/tests/rlp.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use std::borrow::Cow; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex::ToHex; use tw_evm::evm_entry::EvmEntryExt; use tw_proto::EthereumRlp::Proto as RlpProto; diff --git a/rust/chains/tw_ronin/tests/signer.rs b/rust/chains/tw_ronin/tests/signer.rs index 16e7e747cbd..309592e9278 100644 --- a/rust/chains/tw_ronin/tests/signer.rs +++ b/rust/chains/tw_ronin/tests/signer.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use tw_coin_entry::coin_entry_ext::CoinEntryExt; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_number::U256; diff --git a/rust/chains/tw_solana/src/address.rs b/rust/chains/tw_solana/src/address.rs index f15718964eb..9832509cdfa 100644 --- a/rust/chains/tw_solana/src/address.rs +++ b/rust/chains/tw_solana/src/address.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; use std::str::FromStr; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::base58; use tw_hash::{as_byte_sequence, sha2, H256}; use tw_keypair::{ed25519, tw}; diff --git a/rust/chains/tw_solana/src/compiler.rs b/rust/chains/tw_solana/src/compiler.rs index ca50e214776..483ddac98cb 100644 --- a/rust/chains/tw_solana/src/compiler.rs +++ b/rust/chains/tw_solana/src/compiler.rs @@ -11,7 +11,7 @@ use std::borrow::Cow; use std::collections::HashMap; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_encoding::{base58, base64}; use tw_keypair::ed25519; @@ -73,7 +73,8 @@ impl SolanaCompiler { }; if signatures.len() != public_keys.len() { - return Err(SigningError(SigningErrorType::Error_signatures_count)); + return SigningError::err(SigningErrorType::Error_signatures_count) + .context("Expected the same number of signatures and public keys"); } let builder = MessageBuilder::new(input); @@ -89,7 +90,8 @@ impl SolanaCompiler { if !pubkey.verify(signature.clone(), data_to_sign.clone()) && !signature.to_bytes().is_zero() { - return Err(SigningError(SigningErrorType::Error_signing)); + return SigningError::err(SigningErrorType::Error_signing) + .context("Error verifying the given signature"); } key_signs.insert(SolanaAddress::with_public_key_ed25519(&pubkey), signature); @@ -98,7 +100,8 @@ impl SolanaCompiler { let signed_tx = TxSigner::compile_versioned(unsigned_msg, key_signs)?; let signed_encoded = bincode::serialize(&signed_tx) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing signed transaction")?; let signed_encoded = encode(&signed_encoded); let unsigned_encoded = encode(&data_to_sign); diff --git a/rust/chains/tw_solana/src/entry.rs b/rust/chains/tw_solana/src/entry.rs index 62356a87bf6..cc30d20c6fa 100644 --- a/rust/chains/tw_solana/src/entry.rs +++ b/rust/chains/tw_solana/src/entry.rs @@ -11,7 +11,7 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::AddressResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/rust/chains/tw_solana/src/modules/compiled_instructions.rs b/rust/chains/tw_solana/src/modules/compiled_instructions.rs index 92d16320a16..4e0e02a0c9c 100644 --- a/rust/chains/tw_solana/src/modules/compiled_instructions.rs +++ b/rust/chains/tw_solana/src/modules/compiled_instructions.rs @@ -5,7 +5,7 @@ use crate::address::SolanaAddress; use crate::instruction::Instruction; use crate::transaction::CompiledInstruction; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; pub fn compile_instructions( ixs: &[Instruction], @@ -18,7 +18,7 @@ fn position(keys: &[SolanaAddress], key: &SolanaAddress) -> SigningResult { keys.iter() .position(|k| k == key) .map(|k| k as u8) - .ok_or(SigningError(SigningErrorType::Error_internal)) + .or_tw_err(SigningErrorType::Error_internal) } /// https://github.com/solana-labs/solana/blob/4b65cc8eef6ef79cb9b9cbc534a99b4900e58cf7/sdk/program/src/message/legacy.rs#L72-L84 @@ -30,10 +30,12 @@ pub(crate) fn compile_instruction( .accounts .iter() .map(|account_meta| position(keys, &account_meta.pubkey)) - .collect::>>()?; + .collect::>>() + .context("Cannot build account metas")?; Ok(CompiledInstruction { - program_id_index: position(keys, &ix.program_id)?, + program_id_index: position(keys, &ix.program_id) + .context("Program ID account is not provided")?, data: ix.data.clone(), accounts, }) diff --git a/rust/chains/tw_solana/src/modules/compiled_keys.rs b/rust/chains/tw_solana/src/modules/compiled_keys.rs index 3272f17b64b..e138c1d9b29 100644 --- a/rust/chains/tw_solana/src/modules/compiled_keys.rs +++ b/rust/chains/tw_solana/src/modules/compiled_keys.rs @@ -9,7 +9,7 @@ use crate::instruction::Instruction; use crate::transaction::MessageHeader; use std::collections::hash_map::Entry; use std::collections::HashMap; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] struct CompiledKeyMeta { @@ -68,7 +68,7 @@ impl CompiledKeys { pub fn try_into_message_components(self) -> SigningResult<(MessageHeader, Vec)> { let try_into_u8 = |num: usize| -> SigningResult { - u8::try_from(num).map_err(|_| SigningError(SigningErrorType::Error_tx_too_big)) + u8::try_from(num).tw_err(|_| SigningErrorType::Error_tx_too_big) }; let Self { @@ -103,9 +103,12 @@ impl CompiledKeys { .saturating_add(readonly_signer_keys.len()); let header = MessageHeader { - num_required_signatures: try_into_u8(signers_len)?, - num_readonly_signed_accounts: try_into_u8(readonly_signer_keys.len())?, - num_readonly_unsigned_accounts: try_into_u8(readonly_non_signer_keys.len())?, + num_required_signatures: try_into_u8(signers_len) + .context("Too many signatures required")?, + num_readonly_signed_accounts: try_into_u8(readonly_signer_keys.len()) + .context("Too many accounts in the transaction")?, + num_readonly_unsigned_accounts: try_into_u8(readonly_non_signer_keys.len()) + .context("Too many accounts in the transaction")?, }; let static_account_keys: Vec<_> = std::iter::empty() diff --git a/rust/chains/tw_solana/src/modules/instruction_builder/stake_instruction.rs b/rust/chains/tw_solana/src/modules/instruction_builder/stake_instruction.rs index a0dfd9139fb..ad02414ec72 100644 --- a/rust/chains/tw_solana/src/modules/instruction_builder/stake_instruction.rs +++ b/rust/chains/tw_solana/src/modules/instruction_builder/stake_instruction.rs @@ -9,7 +9,6 @@ use crate::instruction::{AccountMeta, Instruction}; use crate::modules::instruction_builder::system_instruction::SystemInstructionBuilder; use crate::program::stake_program::StakeProgram; use serde::{Deserialize, Serialize}; -use tw_coin_entry::error::SigningResult; type UnixTimestamp = i64; type Epoch = u64; @@ -373,7 +372,7 @@ impl StakeInstructionBuilder { } /// The function represents "stake delegation" operation that consists of several small instructions. - pub fn deposit_stake(args: DepositStakeArgs) -> SigningResult> { + pub fn deposit_stake(args: DepositStakeArgs) -> Vec { let stake_addr = args.stake_account.unwrap_or_else(|| { // no stake address specified, generate a new unique StakeProgram::address_from_recent_blockhash(&args.sender, &args.recent_blockhash) @@ -386,7 +385,7 @@ impl StakeInstructionBuilder { }; let lockup = Lockup::default(); - Ok(vec![ + vec![ SystemInstructionBuilder::create_account_with_seed( args.sender, stake_addr, @@ -397,6 +396,6 @@ impl StakeInstructionBuilder { ), StakeInstructionBuilder::stake_initialize(stake_addr, authorized, lockup), StakeInstructionBuilder::delegate(stake_addr, args.validator, args.sender), - ]) + ] } } diff --git a/rust/chains/tw_solana/src/modules/message_builder.rs b/rust/chains/tw_solana/src/modules/message_builder.rs index 59cc70fc739..0ce11d8b67a 100644 --- a/rust/chains/tw_solana/src/modules/message_builder.rs +++ b/rust/chains/tw_solana/src/modules/message_builder.rs @@ -19,7 +19,7 @@ use crate::transaction::versioned::VersionedMessage; use crate::transaction::{legacy, v0, CompiledInstruction, MessageHeader, Signature}; use std::borrow::Cow; use std::str::FromStr; -use tw_coin_entry::error::{AddressResult, SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_keypair::ed25519; use tw_keypair::traits::KeyPairTrait; use tw_proto::Solana::Proto; @@ -41,7 +41,9 @@ impl<'a> MessageBuilder<'a> { let mut signing_keys = Vec::default(); if !self.input.fee_payer_private_key.is_empty() { let fee_payer_private_key = - ed25519::sha512::KeyPair::try_from(self.input.fee_payer_private_key.as_ref())?; + ed25519::sha512::KeyPair::try_from(self.input.fee_payer_private_key.as_ref()) + .into_tw() + .context("Invalid fee payer private key")?; signing_keys.push(fee_payer_private_key); } @@ -50,7 +52,9 @@ impl<'a> MessageBuilder<'a> { // Consider matching other transaction types if they may contain other private keys. if let ProtoTransactionType::create_nonce_account(ref nonce) = self.input.transaction_type { let nonce_private_key = - ed25519::sha512::KeyPair::try_from(nonce.nonce_account_private_key.as_ref())?; + ed25519::sha512::KeyPair::try_from(nonce.nonce_account_private_key.as_ref()) + .into_tw() + .context("Invalid nonce account private key")?; signing_keys.push(nonce_private_key); } @@ -135,7 +139,8 @@ impl<'a> MessageBuilder<'a> { ProtoTransactionType::advance_nonce_account(ref advance_nonce) => { self.advance_nonce_from_proto(advance_nonce) }, - ProtoTransactionType::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + ProtoTransactionType::None => SigningError::err(SigningErrorType::Error_invalid_params) + .context("No transaction type specified"), } } @@ -144,7 +149,10 @@ impl<'a> MessageBuilder<'a> { transfer: &Proto::Transfer<'_>, ) -> SigningResult> { let from = self.signer_address()?; - let to = SolanaAddress::from_str(&transfer.recipient)?; + let to = SolanaAddress::from_str(&transfer.recipient) + .into_tw() + .context("Invalid recipient address")?; + let references = Self::parse_references(&transfer.references)?; let transfer_ix = SystemInstructionBuilder::transfer(from, to, transfer.value) @@ -165,12 +173,16 @@ impl<'a> MessageBuilder<'a> { delegate: &Proto::DelegateStake, ) -> SigningResult> { let sender = self.signer_address()?; - let validator = SolanaAddress::from_str(delegate.validator_pubkey.as_ref())?; + let validator = SolanaAddress::from_str(delegate.validator_pubkey.as_ref()) + .into_tw() + .context("Invalid validator address")?; let stake_account = if delegate.stake_account.is_empty() { None } else { - let stake_account = SolanaAddress::from_str(&delegate.stake_account)?; + let stake_account = SolanaAddress::from_str(&delegate.stake_account) + .into_tw() + .context("Invalid stake account")?; Some(stake_account) }; @@ -181,7 +193,7 @@ impl<'a> MessageBuilder<'a> { recent_blockhash: self.recent_blockhash()?, lamports: delegate.value, space: DEFAULT_SPACE, - })?; + }); let mut builder = InstructionBuilder::default(); builder @@ -197,7 +209,10 @@ impl<'a> MessageBuilder<'a> { deactivate: &Proto::DeactivateStake, ) -> SigningResult> { let sender = self.signer_address()?; - let stake_account = SolanaAddress::from_str(&deactivate.stake_account)?; + let stake_account = SolanaAddress::from_str(&deactivate.stake_account) + .into_tw() + .context("Invalid stake account")?; + let deactivate_ix = StakeInstructionBuilder::deactivate(stake_account, sender); let mut builder = InstructionBuilder::default(); @@ -221,7 +236,8 @@ impl<'a> MessageBuilder<'a> { let stake_account = SolanaAddress::from_str(stake_account.as_ref())?; Ok(StakeInstructionBuilder::deactivate(stake_account, sender)) }) - .collect::>>()?; + .collect::>>() + .context("Invalid stake account(s)")?; let mut builder = InstructionBuilder::default(); builder @@ -237,7 +253,10 @@ impl<'a> MessageBuilder<'a> { withdraw: &Proto::WithdrawStake, ) -> SigningResult> { let sender = self.signer_address()?; - let stake_account = SolanaAddress::from_str(withdraw.stake_account.as_ref())?; + let stake_account = SolanaAddress::from_str(withdraw.stake_account.as_ref()) + .into_tw() + .context("Invalid stake account")?; + let custodian_account = None; let withdraw_ix = StakeInstructionBuilder::withdraw( @@ -267,7 +286,10 @@ impl<'a> MessageBuilder<'a> { .stake_accounts .iter() .map(|withdraw| { - let stake_account = SolanaAddress::from_str(withdraw.stake_account.as_ref())?; + let stake_account = SolanaAddress::from_str(withdraw.stake_account.as_ref()) + .into_tw() + .context("Invalid stake account")?; + let custodian_account = None; Ok(StakeInstructionBuilder::withdraw( stake_account, @@ -293,10 +315,18 @@ impl<'a> MessageBuilder<'a> { create_token_acc: &Proto::CreateTokenAccount, ) -> SigningResult> { let funding_account = self.signer_address()?; - let other_main_address = SolanaAddress::from_str(create_token_acc.main_address.as_ref())?; + let other_main_address = SolanaAddress::from_str(create_token_acc.main_address.as_ref()) + .into_tw() + .context("Invalid main address")?; + let token_mint_address = - SolanaAddress::from_str(create_token_acc.token_mint_address.as_ref())?; - let token_address = SolanaAddress::from_str(create_token_acc.token_address.as_ref())?; + SolanaAddress::from_str(create_token_acc.token_mint_address.as_ref()) + .into_tw() + .context("Invalid token mint address")?; + + let token_address = SolanaAddress::from_str(create_token_acc.token_address.as_ref()) + .into_tw() + .context("Invalid token address to be created")?; let instruction = TokenInstructionBuilder::create_account( funding_account, @@ -319,16 +349,26 @@ impl<'a> MessageBuilder<'a> { ) -> SigningResult> { let signer = self.signer_address()?; let sender_token_address = - SolanaAddress::from_str(token_transfer.sender_token_address.as_ref())?; + SolanaAddress::from_str(token_transfer.sender_token_address.as_ref()) + .into_tw() + .context("Invalid sender token address")?; + let token_mint_address = - SolanaAddress::from_str(token_transfer.token_mint_address.as_ref())?; + SolanaAddress::from_str(token_transfer.token_mint_address.as_ref()) + .into_tw() + .context("Invalid token mint address")?; + let recipient_token_address = - SolanaAddress::from_str(token_transfer.recipient_token_address.as_ref())?; + SolanaAddress::from_str(token_transfer.recipient_token_address.as_ref()) + .into_tw() + .context("Invalid recipient token address")?; let decimals = token_transfer .decimals .try_into() - .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?; + .tw_err(|_| SigningErrorType::Error_invalid_params) + .context("Invalid token decimals. Expected lower than 256")?; + let references = Self::parse_references(&token_transfer.references)?; let transfer_instruction = TokenInstructionBuilder::transfer_checked( @@ -357,20 +397,34 @@ impl<'a> MessageBuilder<'a> { ) -> SigningResult> { let signer = self.signer_address()?; let fee_payer = self.fee_payer()?; + let sender_token_address = - SolanaAddress::from_str(create_and_transfer.sender_token_address.as_ref())?; + SolanaAddress::from_str(create_and_transfer.sender_token_address.as_ref()) + .into_tw() + .context("Invalid sender token address")?; + let token_mint_address = - SolanaAddress::from_str(create_and_transfer.token_mint_address.as_ref())?; + SolanaAddress::from_str(create_and_transfer.token_mint_address.as_ref()) + .into_tw() + .context("Invalid token mint address")?; + let recipient_main_address = - SolanaAddress::from_str(create_and_transfer.recipient_main_address.as_ref())?; + SolanaAddress::from_str(create_and_transfer.recipient_main_address.as_ref()) + .into_tw() + .context("Invalid recipient main address")?; + let recipient_token_address = - SolanaAddress::from_str(create_and_transfer.recipient_token_address.as_ref())?; + SolanaAddress::from_str(create_and_transfer.recipient_token_address.as_ref()) + .into_tw() + .context("Invalid recipient token address")?; + let references = Self::parse_references(&create_and_transfer.references)?; let decimals = create_and_transfer .decimals .try_into() - .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?; + .tw_err(|_| SigningErrorType::Error_invalid_params) + .context("Invalid token decimals. Expected lower than 256")?; let create_account_instruction = TokenInstructionBuilder::create_account( // Can be different from the actual signer. @@ -409,12 +463,15 @@ impl<'a> MessageBuilder<'a> { let prev_nonce_account = self.nonce_account()?; let new_nonce_account = if create_nonce.nonce_account.is_empty() { - let nonce_key = ed25519::sha512::KeyPair::try_from( - create_nonce.nonce_account_private_key.as_ref(), - )?; + let nonce_key = + ed25519::sha512::KeyPair::try_from(create_nonce.nonce_account_private_key.as_ref()) + .into_tw() + .context("Invalid nonce account private key")?; SolanaAddress::with_public_key_ed25519(nonce_key.public()) } else { - SolanaAddress::from_str(create_nonce.nonce_account.as_ref())? + SolanaAddress::from_str(create_nonce.nonce_account.as_ref()) + .into_tw() + .context("Invalid nonce account")? }; let mut builder = InstructionBuilder::default(); @@ -436,8 +493,13 @@ impl<'a> MessageBuilder<'a> { withdraw_nonce: &Proto::WithdrawNonceAccount, ) -> SigningResult> { let signer = self.signer_address()?; - let withdraw_from_nonce = SolanaAddress::from_str(withdraw_nonce.nonce_account.as_ref())?; - let recipient = SolanaAddress::from_str(withdraw_nonce.recipient.as_ref())?; + let withdraw_from_nonce = SolanaAddress::from_str(withdraw_nonce.nonce_account.as_ref()) + .into_tw() + .context("Invalid nonce account")?; + + let recipient = SolanaAddress::from_str(withdraw_nonce.recipient.as_ref()) + .into_tw() + .context("Invalid recipient")?; let mut builder = InstructionBuilder::default(); builder @@ -458,7 +520,9 @@ impl<'a> MessageBuilder<'a> { advance_nonce: &Proto::AdvanceNonceAccount, ) -> SigningResult> { let signer = self.signer_address()?; - let nonce_account = SolanaAddress::from_str(advance_nonce.nonce_account.as_ref())?; + let nonce_account = SolanaAddress::from_str(advance_nonce.nonce_account.as_ref()) + .into_tw() + .context("Invalid nonce account")?; let mut builder = InstructionBuilder::default(); builder @@ -475,12 +539,15 @@ impl<'a> MessageBuilder<'a> { SolanaAddress::from_str(&self.input.nonce_account) .map(Some) .map_err(SigningError::from) + .context("Invalid nonce account") } } fn signer_address(&self) -> SigningResult { if self.input.private_key.is_empty() { - SolanaAddress::from_str(&self.input.sender).map_err(SigningError::from) + SolanaAddress::from_str(&self.input.sender) + .map_err(SigningError::from) + .context("Sender address is either not set or invalid") } else { Ok(SolanaAddress::with_public_key_ed25519( self.signer_key()?.public(), @@ -493,25 +560,31 @@ impl<'a> MessageBuilder<'a> { pub fn fee_payer(&self) -> SigningResult { if !self.input.fee_payer_private_key.is_empty() { let fee_payer_key = - ed25519::sha512::KeyPair::try_from(self.input.fee_payer_private_key.as_ref())?; + ed25519::sha512::KeyPair::try_from(self.input.fee_payer_private_key.as_ref()) + .into_tw() + .context("Invalid fee payer private key")?; return Ok(SolanaAddress::with_public_key_ed25519( fee_payer_key.public(), )); } if !self.input.fee_payer.is_empty() { return SolanaAddress::from_str(self.input.fee_payer.as_ref()) - .map_err(SigningError::from); + .map_err(SigningError::from) + .context("Invalid fee payer address"); } self.signer_address() } fn recent_blockhash(&self) -> SigningResult { - Ok(Blockhash::from_str(&self.input.recent_blockhash)?) + Blockhash::from_str(&self.input.recent_blockhash) + .map_err(SigningError::from) + .context("Invalid recent blockhash") } fn signer_key(&self) -> SigningResult { ed25519::sha512::KeyPair::try_from(self.input.private_key.as_ref()) .map_err(SigningError::from) + .context("Invalid signer key") } fn priority_fee_price(&self) -> Option { @@ -531,7 +604,8 @@ impl<'a> MessageBuilder<'a> { fn parse_references(refs: &[Cow<'_, str>]) -> SigningResult> { refs.iter() .map(|addr| SolanaAddress::from_str(addr).map_err(SigningError::from)) - .collect() + .collect::>>() + .context("Invalid transaction reference(s)") } } @@ -544,7 +618,7 @@ impl RawMessageBuilder { match raw_message.message { RawMessageType::legacy(ref legacy) => Self::build_legacy(legacy), RawMessageType::v0(ref v0) => Self::build_v0(v0), - RawMessageType::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + RawMessageType::None => SigningError::err(SigningErrorType::Error_invalid_params), } } @@ -553,9 +627,17 @@ impl RawMessageBuilder { ) -> SigningResult { let mut key_signs = PubkeySignatureMap::with_capacity(raw_message.signatures.len()); for entry in raw_message.signatures.iter() { - let pubkey = SolanaAddress::from_str(entry.pubkey.as_ref())?; - let signature = Signature::from_str(entry.signature.as_ref())?; - let ed25519_signature = ed25519::Signature::try_from(signature.0.as_slice())?; + let pubkey = SolanaAddress::from_str(entry.pubkey.as_ref()) + .into_tw() + .context("Invalid 'PubkeySignature::public'")?; + + let signature = Signature::from_str(entry.signature.as_ref()) + .context("Invalid 'PubkeySignature::signature'")?; + + let ed25519_signature = ed25519::Signature::try_from(signature.0.as_slice()) + .into_tw() + .context("Invalid 'PubkeySignature::signature'")?; + key_signs.insert(pubkey, ed25519_signature); } Ok(key_signs) @@ -566,7 +648,10 @@ impl RawMessageBuilder { ) -> SigningResult { let header = Self::build_message_header(&legacy.header)?; let account_keys = Self::build_account_keys(&legacy.account_keys)?; - let recent_blockhash = Blockhash::from_str(legacy.recent_blockhash.as_ref())?; + let recent_blockhash = Blockhash::from_str(legacy.recent_blockhash.as_ref()) + .into_tw() + .context("Invalid recent blockhash")?; + let instructions: Vec<_> = Self::build_instructions(&legacy.instructions)?; Ok(VersionedMessage::Legacy(legacy::Message { @@ -580,7 +665,10 @@ impl RawMessageBuilder { fn build_v0(v0: &Proto::mod_RawMessage::MessageV0) -> SigningResult { let header = Self::build_message_header(&v0.header)?; let account_keys = Self::build_account_keys(&v0.account_keys)?; - let recent_blockhash = Blockhash::from_str(v0.recent_blockhash.as_ref())?; + let recent_blockhash = Blockhash::from_str(v0.recent_blockhash.as_ref()) + .into_tw() + .context("Invalid recent blockhash")?; + let instructions: Vec<_> = Self::build_instructions(&v0.instructions)?; let address_table_lookups = v0 .address_table_lookups @@ -602,7 +690,9 @@ impl RawMessageBuilder { ) -> SigningResult { let raw_header = raw_header .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("No message header provided")?; + Ok(MessageHeader { num_required_signatures: try_into_u8(raw_header.num_required_signatures)?, num_readonly_signed_accounts: try_into_u8(raw_header.num_readonly_signed_accounts)?, @@ -616,6 +706,7 @@ impl RawMessageBuilder { .map(|s| SolanaAddress::from_str(s.as_ref())) .collect::>>() .map_err(SigningError::from) + .context("Cannot build account keys") } fn build_instructions( @@ -631,10 +722,11 @@ impl RawMessageBuilder { .accounts .iter() .map(|idx| try_into_u8(*idx)) - .collect::>>()?; + .collect::>>() + .context("Cannot build account metas")?; Ok(CompiledInstruction { - program_id_index: try_into_u8(ix.program_id)?, + program_id_index: try_into_u8(ix.program_id).context("Invalid program ID")?, accounts, data: ix.program_data.to_vec(), }) @@ -643,19 +735,25 @@ impl RawMessageBuilder { fn build_address_lookup_table( lookup: &Proto::mod_RawMessage::MessageAddressTableLookup, ) -> SigningResult { - let account_key = SolanaAddress::from_str(lookup.account_key.as_ref())?; + let account_key = SolanaAddress::from_str(lookup.account_key.as_ref()) + .into_tw() + .context("Invalid lookup's account key")?; + let writable_indexes = lookup .writable_indexes .iter() .copied() .map(try_into_u8) - .collect::>>()?; + .collect::>>() + .context("Invalid writable index(s)")?; + let readonly_indexes = lookup .readonly_indexes .iter() .copied() .map(try_into_u8) - .collect::>>()?; + .collect::>>() + .context("Invalid readonly index(s)")?; Ok(v0::MessageAddressTableLookup { account_key, @@ -669,5 +767,5 @@ fn try_into_u8(num: T) -> SigningResult where u8: TryFrom, { - u8::try_from(num).map_err(|_| SigningError(SigningErrorType::Error_invalid_params)) + u8::try_from(num).tw_err(|_| SigningErrorType::Error_tx_too_big) } diff --git a/rust/chains/tw_solana/src/modules/proto_builder.rs b/rust/chains/tw_solana/src/modules/proto_builder.rs index d5394ab972b..d40ff7ca664 100644 --- a/rust/chains/tw_solana/src/modules/proto_builder.rs +++ b/rust/chains/tw_solana/src/modules/proto_builder.rs @@ -5,13 +5,12 @@ use crate::transaction::v0; use crate::transaction::versioned::{VersionedMessage, VersionedTransaction}; use std::borrow::Cow; -use tw_coin_entry::error::SigningResult; use tw_proto::Solana::Proto::{self, mod_RawMessage::OneOfmessage as ProtoMessageType}; pub struct ProtoBuilder; impl ProtoBuilder { - pub fn build_from_tx(tx: &VersionedTransaction) -> SigningResult> { + pub fn build_from_tx(tx: &VersionedTransaction) -> Proto::RawMessage<'static> { let message_header = Proto::mod_RawMessage::MessageHeader { num_required_signatures: tx.message.header().num_required_signatures as u32, num_readonly_signed_accounts: tx.message.header().num_readonly_signed_accounts as u32, @@ -61,10 +60,10 @@ impl ProtoBuilder { }, }; - Ok(Proto::RawMessage { + Proto::RawMessage { signatures: Self::build_signatures(tx), message, - }) + } } fn build_address_table_lookups( diff --git a/rust/chains/tw_solana/src/modules/transaction_decoder.rs b/rust/chains/tw_solana/src/modules/transaction_decoder.rs index 00bea7705b2..3a98fb58203 100644 --- a/rust/chains/tw_solana/src/modules/transaction_decoder.rs +++ b/rust/chains/tw_solana/src/modules/transaction_decoder.rs @@ -5,7 +5,7 @@ use crate::modules::proto_builder::ProtoBuilder; use crate::transaction::versioned::VersionedTransaction; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::transaction_decoder::TransactionDecoder; use tw_coin_entry::signing_output_error; use tw_proto::Solana::Proto; @@ -27,8 +27,9 @@ impl SolanaTransactionDecoder { tx: &[u8], ) -> SigningResult> { let decoded_tx: VersionedTransaction = bincode::deserialize(tx) - .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; - let transaction = ProtoBuilder::build_from_tx(&decoded_tx)?; + .tw_err(|_| SigningErrorType::Error_input_parse) + .context("Error decoding transaction as 'bincode'")?; + let transaction = ProtoBuilder::build_from_tx(&decoded_tx); Ok(Proto::DecodingTransactionOutput { transaction: Some(transaction), diff --git a/rust/chains/tw_solana/src/modules/tx_signer.rs b/rust/chains/tw_solana/src/modules/tx_signer.rs index f1bb7c2a0fd..5fb421e6876 100644 --- a/rust/chains/tw_solana/src/modules/tx_signer.rs +++ b/rust/chains/tw_solana/src/modules/tx_signer.rs @@ -6,7 +6,7 @@ use crate::address::SolanaAddress; use crate::modules::PubkeySignatureMap; use crate::transaction::{versioned, Signature}; use std::collections::HashMap; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_keypair::ed25519; use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; use tw_memory::Data; @@ -44,8 +44,11 @@ impl TxSigner { ) -> SigningResult { let mut tx = versioned::VersionedTransaction::unsigned(unsigned_msg); - if key_signs.len() != tx.message.num_required_signatures() { - return Err(SigningError(SigningErrorType::Error_signatures_count)); + let actual_signatures = key_signs.len(); + let expected_signatures = tx.message.num_required_signatures(); + if actual_signatures != expected_signatures { + return SigningError::err(SigningErrorType::Error_signatures_count) + .with_context(|| format!("Expected '{expected_signatures}' signatures, provided '{actual_signatures}'")); } for (signing_pubkey, ed25519_signature) in key_signs { @@ -53,11 +56,15 @@ impl TxSigner { let account_index = tx .message .get_account_index(signing_pubkey) - .ok_or(SigningError(SigningErrorType::Error_missing_private_key))?; + .or_tw_err(SigningErrorType::Error_missing_private_key) + .with_context(|| { + format!("Provided a signature for an unexpected account: {signing_pubkey}") + })?; let signature_to_reassign = tx .signatures .get_mut(account_index) - .ok_or(SigningError(SigningErrorType::Error_signatures_count))?; + .or_tw_err(SigningErrorType::Error_signatures_count) + .context("Internal error: invalid number of Tx.signatures[]")?; *signature_to_reassign = Signature(ed25519_signature.to_bytes()); } @@ -65,6 +72,8 @@ impl TxSigner { } pub fn preimage_versioned(msg: &versioned::VersionedMessage) -> SigningResult { - bincode::serialize(&msg).map_err(|_| SigningError(SigningErrorType::Error_internal)) + bincode::serialize(&msg) + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing Solana Message as 'bincode'") } } diff --git a/rust/chains/tw_solana/src/modules/utils.rs b/rust/chains/tw_solana/src/modules/utils.rs index c6b3d713935..0bf4655d161 100644 --- a/rust/chains/tw_solana/src/modules/utils.rs +++ b/rust/chains/tw_solana/src/modules/utils.rs @@ -8,7 +8,7 @@ use crate::modules::PubkeySignatureMap; use crate::transaction::versioned::VersionedTransaction; use crate::SOLANA_ALPHABET; use std::borrow::Cow; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_encoding::{base58, base64}; use tw_hash::H256; @@ -36,13 +36,13 @@ impl SolanaTransaction { let is_url = false; let tx_bytes = base64::decode(encoded_tx, is_url)?; - let tx_to_sign: VersionedTransaction = bincode::deserialize(&tx_bytes) - .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; + let tx_to_sign: VersionedTransaction = + bincode::deserialize(&tx_bytes).map_err(|_| SigningErrorType::Error_input_parse)?; let mut msg_to_sign = tx_to_sign.message; let new_blockchain_hash = base58::decode(recent_blockhash, &SOLANA_ALPHABET)?; let new_blockchain_hash = H256::try_from(new_blockchain_hash.as_slice()) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?; + .tw_err(|_| SigningErrorType::Error_invalid_params)?; // Update the transaction's blockhash and re-sign it. msg_to_sign.set_recent_blockhash(new_blockchain_hash); @@ -64,8 +64,8 @@ impl SolanaTransaction { }; let unsigned_encoded = base64::encode(&unsigned_encoded, is_url); - let signed_encoded = bincode::serialize(&signed_tx) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let signed_encoded = + bincode::serialize(&signed_tx).tw_err(|_| SigningErrorType::Error_internal)?; let signed_encoded = base64::encode(&signed_encoded, is_url); Ok(Proto::SigningOutput { diff --git a/rust/chains/tw_solana/src/modules/wallet_connect/connector.rs b/rust/chains/tw_solana/src/modules/wallet_connect/connector.rs index 31d45860f84..041d50012ac 100644 --- a/rust/chains/tw_solana/src/modules/wallet_connect/connector.rs +++ b/rust/chains/tw_solana/src/modules/wallet_connect/connector.rs @@ -6,7 +6,7 @@ use crate::modules::proto_builder::ProtoBuilder; use crate::modules::wallet_connect::request::SignTransactionRequest; use crate::transaction::versioned::VersionedTransaction; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::wallet_connector::WalletConnector; use tw_coin_entry::signing_output_error; use tw_proto::Solana::Proto; @@ -36,7 +36,8 @@ impl SolanaWalletConnector { WCProto::Method::SolanaSignTransaction => { Self::parse_sign_transaction_request(coin, request) }, - _ => Err(SigningError(SigningErrorType::Error_not_supported)), + _ => SigningError::err(SigningErrorType::Error_not_supported) + .context("Unknown WalletConnect method"), } } @@ -45,12 +46,15 @@ impl SolanaWalletConnector { request: WCProto::ParseRequestInput<'_>, ) -> SigningResult> { let sign_req: SignTransactionRequest = serde_json::from_str(&request.payload) - .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; + .tw_err(|_| SigningErrorType::Error_input_parse) + .context("Error parsing WalletConnect signing request as JSON")?; + let transaction: VersionedTransaction = bincode::deserialize(&sign_req.transaction.0) - .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; + .tw_err(|_| SigningErrorType::Error_input_parse) + .context("Error deserializing Solana transaction as 'bincode'")?; let signing_input = Proto::SigningInput { - raw_message: Some(ProtoBuilder::build_from_tx(&transaction)?), + raw_message: Some(ProtoBuilder::build_from_tx(&transaction)), ..Proto::SigningInput::default() }; diff --git a/rust/chains/tw_solana/src/program/stake_program.rs b/rust/chains/tw_solana/src/program/stake_program.rs index 5161edec832..37ac2e794fb 100644 --- a/rust/chains/tw_solana/src/program/stake_program.rs +++ b/rust/chains/tw_solana/src/program/stake_program.rs @@ -5,7 +5,7 @@ use crate::address::SolanaAddress; use crate::blockhash::Blockhash; use crate::defined_addresses::*; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_hash::sha2::sha256; use tw_hash::H256; diff --git a/rust/chains/tw_solana/src/signer.rs b/rust/chains/tw_solana/src/signer.rs index b03ccd345d0..11b3e8bd2f9 100644 --- a/rust/chains/tw_solana/src/signer.rs +++ b/rust/chains/tw_solana/src/signer.rs @@ -8,7 +8,7 @@ use crate::modules::tx_signer::TxSigner; use crate::SOLANA_ALPHABET; use std::borrow::Cow; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_encoding::{base58, base64}; use tw_proto::Solana::Proto; @@ -39,14 +39,16 @@ impl SolanaSigner { let unsigned_msg = builder.build()?; let encoded_unsigned = bincode::serialize(&unsigned_msg) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing Solana Message as 'bincode'")?; let encoded_unsigned = encode(&encoded_unsigned); let signed_tx = TxSigner::sign_versioned(unsigned_msg, &signing_keys, &external_signatures)?; let encoded_tx = bincode::serialize(&signed_tx) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing Solana Transaction as 'bincode'")?; let encoded_tx = encode(&encoded_tx); Ok(Proto::SigningOutput { diff --git a/rust/chains/tw_solana/src/transaction/mod.rs b/rust/chains/tw_solana/src/transaction/mod.rs index b5848ba006e..7ba4da1ac63 100644 --- a/rust/chains/tw_solana/src/transaction/mod.rs +++ b/rust/chains/tw_solana/src/transaction/mod.rs @@ -6,7 +6,7 @@ use crate::SOLANA_ALPHABET; use serde::{Deserialize, Serialize}; use std::fmt; use std::str::FromStr; -use tw_coin_entry::error::{SigningError, SigningErrorType}; +use tw_coin_entry::error::prelude::*; use tw_encoding::base58; use tw_hash::{as_byte_sequence, H512}; @@ -54,10 +54,12 @@ impl FromStr for Signature { fn from_str(s: &str) -> Result { let data = base58::decode(s, &SOLANA_ALPHABET) - .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; + .tw_err(|_| SigningErrorType::Error_input_parse) + .context("Error decoding Solana Signature from base58")?; H512::try_from(data.as_slice()) .map(Signature) - .map_err(|_| SigningError(SigningErrorType::Error_input_parse)) + .tw_err(|_| SigningErrorType::Error_input_parse) + .context("Solana Signature must be 64 byte length") } } diff --git a/rust/chains/tw_solana/tests/update_blockhash_and_sign.rs b/rust/chains/tw_solana/tests/update_blockhash_and_sign.rs index a4a4cd1c08d..6dffaa32a63 100644 --- a/rust/chains/tw_solana/tests/update_blockhash_and_sign.rs +++ b/rust/chains/tw_solana/tests/update_blockhash_and_sign.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_encoding::base58; use tw_solana::modules::utils::SolanaTransaction; use tw_solana::SOLANA_ALPHABET; diff --git a/rust/chains/tw_sui/src/address.rs b/rust/chains/tw_sui/src/address.rs index 76c359561ec..1ed70526d86 100644 --- a/rust/chains/tw_sui/src/address.rs +++ b/rust/chains/tw_sui/src/address.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use std::fmt; use std::str::FromStr; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex; use tw_hash::blake2::blake2_b; use tw_keypair::ed25519; diff --git a/rust/chains/tw_sui/src/compiler.rs b/rust/chains/tw_sui/src/compiler.rs index adff5bd13d6..90b95ec7a6b 100644 --- a/rust/chains/tw_sui/src/compiler.rs +++ b/rust/chains/tw_sui/src/compiler.rs @@ -9,7 +9,7 @@ use std::borrow::Cow; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; use tw_coin_entry::common::compile_input::SingleSignaturePubkey; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_encoding::base64; use tw_keypair::ed25519; diff --git a/rust/chains/tw_sui/src/entry.rs b/rust/chains/tw_sui/src/entry.rs index 70682041a43..72ec4866299 100644 --- a/rust/chains/tw_sui/src/entry.rs +++ b/rust/chains/tw_sui/src/entry.rs @@ -9,7 +9,7 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/rust/chains/tw_sui/src/modules/tx_builder.rs b/rust/chains/tw_sui/src/modules/tx_builder.rs index 58ec6dff986..44ce73e43d6 100644 --- a/rust/chains/tw_sui/src/modules/tx_builder.rs +++ b/rust/chains/tw_sui/src/modules/tx_builder.rs @@ -8,7 +8,7 @@ use crate::transaction::transaction_builder::TransactionBuilder; use crate::transaction::transaction_data::TransactionData; use std::borrow::Cow; use std::str::FromStr; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::base64; use tw_keypair::ed25519; use tw_keypair::traits::KeyPairTrait; @@ -53,7 +53,7 @@ impl<'a> TWTransactionBuilder<'a> { TransactionType::transfer_object(ref transfer_obj) => { self.transfer_object_from_proto(transfer_obj) }, - TransactionType::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + TransactionType::None => SigningError::err(SigningErrorType::Error_invalid_params), }?; Ok(TWTransaction::Transaction(tx_data)) } @@ -61,13 +61,15 @@ impl<'a> TWTransactionBuilder<'a> { fn sign_direct_from_proto(&self, sign_direct: &Proto::SignDirect<'_>) -> SigningResult { let url = false; base64::decode(&sign_direct.unsigned_tx_msg, url) - .map_err(|_| SigningError(SigningErrorType::Error_input_parse)) + .tw_err(|_| SigningErrorType::Error_input_parse) + .context("Error parsing Raw Unsigned TX message as base64") } fn pay_sui_from_proto(&self, pay_sui: &Proto::PaySui<'_>) -> SigningResult { let signer = self.signer_address()?; let input_coins = Self::build_coins(&pay_sui.input_coins)?; - let recipients = Self::parse_addresses(&pay_sui.recipients)?; + let recipients = Self::parse_addresses(&pay_sui.recipients) + .context("Invalid one of the recipients addresses")?; TransactionBuilder::pay_sui( signer, @@ -85,7 +87,9 @@ impl<'a> TWTransactionBuilder<'a> { ) -> SigningResult { let signer = self.signer_address()?; let input_coins = Self::build_coins(&pay_all_sui.input_coins)?; - let recipient = SuiAddress::from_str(&pay_all_sui.recipient)?; + let recipient = SuiAddress::from_str(&pay_all_sui.recipient) + .into_tw() + .context("Invalid recipient address")?; TransactionBuilder::pay_all_sui( signer, @@ -99,8 +103,9 @@ impl<'a> TWTransactionBuilder<'a> { fn pay_from_proto(&self, pay: &Proto::Pay<'_>) -> SigningResult { let signer = self.signer_address()?; let input_coins = Self::build_coins(&pay.input_coins)?; - let recipients = Self::parse_addresses(&pay.recipients)?; - let gas = Self::require_coin(&pay.gas)?; + let recipients = Self::parse_addresses(&pay.recipients) + .context("Invalid one of the recipients addresses")?; + let gas = Self::require_coin(&pay.gas).context("No 'gas' coin specified")?; TransactionBuilder::pay( signer, @@ -121,8 +126,10 @@ impl<'a> TWTransactionBuilder<'a> { let input_coins = Self::build_coins(&stake.coins)?; let amount = stake.amount.as_ref().map(|a| a.amount); - let validator = SuiAddress::from_str(stake.validator.as_ref())?; - let gas = Self::require_coin(&stake.gas)?; + let validator = SuiAddress::from_str(stake.validator.as_ref()) + .into_tw() + .context("Invalid validator address")?; + let gas = Self::require_coin(&stake.gas).context("No 'gas' coin specified")?; TransactionBuilder::request_add_stake( signer, @@ -141,8 +148,9 @@ impl<'a> TWTransactionBuilder<'a> { ) -> SigningResult { let signer = self.signer_address()?; - let staked_sui = Self::require_coin(&withdraw.staked_sui)?; - let gas = Self::require_coin(&withdraw.gas)?; + let staked_sui = + Self::require_coin(&withdraw.staked_sui).context("No 'staked_sui' coin specified")?; + let gas = Self::require_coin(&withdraw.gas).context("No 'gas' coin specified")?; TransactionBuilder::request_withdraw_stake( signer, @@ -159,9 +167,11 @@ impl<'a> TWTransactionBuilder<'a> { ) -> SigningResult { let signer = self.signer_address()?; - let recipient = SuiAddress::from_str(&transfer_obj.recipient)?; - let object = Self::require_coin(&transfer_obj.object)?; - let gas = Self::require_coin(&transfer_obj.gas)?; + let recipient = SuiAddress::from_str(&transfer_obj.recipient) + .into_tw() + .context("Invalid recipient address")?; + let object = Self::require_coin(&transfer_obj.object).context("No 'object' specified")?; + let gas = Self::require_coin(&transfer_obj.gas).context("No 'gas' coin specified")?; TransactionBuilder::transfer_object( signer, @@ -175,7 +185,9 @@ impl<'a> TWTransactionBuilder<'a> { fn signer_address(&self) -> SigningResult { if self.input.private_key.is_empty() { - SuiAddress::from_str(&self.input.signer).map_err(SigningError::from) + SuiAddress::from_str(&self.input.signer) + .into_tw() + .context("Invalid signer address") } else { let keypair = self.signer_key()?; SuiAddress::with_ed25519_pubkey(keypair.public()).map_err(SigningError::from) @@ -189,12 +201,12 @@ impl<'a> TWTransactionBuilder<'a> { fn require_coin(maybe_coin: &Option) -> SigningResult { let coin = maybe_coin .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params)?; Self::build_coin(coin) } fn build_coin(coin: &Proto::ObjectRef) -> SigningResult { - let object_id = ObjectID::from_str(coin.object_id.as_ref())?; + let object_id = ObjectID::from_str(coin.object_id.as_ref()).context("Invalid Object ID")?; let version = SequenceNumber(coin.version); let object_digest = ObjectDigest::from_str(coin.object_digest.as_ref())?; diff --git a/rust/chains/tw_sui/src/modules/tx_signer.rs b/rust/chains/tw_sui/src/modules/tx_signer.rs index e7364feac56..34eb476f48b 100644 --- a/rust/chains/tw_sui/src/modules/tx_signer.rs +++ b/rust/chains/tw_sui/src/modules/tx_signer.rs @@ -7,7 +7,7 @@ use crate::signature::SuiSignatureInfo; use crate::transaction::transaction_data::TransactionData; use serde::Serialize; use serde_repr::Serialize_repr; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::bcs; use tw_hash::blake2::blake2_b; use tw_hash::H256; @@ -88,11 +88,13 @@ impl TxSigner { let public_key = signer_key.public(); let signer_address = SuiAddress::with_ed25519_pubkey(public_key)?; if signer_address != tx.sender() { - return Err(SigningError(SigningErrorType::Error_missing_private_key)); + return SigningError::err(SigningErrorType::Error_missing_private_key) + .context("Given private key does not belong to the sender address"); } - let unsigned_tx_data = - bcs::encode(tx).map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let unsigned_tx_data = bcs::encode(tx) + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing TransactionData")?; Self::sign_direct(unsigned_tx_data, signer_key) } @@ -107,8 +109,9 @@ impl TxSigner { } pub fn preimage(tx: &TransactionData) -> SigningResult { - let unsigned_tx_data = - bcs::encode(tx).map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let unsigned_tx_data = bcs::encode(tx) + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing TransactionData")?; Self::preimage_direct(unsigned_tx_data) } @@ -118,8 +121,9 @@ impl TxSigner { version: IntentVersion::V0, app_id: AppId::Sui, }; - let intent_data = - bcs::encode(&intent).map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let intent_data = bcs::encode(&intent) + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing Intent message")?; let tx_data_to_sign: Data = intent_data .into_iter() @@ -127,7 +131,7 @@ impl TxSigner { .collect(); let tx_hash_to_sign = blake2_b(&tx_data_to_sign, H256::LEN) .and_then(|hash| H256::try_from(hash.as_slice())) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal)?; Ok(TransactionPreimage { unsigned_tx_data, diff --git a/rust/chains/tw_sui/src/signer.rs b/rust/chains/tw_sui/src/signer.rs index bfb59285678..46546e6e356 100644 --- a/rust/chains/tw_sui/src/signer.rs +++ b/rust/chains/tw_sui/src/signer.rs @@ -6,7 +6,7 @@ use crate::modules::tx_builder::{TWTransaction, TWTransactionBuilder}; use crate::modules::tx_signer::TxSigner; use std::borrow::Cow; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_encoding::base64; use tw_proto::Sui::Proto; diff --git a/rust/chains/tw_sui/src/transaction/programmable_transaction.rs b/rust/chains/tw_sui/src/transaction/programmable_transaction.rs index 6a3529299b5..82445cae86e 100644 --- a/rust/chains/tw_sui/src/transaction/programmable_transaction.rs +++ b/rust/chains/tw_sui/src/transaction/programmable_transaction.rs @@ -9,7 +9,7 @@ use indexmap::IndexMap; use move_core_types::identifier::Identifier; use move_core_types::language_storage::TypeTag; use serde::{Deserialize, Serialize}; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::bcs; /// A series of commands where the results of one command can be used in future @@ -54,7 +54,8 @@ impl ProgrammableTransactionBuilder { let mut coins = coins.into_iter(); let Some(coin) = coins.next() else { // coins vector is empty - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("No coins provided"); }; let coin_arg = self.obj(ObjectArg::ImmOrOwnedObject(coin))?; let merge_args: Vec<_> = coins @@ -140,7 +141,10 @@ impl ProgrammableTransactionBuilder { let id = obj_arg.id(); let obj_arg = if let Some(old_value) = self.inputs.get(&BuilderArg::Object(id)) { let old_obj_arg = match old_value { - CallArg::Pure(_) => return Err(SigningError(SigningErrorType::Error_internal)), + CallArg::Pure(_) => { + return SigningError::err(SigningErrorType::Error_internal) + .context("Expected Object, found Pure") + }, CallArg::Object(arg) => arg, }; match (old_obj_arg, obj_arg) { @@ -158,7 +162,8 @@ impl ProgrammableTransactionBuilder { ) if v1 == &v2 => { if id1 != &id2 || id != id2 { // "invariant violation! object has id does not match call arg" - return Err(SigningError(SigningErrorType::Error_internal)); + return SigningError::err(SigningErrorType::Error_internal) + .context("invariant violation! object has id does not match call arg"); } ObjectArg::SharedObject { id, @@ -168,8 +173,8 @@ impl ProgrammableTransactionBuilder { }, (old_obj_arg, obj_arg) => { if old_obj_arg != &obj_arg { - // "Mismatched Object argument kind for object {id}. {old_value:?} is not compatible with {obj_arg:?}" - return Err(SigningError(SigningErrorType::Error_internal)); + return SigningError::err(SigningErrorType::Error_internal) + .with_context(|| format!("Mismatched Object argument kind for object {id:?}. {old_value:?} is not compatible with {obj_arg:?}")); } obj_arg }, @@ -208,7 +213,11 @@ impl ProgrammableTransactionBuilder { ) -> SigningResult<()> { if recipients.len() != amounts.len() { // "Recipients and amounts mismatch. Got {} recipients but {} amounts" - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params).with_context(|| { + let recipients_num = recipients.len(); + let amounts_num = amounts.len(); + format!("Recipients and amounts mismatch. Got {recipients_num} recipients but {amounts_num} amounts") + }); } if amounts.is_empty() { return Ok(()); @@ -224,7 +233,8 @@ impl ProgrammableTransactionBuilder { } let Argument::Result(split_primary) = self.command(Command::SplitCoins(coin, amt_args)) else { - panic!("self.command should always give a Argument::Result") + return SigningError::err(SigningErrorType::Error_internal) + .context("self.command should always give an Argument::Result"); }; for (recipient, split_secondaries) in recipient_map { let rec_arg = self.pure(recipient).unwrap(); diff --git a/rust/chains/tw_sui/src/transaction/sui_types.rs b/rust/chains/tw_sui/src/transaction/sui_types.rs index b35df1b26d8..625e6bde69e 100644 --- a/rust/chains/tw_sui/src/transaction/sui_types.rs +++ b/rust/chains/tw_sui/src/transaction/sui_types.rs @@ -7,7 +7,7 @@ use crate::constants::{SUI_SYSTEM_STATE_OBJECT_ID, SUI_SYSTEM_STATE_OBJECT_SHARE use move_core_types::account_address::AccountAddress; use serde::{Deserialize, Serialize}; use std::str::FromStr; -use tw_coin_entry::error::{AddressError, SigningError, SigningErrorType}; +use tw_coin_entry::error::prelude::*; use tw_encoding::base58::{self, Alphabet}; use tw_hash::{as_bytes, H256}; use tw_memory::Data; @@ -22,10 +22,12 @@ pub struct SequenceNumber(pub u64); pub struct ObjectID(pub AccountAddress); impl FromStr for ObjectID { - type Err = AddressError; + type Err = SigningError; fn from_str(s: &str) -> Result { - let addr = SuiAddress::from_str(s)?; + let addr = SuiAddress::from_str(s) + .into_tw() + .context("Invalid Object ID")?; Ok(ObjectID(addr.into_inner())) } } @@ -38,10 +40,12 @@ impl FromStr for ObjectDigest { fn from_str(s: &str) -> Result { let bytes = base58::decode(s, Alphabet::BITCOIN) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?; + .tw_err(|_| SigningErrorType::Error_invalid_params) + .context("Invalid Object Digest: expected valid base58 string")?; H256::try_from(bytes.as_slice()) .map(ObjectDigest) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_params)) + .tw_err(|_| SigningErrorType::Error_invalid_params) + .context("Invalid Object Digest: expected exactly 32 bytes") } } diff --git a/rust/chains/tw_sui/src/transaction/transaction_builder.rs b/rust/chains/tw_sui/src/transaction/transaction_builder.rs index 93ad29060f0..3c7309013fb 100644 --- a/rust/chains/tw_sui/src/transaction/transaction_builder.rs +++ b/rust/chains/tw_sui/src/transaction/transaction_builder.rs @@ -11,7 +11,7 @@ use crate::transaction::command::Command; use crate::transaction::programmable_transaction::ProgrammableTransactionBuilder; use crate::transaction::sui_types::{CallArg, ObjectArg, ObjectRef}; use crate::transaction::transaction_data::{TransactionData, TransactionKind}; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::bcs; pub struct TransactionBuilder; @@ -94,8 +94,9 @@ impl TransactionBuilder { gas_price: u64, ) -> SigningResult { if input_coins.iter().any(|coin| coin.0 == gas.0) { - // Gas coin is in input coins of Pay transaction, use PaySui transaction instead!. - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params).context( + "Gas coin is in input coins of Pay transaction, use PaySui transaction instead!", + ); } TransactionData::new_pay( @@ -121,8 +122,8 @@ impl TransactionBuilder { gas_price: u64, ) -> SigningResult { if input_coins.is_empty() { - // "Empty input coins for Pay related transaction" - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("Empty input coins for Pay related transaction"); } let gas_object_ref = input_coins.remove(0); @@ -148,8 +149,8 @@ impl TransactionBuilder { gas_price: u64, ) -> SigningResult { if input_coins.is_empty() { - // "Empty input coins for Pay related transaction" - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("Empty input coins for Pay related transaction"); } let gas_object_ref = input_coins.remove(0); diff --git a/rust/chains/tw_sui/src/transaction/transaction_data.rs b/rust/chains/tw_sui/src/transaction/transaction_data.rs index 6f3329cc419..bfe18fa4e3a 100644 --- a/rust/chains/tw_sui/src/transaction/transaction_data.rs +++ b/rust/chains/tw_sui/src/transaction/transaction_data.rs @@ -10,7 +10,7 @@ use crate::transaction::sui_types::{CallArg, GasData, ObjectID, ObjectRef, Trans use move_core_types::identifier::Identifier; use move_core_types::language_storage::TypeTag; use serde::{Deserialize, Serialize}; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; #[derive(Debug, Deserialize, Serialize)] pub enum TransactionData { diff --git a/rust/chains/tw_thorchain/src/entry.rs b/rust/chains/tw_thorchain/src/entry.rs index 25f0316984a..970701b0d14 100644 --- a/rust/chains/tw_thorchain/src/entry.rs +++ b/rust/chains/tw_thorchain/src/entry.rs @@ -8,7 +8,7 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::AddressResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; diff --git a/rust/tw_any_coin/src/any_address.rs b/rust/tw_any_coin/src/any_address.rs index 4716d920da3..f0e83b169c8 100644 --- a/rust/tw_any_coin/src/any_address.rs +++ b/rust/tw_any_coin/src/any_address.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::prefix::AddressPrefix; use tw_coin_registry::coin_type::CoinType; use tw_coin_registry::dispatcher::coin_dispatcher; diff --git a/rust/tw_any_coin/src/any_signer.rs b/rust/tw_any_coin/src/any_signer.rs index 2e7b3692fcf..014e8a0c736 100644 --- a/rust/tw_any_coin/src/any_signer.rs +++ b/rust/tw_any_coin/src/any_signer.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::{SigningError, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_coin_registry::dispatcher::coin_dispatcher; use tw_memory::Data; diff --git a/rust/tw_any_coin/src/message_signer.rs b/rust/tw_any_coin/src/message_signer.rs index 5ae2f83bc8f..89297a59680 100644 --- a/rust/tw_any_coin/src/message_signer.rs +++ b/rust/tw_any_coin/src/message_signer.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_coin_registry::dispatcher::coin_dispatcher; use tw_memory::Data; diff --git a/rust/tw_any_coin/src/transaction_compiler.rs b/rust/tw_any_coin/src/transaction_compiler.rs index b25d472f858..d20e9ee1cef 100644 --- a/rust/tw_any_coin/src/transaction_compiler.rs +++ b/rust/tw_any_coin/src/transaction_compiler.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; -use tw_coin_entry::error::{SigningError, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_coin_registry::dispatcher::coin_dispatcher; use tw_memory::Data; diff --git a/rust/tw_any_coin/src/transaction_decoder.rs b/rust/tw_any_coin/src/transaction_decoder.rs index fcc10c5dec4..13c40fd7512 100644 --- a/rust/tw_any_coin/src/transaction_decoder.rs +++ b/rust/tw_any_coin/src/transaction_decoder.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_coin_registry::dispatcher::coin_dispatcher; use tw_memory::Data; diff --git a/rust/tw_any_coin/src/wallet_connect_request.rs b/rust/tw_any_coin/src/wallet_connect_request.rs index 72465e79ccd..22099edad58 100644 --- a/rust/tw_any_coin/src/wallet_connect_request.rs +++ b/rust/tw_any_coin/src/wallet_connect_request.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::{SigningError, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_coin_registry::dispatcher::coin_dispatcher; use tw_memory::Data; diff --git a/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs b/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs index b2016790f38..d46b6db874f 100644 --- a/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs +++ b/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs @@ -10,7 +10,7 @@ use crate::chains::aptos::APTOS_COIN_TYPE; use tw_any_coin::ffi::tw_transaction_compiler::{ tw_transaction_compiler_compile, tw_transaction_compiler_pre_image_hashes, }; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex::ToHex; use tw_keypair::ed25519; use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; diff --git a/rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs b/rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs index 44933138f42..b5ca60631b1 100644 --- a/rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs +++ b/rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs @@ -7,7 +7,7 @@ use crate::chains::aptos::test_cases::transfer_b4d62afd::{ }; use crate::chains::aptos::APTOS_COIN_TYPE; use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; use tw_misc::assert_eq_json; diff --git a/rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs b/rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs index 384b8b14cfb..2244b152974 100644 --- a/rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs +++ b/rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::DecodeHex; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/rust/tw_any_coin/tests/chains/ethereum/ethereum_compile.rs b/rust/tw_any_coin/tests/chains/ethereum/ethereum_compile.rs index 6b209eb6451..b83500c18e6 100644 --- a/rust/tw_any_coin/tests/chains/ethereum/ethereum_compile.rs +++ b/rust/tw_any_coin/tests/chains/ethereum/ethereum_compile.rs @@ -7,7 +7,7 @@ use tw_any_coin::ffi::tw_any_signer::tw_any_signer_plan; use tw_any_coin::ffi::tw_transaction_compiler::{ tw_transaction_compiler_compile, tw_transaction_compiler_pre_image_hashes, }; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/rust/tw_any_coin/tests/chains/ethereum/ethereum_message_sign.rs b/rust/tw_any_coin/tests/chains/ethereum/ethereum_message_sign.rs index 38e72d2a3ab..8379148b394 100644 --- a/rust/tw_any_coin/tests/chains/ethereum/ethereum_message_sign.rs +++ b/rust/tw_any_coin/tests/chains/ethereum/ethereum_message_sign.rs @@ -5,7 +5,7 @@ use tw_any_coin::ffi::tw_message_signer::{ tw_message_signer_pre_image_hashes, tw_message_signer_sign, tw_message_signer_verify, }; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/rust/tw_any_coin/tests/chains/ethereum/ethereum_sign.rs b/rust/tw_any_coin/tests/chains/ethereum/ethereum_sign.rs index 5299cebb811..31f6aab6394 100644 --- a/rust/tw_any_coin/tests/chains/ethereum/ethereum_sign.rs +++ b/rust/tw_any_coin/tests/chains/ethereum/ethereum_sign.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_sign.rs b/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_sign.rs index c9e90d27a5e..b8264bffe65 100644 --- a/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_sign.rs +++ b/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_sign.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_cosmos_sdk::test_utils::proto_utils::{make_amount, make_fee, make_message}; use tw_encoding::hex::DecodeHex; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/rust/tw_any_coin/tests/chains/native_injective/native_injective_compile.rs b/rust/tw_any_coin/tests/chains/native_injective/native_injective_compile.rs index 797b7e9394e..5bfa7c71d51 100644 --- a/rust/tw_any_coin/tests/chains/native_injective/native_injective_compile.rs +++ b/rust/tw_any_coin/tests/chains/native_injective/native_injective_compile.rs @@ -5,7 +5,7 @@ use tw_any_coin::ffi::tw_transaction_compiler::{ tw_transaction_compiler_compile, tw_transaction_compiler_pre_image_hashes, }; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_cosmos_sdk::test_utils::proto_utils::{make_amount, make_fee, make_message}; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/rust/tw_any_coin/tests/chains/native_injective/native_injective_sign.rs b/rust/tw_any_coin/tests/chains/native_injective/native_injective_sign.rs index 6ca790dbee2..8240be5a756 100644 --- a/rust/tw_any_coin/tests/chains/native_injective/native_injective_sign.rs +++ b/rust/tw_any_coin/tests/chains/native_injective/native_injective_sign.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_cosmos_sdk::test_utils::proto_utils::{make_amount, make_fee, make_message}; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs b/rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs index 2439165caa7..3b4aa79f304 100644 --- a/rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs +++ b/rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs @@ -9,7 +9,7 @@ use crate::chains::thorchain::test_cases::send_fd0445af::{ use tw_any_coin::ffi::tw_transaction_compiler::{ tw_transaction_compiler_compile, tw_transaction_compiler_pre_image_hashes, }; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::ToHex; use tw_hash::H256; diff --git a/rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs b/rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs index e65d2ec1ebc..c02744e79f3 100644 --- a/rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs +++ b/rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs @@ -6,7 +6,7 @@ use crate::chains::thorchain::test_cases::send_fd0445af::{ signing_input, JSON_SIGNING_SIGNATURE, JSON_SIGNING_SIGNATURE_JSON, JSON_TX, PRIVATE_KEY, }; use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/rust/tw_any_coin/tests/chains/zetachain/zetachain_sign.rs b/rust/tw_any_coin/tests/chains/zetachain/zetachain_sign.rs index d2fa28fc914..ba87a777572 100644 --- a/rust/tw_any_coin/tests/chains/zetachain/zetachain_sign.rs +++ b/rust/tw_any_coin/tests/chains/zetachain/zetachain_sign.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/rust/tw_bech32_address/src/bech32_prefix.rs b/rust/tw_bech32_address/src/bech32_prefix.rs index c644ff6046c..fa548edd09a 100644 --- a/rust/tw_bech32_address/src/bech32_prefix.rs +++ b/rust/tw_bech32_address/src/bech32_prefix.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::AddressError; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::prefix::AddressPrefix; pub struct Bech32Prefix { diff --git a/rust/tw_bech32_address/src/lib.rs b/rust/tw_bech32_address/src/lib.rs index ac5d944054b..220fe1a1b77 100644 --- a/rust/tw_bech32_address/src/lib.rs +++ b/rust/tw_bech32_address/src/lib.rs @@ -8,7 +8,7 @@ use std::fmt; use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::bech32; use tw_hash::hasher::Hasher; use tw_hash::H160; diff --git a/rust/tw_bitcoin/src/entry.rs b/rust/tw_bitcoin/src/entry.rs index a2b60b3fbb5..009133153ce 100644 --- a/rust/tw_bitcoin/src/entry.rs +++ b/rust/tw_bitcoin/src/entry.rs @@ -1,5 +1,5 @@ use crate::modules::signer::Signer; -use crate::{Error, Result}; +use crate::{bitcoin_output_error, Error, Result}; use bitcoin::address::NetworkChecked; use std::borrow::Cow; use std::fmt::Display; @@ -7,14 +7,13 @@ use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{CoinAddress, CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_coin_entry::prefix::NoPrefix; -use tw_coin_entry::signing_output_error; use tw_keypair::tw::PublicKey; use tw_misc::traits::ToBytesVec; use tw_proto::BitcoinV2::Proto; @@ -103,7 +102,7 @@ impl CoinEntry for BitcoinEntry { #[inline] fn sign(&self, _coin: &dyn CoinContext, proto: Self::SigningInput<'_>) -> Self::SigningOutput { Signer::sign_proto(_coin, proto) - .unwrap_or_else(|err| signing_output_error!(Proto::SigningOutput, err)) + .unwrap_or_else(|err| bitcoin_output_error!(Proto::SigningOutput, err)) } #[inline] @@ -113,7 +112,7 @@ impl CoinEntry for BitcoinEntry { proto: Proto::SigningInput<'_>, ) -> Self::PreSigningOutput { self.preimage_hashes_impl(_coin, proto) - .unwrap_or_else(|err| signing_output_error!(Proto::PreSigningOutput, err)) + .unwrap_or_else(|err| bitcoin_output_error!(Proto::PreSigningOutput, err)) } #[inline] @@ -125,7 +124,7 @@ impl CoinEntry for BitcoinEntry { _public_keys: Vec, ) -> Self::SigningOutput { self.compile_impl(_coin, proto, signatures, _public_keys) - .unwrap_or_else(|err| signing_output_error!(Proto::SigningOutput, err)) + .unwrap_or_else(|err| bitcoin_output_error!(Proto::SigningOutput, err)) } #[inline] diff --git a/rust/tw_bitcoin/src/lib.rs b/rust/tw_bitcoin/src/lib.rs index be3e835b6a6..ffbd96a5794 100644 --- a/rust/tw_bitcoin/src/lib.rs +++ b/rust/tw_bitcoin/src/lib.rs @@ -11,6 +11,20 @@ pub use secp256k1; use tw_proto::BitcoinV2::Proto; +/// TODO use the standard `TWError` in tw_bitcoin crate. +#[macro_export] +macro_rules! bitcoin_output_error { + ($output:ty, $error:expr) => {{ + let err = $error; + + let mut output = <$output>::default(); + output.error = err.0; + output.error_message = std::borrow::Cow::from(err.to_string()); + + output + }}; +} + pub type Result = std::result::Result; #[derive(Debug)] diff --git a/rust/tw_coin_entry/src/coin_entry.rs b/rust/tw_coin_entry/src/coin_entry.rs index b6a4cd10e6b..e9609515d68 100644 --- a/rust/tw_coin_entry/src/coin_entry.rs +++ b/rust/tw_coin_entry/src/coin_entry.rs @@ -4,7 +4,7 @@ use crate::coin_context::CoinContext; use crate::derivation::Derivation; -use crate::error::AddressResult; +use crate::error::prelude::*; use crate::modules::json_signer::JsonSigner; use crate::modules::plan_builder::PlanBuilder; use crate::prefix::Prefix; diff --git a/rust/tw_coin_entry/src/coin_entry_ext.rs b/rust/tw_coin_entry/src/coin_entry_ext.rs index 4bf882a08e0..25d00ca27c2 100644 --- a/rust/tw_coin_entry/src/coin_entry_ext.rs +++ b/rust/tw_coin_entry/src/coin_entry_ext.rs @@ -5,8 +5,7 @@ use crate::coin_context::CoinContext; use crate::coin_entry::{CoinAddress, CoinEntry, PublicKeyBytes, SignatureBytes}; use crate::derivation::Derivation; -use crate::error::SigningResult; -use crate::error::{AddressResult, SigningError, SigningErrorType}; +use crate::error::prelude::*; use crate::modules::json_signer::JsonSigner; use crate::modules::message_signer::MessageSigner; use crate::modules::plan_builder::PlanBuilder; @@ -154,7 +153,7 @@ where private_key: PrivateKeyBytes, ) -> SigningResult { let Some(json_signer) = self.json_signer() else { - return Err(SigningError(SigningErrorType::Error_not_supported)); + return TWError::err(SigningErrorType::Error_not_supported); }; let private_key = PrivateKey::new(private_key)?; @@ -181,7 +180,7 @@ where fn plan(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult { let Some(plan_builder) = self.plan_builder() else { - return Err(SigningError(SigningErrorType::Error_not_supported)); + return TWError::err(SigningErrorType::Error_not_supported); }; let input: ::SigningInput<'_> = deserialize(input)?; @@ -191,7 +190,7 @@ where fn sign_message(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult { let Some(message_signer) = self.message_signer() else { - return Err(SigningError(SigningErrorType::Error_not_supported)); + return TWError::err(SigningErrorType::Error_not_supported); }; let input: ::MessageSigningInput<'_> = @@ -202,7 +201,7 @@ where fn message_preimage_hashes(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult { let Some(message_signer) = self.message_signer() else { - return Err(SigningError(SigningErrorType::Error_not_supported)); + return TWError::err(SigningErrorType::Error_not_supported); }; let input: ::MessageSigningInput<'_> = @@ -213,7 +212,7 @@ where fn verify_message(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult { let Some(message_signer) = self.message_signer() else { - return Err(SigningError(SigningErrorType::Error_not_supported)); + return TWError::err(SigningErrorType::Error_not_supported); }; let input: ::MessageVerifyingInput<'_> = @@ -227,7 +226,7 @@ where input: &[u8], ) -> SigningResult { let Some(wc_connector) = self.wallet_connector() else { - return Err(SigningError(SigningErrorType::Error_not_supported)); + return TWError::err(SigningErrorType::Error_not_supported); }; let input: WCProto::ParseRequestInput = deserialize(input)?; @@ -237,7 +236,7 @@ where fn decode_transaction(&self, coin: &dyn CoinContext, tx: &[u8]) -> SigningResult { let Some(tx_decoder) = self.transaction_decoder() else { - return Err(SigningError(SigningErrorType::Error_not_supported)); + return TWError::err(SigningErrorType::Error_not_supported); }; let output = tx_decoder.decode_transaction(coin, tx); diff --git a/rust/tw_coin_entry/src/common/compile_input.rs b/rust/tw_coin_entry/src/common/compile_input.rs index e079d5a5c7b..27ce0318844 100644 --- a/rust/tw_coin_entry/src/common/compile_input.rs +++ b/rust/tw_coin_entry/src/common/compile_input.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use crate::coin_entry::{PublicKeyBytes, SignatureBytes}; -use crate::error::{SigningError, SigningErrorType, SigningResult}; +use crate::error::prelude::*; pub struct SingleSignaturePubkey { pub signature: SignatureBytes, @@ -16,17 +16,20 @@ impl SingleSignaturePubkey { public_keys: Vec, ) -> SigningResult { if signatures.len() > 1 || public_keys.len() > 1 { - return Err(SigningError(SigningErrorType::Error_no_support_n2n)); + return TWError::err(SigningErrorType::Error_no_support_n2n) + .context("Expected exactly one signature and public key"); } let signature = signatures .into_iter() .next() - .ok_or(SigningError(SigningErrorType::Error_signatures_count))?; + .or_tw_err(SigningErrorType::Error_signatures_count) + .context("Expected exactly one signature and public key")?; let public_key = public_keys .into_iter() .next() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("Expected exactly one signature and public key")?; Ok(SingleSignaturePubkey { signature, @@ -36,13 +39,15 @@ impl SingleSignaturePubkey { pub fn from_sign_list(signatures: Vec) -> SigningResult { if signatures.len() > 1 { - return Err(SigningError(SigningErrorType::Error_no_support_n2n)); + return TWError::err(SigningErrorType::Error_no_support_n2n) + .context("Expected exactly one signature"); } let signature = signatures .into_iter() .next() - .ok_or(SigningError(SigningErrorType::Error_signatures_count))?; + .or_tw_err(SigningErrorType::Error_signatures_count) + .context("Expected exactly one signature")?; Ok(SingleSignaturePubkey { signature, diff --git a/rust/tw_coin_entry/src/error.rs b/rust/tw_coin_entry/src/error.rs deleted file mode 100644 index a8a75e9a2e5..00000000000 --- a/rust/tw_coin_entry/src/error.rs +++ /dev/null @@ -1,134 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -use std::fmt; -use std::fmt::Formatter; -use tw_encoding::EncodingError; -use tw_keypair::KeyPairError; -use tw_number::NumberError; -use tw_proto::Common::Proto; -use tw_proto::ProtoError; - -#[macro_export] -macro_rules! signing_output_error { - ($output:ty, $error:expr) => {{ - let err = $error; - - let mut output = <$output>::default(); - output.error = err.0; - output.error_message = std::borrow::Cow::from(err.to_string()); - - output - }}; -} - -pub type AddressResult = Result; - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum AddressError { - UnknownCoinType, - Unsupported, - MissingPrefix, - FromHexError, - FromBase58Error, - FromBech32Error, - PublicKeyTypeMismatch, - UnexpectedAddressPrefix, - UnexpectedHasher, - InvalidHrp, - InvalidRegistry, - InvalidInput, - InvalidChecksum, - Internal, -} - -pub type SigningResult = Result; -pub type SigningErrorType = Proto::SigningError; - -/// The wrapper over the `Common::SigningErrorType` error for convenient use. -#[derive(Debug)] -pub struct SigningError(pub SigningErrorType); - -impl From for SigningError { - #[inline] - fn from(_err: NumberError) -> Self { - SigningError(SigningErrorType::Error_invalid_params) - } -} - -impl From for SigningError { - #[inline] - fn from(_err: AddressError) -> Self { - SigningError(SigningErrorType::Error_invalid_address) - } -} - -impl From for SigningError { - fn from(_value: serde_json::Error) -> Self { - SigningError(SigningErrorType::Error_input_parse) - } -} - -impl From for SigningError { - fn from(_e: EncodingError) -> Self { - SigningError(SigningErrorType::Error_input_parse) - } -} - -impl From for SigningError { - fn from(err: KeyPairError) -> Self { - match err { - KeyPairError::InvalidSecretKey => { - SigningError(SigningErrorType::Error_invalid_private_key) - }, - KeyPairError::InvalidPublicKey - | KeyPairError::InvalidSignature - | KeyPairError::InvalidSignMessage - | KeyPairError::SignatureVerifyError => { - SigningError(SigningErrorType::Error_invalid_params) - }, - KeyPairError::SigningError => SigningError(SigningErrorType::Error_signing), - } - } -} - -impl From for SigningError { - fn from(_e: ProtoError) -> Self { - SigningError(SigningErrorType::Error_input_parse) - } -} - -impl fmt::Display for SigningError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let str = match self.0 { - SigningErrorType::OK => "", - SigningErrorType::Error_general => "Unknown error", - SigningErrorType::Error_internal => "Internal error", - SigningErrorType::Error_low_balance => "Low balance: the sender balance is not enough to cover the send and other auxiliary amount such as fee, deposit, or minimal balance", - SigningErrorType::Error_zero_amount_requested => "Requested amount is zero, send of 0 makes no sense", - SigningErrorType::Error_missing_private_key => "One required key is missing (too few or wrong keys are provided)", - SigningErrorType::Error_invalid_private_key => "A private key provided is invalid (e.g. wrong size, usually should be 32 bytes)", - SigningErrorType::Error_invalid_address => "A provided address (e.g. destination address) is invalid", - SigningErrorType::Error_invalid_utxo => "A provided input UTXO is invalid", - SigningErrorType::Error_invalid_utxo_amount => "The amount of an input UTXO is invalid", - SigningErrorType::Error_wrong_fee => "Wrong fee is given, probably it is too low to cover minimal fee for the transaction", - SigningErrorType::Error_signing => "General signing error", - SigningErrorType::Error_tx_too_big => "Resulting transaction is too large", - SigningErrorType::Error_missing_input_utxos => "No input UTXOs provided", - SigningErrorType::Error_not_enough_utxos => "Not enough non-dust input UTXOs to cover requested amount (dust UTXOs are filtered out)", - SigningErrorType::Error_script_redeem => "Missing required redeem script", - SigningErrorType::Error_script_output => "Invalid required output script", - SigningErrorType::Error_script_witness_program => "Unrecognized witness program", - SigningErrorType::Error_invalid_memo => "Invalid memo", - SigningErrorType::Error_input_parse => "Some input field cannot be parsed", - SigningErrorType::Error_no_support_n2n => "Multi-input and multi-output transaction not supported", - SigningErrorType::Error_signatures_count => "Incorrect count of signatures passed to compile", - SigningErrorType::Error_invalid_params => "Incorrect input parameter", - SigningErrorType::Error_invalid_requested_token_amount => "Invalid input token amount", - SigningErrorType::Error_not_supported => "Operation not supported for the chain", - SigningErrorType::Error_dust_amount_requested => "Requested amount is too low (less dust)", - }; - write!(f, "{str}") - } -} diff --git a/rust/tw_coin_entry/src/error/address_error.rs b/rust/tw_coin_entry/src/error/address_error.rs new file mode 100644 index 00000000000..bfd4231d33b --- /dev/null +++ b/rust/tw_coin_entry/src/error/address_error.rs @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +pub type AddressResult = Result; + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum AddressError { + UnknownCoinType, + Unsupported, + MissingPrefix, + FromHexError, + FromBase58Error, + FromBech32Error, + PublicKeyTypeMismatch, + UnexpectedAddressPrefix, + UnexpectedHasher, + InvalidHrp, + InvalidRegistry, + InvalidInput, + InvalidChecksum, + Internal, +} diff --git a/rust/tw_coin_entry/src/error/impl_from.rs b/rust/tw_coin_entry/src/error/impl_from.rs new file mode 100644 index 00000000000..8af3f926749 --- /dev/null +++ b/rust/tw_coin_entry/src/error/impl_from.rs @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::error::address_error::AddressError; +use crate::error::prelude::*; +use tw_encoding::EncodingError; +use tw_keypair::KeyPairError; +use tw_number::NumberError; +use tw_proto::ProtoError; + +impl From for SigningError { + #[inline] + fn from(_err: NumberError) -> Self { + TWError::new(SigningErrorType::Error_invalid_params) + } +} + +impl From for SigningError { + #[inline] + fn from(_err: AddressError) -> Self { + TWError::new(SigningErrorType::Error_invalid_address) + } +} + +impl From for SigningError { + fn from(_value: serde_json::Error) -> Self { + TWError::new(SigningErrorType::Error_input_parse) + } +} + +impl From for SigningError { + fn from(_e: EncodingError) -> Self { + TWError::new(SigningErrorType::Error_input_parse) + } +} + +impl From for SigningError { + fn from(err: KeyPairError) -> Self { + match err { + KeyPairError::InvalidSecretKey => { + TWError::new(SigningErrorType::Error_invalid_private_key) + }, + KeyPairError::InvalidPublicKey + | KeyPairError::InvalidSignature + | KeyPairError::InvalidSignMessage + | KeyPairError::SignatureVerifyError => { + TWError::new(SigningErrorType::Error_invalid_params) + }, + KeyPairError::SigningError => TWError::new(SigningErrorType::Error_signing), + } + } +} + +impl From for SigningError { + fn from(_e: ProtoError) -> Self { + TWError::new(SigningErrorType::Error_input_parse) + } +} diff --git a/rust/tw_coin_entry/src/error/mod.rs b/rust/tw_coin_entry/src/error/mod.rs new file mode 100644 index 00000000000..3202011e149 --- /dev/null +++ b/rust/tw_coin_entry/src/error/mod.rs @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +mod address_error; +mod impl_from; +mod tw_error; +mod tw_result; + +pub mod prelude { + pub use super::address_error::*; + pub use super::tw_error::*; + pub use super::tw_result::*; + + pub type SigningErrorType = tw_proto::Common::Proto::SigningError; + pub type SigningError = TWError; + pub type SigningResult = Result; +} + +#[macro_export] +macro_rules! signing_output_error { + ($output:ty, $error:expr) => {{ + let err = $error; + + let mut output = <$output>::default(); + output.error = *TWError::error_type(&err); + output.error_message = std::borrow::Cow::from(err.to_string()); + + output + }}; +} + +#[cfg(test)] +mod tests { + use super::prelude::*; + + fn function_signing_error_type() -> Result<(), SigningErrorType> { + Err(SigningErrorType::Error_internal) + } + + fn function_address_error() -> AddressResult<()> { + Err(AddressError::Internal) + } + + /// Test `AddressError` -> `TWError` conversion via [`Result::context`]. + #[test] + fn test_error_convert_via_context() { + let err: SigningError = function_address_error() + .into_tw() + .context("!test_error_convert_via_context") + .unwrap_err(); + + let expected = r#"A provided address (e.g. destination address) is invalid +Context: +0. !test_error_convert_via_context"#; + assert_eq!(err.to_string(), expected); + } + + /// Test `AddressError` -> `TWError` conversion via [`Result::into_tw`]. + #[test] + fn test_error_convert_via_into_tw() { + let err: SigningError = function_signing_error_type().into_tw().unwrap_err(); + + let expected = r#"Internal error"#; + assert_eq!(err.to_string(), expected); + } + + /// Test error chaining. + #[test] + fn test_error_chaining() { + let res: SigningResult<()> = SigningError::err(SigningErrorType::Error_internal) + .context("First context") + .context("Second context"); + + let expected = r#"Internal error +Context: +0. First context +1. Second context"#; + assert_eq!(res.unwrap_err().to_string(), expected); + } +} diff --git a/rust/tw_coin_entry/src/error/tw_error.rs b/rust/tw_coin_entry/src/error/tw_error.rs new file mode 100644 index 00000000000..55dc3b3f901 --- /dev/null +++ b/rust/tw_coin_entry/src/error/tw_error.rs @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::error::tw_result::TWResult; +use std::fmt; + +/// The wrapper over the `Common::TWErrorKindType` error for convenient use. +pub struct TWError { + error: E, + context: Vec, +} + +impl TWError { + /// Converts `PrevE` into `E` and wraps it as [`TWError`]. + pub fn new(error: PrevE) -> Self + where + E: From, + { + TWError { + error: E::from(error), + context: Vec::default(), + } + } + + /// Converts `PrevE` into `E` and wraps it as [`Err(TWError)`]. + pub fn err(error: PrevE) -> TWResult + where + E: From, + { + Err(TWError::new(error)) + } + + /// Adds an error context. + pub fn context(mut self, context: C) -> Self + where + C: fmt::Display, + { + self.context.push(context.to_string()); + self + } + + /// Returns an inner error type. + pub fn error_type(&self) -> &E { + &self.error + } + + /// Converts [`TWError`] into [`TWError`]. + pub fn map_err(self, f: F) -> TWError + where + F: FnOnce(E) -> NewE, + { + TWError { + error: f(self.error), + context: self.context, + } + } + + fn format_context(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.context.is_empty() { + return Ok(()); + } + writeln!(f)?; + writeln!(f, "Context:")?; + for (i, context) in self.context.iter().enumerate() { + write!(f, "{i}. {context}")?; + if i < self.context.len() - 1 { + writeln!(f)?; + } + } + Ok(()) + } +} + +impl From for TWError { + fn from(inner: E) -> Self { + TWError::new(inner) + } +} + +impl fmt::Display for TWError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.error)?; + self.format_context(f) + } +} + +impl fmt::Debug for TWError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.error)?; + self.format_context(f) + } +} diff --git a/rust/tw_coin_entry/src/error/tw_result.rs b/rust/tw_coin_entry/src/error/tw_result.rs new file mode 100644 index 00000000000..01390b58ad1 --- /dev/null +++ b/rust/tw_coin_entry/src/error/tw_result.rs @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::error::prelude::TWError; +use std::fmt; + +pub type TWResult = Result>; + +pub trait ResultContext { + /// Wraps the error value with additional context. + fn context(self, context: C) -> Self + where + C: fmt::Display; + + /// Wraps the error value with additional context that is evaluated lazily + /// only once an error does occur. + fn with_context(self, f: F) -> Self + where + C: fmt::Display, + F: FnOnce() -> C; +} + +pub trait IntoTWError { + /// Wraps the inner `E` error into [`TWError`]. + fn into_tw(self) -> TWResult; +} + +pub trait MapTWError { + /// Maps `PrevE` into [`TWError`] with an `F` mapper. + fn tw_err(self, f: F) -> TWResult + where + F: FnOnce(PrevE) -> E; +} + +pub trait OrTWError { + /// Transforms the [`Option`] into a [`Result>`], mapping [`Some(v)`] to + /// [`Ok(v)`] and [`None`] to [`Err(TWError)`]. + fn or_tw_err(self, error: E) -> TWResult; +} + +impl ResultContext for TWResult { + fn context(self, context: C) -> Self + where + C: fmt::Display, + { + self.map_err(|e| e.context(context)) + } + + fn with_context(self, f: F) -> Self + where + C: fmt::Display, + F: FnOnce() -> C, + { + self.map_err(|e| e.context(f())) + } +} + +impl IntoTWError for Result +where + TWError: From, +{ + fn into_tw(self) -> TWResult { + self.map_err(TWError::from) + } +} + +impl MapTWError for Result { + fn tw_err(self, f: F) -> TWResult + where + F: FnOnce(PrevE) -> E, + { + self.map_err(|e| TWError::new(f(e))) + } +} + +impl OrTWError for Option { + fn or_tw_err(self, error: E) -> TWResult { + self.ok_or(TWError::new(error)) + } +} diff --git a/rust/tw_coin_entry/src/modules/json_signer.rs b/rust/tw_coin_entry/src/modules/json_signer.rs index 3fd0988dae6..9dda8728048 100644 --- a/rust/tw_coin_entry/src/modules/json_signer.rs +++ b/rust/tw_coin_entry/src/modules/json_signer.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use crate::coin_context::CoinContext; -use crate::error::SigningResult; +use crate::error::prelude::*; use tw_keypair::tw::PrivateKey; pub trait JsonSigner { diff --git a/rust/tw_coin_entry/src/prefix.rs b/rust/tw_coin_entry/src/prefix.rs index c534dc33134..942b7fec832 100644 --- a/rust/tw_coin_entry/src/prefix.rs +++ b/rust/tw_coin_entry/src/prefix.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use crate::error::AddressError; +use crate::error::prelude::*; /// An address prefix. It can contain a bech32 prefix that can be used by `Cosmos` based chains. /// Extend when adding new blockchains. diff --git a/rust/tw_coin_registry/src/error.rs b/rust/tw_coin_registry/src/error.rs index bdd941095ca..ee8f91eaefe 100644 --- a/rust/tw_coin_registry/src/error.rs +++ b/rust/tw_coin_registry/src/error.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::{SigningError, SigningErrorType}; +use tw_coin_entry::error::prelude::*; pub type RegistryResult = Result; @@ -16,8 +16,12 @@ impl From for SigningError { #[inline] fn from(e: RegistryError) -> Self { match e { - RegistryError::UnknownCoinType => SigningError(SigningErrorType::Error_invalid_params), - RegistryError::Unsupported => SigningError(SigningErrorType::Error_internal), + RegistryError::UnknownCoinType => { + SigningError::new(SigningErrorType::Error_invalid_params) + .context("Unknown coin type") + }, + RegistryError::Unsupported => SigningError::new(SigningErrorType::Error_internal) + .context("Requested coin type is not supported in Rust yet"), } } } diff --git a/rust/tw_cosmos_sdk/src/address.rs b/rust/tw_cosmos_sdk/src/address.rs index 10cbca9a10c..f860be7ed31 100644 --- a/rust/tw_cosmos_sdk/src/address.rs +++ b/rust/tw_cosmos_sdk/src/address.rs @@ -4,7 +4,7 @@ use serde::Serialize; use std::str::FromStr; -use tw_coin_entry::error::AddressError; +use tw_coin_entry::error::prelude::*; pub type Address = tw_bech32_address::Bech32Address; pub type Bech32Prefix = tw_bech32_address::bech32_prefix::Bech32Prefix; diff --git a/rust/tw_cosmos_sdk/src/modules/broadcast_msg.rs b/rust/tw_cosmos_sdk/src/modules/broadcast_msg.rs index 73b0beafd98..a268e3d7876 100644 --- a/rust/tw_cosmos_sdk/src/modules/broadcast_msg.rs +++ b/rust/tw_cosmos_sdk/src/modules/broadcast_msg.rs @@ -5,7 +5,7 @@ use quick_protobuf::MessageWrite; use serde::Serialize; use serde_json::Value as Json; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::base64::Base64Encoded; use tw_proto::serialize; @@ -46,8 +46,9 @@ impl BroadcastMsg { BroadcastMode::Sync => "sync", } .to_string(); - let tx = - serde_json::to_value(tx).map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let tx = serde_json::to_value(tx) + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing transaction to sign as JSON")?; Ok(BroadcastMsg::Json { mode, tx }) } diff --git a/rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs b/rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs index a8310883057..9e2a372dcd5 100644 --- a/rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs +++ b/rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs @@ -7,7 +7,7 @@ use crate::modules::serializer::json_serializer::JsonSerializer; use crate::public_key::JsonPublicKey; use crate::transaction::UnsignedTransaction; use std::marker::PhantomData; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_hash::hasher::Hasher; use tw_memory::Data; @@ -30,7 +30,8 @@ where ) -> SigningResult { let tx_to_sign = JsonSerializer::build_unsigned_tx(unsigned)?; let encoded_tx = serde_json::to_string(&tx_to_sign) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing transaction to sign as JSON")?; let tx_hash = hasher.hash(encoded_tx.as_bytes()); Ok(JsonTxPreimage { diff --git a/rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs b/rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs index dc82a15920a..57f9aea9116 100644 --- a/rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs +++ b/rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs @@ -6,7 +6,7 @@ use crate::context::CosmosContext; use crate::modules::serializer::protobuf_serializer::{ProtobufSerializer, SignDirectArgs}; use crate::transaction::UnsignedTransaction; use std::marker::PhantomData; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_hash::hasher::Hasher; use tw_memory::Data; use tw_proto::serialize; diff --git a/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs b/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs index 4059f09fbcc..544933f3964 100644 --- a/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs +++ b/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs @@ -15,7 +15,7 @@ use std::marker::PhantomData; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; use tw_coin_entry::common::compile_input::SingleSignaturePubkey; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_misc::traits::ToBytesVec; use tw_proto::Cosmos::Proto; @@ -31,11 +31,8 @@ impl TWTransactionCompiler { coin: &dyn CoinContext, input: Proto::SigningInput<'_>, ) -> CompilerProto::PreSigningOutput<'static> { - match input.signing_mode { - Proto::SigningMode::JSON => Self::preimage_hashes_as_json(coin, input), - Proto::SigningMode::Protobuf => Self::preimage_hashes_as_protobuf(coin, input), - } - .unwrap_or_else(|e| signing_output_error!(CompilerProto::PreSigningOutput, e)) + Self::preimage_hashes_impl(coin, input) + .unwrap_or_else(|e| signing_output_error!(CompilerProto::PreSigningOutput, e)) } #[inline] @@ -45,16 +42,22 @@ impl TWTransactionCompiler { signatures: Vec, public_keys: Vec, ) -> Proto::SigningOutput<'static> { + Self::compile_impl(coin, input, signatures, public_keys) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + #[inline] + pub(crate) fn preimage_hashes_impl( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> SigningResult> { match input.signing_mode { - Proto::SigningMode::JSON => Self::compile_as_json(coin, input, signatures, public_keys), - Proto::SigningMode::Protobuf => { - Self::compile_as_protobuf(coin, input, signatures, public_keys) - }, + Proto::SigningMode::JSON => Self::preimage_hashes_as_json(coin, input), + Proto::SigningMode::Protobuf => Self::preimage_hashes_as_protobuf(coin, input), } - .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) } - pub fn preimage_hashes_as_protobuf( + pub(crate) fn preimage_hashes_as_protobuf( coin: &dyn CoinContext, input: Proto::SigningInput<'_>, ) -> SigningResult> { @@ -79,7 +82,7 @@ impl TWTransactionCompiler { }) } - pub fn preimage_hashes_as_json( + pub(crate) fn preimage_hashes_as_json( coin: &dyn CoinContext, input: Proto::SigningInput<'_>, ) -> SigningResult> { @@ -95,7 +98,22 @@ impl TWTransactionCompiler { }) } - pub fn compile_as_protobuf( + #[inline] + pub(crate) fn compile_impl( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> SigningResult> { + match input.signing_mode { + Proto::SigningMode::JSON => Self::compile_as_json(coin, input, signatures, public_keys), + Proto::SigningMode::Protobuf => { + Self::compile_as_protobuf(coin, input, signatures, public_keys) + }, + } + } + + pub(crate) fn compile_as_protobuf( coin: &dyn CoinContext, mut input: Proto::SigningInput<'_>, signatures: Vec, @@ -133,7 +151,8 @@ impl TWTransactionCompiler { let signature_json = JsonSerializer::::serialize_signature(&public_key, signature.to_vec()); let signature_json = serde_json::to_string(&[signature_json]) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing signatures as JSON")?; Ok(Proto::SigningOutput { signature: Cow::from(signature.to_vec()), @@ -143,7 +162,7 @@ impl TWTransactionCompiler { }) } - pub fn compile_as_json( + pub(crate) fn compile_as_json( coin: &dyn CoinContext, mut input: Proto::SigningInput<'_>, signatures: Vec, @@ -169,7 +188,8 @@ impl TWTransactionCompiler { let broadcast_tx = BroadcastMsg::json(broadcast_mode, &signed_tx_json)?.to_json_string(); let signature_json = serde_json::to_string(&signed_tx_json.signatures) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing signatures as JSON")?; Ok(Proto::SigningOutput { signature: Cow::from(signature.to_vec()), diff --git a/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs b/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs index 1bf3dff3649..f38d8e5d9db 100644 --- a/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs +++ b/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs @@ -9,7 +9,7 @@ use crate::transaction::{Coin, Fee, SignedTransaction, UnsignedTransaction}; use serde::Serialize; use serde_json::Value as Json; use std::marker::PhantomData; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_encoding::base64::Base64Encoded; #[derive(Serialize)] diff --git a/rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs b/rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs index 150667bb5db..91141c2e1f1 100644 --- a/rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs +++ b/rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs @@ -11,7 +11,7 @@ use crate::transaction::{ Coin, Fee, SignMode, SignedTransaction, SignerInfo, TxBody, UnsignedTransaction, }; use std::marker::PhantomData; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; use tw_proto::serialize; diff --git a/rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs b/rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs index 4a6d4b276c0..5908ae38efa 100644 --- a/rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs +++ b/rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs @@ -10,7 +10,7 @@ use crate::public_key::CosmosPublicKey; use std::borrow::Cow; use std::marker::PhantomData; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_proto::Cosmos::Proto; @@ -41,23 +41,14 @@ impl TWSigner { input.public_key = Cow::from(public_key.to_bytes()); let preimage_output = - TWTransactionCompiler::::preimage_hashes(coin, input.clone()); - if preimage_output.error != SigningErrorType::OK { - return Err(SigningError(preimage_output.error)); - } + TWTransactionCompiler::::preimage_hashes_impl(coin, input.clone())?; let signature_data = private_key.sign_tx_hash(&preimage_output.data_hash)?; - let compile_output = TWTransactionCompiler::::compile( + TWTransactionCompiler::::compile_impl( coin, input, vec![signature_data], vec![public_key.to_bytes()], - ); - - if compile_output.error != SigningErrorType::OK { - return Err(SigningError(preimage_output.error)); - } - - Ok(compile_output) + ) } } diff --git a/rust/tw_cosmos_sdk/src/modules/tx_builder.rs b/rust/tw_cosmos_sdk/src/modules/tx_builder.rs index 8bd97cafbb1..519b4b89dae 100644 --- a/rust/tw_cosmos_sdk/src/modules/tx_builder.rs +++ b/rust/tw_cosmos_sdk/src/modules/tx_builder.rs @@ -12,7 +12,7 @@ use crate::transaction::{Coin, Fee, SignMode, SignerInfo, TxBody, UnsignedTransa use std::marker::PhantomData; use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_hash::hasher::Hasher; use tw_keypair::tw; use tw_misc::traits::{OptionalEmpty, ToBytesVec}; @@ -37,7 +37,8 @@ where let fee = input .fee .as_ref() - .ok_or(SigningError(SigningErrorType::Error_wrong_fee))?; + .or_tw_err(SigningErrorType::Error_wrong_fee) + .context("No fee specified")?; let signer = Self::signer_info_from_proto(coin, input)?; Ok(UnsignedTransaction { @@ -100,7 +101,9 @@ where } fn coin_from_proto(input: &Proto::Amount<'_>) -> SigningResult { - let amount = U256::from_str(&input.amount)?; + let amount = U256::from_str(&input.amount) + .into_tw() + .context("Invalid amount, expected string decimal")?; Ok(Coin { amount, denom: input.denom.to_string(), @@ -112,7 +115,8 @@ where input: &Proto::SigningInput<'_>, ) -> SigningResult { if input.messages.is_empty() { - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("No TX messages provided"); } let messages = input @@ -201,7 +205,8 @@ where MessageEnum::sign_direct_message(ref _sign) => { // `SignDirect` message must be handled before this function is called. // Consider using `Self::try_sign_direct_args` instead. - Err(SigningError(SigningErrorType::Error_not_supported)) + SigningError::err(SigningErrorType::Error_not_supported) + .context("Consider using `Self::try_sign_direct_args` instead") }, MessageEnum::auth_grant(ref grant) => Self::auth_grant_msg_from_proto(coin, grant), MessageEnum::auth_revoke(ref revoke) => Self::auth_revoke_msg_from_proto(coin, revoke), @@ -215,7 +220,8 @@ where MessageEnum::thorchain_deposit_message(ref deposit) => { Self::thorchain_deposit_msg_from_proto(coin, deposit) }, - MessageEnum::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + MessageEnum::None => SigningError::err(SigningErrorType::Error_invalid_params) + .context("No TX message provided"), } } @@ -232,8 +238,12 @@ where .collect::>()?; let msg = SendMessage { custom_type_prefix: send.type_prefix.to_string().empty_or_some(), - from_address: Address::from_str(&send.from_address)?, - to_address: Address::from_str(&send.to_address)?, + from_address: Address::from_str(&send.from_address) + .into_tw() + .context("Invalid sender address")?, + to_address: Address::from_str(&send.to_address) + .into_tw() + .context("Invalid receiver address")?, amount: amounts, }; Ok(msg.into_boxed()) @@ -248,19 +258,25 @@ where let token = transfer .token .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("No token specified")?; let token = Self::coin_from_proto(token)?; let height = transfer .timeout_height .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("No timeout height specified")?; let msg = TransferTokensMessage { source_port: transfer.source_port.to_string(), source_channel: transfer.source_channel.to_string(), token, - sender: Address::from_str(&transfer.sender)?, - receiver: Address::from_str(&transfer.receiver)?, + sender: Address::from_str(&transfer.sender) + .into_tw() + .context("Invalid sender address")?, + receiver: Address::from_str(&transfer.receiver) + .into_tw() + .context("Invalid receiver address")?, timeout_height: Height { revision_number: height.revision_number, revision_height: height.revision_height, @@ -279,13 +295,18 @@ where let amount = delegate .amount .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("No amount specified")?; let amount = Self::coin_from_proto(amount)?; let msg = DelegateMessage { custom_type_prefix: delegate.type_prefix.to_string().empty_or_some(), amount, - delegator_address: Address::from_str(&delegate.delegator_address)?, - validator_address: Address::from_str(&delegate.validator_address)?, + delegator_address: Address::from_str(&delegate.delegator_address) + .into_tw() + .context("Invalid delegator address")?, + validator_address: Address::from_str(&delegate.validator_address) + .into_tw() + .context("Invalid validator address")?, }; Ok(msg.into_boxed()) } @@ -299,14 +320,19 @@ where let amount = undelegate .amount .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("No amount specified")?; let amount = Self::coin_from_proto(amount)?; let msg = UndelegateMessage { custom_type_prefix: undelegate.type_prefix.to_string().empty_or_some(), amount, - delegator_address: Address::from_str(&undelegate.delegator_address)?, - validator_address: Address::from_str(&undelegate.validator_address)?, + delegator_address: Address::from_str(&undelegate.delegator_address) + .into_tw() + .context("Invalid delegator address")?, + validator_address: Address::from_str(&undelegate.validator_address) + .into_tw() + .context("Invalid validator address")?, }; Ok(msg.into_boxed()) } @@ -319,8 +345,12 @@ where let msg = WithdrawDelegationRewardMessage { custom_type_prefix: withdraw.type_prefix.to_string().empty_or_some(), - delegator_address: Address::from_str(&withdraw.delegator_address)?, - validator_address: Address::from_str(&withdraw.validator_address)?, + delegator_address: Address::from_str(&withdraw.delegator_address) + .into_tw() + .context("Invalid delegator address")?, + validator_address: Address::from_str(&withdraw.validator_address) + .into_tw() + .context("Invalid validator address")?, }; Ok(msg.into_boxed()) } @@ -333,8 +363,12 @@ where let msg = SetWithdrawAddressMessage { custom_type_prefix: set.type_prefix.to_string().empty_or_some(), - delegator_address: Address::from_str(&set.delegator_address)?, - withdraw_address: Address::from_str(&set.withdraw_address)?, + delegator_address: Address::from_str(&set.delegator_address) + .into_tw() + .context("Invalid delegator address")?, + withdraw_address: Address::from_str(&set.withdraw_address) + .into_tw() + .context("Invalid withdraw address")?, }; Ok(msg.into_boxed()) } @@ -348,15 +382,22 @@ where let amount = redelegate .amount .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("No amount specified")?; let amount = Self::coin_from_proto(amount)?; - let validator_src_address = Address::from_str(&redelegate.validator_src_address)?; - let validator_dst_address = Address::from_str(&redelegate.validator_dst_address)?; + let validator_src_address = Address::from_str(&redelegate.validator_src_address) + .into_tw() + .context("Invalid source validator address")?; + let validator_dst_address = Address::from_str(&redelegate.validator_dst_address) + .into_tw() + .context("Invalid destination validator address")?; let msg = BeginRedelegateMessage { custom_type_prefix: redelegate.type_prefix.to_string().empty_or_some(), amount, - delegator_address: Address::from_str(&redelegate.delegator_address)?, + delegator_address: Address::from_str(&redelegate.delegator_address) + .into_tw() + .context("Invalid delegator address")?, validator_src_address, validator_dst_address, }; @@ -368,7 +409,8 @@ where raw: &Proto::mod_Message::RawJSON<'_>, ) -> SigningResult { let value = serde_json::from_str(&raw.value) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error parsing raw JSON")?; let msg = JsonRawMessage { msg_type: raw.type_pb.to_string(), @@ -385,13 +427,19 @@ where use crate::transaction::message::wasm_message::{ExecuteMsg, WasmExecutePayload}; let execute_payload = WasmExecutePayload::Transfer { - amount: U256::from_big_endian_slice(&transfer.amount)?, + amount: U256::from_big_endian_slice(&transfer.amount) + .into_tw() + .context("Expected U256 big-endian amount")?, recipient: transfer.recipient_address.to_string(), }; let msg = TerraExecuteContractMessage { - sender: Address::from_str(&transfer.sender_address)?, - contract: Address::from_str(&transfer.contract_address)?, + sender: Address::from_str(&transfer.sender_address) + .into_tw() + .context("Invalid sender address")?, + contract: Address::from_str(&transfer.contract_address) + .into_tw() + .context("Invalid contract address")?, execute_msg: ExecuteMsg::json(execute_payload)?, // Used in case you are sending native tokens along with this message. coins: Vec::default(), @@ -407,14 +455,20 @@ where use crate::transaction::message::wasm_message::{ExecuteMsg, WasmExecutePayload}; let execute_payload = WasmExecutePayload::Send { - amount: U256::from_big_endian_slice(&send.amount)?, + amount: U256::from_big_endian_slice(&send.amount) + .into_tw() + .context("Expected U256 big-endian amount")?, contract: send.recipient_contract_address.to_string(), msg: send.msg.to_string(), }; let msg = TerraExecuteContractMessage { - sender: Address::from_str(&send.sender_address)?, - contract: Address::from_str(&send.contract_address)?, + sender: Address::from_str(&send.sender_address) + .into_tw() + .context("Invalid sender address")?, + contract: Address::from_str(&send.contract_address) + .into_tw() + .context("Invalid contract address")?, execute_msg: ExecuteMsg::json(execute_payload)?, // Used in case you are sending native tokens along with this message. coins: Vec::default(), @@ -436,8 +490,12 @@ where .collect::>()?; let msg = TerraExecuteContractMessage { - sender: Address::from_str(&generic.sender_address)?, - contract: Address::from_str(&generic.contract_address)?, + sender: Address::from_str(&generic.sender_address) + .into_tw() + .context("Invalid sender address")?, + contract: Address::from_str(&generic.contract_address) + .into_tw() + .context("Invalid contract address")?, execute_msg: ExecuteMsg::String(generic.execute_msg.to_string()), coins, }; @@ -453,13 +511,19 @@ where }; let transfer_payload = WasmExecutePayload::Transfer { - amount: U256::from_big_endian_slice(&transfer.amount)?, + amount: U256::from_big_endian_slice(&transfer.amount) + .into_tw() + .context("Expected U256 big-endian amount")?, recipient: transfer.recipient_address.to_string(), }; let msg = WasmExecuteContractMessage { - sender: Address::from_str(&transfer.sender_address)?, - contract: Address::from_str(&transfer.contract_address)?, + sender: Address::from_str(&transfer.sender_address) + .into_tw() + .context("Invalid sender address")?, + contract: Address::from_str(&transfer.contract_address) + .into_tw() + .context("Invalid contract address")?, msg: ExecuteMsg::json(transfer_payload)?, // Used in case you are sending native tokens along with this message. coins: Vec::default(), @@ -476,14 +540,20 @@ where }; let execute_payload = WasmExecutePayload::Send { - amount: U256::from_big_endian_slice(&send.amount)?, + amount: U256::from_big_endian_slice(&send.amount) + .into_tw() + .context("Expected U256 big-endian amount")?, contract: send.recipient_contract_address.to_string(), msg: send.msg.to_string(), }; let msg = WasmExecuteContractMessage { - sender: Address::from_str(&send.sender_address)?, - contract: Address::from_str(&send.contract_address)?, + sender: Address::from_str(&send.sender_address) + .into_tw() + .context("Invalid sender address")?, + contract: Address::from_str(&send.contract_address) + .into_tw() + .context("Invalid contract address")?, msg: ExecuteMsg::json(execute_payload)?, // Used in case you are sending native tokens along with this message. coins: Vec::default(), @@ -504,8 +574,12 @@ where .collect::>()?; let msg = WasmExecuteContractMessage { - sender: Address::from_str(&generic.sender_address)?, - contract: Address::from_str(&generic.contract_address)?, + sender: Address::from_str(&generic.sender_address) + .into_tw() + .context("Invalid sender address")?, + contract: Address::from_str(&generic.contract_address) + .into_tw() + .context("Invalid contract address")?, msg: ExecuteMsg::String(generic.execute_msg.to_string()), coins, }; @@ -545,16 +619,22 @@ where ProtoGrantType::grant_stake(ref stake) => google::protobuf::Any { type_url: STAKE_AUTHORIZATION_MSG_TYPE.to_string(), value: serialize(stake) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?, + .tw_err(|_| SigningErrorType::Error_invalid_params) + .context("Error serializing Grant Stake Protobuf message")?, }, ProtoGrantType::None => { - return Err(SigningError(SigningErrorType::Error_invalid_params)) + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("No Grant type specified"); }, }; let msg = AuthGrantMessage { - granter: Address::from_str(&auth.granter)?, - grantee: Address::from_str(&auth.grantee)?, + granter: Address::from_str(&auth.granter) + .into_tw() + .context("Invalid granter address")?, + grantee: Address::from_str(&auth.grantee) + .into_tw() + .context("Invalid grantee address")?, grant_msg, expiration_secs: auth.expiration, }; @@ -568,8 +648,12 @@ where use crate::transaction::message::cosmos_auth_message::AuthRevokeMessage; let msg = AuthRevokeMessage { - granter: Address::from_str(&auth.granter)?, - grantee: Address::from_str(&auth.grantee)?, + granter: Address::from_str(&auth.granter) + .into_tw() + .context("Invalid granter address")?, + grantee: Address::from_str(&auth.grantee) + .into_tw() + .context("Invalid grantee address")?, msg_type_url: auth.msg_type_url.to_string(), }; Ok(msg.into_boxed()) @@ -592,7 +676,9 @@ where let msg = VoteMessage { proposal_id: vote.proposal_id, - voter: Address::from_str(&vote.voter)?, + voter: Address::from_str(&vote.voter) + .into_tw() + .context("Invalid voter address")?, option, }; Ok(msg.into_boxed()) @@ -605,8 +691,12 @@ where use crate::transaction::message::stride_message::StrideLiquidStakeMessage; let msg = StrideLiquidStakeMessage { - creator: Address::from_str(&stake.creator)?, - amount: U256::from_str(&stake.amount)?, + creator: Address::from_str(&stake.creator) + .into_tw() + .context("Invalid creator address")?, + amount: U256::from_str(&stake.amount) + .into_tw() + .context("Expected U256 big-endian amount")?, host_denom: stake.host_denom.to_string(), }; Ok(msg.into_boxed()) @@ -620,7 +710,9 @@ where let msg = StrideLiquidRedeemMessage { creator: redeem.creator.to_string(), - amount: U256::from_str(&redeem.amount)?, + amount: U256::from_str(&redeem.amount) + .into_tw() + .context("Expected U256 big-endian amount")?, receiver: redeem.receiver.to_string(), host_zone: redeem.host_zone.to_string(), }; @@ -640,7 +732,8 @@ where let asset_proto = coin_proto .asset .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + .or_tw_err(SigningErrorType::Error_invalid_params) + .context("No Deposit Asset specified")?; let asset = ThorchainAsset { chain: asset_proto.chain.to_string(), @@ -650,7 +743,9 @@ where }; coins.push(ThorchainCoin { asset, - amount: U256::from_str(&coin_proto.amount)?, + amount: U256::from_str(&coin_proto.amount) + .into_tw() + .context("Expected U256 big-endian Deposit amount")?, decimals: coin_proto.decimals, }); } diff --git a/rust/tw_cosmos_sdk/src/private_key/mod.rs b/rust/tw_cosmos_sdk/src/private_key/mod.rs index bb9bb32ae46..7e60ed1dec9 100644 --- a/rust/tw_cosmos_sdk/src/private_key/mod.rs +++ b/rust/tw_cosmos_sdk/src/private_key/mod.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_keypair::{tw, KeyPairError}; use tw_memory::Data; use tw_misc::traits::FromSlice; diff --git a/rust/tw_cosmos_sdk/src/private_key/secp256k1.rs b/rust/tw_cosmos_sdk/src/private_key/secp256k1.rs index 8318bc676f5..655b80d5a51 100644 --- a/rust/tw_cosmos_sdk/src/private_key/secp256k1.rs +++ b/rust/tw_cosmos_sdk/src/private_key/secp256k1.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use crate::private_key::CosmosPrivateKey; -use tw_coin_entry::error::{SigningError, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_keypair::tw; use tw_keypair::tw::Curve; use tw_keypair::KeyPairError; diff --git a/rust/tw_cosmos_sdk/src/test_utils/sign_utils.rs b/rust/tw_cosmos_sdk/src/test_utils/sign_utils.rs index 93c0dc3b64e..88fb612fb00 100644 --- a/rust/tw_cosmos_sdk/src/test_utils/sign_utils.rs +++ b/rust/tw_cosmos_sdk/src/test_utils/sign_utils.rs @@ -6,7 +6,7 @@ use crate::context::CosmosContext; use crate::modules::compiler::tw_compiler::TWTransactionCompiler; use crate::modules::signer::tw_signer::TWSigner; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_proto::Common::Proto::SigningError; use tw_proto::Cosmos::Proto; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_auth_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_auth_message.rs index dd0e2df4957..32918948a7f 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_auth_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_auth_message.rs @@ -5,7 +5,7 @@ use crate::address::CosmosAddress; use crate::proto::cosmos; use crate::transaction::message::{CosmosMessage, ProtobufMessage}; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_proto::{google, to_any}; /// Supports Protobuf serialization only. diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs index b5fa6a99a4c..7b09ec52056 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs @@ -8,7 +8,7 @@ use crate::proto::cosmos; use crate::transaction::message::{message_to_json, CosmosMessage, JsonMessage, ProtobufMessage}; use crate::transaction::Coin; use serde::Serialize; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_proto::to_any; const DEFAULT_JSON_SEND_TYPE: &str = "cosmos-sdk/MsgSend"; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_generic_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_generic_message.rs index c6762b8ec07..584ea6e3a22 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_generic_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_generic_message.rs @@ -4,7 +4,7 @@ use crate::transaction::message::{CosmosMessage, JsonMessage}; use serde_json::Value as Json; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; /// Any raw JSON message. /// Supports JSON serialization only. diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_gov_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_gov_message.rs index 3f72c02bb35..6c990c14fff 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_gov_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_gov_message.rs @@ -5,7 +5,7 @@ use crate::address::CosmosAddress; use crate::proto::cosmos; use crate::transaction::message::{CosmosMessage, ProtobufMessage}; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_proto::to_any; pub enum VoteOption { diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_staking_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_staking_message.rs index 9c8d47d1281..dc3c6238b9c 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_staking_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_staking_message.rs @@ -8,7 +8,7 @@ use crate::proto::cosmos; use crate::transaction::message::{message_to_json, CosmosMessage, JsonMessage, ProtobufMessage}; use crate::transaction::Coin; use serde::Serialize; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_proto::to_any; const DEFAULT_JSON_SET_WITHDRAW_ADDRESS_TYPE: &str = "cosmos-sdk/MsgSetWithdrawAddress"; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/ibc_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/ibc_message.rs index 54eb8dca0b6..e3cb7ae29eb 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/ibc_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/ibc_message.rs @@ -7,7 +7,7 @@ use crate::modules::serializer::protobuf_serializer::build_coin; use crate::proto::ibc; use crate::transaction::message::{CosmosMessage, ProtobufMessage}; use crate::transaction::Coin; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_proto::to_any; pub struct Height { diff --git a/rust/tw_cosmos_sdk/src/transaction/message/mod.rs b/rust/tw_cosmos_sdk/src/transaction/message/mod.rs index 3674b107903..0ecc466e1fc 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/mod.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/mod.rs @@ -5,7 +5,7 @@ use crate::modules::serializer::json_serializer::AnyMsg; use serde::Serialize; use serde_json::Value as Json; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_proto::google; pub mod cosmos_auth_message; @@ -33,20 +33,23 @@ pub trait CosmosMessage { /// Override the method if the message can be represented as a Protobuf message. fn to_proto(&self) -> SigningResult { - Err(SigningError(SigningErrorType::Error_not_supported)) + SigningError::err(SigningErrorType::Error_not_supported) + .context("Message cannot be converted to Protobuf") } /// Override the method if the message can be represented as a JSON object. fn to_json(&self) -> SigningResult { - Err(SigningError(SigningErrorType::Error_not_supported)) + SigningError::err(SigningErrorType::Error_not_supported) + .context("Message cannot be converted to JSON") } } /// A standard implementation of the [`CosmosMessage::to_json`] method. /// This suits any message type that implements the `serialize` trait. pub fn message_to_json(msg_type: &str, msg: &T) -> SigningResult { - let value = - serde_json::to_value(msg).map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let value = serde_json::to_value(msg) + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing Cosmos message to JSON")?; Ok(JsonMessage { msg_type: msg_type.to_string(), value, diff --git a/rust/tw_cosmos_sdk/src/transaction/message/stride_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/stride_message.rs index 0565add30ab..1ef8e86eaec 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/stride_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/stride_message.rs @@ -5,7 +5,7 @@ use crate::address::CosmosAddress; use crate::proto::stride; use crate::transaction::message::{CosmosMessage, ProtobufMessage}; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_number::U256; use tw_proto::to_any; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/terra_wasm_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/terra_wasm_message.rs index 0613c22aa3f..b74e6a01bd5 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/terra_wasm_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/terra_wasm_message.rs @@ -10,7 +10,7 @@ use crate::transaction::message::{CosmosMessage, JsonMessage, ProtobufMessage}; use crate::transaction::Coin; use serde::Serialize; use serde_json::json; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_proto::to_any; const DEFAULT_JSON_MSG_TYPE: &str = "wasm/MsgExecuteContract"; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/thorchain_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/thorchain_message.rs index 003adb077ec..6158c4f5815 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/thorchain_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/thorchain_message.rs @@ -6,7 +6,7 @@ use crate::modules::serializer::protobuf_serializer::build_coin; use crate::proto::types; use crate::transaction::message::{CosmosMessage, ProtobufMessage}; use crate::transaction::Coin; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; use tw_number::U256; use tw_proto::to_any; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/wasm_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/wasm_message.rs index f647367d469..21163f691a3 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/wasm_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/wasm_message.rs @@ -9,7 +9,7 @@ use crate::transaction::message::{CosmosMessage, JsonMessage, ProtobufMessage}; use crate::transaction::Coin; use serde::Serialize; use serde_json::{json, Value as Json}; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; use tw_number::U256; use tw_proto::to_any; @@ -38,7 +38,8 @@ impl ExecuteMsg { pub fn json(payload: Payload) -> SigningResult { let payload = serde_json::to_value(payload) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + .tw_err(|_| SigningErrorType::Error_internal) + .context("Error serializing message payload to JSON")?; Ok(ExecuteMsg::Json(payload)) } diff --git a/rust/tw_evm/src/abi/contract.rs b/rust/tw_evm/src/abi/contract.rs index 0226bb48744..f2ade089792 100644 --- a/rust/tw_evm/src/abi/contract.rs +++ b/rust/tw_evm/src/abi/contract.rs @@ -3,9 +3,10 @@ // Copyright © 2017 Trust Wallet. use crate::abi::function::Function; -use crate::abi::{AbiError, AbiErrorKind, AbiResult}; +use crate::abi::{AbiErrorKind, AbiResult}; use serde::{Deserialize, Deserializer}; use std::collections::BTreeMap; +use tw_coin_entry::error::prelude::*; /// API building calls to contracts ABI. /// Consider adding missing field such as `errors`, `events` etc. @@ -22,7 +23,8 @@ impl Contract { .into_iter() .flatten() .next() - .ok_or(AbiError(AbiErrorKind::Error_abi_mismatch)) + .or_tw_err(AbiErrorKind::Error_abi_mismatch) + .with_context(|| format!("The given Smart Contract does not have '{name}' function")) } } diff --git a/rust/tw_evm/src/abi/decode.rs b/rust/tw_evm/src/abi/decode.rs index 0b13d192076..51165749d70 100644 --- a/rust/tw_evm/src/abi/decode.rs +++ b/rust/tw_evm/src/abi/decode.rs @@ -10,6 +10,7 @@ use crate::abi::token::Token; use crate::abi::{AbiError, AbiErrorKind, AbiResult}; use crate::address::Address; use lazy_static::lazy_static; +use tw_coin_entry::error::prelude::*; use tw_hash::{H160, H256}; use tw_number::{I256, U256}; @@ -22,8 +23,7 @@ lazy_static! { pub fn decode_params(params: &[Param], data: &[u8]) -> AbiResult> { let param_types: Vec<_> = params.iter().map(|param| param.kind.clone()).collect(); - let decoded_tokens = decode_params_impl(¶m_types, data) - .map_err(|_| AbiError(AbiErrorKind::Error_decoding_data))?; + let decoded_tokens = decode_params_impl(¶m_types, data)?; let named_tokens: Vec<_> = params .iter() @@ -65,7 +65,8 @@ fn decode_params_impl(types: &[ParamType], data: &[u8]) -> AbiResult> fn decode_offset(types: &[ParamType], data: &[u8]) -> AbiResult<(Vec, usize)> { // We don't support empty `FixedBytes` or `FixedArray` collections. if data.is_empty() { - return Err(AbiError(AbiErrorKind::Error_decoding_data)); + return AbiError::err(AbiErrorKind::Error_decoding_data) + .context("Empty `FixedBytes` or `FixedArray` collections are not allowed"); } let mut tokens = vec![]; @@ -83,7 +84,8 @@ fn decode_offset(types: &[ParamType], data: &[u8]) -> AbiResult<(Vec, usi fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult { match param { ParamType::Address => { - let slice = peek_32_bytes(data, offset)?; + let slice = peek_32_bytes(data, offset) + .with_context(|| format!("Error decoding Address parameter at {offset}"))?; let mut address = H160::default(); address.copy_from_slice(&slice[12..]); let result = DecodeResult { @@ -93,7 +95,8 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult { - let slice = peek_32_bytes(data, offset)?; + let slice = peek_32_bytes(data, offset) + .with_context(|| format!("Error decoding Int parameter at {offset}"))?; let result = DecodeResult { token: Token::Int { int: I256::from_big_endian(slice), @@ -104,7 +107,8 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult { - let slice = peek_32_bytes(data, offset)?; + let slice = peek_32_bytes(data, offset) + .with_context(|| format!("Error decoding Uint parameter at {offset}"))?; let result = DecodeResult { token: Token::Uint { uint: U256::from_big_endian(slice), @@ -115,7 +119,9 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult { - let b = as_bool(&peek_32_bytes(data, offset)?)?; + let b = peek_32_bytes(data, offset) + .and_then(as_bool) + .with_context(|| format!("Error decoding Bool parameter at {offset}"))?; let result = DecodeResult { token: Token::Bool(b), new_offset: offset + WORD_LEN, @@ -125,8 +131,10 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult { // FixedBytes is anything from bytes1 to bytes32. These values // are padded with trailing zeros to fill 32 bytes. - let bytes = take_bytes(data, offset, len.get())?; - let checked_bytes = NonEmptyBytes::new(bytes)?; + let bytes = take_bytes(data, offset, len.get()) + .with_context(|| format!("Error decoding FixedBytes parameter at {offset}"))?; + let checked_bytes = NonEmptyBytes::new(bytes) + .context("Empty `FixedBytes` collection is not allowed")?; let result = DecodeResult { token: Token::FixedBytes(checked_bytes), new_offset: offset + WORD_LEN, @@ -134,11 +142,17 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult { - let dynamic_offset = as_usize(&peek_32_bytes(data, offset)?)?; - let bytes_offset = add_checked(dynamic_offset, WORD_LEN)?; - - let len = as_usize(&peek_32_bytes(data, dynamic_offset)?)?; - let bytes = take_bytes(data, bytes_offset, len)?; + let dynamic_offset = peek_32_bytes(data, offset) + .and_then(as_usize) + .with_context(|| format!("Error decoding Bytes dynamic offset at {offset}"))?; + let bytes_offset = add_checked(dynamic_offset, WORD_LEN) + .with_context(|| format!("Dynamic offset is too big at {offset}"))?; + + let len = peek_32_bytes(data, dynamic_offset) + .and_then(as_usize) + .with_context(|| format!("Error decoding Bytes length at {dynamic_offset}"))?; + let bytes = take_bytes(data, bytes_offset, len) + .with_context(|| format!("Error decoding bytes at {bytes_offset}"))?; let result = DecodeResult { token: Token::Bytes(bytes), new_offset: offset + WORD_LEN, @@ -146,11 +160,17 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult { - let dynamic_offset = as_usize(&peek_32_bytes(data, offset)?)?; - let bytes_offset = add_checked(dynamic_offset, WORD_LEN)?; - - let len = as_usize(&peek_32_bytes(data, dynamic_offset)?)?; - let bytes = take_bytes(data, bytes_offset, len)?; + let dynamic_offset = peek_32_bytes(data, offset) + .and_then(as_usize) + .with_context(|| format!("Error decoding String dynamic offset at {offset}"))?; + let bytes_offset = add_checked(dynamic_offset, WORD_LEN) + .with_context(|| format!("Dynamic offset is too big at {offset}"))?; + + let len = peek_32_bytes(data, dynamic_offset) + .and_then(as_usize) + .with_context(|| format!("Error decoding String length at {dynamic_offset}"))?; + let bytes = take_bytes(data, bytes_offset, len) + .with_context(|| format!("Error decoding String parameter at {bytes_offset}"))?; let result = DecodeResult { // NOTE: We're decoding strings using lossy UTF-8 decoding to // prevent invalid strings written into contracts by either users or @@ -162,17 +182,23 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult { - let len_offset = as_usize(&peek_32_bytes(data, offset)?)?; - let len = as_usize(&peek_32_bytes(data, len_offset)?)?; - - let tail_offset = add_checked(len_offset, WORD_LEN)?; + let len_offset = peek_32_bytes(data, offset) + .and_then(as_usize) + .with_context(|| format!("Error decoding Array length offset at {offset}"))?; + let len = peek_32_bytes(data, len_offset) + .and_then(as_usize) + .with_context(|| format!("Error decoding Array length at {len_offset}"))?; + + let tail_offset = add_checked(len_offset, WORD_LEN) + .with_context(|| format!("Array length offset is too big: {len_offset}"))?; let tail = &data[tail_offset..]; let mut tokens = vec![]; let mut new_offset = 0; - for _ in 0..len { - let res = decode_param(kind, tail, new_offset)?; + for elem_idx in 0..len { + let res = decode_param(kind, tail, new_offset) + .with_context(|| format!("Error decoding '{elem_idx}' Array element"))?; new_offset = res.new_offset; tokens.push(res.token); } @@ -191,9 +217,14 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult data.len() { - return Err(AbiError(AbiErrorKind::Error_decoding_data)); + return AbiError::err(AbiErrorKind::Error_decoding_data) + .context("FixedArray dynamic offset is out of bounds"); } (&data[offset..], 0) } else { @@ -202,13 +233,15 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult AbiResult data.len() { - return Err(AbiError(AbiErrorKind::Error_decoding_data)); + return AbiError::err(AbiErrorKind::Error_decoding_data) + .context("Tuple dynamic offset is out of bounds"); } (&data[offset..], 0) } else { @@ -239,8 +275,9 @@ fn decode_param(param: &ParamType, data: &[u8], offset: usize) -> AbiResult AbiResult AbiResult { +fn as_usize(slice: H256) -> AbiResult { if !slice[..28].iter().all(|x| *x == 0) { - return Err(AbiError(AbiErrorKind::Error_decoding_data)); + return AbiError::err(AbiErrorKind::Error_decoding_data); } let result = ((slice[28] as usize) << 24) @@ -281,9 +318,9 @@ fn as_usize(slice: &H256) -> AbiResult { Ok(result) } -fn as_bool(slice: &H256) -> AbiResult { +fn as_bool(slice: H256) -> AbiResult { if !slice[..31].iter().all(|x| *x == 0) { - return Err(AbiError(AbiErrorKind::Error_decoding_data)); + return AbiError::err(AbiErrorKind::Error_decoding_data); } Ok(slice[31] == 1) @@ -292,7 +329,7 @@ fn as_bool(slice: &H256) -> AbiResult { fn peek(data: &[u8], offset: usize, len: usize) -> AbiResult<&[u8]> { let end = add_checked(offset, len)?; if end > data.len() { - Err(AbiError(AbiErrorKind::Error_decoding_data)) + AbiError::err(AbiErrorKind::Error_decoding_data) } else { Ok(&data[offset..end]) } @@ -309,7 +346,7 @@ fn peek_32_bytes(data: &[u8], offset: usize) -> AbiResult { fn take_bytes(data: &[u8], offset: usize, len: usize) -> AbiResult> { let end = add_checked(offset, len)?; if end > data.len() { - Err(AbiError(AbiErrorKind::Error_decoding_data)) + AbiError::err(AbiErrorKind::Error_decoding_data) } else { Ok(data[offset..end].to_vec()) } @@ -317,7 +354,7 @@ fn take_bytes(data: &[u8], offset: usize, len: usize) -> AbiResult> { fn add_checked(left: usize, right: usize) -> AbiResult { left.checked_add(right) - .ok_or(AbiError(AbiErrorKind::Error_decoding_data)) + .or_tw_err(AbiErrorKind::Error_decoding_data) } #[cfg(test)] diff --git a/rust/tw_evm/src/abi/function.rs b/rust/tw_evm/src/abi/function.rs index 93a4d3d0441..8bb098625eb 100644 --- a/rust/tw_evm/src/abi/function.rs +++ b/rust/tw_evm/src/abi/function.rs @@ -11,6 +11,7 @@ use crate::abi::token::Token; use crate::abi::{AbiError, AbiErrorKind, AbiResult}; use itertools::Itertools; use serde::Deserialize; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; #[derive(Clone, Debug, Default, Deserialize)] @@ -52,9 +53,14 @@ impl Function { // Check if the given tokens match `Self::inputs` ABI. let input_param_types: Vec<_> = self.inputs.iter().map(|param| param.kind.clone()).collect(); - for (token, kind) in tokens.iter().zip(input_param_types.iter()) { - if token.to_param_type() != *kind { - return Err(AbiError(AbiErrorKind::Error_abi_mismatch)); + for (token_idx, (token, kind)) in tokens.iter().zip(input_param_types.iter()).enumerate() { + let actual_kind = token.to_param_type(); + if actual_kind != *kind { + return AbiError::err(AbiErrorKind::Error_abi_mismatch).with_context(|| { + format!( + "Expected {kind:?} type parameter at {token_idx}, found {actual_kind:?}" + ) + }); } } diff --git a/rust/tw_evm/src/abi/mod.rs b/rust/tw_evm/src/abi/mod.rs index 0be990ac556..dd59d3c6370 100644 --- a/rust/tw_evm/src/abi/mod.rs +++ b/rust/tw_evm/src/abi/mod.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::{SigningError, SigningErrorType}; +use tw_coin_entry::error::prelude::*; pub mod contract; pub mod decode; @@ -23,25 +23,21 @@ macro_rules! abi_output_error { let err = $error; let mut output = <$output>::default(); - output.error = err.0; - output.error_message = std::borrow::Cow::from(format!("{err:?}")); + output.error = *TWError::error_type(&err); + output.error_message = std::borrow::Cow::from(err.to_string()); output }}; } -pub type AbiResult = Result; pub type AbiErrorKind = tw_proto::EthereumAbi::Proto::AbiError; +pub type AbiError = TWError; +pub type AbiResult = Result; -#[derive(Debug)] -pub struct AbiError(pub AbiErrorKind); - -impl From for SigningError { - fn from(err: AbiError) -> Self { - match err.0 { - AbiErrorKind::OK => SigningError(SigningErrorType::OK), - AbiErrorKind::Error_internal => SigningError(SigningErrorType::Error_internal), - _ => SigningError(SigningErrorType::Error_invalid_params), - } - } +pub fn abi_to_signing_error(abi: AbiError) -> SigningError { + abi.map_err(|abi_kind| match abi_kind { + AbiErrorKind::OK => SigningErrorType::OK, + AbiErrorKind::Error_internal => SigningErrorType::Error_internal, + _ => SigningErrorType::Error_invalid_params, + }) } diff --git a/rust/tw_evm/src/abi/non_empty_array.rs b/rust/tw_evm/src/abi/non_empty_array.rs index 6bf1cd2f4e9..5b1c803dca5 100644 --- a/rust/tw_evm/src/abi/non_empty_array.rs +++ b/rust/tw_evm/src/abi/non_empty_array.rs @@ -6,6 +6,7 @@ use crate::abi::{AbiError, AbiErrorKind, AbiResult}; use core::fmt; use std::num::NonZeroUsize; use std::ops::Deref; +use tw_coin_entry::error::prelude::*; pub type NonEmptyBytes = NonEmptyArray; @@ -16,7 +17,7 @@ pub struct NonZeroLen(NonZeroUsize); impl NonZeroLen { pub fn new(len: usize) -> AbiResult { NonZeroUsize::new(len) - .ok_or(AbiError(AbiErrorKind::Error_invalid_param_type)) + .or_tw_err(AbiErrorKind::Error_invalid_param_type) .map(NonZeroLen) } @@ -43,7 +44,7 @@ pub struct NonEmptyArray(Vec); impl NonEmptyArray { pub fn new(elements: Vec) -> AbiResult> { if elements.is_empty() { - return Err(AbiError(AbiErrorKind::Error_empty_type)); + return AbiError::err(AbiErrorKind::Error_empty_type); } Ok(NonEmptyArray(elements)) } diff --git a/rust/tw_evm/src/abi/param_type/constructor.rs b/rust/tw_evm/src/abi/param_type/constructor.rs index 25c25b8302b..d853cb9834c 100644 --- a/rust/tw_evm/src/abi/param_type/constructor.rs +++ b/rust/tw_evm/src/abi/param_type/constructor.rs @@ -6,6 +6,7 @@ use crate::abi::non_empty_array::NonZeroLen; use crate::abi::param_type::ParamType; use crate::abi::uint::UintBits; use crate::abi::{AbiError, AbiErrorKind, AbiResult}; +use tw_coin_entry::error::prelude::ResultContext; pub trait TypeConstructor: Sized { fn address() -> Self; @@ -99,7 +100,8 @@ impl TypeConstructor for ParamType { }) } - fn custom(_s: &str) -> AbiResult { - Err(AbiError(AbiErrorKind::Error_invalid_param_type)) + fn custom(s: &str) -> AbiResult { + AbiError::err(AbiErrorKind::Error_invalid_param_type) + .with_context(|| format!("`ParamType` doesn't support custom types like '{s}'")) } } diff --git a/rust/tw_evm/src/abi/param_type/reader.rs b/rust/tw_evm/src/abi/param_type/reader.rs index 826ebedf5c5..da7ee184428 100644 --- a/rust/tw_evm/src/abi/param_type/reader.rs +++ b/rust/tw_evm/src/abi/param_type/reader.rs @@ -7,6 +7,7 @@ use crate::abi::param_type::constructor::TypeConstructor; use crate::abi::uint::UintBits; use crate::abi::{AbiError, AbiErrorKind, AbiResult}; use std::str::FromStr; +use tw_coin_entry::error::prelude::*; pub struct Reader; @@ -16,11 +17,15 @@ impl Reader { // Array if let Some(remaining) = s.strip_suffix(']') { let Some((element_type_str, len_str)) = remaining.rsplit_once('[') else { - return Err(AbiError(AbiErrorKind::Error_invalid_param_type)); + return AbiError::err(AbiErrorKind::Error_invalid_param_type) + .with_context(|| format!("Invalid array type: {s}")); }; - let element_type = Reader::parse_type::(element_type_str)?; - if let Some(len) = parse_len(len_str)? { + let element_type = Reader::parse_type::(element_type_str) + .with_context(|| format!("Error parsing inner array type: {element_type_str}"))?; + if let Some(len) = parse_len(len_str) + .with_context(|| format!("Error parsing fixed_array length: {len_str}"))? + { return Ok(T::fixed_array_checked(len, element_type)); } return Ok(T::array(element_type)); @@ -28,28 +33,35 @@ impl Reader { let all_alphanumeric = s.chars().all(|ch| ch.is_ascii_alphanumeric()); if s.is_empty() || !all_alphanumeric { - return Err(AbiError(AbiErrorKind::Error_invalid_param_type)); + return AbiError::err(AbiErrorKind::Error_invalid_param_type) + .with_context(|| format!("Expected an alpha-numeric string type: {s}")); } if s.contains(['[', ']']) { - return Err(AbiError(AbiErrorKind::Error_invalid_param_type)); + return AbiError::err(AbiErrorKind::Error_invalid_param_type); } // uint, uint32, ... if let Some(len_str) = s.strip_prefix("uint") { - let bits = parse_uint_bits(len_str)?.unwrap_or_default(); + let bits = parse_uint_bits(len_str) + .with_context(|| format!("Error parsing uint bits: {len_str}"))? + .unwrap_or_default(); return Ok(T::uint_checked(bits)); } // int, int32, ... if let Some(len_str) = s.strip_prefix("int") { - let bits = parse_uint_bits(len_str)?.unwrap_or_default(); + let bits = parse_uint_bits(len_str) + .with_context(|| format!("Error parsing int bits: {len_str}"))? + .unwrap_or_default(); return Ok(T::int_checked(bits)); } // bytes, bytes32, ... if let Some(len_str) = s.strip_prefix("bytes") { - if let Some(len) = parse_len(len_str)? { + if let Some(len) = parse_len(len_str) + .with_context(|| format!("Error parsing fixed_bytes length: {len_str}"))? + { // Fixed-len bytes. return Ok(T::fixed_bytes_checked(len)); } @@ -93,9 +105,11 @@ fn parse_usize(usize_str: &str) -> AbiResult> { return Ok(None); } if usize_str.starts_with('0') { - return Err(AbiError(AbiErrorKind::Error_invalid_param_type)); + return AbiError::err(AbiErrorKind::Error_invalid_param_type) + .context("Number cannot start with 0"); } usize::from_str(usize_str) .map(Some) - .map_err(|_| AbiError(AbiErrorKind::Error_invalid_param_type)) + .tw_err(|_| AbiErrorKind::Error_invalid_param_type) + .with_context(|| format!("Expected a decimal string: {usize_str}")) } diff --git a/rust/tw_evm/src/abi/uint.rs b/rust/tw_evm/src/abi/uint.rs index c2b090c274b..882b536b0a7 100644 --- a/rust/tw_evm/src/abi/uint.rs +++ b/rust/tw_evm/src/abi/uint.rs @@ -4,6 +4,7 @@ use crate::abi::{AbiError, AbiErrorKind, AbiResult}; use std::fmt; +use tw_coin_entry::error::prelude::ResultContext; use tw_number::U256; #[derive(Clone, Copy, PartialEq)] @@ -47,7 +48,8 @@ impl From for usize { // https://docs.soliditylang.org/en/latest/abi-spec.html#types pub fn check_uint_bits(bits: usize) -> AbiResult<()> { if bits % 8 != 0 || bits == 0 || bits > 256 { - return Err(AbiError(AbiErrorKind::Error_invalid_uint_value)); + return AbiError::err(AbiErrorKind::Error_invalid_uint_value) + .with_context(|| format!("Unexpected Uint bits: {bits}")); } Ok(()) } diff --git a/rust/tw_evm/src/address.rs b/rust/tw_evm/src/address.rs index 6da55393fde..8cff9fb514d 100644 --- a/rust/tw_evm/src/address.rs +++ b/rust/tw_evm/src/address.rs @@ -8,7 +8,7 @@ use std::fmt::{Display, Formatter}; use std::ops::{RangeFrom, RangeInclusive}; use std::str::FromStr; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex; use tw_hash::{sha3::keccak256, H160, H256}; use tw_keypair::ecdsa::secp256k1; diff --git a/rust/tw_evm/src/message/eip712/eip712_message.rs b/rust/tw_evm/src/message/eip712/eip712_message.rs index ccb3f1bea91..0d26ad22b1b 100644 --- a/rust/tw_evm/src/message/eip712/eip712_message.rs +++ b/rust/tw_evm/src/message/eip712/eip712_message.rs @@ -8,11 +8,14 @@ use crate::abi::token::Token; use crate::address::Address; use crate::message::eip712::message_types::CustomTypes; use crate::message::eip712::property::PropertyType; -use crate::message::{EthMessage, MessageSigningError, MessageSigningResult}; +use crate::message::{ + EthMessage, MessageSigningError, MessageSigningErrorKind, MessageSigningResult, +}; use itertools::Itertools; use serde::{Deserialize, Serialize}; use serde_json::Value as Json; use std::str::FromStr; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex::{self, DecodeHex}; use tw_hash::sha3::keccak256; use tw_hash::{H160, H256}; @@ -38,11 +41,13 @@ impl Eip712Message { /// Tries to construct an EIP712 message from the given string. pub fn new>(message_to_sign: S) -> MessageSigningResult { let eip712_msg: Eip712Message = serde_json::from_str(message_to_sign.as_ref()) - .map_err(|_| MessageSigningError::TypeValueMismatch)?; + .tw_err(|_| MessageSigningErrorKind::TypeValueMismatch) + .context("Error deserializing EIP712 message as JSON")?; // Check if the given message is actually EIP712. if !eip712_msg.types.contains_key(EIP712_DOMAIN) { - return Err(MessageSigningError::TypeValueMismatch); + return MessageSigningError::err(MessageSigningErrorKind::TypeValueMismatch) + .context("EIP712 message does not contain domain info"); } Ok(eip712_msg) } @@ -56,9 +61,12 @@ impl Eip712Message { // Check if `domain.chainId` is expected. let chain_id_value = msg.domain["chainId"].clone(); let chain_id = U256::from_u64_or_decimal_str(chain_id_value) - .map_err(|_| MessageSigningError::TypeValueMismatch)?; + .tw_err(|_| MessageSigningErrorKind::TypeValueMismatch) + .context("Invalid chainId")?; if chain_id != expected_chain_id { - return Err(MessageSigningError::InvalidChainId); + return MessageSigningError::err(MessageSigningErrorKind::InvalidChainId).with_context( + || format!("Expected '{expected_chain_id}' chainId, found '{chain_id}'"), + ); } Ok(msg) @@ -71,12 +79,15 @@ impl EthMessage for Eip712Message { &self.types, PropertyType::Custom(EIP712_DOMAIN.to_string()), &self.domain, - )?; + ) + .context("Error encoding EIP712Domain")?; + let primary_data_hash = encode_data( &self.types, PropertyType::Custom(self.primary_type.clone()), &self.message, - )?; + ) + .context("Error encoding primary type")?; let concat = [ PREFIX.as_slice(), @@ -96,17 +107,22 @@ fn encode_data( data: &Json, ) -> MessageSigningResult> { match data_type { - PropertyType::Bool => encode_bool(data), - PropertyType::String => encode_string(data), - PropertyType::Int => encode_i256(data), - PropertyType::Uint => encode_u256(data), - PropertyType::Address => encode_address(data), - PropertyType::FixBytes { len } => encode_fix_bytes(data, len.get()), - PropertyType::Bytes => encode_bytes(data), - PropertyType::Custom(custom) => encode_custom(custom_types, &custom, data), - PropertyType::Array(element_type) => encode_array(custom_types, *element_type, data, None), + PropertyType::Bool => encode_bool(data).context("Error encoding 'bool' parameter"), + PropertyType::String => encode_string(data).context("Error encoding 'string' parameter"), + PropertyType::Int => encode_i256(data).context("Error encoding 'i256' parameter"), + PropertyType::Uint => encode_u256(data).context("Error encoding 'u256' parameter"), + PropertyType::Address => encode_address(data).context("Error encoding 'address' parameter"), + PropertyType::FixBytes { len } => { + encode_fix_bytes(data, len.get()).context("Error encoding 'bytes[N]' parameter") + }, + PropertyType::Bytes => encode_bytes(data).context("Error encoding 'bytes' parameter"), + PropertyType::Custom(custom) => encode_custom(custom_types, &custom, data) + .with_context(|| format!("Error encoding '{custom}' custom parameter")), + PropertyType::Array(element_type) => encode_array(custom_types, *element_type, data, None) + .context("Error encoding 'array' parameter"), PropertyType::FixArray { len, element_type } => { encode_array(custom_types, *element_type, data, Some(len.get())) + .context("Error encoding 'array[N]' parameter") }, } } @@ -114,14 +130,14 @@ fn encode_data( fn encode_bool(value: &Json) -> MessageSigningResult { let bin = value .as_bool() - .ok_or(MessageSigningError::InvalidParameterValue)?; + .or_tw_err(MessageSigningErrorKind::InvalidParameterValue)?; Ok(encode_tokens(&[Token::Bool(bin)])) } fn encode_string(value: &Json) -> MessageSigningResult { let string = value .as_str() - .ok_or(MessageSigningError::InvalidParameterValue)?; + .or_tw_err(MessageSigningErrorKind::InvalidParameterValue)?; let hash = keccak256(string.as_bytes()); let checked_bytes = NonEmptyBytes::new(hash).expect("`hash` must not be empty"); Ok(encode_tokens(&[Token::FixedBytes(checked_bytes)])) @@ -129,23 +145,24 @@ fn encode_string(value: &Json) -> MessageSigningResult { fn encode_u256(value: &Json) -> MessageSigningResult { let uint = U256::from_u64_or_decimal_str(value.clone()) - .map_err(|_| MessageSigningError::InvalidParameterValue)?; + .tw_err(|_| MessageSigningErrorKind::InvalidParameterValue)?; Ok(encode_tokens(&[Token::u256(uint)])) } fn encode_i256(value: &Json) -> MessageSigningResult { let int = I256::from_i64_or_decimal_str(value.clone()) - .map_err(|_| MessageSigningError::InvalidParameterValue)?; + .tw_err(|_| MessageSigningErrorKind::InvalidParameterValue)?; Ok(encode_tokens(&[Token::i256(int)])) } fn encode_address(value: &Json) -> MessageSigningResult { let addr_str = value .as_str() - .ok_or(MessageSigningError::InvalidParameterValue)?; + .or_tw_err(MessageSigningErrorKind::InvalidParameterValue)?; // H160 doesn't require the string to be `0x` prefixed. let addr_data = - H160::from_str(addr_str).map_err(|_| MessageSigningError::InvalidParameterValue)?; + H160::from_str(addr_str).tw_err(|_| MessageSigningErrorKind::InvalidParameterValue)?; + let addr = Address::from_bytes(addr_data); Ok(encode_tokens(&[Token::Address(addr)])) } @@ -153,24 +170,29 @@ fn encode_address(value: &Json) -> MessageSigningResult { fn encode_fix_bytes(value: &Json, expected_len: usize) -> MessageSigningResult { let str = value .as_str() - .ok_or(MessageSigningError::InvalidParameterValue)?; + .or_tw_err(MessageSigningErrorKind::InvalidParameterValue)?; let fix_bytes = - hex::decode_lenient(str).map_err(|_| MessageSigningError::InvalidParameterValue)?; - if fix_bytes.len() > expected_len { - return Err(MessageSigningError::TypeValueMismatch); + hex::decode_lenient(str).tw_err(|_| MessageSigningErrorKind::InvalidParameterValue)?; + + let actual_len = fix_bytes.len(); + if actual_len > expected_len { + return MessageSigningError::err(MessageSigningErrorKind::TypeValueMismatch) + .with_context(|| format!("Expected '{expected_len}' bytes, found '{actual_len}'")); } - let checked_bytes = - NonEmptyBytes::new(fix_bytes).map_err(|_| MessageSigningError::InvalidParameterValue)?; + let checked_bytes = NonEmptyBytes::new(fix_bytes) + .tw_err(|_| MessageSigningErrorKind::InvalidParameterValue) + .context("Empty 'FixBytes' is not allowed")?; Ok(encode_tokens(&[Token::FixedBytes(checked_bytes)])) } fn encode_bytes(value: &Json) -> MessageSigningResult { let str = value .as_str() - .ok_or(MessageSigningError::InvalidParameterValue)?; + .or_tw_err(MessageSigningErrorKind::InvalidParameterValue)?; let bytes = str .decode_hex() - .map_err(|_| MessageSigningError::InvalidParameterValue)?; + .tw_err(|_| MessageSigningErrorKind::InvalidParameterValue)?; + let hash = keccak256(&bytes); let checked_bytes = NonEmptyBytes::new(hash).expect("`hash` must not be empty"); Ok(encode_tokens(&[Token::FixedBytes(checked_bytes)])) @@ -184,16 +206,20 @@ fn encode_array( ) -> MessageSigningResult { let elements = data .as_array() - .ok_or(MessageSigningError::InvalidParameterValue)?; + .or_tw_err(MessageSigningErrorKind::InvalidParameterValue)?; // Check if the type definition actually matches the length of items to be encoded. - if expected_len.is_some() && Some(elements.len()) != expected_len { - return Err(MessageSigningError::TypeValueMismatch); + let actual_elements = elements.len(); + if expected_len.is_some() && Some(actual_elements) != expected_len { + return MessageSigningError::err(MessageSigningErrorKind::TypeValueMismatch).with_context( + || format!("Expected '{expected_len:?}' array elements, found '{actual_elements}'"), + ); } let mut encoded_items = vec![]; - for item in elements { - let mut encoded = encode_data(custom_types, element_type.clone(), item)?; + for (item_idx, item) in elements.iter().enumerate() { + let mut encoded = encode_data(custom_types, element_type.clone(), item) + .with_context(|| format!("Error encoding '{item_idx}' array element"))?; encoded_items.append(&mut encoded); } @@ -207,16 +233,18 @@ fn encode_custom( ) -> MessageSigningResult { let data_properties = custom_types .get(data_ident) - .ok_or(MessageSigningError::TypeValueMismatch)?; + .or_tw_err(MessageSigningErrorKind::TypeValueMismatch) + .with_context(|| format!("'{data_ident}' custom type is not specified"))?; let type_hash = encode_custom_type::type_hash(data_ident, custom_types)?; let checked_bytes = - NonEmptyBytes::new(type_hash).map_err(|_| MessageSigningError::InvalidParameterValue)?; + NonEmptyBytes::new(type_hash).tw_err(|_| MessageSigningErrorKind::InvalidParameterValue)?; let mut encoded_tokens = encode_tokens(&[Token::FixedBytes(checked_bytes)]); - for field in data_properties.iter() { + for (field_idx, field) in data_properties.iter().enumerate() { let field_value = &data[&field.name]; - let field_property = PropertyType::from_str(&field.property_type)?; + let field_property = PropertyType::from_str(&field.property_type) + .with_context(|| format!("Error encoding '{field_idx}' field"))?; let mut encoded = encode_data(custom_types, field_property, field_value)?; encoded_tokens.append(&mut encoded); } @@ -242,7 +270,10 @@ mod encode_custom_type { ) -> MessageSigningResult { let deps = { let mut temp = build_dependencies(data_type, custom_types) - .ok_or(MessageSigningError::TypeValueMismatch)?; + .or_tw_err(MessageSigningErrorKind::TypeValueMismatch) + .with_context(|| { + format!("Error building '{data_type}' custom type dependencies") + })?; temp.remove(data_type); let mut temp = temp.into_iter().collect::>(); temp.sort_unstable(); diff --git a/rust/tw_evm/src/message/eip712/property.rs b/rust/tw_evm/src/message/eip712/property.rs index 3a85ed1c24c..fa9dfae8dae 100644 --- a/rust/tw_evm/src/message/eip712/property.rs +++ b/rust/tw_evm/src/message/eip712/property.rs @@ -7,10 +7,11 @@ use crate::abi::param_type::constructor::TypeConstructor; use crate::abi::param_type::reader::Reader; use crate::abi::uint::UintBits; use crate::abi::{AbiError, AbiErrorKind, AbiResult}; -use crate::message::MessageSigningError; +use crate::message::{MessageSigningError, MessageSigningErrorKind}; use serde::{Deserialize, Serialize}; use std::fmt; use std::str::FromStr; +use tw_coin_entry::error::prelude::*; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Property { @@ -79,7 +80,8 @@ impl TypeConstructor for PropertyType { } fn empty_tuple() -> AbiResult { - Err(AbiError(AbiErrorKind::Error_invalid_param_type)) + AbiError::err(AbiErrorKind::Error_invalid_param_type) + .context("`PropertyType` doesn't support tuples") } fn custom(s: &str) -> AbiResult { @@ -112,7 +114,7 @@ impl FromStr for PropertyType { type Err = MessageSigningError; fn from_str(s: &str) -> Result { - Reader::parse_type(s).map_err(|_| MessageSigningError::InvalidParameterType) + Reader::parse_type(s).tw_err(|_| MessageSigningErrorKind::InvalidParameterType) } } diff --git a/rust/tw_evm/src/message/mod.rs b/rust/tw_evm/src/message/mod.rs index f92a958e4a9..44b27d428ab 100644 --- a/rust/tw_evm/src/message/mod.rs +++ b/rust/tw_evm/src/message/mod.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::{SigningError, SigningErrorType}; +use tw_coin_entry::error::prelude::*; use tw_hash::H256; pub mod eip191; @@ -11,9 +11,10 @@ pub mod signature; pub type EthMessageBoxed = Box; pub type MessageSigningResult = Result; +pub type MessageSigningError = TWError; #[derive(Debug)] -pub enum MessageSigningError { +pub enum MessageSigningErrorKind { InvalidParameterType, InvalidParameterValue, TypeValueMismatch, @@ -21,17 +22,15 @@ pub enum MessageSigningError { Internal, } -impl From for SigningError { - fn from(err: MessageSigningError) -> Self { - match err { - MessageSigningError::InvalidParameterType - | MessageSigningError::InvalidParameterValue - | MessageSigningError::TypeValueMismatch - | MessageSigningError::InvalidChainId => { - SigningError(SigningErrorType::Error_invalid_params) - }, - MessageSigningError::Internal => SigningError(SigningErrorType::Error_internal), - } +pub fn to_signing(msg_err: MessageSigningError) -> SigningError { + match msg_err.error_type() { + MessageSigningErrorKind::InvalidParameterType + | MessageSigningErrorKind::InvalidParameterValue + | MessageSigningErrorKind::TypeValueMismatch + | MessageSigningErrorKind::InvalidChainId => { + SigningError::new(SigningErrorType::Error_invalid_params) + }, + MessageSigningErrorKind::Internal => SigningError::new(SigningErrorType::Error_internal), } } diff --git a/rust/tw_evm/src/modules/abi_encoder.rs b/rust/tw_evm/src/modules/abi_encoder.rs index de70367d618..4849bf6a735 100644 --- a/rust/tw_evm/src/modules/abi_encoder.rs +++ b/rust/tw_evm/src/modules/abi_encoder.rs @@ -26,6 +26,7 @@ use tw_proto::EthereumAbi::Proto; use crate::abi::non_empty_array::{NonEmptyArray, NonEmptyBytes, NonZeroLen}; use crate::abi::uint::UintBits; +use tw_coin_entry::error::prelude::*; use Proto::mod_ParamType::OneOfparam as ProtoParamType; use Proto::mod_ParamsDecodingInput::OneOfabi as AbiEnum; use Proto::mod_Token::OneOftoken as TokenEnum; @@ -76,7 +77,8 @@ impl AbiEncoder { input: Proto::ContractCallDecodingInput, ) -> AbiResult> { if input.encoded.len() < H32::len() { - return Err(AbiError(AbiErrorKind::Error_decoding_data)); + return AbiError::err(AbiErrorKind::Error_decoding_data) + .context("Encoded Contract Call bytes too short"); } let short_signature = &input.encoded[0..H32::len()]; let short_signature = @@ -85,12 +87,18 @@ impl AbiEncoder { let mut abi_json: SmartContractCallAbiJson = serde_json::from_str(&input.smart_contract_abi_json) - .map_err(|_| AbiError(AbiErrorKind::Error_invalid_abi))?; + .tw_err(|_| AbiErrorKind::Error_invalid_abi) + .context("Error deserializing Smart Contract ABI as JSON")?; let function = abi_json .map .get_mut(&ContractCallSignature(short_signature)) - .ok_or(AbiError(AbiErrorKind::Error_abi_mismatch))?; + .or_tw_err(AbiErrorKind::Error_abi_mismatch) + .with_context(|| { + format!( + "Contract Call ABI does not have a function with {short_signature} signature" + ) + })?; let decoded_tokens = function.decode_input(encoded_data)?; @@ -105,7 +113,8 @@ impl AbiEncoder { inputs: &decoded_tokens, }; let decoded_json = serde_json::to_string(&decoded_res) - .map_err(|_| AbiError(AbiErrorKind::Error_internal))?; + .tw_err(|_| AbiErrorKind::Error_internal) + .context("Error serializing Smart Contract Input as JSON")?; // Serialize the Proto parameters. let decoded_protos = decoded_tokens @@ -125,13 +134,16 @@ impl AbiEncoder { ) -> AbiResult> { let abi = match input.abi { AbiEnum::abi_json(abi_json) => serde_json::from_str(&abi_json) - .map_err(|_| AbiError(AbiErrorKind::Error_invalid_abi))?, + .tw_err(|_| AbiErrorKind::Error_invalid_abi) + .context("Error deserializing ABI as JSON")?, AbiEnum::abi_params(abi_params) => abi_params .params .into_iter() .map(Self::param_from_proto) .collect::>>()?, - AbiEnum::None => return Err(AbiError(AbiErrorKind::Error_invalid_abi)), + AbiEnum::None => { + return AbiError::err(AbiErrorKind::Error_invalid_abi).context("No ABI specified") + }, }; let decoded_tokens = decode_params(&abi, &input.encoded)?; @@ -150,7 +162,9 @@ impl AbiEncoder { fn decode_value_impl( input: Proto::ValueDecodingInput<'_>, ) -> AbiResult> { - let param_type = DecodingValueType::from_str(&input.param_type)?.0; + let param_type = DecodingValueType::from_str(&input.param_type) + .context("Invalid parameter type")? + .0; let token = decode_value(¶m_type, &input.encoded)?; let token_str = token.to_string(); Ok(Proto::ValueDecodingOutput { @@ -218,7 +232,8 @@ impl AbiEncoder { let proto_param_type = param .param - .ok_or(AbiError(AbiErrorKind::Error_missing_param_type))?; + .or_tw_err(AbiErrorKind::Error_missing_param_type) + .context("Missing parameter type")?; let kind = Self::param_type_from_proto(proto_param_type)?; Ok(Param { @@ -250,12 +265,13 @@ impl AbiEncoder { TokenEnum::string_value(str) => Ok(Token::String(str.to_string())), TokenEnum::address(addr) => { let addr = Address::from_str(&addr) - .map_err(|_| AbiError(AbiErrorKind::Error_invalid_address_value))?; + .tw_err(|_| AbiErrorKind::Error_invalid_address_value)?; Ok(Token::Address(addr)) }, TokenEnum::byte_array(bytes) => Ok(Token::Bytes(bytes.to_vec())), TokenEnum::byte_array_fix(bytes) => { - let checked_bytes = NonEmptyBytes::new(bytes.to_vec())?; + let checked_bytes = NonEmptyBytes::new(bytes.to_vec()) + .context("Empty `FixedBytes` collection is not allowed")?; Ok(Token::FixedBytes(checked_bytes)) }, TokenEnum::array(arr) => { @@ -264,7 +280,8 @@ impl AbiEncoder { }, TokenEnum::fixed_array(arr) => { let (arr, kind) = Self::array_from_proto(arr)?; - let arr = NonEmptyArray::new(arr)?; + let arr = NonEmptyArray::new(arr) + .context("Empty `FixedArray` collection is not allowed")?; Ok(Token::FixedArray { arr, kind }) }, TokenEnum::tuple(Proto::TupleParam { params }) => { @@ -274,14 +291,14 @@ impl AbiEncoder { .collect::>>()?; Ok(Token::Tuple { params }) }, - TokenEnum::None => Err(AbiError(AbiErrorKind::Error_missing_param_value)), + TokenEnum::None => AbiError::err(AbiErrorKind::Error_missing_param_value), } } fn array_from_proto(array: Proto::ArrayParam<'_>) -> AbiResult<(Vec, ParamType)> { let element_type = array .element_type - .ok_or(AbiError(AbiErrorKind::Error_missing_param_type))?; + .or_tw_err(AbiErrorKind::Error_missing_param_type)?; let element_type = Self::param_type_from_proto(element_type)?; let mut array_tokens = Vec::with_capacity(array.elements.len()); @@ -291,7 +308,9 @@ impl AbiEncoder { // Check if all tokens are the same as declared in `ArrayParam::element_type`. if token_type != element_type { - return Err(AbiError(AbiErrorKind::Error_invalid_param_type)); + return AbiError::err(AbiErrorKind::Error_invalid_param_type).with_context(|| { + format!("Expected '{element_type:?}' array element type, found {token_type:?}") + }); } array_tokens.push(token); } @@ -347,13 +366,14 @@ impl AbiEncoder { ProtoParamType::address(_) => Ok(ParamType::Address), ProtoParamType::byte_array(_) => Ok(ParamType::Bytes), ProtoParamType::byte_array_fix(bytes) => { - let len = NonZeroLen::new(bytes.size as usize)?; + let len = NonZeroLen::new(bytes.size as usize) + .context("Expected non-zero 'FixByteArray' length")?; Ok(ParamType::FixedBytes { len }) }, ProtoParamType::array(arr) => { let element_type = arr .element_type - .ok_or(AbiError(AbiErrorKind::Error_missing_param_type))?; + .or_tw_err(AbiErrorKind::Error_missing_param_type)?; let kind = Self::param_type_from_proto(*element_type)?; Ok(ParamType::Array { kind: Box::new(kind), @@ -362,7 +382,7 @@ impl AbiEncoder { ProtoParamType::fixed_array(arr) => { let element_type = arr .element_type - .ok_or(AbiError(AbiErrorKind::Error_missing_param_type))?; + .or_tw_err(AbiErrorKind::Error_missing_param_type)?; let kind = Box::new(Self::param_type_from_proto(*element_type)?); let len = NonZeroLen::new(arr.size as usize)?; Ok(ParamType::FixedArray { kind, len }) @@ -374,11 +394,12 @@ impl AbiEncoder { .map(Self::param_from_proto) .collect::>>()?; if params.is_empty() { - return Err(AbiError(AbiErrorKind::Error_invalid_abi)); + return AbiError::err(AbiErrorKind::Error_invalid_abi) + .context("Empty 'Tuple' collection is not allowed"); } Ok(ParamType::Tuple { params }) }, - ProtoParamType::None => Err(AbiError(AbiErrorKind::Error_missing_param_type)), + ProtoParamType::None => AbiError::err(AbiErrorKind::Error_missing_param_type), } } @@ -413,13 +434,11 @@ impl AbiEncoder { } fn s_number_n_from_proto(encoded: &[u8]) -> AbiResult { - I256::from_big_endian_slice(encoded) - .map_err(|_| AbiError(AbiErrorKind::Error_invalid_uint_value)) + I256::from_big_endian_slice(encoded).tw_err(|_| AbiErrorKind::Error_invalid_uint_value) } fn u_number_n_from_proto(encoded: &[u8]) -> AbiResult { - U256::from_big_endian_slice(encoded) - .map_err(|_| AbiError(AbiErrorKind::Error_invalid_uint_value)) + U256::from_big_endian_slice(encoded).tw_err(|_| AbiErrorKind::Error_invalid_uint_value) } fn s_number_n_proto(i: I256, bits: UintBits) -> Proto::NumberNParam<'static> { @@ -474,7 +493,7 @@ impl FromStr for DecodingValueType { fn from_str(s: &str) -> Result { let param_type = ParamType::try_from_type_short(s)?; if param_type.has_tuple_components() { - return Err(AbiError(AbiErrorKind::Error_invalid_param_type)); + return AbiError::err(AbiErrorKind::Error_invalid_param_type); } Ok(DecodingValueType(param_type)) } diff --git a/rust/tw_evm/src/modules/compiler.rs b/rust/tw_evm/src/modules/compiler.rs index cd961f6c66c..6dcac772299 100644 --- a/rust/tw_evm/src/modules/compiler.rs +++ b/rust/tw_evm/src/modules/compiler.rs @@ -8,7 +8,7 @@ use std::borrow::Cow; use std::marker::PhantomData; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; use tw_coin_entry::common::compile_input::SingleSignaturePubkey; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_keypair::ecdsa::secp256k1; use tw_number::U256; @@ -41,7 +41,9 @@ impl Compiler { fn preimage_hashes_impl( input: Proto::SigningInput<'_>, ) -> SigningResult> { - let chain_id = U256::from_big_endian_slice(&input.chain_id)?; + let chain_id = U256::from_big_endian_slice(&input.chain_id) + .into_tw() + .context("Invalid chain ID")?; let unsigned = TxBuilder::::tx_from_proto(&input)?; let prehash = unsigned.pre_hash(chain_id); @@ -65,7 +67,9 @@ impl Compiler { } = SingleSignaturePubkey::from_sign_list(signatures)?; let signature = secp256k1::Signature::from_bytes(&signature)?; - let chain_id = U256::from_big_endian_slice(&input.chain_id)?; + let chain_id = U256::from_big_endian_slice(&input.chain_id) + .into_tw() + .context("Invalid chain ID")?; let unsigned = TxBuilder::::tx_from_proto(&input)?; diff --git a/rust/tw_evm/src/modules/json_signer.rs b/rust/tw_evm/src/modules/json_signer.rs deleted file mode 100644 index d5a60a288bc..00000000000 --- a/rust/tw_evm/src/modules/json_signer.rs +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -use crate::evm_context::EvmContext; -use std::marker::PhantomData; -use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; -use tw_coin_entry::modules::json_signer::JsonSigner; -use tw_keypair::tw::PrivateKey; - -#[derive(Default)] -pub struct EthJsonSigner { - _phantom: PhantomData, -} - -impl JsonSigner for EthJsonSigner { - #[inline] - fn sign_json( - &self, - _coin: &dyn CoinContext, - _input_json: &str, - _key: &PrivateKey, - ) -> SigningResult { - // TODO implement when `quick_protobuf` is replaced with `rust-protobuf`. - Err(SigningError(SigningErrorType::Error_internal)) - } -} diff --git a/rust/tw_evm/src/modules/message_signer.rs b/rust/tw_evm/src/modules/message_signer.rs index 3b8b6c66e35..a67a60fb5ac 100644 --- a/rust/tw_evm/src/modules/message_signer.rs +++ b/rust/tw_evm/src/modules/message_signer.rs @@ -5,11 +5,11 @@ use crate::message::eip191::Eip191Message; use crate::message::eip712::eip712_message::Eip712Message; use crate::message::signature::{MessageSignature, SignatureType}; -use crate::message::{EthMessage, EthMessageBoxed}; +use crate::message::{to_signing, EthMessage, EthMessageBoxed}; use std::borrow::Cow; use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::message_signer::MessageSigner; use tw_coin_entry::signing_output_error; use tw_encoding::hex::ToHex; @@ -61,7 +61,7 @@ impl EthMessageSigner { input: Proto::MessageSigningInput<'_>, ) -> SigningResult> { let msg = Self::message_from_proto(input)?; - let hash = msg.hash()?.to_vec(); + let hash = msg.hash().map_err(to_signing)?.to_vec(); Ok(CompilerProto::PreSigningOutput { data: Cow::Owned(hash.clone()), data_hash: Cow::Owned(hash), @@ -78,7 +78,7 @@ impl EthMessageSigner { let msg = Self::message_from_proto(input)?; - let hash_to_sign = msg.hash()?; + let hash_to_sign = msg.hash().map_err(to_signing)?; let secp_sign = private_key.sign(hash_to_sign)?; let prepared_sign = MessageSignature::prepared(secp_sign, signature_type)?; @@ -91,7 +91,9 @@ impl EthMessageSigner { fn verify_message_impl(input: Proto::MessageVerifyingInput<'_>) -> SigningResult { let public_key = secp256k1::PublicKey::try_from(input.public_key.as_ref())?; - let msg_hash = Self::message_from_str(&input.message)?.hash()?; + let msg_hash = Self::message_from_str(&input.message)? + .hash() + .map_err(to_signing)?; let secp_signature = MessageSignature::from_str(&input.signature)?.to_secp256k1_signature()?; @@ -113,9 +115,13 @@ impl EthMessageSigner { | Proto::MessageType::MessageType_typed_eip155 => match input.chain_id { Some(expected_chain_id) => { let expected_chain_id = U256::from(expected_chain_id.chain_id); - Ok(Eip712Message::new_checked(input.message, expected_chain_id)?.into_boxed()) + Ok(Eip712Message::new_checked(input.message, expected_chain_id) + .map_err(to_signing)? + .into_boxed()) }, - None => Ok(Eip712Message::new(input.message)?.into_boxed()), + None => Ok(Eip712Message::new(input.message) + .map_err(to_signing)? + .into_boxed()), }, } } diff --git a/rust/tw_evm/src/modules/mod.rs b/rust/tw_evm/src/modules/mod.rs index 6c23a104493..526e4bf78a1 100644 --- a/rust/tw_evm/src/modules/mod.rs +++ b/rust/tw_evm/src/modules/mod.rs @@ -4,7 +4,6 @@ pub mod abi_encoder; pub mod compiler; -pub mod json_signer; pub mod message_signer; pub mod rlp_encoder; pub mod signer; diff --git a/rust/tw_evm/src/modules/rlp_encoder.rs b/rust/tw_evm/src/modules/rlp_encoder.rs index 2530585e044..8f22880d811 100644 --- a/rust/tw_evm/src/modules/rlp_encoder.rs +++ b/rust/tw_evm/src/modules/rlp_encoder.rs @@ -9,7 +9,7 @@ use crate::rlp::RlpEncode; use std::borrow::Cow; use std::marker::PhantomData; use std::str::FromStr; -use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_memory::Data; use tw_number::U256; @@ -41,7 +41,8 @@ impl RlpEncoder { input: Proto::EncodingInput<'_>, ) -> SigningResult> { let Some(rlp_item) = input.item else { - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("No RLP item provided"); }; let initial_depth = 0; @@ -56,14 +57,18 @@ impl RlpEncoder { use Proto::mod_RlpItem::OneOfitem as Item; if depth >= RECURSION_LIMIT { - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params).with_context(|| { + format!("Allowed complex types with the {RECURSION_LIMIT} maximum depth") + }); } let encoded_item = match rlp_item.item { Item::string_item(str) => RlpEncoder::::encode(str.as_ref()), Item::number_u64(num) => RlpEncoder::::encode(U256::from(num)), Item::number_u256(num_be) => { - let num = U256::from_big_endian_slice(num_be.as_ref())?; + let num = U256::from_big_endian_slice(num_be.as_ref()) + .into_tw() + .context("Invalid U256 number")?; RlpEncoder::::encode(num) }, Item::address(addr_s) => { @@ -83,7 +88,10 @@ impl RlpEncoder { }, // Pass the `raw_encoded` item as it is. Item::raw_encoded(encoded) => encoded.to_vec(), - Item::None => return Err(SigningError(SigningErrorType::Error_invalid_params)), + Item::None => { + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("No RLP item specified") + }, }; Ok(encoded_item) } diff --git a/rust/tw_evm/src/modules/signer.rs b/rust/tw_evm/src/modules/signer.rs index 8ca9b5e2b86..e136d2df1ec 100644 --- a/rust/tw_evm/src/modules/signer.rs +++ b/rust/tw_evm/src/modules/signer.rs @@ -6,7 +6,7 @@ use crate::evm_context::EvmContext; use crate::modules::tx_builder::TxBuilder; use std::borrow::Cow; use std::marker::PhantomData; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::signing_output_error; use tw_keypair::ecdsa::secp256k1; use tw_keypair::traits::SigningKeyTrait; @@ -29,7 +29,9 @@ impl Signer { fn sign_proto_impl( input: Proto::SigningInput<'_>, ) -> SigningResult> { - let chain_id = U256::from_big_endian_slice(&input.chain_id)?; + let chain_id = U256::from_big_endian_slice(&input.chain_id) + .into_tw() + .context("Invalid chain ID")?; let private_key = secp256k1::PrivateKey::try_from(input.private_key.as_ref())?; let unsigned = TxBuilder::::tx_from_proto(&input)?; diff --git a/rust/tw_evm/src/modules/tx_builder.rs b/rust/tw_evm/src/modules/tx_builder.rs index 5d23f9afa20..394fc6715c1 100644 --- a/rust/tw_evm/src/modules/tx_builder.rs +++ b/rust/tw_evm/src/modules/tx_builder.rs @@ -2,6 +2,7 @@ // // Copyright © 2017 Trust Wallet. +use crate::abi::abi_to_signing_error; use crate::abi::prebuild::erc1155::Erc1155; use crate::abi::prebuild::erc20::Erc20; use crate::abi::prebuild::erc4337::{Erc4337SimpleAccount, ExecuteArgs}; @@ -14,7 +15,7 @@ use crate::transaction::user_operation::UserOperation; use crate::transaction::UnsignedTransactionBox; use std::marker::PhantomData; use std::str::FromStr; -use tw_coin_entry::error::{AddressResult, SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::error::prelude::*; use tw_memory::Data; use tw_number::U256; use tw_proto::Common::Proto::SigningError as CommonError; @@ -32,63 +33,108 @@ impl TxBuilder { use Proto::TransactionMode as TxMode; let Some(ref transaction) = input.transaction else { - return Err(SigningError(CommonError::Error_invalid_params)); + return SigningError::err(CommonError::Error_invalid_params) + .context("No transaction specified"); }; let (eth_amount, payload, to) = match transaction.transaction_oneof { Tx::transfer(ref transfer) => { - let amount = U256::from_big_endian_slice(&transfer.amount)?; - let to_address = Self::parse_address(&input.to_address)?; + let amount = U256::from_big_endian_slice(&transfer.amount) + .into_tw() + .context("Invalid amount")?; + + let to_address = Self::parse_address(&input.to_address) + .context("Invalid destination address")?; + (amount, transfer.data.to_vec(), Some(to_address)) }, Tx::erc20_transfer(ref erc20_transfer) => { - let token_to_address = Self::parse_address(&erc20_transfer.to)?; - let token_amount = U256::from_big_endian_slice(&erc20_transfer.amount)?; - let contract_address = Self::parse_address(&input.to_address)?; + let token_to_address = Self::parse_address(&erc20_transfer.to) + .context("Invalid destination address")?; + + let token_amount = U256::from_big_endian_slice(&erc20_transfer.amount) + .into_tw() + .context("Invalid amount")?; + + let contract_address = + Self::parse_address(&input.to_address).context("Invalid Contract address")?; - let payload = Erc20::transfer(token_to_address, token_amount)?; + let payload = Erc20::transfer(token_to_address, token_amount) + .map_err(abi_to_signing_error)?; (U256::zero(), payload, Some(contract_address)) }, Tx::erc20_approve(ref erc20_approve) => { - let spender = Self::parse_address(&erc20_approve.spender)?; - let token_amount = U256::from_big_endian_slice(&erc20_approve.amount)?; - let contract_address = Self::parse_address(&input.to_address)?; + let spender = Self::parse_address(&erc20_approve.spender) + .context("Invalid sender address")?; - let payload = Erc20::approve(spender, token_amount)?; + let token_amount = U256::from_big_endian_slice(&erc20_approve.amount) + .into_tw() + .context("Invalid amount")?; + + let contract_address = + Self::parse_address(&input.to_address).context("Invalid Contract address")?; + + let payload = + Erc20::approve(spender, token_amount).map_err(abi_to_signing_error)?; (U256::zero(), payload, Some(contract_address)) }, Tx::erc721_transfer(ref erc721_transfer) => { - let from = Self::parse_address(&erc721_transfer.from)?; - let token_to_address = Self::parse_address(&erc721_transfer.to)?; - let token_id = U256::from_big_endian_slice(&erc721_transfer.token_id)?; - let contract_address = Self::parse_address(&input.to_address)?; + let from = + Self::parse_address(&erc721_transfer.from).context("Invalid sender address")?; + let token_to_address = Self::parse_address(&erc721_transfer.to) + .context("Invalid destination address")?; + + let token_id = U256::from_big_endian_slice(&erc721_transfer.token_id) + .into_tw() + .context("Invalid token ID")?; - let payload = Erc721::encode_transfer_from(from, token_to_address, token_id)?; + let contract_address = + Self::parse_address(&input.to_address).context("Invalid Contract address")?; + + let payload = Erc721::encode_transfer_from(from, token_to_address, token_id) + .map_err(abi_to_signing_error)?; (U256::zero(), payload, Some(contract_address)) }, Tx::erc1155_transfer(ref erc1155_transfer) => { - let from = Self::parse_address(&erc1155_transfer.from)?; - let to = Self::parse_address(&erc1155_transfer.to)?; - let token_id = U256::from_big_endian_slice(&erc1155_transfer.token_id)?; - let value = U256::from_big_endian_slice(&erc1155_transfer.value)?; + let from = Self::parse_address(&erc1155_transfer.from) + .context("Invalid sender address")?; + + let to = Self::parse_address(&erc1155_transfer.to) + .context("Invalid destination address")?; + + let token_id = U256::from_big_endian_slice(&erc1155_transfer.token_id) + .into_tw() + .context("Invalid token ID")?; + + let value = U256::from_big_endian_slice(&erc1155_transfer.value) + .into_tw() + .context("Invalid value")?; + let data = erc1155_transfer.data.to_vec(); - let contract_address = Self::parse_address(&input.to_address)?; + let contract_address = + Self::parse_address(&input.to_address).context("Invalid Contract address")?; - let payload = Erc1155::encode_safe_transfer_from(from, to, token_id, value, data)?; + let payload = Erc1155::encode_safe_transfer_from(from, to, token_id, value, data) + .map_err(abi_to_signing_error)?; (U256::zero(), payload, Some(contract_address)) }, Tx::contract_generic(ref contract_generic) => { - let amount = U256::from_big_endian_slice(&contract_generic.amount)?; + let amount = U256::from_big_endian_slice(&contract_generic.amount) + .into_tw() + .context("Invalid amount")?; + let payload = contract_generic.data.to_vec(); // `to_address` can be omitted for the generic contract call. // For example, on creating a new smart contract. - let to_address = Self::parse_address_optional(&input.to_address)?; + let to_address = Self::parse_address_optional(&input.to_address) + .context("Invalid destination address")?; (amount, payload, to_address) }, Tx::batch(ref batch) => { if input.tx_mode != TxMode::UserOp { - return Err(SigningError(SigningErrorType::Error_invalid_params)); + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("Transaction batch can be used in User Operation mode only"); } // Payload should match ERC4337 standard. @@ -97,12 +143,16 @@ impl TxBuilder { .iter() .map(Self::erc4337_execute_call_from_proto) .collect::, _>>()?; - let payload = Erc4337SimpleAccount::encode_execute_batch(calls)?; + let payload = Erc4337SimpleAccount::encode_execute_batch(calls) + .map_err(abi_to_signing_error)?; return Self::user_operation_from_proto(input, payload) .map(UserOperation::into_boxed); }, - Tx::None => return Err(SigningError(SigningErrorType::Error_invalid_params)), + Tx::None => { + return SigningError::err(SigningErrorType::Error_invalid_params) + .context("No transaction specified") + }, }; let tx = match input.tx_mode { @@ -113,13 +163,16 @@ impl TxBuilder { Self::transaction_eip1559_from_proto(input, eth_amount, payload, to)?.into_boxed() }, TxMode::UserOp => { - let to = to.ok_or(SigningError(SigningErrorType::Error_invalid_address))?; + let to = to + .or_tw_err(SigningErrorType::Error_invalid_address) + .context("No contract/destination address specified")?; // Payload should match the ERC4337 standard. let payload = Erc4337SimpleAccount::encode_execute(ExecuteArgs { to, value: eth_amount, data: payload, - })?; + }) + .map_err(abi_to_signing_error)?; Self::user_operation_from_proto(input, payload)?.into_boxed() }, @@ -131,8 +184,11 @@ impl TxBuilder { fn erc4337_execute_call_from_proto( call: &Proto::mod_Transaction::mod_Batch::BatchedCall, ) -> SigningResult { - let to = Self::parse_address(&call.address)?; - let value = U256::from_big_endian_slice(&call.amount)?; + let to = Self::parse_address(&call.address) + .context("Invalid 'BatchedCall' destination address")?; + let value = U256::from_big_endian_slice(&call.amount) + .into_tw() + .context("Invalid 'BatchedCall' amount")?; Ok(ExecuteArgs { to, value, @@ -147,9 +203,17 @@ impl TxBuilder { payload: Data, to_address: Option
, ) -> SigningResult { - let nonce = U256::from_big_endian_slice(&input.nonce)?; - let gas_price = U256::from_big_endian_slice(&input.gas_price)?; - let gas_limit = U256::from_big_endian_slice(&input.gas_limit)?; + let nonce = U256::from_big_endian_slice(&input.nonce) + .into_tw() + .context("Invalid nonce")?; + + let gas_price = U256::from_big_endian_slice(&input.gas_price) + .into_tw() + .context("Invalid gas price")?; + + let gas_limit = U256::from_big_endian_slice(&input.gas_limit) + .into_tw() + .context("Invalid gas limit")?; Ok(TransactionNonTyped { nonce, @@ -168,11 +232,22 @@ impl TxBuilder { payload: Data, to_address: Option
, ) -> SigningResult { - let nonce = U256::from_big_endian_slice(&input.nonce)?; - let gas_limit = U256::from_big_endian_slice(&input.gas_limit)?; + let nonce = U256::from_big_endian_slice(&input.nonce) + .into_tw() + .context("Invalid nonce")?; + + let gas_limit = U256::from_big_endian_slice(&input.gas_limit) + .into_tw() + .context("Invalid gas limit")?; + let max_inclusion_fee_per_gas = - U256::from_big_endian_slice(&input.max_inclusion_fee_per_gas)?; - let max_fee_per_gas = U256::from_big_endian_slice(&input.max_fee_per_gas)?; + U256::from_big_endian_slice(&input.max_inclusion_fee_per_gas) + .into_tw() + .context("Invalid max inclusion fee per gas")?; + + let max_fee_per_gas = U256::from_big_endian_slice(&input.max_fee_per_gas) + .into_tw() + .context("Invalid max fee per gas")?; Ok(TransactionEip1559 { nonce, @@ -190,19 +265,40 @@ impl TxBuilder { erc4337_payload: Data, ) -> SigningResult { let Some(ref user_op) = input.user_operation else { - return Err(SigningError(CommonError::Error_invalid_params)); + return SigningError::err(CommonError::Error_invalid_params) + .context("No user operation specified"); }; - let nonce = U256::from_big_endian_slice(&input.nonce)?; - let gas_limit = U256::from_big_endian_slice(&input.gas_limit)?; + let nonce = U256::from_big_endian_slice(&input.nonce) + .into_tw() + .context("Invalid nonce")?; + + let gas_limit = U256::from_big_endian_slice(&input.gas_limit) + .into_tw() + .context("Invalid gas limit")?; + let max_inclusion_fee_per_gas = - U256::from_big_endian_slice(&input.max_inclusion_fee_per_gas)?; - let max_fee_per_gas = U256::from_big_endian_slice(&input.max_fee_per_gas)?; + U256::from_big_endian_slice(&input.max_inclusion_fee_per_gas) + .into_tw() + .context("Invalid max inclusion fee per gas")?; + + let max_fee_per_gas = U256::from_big_endian_slice(&input.max_fee_per_gas) + .into_tw() + .context("Invalid max fee per gas")?; + + let entry_point = + Self::parse_address(user_op.entry_point.as_ref()).context("Invalid entry point")?; + + let sender = Self::parse_address(user_op.sender.as_ref()) + .context("Invalid User Operation sender")?; + + let verification_gas_limit = U256::from_big_endian_slice(&user_op.verification_gas_limit) + .into_tw() + .context("Invalid verification gas limit")?; - let entry_point = Self::parse_address(user_op.entry_point.as_ref())?; - let sender = Self::parse_address(user_op.sender.as_ref())?; - let verification_gas_limit = U256::from_big_endian_slice(&user_op.verification_gas_limit)?; - let pre_verification_gas = U256::from_big_endian_slice(&user_op.pre_verification_gas)?; + let pre_verification_gas = U256::from_big_endian_slice(&user_op.pre_verification_gas) + .into_tw() + .context("Invalid pre-verification gas")?; Ok(UserOperation { nonce, @@ -220,16 +316,18 @@ impl TxBuilder { } #[inline] - fn parse_address(addr: &str) -> AddressResult
{ - Context::Address::from_str(addr).map(Context::Address::into) + fn parse_address(addr: &str) -> SigningResult
{ + Context::Address::from_str(addr) + .map(Context::Address::into) + .map_err(SigningError::from) } #[inline] - fn parse_address_optional(addr: &str) -> AddressResult> { + fn parse_address_optional(addr: &str) -> SigningResult> { match Context::Address::from_str_optional(addr) { Ok(Some(addr)) => Ok(Some(addr.into())), Ok(None) => Ok(None), - Err(e) => Err(e), + Err(e) => Err(SigningError::from(e)), } } } diff --git a/rust/tw_evm/src/transaction/mod.rs b/rust/tw_evm/src/transaction/mod.rs index 04883063628..aa91320ce31 100644 --- a/rust/tw_evm/src/transaction/mod.rs +++ b/rust/tw_evm/src/transaction/mod.rs @@ -10,7 +10,7 @@ //! - User operations (EIP4337) use crate::transaction::signature::EthSignature; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_hash::{sha3::keccak256, H256}; use tw_keypair::ecdsa::secp256k1; use tw_memory::Data; diff --git a/rust/tw_evm/src/transaction/transaction_eip1559.rs b/rust/tw_evm/src/transaction/transaction_eip1559.rs index b86c28b2196..4d2c5130970 100644 --- a/rust/tw_evm/src/transaction/transaction_eip1559.rs +++ b/rust/tw_evm/src/transaction/transaction_eip1559.rs @@ -6,7 +6,7 @@ use crate::address::Address; use crate::rlp::list::RlpList; use crate::transaction::signature::{EthSignature, Signature}; use crate::transaction::{SignedTransaction, TransactionCommon, UnsignedTransaction}; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_keypair::ecdsa::secp256k1; use tw_memory::Data; use tw_number::U256; diff --git a/rust/tw_evm/src/transaction/transaction_non_typed.rs b/rust/tw_evm/src/transaction/transaction_non_typed.rs index 7e2fd1194c9..9efd792020f 100644 --- a/rust/tw_evm/src/transaction/transaction_non_typed.rs +++ b/rust/tw_evm/src/transaction/transaction_non_typed.rs @@ -6,7 +6,7 @@ use crate::address::Address; use crate::rlp::list::RlpList; use crate::transaction::signature::{EthSignature, SignatureEip155}; use crate::transaction::{SignedTransaction, TransactionCommon, UnsignedTransaction}; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_keypair::ecdsa::secp256k1; use tw_memory::Data; use tw_number::U256; diff --git a/rust/tw_evm/src/transaction/user_operation.rs b/rust/tw_evm/src/transaction/user_operation.rs index 0f3f23167ff..3f55658bb57 100644 --- a/rust/tw_evm/src/transaction/user_operation.rs +++ b/rust/tw_evm/src/transaction/user_operation.rs @@ -9,7 +9,7 @@ use crate::address::Address; use crate::transaction::signature::Signature; use crate::transaction::{SignedTransaction, TransactionCommon, UnsignedTransaction}; use serde::Serialize; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex; use tw_hash::sha3::keccak256; use tw_hash::H256; diff --git a/rust/tw_evm/tests/barz.rs b/rust/tw_evm/tests/barz.rs index cea9732f5c5..e41a12aa244 100644 --- a/rust/tw_evm/tests/barz.rs +++ b/rust/tw_evm/tests/barz.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use std::borrow::Cow; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex; use tw_evm::abi::prebuild::erc20::Erc20; use tw_evm::address::Address; diff --git a/rust/tw_evm/tests/message_signer.rs b/rust/tw_evm/tests/message_signer.rs index a00802a4c3d..e09efef30fc 100644 --- a/rust/tw_evm/tests/message_signer.rs +++ b/rust/tw_evm/tests/message_signer.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_entry::modules::message_signer::MessageSigner; use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_encoding::hex::{DecodeHex, ToHex}; diff --git a/rust/tw_evm/tests/rlp.rs b/rust/tw_evm/tests/rlp.rs index f25a831a0f5..490add080d1 100644 --- a/rust/tw_evm/tests/rlp.rs +++ b/rust/tw_evm/tests/rlp.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use std::str::FromStr; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_evm::evm_context::StandardEvmContext; use tw_evm::modules::rlp_encoder::{RlpEncoder, RECURSION_LIMIT}; diff --git a/rust/tw_evm/tests/signer.rs b/rust/tw_evm/tests/signer.rs index 33f50fa3080..eace3005b4c 100644 --- a/rust/tw_evm/tests/signer.rs +++ b/rust/tw_evm/tests/signer.rs @@ -3,7 +3,7 @@ // Copyright © 2017 Trust Wallet. use std::borrow::Cow; -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_encoding::hex::{self, ToHex}; use tw_evm::evm_context::StandardEvmContext; use tw_evm::modules::signer::Signer; diff --git a/rust/tw_proto/src/impls.rs b/rust/tw_proto/src/impls.rs new file mode 100644 index 00000000000..73c07a97ae0 --- /dev/null +++ b/rust/tw_proto/src/impls.rs @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::Common::Proto::SigningError; +use crate::EthereumAbi::Proto::AbiError; +use std::fmt; + +impl fmt::Display for SigningError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let err = match self { + SigningError::OK => "", + SigningError::Error_general => "Unknown error", + SigningError::Error_internal => "Internal error", + SigningError::Error_low_balance => "Low balance: the sender balance is not enough to cover the send and other auxiliary amount such as fee, deposit, or minimal balance", + SigningError::Error_zero_amount_requested => "Requested amount is zero, send of 0 makes no sense", + SigningError::Error_missing_private_key => "One required key is missing (too few or wrong keys are provided)", + SigningError::Error_invalid_private_key => "A private key provided is invalid (e.g. wrong size, usually should be 32 bytes)", + SigningError::Error_invalid_address => "A provided address (e.g. destination address) is invalid", + SigningError::Error_invalid_utxo => "A provided input UTXO is invalid", + SigningError::Error_invalid_utxo_amount => "The amount of an input UTXO is invalid", + SigningError::Error_wrong_fee => "Wrong fee is given, probably it is too low to cover minimal fee for the transaction", + SigningError::Error_signing => "General signing error", + SigningError::Error_tx_too_big => "Resulting transaction is too large", + SigningError::Error_missing_input_utxos => "No input UTXOs provided", + SigningError::Error_not_enough_utxos => "Not enough non-dust input UTXOs to cover requested amount (dust UTXOs are filtered out)", + SigningError::Error_script_redeem => "Missing required redeem script", + SigningError::Error_script_output => "Invalid required output script", + SigningError::Error_script_witness_program => "Unrecognized witness program", + SigningError::Error_invalid_memo => "Invalid memo", + SigningError::Error_input_parse => "Some input field cannot be parsed", + SigningError::Error_no_support_n2n => "Multi-input and multi-output transaction not supported", + SigningError::Error_signatures_count => "Incorrect count of signatures passed to compile", + SigningError::Error_invalid_params => "Incorrect input parameter", + SigningError::Error_invalid_requested_token_amount => "Invalid input token amount", + SigningError::Error_not_supported => "Operation not supported for the chain", + SigningError::Error_dust_amount_requested => "Requested amount is too low (less dust)", + }; + write!(f, "{err}") + } +} + +impl fmt::Display for AbiError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let err = match self { + AbiError::OK => "", + AbiError::Error_internal => "Internal error", + AbiError::Error_abi_mismatch => "ABI mismatch", + AbiError::Error_invalid_abi => "Invalid ABI provided", + AbiError::Error_invalid_param_type => "Invalid parameter type", + AbiError::Error_invalid_address_value => "Invalid address", + AbiError::Error_invalid_uint_value => "Invalid Uint", + AbiError::Error_missing_param_type => "Missing one of the parameter types", + AbiError::Error_missing_param_value => "Missing one of the parameter values", + AbiError::Error_decoding_data => "Error decoding data", + AbiError::Error_empty_type => "Empty array/tuple not allowed", + }; + write!(f, "{}", err) + } +} diff --git a/rust/tw_proto/src/lib.rs b/rust/tw_proto/src/lib.rs index aa7a3345b54..d5de0751f6b 100644 --- a/rust/tw_proto/src/lib.rs +++ b/rust/tw_proto/src/lib.rs @@ -7,6 +7,7 @@ use quick_protobuf::{BytesReader, MessageInfo, Writer}; #[allow(non_snake_case)] #[rustfmt::skip] mod common; +mod impls; #[allow(non_snake_case)] #[rustfmt::skip] diff --git a/rust/wallet_core_rs/tests/ethereum_rlp.rs b/rust/wallet_core_rs/tests/ethereum_rlp.rs index 48eec4bfa51..c5e4cf0ea63 100644 --- a/rust/wallet_core_rs/tests/ethereum_rlp.rs +++ b/rust/wallet_core_rs/tests/ethereum_rlp.rs @@ -2,7 +2,7 @@ // // Copyright © 2017 Trust Wallet. -use tw_coin_entry::error::SigningErrorType; +use tw_coin_entry::error::prelude::*; use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::ToHex; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/tools/install-sys-dependencies-linux b/tools/install-sys-dependencies-linux index b9173e25e42..eec6524cd06 100755 --- a/tools/install-sys-dependencies-linux +++ b/tools/install-sys-dependencies-linux @@ -2,5 +2,23 @@ set -e - # build-essential clang-14 libc++-dev libc++abi-dev ruby-full cmake - sudo apt-get update && sudo apt-get install ninja-build lcov llvm-14 clang-tidy-14 libboost-all-dev rustc --fix-missing +# Downgrade libc to temporarily fix https://github.com/actions/runner-images/issues/8659 +if [[ "$1" == "ci" ]]; then + LIBSTD_PACKAGE_VERSION="12.3.0-1ubuntu1~22.04" + # Bump this version if the CI has been broken due to the packages update. + LIBC_PACKAGE_VERSION="2.35-0ubuntu3.7" + + echo "Remove GCC 13 from runner image - runner-images/8659 workaround" + echo "NOTE: Bump $LIBC_PACKAGE_VERSION version if the CI has been broken due to the packages update" + + sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r-ubuntu-test-jammy.list + sudo apt-get update + sudo apt-get install -y --allow-downgrades \ + libc6=$LIBC_PACKAGE_VERSION \ + libc6-dev=$LIBC_PACKAGE_VERSION \ + libstdc++6=$LIBSTD_PACKAGE_VERSION \ + libgcc-s1=$LIBSTD_PACKAGE_VERSION +fi + +# build-essential clang-14 libc++-dev libc++abi-dev ruby-full cmake +sudo apt-get update && sudo apt-get install ninja-build lcov llvm-14 clang-tidy-14 libboost-all-dev rustc --fix-missing From b002af3e46742223ea75404fdf5897a3b7e1f3c2 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Fri, 10 May 2024 12:52:56 +0200 Subject: [PATCH 088/128] [iOS]: Add Privacy Manifest file (#3830) --- swift/PrivacyInfo.xcprivacy | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 swift/PrivacyInfo.xcprivacy diff --git a/swift/PrivacyInfo.xcprivacy b/swift/PrivacyInfo.xcprivacy new file mode 100644 index 00000000000..19df86cbf91 --- /dev/null +++ b/swift/PrivacyInfo.xcprivacy @@ -0,0 +1,33 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryDiskSpace + NSPrivacyAccessedAPITypeReasons + + 85F4.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + + From 2c4eb4aed537b17537ca14c17cc4a1d579fd1d9b Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Fri, 10 May 2024 12:53:20 +0200 Subject: [PATCH 089/128] [Aeternity]: Catch all exceptions on `TWAnyAddressIsValid` for Aeternity coin (#3832) --- src/Coin.cpp | 37 ++++++++++++-------- tests/chains/Aeternity/AddressTests.cpp | 3 +- tests/chains/Aeternity/TWAnyAddressTests.cpp | 18 ++++++++++ 3 files changed, 43 insertions(+), 15 deletions(-) create mode 100644 tests/chains/Aeternity/TWAnyAddressTests.cpp diff --git a/src/Coin.cpp b/src/Coin.cpp index 12b7f596563..6033666b9ef 100644 --- a/src/Coin.cpp +++ b/src/Coin.cpp @@ -210,7 +210,11 @@ bool TW::validateAddress(TWCoinType coin, const string& address, const PrefixVar // dispatch auto* dispatcher = coinDispatcher(coin); assert(dispatcher != nullptr); - return dispatcher->validateAddress(coin, address, prefix); + try { + return dispatcher->validateAddress(coin, address, prefix); + } catch (...) { + return false; + } } bool TW::validateAddress(TWCoinType coin, const std::string& string) { @@ -221,20 +225,25 @@ bool TW::validateAddress(TWCoinType coin, const std::string& string) { // dispatch auto* dispatcher = coinDispatcher(coin); assert(dispatcher != nullptr); - bool isValid = false; - // First check HRP. - if (hrp != nullptr && !std::string(hrp).empty()) { - isValid = dispatcher->validateAddress(coin, string, Bech32Prefix(hrp)); - } - // Then check UTXO - if ((p2pkh != 0 || p2sh != 0) && !isValid) { - return isValid || dispatcher->validateAddress(coin, string, Base58Prefix{.p2pkh = p2pkh, .p2sh = p2sh}); - } - // Then check normal - if (!isValid) { - isValid = dispatcher->validateAddress(coin, string, std::monostate()); + + try { + bool isValid = false; + // First check HRP. + if (hrp != nullptr && !std::string(hrp).empty()) { + isValid = dispatcher->validateAddress(coin, string, Bech32Prefix(hrp)); + } + // Then check UTXO + if ((p2pkh != 0 || p2sh != 0) && !isValid) { + return isValid || dispatcher->validateAddress(coin, string, Base58Prefix{.p2pkh = p2pkh, .p2sh = p2sh}); + } + // Then check normal + if (!isValid) { + isValid = dispatcher->validateAddress(coin, string, std::monostate()); + } + return isValid; + } catch (...) { + return false; } - return isValid; } namespace TW::internal { diff --git a/tests/chains/Aeternity/AddressTests.cpp b/tests/chains/Aeternity/AddressTests.cpp index 2334129c5d9..581a9d75b86 100644 --- a/tests/chains/Aeternity/AddressTests.cpp +++ b/tests/chains/Aeternity/AddressTests.cpp @@ -2,9 +2,9 @@ // // Copyright © 2017 Trust Wallet. +#include #include #include -#include #include namespace TW::Aeternity::tests { @@ -20,6 +20,7 @@ TEST(AeternityAddress, FromString) { auto address = Address("ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"); ASSERT_EQ(address.string(), "ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"); ASSERT_ANY_THROW(Address("invalid")); + ASSERT_ANY_THROW(Address("behave@wallet")); } } // namespace TW::Aeternity::tests diff --git a/tests/chains/Aeternity/TWAnyAddressTests.cpp b/tests/chains/Aeternity/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..f747af3e862 --- /dev/null +++ b/tests/chains/Aeternity/TWAnyAddressTests.cpp @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#include +#include +#include + +namespace TW::Aeternity::tests { + +// `TWAnyAddressIsValid` must catch exceptions and return false. +TEST(AeternityAddress, IsValid) { + ASSERT_FALSE(TWAnyAddressIsValid(STRING("invalid").get(), TWCoinTypeAeternity)); + ASSERT_FALSE(TWAnyAddressIsValid(STRING("behave@wallet").get(), TWCoinTypeAeternity)); + ASSERT_FALSE(TWAnyAddressIsValid(STRING("a").get(), TWCoinTypeAeternity)); +} + +} // namespace TW::Aeternity::tests From 484dc2a98c7a7ed9023ebae6fe30b1e331fff6e0 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Fri, 10 May 2024 13:24:02 +0200 Subject: [PATCH 090/128] [Ethereum]: Properly handle hex encoded EIP-191 messages (#3833) --- .../ethereum/TestEthereumMessageSigner.kt | 11 +++++++++++ rust/tw_evm/src/message/eip191.rs | 15 +++++++++++++-- rust/tw_evm/tests/message_signer.rs | 13 +++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumMessageSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumMessageSigner.kt index 6b86f698769..6245751d124 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumMessageSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ethereum/TestEthereumMessageSigner.kt @@ -36,6 +36,17 @@ class TestEthereumMessageSigner { assertTrue(EthereumMessageSigner.verifyMessage(publicKey, msg, signature)) } + @Test + fun testEthereumSignAndVerifyMessageLegacyHex() { + val data = Numeric.hexStringToByteArray("9066aa168c379a403becb235c15e7129c133c244e56a757ab07bc369288bcab0") + val privateKey = PrivateKey(data) + val publicKey = privateKey.getPublicKey(CoinType.ETHEREUM) + val msg = "0xc0a96273d5c3fbe4d4000491f08daef9c17f88df846c1d6f57eb5f33c1fbd035" + val signature = EthereumMessageSigner.signMessage(privateKey, msg) + assertEquals(signature, "b18a666ad08bf9bfcd39920b26b5a5d1486b67b45119810b3c7bda22e41e5c4c1bfbe0c932f6c14df4947a18ba310831a37b7307d724a3ac2a4935b99d7075141b"); + assertTrue(EthereumMessageSigner.verifyMessage(publicKey, msg, signature)) + } + @Test fun testEthereumSignAndVerifyMessage712Legacy() { val data = Numeric.hexStringToByteArray("03a9ca895dca1623c7dfd69693f7b4111f5d819d2e145536e0b03c136025a25d") diff --git a/rust/tw_evm/src/message/eip191.rs b/rust/tw_evm/src/message/eip191.rs index cb2bd715d8e..ed532f6cc2e 100644 --- a/rust/tw_evm/src/message/eip191.rs +++ b/rust/tw_evm/src/message/eip191.rs @@ -3,6 +3,7 @@ // Copyright © 2017 Trust Wallet. use crate::message::{EthMessage, MessageSigningResult}; +use tw_encoding::hex; use tw_hash::sha3::keccak256; use tw_hash::H256; @@ -27,8 +28,18 @@ impl Eip191Message { data.push(ETHEREUM_PREFIX); data.extend_from_slice(ETHEREUM_MESSAGE_PREFIX.as_bytes()); - data.extend_from_slice(self.user_message.len().to_string().as_bytes()); - data.extend_from_slice(self.user_message.as_bytes()); + + let mut do_extend = |bytes: &[u8]| { + data.extend_from_slice(bytes.len().to_string().as_bytes()); + data.extend_from_slice(bytes); + }; + + match hex::decode(&self.user_message) { + // Handle the message as hex-encoded. + Ok(raw_msg) => do_extend(&raw_msg), + // Handle as a regular string. + Err(_) => do_extend(self.user_message.as_bytes()), + } data } diff --git a/rust/tw_evm/tests/message_signer.rs b/rust/tw_evm/tests/message_signer.rs index e09efef30fc..447e48df6db 100644 --- a/rust/tw_evm/tests/message_signer.rs +++ b/rust/tw_evm/tests/message_signer.rs @@ -127,6 +127,19 @@ fn test_message_signer_sign_verify_legacy() { }); } +/// https://github.com/trustwallet/wallet-core/issues/3831 +#[test] +fn test_message_signer_sign_verify_legacy_hex() { + test_message_signer_sign_verify(SignVerifyTestInput { + // 0x9d1d97adfcd324bbd603d3872bd78e04098510b1 + private_key: "9066aa168c379a403becb235c15e7129c133c244e56a757ab07bc369288bcab0", + msg: "0xc0a96273d5c3fbe4d4000491f08daef9c17f88df846c1d6f57eb5f33c1fbd035", + msg_type: Proto::MessageType::MessageType_legacy, + chain_id: None, + signature: "b18a666ad08bf9bfcd39920b26b5a5d1486b67b45119810b3c7bda22e41e5c4c1bfbe0c932f6c14df4947a18ba310831a37b7307d724a3ac2a4935b99d7075141b", + }); +} + #[test] fn test_message_signer_sign_verify_eip155() { test_message_signer_sign_verify(SignVerifyTestInput { From de287c8edc1bd5c5c8aec1b1185d38c9fd1f9b5d Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Mon, 13 May 2024 11:12:10 +0200 Subject: [PATCH 091/128] [Sui]: Fix address formatting (#3838) --- rust/chains/tw_sui/src/address.rs | 11 ++++++++++- .../tw_any_coin/tests/coin_address_derivation_test.rs | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/rust/chains/tw_sui/src/address.rs b/rust/chains/tw_sui/src/address.rs index 1ed70526d86..063418906dc 100644 --- a/rust/chains/tw_sui/src/address.rs +++ b/rust/chains/tw_sui/src/address.rs @@ -57,7 +57,8 @@ impl FromStr for SuiAddress { impl fmt::Display for SuiAddress { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0.to_hex_literal()) + let prefixed = true; + write!(f, "{}", hex::encode(self.0.as_ref(), prefixed)) } } @@ -86,4 +87,12 @@ mod tests { "0x259ff8074ab425cbb489f236e18e08f03f1a7856bdf7c7a2877bd64f738b5015" ); } + + /// https://github.com/trustwallet/wallet-core/issues/3837 + #[test] + fn test_sui_address_str_with_leading_zero() { + let s = "0x0cf10169225a251113b3198dc81d15ba72286f73353a8212f03bad10bd0f0a99"; + let addr = SuiAddress::from_str(s).unwrap(); + assert_eq!(addr.to_string(), s); + } } diff --git a/rust/tw_any_coin/tests/coin_address_derivation_test.rs b/rust/tw_any_coin/tests/coin_address_derivation_test.rs index 6be2411064a..2e9c4b0c018 100644 --- a/rust/tw_any_coin/tests/coin_address_derivation_test.rs +++ b/rust/tw_any_coin/tests/coin_address_derivation_test.rs @@ -153,7 +153,7 @@ fn test_coin_address_derivation() { CoinType::NativeZetaChain => "zeta14s0vgnj0pjnazu4hsqlksdk7slah9vcfcwctsr", CoinType::Dydx => "dydx1ten42eesehw0ktddcp0fws7d3ycsqez3kaamq3", CoinType::Solana => "5sn9QYhDaq61jLXJ8Li5BKqGL4DDMJQvU1rdN8XgVuwC", - CoinType::Sui => "0x1a5c6c1b74cec4fbd12b3e17252b83448136065afcdf24954dc3a9c26df4905", + CoinType::Sui => "0x01a5c6c1b74cec4fbd12b3e17252b83448136065afcdf24954dc3a9c26df4905", // end_of_coin_address_derivation_tests_marker_do_not_modify _ => panic!("{:?} must be covered", coin), }; From a3d763b8e54d20e0dde860d5c6d413a810d5042b Mon Sep 17 00:00:00 2001 From: Jaime Toca Date: Wed, 29 May 2024 09:56:19 +0200 Subject: [PATCH 092/128] [Algorand]: Update explorer for Tx and Account (#3858) * Change Algorand explorer * remove s char * Update test and comments --- registry.json | 6 +++--- tests/chains/Algorand/SignerTests.cpp | 10 +++++----- tests/chains/Algorand/TWAnySignerTests.cpp | 2 +- tests/chains/Algorand/TWCoinTypeTests.cpp | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/registry.json b/registry.json index 0a2f370590a..c4953ef0313 100644 --- a/registry.json +++ b/registry.json @@ -1780,9 +1780,9 @@ "curve": "ed25519", "publicKeyType": "ed25519", "explorer": { - "url": "https://algoexplorer.io", - "txPath": "/tx/", - "accountPath": "/address/", + "url": "https://app.dappflow.org/explorer", + "txPath": "/transaction/", + "accountPath": "/account/", "sampleTx": "CR7POXFTYDLC7TV3IXHA7AZKWABUJC52BACLHJQNXAKZJGRPQY3A", "sampleAccount": "J4AEINCSSLDA7LNBNWM4ZXFCTLTOZT5LG3F5BLMFPJYGFWVCMU37EZI2AM" }, diff --git a/tests/chains/Algorand/SignerTests.cpp b/tests/chains/Algorand/SignerTests.cpp index 6b38bf6ce1d..1eef1ada080 100644 --- a/tests/chains/Algorand/SignerTests.cpp +++ b/tests/chains/Algorand/SignerTests.cpp @@ -84,7 +84,7 @@ TEST(AlgorandSigner, Sign) { } TEST(AlgorandSigner, SignAssetNFTTransfer) { - // Successfully broadcasted: https://algoexplorer.io/tx/FFLUH4QKZHG744RIQ2AZNWZUSIIH262KZ4MEWSY4RXMWN5NMOOJA + // Successfully broadcasted: https://app.dappflow.org/explorer/transaction/FFLUH4QKZHG744RIQ2AZNWZUSIIH262KZ4MEWSY4RXMWN5NMOOJA auto key = PrivateKey(parse_hex("dc6051ffc7b3ec601bde432f6dea34d40fe3855e4181afa0f0524c42194a6da7")); auto publicKey = key.getPublicKey(TWPublicKeyTypeED25519); auto from = Address(publicKey); @@ -115,7 +115,7 @@ TEST(AlgorandSigner, SignAssetNFTTransfer) { } TEST(AlgorandSigner, SignAsset) { - // https://testnet.algoexplorer.io/tx/NJ62HYO2LC222AVLIN2GW5LKIWKLGC7NZLIQ3DUL2RDVRYO2UW7A + // https://explorer.bitquery.io/algorand_testnet/tx/NJ62HYO2LC222AVLIN2GW5LKIWKLGC7NZLIQ3DUL2RDVRYO2UW7A auto key = PrivateKey(parse_hex("5a6a3cfe5ff4cc44c19381d15a0d16de2a76ee5c9b9d83b232e38cb5a2c84b04")); auto publicKey = key.getPublicKey(TWPublicKeyTypeED25519); auto from = Address(publicKey); @@ -172,7 +172,7 @@ TEST(AlgorandSigner, SignAssetWithNote) { } TEST(AlgorandSigner, SignAssetOptIn) { - // https://testnet.algoexplorer.io/tx/47LE2QS4B5N6IFHXOUN2MJUTCOQCHNY6AB3AJYECK4IM2VYKJDKQ + // https://explorer.bitquery.io/algorand_testnet/tx/47LE2QS4B5N6IFHXOUN2MJUTCOQCHNY6AB3AJYECK4IM2VYKJDKQ auto key = PrivateKey(parse_hex("5a6a3cfe5ff4cc44c19381d15a0d16de2a76ee5c9b9d83b232e38cb5a2c84b04")); auto publicKey = key.getPublicKey(TWPublicKeyTypeED25519); auto address = Address(publicKey); @@ -200,7 +200,7 @@ TEST(AlgorandSigner, SignAssetOptIn) { } TEST(AlgorandSigner, ProtoSignerOptIn) { - // https://testnet.algoexplorer.io/tx/47LE2QS4B5N6IFHXOUN2MJUTCOQCHNY6AB3AJYECK4IM2VYKJDKQ + // https://explorer.bitquery.io/algorand_testnet/tx/47LE2QS4B5N6IFHXOUN2MJUTCOQCHNY6AB3AJYECK4IM2VYKJDKQ auto optIn = new Proto::AssetOptIn(); optIn->set_asset_id(13379146); @@ -224,7 +224,7 @@ TEST(AlgorandSigner, ProtoSignerOptIn) { } TEST(AlgorandSigner, ProtoSignerAssetTransaction) { - // https://testnet.algoexplorer.io/tx/NJ62HYO2LC222AVLIN2GW5LKIWKLGC7NZLIQ3DUL2RDVRYO2UW7A + // https://explorer.bitquery.io/algorand_testnet/tx/NJ62HYO2LC222AVLIN2GW5LKIWKLGC7NZLIQ3DUL2RDVRYO2UW7A auto transaction = new Proto::AssetTransfer(); transaction->set_asset_id(13379146); transaction->set_amount(1000000); diff --git a/tests/chains/Algorand/TWAnySignerTests.cpp b/tests/chains/Algorand/TWAnySignerTests.cpp index 0ac2c03375b..7d82693add4 100644 --- a/tests/chains/Algorand/TWAnySignerTests.cpp +++ b/tests/chains/Algorand/TWAnySignerTests.cpp @@ -15,7 +15,7 @@ using namespace TW; namespace TW::Algorand::tests { TEST(TWAnySignerAlgorand, SignAssetNFTTransfer) { - // Successfully broadcasted: https://algoexplorer.io/tx/FFLUH4QKZHG744RIQ2AZNWZUSIIH262KZ4MEWSY4RXMWN5NMOOJA + // Successfully broadcasted: https://app.dappflow.org/explorer/transaction/FFLUH4QKZHG744RIQ2AZNWZUSIIH262KZ4MEWSY4RXMWN5NMOOJA auto privateKey = parse_hex("dc6051ffc7b3ec601bde432f6dea34d40fe3855e4181afa0f0524c42194a6da7"); Data note = Base64::decode("VFdUIFRPIFRIRSBNT09O"); auto genesisHash = Base64::decode("wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8="); diff --git a/tests/chains/Algorand/TWCoinTypeTests.cpp b/tests/chains/Algorand/TWCoinTypeTests.cpp index 8aba2311005..0f7caba2bb3 100644 --- a/tests/chains/Algorand/TWCoinTypeTests.cpp +++ b/tests/chains/Algorand/TWCoinTypeTests.cpp @@ -25,8 +25,8 @@ TEST(TWAlgorandCoinType, TWCoinType) { ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeAlgorand)); ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeAlgorand)); assertStringsEqual(symbol, "ALGO"); - assertStringsEqual(txUrl, "https://algoexplorer.io/tx/CR7POXFTYDLC7TV3IXHA7AZKWABUJC52BACLHJQNXAKZJGRPQY3A"); - assertStringsEqual(accUrl, "https://algoexplorer.io/address/J4AEINCSSLDA7LNBNWM4ZXFCTLTOZT5LG3F5BLMFPJYGFWVCMU37EZI2AM"); + assertStringsEqual(txUrl, "https://app.dappflow.org/explorer/transaction/CR7POXFTYDLC7TV3IXHA7AZKWABUJC52BACLHJQNXAKZJGRPQY3A"); + assertStringsEqual(accUrl, "https://app.dappflow.org/explorer/account/J4AEINCSSLDA7LNBNWM4ZXFCTLTOZT5LG3F5BLMFPJYGFWVCMU37EZI2AM"); assertStringsEqual(id, "algorand"); assertStringsEqual(name, "Algorand"); } From 55ca3e561d6c0815a5601a51fd66ffc679061660 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Fri, 31 May 2024 11:18:28 +0200 Subject: [PATCH 093/128] [Misc]: Add WalletCore binary binary tools (#3865) * [Misc]: Add wallet_core_bin crate with binary tools * Add `registry-stats` command * [Misc]: Add a helper function to `tools/registry` * [Misc]: Fix build and test scripts * [Misc]: Fix linux-ci-rust CI * [Misc]: Use a script in linux-ci-rust.yml * [Misc]: Fix linux-ci-rust.yml --- .github/workflows/linux-ci-rust.yml | 3 +- rust/Cargo.lock | 9 +++ rust/Cargo.toml | 1 + rust/tw_coin_registry/src/registry.rs | 2 +- rust/wallet_core_bin/Cargo.toml | 9 +++ rust/wallet_core_bin/src/main.rs | 39 ++++++++++++ rust/wallet_core_bin/src/registry_stats.rs | 72 ++++++++++++++++++++++ tools/registry | 22 ++++++- tools/rust-bindgen | 8 +-- tools/rust-coverage | 4 +- tools/rust-test | 2 +- 11 files changed, 160 insertions(+), 11 deletions(-) create mode 100644 rust/wallet_core_bin/Cargo.toml create mode 100644 rust/wallet_core_bin/src/main.rs create mode 100644 rust/wallet_core_bin/src/registry_stats.rs diff --git a/.github/workflows/linux-ci-rust.yml b/.github/workflows/linux-ci-rust.yml index cd6302e6aa0..768af167d88 100644 --- a/.github/workflows/linux-ci-rust.yml +++ b/.github/workflows/linux-ci-rust.yml @@ -53,8 +53,7 @@ jobs: - name: Run tests run: | - cargo llvm-cov test --no-fail-fast --lcov --output-path coverage.info - working-directory: rust + tools/rust-coverage - name: Gather and check Rust code coverage run: | diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 3d7c0c29603..ed95ae6b7a0 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -2131,6 +2131,15 @@ dependencies = [ "uuid", ] +[[package]] +name = "wallet_core_bin" +version = "0.1.0" +dependencies = [ + "serde", + "serde_json", + "tw_coin_registry", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 183f29e20a6..125780d2be9 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -27,6 +27,7 @@ members = [ "tw_number", "tw_proto", "tw_utxo", + "wallet_core_bin", "wallet_core_rs", ] diff --git a/rust/tw_coin_registry/src/registry.rs b/rust/tw_coin_registry/src/registry.rs index 595ed152f28..37db97f44f1 100644 --- a/rust/tw_coin_registry/src/registry.rs +++ b/rust/tw_coin_registry/src/registry.rs @@ -14,7 +14,7 @@ use tw_keypair::tw::PublicKeyType; type RegistryMap = HashMap; /// cbindgen:ignore -const REGISTRY_JSON: &str = +pub const REGISTRY_JSON: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../registry.json")); lazy_static! { diff --git a/rust/wallet_core_bin/Cargo.toml b/rust/wallet_core_bin/Cargo.toml new file mode 100644 index 00000000000..49896eb72b8 --- /dev/null +++ b/rust/wallet_core_bin/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "wallet_core_bin" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = "1.0" +serde_json = "1.0" +tw_coin_registry = { path = "../tw_coin_registry" } diff --git a/rust/wallet_core_bin/src/main.rs b/rust/wallet_core_bin/src/main.rs new file mode 100644 index 00000000000..cf9e7c554ac --- /dev/null +++ b/rust/wallet_core_bin/src/main.rs @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +mod registry_stats; + +#[derive(Debug)] +enum Error { + MissingArguments, + UnknownCommand, + InvalidRegistry, + InvalidRegistryParam { + #[allow(dead_code)] + param: &'static str, + }, +} + +fn help() { + println!("WalletCore binary tools:"); + println!(); + println!("\tregistry-stats Print registry statistic (e.g Rust transition progress)"); +} + +fn main() -> Result<(), Error> { + let args: Vec = std::env::args().collect(); + + if args.len() < 2 { + help(); + return Err(Error::MissingArguments); + } + + match args[1].as_str() { + "registry-stats" => registry_stats::registry_stats(), + _ => { + help(); + Err(Error::UnknownCommand) + }, + } +} diff --git a/rust/wallet_core_bin/src/registry_stats.rs b/rust/wallet_core_bin/src/registry_stats.rs new file mode 100644 index 00000000000..8ebd6b6dda7 --- /dev/null +++ b/rust/wallet_core_bin/src/registry_stats.rs @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::Error; +use serde_json::Value as Json; +use std::collections::HashSet; +use tw_coin_registry::blockchain_type::BlockchainType; +use tw_coin_registry::registry; + +struct BlockchainStats { + total: usize, + supported: usize, +} + +fn blockchain_stats() -> Result { + let chains: Vec = + serde_json::from_str(registry::REGISTRY_JSON).map_err(|_| Error::InvalidRegistry)?; + + fn extract_blockchain(chain: &Json) -> Option<(&str, BlockchainType)> { + let blockchain_val = chain.get("blockchain")?; + let blockchain_str = chain["blockchain"].as_str()?; + let blockchain_type: BlockchainType = + serde_json::from_value(blockchain_val.clone()).ok()?; + Some((blockchain_str, blockchain_type)) + } + + let mut supported = HashSet::new(); + let mut total = HashSet::new(); + + for chain in chains.iter() { + let (blockchain_str, blockchain_type) = + extract_blockchain(chain).ok_or(Error::InvalidRegistryParam { + param: "blockchain", + })?; + + total.insert(blockchain_str); + match blockchain_type { + BlockchainType::Unsupported => (), + _ => { + supported.insert(blockchain_str); + }, + } + } + + Ok(BlockchainStats { + total: total.len(), + supported: supported.len(), + }) +} + +pub fn registry_stats() -> Result<(), Error> { + let total_chains = registry::registry_iter().count(); + let chains_in_rust = registry::supported_coin_items().count(); + let rust_chains_progress = (chains_in_rust * 100) as f64 / total_chains as f64; + + let blockchain_stats = blockchain_stats()?; + let blockchain_progress = + (blockchain_stats.supported * 100) as f64 / blockchain_stats.total as f64; + + println!(); + println!("total chains: {total_chains}"); + println!("supported chains: {chains_in_rust}"); + println!("chains transition progress: {rust_chains_progress:.1}%"); + + println!(); + println!("total blockchain impls: {}", blockchain_stats.total); + println!("supported blockchain impls: {}", blockchain_stats.supported); + println!("blockchains transition progress: {blockchain_progress:.1}%"); + + Ok(()) +} diff --git a/tools/registry b/tools/registry index c044a37394c..2f322890910 100755 --- a/tools/registry +++ b/tools/registry @@ -2,8 +2,9 @@ import argparse import json -import sys import requests +import subprocess +import sys PATH_TO_REGISTRY_JSON = "registry.json" @@ -34,6 +35,21 @@ def ping_explorers(_args): print("\tError: ", exception) +def registry_stats(_args): + """Print registry stats""" + subprocess.run([ + "cargo", + "run", + "--manifest-path", + "rust/Cargo.toml", + "-p", + "wallet_core_bin", + "--", + "registry-stats" + ], + shell=False) + + if __name__ == '__main__': parser = argparse.ArgumentParser(description="Operations over registry.json") subparsers = parser.add_subparsers() @@ -42,5 +58,9 @@ if __name__ == '__main__': help="Ping blockchain explorers inside 'registry.json'") ping_explorers_parser.set_defaults(func=ping_explorers) + registry_stats_parser = subparsers.add_parser('registry-stats', + help="Print registry statistic (e.g Rust transition progress)") + registry_stats_parser.set_defaults(func=registry_stats) + args = parser.parse_args() args.func(args) diff --git a/tools/rust-bindgen b/tools/rust-bindgen index 1b0afa3a6a5..0ca640b7766 100755 --- a/tools/rust-bindgen +++ b/tools/rust-bindgen @@ -49,14 +49,14 @@ fi if [[ "$NATIVE" == "true" ]]; then echo "Generating Native target" - cargo build --release + cargo build --release --lib fi export RUSTFLAGS="-Zlocation-detail=none" if [[ "$WASM" == "true" ]]; then echo "Generating WASM target" - cargo build -Z build-std=std,panic_abort --target wasm32-unknown-emscripten --release + cargo build -Z build-std=std,panic_abort --target wasm32-unknown-emscripten --release --lib fi if [[ "$ANDROID" == "true" ]]; then @@ -69,12 +69,12 @@ if [[ "$ANDROID" == "true" ]]; then export CC_armv7_linux_androideabi="$NDK_BIN_PATH/armv7a-linux-androideabi$NDK_API_LEVEL-clang" echo "Generating Android targets" - cargo build -Z build-std=std,panic_abort --target aarch64-linux-android --target armv7-linux-androideabi --target x86_64-linux-android --target i686-linux-android --release + cargo build -Z build-std=std,panic_abort --target aarch64-linux-android --target armv7-linux-androideabi --target x86_64-linux-android --target i686-linux-android --release --lib fi if [[ "$IOS" == "true" ]]; then echo "Generating iOS targets" - cargo build -Z build-std=std,panic_abort --target aarch64-apple-ios --target aarch64-apple-ios-sim --target x86_64-apple-ios --target aarch64-apple-darwin --target x86_64-apple-darwin --target aarch64-apple-ios-macabi --target x86_64-apple-ios-macabi --release & + cargo build -Z build-std=std,panic_abort --target aarch64-apple-ios --target aarch64-apple-ios-sim --target x86_64-apple-ios --target aarch64-apple-darwin --target x86_64-apple-darwin --target aarch64-apple-ios-macabi --target x86_64-apple-ios-macabi --release --lib & wait lipo $BUILD_FOLDER/x86_64-apple-ios/release/$TARGET_NAME $BUILD_FOLDER/aarch64-apple-ios-sim/release/$TARGET_NAME -create -output $BUILD_FOLDER/$TARGET_NAME mkdir -p $BUILD_FOLDER/darwin_universal diff --git a/tools/rust-coverage b/tools/rust-coverage index f89753f935f..835b13997b3 100755 --- a/tools/rust-coverage +++ b/tools/rust-coverage @@ -18,10 +18,10 @@ pushd rust # Generate HTML report if requested if [[ "$1" == "html" ]]; then - cargo llvm-cov test --html + cargo llvm-cov test --workspace --exclude wallet_core_bin --html cargo llvm-cov report --lcov --output-path coverage.info else - cargo llvm-cov test --lcov --output-path coverage.info + cargo llvm-cov test --workspace --exclude wallet_core_bin --lcov --output-path coverage.info fi popd diff --git a/tools/rust-test b/tools/rust-test index 1b20938f589..ad72918e65b 100755 --- a/tools/rust-test +++ b/tools/rust-test @@ -18,7 +18,7 @@ if [[ "$1" == "wasm" ]]; then source ../emsdk/emsdk_env.sh export CARGO_TARGET_WASM32_UNKNOWN_EMSCRIPTEN_RUNNER=node - cargo test --target wasm32-unknown-emscripten --profile wasm-test + cargo test --target wasm32-unknown-emscripten --profile wasm-test --workspace --exclude wallet_core_bin else cargo test --all fi From a85c7d401f0d814eabbc72577a4b24b9ff2dda41 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 5 Jun 2024 07:11:52 +0200 Subject: [PATCH 094/128] feat(zkLink): Add zkLink Nova Mainnet EVM chain (#3881) --- .../blockchains/CoinAddressDerivationTests.kt | 2 +- docs/registry.md | 1 + include/TrustWalletCore/TWCoinType.h | 1 + .../core/test/CoinAddressDerivationTests.kt | 2 +- registry.json | 31 +++++++++++++++++++ .../tests/coin_address_derivation_test.rs | 1 + swift/Tests/CoinAddressDerivationTests.swift | 3 +- tests/chains/ZkLinkNova/TWCoinTypeTests.cpp | 29 +++++++++++++++++ tests/common/CoinAddressDerivationTests.cpp | 1 + 9 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 tests/chains/ZkLinkNova/TWCoinTypeTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index 3fa855cc07b..243b33df519 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -47,7 +47,7 @@ class CoinAddressDerivationTests { FANTOM, CELO, CRONOSCHAIN, SMARTBITCOINCASH, KUCOINCOMMUNITYCHAIN, BOBA, METIS, AURORA, EVMOS, MOONRIVER, MOONBEAM, KAVAEVM, KLAYTN, METER, OKXCHAIN, POLYGONZKEVM, SCROLL, CONFLUXESPACE, ACALAEVM, OPBNB, NEON, BASE, LINEA, GREENFIELD, MANTLE, ZENEON, MANTAPACIFIC, - ZETAEVM, MERLIN, LIGHTLINK, BLAST, BOUNCEBIT, + ZETAEVM, MERLIN, LIGHTLINK, BLAST, BOUNCEBIT, ZKLINKNOVA, -> assertEquals("0x8f348F300873Fd5DA36950B2aC75a26584584feE", address) RONIN -> assertEquals("ronin:8f348F300873Fd5DA36950B2aC75a26584584feE", address) diff --git a/docs/registry.md b/docs/registry.md index ad4c51171a7..b278eaba2a0 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -104,6 +104,7 @@ This list is generated from [./registry.json](../registry.json) | 81457 | Blast | ETH | | | | 105105 | Stratis | STRAX | | | | 534352 | Scroll | ETH | | | +| 810180 | zkLink Nova Mainnet | ETH | | | | 5718350 | Wanchain | WAN | | | | 5741564 | Waves | WAVES | | | | 10000025 | Cronos Chain | CRO | | | diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index ce3f2aa0d20..0726ca25b44 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -185,6 +185,7 @@ enum TWCoinType { TWCoinTypeLightlink = 1890, TWCoinTypeBlast = 81457, TWCoinTypeBounceBit = 6001, + TWCoinTypeZkLinkNova = 810180, // end_of_tw_coin_type_marker_do_not_modify }; diff --git a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt index 1714a611aba..39f84b264cd 100644 --- a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt +++ b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt @@ -40,7 +40,7 @@ class CoinAddressDerivationTests { Fantom, Celo, CronosChain, SmartBitcoinCash, KuCoinCommunityChain, Boba, Metis, Aurora, Evmos, Moonriver, Moonbeam, KavaEvm, Klaytn, Meter, OKXChain, PolygonzkEVM, Scroll, ConfluxeSpace, AcalaEVM, OpBNB, Neon, Base, Linea, Greenfield, Mantle, ZenEON, MantaPacific, - ZetaEVM, Merlin, Lightlink, Blast, BounceBit, + ZetaEVM, Merlin, Lightlink, Blast, BounceBit, ZkLinkNova, -> "0x8f348F300873Fd5DA36950B2aC75a26584584feE" Ronin -> "ronin:8f348F300873Fd5DA36950B2aC75a26584584feE" diff --git a/registry.json b/registry.json index c4953ef0313..d1c60580978 100644 --- a/registry.json +++ b/registry.json @@ -4749,5 +4749,36 @@ "rpc": "https://fullnode-mainnet.bouncebitapi.com", "documentation": "https://docs.bouncebit.io" } + }, + { + "id": "zklinknova", + "name": "ZkLinkNova", + "displayName": "zkLink Nova Mainnet", + "coinId": 810180, + "symbol": "ETH", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "810180", + "addressHasher": "keccak256", + "explorer": { + "url": "https://explorer.zklink.io", + "txPath": "/tx/", + "accountPath": "/address/", + "sampleTx": "0xeb5eb8710369c89115a83f3e744c15c9d388030cfce2fd3a653dbd18f2947400", + "sampleAccount": "0xF95115BaD9a4585B3C5e2bfB50579f17163A45aA" + }, + "info": { + "url": "https://zklink.io", + "source": "https://github.com/zkLinkProtocol", + "rpc": "https://rpc.zklink.io", + "documentation": "https://docs.zklink.io" + } } ] diff --git a/rust/tw_any_coin/tests/coin_address_derivation_test.rs b/rust/tw_any_coin/tests/coin_address_derivation_test.rs index 2e9c4b0c018..ab873f5d3df 100644 --- a/rust/tw_any_coin/tests/coin_address_derivation_test.rs +++ b/rust/tw_any_coin/tests/coin_address_derivation_test.rs @@ -90,6 +90,7 @@ fn test_coin_address_derivation() { | CoinType::Lightlink | CoinType::Blast | CoinType::BounceBit + | CoinType::ZkLinkNova // end_of_evm_address_derivation_tests_marker_do_not_modify => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", CoinType::Bitcoin diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index e6cda80b29a..7e17c1f3503 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -118,7 +118,8 @@ class CoinAddressDerivationTests: XCTestCase { .merlin, .lightlink, .blast, - .bounceBit: + .bounceBit, + .zkLinkNova: let expectedResult = "0x8f348F300873Fd5DA36950B2aC75a26584584feE" assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .ronin: diff --git a/tests/chains/ZkLinkNova/TWCoinTypeTests.cpp b/tests/chains/ZkLinkNova/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..933ab752af8 --- /dev/null +++ b/tests/chains/ZkLinkNova/TWCoinTypeTests.cpp @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#include "TestUtilities.h" +#include +#include + +TEST(TWZkLinkNovaCoinType, TWCoinType) { + const auto coin = TWCoinTypeZkLinkNova; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xeb5eb8710369c89115a83f3e744c15c9d388030cfce2fd3a653dbd18f2947400")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xF95115BaD9a4585B3C5e2bfB50579f17163A45aA")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "zklinknova"); + assertStringsEqual(name, "zkLink Nova Mainnet"); + assertStringsEqual(symbol, "ETH"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainEthereum); + ASSERT_EQ(TWCoinTypeP2pkhPrefix(coin), 0); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0); + assertStringsEqual(txUrl, "https://explorer.zklink.io/tx/0xeb5eb8710369c89115a83f3e744c15c9d388030cfce2fd3a653dbd18f2947400"); + assertStringsEqual(accUrl, "https://explorer.zklink.io/address/0xF95115BaD9a4585B3C5e2bfB50579f17163A45aA"); +} diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index 351b5dd4954..96ceb19b711 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -88,6 +88,7 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeLightlink: case TWCoinTypeBlast: case TWCoinTypeBounceBit: + case TWCoinTypeZkLinkNova: // end_of_evm_address_derivation_tests_marker_do_not_modify EXPECT_EQ(address, "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); break; From 3e8484cb15a82a01ab88909c3e32732fe82a1818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20=22Doom=22=20Doumergue?= Date: Fri, 14 Jun 2024 04:58:43 +0200 Subject: [PATCH 095/128] Update Polkadot extrinsic encoding to support spec 1002005 (#3893) * Update Polkadot extrinsic encoding to support spec 1002005 * Update LIBC_PACKAGE_VERSION for Linux CI --- src/Polkadot/Extrinsic.cpp | 18 +++++++++++++++ tests/chains/Polkadot/ExtrinsicTests.cpp | 29 ++++++++++++++++++++++++ tools/install-sys-dependencies-linux | 2 +- 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/Polkadot/Extrinsic.cpp b/src/Polkadot/Extrinsic.cpp index ebfbe67d2b0..8ec0b509c6e 100644 --- a/src/Polkadot/Extrinsic.cpp +++ b/src/Polkadot/Extrinsic.cpp @@ -388,8 +388,15 @@ Data Extrinsic::encodeIdentityAddAuthorization(const Proto::Identity::AddAuthori return data; } +static bool requires_new_spec_compatbility(uint32_t network, uint32_t specVersion) noexcept { + // version 1002005 introduces a breaking change for Polkadot and Kusama + return ((network == 0 || network == 2) && specVersion >= 1002005); +} + Data Extrinsic::encodePayload() const { Data data; + auto use_new_spec = requires_new_spec_compatbility(network, specVersion); + // call append(data, call); // era / nonce / tip @@ -398,6 +405,12 @@ Data Extrinsic::encodePayload() const { if (!feeAssetId.empty()) { append(data, feeAssetId); } + + if (use_new_spec) { + // mode (currently always 0) + data.push_back(0x00); + } + // specVersion encode32LE(specVersion, data); // transactionVersion @@ -406,6 +419,11 @@ Data Extrinsic::encodePayload() const { append(data, genesisHash); // block hash append(data, blockHash); + + if (use_new_spec) { + // empty metadata hash + data.push_back(0x00); + } return data; } diff --git a/tests/chains/Polkadot/ExtrinsicTests.cpp b/tests/chains/Polkadot/ExtrinsicTests.cpp index dd921a64343..4f5c969722a 100644 --- a/tests/chains/Polkadot/ExtrinsicTests.cpp +++ b/tests/chains/Polkadot/ExtrinsicTests.cpp @@ -159,4 +159,33 @@ TEST(PolkadotExtrinsic, Kusama_encodeAssetTransferNoCallIndices) { EXPECT_THROW(Polkadot::Extrinsic(input).encodeCall(input), std::invalid_argument); } +TEST(PolkadotExtrinsic, Polkadot_EncodePayloadWithNewSpec) { + Polkadot::Proto::SigningInput input; + input.set_network(0); + input.set_multi_address(true); + + auto* transfer = input.mutable_balance_call()->mutable_asset_transfer(); + transfer->set_to_address("14ixj163bkk2UEKLEXsEWosuFNuijpqEWZbX5JzN4yMHbUVD"); + auto* callIndices = transfer->mutable_call_indices()->mutable_custom(); + callIndices->set_module_index(0x32); + callIndices->set_method_index(0x05); + + auto value = store(999500000); + transfer->set_value(std::string(value.begin(), value.end())); + transfer->set_asset_id(1984); + + input.set_spec_version(1002000); // breaking change happens at version 1002005 + auto result = Polkadot::Extrinsic(input).encodePayload(); + EXPECT_EQ(hex(result), "3205011f00a4b558a0342ae6e379a7ed00d23ff505f1101646cb279844496ad608943eda0d82a34cee00000000104a0f0000000000"); + + input.set_spec_version(1002005); // >= 1002005 + result = Polkadot::Extrinsic(input).encodePayload(); + EXPECT_EQ(hex(result), "3205011f00a4b558a0342ae6e379a7ed00d23ff505f1101646cb279844496ad608943eda0d82a34cee0000000000154a0f000000000000"); + + input.set_spec_version(1002006); // >= 1002005 + result = Polkadot::Extrinsic(input).encodePayload(); + EXPECT_EQ(hex(result), "3205011f00a4b558a0342ae6e379a7ed00d23ff505f1101646cb279844496ad608943eda0d82a34cee0000000000164a0f000000000000"); +} + + } // namespace TW::Polkadot::tests diff --git a/tools/install-sys-dependencies-linux b/tools/install-sys-dependencies-linux index eec6524cd06..2f1e7db8e44 100755 --- a/tools/install-sys-dependencies-linux +++ b/tools/install-sys-dependencies-linux @@ -6,7 +6,7 @@ set -e if [[ "$1" == "ci" ]]; then LIBSTD_PACKAGE_VERSION="12.3.0-1ubuntu1~22.04" # Bump this version if the CI has been broken due to the packages update. - LIBC_PACKAGE_VERSION="2.35-0ubuntu3.7" + LIBC_PACKAGE_VERSION="2.35-0ubuntu3.8" echo "Remove GCC 13 from runner image - runner-images/8659 workaround" echo "NOTE: Bump $LIBC_PACKAGE_VERSION version if the CI has been broken due to the packages update" From 94116a24445c2052edbc7203baf68296c68ce8f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20=22Doom=22=20Doumergue?= Date: Fri, 14 Jun 2024 18:29:13 +0200 Subject: [PATCH 096/128] Update Polkadot signature encoding to support spec 1002005 (#3897) * Update Polkadot signature encoding to support spec 1002005 * Fix encoding order * Add a test for signing Kusama transaction with new spec * Move mode back after feeAssetId --- src/Polkadot/Extrinsic.cpp | 17 +++++++++--- tests/chains/Polkadot/SignerTests.cpp | 39 +++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/Polkadot/Extrinsic.cpp b/src/Polkadot/Extrinsic.cpp index 8ec0b509c6e..b88e5e9a843 100644 --- a/src/Polkadot/Extrinsic.cpp +++ b/src/Polkadot/Extrinsic.cpp @@ -407,8 +407,8 @@ Data Extrinsic::encodePayload() const { } if (use_new_spec) { - // mode (currently always 0) - data.push_back(0x00); + // mode (currently always 0) + data.push_back(0x00); } // specVersion @@ -421,14 +421,16 @@ Data Extrinsic::encodePayload() const { append(data, blockHash); if (use_new_spec) { - // empty metadata hash - data.push_back(0x00); + // empty metadata hash + data.push_back(0x00); } return data; } Data Extrinsic::encodeSignature(const PublicKey& signer, const Data& signature) const { Data data; + auto use_new_spec = requires_new_spec_compatbility(network, specVersion); + // version header append(data, Data{extrinsicFormat | signedBit}); // signer public key @@ -439,10 +441,17 @@ Data Extrinsic::encodeSignature(const PublicKey& signer, const Data& signature) append(data, signature); // era / nonce / tip append(data, encodeEraNonceTip()); + // fee asset id if (!feeAssetId.empty()) { append(data, feeAssetId); } + + if (use_new_spec) { + // mode (currently always 0) + data.push_back(0x00); + } + // call append(data, call); // append length diff --git a/tests/chains/Polkadot/SignerTests.cpp b/tests/chains/Polkadot/SignerTests.cpp index ca1631603be..7e712160f51 100644 --- a/tests/chains/Polkadot/SignerTests.cpp +++ b/tests/chains/Polkadot/SignerTests.cpp @@ -654,4 +654,43 @@ TEST(PolkadotSigner, Kusama_SignBond_NoController) { ASSERT_EQ(hex(output.encoded()), "c101840088dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee00bc4d7a166bd1e7e2bfe9b53e81239c9e340d5a326f17c0a3d2768fcc127f20f4f85d888ecb90aa3ed9a0943f8ae8116b9a19747e563c8d8151dfe3b1b5deb40ca5020c0006000700b08ef01b02"); } +TEST(PolkadotSigner, SignTransfer_KusamaNewSpec) { + auto toAddress = Address("DAbYHrSQTULYZsuA1kvH2cQ33oBsCxxSRPM1XkhzGLeJuHG", ss58Prefix(TWCoinTypeKusama)); + + auto input = Proto::SigningInput(); + auto genesisHashData = parse_hex("0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe"); + input.set_genesis_hash(genesisHashData.data(), genesisHashData.size()); + auto blockHashData = parse_hex("0x0c731c2b7f5332749432eae61cd5a919592965b28181cf9b73b0a1258ea73303"); + input.set_block_hash(blockHashData.data(), blockHashData.size()); + input.set_nonce(150); + input.set_spec_version(1002005); + { + PublicKey publicKey = privateKeyThrow2.getPublicKey(TWPublicKeyTypeED25519); + Address address = Address(publicKey); + EXPECT_EQ(address.string(), addressThrow2); + } + input.set_private_key(privateKeyThrow2.bytes.data(), privateKeyThrow2.bytes.size()); + input.set_network(ss58Prefix(TWCoinTypeKusama)); + input.set_transaction_version(26); + + // era: for blockhash and block number, use curl -H "Content-Type: application/json" -H "Accept: text/plain" https:///transaction/material?noMeta=true + auto era = input.mutable_era(); + era->set_block_number(23610713); + era->set_period(64); + + auto balanceCall = input.mutable_balance_call(); + auto transfer = balanceCall->mutable_transfer(); + auto value = store(uint256_t(2000000000)); // 0.2 + transfer->set_to_address(toAddress.string()); + transfer->set_value(value.data(), value.size()); + + auto extrinsic = Extrinsic(input); + auto preimage = extrinsic.encodePayload(); + EXPECT_EQ(hex(preimage), "0400001a2447c661c9b168bba4a2a178baef7d79eee006c1d145ffc832be76ff6ee9ce0300943577950159020000154a0f001a000000b0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe0c731c2b7f5332749432eae61cd5a919592965b28181cf9b73b0a1258ea7330300"); + + auto output = Signer::sign(input); + EXPECT_EQ(hex(output.encoded()), "450284009dca538b7a925b8ea979cc546464a3c5f81d2398a3a272f6f93bdf4803f2f78300fc5a463d3b6972ac7e0b701110f9d95d377be5b6a2f356765553104c04765fc0066c235c11dabde650d487760dc310003d607abceaf85a0a0f47f1a90e3680029501590200000400001a2447c661c9b168bba4a2a178baef7d79eee006c1d145ffc832be76ff6ee9ce0300943577"); +} + + } // namespace TW::Polkadot::tests From be0058f30fbd0734da7d34fbc1038969590554aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Jun 2024 13:27:01 +0200 Subject: [PATCH 097/128] Bump curve25519-dalek from 4.1.2 to 4.1.3 in /rust (#3906) Bumps [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek) from 4.1.2 to 4.1.3. - [Release notes](https://github.com/dalek-cryptography/curve25519-dalek/releases) - [Commits](https://github.com/dalek-cryptography/curve25519-dalek/compare/curve25519-4.1.2...curve25519-4.1.3) --- updated-dependencies: - dependency-name: curve25519-dalek dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- rust/Cargo.lock | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index ed95ae6b7a0..38a72095cd9 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -426,16 +426,15 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.2" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", "digest 0.10.6", "fiat-crypto", - "platforms", "rustc_version", "subtle", "zeroize", @@ -1016,12 +1015,6 @@ dependencies = [ "spki", ] -[[package]] -name = "platforms" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" - [[package]] name = "ppv-lite86" version = "0.2.17" From 13ff3423735a2fd9cd0fc366d63313ec7bedea05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Jun 2024 13:27:23 +0200 Subject: [PATCH 098/128] Bump braces from 3.0.2 to 3.0.3 in /wasm (#3908) Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3. - [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3) --- updated-dependencies: - dependency-name: braces dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> --- wasm/package-lock.json | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/wasm/package-lock.json b/wasm/package-lock.json index d9115d32d77..bd207c0436d 100644 --- a/wasm/package-lock.json +++ b/wasm/package-lock.json @@ -284,12 +284,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -544,9 +544,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -1560,12 +1560,12 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "browser-stdout": { @@ -1743,9 +1743,9 @@ "dev": true }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" From feafc275fc8affc992f1d9677e04c3b81fc2fe3d Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Mon, 1 Jul 2024 11:51:24 +0200 Subject: [PATCH 099/128] [TON]: Add functions to help to generate Jetton user address (#3922) * [TON]: Add functions to help to generate Jetton user address * [TON]: Rename `TONAddress` module to `TONAddressConverter` * Add ios, android tests * [CI] Trigger CI * [TON]: Fix Android test --- .../TestTheOpenNetworkAddress.kt | 15 ++ .../TrustWalletCore/TWTONAddressConverter.h | 34 ++++ rust/Cargo.lock | 10 ++ rust/tw_memory/src/ffi/c_result.rs | 7 + rust/wallet_core_rs/Cargo.toml | 2 + .../src/ffi/utils/bit_reader_ffi.rs | 161 ++++++++++++++++++ rust/wallet_core_rs/src/ffi/utils/mod.rs | 1 + rust/wallet_core_rs/tests/bit_reader.rs | 69 ++++++++ src/Everscale/CommonTON/BitReader.cpp | 39 +++++ src/Everscale/CommonTON/BitReader.h | 31 ++++ src/Everscale/CommonTON/Cell.cpp | 48 ++++++ src/Everscale/CommonTON/Cell.h | 4 + src/TheOpenNetwork/Address.cpp | 26 +++ src/TheOpenNetwork/Address.h | 6 + src/interface/TWTONAddressConverter.cpp | 40 +++++ src/rust/Wrapper.h | 15 ++ .../Blockchains/TheOpenNetworkTests.swift | 14 ++ tests/chains/TheOpenNetwork/AddressTests.cpp | 69 ++++++++ 18 files changed, 591 insertions(+) create mode 100644 include/TrustWalletCore/TWTONAddressConverter.h create mode 100644 rust/wallet_core_rs/src/ffi/utils/bit_reader_ffi.rs create mode 100644 rust/wallet_core_rs/tests/bit_reader.rs create mode 100644 src/Everscale/CommonTON/BitReader.cpp create mode 100644 src/Everscale/CommonTON/BitReader.h create mode 100644 src/interface/TWTONAddressConverter.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkAddress.kt index c96c5ebb21a..66433c2e0a8 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkAddress.kt @@ -45,4 +45,19 @@ class TestTheOpenNetworkAddress { val address = AnyAddress(addressString, CoinType.TON) assertEquals(address.description(), "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") } + + @Test + fun testGenerateJettonAddress() { + val mainAddress = "UQBjKqthWBE6GEcqb_epTRFrQ1niS6Z1Z1MHMwR-mnAYRoYr" + val mainAddressBoc = TONAddressConverter.toBoc(mainAddress) + assertEquals(mainAddressBoc, "te6ccgICAAEAAQAAACQAAABDgAxlVWwrAidDCOVN/vUpoi1oazxJdM6s6mDmYI/TTgMI0A==") + + // curl --location 'https://toncenter.com/api/v2/runGetMethod' --header 'Content-Type: application/json' --data \ + // '{"address":"EQAvlWFDxGF2lXm67y4yzC17wYKD9A0guwPkMs1gOsM__NOT","method":"get_wallet_address","method":"get_wallet_address","stack":[["tvm.Slice","te6ccgICAAEAAQAAACQAAABDgAxlVWwrAidDCOVN/vUpoi1oazxJdM6s6mDmYI/TTgMI0A=="]]}' + + // Parse the `get_wallet_address` RPC response. + val jettonAddressBocEncoded = "te6cckEBAQEAJAAAQ4AFvT5rqwxcbKfITqnkwL+go4Zi9bulRHAtLt4cjjFdK7B8L+Cq" + val jettonAddress = TONAddressConverter.fromBoc(jettonAddressBocEncoded) + assertEquals(jettonAddress, "UQAt6fNdWGLjZT5CdU8mBf0FHDMXrd0qI4FpdvDkcYrpXV5H") + } } diff --git a/include/TrustWalletCore/TWTONAddressConverter.h b/include/TrustWalletCore/TWTONAddressConverter.h new file mode 100644 index 00000000000..55dfcaff58a --- /dev/null +++ b/include/TrustWalletCore/TWTONAddressConverter.h @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#pragma once + +#include "TWBase.h" +#include "TWString.h" + +TW_EXTERN_C_BEGIN + +/// TON address operations. +TW_EXPORT_CLASS +struct TWTONAddressConverter; + +/// Converts a TON user address into a Bag of Cells (BoC) with a single root Cell. +/// The function is mostly used to request a Jetton user address via `get_wallet_address` RPC. +/// https://docs.ton.org/develop/dapps/asset-processing/jettons#retrieving-jetton-wallet-addresses-for-a-given-user +/// +/// \param address Address to be converted into a Bag Of Cells (BoC). +/// \return Pointer to a base64 encoded Bag Of Cells (BoC). Null if invalid address provided. +TW_EXPORT_STATIC_METHOD +TWString *_Nullable TWTONAddressConverterToBoc(TWString *_Nonnull address); + +/// Parses a TON address from a Bag of Cells (BoC) with a single root Cell. +/// The function is mostly used to parse a Jetton user address received on `get_wallet_address` RPC. +/// https://docs.ton.org/develop/dapps/asset-processing/jettons#retrieving-jetton-wallet-addresses-for-a-given-user +/// +/// \param boc Base64 encoded Bag Of Cells (BoC). +/// \return Pointer to a Jetton address. +TW_EXPORT_STATIC_METHOD +TWString *_Nullable TWTONAddressConverterFromBoc(TWString *_Nonnull boc); + +TW_EXTERN_C_END diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 38a72095cd9..73378992bdb 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -211,6 +211,15 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitreader" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd859c9d97f7c468252795b35aeccc412bdbb1e90ee6969c4fa6328272eaeff" +dependencies = [ + "cfg-if", +] + [[package]] name = "bitvec" version = "0.20.4" @@ -2107,6 +2116,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wallet-core-rs" version = "0.1.0" dependencies = [ + "bitreader", "serde_json", "tw_any_coin", "tw_bitcoin", diff --git a/rust/tw_memory/src/ffi/c_result.rs b/rust/tw_memory/src/ffi/c_result.rs index fe6ed25bc32..a1db2322139 100644 --- a/rust/tw_memory/src/ffi/c_result.rs +++ b/rust/tw_memory/src/ffi/c_result.rs @@ -95,6 +95,12 @@ pub struct CBoolResult { pub result: bool, } +#[repr(C)] +pub struct CUInt8Result { + pub code: i32, + pub result: u8, +} + #[repr(C)] pub struct CUInt64Result { pub code: i32, @@ -104,4 +110,5 @@ pub struct CUInt64Result { impl_c_result!(CStrResult, *const c_char, core::ptr::null()); impl_c_result!(CStrMutResult, *mut c_char, core::ptr::null_mut()); impl_c_result!(CBoolResult, bool, false); +impl_c_result!(CUInt8Result, u8, 0); impl_c_result!(CUInt64Result, u64, 0); diff --git a/rust/wallet_core_rs/Cargo.toml b/rust/wallet_core_rs/Cargo.toml index eb148ecab18..3fb32b403a2 100644 --- a/rust/wallet_core_rs/Cargo.toml +++ b/rust/wallet_core_rs/Cargo.toml @@ -31,6 +31,7 @@ utils = [ ] [dependencies] +bitreader = "0.3.8" tw_any_coin = { path = "../tw_any_coin", optional = true } tw_bitcoin = { path = "../tw_bitcoin", optional = true } tw_coin_registry = { path = "../tw_coin_registry", optional = true } @@ -49,5 +50,6 @@ uuid = { version = "1.7", features = ["v4"], optional = true } serde_json = "1.0" tw_any_coin = { path = "../tw_any_coin", features = ["test-utils"] } tw_coin_entry = { path = "../tw_coin_entry", features = ["test-utils"] } +tw_encoding = { path = "../tw_encoding" } tw_memory = { path = "../tw_memory", features = ["test-utils"] } tw_number = { path = "../tw_number", features = ["helpers"] } diff --git a/rust/wallet_core_rs/src/ffi/utils/bit_reader_ffi.rs b/rust/wallet_core_rs/src/ffi/utils/bit_reader_ffi.rs new file mode 100644 index 00000000000..e9fcc925635 --- /dev/null +++ b/rust/wallet_core_rs/src/ffi/utils/bit_reader_ffi.rs @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#![allow(clippy::missing_safety_doc)] + +use bitreader::{BitReader, BitReaderError}; +use tw_memory::ffi::c_byte_array::{CByteArray, CByteArrayResult}; +use tw_memory::ffi::c_result::{CUInt8Result, ErrorCode}; +use tw_memory::ffi::tw_data::TWData; +use tw_memory::ffi::RawPtrTrait; +use tw_memory::Data; +use tw_misc::try_or_else; + +#[derive(Debug, Eq, PartialEq)] +#[repr(C)] +pub enum CBitReaderCode { + Ok = 0, + /// Requested more bits than there are left in the byte slice at the current position. + NotEnoughData = 1, + /// Requested more bits than the returned variable can hold, for example more than 8 bits when + /// reading into a u8. + TooManyBitsForType = 2, + InalidInput = 3, +} + +impl From for CBitReaderCode { + fn from(value: BitReaderError) -> Self { + match value { + BitReaderError::NotEnoughData { .. } => CBitReaderCode::NotEnoughData, + BitReaderError::TooManyBitsForType { .. } => CBitReaderCode::TooManyBitsForType, + } + } +} + +impl From for ErrorCode { + fn from(error: CBitReaderCode) -> Self { + error as ErrorCode + } +} + +/// BitReader reads data from a big-endian byte slice at the granularity of a single bit. +#[derive(Debug)] +pub struct TWBitReader { + buffer: Data, + bit_position: u64, + bit_len: u64, +} + +impl TWBitReader { + pub fn with_relative_bit_len(buffer: Data, bit_len: u64) -> TWBitReader { + TWBitReader { + buffer, + bit_position: 0, + bit_len, + } + } + + /// Read at most 8 bits into a u8. + pub fn read_u8(&mut self, bit_count: u8) -> Result { + let mut reader = self.make_reader()?; + let res = reader.read_u8(bit_count)?; + // Update the bit position in case of success read. + self.bit_position += bit_count as u64; + Ok(res) + } + + // Reads an entire slice of `byte_count` bytes. If there aren't enough bits remaining + // after the internal cursor's current position, returns none. + pub fn read_u8_slice(&mut self, byte_count: usize) -> Result { + let mut reader = self.make_reader()?; + + let mut res = vec![0_u8; byte_count]; + reader.read_u8_slice(&mut res)?; + + // Update the bit position in case of success read. + self.bit_position += byte_count as u64 * 8; + Ok(res) + } + + pub fn is_finished(&self) -> bool { + self.bit_len == self.bit_position + } + + fn make_reader(&self) -> Result, CBitReaderCode> { + let mut reader = BitReader::new(&self.buffer).relative_reader_atmost(self.bit_len); + reader.skip(self.bit_position)?; + Ok(reader) + } +} + +impl RawPtrTrait for TWBitReader {} + +/// Constructs a new `TWBitReader` from a big-endian byte slice +/// that will not allow reading more than `bit_len` bits. It must be deleted at the end. +/// +/// \param data big-endian byte slice to be read. +/// \param bit_len length this reader is allowed to read from the slice. +/// \return nullable pointer to a `TWBitReader` instance. +#[no_mangle] +pub unsafe extern "C" fn tw_bit_reader_create( + data: *const TWData, + bit_len: u64, +) -> *mut TWBitReader { + let data = try_or_else!(TWData::from_ptr_as_ref(data), std::ptr::null_mut); + TWBitReader::with_relative_bit_len(data.to_vec(), bit_len).into_ptr() +} + +/// Deletes a `TWBitReader` and frees the memory. +/// \param reader a `TWBitReader` pointer. +#[no_mangle] +pub unsafe extern "C" fn tw_bit_reader_delete(reader: *mut TWBitReader) { + // Take the ownership back to rust and drop the owner. + let _ = TWBitReader::from_ptr(reader); +} + +/// Read at most 8 bits into a u8. +/// +/// \param reader a `TWBitReader` pointer. +/// \param bit_count number of bits to read. Expected from 1 to 8. +/// \return u8 or error. +#[no_mangle] +pub unsafe extern "C" fn tw_bit_reader_read_u8( + reader: *mut TWBitReader, + bit_count: u8, +) -> CUInt8Result { + let tw_reader = try_or_else!( + TWBitReader::from_ptr_as_mut(reader), + || CUInt8Result::error(CBitReaderCode::InalidInput) + ); + tw_reader.read_u8(bit_count).into() +} + +/// Reads an entire slice of `byteCount` bytes. If there aren't enough bits remaining +/// after the internal cursor's current position, returns null. +/// +/// \param reader a `TWBitReader` pointer. +/// \param byte_count number of bytes to read. +/// \return byte array or error. +#[no_mangle] +pub unsafe extern "C" fn tw_bit_reader_read_u8_slice( + reader: *mut TWBitReader, + byte_count: usize, +) -> CByteArrayResult { + let tw_reader = try_or_else!(TWBitReader::from_ptr_as_mut(reader), || { + CByteArrayResult::error(CBitReaderCode::InalidInput) + }); + tw_reader + .read_u8_slice(byte_count) + .map(CByteArray::from) + .into() +} + +/// Checks whether all bits were read. +/// +/// \param reader a `TWBitReader` pointer. +/// \return whether all bits were read. +#[no_mangle] +pub unsafe extern "C" fn tw_bit_reader_finished(reader: *const TWBitReader) -> bool { + try_or_else!(TWBitReader::from_ptr_as_ref(reader), || true).is_finished() +} diff --git a/rust/wallet_core_rs/src/ffi/utils/mod.rs b/rust/wallet_core_rs/src/ffi/utils/mod.rs index f233b194cb3..a6b8ac45aec 100644 --- a/rust/wallet_core_rs/src/ffi/utils/mod.rs +++ b/rust/wallet_core_rs/src/ffi/utils/mod.rs @@ -2,4 +2,5 @@ // // Copyright © 2017 Trust Wallet. +pub mod bit_reader_ffi; pub mod uuid_ffi; diff --git a/rust/wallet_core_rs/tests/bit_reader.rs b/rust/wallet_core_rs/tests/bit_reader.rs new file mode 100644 index 00000000000..ceb7d5fc5ba --- /dev/null +++ b/rust/wallet_core_rs/tests/bit_reader.rs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use wallet_core_rs::ffi::utils::bit_reader_ffi::{ + tw_bit_reader_create, tw_bit_reader_finished, tw_bit_reader_read_u8, + tw_bit_reader_read_u8_slice, CBitReaderCode, +}; + +#[test] +fn test_tw_bit_reader_success() { + let ton_address_cell = "8005bd3e6bab0c5c6ca7c84ea9e4c0bfa0a38662f5bba544702d2ede1c8e315d2ba0" + .decode_hex() + .unwrap(); + let ton_address_cell = TWDataHelper::create(ton_address_cell); + + let reader = unsafe { tw_bit_reader_create(ton_address_cell.ptr(), 267) }; + assert!(!reader.is_null()); + + let tp = unsafe { tw_bit_reader_read_u8(reader, 2) }; + assert_eq!(tp.into_result(), Ok(2)); + + let res1 = unsafe { tw_bit_reader_read_u8(reader, 1) }; + assert_eq!(res1.into_result(), Ok(0)); + + let wc = unsafe { tw_bit_reader_read_u8(reader, 8) }; + assert_eq!(wc.into_result(), Ok(0)); + + assert!(!unsafe { tw_bit_reader_finished(reader) }); + + let hash_part = unsafe { tw_bit_reader_read_u8_slice(reader, 32).unwrap().into_vec() }; + assert_eq!( + hash_part.to_hex(), + "2de9f35d5862e3653e42754f2605fd051c3317addd2a23816976f0e4718ae95d" + ); + + assert!(unsafe { tw_bit_reader_finished(reader) }); +} + +#[test] +fn test_tw_bit_reader_error() { + let bytes_len = 2; + // Less than two bytes. + let bits_len = 15; + + let data = TWDataHelper::create(vec![1; bytes_len]); + + let reader = unsafe { tw_bit_reader_create(data.ptr(), bits_len as u64) }; + assert!(!reader.is_null()); + + // Cannot read u8 from 9 bits. + let res = unsafe { tw_bit_reader_read_u8(reader, 9) }; + assert_eq!( + res.into_result().unwrap_err(), + CBitReaderCode::TooManyBitsForType as i32 + ); + + // Read a dummy u8. + let _ = unsafe { tw_bit_reader_read_u8_slice(reader, 8) }; + + // Cannot read 8 bits as there are 7 bits left only. + let res = unsafe { tw_bit_reader_read_u8_slice(reader, 8) }; + assert_eq!( + res.into_result().unwrap_err(), + CBitReaderCode::NotEnoughData as i32 + ); +} diff --git a/src/Everscale/CommonTON/BitReader.cpp b/src/Everscale/CommonTON/BitReader.cpp new file mode 100644 index 00000000000..44220144801 --- /dev/null +++ b/src/Everscale/CommonTON/BitReader.cpp @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#include "BitReader.h" + +namespace TW::CommonTON { + +std::optional BitReader::createExact(const TW::Data &buffer, uint64_t bitLen) { + Rust::TWDataWrapper twData(buffer); + auto *readerPtr = Rust::tw_bit_reader_create(twData.get(), bitLen); + if (!readerPtr) { + return std::nullopt; + } + + return BitReader(std::shared_ptr(readerPtr, Rust::tw_bit_reader_delete)); +} + +std::optional BitReader::readU8(uint8_t bitCount) { + Rust::CUInt8ResultWrapper res = Rust::tw_bit_reader_read_u8(reader.get(), bitCount); + if (res.isErr()) { + return std::nullopt; + } + return res.unwrap().value; +} + +std::optional BitReader::readU8Slice(uint64_t byteCount) { + Rust::CByteArrayResultWrapper res = Rust::tw_bit_reader_read_u8_slice(reader.get(), byteCount); + if (res.isErr()) { + return std::nullopt; + } + return res.unwrap().data; +} + +bool BitReader::finished() const { + return Rust::tw_bit_reader_finished(reader.get()); +} + +} // namespace TW::CommonTON diff --git a/src/Everscale/CommonTON/BitReader.h b/src/Everscale/CommonTON/BitReader.h new file mode 100644 index 00000000000..51177f25b09 --- /dev/null +++ b/src/Everscale/CommonTON/BitReader.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#pragma once + +#include "rust/Wrapper.h" + +namespace TW::CommonTON { + +class BitReader { +public: + // Tries to create a bit reader with exact `bitLen` number of bits. + static std::optional createExact(const Data& buffer, uint64_t bitLen); + + // Read at most 8 bits into a u8. + std::optional readU8(uint8_t bitCount); + + // Reads an entire slice of `byteCount` bytes. If there aren't enough bits remaining + // after the internal cursor's current position, returns none. + std::optional readU8Slice(uint64_t byteCount); + + bool finished() const; + +private: + explicit BitReader(std::shared_ptr reader): reader(std::move(reader)) {} + + std::shared_ptr reader; +}; + +} // namespace TW::CommonTON diff --git a/src/Everscale/CommonTON/Cell.cpp b/src/Everscale/CommonTON/Cell.cpp index 8329c299713..b3be8774405 100644 --- a/src/Everscale/CommonTON/Cell.cpp +++ b/src/Everscale/CommonTON/Cell.cpp @@ -12,6 +12,7 @@ #include "Base64.h" #include "BinaryCoding.h" +#include "BitReader.h" using namespace TW; @@ -390,4 +391,51 @@ void Cell::finalize() { finalized = true; } +std::optional Cell::parseAddress() const { + auto reader = BitReader::createExact(data, static_cast(bitLen)); + if (!reader) { + return std::nullopt; + } + + auto tp = reader->readU8(2); + if (!tp) { + return std::nullopt; + } + + if (tp.value() == 0) { + // Hole address (default). Check if the Cell does not contain more bits. + if (!reader->finished()) { + return std::nullopt; + } + return AddressData(); + } + + // We expect type=0 or type=2 addresses only. + if (tp.value() != 2) { + return std::nullopt; + } + + // Ignore res1 value. + reader->readU8(1); + + auto workchain = reader->readU8(8); + if (!workchain) { + return std::nullopt; + } + + auto hashPart = reader->readU8Slice(AddressData::size); + if (!hashPart) { + return std::nullopt; + } + + if (!reader->finished()) { + return std::nullopt; + } + + std::array parsedHash {}; + std::copy(begin(hashPart.value()), end(hashPart.value()), begin(parsedHash)); + + return AddressData(static_cast(workchain.value()), parsedHash); +} + } // namespace TW::CommonTON diff --git a/src/Everscale/CommonTON/Cell.h b/src/Everscale/CommonTON/Cell.h index aaa03d67a9d..2341fd778db 100644 --- a/src/Everscale/CommonTON/Cell.h +++ b/src/Everscale/CommonTON/Cell.h @@ -10,6 +10,7 @@ #include "Data.h" #include "Hash.h" +#include "RawAddress.h" namespace TW::CommonTON { @@ -57,6 +58,9 @@ class Cell { [[nodiscard]] inline size_t serializedSize(uint8_t refSize) const noexcept { return 2 + (bitLen + 7) / 8 + refCount * refSize; } + + // Tries to parse an address from the Cell. + std::optional parseAddress() const; }; } // namespace TW::CommonTON diff --git a/src/TheOpenNetwork/Address.cpp b/src/TheOpenNetwork/Address.cpp index d8f0a194455..eb25f25aa2d 100644 --- a/src/TheOpenNetwork/Address.cpp +++ b/src/TheOpenNetwork/Address.cpp @@ -6,6 +6,7 @@ #include "Base64.h" #include "Crc.h" +#include "Everscale/CommonTON/CellBuilder.h" #include "WorkchainType.h" @@ -124,4 +125,29 @@ std::string Address::string(bool userFriendly, bool bounceable, bool testOnly) return Base64::encodeBase64Url(data); } +std::string Address::toBoc() const { + CommonTON::CellBuilder cellBuilder; + cellBuilder.appendAddress(addressData); + const auto cell = cellBuilder.intoCell(); + + Data bocData; + cell->serialize(bocData); + + return Base64::encode(bocData); +} + +std::optional
Address::fromBoc(const std::string& bocEncoded) { + const auto cell = CommonTON::Cell::fromBase64(bocEncoded); + if (!cell) { + return std::nullopt; + } + + const auto addressData = cell->parseAddress(); + if (!addressData) { + return std::nullopt; + } + + return std::make_optional
(addressData->workchainId, addressData->hash); +} + } // namespace TW::TheOpenNetwork diff --git a/src/TheOpenNetwork/Address.h b/src/TheOpenNetwork/Address.h index f830de83877..fe938a8f7b5 100644 --- a/src/TheOpenNetwork/Address.h +++ b/src/TheOpenNetwork/Address.h @@ -57,6 +57,12 @@ class Address { /// Returns a string representation of the address. [[nodiscard]] std::string string() const; [[nodiscard]] std::string string(bool userFriendly, bool bounceable = true, bool testOnly = false) const; + + // Converts a TON user address into a Bag of Cells (BoC) with a single root Cell. + [[nodiscard]] std::string toBoc() const; + + // Parses a TON address from a Bag of Cells (BoC) with a single root Cell. + [[nodiscard]] static std::optional
fromBoc(const std::string& bocEncoded); }; } // namespace TW::TheOpenNetwork diff --git a/src/interface/TWTONAddressConverter.cpp b/src/interface/TWTONAddressConverter.cpp new file mode 100644 index 00000000000..8da3613f8b0 --- /dev/null +++ b/src/interface/TWTONAddressConverter.cpp @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#include + +#include "Base64.h" +#include "TheOpenNetwork/Address.h" + +using namespace TW; + +TWString *_Nullable TWTONAddressConverterToBoc(TWString *_Nonnull address) { + auto& addressString = *reinterpret_cast(address); + if (!TheOpenNetwork::Address::isValid(addressString)) { + return nullptr; + } + + const TheOpenNetwork::Address addressTon(addressString); + auto bocEncoded = addressTon.toBoc(); + return TWStringCreateWithUTF8Bytes(bocEncoded.c_str()); +} + +TWString *_Nullable TWTONAddressConverterFromBoc(TWString *_Nonnull boc) { + auto& bocEncoded = *reinterpret_cast(boc); + + try { + auto address = TheOpenNetwork::Address::fromBoc(bocEncoded); + if (!address) { + return nullptr; + } + + auto userFriendly = true; + auto bounceable = false; + auto addressStr = address->string(userFriendly, bounceable); + + return TWStringCreateWithUTF8Bytes(addressStr.c_str()); + } catch (...) { + return nullptr; + } +} diff --git a/src/rust/Wrapper.h b/src/rust/Wrapper.h index 6c730e99782..bb3feb3dddf 100644 --- a/src/rust/Wrapper.h +++ b/src/rust/Wrapper.h @@ -153,6 +153,20 @@ struct CStringWrapper { std::string str; }; +struct CUInt8Wrapper { + /// Implicit move constructor. + CUInt8Wrapper(uint8_t c_u8) { + *this = c_u8; + } + + CUInt8Wrapper& operator=(uint8_t c_u8) { + value = c_u8; + return *this; + } + + uint8_t value; +}; + struct CUInt64Wrapper { /// Implicit move constructor. CUInt64Wrapper(uint64_t c_u64) { @@ -214,6 +228,7 @@ class CResult { }; using CByteArrayResultWrapper = CResult; +using CUInt8ResultWrapper = CResult; using CUInt64ResultWrapper = CResult; } // namespace TW::Rust diff --git a/swift/Tests/Blockchains/TheOpenNetworkTests.swift b/swift/Tests/Blockchains/TheOpenNetworkTests.swift index d0265404742..2a64fc9204b 100644 --- a/swift/Tests/Blockchains/TheOpenNetworkTests.swift +++ b/swift/Tests/Blockchains/TheOpenNetworkTests.swift @@ -33,6 +33,20 @@ class TheOpenNetworkTests: XCTestCase { XCTAssertEqual(address!.description, "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") } + func testGenerateJettonAddress() { + let mainAddress = "UQBjKqthWBE6GEcqb_epTRFrQ1niS6Z1Z1MHMwR-mnAYRoYr" + let mainAddressBoc = TONAddressConverter.toBoc(address: mainAddress) + XCTAssertEqual(mainAddressBoc, "te6ccgICAAEAAQAAACQAAABDgAxlVWwrAidDCOVN/vUpoi1oazxJdM6s6mDmYI/TTgMI0A==") + + // curl --location 'https://toncenter.com/api/v2/runGetMethod' --header 'Content-Type: application/json' --data \ + // '{"address":"EQAvlWFDxGF2lXm67y4yzC17wYKD9A0guwPkMs1gOsM__NOT","method":"get_wallet_address","method":"get_wallet_address","stack":[["tvm.Slice","te6ccgICAAEAAQAAACQAAABDgAxlVWwrAidDCOVN/vUpoi1oazxJdM6s6mDmYI/TTgMI0A=="]]}' + + // Parse the `get_wallet_address` RPC response. + let jettonAddressBocEncoded = "te6cckEBAQEAJAAAQ4AFvT5rqwxcbKfITqnkwL+go4Zi9bulRHAtLt4cjjFdK7B8L+Cq" + let jettonAddress = TONAddressConverter.fromBoc(boc: jettonAddressBocEncoded) + XCTAssertEqual(jettonAddress, "UQAt6fNdWGLjZT5CdU8mBf0FHDMXrd0qI4FpdvDkcYrpXV5H") + } + func testSign() { let privateKeyData = Data(hexString: "c38f49de2fb13223a9e7d37d5d0ffbdd89a5eb7c8b0ee4d1c299f2cefe7dc4a0")! diff --git a/tests/chains/TheOpenNetwork/AddressTests.cpp b/tests/chains/TheOpenNetwork/AddressTests.cpp index 297fe113531..a46d2d53fd6 100644 --- a/tests/chains/TheOpenNetwork/AddressTests.cpp +++ b/tests/chains/TheOpenNetwork/AddressTests.cpp @@ -5,11 +5,14 @@ #include "HexCoding.h" #include "PublicKey.h" #include "PrivateKey.h" +#include "TestUtilities.h" #include "TheOpenNetwork/Address.h" #include "TheOpenNetwork/wallet/WalletV4R2.h" #include "TheOpenNetwork/WorkchainType.h" +#include "TrustWalletCore/TWTONAddressConverter.h" + #include namespace TW::TheOpenNetwork::tests { @@ -111,4 +114,70 @@ TEST(TheOpenNetworkAddress, FromPublicKeyV4R2) { ASSERT_EQ(address.string(), "EQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorkdl"); } +TEST(TheOpenNetworkAddress, GetJettonNotcoinAddress) { + auto mainAddress = STRING("UQBjKqthWBE6GEcqb_epTRFrQ1niS6Z1Z1MHMwR-mnAYRoYr"); + auto addressBocEncoded = WRAPS(TWTONAddressConverterToBoc(mainAddress.get())); + assertStringsEqual(addressBocEncoded, "te6ccgICAAEAAQAAACQAAABDgAxlVWwrAidDCOVN/vUpoi1oazxJdM6s6mDmYI/TTgMI0A=="); + + // curl --location 'https://toncenter.com/api/v2/runGetMethod' --header 'Content-Type: application/json' --data \ + // '{"address":"EQAvlWFDxGF2lXm67y4yzC17wYKD9A0guwPkMs1gOsM__NOT","method":"get_wallet_address","method":"get_wallet_address","stack":[["tvm.Slice","te6ccgICAAEAAQAAACQAAABDgAxlVWwrAidDCOVN/vUpoi1oazxJdM6s6mDmYI/TTgMI0A=="]]}' + + // `get_wallet_address` response: + auto jettonAddressBocEncoded = STRING("te6cckEBAQEAJAAAQ4AFvT5rqwxcbKfITqnkwL+go4Zi9bulRHAtLt4cjjFdK7B8L+Cq"); + auto jettonAddress = WRAPS(TWTONAddressConverterFromBoc(jettonAddressBocEncoded.get())); + assertStringsEqual(jettonAddress, "UQAt6fNdWGLjZT5CdU8mBf0FHDMXrd0qI4FpdvDkcYrpXV5H"); +} + +TEST(TheOpenNetworkAddress, GetJettonUSDTAddress) { + auto mainAddress = STRING("UQBjKqthWBE6GEcqb_epTRFrQ1niS6Z1Z1MHMwR-mnAYRoYr"); + auto addressBocEncoded = WRAPS(TWTONAddressConverterToBoc(mainAddress.get())); + assertStringsEqual(addressBocEncoded, "te6ccgICAAEAAQAAACQAAABDgAxlVWwrAidDCOVN/vUpoi1oazxJdM6s6mDmYI/TTgMI0A=="); + + // curl --location 'https://toncenter.com/api/v2/runGetMethod' --header 'Content-Type: application/json' --data \ + // '{"address":"EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs","method":"get_wallet_address","method":"get_wallet_address","stack":[["tvm.Slice","te6ccgICAAEAAQAAACQAAABDgAxlVWwrAidDCOVN/vUpoi1oazxJdM6s6mDmYI/TTgMI0A=="]]}' + + // `get_wallet_address` response: + auto jettonAddressBocEncoded = STRING("te6cckEBAQEAJAAAQ4Aed71FEI46jdFXghsGUIG2GIR8wpbQaLzrKNj7BtHOEHBSO5Mf"); + auto jettonAddress = WRAPS(TWTONAddressConverterFromBoc(jettonAddressBocEncoded.get())); + assertStringsEqual(jettonAddress, "UQDzveoohHHUboq8ENgyhA2wxCPmFLaDRedZRsfYNo5wg4TL"); +} + +TEST(TheOpenNetworkAddress, GetJettonStonAddress) { + auto mainAddress = STRING("EQATQPeCwtMzQ9u54nTjUNcK4n_0VRSxPOOROLf_IE0OU3XK"); + auto addressBocEncoded = WRAPS(TWTONAddressConverterToBoc(mainAddress.get())); + assertStringsEqual(addressBocEncoded, "te6ccgICAAEAAQAAACQAAABDgAJoHvBYWmZoe3c8TpxqGuFcT/6KopYnnHInFv/kCaHKcA=="); + + // curl --location 'https://toncenter.com/api/v2/runGetMethod' --header 'Content-Type: application/json' --data \ + // '{"address":"EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO","method":"get_wallet_address","method":"get_wallet_address","stack":[["tvm.Slice","te6ccgICAAEAAQAAACQAAABDgAxlVWwrAidDCOVN/vUpoi1oazxJdM6s6mDmYI/TTgMI0A=="]]}' + + // `get_wallet_address` response: + auto jettonAddressBocEncoded = STRING("te6cckEBAQEAJAAAQ4ALPu0dyA1gHd3r7J1rxlvhXSvT5y3rokMDMiCQ86TsUJDnt69H"); + auto jettonAddress = WRAPS(TWTONAddressConverterFromBoc(jettonAddressBocEncoded.get())); + assertStringsEqual(jettonAddress, "UQBZ92juQGsA7u9fZOteMt8K6V6fOW9dEhgZkQSHnSdihHPH"); +} + +TEST(TheOpenNetworkAddress, FromBocNullAddress) { + auto jettonAddressBocEncoded = STRING("te6cckEBAQEAAwAAASCUQYZV"); + auto jettonAddress = WRAPS(TWTONAddressConverterFromBoc(jettonAddressBocEncoded.get())); + assertStringsEqual(jettonAddress, "UQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKZ"); +} + +TEST(TheOpenNetworkAddress, FromBocError) { + // No type bit. + auto boc1 = STRING("te6cckEBAQEAAwAAAcCO6ba2"); + ASSERT_EQ(TWTONAddressConverterFromBoc(boc1.get()), nullptr); + + // No res1 and workchain bits. + auto boc2 = STRING("te6cckEBAQEAAwAAAaDsenDX"); + ASSERT_EQ(TWTONAddressConverterFromBoc(boc2.get()), nullptr); + + // Incomplete hash (31 bytes instead of 32). + auto boc3 = STRING("te6cckEBAQEAIwAAQYAgQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAUGJnJWk="); + ASSERT_EQ(TWTONAddressConverterFromBoc(boc3.get()), nullptr); + + // Expected 267 bits, found 268. + auto boc4 = STRING("te6cckEBAQEAJAAAQ4AgQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEgGG0Gq"); + ASSERT_EQ(TWTONAddressConverterFromBoc(boc4.get()), nullptr); +} + } // namespace TW::TheOpenNetwork::tests From 6ee73c8c345f38996f18bee9f7ad9d587c860fff Mon Sep 17 00:00:00 2001 From: Jaime Toca Date: Thu, 4 Jul 2024 08:56:33 +0200 Subject: [PATCH 100/128] Enable Tonviewer explorer (#3926) --- registry.json | 6 +++--- tests/chains/TheOpenNetwork/SignerTests.cpp | 17 ++++++++--------- tests/chains/TheOpenNetwork/TWCoinTypeTests.cpp | 4 ++-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/registry.json b/registry.json index d1c60580978..f3a8dca671d 100644 --- a/registry.json +++ b/registry.json @@ -4526,9 +4526,9 @@ "curve": "ed25519", "publicKeyType": "ed25519", "explorer": { - "url": "https://tonscan.org", - "txPath": "/tx/", - "accountPath": "/address/", + "url": "https://tonviewer.com", + "txPath": "/transaction/", + "accountPath": "/", "sampleTx": "fJXfn0EVhV09HFuEgUHu4Cchb24nUQtIMwSzmzk2tLs=", "sampleAccount": "EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N" }, diff --git a/tests/chains/TheOpenNetwork/SignerTests.cpp b/tests/chains/TheOpenNetwork/SignerTests.cpp index 796ccb94acb..976affa59ed 100644 --- a/tests/chains/TheOpenNetwork/SignerTests.cpp +++ b/tests/chains/TheOpenNetwork/SignerTests.cpp @@ -29,7 +29,7 @@ TEST(TheOpenNetworkSigner, TransferAndDeploy) { ASSERT_EQ(hex(CommonTON::Cell::fromBase64(output.encoded())->hash), "b3d9462c13a8c67e19b62002447839c386de51415ace3ff6473b1e6294299819"); - // tx: https://tonscan.org/tx/6ZzWOFKZt_m3kZjbwfbATwLaVwmUOdDp0xjhuY7PO3k= + // tx: https://tonviewer.com/transaction/6ZzWOFKZt_m3kZjbwfbATwLaVwmUOdDp0xjhuY7PO3k= ASSERT_EQ(output.encoded(), "te6ccgICABoAAQAAA8sAAAJFiADN98eLgHfrkE8l8gmT8X5REpTVR6QnqDhArTbKlVvbZh4ABAABAZznxvGBhoRXhPogxNY8QmHlihJWxg5t6KptqcAIZlVks1r+Z+r1avCWNCeqeLC/oaiVN4mDx/E1+Zhi33G25rcIKamjF/////8AAAAAAAMAAgFiYgBsLf6vJOEq42xW0AoyWX0K+uBMUcXFDLFqmkDg6k1Io4hQAAAAAAAAAAAAAAAAAQADAAACATQABgAFAFEAAAAAKamjF/Qsd/kxvqIOxdAVBzEna7suKGCUdmEkWyMZ74Ez7o1BQAEU/wD0pBP0vPLICwAHAgEgAA0ACAT48oMI1xgg0x/TH9MfAvgju/Jk7UTQ0x/TH9P/9ATRUUO68qFRUbryogX5AVQQZPkQ8qP4ACSkyMsfUkDLH1Iwy/9SEPQAye1U+A8B0wchwACfbFGTINdKltMH1AL7AOgw4CHAAeMAIcAC4wABwAORMOMNA6TIyx8Syx/L/wAMAAsACgAJAAr0AMntVABsgQEI1xj6ANM/MFIkgQEI9Fnyp4IQZHN0cnB0gBjIywXLAlAFzxZQA/oCE8tqyx8Syz/Jc/sAAHCBAQjXGPoA0z/IVCBHgQEI9FHyp4IQbm90ZXB0gBjIywXLAlAGzxZQBPoCFMtqEssfyz/Jc/sAAgBu0gf6ANTUIvkABcjKBxXL/8nQd3SAGMjLBcsCIs8WUAX6AhTLaxLMzMlz+wDIQBSBAQj0UfKnAgIBSAAXAA4CASAAEAAPAFm9JCtvaiaECAoGuQ+gIYRw1AgIR6STfSmRDOaQPp/5g3gSgBt4EBSJhxWfMYQCASAAEgARABG4yX7UTQ1wsfgCAVgAFgATAgEgABUAFAAZrx32omhAEGuQ64WPwAAZrc52omhAIGuQ64X/wAA9sp37UTQgQFA1yH0BDACyMoHy//J0AGBAQj0Cm+hMYALm0AHQ0wMhcbCSXwTgItdJwSCSXwTgAtMfIYIQcGx1Z70ighBkc3RyvbCSXwXgA/pAMCD6RAHIygfL/8nQ7UTQgQFA1yH0BDBcgQEI9ApvoTGzkl8H4AXTP8glghBwbHVnupI4MOMNA4IQZHN0crqSXwbjDQAZABgAilAEgQEI9Fkw7UTQgQFA1yDIAc8W9ADJ7VQBcrCOI4IQZHN0coMesXCAGFAFywVQA88WI/oCE8tqyx/LP8mAQPsAkl8D4gB4AfoA9AQw+CdvIjBQCqEhvvLgUIIQcGx1Z4MesXCAGFAEywUmzxZY+gIZ9ADLaRfLH1Jgyz8gyYBA+wAG"); } @@ -52,7 +52,7 @@ TEST(TheOpenNetworkSigner, TransferOrdinary) { ASSERT_EQ(hex(CommonTON::Cell::fromBase64(output.encoded())->hash), "3908cf8b570c1d3d261c62620c9f368db11f6e821a07614cff64de2e7319f81b"); - // tx: https://tonscan.org/tx/3Z4tHpXNLyprecgu5aTQHWtY7dpHXEoo11MAX61Xyg0= + // tx: https://tonviewer.com/transaction/3Z4tHpXNLyprecgu5aTQHWtY7dpHXEoo11MAX61Xyg0= ASSERT_EQ(output.encoded(), "te6ccgICAAQAAQAAALAAAAFFiAGwt/q8k4SrjbFbQCjJZfQr64ExRxcUMsWqaQODqTUijgwAAQGcEUPkil2aZ4s8KKparSep/OKHMC8vuXafFbW2HGp/9AcTRv0J5T4dwyW1G0JpHw+g5Ov6QI3Xo0O9RFr3KidICimpoxdjm3UYAAAABgADAAIBYmIAM33x4uAd+uQTyXyCZPxflESlNVHpCeoOECtNsqVW9tmIUAAAAAAAAAAAAAAAAAEAAwAA"); } @@ -75,7 +75,7 @@ TEST(TheOpenNetworkSigner, TransferAllBalance) { ASSERT_EQ(hex(CommonTON::Cell::fromBase64(output.encoded())->hash), "d5c5980c9083f697a7f114426effbbafac6d5c88554297d290eb65c8def3008e"); - // tx: https://tonscan.org/tx/cVcXgI9DWNWlN2iyTsteaWJckTswVqWZnRVvX5krXeA= + // tx: https://tonviewer.com/transaction/cVcXgI9DWNWlN2iyTsteaWJckTswVqWZnRVvX5krXeA= ASSERT_EQ(output.encoded(), "te6ccgICAAQAAQAAAK8AAAFFiAGwt/q8k4SrjbFbQCjJZfQr64ExRxcUMsWqaQODqTUijgwAAQGc58rMUQc/u78bg+Wtt8ETkyM0udf7S+F7wWk7lnPib2KChnBx9dZ7a/zLzhfLq+W9LjLZZfx995J17+0sbkvGCympoxdkM5WOAAAABwCCAAIBYGIAM33x4uAd+uQTyXyCZPxflESlNVHpCeoOECtNsqVW9tmAAAAAAAAAAAAAAAAAAQADAAA="); } @@ -98,7 +98,7 @@ TEST(TheOpenNetworkSigner, TransferAllBalanceNonBounceable) { ASSERT_EQ(hex(CommonTON::Cell::fromBase64(output.encoded())->hash), "e9c816780fa8e578bae309c2e098db8eb16aa25545b3ad2b61bb711ec9562795"); - // tx: https://tonscan.org/tx/0sJkPKu6u6uObVRuSWGd_bVGiyy5lJuzEKDqSXifQEA= + // tx: https://tonviewer.com/transaction/0sJkPKu6u6uObVRuSWGd_bVGiyy5lJuzEKDqSXifQEA= ASSERT_EQ(output.encoded(), "te6ccgICAAQAAQAAAK8AAAFFiAGwt/q8k4SrjbFbQCjJZfQr64ExRxcUMsWqaQODqTUijgwAAQGcRQQvxdU1u4QoE2Pas0AsZQMc9lea3+wtSvaC6QfLUlyJ9oISMCFnaErpyFHelDhPu4iuZqhkoLwjkR1VYhFSCimpoxdkM5WOAAAACACCAAIBYEIAM33x4uAd+uQTyXyCZPxflESlNVHpCeoOECtNsqVW9tmAAAAAAAAAAAAAAAAAAQADAAA="); } @@ -122,7 +122,7 @@ TEST(TheOpenNetworkSigner, TransferWithASCIIComment) { ASSERT_EQ(hex(CommonTON::Cell::fromBase64(output.encoded())->hash), "a8c6943d5587f590c43fcdb0e894046f1965c615e19bcaf0c8407e9ccb74518d"); - // tx: https://tonscan.org/tx/9wjD-VrgEDpa0D9u1g03KSD7kvTNsxRocR7LEdQtCNQ= + // tx: https://tonviewer.com/transaction/9wjD-VrgEDpa0D9u1g03KSD7kvTNsxRocR7LEdQtCNQ= ASSERT_EQ(output.encoded(), "te6ccgICAAQAAQAAAMAAAAFFiAGwt/q8k4SrjbFbQCjJZfQr64ExRxcUMsWqaQODqTUijgwAAQGcY4XlvKqu7spxyjL6vyBSKjbskDgqkHhqBsdTe900RGrzExtpvwc04j94v8HOczEWSMCXjTXk0z+CVUXSL54qCimpoxdkM5WOAAAACgADAAIBYmIAM33x4uAd+uQTyXyCZPxflESlNVHpCeoOECtNsqVW9tmIUAAAAAAAAAAAAAAAAAEAAwAgAAAAAHRlc3QgY29tbWVudA=="); } @@ -146,7 +146,7 @@ TEST(TheOpenNetworkSigner, TransferWithUTF8Comment) { ASSERT_EQ(hex(CommonTON::Cell::fromBase64(output.encoded())->hash), "1091dfae81583d3972825633592c24eab0d3d74c91f60fda9d4afe7535103633"); - // tx: https://tonscan.org/tx/VOTt8HW6eRuWHmuM_P3aC-Dy4TMu4cCRePoTAiDfcoQ= + // tx: https://tonviewer.com/transaction/VOTt8HW6eRuWHmuM_P3aC-Dy4TMu4cCRePoTAiDfcoQ= ASSERT_EQ(output.encoded(), "te6ccgICAAQAAQAAANsAAAFFiAGwt/q8k4SrjbFbQCjJZfQr64ExRxcUMsWqaQODqTUijgwAAQGchoDa7EdGQuPuehHy3+0X9WNVEvYxdBtaEWn15oYUX8PEKyzztYy94Xq0T2XdhVvj2H7PTSQ+D/Ny1IBRCxk0BimpoxdkM5WOAAAACwADAAIBYmIAM33x4uAd+uQTyXyCZPxflESlNVHpCeoOECtNsqVW9tmIUAAAAAAAAAAAAAAAAAEAAwBWAAAAANGC0LXRgdGC0L7QstGL0Lkg0LrQvtC80LzQtdC90YLQsNGA0LjQuQ=="); } @@ -192,7 +192,7 @@ TEST(TheOpenNetworkSigner, JettonTransfer) { ASSERT_EQ(hex(CommonTON::Cell::fromBase64(output.encoded())->hash), "3e4dac37acdc99ca670b3747ab2730e818727d9d25c80d3987abe501356d0da0"); - // tx: https://testnet.tonscan.org/tx/2HOPGAXhez3v6sdfj-5p8mPHX4S4T0CgxVbm0E2swxE= + // tx: https://testnet.tonviewer.com/transaction/2HOPGAXhez3v6sdfj-5p8mPHX4S4T0CgxVbm0E2swxE= ASSERT_EQ(output.encoded(), "te6ccgICABoAAQAABCMAAAJFiAC0UQZVyBNtT/W+jqQKnhYasPiDIdSWnNgo1FPyLHxLKh4ABAABAZz3iNHD1z2mxbtpFAtmbVevYMnB4yHPkF3WAsL3KHcrqCw0SWezOg4lVz1zzSReeFDx98ByAqY9+eR5VF3xyugAKamjF/////8AAAAAAAMAAgFoYgAxNB+Hnam4Pt4pSYNuGp+1rhx1QxEXrrZTGnfPOq6D8yAvrwgAAAAAAAAAAAAAAAAAAQADAKoPin6lAAAAAAAAAEVDuaygCAALgZZzC14dAz6ZxChX5pn6bIJ3WNipSJrCELO7Ex0TOQAWiiDKuQJtqf630dSBU8LDVh8QZDqS05sFGop+RY+JZUICAgE0AAYABQBRAAAAACmpoxfOamBhePRNnx/pqQViBzW0dDCy/+1WLV1VhgbVTL6i30ABFP8A9KQT9LzyyAsABwIBIAANAAgE+PKDCNcYINMf0x/THwL4I7vyZO1E0NMf0x/T//QE0VFDuvKhUVG68qIF+QFUEGT5EPKj+AAkpMjLH1JAyx9SMMv/UhD0AMntVPgPAdMHIcAAn2xRkyDXSpbTB9QC+wDoMOAhwAHjACHAAuMAAcADkTDjDQOkyMsfEssfy/8ADAALAAoACQAK9ADJ7VQAbIEBCNcY+gDTPzBSJIEBCPRZ8qeCEGRzdHJwdIAYyMsFywJQBc8WUAP6AhPLassfEss/yXP7AABwgQEI1xj6ANM/yFQgR4EBCPRR8qeCEG5vdGVwdIAYyMsFywJQBs8WUAT6AhTLahLLH8s/yXP7AAIAbtIH+gDU1CL5AAXIygcVy//J0Hd0gBjIywXLAiLPFlAF+gIUy2sSzMzJc/sAyEAUgQEI9FHypwICAUgAFwAOAgEgABAADwBZvSQrb2omhAgKBrkPoCGEcNQICEekk30pkQzmkD6f+YN4EoAbeBAUiYcVnzGEAgEgABIAEQARuMl+1E0NcLH4AgFYABYAEwIBIAAVABQAGa8d9qJoQBBrkOuFj8AAGa3OdqJoQCBrkOuF/8AAPbKd+1E0IEBQNch9AQwAsjKB8v/ydABgQEI9ApvoTGAC5tAB0NMDIXGwkl8E4CLXScEgkl8E4ALTHyGCEHBsdWe9IoIQZHN0cr2wkl8F4AP6QDAg+kQByMoHy//J0O1E0IEBQNch9AQwXIEBCPQKb6Exs5JfB+AF0z/IJYIQcGx1Z7qSODDjDQOCEGRzdHK6kl8G4w0AGQAYAIpQBIEBCPRZMO1E0IEBQNcgyAHPFvQAye1UAXKwjiOCEGRzdHKDHrFwgBhQBcsFUAPPFiP6AhPLassfyz/JgED7AJJfA+IAeAH6APQEMPgnbyIwUAqhIb7y4FCCEHBsdWeDHrFwgBhQBMsFJs8WWPoCGfQAy2kXyx9SYMs/IMmAQPsABg=="); } @@ -222,8 +222,7 @@ TEST(TheOpenNetworkSigner, JettonTransferComment) { ASSERT_EQ(hex(CommonTON::Cell::fromBase64(output.encoded())->hash), "c98c205c8dd37d9a6ab5db6162f5b9d37cefa067de24a765154a5eb7a359f22f"); - // tx: https://testnet.tonscan.org/tx/Er_oT5R3QK7D-qVPBKUGkJAOOq6ayVls-mgEphpI9Ck= - // comment can be seen here: https://testnet.tonviewer.com/transaction/12bfe84f947740aec3faa54f04a50690900e3aae9ac9596cfa6804a61a48f429 + // tx: https://testnet.tonviewer.com/transaction/12bfe84f947740aec3faa54f04a50690900e3aae9ac9596cfa6804a61a48f429 ASSERT_EQ(output.encoded(), "te6ccgICAAQAAQAAARgAAAFFiAC0UQZVyBNtT/W+jqQKnhYasPiDIdSWnNgo1FPyLHxLKgwAAQGcaIWVosi1XnveAmoG9y0/mPeNUqUu7GY76mdbRAaVeNeDOPDlh5M3BEb26kkc6XoYDekV60o2iOobN+TGS76jBSmpoxdqjgf2AAAAAQADAAIBaGIAMTQfh52puD7eKUmDbhqfta4cdUMRF662Uxp3zzqug/MgL68IAAAAAAAAAAAAAAAAAAEAAwDKD4p+pQAAAAAAAAAAQdzWUAgAC4GWcwteHQM+mcQoV+aZ+myCd1jYqUiawhCzuxMdEzkAFoogyrkCban+t9HUgVPCw1YfEGQ6ktObBRqKfkWPiWVCAgAAAAB0ZXN0IGNvbW1lbnQ="); } diff --git a/tests/chains/TheOpenNetwork/TWCoinTypeTests.cpp b/tests/chains/TheOpenNetwork/TWCoinTypeTests.cpp index 533746d4828..c35df437493 100644 --- a/tests/chains/TheOpenNetwork/TWCoinTypeTests.cpp +++ b/tests/chains/TheOpenNetwork/TWCoinTypeTests.cpp @@ -29,6 +29,6 @@ TEST(TWTONCoinType, TWCoinType) { ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainTheOpenNetwork); ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); - assertStringsEqual(txUrl, "https://tonscan.org/tx/fJXfn0EVhV09HFuEgUHu4Cchb24nUQtIMwSzmzk2tLs="); - assertStringsEqual(accUrl, "https://tonscan.org/address/EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N"); + assertStringsEqual(txUrl, "https://tonviewer.com/transaction/fJXfn0EVhV09HFuEgUHu4Cchb24nUQtIMwSzmzk2tLs="); + assertStringsEqual(accUrl, "https://tonviewer.com/EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N"); } From ca0582111f785c9bf98a544e643f8b8f0d3ce077 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Thu, 4 Jul 2024 16:18:56 +0200 Subject: [PATCH 101/128] [TON]: Return non-bounceable address by default (BREAKING CHANGES) (#3925) * [TON]: Return non-bounceable address by default * [TON]: `TWTONAddressConverterFromBoc` returns bounceable address * [TON]: Add `TWTONAddressConverterToUserFriendly` function * [TON]: `TWTONAddressConverterFromBoc` returns non-bounceable address * Fix tests * [TON]: Add `TWStoredKeyUpdateAddress` function to update address for a chain * [TON]: Fix generation error --- .../blockchains/CoinAddressDerivationTests.kt | 2 +- .../TestTheOpenNetworkAddress.kt | 26 ++++-- include/TrustWalletCore/TWStoredKey.h | 11 +++ .../TrustWalletCore/TWTONAddressConverter.h | 8 ++ .../core/test/CoinAddressDerivationTests.kt | 2 +- src/Keystore/StoredKey.cpp | 39 +++++++-- src/Keystore/StoredKey.h | 9 ++ src/TheOpenNetwork/Address.h | 2 +- src/TheOpenNetwork/Entry.cpp | 2 +- src/interface/TWStoredKey.cpp | 9 ++ src/interface/TWTONAddressConverter.cpp | 26 ++++-- .../Blockchains/TheOpenNetworkTests.swift | 22 +++-- swift/Tests/CoinAddressDerivationTests.swift | 2 +- tests/chains/TheOpenNetwork/AddressTests.cpp | 58 ++++++++++++- .../TheOpenNetwork/TWAnyAddressTests.cpp | 2 +- tests/common/CoinAddressDerivationTests.cpp | 2 +- tests/common/CoinAddressValidationTests.cpp | 2 +- tests/interface/TWStoredKeyTests.cpp | 85 +++++++++++++++++++ wasm/tests/Blockchain/TheOpenNetwork.test.ts | 8 +- 19 files changed, 282 insertions(+), 35 deletions(-) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index 243b33df519..088079ae8fe 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -122,7 +122,7 @@ class CoinAddressDerivationTests { NATIVEEVMOS -> assertEquals("evmos13u6g7vqgw074mgmf2ze2cadzvkz9snlwstd20d", address) NERVOS -> assertEquals("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02wectaumxn0664yw2jd53lqk4mxg3", address) EVERSCALE -> assertEquals("0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04", address) - TON -> assertEquals("EQDgEMqToTacHic7SnvnPFmvceG5auFkCcAw0mSCvzvKUfk9", address) + TON -> assertEquals("UQDgEMqToTacHic7SnvnPFmvceG5auFkCcAw0mSCvzvKUaT4", address) APTOS -> assertEquals("0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", address) NEBL -> assertEquals("NgDVaXAwNgBwb88xLiFKomfBmPkEh9F2d7", address) SUI -> assertEquals("0xada112cfb90b44ba889cc5d39ac2bf46281e4a91f7919c693bcd9b8323e81ed2", address) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkAddress.kt index 66433c2e0a8..f0ae94056e6 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkAddress.kt @@ -22,28 +22,44 @@ class TestTheOpenNetworkAddress { val publicKey = privateKey.getPublicKeyEd25519() val address = AnyAddress(publicKey, CoinType.TON) assertEquals(publicKey.data().toHex(), "0xf42c77f931bea20ec5d0150731276bbb2e2860947661245b2319ef8133ee8d41") - assertEquals(address.description(), "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") + assertEquals(address.description(), "UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV") } @Test fun testAddressFromPublicKey() { val publicKey = PublicKey("f42c77f931bea20ec5d0150731276bbb2e2860947661245b2319ef8133ee8d41".toHexByteArray(), PublicKeyType.ED25519) val address = AnyAddress(publicKey, CoinType.TON) - assertEquals(address.description(), "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") + assertEquals(address.description(), "UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV") } @Test fun testAddressFromRawString() { val addressString = "0:66fbe3c5c03bf5c82792f904c9f8bf28894a6aa3d213d41c20569b654aadedb3" val address = AnyAddress(addressString, CoinType.TON) - assertEquals(address.description(), "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") + assertEquals(address.description(), "UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV") } @Test - fun testAddressFromUserFriendlyString() { + fun testAddressFromBounceableString() { val addressString = "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q" val address = AnyAddress(addressString, CoinType.TON) - assertEquals(address.description(), "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") + assertEquals(address.description(), "UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV") + } + + @Test + fun testAddressFromUserFriendlyString() { + val addressString = "UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV" + val address = AnyAddress(addressString, CoinType.TON) + assertEquals(address.description(), "UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV") + } + + @Test + fun testAddressToBounceable() { + val addressString = "UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV" + val bounceable = true + val testnet = false + val address = TONAddressConverter.toUserFriendly(addressString, bounceable, testnet) + assertEquals(address, "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") } @Test diff --git a/include/TrustWalletCore/TWStoredKey.h b/include/TrustWalletCore/TWStoredKey.h index e378f7cdd42..51082a93898 100644 --- a/include/TrustWalletCore/TWStoredKey.h +++ b/include/TrustWalletCore/TWStoredKey.h @@ -295,6 +295,17 @@ TWData* _Nullable TWStoredKeyExportJSON(struct TWStoredKey* _Nonnull key); TW_EXPORT_METHOD bool TWStoredKeyFixAddresses(struct TWStoredKey* _Nonnull key, TWData* _Nonnull password); +/// Re-derives address and public key for the specified chain. +/// It can be used to update the address if the default address format for the given chain has been changed. +/// This method needs the encryption password to re-write address. +/// +/// \param key Non-null pointer to a stored key +/// \param password Non-null block of data, password of the stored key +/// \param coin Coin type for which to update the address and public key +/// \return `false` if the password is incorrect or there is no data for the specified chain, true otherwise. +TW_EXPORT_METHOD +bool TWStoredKeyUpdateAddress(struct TWStoredKey* _Nonnull key, TWData* _Nonnull password, enum TWCoinType coin); + /// Retrieve stored key encoding parameters, as JSON string. /// /// \param key Non-null pointer to a stored key diff --git a/include/TrustWalletCore/TWTONAddressConverter.h b/include/TrustWalletCore/TWTONAddressConverter.h index 55dfcaff58a..142c98a4cb7 100644 --- a/include/TrustWalletCore/TWTONAddressConverter.h +++ b/include/TrustWalletCore/TWTONAddressConverter.h @@ -31,4 +31,12 @@ TWString *_Nullable TWTONAddressConverterToBoc(TWString *_Nonnull address); TW_EXPORT_STATIC_METHOD TWString *_Nullable TWTONAddressConverterFromBoc(TWString *_Nonnull boc); +/// Converts any TON address format to user friendly with the given parameters. +/// +/// \param address raw or user-friendly address to be converted. +/// \param bounceable whether the result address should be bounceable. +/// \param testnet whether the result address should be testnet. +TW_EXPORT_STATIC_METHOD +TWString *_Nullable TWTONAddressConverterToUserFriendly(TWString *_Nonnull address, bool bounceable, bool testnet); + TW_EXTERN_C_END diff --git a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt index 39f84b264cd..6a41399d554 100644 --- a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt +++ b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt @@ -115,7 +115,7 @@ class CoinAddressDerivationTests { NativeEvmos -> "evmos13u6g7vqgw074mgmf2ze2cadzvkz9snlwstd20d" Nervos -> "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02wectaumxn0664yw2jd53lqk4mxg3" Everscale -> "0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04" - TON -> "EQDgEMqToTacHic7SnvnPFmvceG5auFkCcAw0mSCvzvKUfk9" + TON -> "UQDgEMqToTacHic7SnvnPFmvceG5auFkCcAw0mSCvzvKUaT4" Aptos -> "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30" Nebl -> "NgDVaXAwNgBwb88xLiFKomfBmPkEh9F2d7" Sui -> "0xada112cfb90b44ba889cc5d39ac2bf46281e4a91f7919c693bcd9b8323e81ed2" diff --git a/src/Keystore/StoredKey.cpp b/src/Keystore/StoredKey.cpp index fb0d8701f00..1d47a58feb7 100644 --- a/src/Keystore/StoredKey.cpp +++ b/src/Keystore/StoredKey.cpp @@ -155,6 +155,12 @@ Account StoredKey::fillAddressIfMissing(Account& account, const HDWallet<>* wall return account; } +void StoredKey::updateAddressForAccount(const PrivateKey& privKey, Account& account) { + const auto pubKey = privKey.getPublicKey(TW::publicKeyType(account.coin)); + account.address = TW::deriveAddress(account.coin, pubKey, account.derivation); + account.publicKey = hex(pubKey.bytes); +} + std::optional StoredKey::account(TWCoinType coin, const HDWallet<>* wallet) { const auto account = getDefaultAccountOrAny(coin, wallet); if (account.has_value()) { @@ -269,9 +275,7 @@ void StoredKey::fixAddresses(const Data& password) { } const auto& derivationPath = account.derivationPath; const auto key = wallet.getKey(account.coin, derivationPath); - const auto pubKey = key.getPublicKey(TW::publicKeyType(account.coin)); - account.address = TW::deriveAddress(account.coin, pubKey, account.derivation); - account.publicKey = hex(pubKey.bytes); + updateAddressForAccount(key, account); } } break; @@ -282,14 +286,37 @@ void StoredKey::fixAddresses(const Data& password) { TW::validateAddress(account.coin, account.address)) { continue; } - const auto pubKey = key.getPublicKey(TW::publicKeyType(account.coin)); - account.address = TW::deriveAddress(account.coin, pubKey, account.derivation); - account.publicKey = hex(pubKey.bytes); + updateAddressForAccount(key, account); } } break; } } +bool StoredKey::updateAddress(TWCoinType coin, const Data& password) { + auto account = std::find_if(accounts.begin(), accounts.end(), [coin](const auto &account) { + return account.coin == coin; + }); + if (account == accounts.end()) { + return false; + } + + switch (type) { + case StoredKeyType::mnemonicPhrase: { + const auto wallet = this->wallet(password); + const auto& derivationPath = account->derivationPath; + const auto key = wallet.getKey(account->coin, derivationPath); + updateAddressForAccount(key, *account); + } break; + + case StoredKeyType::privateKey: { + auto key = PrivateKey(payload.decrypt(password)); + updateAddressForAccount(key, *account); + } break; + } + + return true; +} + // ----------------- // Encoding/Decoding // ----------------- diff --git a/src/Keystore/StoredKey.h b/src/Keystore/StoredKey.h index 6fae0da8a6d..f50afb25763 100644 --- a/src/Keystore/StoredKey.h +++ b/src/Keystore/StoredKey.h @@ -144,6 +144,12 @@ class StoredKey { /// the encryption password to re-derive addresses from private keys. void fixAddresses(const Data& password); + /// Re-derives address and public key for the specified chain. + /// + /// Use when address format for the given chain has been changed. This method needs + /// the encryption password to re-derive addresses from private keys. + bool updateAddress(TWCoinType coin, const Data& password); + private: /// Default constructor, private StoredKey() : type(StoredKeyType::mnemonicPhrase) {} @@ -169,6 +175,9 @@ class StoredKey { /// Re-derive account address if missing Account fillAddressIfMissing(Account& account, const HDWallet<>* wallet) const; + + /// Re-derives public key and address for the specified account. + static void updateAddressForAccount(const PrivateKey& privKey, Account& account); }; } // namespace TW::Keystore diff --git a/src/TheOpenNetwork/Address.h b/src/TheOpenNetwork/Address.h index fe938a8f7b5..15b85bbd00a 100644 --- a/src/TheOpenNetwork/Address.h +++ b/src/TheOpenNetwork/Address.h @@ -48,7 +48,7 @@ class Address { /// Initializes an address with its parts explicit Address( int8_t workchainId, std::array hash, - bool userFriendly = true, bool bounceable = true, bool testOnly = false + bool userFriendly = true, bool bounceable = false, bool testOnly = false ) : addressData(workchainId, hash), isUserFriendly(userFriendly), isBounceable(bounceable), diff --git a/src/TheOpenNetwork/Entry.cpp b/src/TheOpenNetwork/Entry.cpp index b35c1c0e886..0cf95eb5ec6 100644 --- a/src/TheOpenNetwork/Entry.cpp +++ b/src/TheOpenNetwork/Entry.cpp @@ -17,7 +17,7 @@ bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, [[maybe_unused]] c } std::string Entry::normalizeAddress([[maybe_unused]] TWCoinType coin, const std::string& address) const { - return Address(address).string(true, true, false); + return Address(address).string(true, false, false); } std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TWDerivation derivation, [[maybe_unused]] const PrefixVariant& addressPrefix) const { diff --git a/src/interface/TWStoredKey.cpp b/src/interface/TWStoredKey.cpp index b46ce5de1fa..a5f95bf46df 100644 --- a/src/interface/TWStoredKey.cpp +++ b/src/interface/TWStoredKey.cpp @@ -224,6 +224,15 @@ bool TWStoredKeyFixAddresses(struct TWStoredKey* _Nonnull key, TWData* _Nonnull } } +bool TWStoredKeyUpdateAddress(struct TWStoredKey* _Nonnull key, TWData* _Nonnull password, enum TWCoinType coin) { + try { + const auto passwordData = TW::data(TWDataBytes(password), TWDataSize(password)); + return key->impl.updateAddress(coin, passwordData); + } catch (...) { + return false; + } +} + TWString* _Nullable TWStoredKeyEncryptionParameters(struct TWStoredKey* _Nonnull key) { if (!key->impl.id) { return nullptr; diff --git a/src/interface/TWTONAddressConverter.cpp b/src/interface/TWTONAddressConverter.cpp index 8da3613f8b0..c9d2a26695f 100644 --- a/src/interface/TWTONAddressConverter.cpp +++ b/src/interface/TWTONAddressConverter.cpp @@ -11,13 +11,14 @@ using namespace TW; TWString *_Nullable TWTONAddressConverterToBoc(TWString *_Nonnull address) { auto& addressString = *reinterpret_cast(address); - if (!TheOpenNetwork::Address::isValid(addressString)) { + + try { + const TheOpenNetwork::Address addressTon(addressString); + auto bocEncoded = addressTon.toBoc(); + return TWStringCreateWithUTF8Bytes(bocEncoded.c_str()); + } catch (...) { return nullptr; } - - const TheOpenNetwork::Address addressTon(addressString); - auto bocEncoded = addressTon.toBoc(); - return TWStringCreateWithUTF8Bytes(bocEncoded.c_str()); } TWString *_Nullable TWTONAddressConverterFromBoc(TWString *_Nonnull boc) { @@ -38,3 +39,18 @@ TWString *_Nullable TWTONAddressConverterFromBoc(TWString *_Nonnull boc) { return nullptr; } } + +TWString *_Nullable TWTONAddressConverterToUserFriendly(TWString *_Nonnull address, bool bounceable, bool testnet) { + auto& addressString = *reinterpret_cast(address); + + try { + const TheOpenNetwork::Address addressTon(addressString); + + auto userFriendly = true; + const auto addressFormatted = addressTon.string(userFriendly, bounceable, testnet); + + return TWStringCreateWithUTF8Bytes(addressFormatted.c_str()); + } catch (...) { + return nullptr; + } +} diff --git a/swift/Tests/Blockchains/TheOpenNetworkTests.swift b/swift/Tests/Blockchains/TheOpenNetworkTests.swift index 2a64fc9204b..e8a5faf5dd6 100644 --- a/swift/Tests/Blockchains/TheOpenNetworkTests.swift +++ b/swift/Tests/Blockchains/TheOpenNetworkTests.swift @@ -11,26 +11,38 @@ class TheOpenNetworkTests: XCTestCase { let privateKey = PrivateKey(data: data!)! let publicKey = privateKey.getPublicKeyEd25519() let address = AnyAddress(publicKey: publicKey, coin: .ton) - XCTAssertEqual(address.description, "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") + XCTAssertEqual(address.description, "UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV") } func testAddressFromPublicKey() { let data = Data(hexString: "f42c77f931bea20ec5d0150731276bbb2e2860947661245b2319ef8133ee8d41") let publicKey = PublicKey(data: data!, type: PublicKeyType.ed25519)! let address = AnyAddress(publicKey: publicKey, coin: .ton) - XCTAssertEqual(address.description, "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") + XCTAssertEqual(address.description, "UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV") } func testAddressFromRawString() { let addressString = "0:66fbe3c5c03bf5c82792f904c9f8bf28894a6aa3d213d41c20569b654aadedb3" let address = AnyAddress(string: addressString, coin: .ton) - XCTAssertEqual(address!.description, "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") + XCTAssertEqual(address!.description, "UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV") } - func testAddressFromUserFriendlyString() { + func testAddressFromBounceableString() { let addressString = "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q" let address = AnyAddress(string: addressString, coin: .ton) - XCTAssertEqual(address!.description, "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") + XCTAssertEqual(address!.description, "UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV") + } + + func testAddressFromUserFriendlyString() { + let addressString = "UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV" + let address = AnyAddress(string: addressString, coin: .ton) + XCTAssertEqual(address!.description, "UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV") + } + + func testAddressToBounceable() { + let addressString = "UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV" + let address = TONAddressConverter.toUserFriendly(address: addressString, bounceable: true, testnet: false) + XCTAssertEqual(address, "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q") } func testGenerateJettonAddress() { diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 7e17c1f3503..aeecba35076 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -308,7 +308,7 @@ class CoinAddressDerivationTests: XCTestCase { let expectedResult = "0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04"; assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .ton: - let expectedResult = "EQDgEMqToTacHic7SnvnPFmvceG5auFkCcAw0mSCvzvKUfk9"; + let expectedResult = "UQDgEMqToTacHic7SnvnPFmvceG5auFkCcAw0mSCvzvKUaT4"; assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .aptos: let expectedResult = "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"; diff --git a/tests/chains/TheOpenNetwork/AddressTests.cpp b/tests/chains/TheOpenNetwork/AddressTests.cpp index a46d2d53fd6..a92e3a0e4cd 100644 --- a/tests/chains/TheOpenNetwork/AddressTests.cpp +++ b/tests/chains/TheOpenNetwork/AddressTests.cpp @@ -102,7 +102,7 @@ TEST(TheOpenNetworkAddress, FromPrivateKeyV4R2) { WalletV4R2 wallet(publicKey, WorkchainType::Basechain); const auto address = wallet.getAddress(); - ASSERT_EQ(address.string(), "EQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorkdl"); + ASSERT_EQ(address.string(), "UQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorhqg"); } TEST(TheOpenNetworkAddress, FromPublicKeyV4R2) { @@ -111,7 +111,7 @@ TEST(TheOpenNetworkAddress, FromPublicKeyV4R2) { WalletV4R2 wallet(publicKey, WorkchainType::Basechain); const auto address = wallet.getAddress(); - ASSERT_EQ(address.string(), "EQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorkdl"); + ASSERT_EQ(address.string(), "UQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorhqg"); } TEST(TheOpenNetworkAddress, GetJettonNotcoinAddress) { @@ -180,4 +180,58 @@ TEST(TheOpenNetworkAddress, FromBocError) { ASSERT_EQ(TWTONAddressConverterFromBoc(boc4.get()), nullptr); } +TEST(TheOpenNetworkAddress, ToUserFriendly) { + auto rawAddress = "0:8a8627861a5dd96c9db3ce0807b122da5ed473934ce7568a5b4b1c361cbb28ae"; + auto bounceable = "EQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorkdl"; + auto nonBounceable = "UQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorhqg"; + auto bounceableTestnet = "kQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorvzv"; + auto nonBounceableTestnet = "0QCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorqEq"; + + // Raw to user friendly. + assertStringsEqual( + WRAPS(TWTONAddressConverterToUserFriendly(STRING(rawAddress).get(), true, false)), + bounceable + ); + assertStringsEqual( + WRAPS(TWTONAddressConverterToUserFriendly(STRING(rawAddress).get(), false, false)), + nonBounceable + ); + assertStringsEqual( + WRAPS(TWTONAddressConverterToUserFriendly(STRING(rawAddress).get(), true, true)), + bounceableTestnet + ); + assertStringsEqual( + WRAPS(TWTONAddressConverterToUserFriendly(STRING(rawAddress).get(), false, true)), + nonBounceableTestnet + ); + + // Bounceable to non-bounceable. + assertStringsEqual( + WRAPS(TWTONAddressConverterToUserFriendly(STRING(bounceable).get(), false, false)), + nonBounceable + ); + + // Non-bounceable to bounceable. + assertStringsEqual( + WRAPS(TWTONAddressConverterToUserFriendly(STRING(nonBounceable).get(), true, false)), + bounceable + ); + + // Non-bounceable to non-bounceable. + assertStringsEqual( + WRAPS(TWTONAddressConverterToUserFriendly(STRING(nonBounceable).get(), false, false)), + nonBounceable + ); +} + +TEST(TheOpenNetworkAddress, ToUserFriendlyError) { + // No "0:" prefix. + auto invalid1 = STRING("8a8627861a5dd96c9db3ce0807b122da5ed473934ce7568a5b4b1c361cbb28ae"); + ASSERT_EQ(TWTONAddressConverterToUserFriendly(invalid1.get(), true, false), nullptr); + + // Too short. + auto invalid2 = STRING("EQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsor"); + ASSERT_EQ(TWTONAddressConverterToUserFriendly(invalid1.get(), false, false), nullptr); +} + } // namespace TW::TheOpenNetwork::tests diff --git a/tests/chains/TheOpenNetwork/TWAnyAddressTests.cpp b/tests/chains/TheOpenNetwork/TWAnyAddressTests.cpp index 003faec99a6..35f4dbd16fd 100644 --- a/tests/chains/TheOpenNetwork/TWAnyAddressTests.cpp +++ b/tests/chains/TheOpenNetwork/TWAnyAddressTests.cpp @@ -25,7 +25,7 @@ TEST(TWTheOpenNetwork, Address) { const auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeTON)); const auto addressStr = WRAPS(TWAnyAddressDescription(address.get())); - assertStringsEqual(addressStr, "EQDYW_1eScJVxtitoBRksvoV9cCYo4uKGWLVNIHB1JqRR3n0"); + assertStringsEqual(addressStr, "UQDYW_1eScJVxtitoBRksvoV9cCYo4uKGWLVNIHB1JqRRyQx"); } } // namespace TW::TheOpenNetwork::tests diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index 96ceb19b711..3be107dee13 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -187,7 +187,7 @@ TEST(Coin, DeriveAddress) { EXPECT_EQ(address, "0:ef64d51f95ef17973b737277cfecbd2a8d551141be2f58f5fb362575fc3eb5b0"); break; case TWCoinTypeTON: - EXPECT_EQ(address, "EQAoYT8nMLfeNh6h0uIoK_wLm9JkvxiGxJDr6GRXJGu2ZhpY"); + EXPECT_EQ(address, "UQAoYT8nMLfeNh6h0uIoK_wLm9JkvxiGxJDr6GRXJGu2Zked"); break; case TWCoinTypeFIO: EXPECT_EQ(address, "FIO5TrYnZP1RkDSUMzBY4GanCy6AP68kCMdkAb5EACkAwkdgRLShz"); diff --git a/tests/common/CoinAddressValidationTests.cpp b/tests/common/CoinAddressValidationTests.cpp index 06fea830573..b02fe64d635 100644 --- a/tests/common/CoinAddressValidationTests.cpp +++ b/tests/common/CoinAddressValidationTests.cpp @@ -419,7 +419,7 @@ TEST(Coin, ValidateAddressTheOpenNetwork) { EXPECT_TRUE(validateAddress(TWCoinTypeTON, "0:8a8627861a5dd96c9db3ce0807b122da5ed473934ce7568a5b4b1c361cbb28ae")); EXPECT_FALSE(validateAddress(TWCoinTypeTON, "8a8627861a5dd96c9db3ce0807b122da5ed473934ce7568a5b4b1c361cbb28ae")); - ASSERT_EQ(normalizeAddress(TWCoinTypeTON, "0:8a8627861a5dd96c9db3ce0807b122da5ed473934ce7568a5b4b1c361cbb28ae"), "EQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorkdl"); + ASSERT_EQ(normalizeAddress(TWCoinTypeTON, "0:8a8627861a5dd96c9db3ce0807b122da5ed473934ce7568a5b4b1c361cbb28ae"), "UQCKhieGGl3ZbJ2zzggHsSLaXtRzk0znVopbSxw2HLsorhqg"); } } // namespace TW diff --git a/tests/interface/TWStoredKeyTests.cpp b/tests/interface/TWStoredKeyTests.cpp index af17983161a..dc2bd126f3f 100644 --- a/tests/interface/TWStoredKeyTests.cpp +++ b/tests/interface/TWStoredKeyTests.cpp @@ -278,6 +278,91 @@ TEST(TWStoredKey, fixAddresses) { EXPECT_TRUE(TWStoredKeyFixAddresses(key.get(), password.get())); } +// In this test, we add a TON account with an outdated bounceable (`EQ`) address to the key storage, +// and then check if `TWStoredKeyUpdateAddress` re-derives non-bounceable `UQ` instead. +TEST(TWStoredKey, UpdateAddressWithMnemonic) { + const auto keyName = STRING("key"); + const string passwordString = "password"; + const auto password = WRAPD(TWDataCreateWithBytes((const uint8_t*)passwordString.c_str(), passwordString.size())); + + // Create stored key with a dummy Bitcoin account. + auto key = createAStoredKey(TWCoinTypeBitcoin, password.get()); + + const auto oldAddress = "EQDSRYDMMez8BdcOuPEiaR6aJZpO6EjlIwmOBFn14mMbnUtk"; + const auto newAddress = "UQDSRYDMMez8BdcOuPEiaR6aJZpO6EjlIwmOBFn14mMbnRah"; + const auto derivationPath = "m/44'/607'/0'"; + const auto extPubKey = ""; + const auto pubKey = "b191d35f81aa8b144aa91c90a6b887e0b165ad9c2933b1c5266eb5c4e8bea241"; + + // Add a TON account with an outdated address (bounceable). + TWStoredKeyAddAccount(key.get(), + STRING(oldAddress).get(), + TWCoinTypeTON, + STRING(derivationPath).get(), + STRING(pubKey).get(), + STRING(extPubKey).get()); + EXPECT_EQ(TWStoredKeyAccountCount(key.get()), 2ul); + + // Last step - update TON account address. + // Expect to have a non-bounceable address in the end. + ASSERT_TRUE(TWStoredKeyUpdateAddress(key.get(), password.get(), TWCoinTypeTON)); + const auto tonAccount = WRAP(TWAccount, TWStoredKeyAccountForCoin(key.get(), TWCoinTypeTON, nullptr)); + assertStringsEqual(WRAPS(TWAccountAddress(tonAccount.get())), newAddress); +} + +// In this test, we add an Ethereum account with an outdated lowercase address to the key storage, +// and then check if `TWStoredKeyUpdateAddress` re-derives checksummed address instead. +TEST(TWStoredKey, UpdateAddressWithPrivateKey) { + const auto keyName = STRING("key"); + const auto privateKey = DATA("3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266"); + const string passwordString = "password"; + const auto password = WRAPD(TWDataCreateWithBytes((const uint8_t*)passwordString.c_str(), passwordString.size())); + + // Create stored key with a dummy Bitcoin account. + auto key = WRAP(TWStoredKey, TWStoredKeyImportPrivateKey(privateKey.get(), keyName.get(), password.get(), TWCoinTypeBitcoin)); + + const auto oldAddress = "0xc2d7cf95645d33006175b78989035c7c9061d3f9"; + const auto newAddress = "0xC2D7CF95645D33006175B78989035C7c9061d3F9"; + const auto derivationPath = "m/44'/60'/0'"; + const auto extPubKey = ""; + const auto pubKey = "04efb99d9860f4dec4cb548a5722c27e9ef58e37fbab9719c5b33d55c216db49311221a01f638ce5f255875b194e0acaa58b19a89d2e56a864427298f826a7f887"; + + // Add an Ethereum account with an outdated address (lowercase). + TWStoredKeyAddAccount(key.get(), + STRING(oldAddress).get(), + TWCoinTypeEthereum, + STRING(derivationPath).get(), + STRING(pubKey).get(), + STRING(extPubKey).get()); + EXPECT_EQ(TWStoredKeyAccountCount(key.get()), 2ul); + + // Last step - update Ethereum account address. + // Expect to have a checksummed address in the end. + ASSERT_TRUE(TWStoredKeyUpdateAddress(key.get(), password.get(), TWCoinTypeEthereum)); + const auto ethAccount = WRAP(TWAccount, TWStoredKeyAccountForCoin(key.get(), TWCoinTypeEthereum, nullptr)); + assertStringsEqual(WRAPS(TWAccountAddress(ethAccount.get())), newAddress); +} + +TEST(TWStoredKey, updateAddressInvalidPassword) { + const auto keyName = STRING("key"); + const string passwordString = "password"; + const string invalidPasswordString = "invalid password"; + const auto password = WRAPD(TWDataCreateWithBytes((const uint8_t*)passwordString.c_str(), passwordString.size())); + const auto invalidPassword = WRAPD(TWDataCreateWithBytes((const uint8_t*)invalidPasswordString.c_str(), invalidPasswordString.size())); + + auto key = createAStoredKey(TWCoinTypeBitcoin, password.get()); + ASSERT_FALSE(TWStoredKeyUpdateAddress(key.get(), invalidPassword.get(), TWCoinTypeBitcoin)); +} + +TEST(TWStoredKey, updateAddressUnknownAccount) { + const auto keyName = STRING("key"); + const string passwordString = "password"; + const auto password = WRAPD(TWDataCreateWithBytes((const uint8_t*)passwordString.c_str(), passwordString.size())); + + auto key = createAStoredKey(TWCoinTypeBitcoin, password.get()); + ASSERT_FALSE(TWStoredKeyUpdateAddress(key.get(), password.get(), TWCoinTypeEthereum)); +} + TEST(TWStoredKey, importInvalidKey) { auto bytes = TW::parse_hex("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"); auto data = WRAPD(TWDataCreateWithBytes(bytes.data(), bytes.size())); diff --git a/wasm/tests/Blockchain/TheOpenNetwork.test.ts b/wasm/tests/Blockchain/TheOpenNetwork.test.ts index 6cd2eea2261..d7cda0c4a22 100644 --- a/wasm/tests/Blockchain/TheOpenNetwork.test.ts +++ b/wasm/tests/Blockchain/TheOpenNetwork.test.ts @@ -20,21 +20,21 @@ describe("TheOpenNetwork", () => { let address = AnyAddress.createWithPublicKey(publicKey, CoinType.ton) assert.equal(publicKey.description(), "f42c77f931bea20ec5d0150731276bbb2e2860947661245b2319ef8133ee8d41"); - assert.equal(address.description(), "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q"); + assert.equal(address.description(), "UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV"); }); it("test address from public key TheOpenNetwork", () => { const { PublicKey, PublicKeyType, HexCoding, AnyAddress, CoinType } = globalThis.core; let publicKey = PublicKey.createWithData(HexCoding.decode("f42c77f931bea20ec5d0150731276bbb2e2860947661245b2319ef8133ee8d41"), PublicKeyType.ed25519); let address = AnyAddress.createWithPublicKey(publicKey, CoinType.ton); - assert.equal(address.description(), "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q"); + assert.equal(address.description(), "UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV"); }); it("test address from raw string TheOpenNetwork", () => { const { AnyAddress, CoinType } = globalThis.core; let addressString = "0:66fbe3c5c03bf5c82792f904c9f8bf28894a6aa3d213d41c20569b654aadedb3"; let address = AnyAddress.createWithString(addressString, CoinType.ton); - assert.equal(address.description(), "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q"); + assert.equal(address.description(), "UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV"); }); it("test address invalid hex TheOpenNetwork", () => { @@ -55,7 +55,7 @@ describe("TheOpenNetwork", () => { const { AnyAddress, CoinType } = globalThis.core; let addressString = "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q"; let address = AnyAddress.createWithString(addressString, CoinType.ton); - assert.equal(address.description(), "EQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts90Q"); + assert.equal(address.description(), "UQBm--PFwDv1yCeS-QTJ-L8oiUpqo9IT1BwgVptlSq3ts4DV"); }); it("test address from user friendly invalid base64 decoding TheOpenNetwork", () => { From 352e0833678151ae124e1e70d325fc056d76d150 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Tue, 9 Jul 2024 16:46:18 +0700 Subject: [PATCH 102/128] [TON]: Do not require password at `TWStoredKeyUpdateAddress` (#3931) --- include/TrustWalletCore/TWStoredKey.h | 13 +++++------ src/Keystore/StoredKey.cpp | 33 +++++++++++---------------- src/Keystore/StoredKey.h | 8 +++---- src/interface/TWStoredKey.cpp | 5 ++-- tests/interface/TWStoredKeyTests.cpp | 19 ++++----------- 5 files changed, 29 insertions(+), 49 deletions(-) diff --git a/include/TrustWalletCore/TWStoredKey.h b/include/TrustWalletCore/TWStoredKey.h index 51082a93898..58a07e521c0 100644 --- a/include/TrustWalletCore/TWStoredKey.h +++ b/include/TrustWalletCore/TWStoredKey.h @@ -295,16 +295,15 @@ TWData* _Nullable TWStoredKeyExportJSON(struct TWStoredKey* _Nonnull key); TW_EXPORT_METHOD bool TWStoredKeyFixAddresses(struct TWStoredKey* _Nonnull key, TWData* _Nonnull password); -/// Re-derives address and public key for the specified chain. -/// It can be used to update the address if the default address format for the given chain has been changed. -/// This method needs the encryption password to re-write address. +/// Re-derives address for the account(s) associated with the given coin. +/// This method can be used if address format has been changed. +/// In case of multiple accounts, all of them will be updated. /// /// \param key Non-null pointer to a stored key -/// \param password Non-null block of data, password of the stored key -/// \param coin Coin type for which to update the address and public key -/// \return `false` if the password is incorrect or there is no data for the specified chain, true otherwise. +/// \param coin Account(s) coin type to be updated +/// \return `false` if there are no accounts associated with the given coin, true otherwise TW_EXPORT_METHOD -bool TWStoredKeyUpdateAddress(struct TWStoredKey* _Nonnull key, TWData* _Nonnull password, enum TWCoinType coin); +bool TWStoredKeyUpdateAddress(struct TWStoredKey* _Nonnull key, enum TWCoinType coin); /// Retrieve stored key encoding parameters, as JSON string. /// diff --git a/src/Keystore/StoredKey.cpp b/src/Keystore/StoredKey.cpp index 1d47a58feb7..b216461fe31 100644 --- a/src/Keystore/StoredKey.cpp +++ b/src/Keystore/StoredKey.cpp @@ -292,29 +292,22 @@ void StoredKey::fixAddresses(const Data& password) { } } -bool StoredKey::updateAddress(TWCoinType coin, const Data& password) { - auto account = std::find_if(accounts.begin(), accounts.end(), [coin](const auto &account) { - return account.coin == coin; - }); - if (account == accounts.end()) { - return false; - } +bool StoredKey::updateAddress(TWCoinType coin) { + bool addressUpdated = false; + const auto publicKeyType = TW::publicKeyType(coin); - switch (type) { - case StoredKeyType::mnemonicPhrase: { - const auto wallet = this->wallet(password); - const auto& derivationPath = account->derivationPath; - const auto key = wallet.getKey(account->coin, derivationPath); - updateAddressForAccount(key, *account); - } break; - - case StoredKeyType::privateKey: { - auto key = PrivateKey(payload.decrypt(password)); - updateAddressForAccount(key, *account); - } break; + for (auto& account : accounts) { + // Update the address for the given chain if only `publicKey` is set. + if (account.coin == coin && !account.publicKey.empty()) { + const auto publicKeyBytes = parse_hex(account.publicKey); + const PublicKey publicKey(publicKeyBytes, publicKeyType); + account.address = TW::deriveAddress(account.coin, publicKey, account.derivation); + + addressUpdated = true; + } } - return true; + return addressUpdated; } // ----------------- diff --git a/src/Keystore/StoredKey.h b/src/Keystore/StoredKey.h index f50afb25763..12eaf5038f4 100644 --- a/src/Keystore/StoredKey.h +++ b/src/Keystore/StoredKey.h @@ -144,11 +144,11 @@ class StoredKey { /// the encryption password to re-derive addresses from private keys. void fixAddresses(const Data& password); - /// Re-derives address and public key for the specified chain. + /// Re-derives address for the account(s) associated with the given coin. /// - /// Use when address format for the given chain has been changed. This method needs - /// the encryption password to re-derive addresses from private keys. - bool updateAddress(TWCoinType coin, const Data& password); + /// This method can be used if address format has been changed. + /// In case of multiple accounts, all of them will be updated. + bool updateAddress(TWCoinType coin); private: /// Default constructor, private diff --git a/src/interface/TWStoredKey.cpp b/src/interface/TWStoredKey.cpp index a5f95bf46df..f756ce5e6b3 100644 --- a/src/interface/TWStoredKey.cpp +++ b/src/interface/TWStoredKey.cpp @@ -224,10 +224,9 @@ bool TWStoredKeyFixAddresses(struct TWStoredKey* _Nonnull key, TWData* _Nonnull } } -bool TWStoredKeyUpdateAddress(struct TWStoredKey* _Nonnull key, TWData* _Nonnull password, enum TWCoinType coin) { +bool TWStoredKeyUpdateAddress(struct TWStoredKey* _Nonnull key, enum TWCoinType coin) { try { - const auto passwordData = TW::data(TWDataBytes(password), TWDataSize(password)); - return key->impl.updateAddress(coin, passwordData); + return key->impl.updateAddress(coin); } catch (...) { return false; } diff --git a/tests/interface/TWStoredKeyTests.cpp b/tests/interface/TWStoredKeyTests.cpp index dc2bd126f3f..ac9bd0cc366 100644 --- a/tests/interface/TWStoredKeyTests.cpp +++ b/tests/interface/TWStoredKeyTests.cpp @@ -305,7 +305,7 @@ TEST(TWStoredKey, UpdateAddressWithMnemonic) { // Last step - update TON account address. // Expect to have a non-bounceable address in the end. - ASSERT_TRUE(TWStoredKeyUpdateAddress(key.get(), password.get(), TWCoinTypeTON)); + ASSERT_TRUE(TWStoredKeyUpdateAddress(key.get(), TWCoinTypeTON)); const auto tonAccount = WRAP(TWAccount, TWStoredKeyAccountForCoin(key.get(), TWCoinTypeTON, nullptr)); assertStringsEqual(WRAPS(TWAccountAddress(tonAccount.get())), newAddress); } @@ -338,29 +338,18 @@ TEST(TWStoredKey, UpdateAddressWithPrivateKey) { // Last step - update Ethereum account address. // Expect to have a checksummed address in the end. - ASSERT_TRUE(TWStoredKeyUpdateAddress(key.get(), password.get(), TWCoinTypeEthereum)); + ASSERT_TRUE(TWStoredKeyUpdateAddress(key.get(), TWCoinTypeEthereum)); const auto ethAccount = WRAP(TWAccount, TWStoredKeyAccountForCoin(key.get(), TWCoinTypeEthereum, nullptr)); assertStringsEqual(WRAPS(TWAccountAddress(ethAccount.get())), newAddress); } -TEST(TWStoredKey, updateAddressInvalidPassword) { +TEST(TWStoredKey, updateAddressNoAssociatedAccounts) { const auto keyName = STRING("key"); const string passwordString = "password"; - const string invalidPasswordString = "invalid password"; const auto password = WRAPD(TWDataCreateWithBytes((const uint8_t*)passwordString.c_str(), passwordString.size())); - const auto invalidPassword = WRAPD(TWDataCreateWithBytes((const uint8_t*)invalidPasswordString.c_str(), invalidPasswordString.size())); auto key = createAStoredKey(TWCoinTypeBitcoin, password.get()); - ASSERT_FALSE(TWStoredKeyUpdateAddress(key.get(), invalidPassword.get(), TWCoinTypeBitcoin)); -} - -TEST(TWStoredKey, updateAddressUnknownAccount) { - const auto keyName = STRING("key"); - const string passwordString = "password"; - const auto password = WRAPD(TWDataCreateWithBytes((const uint8_t*)passwordString.c_str(), passwordString.size())); - - auto key = createAStoredKey(TWCoinTypeBitcoin, password.get()); - ASSERT_FALSE(TWStoredKeyUpdateAddress(key.get(), password.get(), TWCoinTypeEthereum)); + ASSERT_FALSE(TWStoredKeyUpdateAddress(key.get(), TWCoinTypeEthereum)); } TEST(TWStoredKey, importInvalidKey) { From c072e13d27ae4a97b45d90822593f3565873963d Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Tue, 9 Jul 2024 18:46:21 +0700 Subject: [PATCH 103/128] [Rust]: Update rust toolchain to nightly-2024-06-13 (#3932) * [Rust]: Update rust toolchain to nightly-2024-06-13 * [Rust]: Revert Dockerfile --- Dockerfile | 2 +- rust/Cargo.lock | 29 ++++--------------- .../tw_internet_computer/src/address.rs | 2 ++ .../tw_solana/src/transaction/short_vec.rs | 12 ++++---- rust/tw_evm/src/transaction/mod.rs | 4 +-- rust/tw_hash/Cargo.toml | 2 +- rust/tw_keypair/Cargo.toml | 2 +- rust/tw_misc/Cargo.toml | 2 +- rust/tw_number/src/i256.rs | 2 -- tools/install-rust-dependencies | 2 +- 10 files changed, 20 insertions(+), 39 deletions(-) diff --git a/Dockerfile b/Dockerfile index 179fa0d8973..082d5fd1969 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,7 +38,7 @@ ENV CXX=/usr/bin/clang++-14 RUN wget "https://sh.rustup.rs" -O rustup.sh \ && sh rustup.sh -y ENV PATH="/root/.cargo/bin:${PATH}" -RUN rustup default nightly-2024-02-09 +RUN rustup default nightly-2024-06-13 RUN cargo install --force cbindgen \ && rustup target add wasm32-unknown-emscripten diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 73378992bdb..e94bd038f53 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1544,18 +1544,6 @@ dependencies = [ "syn 2.0.37", ] -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", - "unicode-xid", -] - [[package]] name = "tap" version = "1.0.1" @@ -2079,12 +2067,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - [[package]] name = "untrusted" version = "0.7.1" @@ -2270,21 +2252,20 @@ checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" [[package]] name = "zeroize" -version = "1.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.3.3" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", - "synstructure", + "syn 2.0.37", ] diff --git a/rust/chains/tw_internet_computer/src/address.rs b/rust/chains/tw_internet_computer/src/address.rs index a30ab2ef984..e439b573a9d 100644 --- a/rust/chains/tw_internet_computer/src/address.rs +++ b/rust/chains/tw_internet_computer/src/address.rs @@ -24,9 +24,11 @@ pub trait IcpAddress: std::str::FromStr + Into VisitResult { } let shift = u32::try_from(nth_byte) - .unwrap_or(std::u32::MAX) + .unwrap_or(u32::MAX) .saturating_mul(7); - let elem_val = elem_val.checked_shl(shift).unwrap_or(std::u32::MAX); + let elem_val = elem_val.checked_shl(shift).unwrap_or(u32::MAX); let new_val = val | elem_val; let val = u16::try_from(new_val).map_err(|_| VisitError::Overflow(new_val))?; @@ -172,7 +172,7 @@ pub fn serialize( let mut seq = serializer.serialize_tuple(1)?; let len = elements.len(); - if len > std::u16::MAX as usize { + if len > u16::MAX as usize { return Err(ser::Error::custom("length larger than u16")); } let short_len = ShortU16(len as u16); @@ -229,7 +229,7 @@ where T: Deserialize<'de>, { let visitor = ShortVecVisitor { _t: PhantomData }; - deserializer.deserialize_tuple(std::usize::MAX, visitor) + deserializer.deserialize_tuple(usize::MAX, visitor) } pub struct ShortVec(pub Vec); @@ -361,10 +361,10 @@ mod tests { #[test] fn test_short_vec_u8_too_long() { - let vec = ShortVec(vec![4u8; std::u16::MAX as usize]); + let vec = ShortVec(vec![4u8; u16::MAX as usize]); assert!(matches!(serialize(&vec), Ok(_))); - let vec = ShortVec(vec![4u8; std::u16::MAX as usize + 1]); + let vec = ShortVec(vec![4u8; u16::MAX as usize + 1]); assert!(matches!(serialize(&vec), Err(_))); } diff --git a/rust/tw_evm/src/transaction/mod.rs b/rust/tw_evm/src/transaction/mod.rs index aa91320ce31..949153e7946 100644 --- a/rust/tw_evm/src/transaction/mod.rs +++ b/rust/tw_evm/src/transaction/mod.rs @@ -4,8 +4,8 @@ //! Transactions can be: //! - Non-typed (legacy, pre-EIP2718) transactions: -//! -- simple ETH transfer -//! -- others with payload, function call, e.g. ERC20 transfer +//! - simple ETH transfer +//! - others with payload, function call, e.g. ERC20 transfer //! - Typed transactions (enveloped, EIP2718), with specific type and transaction payload //! - User operations (EIP4337) diff --git a/rust/tw_hash/Cargo.toml b/rust/tw_hash/Cargo.toml index 2e1740b15c1..c9b8ab18c42 100644 --- a/rust/tw_hash/Cargo.toml +++ b/rust/tw_hash/Cargo.toml @@ -20,7 +20,7 @@ sha2 = "0.10.6" sha3 = "0.10.6" tw_encoding = { path = "../tw_encoding" } tw_memory = { path = "../tw_memory" } -zeroize = "1.6.0" +zeroize = "1.8.1" [dev-dependencies] serde_json = "1.0" diff --git a/rust/tw_keypair/Cargo.toml b/rust/tw_keypair/Cargo.toml index b332c1e4aa4..b71d0b70b62 100644 --- a/rust/tw_keypair/Cargo.toml +++ b/rust/tw_keypair/Cargo.toml @@ -16,7 +16,7 @@ tw_encoding = { path = "../tw_encoding" } tw_hash = { path = "../tw_hash" } tw_memory = { path = "../tw_memory" } tw_misc = { path = "../tw_misc" } -zeroize = "1.6.0" +zeroize = "1.8.1" # ECDSA specific: ecdsa = "0.16.6" der = "0.7.3" diff --git a/rust/tw_misc/Cargo.toml b/rust/tw_misc/Cargo.toml index 13747bab355..49f5b9f0c5d 100644 --- a/rust/tw_misc/Cargo.toml +++ b/rust/tw_misc/Cargo.toml @@ -9,4 +9,4 @@ test-utils = ["serde", "serde_json"] [dependencies] serde = { version = "1.0", features = ["derive"], optional = true } serde_json = { version = "1.0", optional = true } -zeroize = "1.6.0" +zeroize = "1.8.1" diff --git a/rust/tw_number/src/i256.rs b/rust/tw_number/src/i256.rs index 49a617c4d6f..3f62160ba27 100644 --- a/rust/tw_number/src/i256.rs +++ b/rust/tw_number/src/i256.rs @@ -40,7 +40,6 @@ impl I256 { /// - `0` is [`U256::zero()`] /// - `-1` is [`U256::MAX`] /// - `-2` is [`U256::MAX - 1`] - /// ... #[inline] pub fn to_u256_repr(&self) -> U256 { U256::from(self.0) @@ -52,7 +51,6 @@ impl I256 { /// - [`U256::zero()`] is `0` /// - [`U256::MAX`] is `-1` /// - [`U256::MAX - 1`] is `-2` - /// ... #[inline] pub fn from_u256_repr(unsigned: U256) -> I256 { I256(unsigned.0) diff --git a/tools/install-rust-dependencies b/tools/install-rust-dependencies index 632c2290f1c..1b12239c010 100755 --- a/tools/install-rust-dependencies +++ b/tools/install-rust-dependencies @@ -2,7 +2,7 @@ set -e -NIGHTLY="nightly-2024-02-09" +NIGHTLY="nightly-2024-06-13" rustup toolchain install $NIGHTLY rustup default $NIGHTLY From 93736105eb1fa1eba0efc88e33a1197044e199bf Mon Sep 17 00:00:00 2001 From: stellrust Date: Mon, 15 Jul 2024 17:58:57 +0800 Subject: [PATCH 104/128] chore: fix some comments (#3873) Signed-off-by: stellrust --- src/FIO/Signer.cpp | 2 +- tools/ios-build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FIO/Signer.cpp b/src/FIO/Signer.cpp index 440e0c5535a..0e90e84df30 100644 --- a/src/FIO/Signer.cpp +++ b/src/FIO/Signer.cpp @@ -61,7 +61,7 @@ bool Signer::verify(const PublicKey& pubKey, const Data& data, const Data& signa return pubKey.verify(TW::data(signature.data() + 1, signature.size() - 1), data); } -// canonical check for FIO, both R and S lenght is 32 +// canonical check for FIO, both R and S length is 32 int Signer::isCanonical([[maybe_unused]] uint8_t by, uint8_t sig[64]) { return !(sig[0] & 0x80) && !(sig[0] == 0 && !(sig[1] & 0x80)) diff --git a/tools/ios-build b/tools/ios-build index c7ddc0e5721..6a7b73eb96e 100755 --- a/tools/ios-build +++ b/tools/ios-build @@ -43,7 +43,7 @@ build_mac_x64_arm64() { } create_xc_framework() { - echo "Createing xcframework..." + echo "Creating xcframework..." xcodebuild -create-xcframework -output $BUILD_FOLDER/$FRAMEWORK.xcframework \ -framework $BUILD_FOLDER/ios-arm64.xcarchive/Products/Library/Frameworks/$FRAMEWORK.framework \ -framework $BUILD_FOLDER/ios-arm64_x86_64-simulator.xcarchive/Products/Library/Frameworks/$FRAMEWORK.framework \ From 221584f3ea91da1ba236d06834db9ce5d24a9f40 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Mon, 15 Jul 2024 18:01:42 +0700 Subject: [PATCH 105/128] [Bitcoin/Rust]: Finalize Bitcoin V2 refactoring (#3848) * [Bitcoin/V2]: Add a `rust/frameworks` directory, move `tw_utxo` there * [Bitcoin/V2]: Add Bitcoin Legacy address implementation * [Bitcoin/V2]: Add `StandardBitcoinAddres` * [Bitcoin/V2]: Add segwit address * Switch to TW Bitcoin Address implementation in `BitcoinEntry` * [Bitcoin/V2]: Implement signAsDER and verifyAsDER used in Bitcoin * [BitcoinV2]: Add standard Unsigned and Signed Transaction declarations * Add and implement Bitcoin encoding for Signed Transaction * [BitcoinV2]: Refactor `TWDerivation` to avoid Coin names * [BitcoinV2]: Refactor preimage hashing * Delegate transaction preimage hashing to `LegacyPreimage`, `Witness0Preimage` helpers * [BitcoinV2]: Design a `TransactionSigner` API * [BitcoinV2]: Design an API for `TransactionPlanner`, `TransactionBuilder` * add Script check methods * cargo fmt * make signing_method optional, try to automatically determine signing method if unavailable * start expanding verify_signatures * add schnorr module to tw_keypair, wip * expand KeyPair impl for schnorr * expand enums in tw module with Schnorr * simplify schnorr module structure * complete SigningKeyTrait and VerifyingKeyTrait impl for KeyPair * complete verify_signatures mechanism * adjust some types, complete compile method * add test interface for signer, wip * handle Schnorr variant in tw_keypair * ignore the recovery byte in ECDSA signature * update comment * specify the keys explicitly in tests * continue debugging * remove UseDefault from SighashBase, use All as default, fix sighash calculation * formatting * add Witness type, correctly consensus-encode items * fix P2WPKH scriptPubkey generation * add standard OPCodes and add convenience functions for Script * complete standard Script builders and checkers * update script submodule structures * build claim scripts for standardized transaction variants * fix Script method call * update comments, use H264 for new_p2pk instead of PublicKey type * small cleanup to push_slice * fix typo * rename Sighash to SighashType * expand TransactionBuilder * adjust builder and add test, wip * update TransactionBuilder, init test, wip * complete build_legacy_tx test * assert in build_legacy_tx * add BitcoinEcdsaSignature type, serialize correctly for claim script * add SER_SIZE const to BitcoinEcdsaSignature * rename TransactionSigner to SighashComputer * add a ClaimBuilder * rename some types * add tests for building Segwit transactions * delete playgrounds * track transaction builder test * add fee calculation traits, implement for standard Transaction, Inputs and Outputs * implement TransactionFee::fee * implement CompactInteger::serialized_len * consider Segwith flag and marker when calculating weight/size/fee * add select_inputs logic to TransactionFee, handle vbyte/size calculation edge case * expand tests with size, vsize and weight calculation * add fee() calculation tests * add input selection tests, wip * add base_size to TransactionFee trait, implement method * move input selection logic out of TransactionFee trait and to ComputedTransaction * add change amount related methods * continue with change output build pattern, wip * mvoe utxo input selector to separate module * complete test for utxo selection with change output generation * add note slight fee difference * extra comments * introduce CompiledSelectionBuilder * add p2tr condition builder functions * add builders for input, claim and output P2TR-key-path scripts * track taproot1_sighash, wip * extra conditions * add tests for Taproot inputs, spending conditions and claims. wip * continue on Taproot sighash calculation, wip * continuing with Taproot Sighash debugging... * add mockup of TapsighHash * extend test, wip * remove unused fields in UtxoPreiamgeArgs * implement Hasher::TapSighash variant * rename TapSighash prefix const * seperate sighash methods on PreimageInterface into legacy/segwit and taproot * move spent_* hashing methods to TransactionHasher * adjust TapSighash workflow to other variants * move tx_hasher and sighash_ty to a per-input basis * add const for Annex indicator * include leaf_hash and separator as part of TapSighash * construct Taproot script-path spending info and merkle root, wip * add p2tr_script_path spending script builder * init asset/ module, move brc20 and ordinals there * adjust brc20 and ordinal code for new changes, add brc20_transfer input builder * separate input builder into individual modules * fix and format * remove unused DEFAULT_TX_HASHER in utxo_selector * build brc20 spending input * build brc20 transfer output * add tweak support to tw_keypair * build brc20 transfer output * add support for non aux rand data when signing for schnorr, complete P2TR key-path test * add test for BRC20 transfer output, fix issue with payload * testing brc20 transfer reveal, wip * finalize brc20 reveal construction with tests * get rid of dbg! * add custom script builders * wipe tw_bitcoin's preimage_hashes_impl, start with utxo builder migration * handle custom script proto builder * start migrating output proto to builder matcher, wip * use SelectionBuilder * expand migration of tw_bitcoin preimage_hashes_impl * create dummy claims for preiamge_hashes_impl * add and handle a UseAll selector * complete construction of proto outputs * add init module with proto_input_to_native function * implement proto_output_to_native * process change output * handle custom script_pubkey outputs * fix signature issue for p2tr_script_path, expand p2wsh impl * set send_to_address tests to ignore for now * remove ignore attribute for ordinals and brc20, all related tests pass * derive P2PKH and P2SH from base58 address * derive P2WSH and P2WPKH from bech32 address * move address to native derivation logic to individual function * init taproot module with TaprootAddress * implement p2tr_with_coin_and_prefix for TaprootAddress, derive from StandardBitcoinAddress * fix tests for reference changes * distinguish between Segwit and Taproot for StandardBitcoinAddress * derive Taproot program from address, do extra witness program check * complete builders for P2WPKH and P2WSH derived from address string * complete legacy P2PKH and P2SH builders derived from address string * move address derivation logic to address_to_native * complete proto builders for p2pkh and p2wpkh from_hash variants * expand output builder for P2SH, both redeem script and hash * expand P2SH input claiming, wip * set ignore attribute to p2sh tests for now * add option to provide sigs to proto_input_to_native, deprecate some legacy code * construct proto return value for compile method * implement txid and fix ownership issue with proto return structure * update tw_bitcoin calls in wallet_core_rs for legacy FFI functions * update p2tr_script_path test module, use updated tw_utxo crate * adjust legacy_script tests to tw_utxo changes * remove unused imports * remove print/dbg statements * remove proto interface for tw_utxo * add proto_input_to_spending_data function * rename to proto_sighash_to_sig * sign sighashes and compile final transaction in tw_bitcon entry * formatting * add todo to fn txid * [Bitcoin]: Cleanups * [Bitcoin]: Fix most of clippy warnings * [Bitcoin]: Refactor schnorr private and public keys * [Bitcoin]: Fix BRC20 tests * [Bitcoin]: Fix txid * [Bitcoin]: Temporary add `BitcoinV3.proto` - refactored API * [Bitcoin]: Deprecate tw_bitcoin/src/modules * [Bitcoin]: Add `TransactionProtobuf` module * [Bitcoin]: Use `TWError` in tw_utxo * [Bitcoin]: Add `UtxoProtobuf` builder * Merge `SigningMethod::TaprootAll` and `SigningMethod::TaprootOnePrevout` * Temporary remove `OrdinalInscription` * [Bitcoin]: Add a `SpendingDataConstructor` pattern * Refactor `UtxoBuilder` to create a corresponding `SpendingDataConstructor` * Slightly refactor `BitcoinV3.proto` API * [Bitcoin]: Finalize UTXO selectors * Add `MaxSelector`, add `SigningInput::max_amount_output` * Refactor `SelectorBuilder` as `ExactSelector` * Add `DustFilter` and `DustPolicy` * Slightly refactor `BitcoinV3.proto` API * [Bitcoin]: Finalize UTXO selectors, split `SighashComputer` * Add `MaxSelector`, add `SigningInput::max_amount_output` * Refactor `SelectorBuilder` as `ExactSelector` * Add `DustFilter` and `DustPolicy` * Slightly refactor `BitcoinV3.proto` API * Split `SighashComputer` into several modules * Finalize `TxCompiler` * Finalize `SighashVerifier` * [Bitcoin]: Add `TxSigner` * [Bitcoin]: Add `OutputProtobuf` builder * Fix Segwit address to not allow witness version > 0 * [Bitcoin]: Refactor `TaprootAddress` and `SegwitAddress` * [Bitcoin]: Add transaction signer * [Bitcoin]: Add transaction planner * [Bitcoin]: Add transaction compiler * [Bitcoin]: Make `TxPlanner` generic * [Bitcoin]: Some clean ups * [Bitcoin]: Remove legacy BRC20 API * [Bitcoin]: Add BRC20 test * Add default chain info if not provided * Add default sequence if not provided * [Bitcoin]: Add P2PKH test * [Bitcoin]: Fix final fee calculation * [Bitcoin]: Add p2tr_key_path * [Bitcoin]: Move unit tests to `tw_any_coin/tests/chains/bitcoin` * [Bitcoin]: Fix `send_to_address` tests * [Bitcoin]: Fix broken tests * [Bitcoin]: Move UTXO selection to `tw_any_coin/tests` * [Bitcoin]: Add exact selector with and without change test * [Bitcoin]: Check transaction weight * Add more exact_selector tests * [Bitcoin]: Add more selector tests, increase code coverage * [Bitcoin]: Add p2pkh compile test * [Bitcoin]: Add mainnet BCH test * Fix fork_id sighashing * [Bitcoin]: Add send to p2sh and p2wsh address tests * Add p2wpkh signing test * Set V1 default transaction version * [Bitcoin]: Add OP_RETURN test * [Bitcoin]: Allow to pass a DER encoded signature to `TransactionCompiler` * [Bitcoin]: Test Bitcoin addresses * [Bitcoin]: Add BRC20 compile tests * [Bitcoin]: Fix transaction weight * [Bitcoin]: Remove legacy FFI TWBitcoinFeeCalculateFee * Return an error if `is_it_brc_operation` is true * [Bitcoin]: Add P2SH output signing test * [Bitcoin]: Add p2tr_script_path output test * Reorg p2tr_key_path tests to cover multiple protobuf types * [Bitcoin]: Fix p2tr custom script * [Bitcoin]: Fix P2PK * Extend P2PKH test to cover all protobuf inputs * Add P2WSH output test * Add sighash single test * [Bitcoin]: Fix some TODOs * [Bitcoin]: Finalize BitcoinV2 * Remove `Bitcoin.SigningInput.is_it_brc_operation` and `Bitcoin.SigningInput.planning_v2` * TODO adopt C++ BRC20 tests for BitcoinV2 * [Bitcoin]: Fix one of Android tests * Add Rust `test_bitcoin_sign_brc20_transfer` test * [Bitcoin]: Uncomment out Android tests * [Bitcoin]: Fix iOS tests * Remove NFT data from Android and iOS test suits * [Bitcoin]: Fix WASM BRC20 tests * [CI] Trigger CI * [Bitcoin]: Minor fixes * [Bitcoin]: Fix Rust WASM tests * [Bitcoin]: Add real P2TR transaction tests * [TON]: Add P2TR test in WASM --------- Co-authored-by: lamafab <42901763+lamafab@users.noreply.github.com> --- .../core/app/blockchains/TestCoinType.kt | 6 +- .../app/blockchains/bitcoin/TestBitcoinFee.kt | 47 - .../blockchains/bitcoin/TestBitcoinSigning.kt | 977 +++--------------- .../core/app/utils/TestAnyAddress.kt | 4 +- .../core/app/utils/TestHDWallet.kt | 12 +- codegen/bin/coins | 5 +- codegen/lib/templates/CoinInfoData.cpp.erb | 2 +- codegen/lib/templates/TWDerivation.h.erb | 7 +- include/TrustWalletCore/TWBitcoinFee.h | 23 - include/TrustWalletCore/TWBitcoinScript.h | 20 - registry.json | 38 + rust/Cargo.lock | 33 + rust/Cargo.toml | 3 +- rust/chains/tw_solana/src/address.rs | 4 +- rust/chains/tw_solana/src/blockhash.rs | 4 +- rust/chains/tw_solana/src/compiler.rs | 2 +- rust/chains/tw_solana/src/lib.rs | 2 +- .../src/modules/compiled_instructions.rs | 4 +- rust/chains/tw_solana/src/modules/utils.rs | 2 +- rust/chains/tw_solana/src/signer.rs | 2 +- rust/chains/tw_solana/src/transaction/mod.rs | 8 +- .../tests/update_blockhash_and_sign.rs | 2 +- .../tw_sui/src/transaction/sui_types.rs | 2 +- rust/frameworks/tw_utxo/Cargo.toml | 24 + .../tw_utxo/src/address/derivation.rs | 41 + rust/frameworks/tw_utxo/src/address/legacy.rs | 134 +++ rust/frameworks/tw_utxo/src/address/mod.rs | 12 + rust/frameworks/tw_utxo/src/address/segwit.rs | 207 ++++ .../tw_utxo/src/address/standard_bitcoin.rs | 169 +++ .../frameworks/tw_utxo/src/address/taproot.rs | 250 +++++ .../tw_utxo/src/address/witness_program.rs | 141 +++ rust/frameworks/tw_utxo/src/constants.rs | 7 + .../tw_utxo/src/dust/dust_filter.rs | 58 ++ rust/frameworks/tw_utxo/src/dust/mod.rs | 22 + .../tw_utxo/src/encode/compact_integer.rs | 82 ++ rust/frameworks/tw_utxo/src/encode/impls.rs | 105 ++ rust/frameworks/tw_utxo/src/encode/mod.rs | 27 + rust/frameworks/tw_utxo/src/encode/stream.rs | 66 ++ rust/frameworks/tw_utxo/src/lib.rs | 15 + .../tw_utxo/src/modules/fee_estimator.rs | 23 + .../tw_utxo/src/modules/keys_manager.rs | 76 ++ rust/frameworks/tw_utxo/src/modules/mod.rs | 12 + .../tw_utxo/src/modules/sighash_computer.rs | 130 +++ .../tw_utxo/src/modules/sighash_verifier.rs | 139 +++ .../tw_utxo/src/modules/tx_compiler.rs | 123 +++ .../tw_utxo/src/modules/tx_planner.rs | 92 ++ .../tw_utxo/src/modules/tx_signer.rs | 86 ++ .../modules/utxo_selector/exact_selector.rs | 171 +++ .../src/modules/utxo_selector/max_selector.rs | 124 +++ .../tw_utxo/src/modules/utxo_selector/mod.rs | 48 + rust/frameworks/tw_utxo/src/script/mod.rs | 205 ++++ .../src/script/standard_script/claims.rs | 111 ++ .../src/script/standard_script/conditions.rs | 190 ++++ .../tw_utxo/src/script/standard_script/mod.rs | 7 + .../src/script/standard_script/opcodes.rs | 256 +++++ rust/frameworks/tw_utxo/src/sighash.rs | 108 ++ rust/frameworks/tw_utxo/src/signature.rs | 161 +++ rust/frameworks/tw_utxo/src/signing_mode.rs | 13 + .../tw_utxo/src/spending_data/mod.rs | 43 + .../src/spending_data/standard_constructor.rs | 83 ++ .../tw_utxo/src/transaction/asset/brc20.rs | 71 ++ .../tw_utxo/src/transaction/asset/mod.rs | 2 + .../tw_utxo/src/transaction/asset/ordinal.rs} | 64 +- .../frameworks/tw_utxo/src/transaction/mod.rs | 63 ++ .../standard_transaction/builder/mod.rs | 85 ++ .../standard_transaction/builder/output.rs | 159 +++ .../standard_transaction/builder/utxo.rs | 350 +++++++ .../transaction/standard_transaction/mod.rs | 360 +++++++ .../src/transaction/transaction_hashing.rs | 100 ++ .../src/transaction/transaction_interface.rs | 67 ++ .../src/transaction/transaction_parts.rs | 27 + .../transaction_sighash/fork_id_sighash.rs | 22 + .../transaction_sighash/legacy_sighash.rs | 111 ++ .../transaction/transaction_sighash/mod.rs | 8 + .../transaction_sighash/taproot1_sighash.rs | 79 ++ .../transaction_sighash/witness0_sighash.rs | 55 + .../src/transaction/unsigned_transaction.rs | 154 +++ rust/frameworks/tw_utxo/tests/build_tx.rs | 376 +++++++ rust/{ => frameworks}/tw_utxo/tests/common.rs | 0 rust/tw_any_coin/Cargo.toml | 1 + rust/tw_any_coin/src/ffi/tw_any_address.rs | 57 +- .../src/test_utils/address_utils.rs | 48 +- rust/tw_any_coin/src/test_utils/mod.rs | 1 + rust/tw_any_coin/src/test_utils/plan_utils.rs | 30 + .../tests/chains/bitcoin/bitcoin_address.rs | 80 +- .../chains/bitcoin/bitcoin_compile/brc20.rs | 161 +++ .../bitcoin/bitcoin_compile/compile_error.rs | 61 ++ .../chains/bitcoin/bitcoin_compile/mod.rs | 7 + .../chains/bitcoin/bitcoin_compile/p2pkh.rs | 84 ++ .../tests/chains/bitcoin/bitcoin_plan/mod.rs | 8 + .../chains/bitcoin/bitcoin_plan/plan_exact.rs | 272 +++++ .../bitcoin/bitcoin_plan/plan_exact_error.rs | 207 ++++ .../chains/bitcoin/bitcoin_plan/plan_max.rs | 127 +++ .../bitcoin/bitcoin_plan/plan_max_error.rs | 54 + .../chains/bitcoin/bitcoin_sign/brc20.rs | 182 ++++ .../tests/chains/bitcoin/bitcoin_sign/mod.rs | 15 + .../chains/bitcoin/bitcoin_sign/op_return.rs | 76 ++ .../bitcoin/bitcoin_sign/ordinal_nft.rs | 128 +++ .../chains/bitcoin/bitcoin_sign/p2pkh.rs | 184 ++++ .../tests/chains/bitcoin/bitcoin_sign/p2sh.rs | 186 ++++ .../bitcoin/bitcoin_sign/p2tr_key_path.rs | 289 ++++++ .../bitcoin/bitcoin_sign/p2tr_script_path.rs | 145 +++ .../chains/bitcoin/bitcoin_sign/p2wpkh.rs | 85 ++ .../chains/bitcoin/bitcoin_sign/p2wsh.rs | 177 ++++ .../bitcoin/bitcoin_sign/send_to_address.rs | 272 +++++ .../bitcoin/bitcoin_sign/sighash_single.rs | 85 ++ rust/tw_any_coin/tests/chains/bitcoin/mod.rs | 3 + .../tests/chains/common/bitcoin/compile.rs | 55 + .../tests/chains/common/bitcoin}/data.rs | 0 .../tests/chains/common/bitcoin/mod.rs | 213 ++++ .../tests/chains/common/bitcoin/plan.rs | 149 +++ .../tests/chains/common/bitcoin/preimage.rs | 128 +++ .../tests/chains/common/bitcoin/sign.rs | 118 +++ rust/tw_any_coin/tests/chains/common/mod.rs | 5 + rust/tw_any_coin/tests/chains/mod.rs | 1 + .../tests/chains/solana/solana_compile.rs | 18 +- .../tests/chains/solana/solana_sign.rs | 2 +- .../tests/chains/solana/solana_transaction.rs | 2 +- .../chains/solana/solana_wallet_connect.rs | 2 +- .../tests/coin_address_derivation_test.rs | 41 +- rust/tw_base58_address/Cargo.toml | 12 + rust/tw_base58_address/src/lib.rs | 111 ++ rust/tw_bech32_address/src/bech32_prefix.rs | 1 + rust/tw_bitcoin/Cargo.toml | 11 +- rust/tw_bitcoin/src/entry.rs | 347 +------ rust/tw_bitcoin/src/lib.rs | 81 +- rust/tw_bitcoin/src/modules/compiler.rs | 119 +++ .../src/modules/legacy/build_and_sign.rs | 248 ----- rust/tw_bitcoin/src/modules/legacy/mod.rs | 6 - rust/tw_bitcoin/src/modules/mod.rs | 11 +- rust/tw_bitcoin/src/modules/planner.rs | 85 ++ .../src/modules/protobuf_builder/mod.rs | 58 ++ rust/tw_bitcoin/src/modules/signer.rs | 293 ++---- .../src/modules/signing_request/mod.rs | 169 +++ .../src/modules/transactions/brc20.rs | 93 -- .../src/modules/transactions/input_builder.rs | 323 ------ .../transactions/input_claim_builder.rs | 176 ---- .../src/modules/transactions/mod.rs | 26 - .../modules/transactions/output_builder.rs | 374 ------- rust/tw_bitcoin/src/modules/tx_builder/mod.rs | 12 + .../src/modules/tx_builder/output_protobuf.rs | 268 +++++ .../src/modules/tx_builder/public_keys.rs | 44 + .../src/modules/tx_builder/utxo_protobuf.rs | 328 ++++++ rust/tw_bitcoin/tests/brc20.rs | 137 --- rust/tw_bitcoin/tests/common/mod.rs | 12 - rust/tw_bitcoin/tests/input_selection.rs | 874 ---------------- rust/tw_bitcoin/tests/legacy_build_sign.rs | 446 -------- rust/tw_bitcoin/tests/legacy_scripts.rs | 178 ---- rust/tw_bitcoin/tests/ordinal_nft.rs | 130 --- rust/tw_bitcoin/tests/p2pkh.rs | 60 -- rust/tw_bitcoin/tests/p2sh.rs | 147 --- rust/tw_bitcoin/tests/p2tr_key_path.rs | 98 -- rust/tw_bitcoin/tests/p2tr_script_path.rs | 162 --- rust/tw_bitcoin/tests/p2wpkh.rs | 102 -- rust/tw_bitcoin/tests/p2wsh.rs | 142 --- rust/tw_bitcoin/tests/send_to_address.rs | 316 ------ rust/tw_coin_entry/Cargo.toml | 2 + rust/tw_coin_entry/src/coin_context.rs | 10 + rust/tw_coin_entry/src/coin_entry_ext.rs | 2 +- rust/tw_coin_entry/src/derivation.rs | 40 +- rust/tw_coin_entry/src/error/address_error.rs | 1 + .../tw_coin_entry/src/modules/plan_builder.rs | 8 +- rust/tw_coin_entry/src/prefix.rs | 7 + .../src/test_utils/test_context.rs | 15 + rust/tw_coin_registry/src/coin_context.rs | 16 + rust/tw_coin_registry/src/lib.rs | 1 + rust/tw_coin_registry/src/registry.rs | 4 + rust/tw_coin_registry/src/tw_derivation.rs | 34 + rust/tw_encoding/src/base58.rs | 37 +- rust/tw_encoding/src/ffi.rs | 7 +- rust/tw_evm/src/abi/param_type/constructor.rs | 2 +- rust/tw_evm/src/abi/uint.rs | 2 +- rust/tw_hash/src/hash_array.rs | 6 + rust/tw_hash/src/hasher.rs | 56 +- rust/tw_hash/src/ripemd.rs | 9 + rust/tw_keypair/Cargo.toml | 4 + rust/tw_keypair/src/ecdsa/der.rs | 76 +- rust/tw_keypair/src/ecdsa/secp256k1/mod.rs | 24 + rust/tw_keypair/src/ecdsa/signature.rs | 36 +- rust/tw_keypair/src/lib.rs | 1 + rust/tw_keypair/src/schnorr/keypair.rs | 74 ++ rust/tw_keypair/src/schnorr/mod.rs | 126 +++ rust/tw_keypair/src/schnorr/private.rs | 107 ++ rust/tw_keypair/src/schnorr/public.rs | 104 ++ rust/tw_keypair/src/schnorr/signature.rs | 43 + rust/tw_keypair/src/tw/mod.rs | 12 +- rust/tw_keypair/src/tw/private.rs | 12 + rust/tw_keypair/src/tw/public.rs | 18 + .../tw_keypair/tests/private_key_ffi_tests.rs | 43 +- rust/tw_utxo/Cargo.toml | 16 - rust/tw_utxo/src/compiler.rs | 507 --------- rust/tw_utxo/src/lib.rs | 26 - rust/tw_utxo/tests/input_selection.rs | 589 ----------- rust/tw_utxo/tests/p2pkh.rs | 53 - rust/tw_utxo/tests/p2tr.rs | 56 - rust/tw_utxo/tests/p2wpkh.rs | 100 -- rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs | 312 ------ rust/wallet_core_rs/src/ffi/bitcoin/mod.rs | 2 - .../tests/solana_transaction.rs | 6 +- samples/rust/src/build.rs | 1 - src/Bitcoin/Entry.cpp | 7 +- src/Bitcoin/Script.cpp | 23 - src/Bitcoin/Script.h | 6 - src/Bitcoin/Signer.cpp | 25 +- src/Bitcoin/Transaction.cpp | 8 - src/Bitcoin/Transaction.h | 2 - src/BitcoinDiamond/Entry.cpp | 2 +- src/Groestlcoin/Entry.cpp | 2 +- src/Verge/Entry.cpp | 2 +- src/interface/TWBitcoinFee.cpp | 18 - src/interface/TWBitcoinScript.cpp | 18 - src/proto/Bitcoin.proto | 7 - src/proto/BitcoinV2.proto | 565 +++++----- src/proto/Utxo.proto | 228 ---- swift/Tests/AnyAddressTests.swift | 4 +- swift/Tests/Blockchains/BitcoinTests.swift | 957 +++-------------- swift/Tests/CoinTypeTests.swift | 6 +- swift/Tests/HDWalletTests.swift | 12 +- swift/Tests/Keystore/KeyStoreTests.swift | 4 +- tests/chains/Bitcoin/SegwitAddressTests.cpp | 6 +- tests/chains/Bitcoin/TWBitcoinFeeTests.cpp | 38 - .../chains/Bitcoin/TWBitcoinSigningTests.cpp | 294 +----- tests/chains/BitcoinDiamond/AddressTests.cpp | 4 +- tests/chains/BitcoinDiamond/SignerTests.cpp | 4 +- tests/chains/Groestlcoin/AddressTests.cpp | 6 +- .../chains/Litecoin/LitecoinAddressTests.cpp | 4 +- tests/chains/Nebl/AddressTests.cpp | 2 +- tests/chains/Verge/AddressTests.cpp | 4 +- tests/common/AnyAddressTests.cpp | 2 +- tests/common/Keystore/DerivationPathTests.cpp | 14 +- tests/common/Keystore/StoredKeyTests.cpp | 18 +- tests/interface/TWAnyAddressTests.cpp | 4 +- tests/interface/TWCoinTypeTests.cpp | 6 +- tests/interface/TWHDWalletTests.cpp | 16 +- tests/interface/TWStoredKeyTests.cpp | 2 +- wasm/tests/Blockchain/Bitcoin.test.ts | 456 +++++--- wasm/tests/CoinType.test.ts | 4 +- 237 files changed, 12669 insertions(+), 9400 deletions(-) delete mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bitcoin/TestBitcoinFee.kt delete mode 100644 include/TrustWalletCore/TWBitcoinFee.h create mode 100644 rust/frameworks/tw_utxo/Cargo.toml create mode 100644 rust/frameworks/tw_utxo/src/address/derivation.rs create mode 100644 rust/frameworks/tw_utxo/src/address/legacy.rs create mode 100644 rust/frameworks/tw_utxo/src/address/mod.rs create mode 100644 rust/frameworks/tw_utxo/src/address/segwit.rs create mode 100644 rust/frameworks/tw_utxo/src/address/standard_bitcoin.rs create mode 100644 rust/frameworks/tw_utxo/src/address/taproot.rs create mode 100644 rust/frameworks/tw_utxo/src/address/witness_program.rs create mode 100644 rust/frameworks/tw_utxo/src/constants.rs create mode 100644 rust/frameworks/tw_utxo/src/dust/dust_filter.rs create mode 100644 rust/frameworks/tw_utxo/src/dust/mod.rs create mode 100644 rust/frameworks/tw_utxo/src/encode/compact_integer.rs create mode 100644 rust/frameworks/tw_utxo/src/encode/impls.rs create mode 100644 rust/frameworks/tw_utxo/src/encode/mod.rs create mode 100644 rust/frameworks/tw_utxo/src/encode/stream.rs create mode 100644 rust/frameworks/tw_utxo/src/lib.rs create mode 100644 rust/frameworks/tw_utxo/src/modules/fee_estimator.rs create mode 100644 rust/frameworks/tw_utxo/src/modules/keys_manager.rs create mode 100644 rust/frameworks/tw_utxo/src/modules/mod.rs create mode 100644 rust/frameworks/tw_utxo/src/modules/sighash_computer.rs create mode 100644 rust/frameworks/tw_utxo/src/modules/sighash_verifier.rs create mode 100644 rust/frameworks/tw_utxo/src/modules/tx_compiler.rs create mode 100644 rust/frameworks/tw_utxo/src/modules/tx_planner.rs create mode 100644 rust/frameworks/tw_utxo/src/modules/tx_signer.rs create mode 100644 rust/frameworks/tw_utxo/src/modules/utxo_selector/exact_selector.rs create mode 100644 rust/frameworks/tw_utxo/src/modules/utxo_selector/max_selector.rs create mode 100644 rust/frameworks/tw_utxo/src/modules/utxo_selector/mod.rs create mode 100644 rust/frameworks/tw_utxo/src/script/mod.rs create mode 100644 rust/frameworks/tw_utxo/src/script/standard_script/claims.rs create mode 100644 rust/frameworks/tw_utxo/src/script/standard_script/conditions.rs create mode 100644 rust/frameworks/tw_utxo/src/script/standard_script/mod.rs create mode 100644 rust/frameworks/tw_utxo/src/script/standard_script/opcodes.rs create mode 100644 rust/frameworks/tw_utxo/src/sighash.rs create mode 100644 rust/frameworks/tw_utxo/src/signature.rs create mode 100644 rust/frameworks/tw_utxo/src/signing_mode.rs create mode 100644 rust/frameworks/tw_utxo/src/spending_data/mod.rs create mode 100644 rust/frameworks/tw_utxo/src/spending_data/standard_constructor.rs create mode 100644 rust/frameworks/tw_utxo/src/transaction/asset/brc20.rs create mode 100644 rust/frameworks/tw_utxo/src/transaction/asset/mod.rs rename rust/{tw_bitcoin/src/modules/transactions/ordinals.rs => frameworks/tw_utxo/src/transaction/asset/ordinal.rs} (68%) create mode 100644 rust/frameworks/tw_utxo/src/transaction/mod.rs create mode 100644 rust/frameworks/tw_utxo/src/transaction/standard_transaction/builder/mod.rs create mode 100644 rust/frameworks/tw_utxo/src/transaction/standard_transaction/builder/output.rs create mode 100644 rust/frameworks/tw_utxo/src/transaction/standard_transaction/builder/utxo.rs create mode 100644 rust/frameworks/tw_utxo/src/transaction/standard_transaction/mod.rs create mode 100644 rust/frameworks/tw_utxo/src/transaction/transaction_hashing.rs create mode 100644 rust/frameworks/tw_utxo/src/transaction/transaction_interface.rs create mode 100644 rust/frameworks/tw_utxo/src/transaction/transaction_parts.rs create mode 100644 rust/frameworks/tw_utxo/src/transaction/transaction_sighash/fork_id_sighash.rs create mode 100644 rust/frameworks/tw_utxo/src/transaction/transaction_sighash/legacy_sighash.rs create mode 100644 rust/frameworks/tw_utxo/src/transaction/transaction_sighash/mod.rs create mode 100644 rust/frameworks/tw_utxo/src/transaction/transaction_sighash/taproot1_sighash.rs create mode 100644 rust/frameworks/tw_utxo/src/transaction/transaction_sighash/witness0_sighash.rs create mode 100644 rust/frameworks/tw_utxo/src/transaction/unsigned_transaction.rs create mode 100644 rust/frameworks/tw_utxo/tests/build_tx.rs rename rust/{ => frameworks}/tw_utxo/tests/common.rs (100%) create mode 100644 rust/tw_any_coin/src/test_utils/plan_utils.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_compile/brc20.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_compile/compile_error.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_compile/mod.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_compile/p2pkh.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_plan/mod.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_plan/plan_exact.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_plan/plan_exact_error.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_plan/plan_max.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_plan/plan_max_error.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_sign/brc20.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_sign/mod.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_sign/op_return.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_sign/ordinal_nft.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_sign/p2pkh.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_sign/p2sh.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_sign/p2tr_key_path.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_sign/p2tr_script_path.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_sign/p2wpkh.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_sign/p2wsh.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_sign/send_to_address.rs create mode 100644 rust/tw_any_coin/tests/chains/bitcoin/bitcoin_sign/sighash_single.rs create mode 100644 rust/tw_any_coin/tests/chains/common/bitcoin/compile.rs rename rust/{tw_bitcoin/tests/common => tw_any_coin/tests/chains/common/bitcoin}/data.rs (100%) create mode 100644 rust/tw_any_coin/tests/chains/common/bitcoin/mod.rs create mode 100644 rust/tw_any_coin/tests/chains/common/bitcoin/plan.rs create mode 100644 rust/tw_any_coin/tests/chains/common/bitcoin/preimage.rs create mode 100644 rust/tw_any_coin/tests/chains/common/bitcoin/sign.rs create mode 100644 rust/tw_any_coin/tests/chains/common/mod.rs create mode 100644 rust/tw_base58_address/Cargo.toml create mode 100644 rust/tw_base58_address/src/lib.rs create mode 100644 rust/tw_bitcoin/src/modules/compiler.rs delete mode 100644 rust/tw_bitcoin/src/modules/legacy/build_and_sign.rs delete mode 100644 rust/tw_bitcoin/src/modules/legacy/mod.rs create mode 100644 rust/tw_bitcoin/src/modules/planner.rs create mode 100644 rust/tw_bitcoin/src/modules/protobuf_builder/mod.rs create mode 100644 rust/tw_bitcoin/src/modules/signing_request/mod.rs delete mode 100644 rust/tw_bitcoin/src/modules/transactions/brc20.rs delete mode 100644 rust/tw_bitcoin/src/modules/transactions/input_builder.rs delete mode 100644 rust/tw_bitcoin/src/modules/transactions/input_claim_builder.rs delete mode 100644 rust/tw_bitcoin/src/modules/transactions/mod.rs delete mode 100644 rust/tw_bitcoin/src/modules/transactions/output_builder.rs create mode 100644 rust/tw_bitcoin/src/modules/tx_builder/mod.rs create mode 100644 rust/tw_bitcoin/src/modules/tx_builder/output_protobuf.rs create mode 100644 rust/tw_bitcoin/src/modules/tx_builder/public_keys.rs create mode 100644 rust/tw_bitcoin/src/modules/tx_builder/utxo_protobuf.rs delete mode 100644 rust/tw_bitcoin/tests/brc20.rs delete mode 100644 rust/tw_bitcoin/tests/common/mod.rs delete mode 100644 rust/tw_bitcoin/tests/input_selection.rs delete mode 100644 rust/tw_bitcoin/tests/legacy_build_sign.rs delete mode 100644 rust/tw_bitcoin/tests/legacy_scripts.rs delete mode 100644 rust/tw_bitcoin/tests/ordinal_nft.rs delete mode 100644 rust/tw_bitcoin/tests/p2pkh.rs delete mode 100644 rust/tw_bitcoin/tests/p2sh.rs delete mode 100644 rust/tw_bitcoin/tests/p2tr_key_path.rs delete mode 100644 rust/tw_bitcoin/tests/p2tr_script_path.rs delete mode 100644 rust/tw_bitcoin/tests/p2wpkh.rs delete mode 100644 rust/tw_bitcoin/tests/p2wsh.rs delete mode 100644 rust/tw_bitcoin/tests/send_to_address.rs create mode 100644 rust/tw_coin_registry/src/tw_derivation.rs create mode 100644 rust/tw_keypair/src/schnorr/keypair.rs create mode 100644 rust/tw_keypair/src/schnorr/mod.rs create mode 100644 rust/tw_keypair/src/schnorr/private.rs create mode 100644 rust/tw_keypair/src/schnorr/public.rs create mode 100644 rust/tw_keypair/src/schnorr/signature.rs delete mode 100644 rust/tw_utxo/Cargo.toml delete mode 100644 rust/tw_utxo/src/compiler.rs delete mode 100644 rust/tw_utxo/src/lib.rs delete mode 100644 rust/tw_utxo/tests/input_selection.rs delete mode 100644 rust/tw_utxo/tests/p2pkh.rs delete mode 100644 rust/tw_utxo/tests/p2tr.rs delete mode 100644 rust/tw_utxo/tests/p2wpkh.rs delete mode 100644 rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs delete mode 100644 src/interface/TWBitcoinFee.cpp delete mode 100644 src/proto/Utxo.proto delete mode 100644 tests/chains/Bitcoin/TWBitcoinFeeTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/TestCoinType.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/TestCoinType.kt index d565a1a08be..6c518ea54e7 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/TestCoinType.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/TestCoinType.kt @@ -52,9 +52,9 @@ class TestCoinType { fun testDerivationPath() { var res = CoinType.createFromValue(CoinType.BITCOIN.value()).derivationPath().toString() assertEquals(res, "m/84'/0'/0'/0/0") - res = CoinType.createFromValue(CoinType.BITCOIN.value()).derivationPathWithDerivation(Derivation.BITCOINLEGACY).toString() + res = CoinType.createFromValue(CoinType.BITCOIN.value()).derivationPathWithDerivation(Derivation.LEGACY).toString() assertEquals(res, "m/44'/0'/0'/0/0") - res = CoinType.createFromValue(CoinType.SOLANA.value()).derivationPathWithDerivation(Derivation.SOLANASOLANA).toString() + res = CoinType.createFromValue(CoinType.SOLANA.value()).derivationPathWithDerivation(Derivation.SOLANA).toString() assertEquals(res, "m/44'/501'/0'/0'") } @@ -62,7 +62,7 @@ class TestCoinType { fun testDeriveAddressFromPublicKeyAndDerivation() { val publicKey = PublicKey("0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798".toHexByteArray(), PublicKeyType.SECP256K1) - val address = CoinType.BITCOIN.deriveAddressFromPublicKeyAndDerivation(publicKey, Derivation.BITCOINSEGWIT) + val address = CoinType.BITCOIN.deriveAddressFromPublicKeyAndDerivation(publicKey, Derivation.SEGWIT) assertEquals(address, "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4") } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bitcoin/TestBitcoinFee.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bitcoin/TestBitcoinFee.kt deleted file mode 100644 index 15d4ee2d593..00000000000 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bitcoin/TestBitcoinFee.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.trustwallet.core.app.blockchains.bitcoin - -import com.trustwallet.core.app.utils.Numeric -import com.trustwallet.core.app.utils.toHexBytes -import org.junit.Assert.assertEquals -import org.junit.Test -import wallet.core.jni.BitcoinFee - -class TestBitcoinFee { - - init { - System.loadLibrary("TrustWalletCore") - } - - @Test - fun P2pkhCalculateFee() { - val satVb = "10" - val tx = Numeric.hexStringToByteArray("02000000017be4e642bb278018ab12277de9427773ad1c5f5b1d164a157e0d99aa48dc1c1e000000006a473044022078eda020d4b86fcb3af78ef919912e6d79b81164dbbb0b0b96da6ac58a2de4b102201a5fd8d48734d5a02371c4b5ee551a69dca3842edbf577d863cf8ae9fdbbd4590121036666dd712e05a487916384bfcd5973eb53e8038eccbbf97f7eed775b87389536ffffffff01c0aff629010000001976a9145eaaa4f458f9158f86afcba08dd7448d27045e3d88ac00000000") - var fee = BitcoinFee.calculateFee(tx, satVb).toLong() - - assertEquals(fee, 191 * satVb.toLong()) - } - - @Test - fun P2wpkhCalculateFee() { - val satVb = "12" - val tx = Numeric.hexStringToByteArray("020000000111b9f62923af73e297abb69f749e7a1aa2735fbdfd32ac5f6aa89e5c96841c18000000006b483045022100df9ed0b662b759e68b89a42e7144cddf787782a7129d4df05642dd825930e6e6022051a08f577f11cc7390684bbad2951a6374072253ffcf2468d14035ed0d8cd6490121028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28fffffffff01c0aff629010000001600140d0e1cec6c2babe8badde5e9b3dea667da90036d00000000") - var fee = BitcoinFee.calculateFee(tx, satVb).toLong() - - assertEquals(fee, 189 * satVb.toLong()) - } - - @Test - // Metadata can be observed live on: - // https://www.blockchain.com/explorer/transactions/btc/797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1 - // - // Fee/VB 19.608 sat/vByte - // Size 235 Bytes - // Weight 610 - fun Brc20TransferCommitCalculateFee() { - val satVb = "19" - val tx = Numeric.hexStringToByteArray("02000000000101089098890d2653567b9e8df2d1fbe5c3c8bf1910ca7184e301db0ad3b495c88e0100000000ffffffff02581b000000000000225120e8b706a97732e705e22ae7710703e7f589ed13c636324461afa443016134cc051040000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d02483045022100a44aa28446a9a886b378a4a65e32ad9a3108870bd725dc6105160bed4f317097022069e9de36422e4ce2e42b39884aa5f626f8f94194d1013007d5a1ea9220a06dce0121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000") - var fee = BitcoinFee.calculateFee(tx, satVb).toLong() - - assertEquals(fee, 153 * satVb.toLong()) // 153 = ceil(610/4) - } -} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bitcoin/TestBitcoinSigning.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bitcoin/TestBitcoinSigning.kt index 0aa1ddac322..9d6bcdab802 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bitcoin/TestBitcoinSigning.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bitcoin/TestBitcoinSigning.kt @@ -16,6 +16,7 @@ import wallet.core.jni.PublicKey import wallet.core.jni.PublicKeyType import wallet.core.jni.proto.Bitcoin import wallet.core.jni.proto.Bitcoin.SigningOutput +import wallet.core.jni.proto.BitcoinV2 import wallet.core.jni.proto.Common.SigningError class TestBitcoinSigning { @@ -160,842 +161,196 @@ class TestBitcoinSigning { Numeric.toHexString(encoded.toByteArray())); } - @Test - fun testSignBrc20Transfer() { - // Successfully broadcasted: https://www.blockchain.com/explorer/transactions/btc/3e3576eb02667fac284a5ecfcb25768969680cc4c597784602d0a33ba7c654b7 - val privateKeyData = (Numeric.hexStringToByteArray("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129")) - val fullAmount = 26400 - val minerFee = 3000 - val brcInscribeAmount = 7000 - val dustSatoshis = 546 - val forFeeAmount = fullAmount - brcInscribeAmount - minerFee - val txIdInscription = Numeric.hexStringToByteArray("7046dc2689a27e143ea2ad1039710885147e9485ab6453fa7e87464aa7dd3eca").reversedArray() - val txIDForFees = Numeric.hexStringToByteArray("797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1").reversedArray() - - val privateKey = PrivateKey(privateKeyData) - val publicKey = privateKey.getPublicKeySecp256k1(true) - val pubKeyHash = Hash.ripemd(Hash.sha256(publicKey.data())) - val bobPubkey = PublicKey(Numeric.hexStringToByteArray("02f453bb46e7afc8796a9629e89e07b5cb0867e9ca340b571e7bcc63fc20c43f2e"), PublicKeyType.SECP256K1) - val bobPubkeyHash = Hash.ripemd(Hash.sha256(bobPubkey.data())) - - val p2wpkh = BitcoinScript.buildPayToWitnessPubkeyHash(pubKeyHash) - val outputP2wpkh = BitcoinScript.buildPayToWitnessPubkeyHash(bobPubkeyHash) - - val input = Bitcoin.SigningInput.newBuilder() - .setIsItBrcOperation(true) - .addPrivateKey(ByteString.copyFrom(privateKeyData)) - - val unspentOutputPoint0 = Bitcoin.OutPoint.newBuilder() - .setHash(ByteString.copyFrom(txIdInscription)) - .setIndex(0) - .build() - val utxo0 = Bitcoin.UnspentTransaction.newBuilder() - .setScript(ByteString.copyFrom(p2wpkh.data())) - .setAmount(dustSatoshis.toLong()) - .setVariant(Bitcoin.TransactionVariant.P2WPKH) - .setOutPoint(unspentOutputPoint0) - .build() - - val unspentOutputPoint1 = Bitcoin.OutPoint.newBuilder() - .setHash(ByteString.copyFrom(txIDForFees)) - .setIndex(1) - .build() - val utxo1 = Bitcoin.UnspentTransaction.newBuilder() - .setScript(ByteString.copyFrom(p2wpkh.data())) - .setAmount(forFeeAmount.toLong()) - .setVariant(Bitcoin.TransactionVariant.P2WPKH) - .setOutPoint(unspentOutputPoint1) - .build() - - input.addUtxo(utxo0) - input.addUtxo(utxo1) - - val utxoPlan0 = Bitcoin.UnspentTransaction.newBuilder() - .setScript(ByteString.copyFrom(outputP2wpkh.data())) - .setAmount(dustSatoshis.toLong()) - .setVariant(Bitcoin.TransactionVariant.P2WPKH) - .build() - val utxoPlan1 = Bitcoin.UnspentTransaction.newBuilder() - .setScript(ByteString.copyFrom(p2wpkh.data())) - .setAmount((forFeeAmount - minerFee).toLong()) - .setVariant(Bitcoin.TransactionVariant.P2WPKH) - .build() - - val plan = Bitcoin.TransactionPlan.newBuilder() - .addUtxos(utxoPlan0) - .addUtxos(utxoPlan1) - .build() - input.plan = plan - - val output = AnySigner.sign(input.build(), BITCOIN, SigningOutput.parser()) - - assertEquals(output.error, SigningError.OK) - assertEquals(output.transactionId, "3e3576eb02667fac284a5ecfcb25768969680cc4c597784602d0a33ba7c654b7") - assertEquals(Numeric.toHexString(output.encoded.toByteArray()), "0x02000000000102ca3edda74a46877efa5364ab85947e148508713910ada23e147ea28926dc46700000000000ffffffffb11f1782607a1fe5f033ccf9dc17404db020a0dedff94183596ee67ad4177d790100000000ffffffff022202000000000000160014e891850afc55b64aa8247b2076f8894ebdf889015834000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d024830450221008798393eb0b7390217591a8c33abe18dd2f7ea7009766e0d833edeaec63f2ec302200cf876ff52e68dbaf108a3f6da250713a9b04949a8f1dcd1fb867b24052236950121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb0248304502210096bbb9d1f0596d69875646689e46f29485e8ceccacde9d0025db87fd96d3066902206d6de2dd69d965d28df3441b94c76e812384ab9297e69afe3480ee4031e1b2060121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000") - - val signedTransaction = output.transaction - assert(signedTransaction.isInitialized) - } - @Test fun testSignBrc20Commit() { // Successfully broadcasted: https://www.blockchain.com/explorer/transactions/btc/797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1 val privateKeyData = (Numeric.hexStringToByteArray("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129")) - val fullAmount = 26400 - val minerFee = 3000 - val brcInscribeAmount = 7000 - val forFeeAmount = fullAmount - brcInscribeAmount - minerFee - val txId = Numeric.hexStringToByteArray("089098890d2653567b9e8df2d1fbe5c3c8bf1910ca7184e301db0ad3b495c88e") - val brc20Ticker = "oadf" - val brc20Amount = "20" + val dustSatoshis = 546.toLong() + val txId = Numeric.hexStringToByteArray("8ec895b4d30adb01e38471ca1019bfc8c3e5fbd1f28d9e7b5653260d89989008").reversedArray() val privateKey = PrivateKey(privateKeyData) - val publicKey = privateKey.getPublicKeySecp256k1(true) - val pubKeyHash = Hash.ripemd(Hash.sha256(publicKey.data())) - - val p2wpkh = BitcoinScript.buildPayToWitnessPubkeyHash(pubKeyHash) - val outputInscribe = BitcoinScript.buildBRC20InscribeTransfer(brc20Ticker, brc20Amount, publicKey.data()) - val outputInscribeProto = Bitcoin.TransactionOutput.parseFrom(outputInscribe) - - val input = Bitcoin.SigningInput.newBuilder() - .setIsItBrcOperation(true) - .addPrivateKey(ByteString.copyFrom(privateKeyData)) - - val unspentOutputPoint = Bitcoin.OutPoint.newBuilder() - .setHash(ByteString.copyFrom(txId)) - .setIndex(1) - .build() - - val utxo0 = Bitcoin.UnspentTransaction.newBuilder() - .setScript(ByteString.copyFrom(p2wpkh.data())) - .setAmount(fullAmount.toLong()) - .setVariant(Bitcoin.TransactionVariant.P2WPKH) - .setOutPoint(unspentOutputPoint) - .build() - input.addUtxo(utxo0) - - val utxoPlan0 = Bitcoin.UnspentTransaction.newBuilder() - .setScript(outputInscribeProto.script) - .setAmount(brcInscribeAmount.toLong()) - .setVariant(Bitcoin.TransactionVariant.BRC20TRANSFER) - .build() - val utxoPlan1 = Bitcoin.UnspentTransaction.newBuilder() - .setScript(ByteString.copyFrom(p2wpkh.data())) - .setAmount(forFeeAmount.toLong()) - .setVariant(Bitcoin.TransactionVariant.P2WPKH) - .build() - - val plan = Bitcoin.TransactionPlan.newBuilder() - .addUtxos(utxoPlan0) - .addUtxos(utxoPlan1) - .build() - input.plan = plan - - val output = AnySigner.sign(input.build(), BITCOIN, SigningOutput.parser()) + val publicKey = ByteString.copyFrom(privateKey.getPublicKeySecp256k1(true).data()) + + val utxo0 = BitcoinV2.Input.newBuilder() + .setOutPoint(BitcoinV2.OutPoint.newBuilder().apply { + hash = ByteString.copyFrom(txId) + vout = 1 + }) + .setValue(26_400) + .setSighashType(BitcoinSigHashType.ALL.value()) + .setScriptBuilder(BitcoinV2.Input.InputBuilder.newBuilder().apply { + p2Wpkh = BitcoinV2.PublicKeyOrHash.newBuilder().setPubkey(publicKey).build() + }) + + val out0 = BitcoinV2.Output.newBuilder() + .setValue(7_000) + .setBuilder(BitcoinV2.Output.OutputBuilder.newBuilder().apply { + brc20Inscribe = BitcoinV2.Output.OutputBrc20Inscription.newBuilder().apply { + inscribeTo = publicKey + ticker = "oadf" + transferAmount = "20" + }.build() + }) + + val changeOutput = BitcoinV2.Output.newBuilder() + .setValue(16_400) + .setBuilder(BitcoinV2.Output.OutputBuilder.newBuilder().apply { + p2Wpkh = BitcoinV2.PublicKeyOrHash.newBuilder().setPubkey(publicKey).build() + }) + + val signingInput = BitcoinV2.SigningInput.newBuilder() + .setVersion(BitcoinV2.TransactionVersion.V2) + .addPrivateKeys(ByteString.copyFrom(privateKeyData)) + .addInputs(utxo0) + .addOutputs(out0) + .addOutputs(changeOutput) + .setInputSelector(BitcoinV2.InputSelector.UseAll) + .setChainInfo(BitcoinV2.ChainInfo.newBuilder().apply { + p2PkhPrefix = 0 + p2ShPrefix = 5 + }) + .setDangerousUseFixedSchnorrRng(true) + .setFixedDustThreshold(dustSatoshis) + .build() + + val legacySigningInput = Bitcoin.SigningInput.newBuilder().apply { + signingV2 = signingInput + } + + val output = AnySigner.sign(legacySigningInput.build(), BITCOIN, SigningOutput.parser()) assertEquals(output.error, SigningError.OK) - assertEquals(output.transactionId, "797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1") - assertEquals(Numeric.toHexString(output.encoded.toByteArray()), "0x02000000000101089098890d2653567b9e8df2d1fbe5c3c8bf1910ca7184e301db0ad3b495c88e0100000000ffffffff02581b000000000000225120e8b706a97732e705e22ae7710703e7f589ed13c636324461afa443016134cc051040000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d02483045022100a44aa28446a9a886b378a4a65e32ad9a3108870bd725dc6105160bed4f317097022069e9de36422e4ce2e42b39884aa5f626f8f94194d1013007d5a1ea9220a06dce0121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000") - - val signedTransaction = output.transaction - assert(signedTransaction.isInitialized) + assertEquals(output.signingResultV2.error, SigningError.OK) + assertEquals(Numeric.toHexString(output.signingResultV2.encoded.toByteArray()), "0x02000000000101089098890d2653567b9e8df2d1fbe5c3c8bf1910ca7184e301db0ad3b495c88e0100000000ffffffff02581b000000000000225120e8b706a97732e705e22ae7710703e7f589ed13c636324461afa443016134cc051040000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d02483045022100a44aa28446a9a886b378a4a65e32ad9a3108870bd725dc6105160bed4f317097022069e9de36422e4ce2e42b39884aa5f626f8f94194d1013007d5a1ea9220a06dce0121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000") + assertEquals(Numeric.toHexString(output.signingResultV2.txid.toByteArray()), "0x797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1") } @Test fun testSignBrc20Reveal() { // Successfully broadcasted: https://www.blockchain.com/explorer/transactions/btc/7046dc2689a27e143ea2ad1039710885147e9485ab6453fa7e87464aa7dd3eca val privateKeyData = (Numeric.hexStringToByteArray("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129")) - val brcInscribeAmount = 7000 - val dustSatoshis = 546 - val txId = Numeric.hexStringToByteArray("b11f1782607a1fe5f033ccf9dc17404db020a0dedff94183596ee67ad4177d79") - val brc20Ticker = "oadf" - val brc20Amount = "20" - - val privateKey = PrivateKey(privateKeyData) - val publicKey = privateKey.getPublicKeySecp256k1(true) - val pubKeyHash = Hash.ripemd(Hash.sha256(publicKey.data())) - val p2wpkh = BitcoinScript.buildPayToWitnessPubkeyHash(pubKeyHash) - val outputInscribe = BitcoinScript.buildBRC20InscribeTransfer(brc20Ticker, brc20Amount, publicKey.data()) - val outputInscribeProto = Bitcoin.TransactionOutput.parseFrom(outputInscribe) - - val input = Bitcoin.SigningInput.newBuilder() - .setIsItBrcOperation(true) - .addPrivateKey(ByteString.copyFrom(privateKeyData)) - - val unspentOutputPoint0 = Bitcoin.OutPoint.newBuilder() - .setHash(ByteString.copyFrom(txId)) - .setIndex(0) - .build() - val utxo0 = Bitcoin.UnspentTransaction.newBuilder() - .setScript(outputInscribeProto.script) - .setAmount(brcInscribeAmount.toLong()) - .setVariant(Bitcoin.TransactionVariant.BRC20TRANSFER) - .setSpendingScript(outputInscribeProto.spendingScript) - .setOutPoint(unspentOutputPoint0) - .build() - - input.addUtxo(utxo0) - - val utxoPlan0 = Bitcoin.UnspentTransaction.newBuilder() - .setScript(ByteString.copyFrom(p2wpkh.data())) - .setAmount(dustSatoshis.toLong()) - .setVariant(Bitcoin.TransactionVariant.P2WPKH) - .build() - - val plan = Bitcoin.TransactionPlan.newBuilder() - .addUtxos(utxoPlan0) - .build() - input.plan = plan - - val output = AnySigner.sign(input.build(), BITCOIN, SigningOutput.parser()) - - assertEquals(output.error, SigningError.OK) - assertEquals(output.transactionId, "7046dc2689a27e143ea2ad1039710885147e9485ab6453fa7e87464aa7dd3eca") - val encodedHex = Numeric.toHexString(output.encoded.toByteArray()) - assert(encodedHex.startsWith("0x02000000000101b11f1782607a1fe5f033ccf9dc17404db020a0dedff94183596ee67ad4177d790000000000ffffffff012202000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d0340")) - assert(encodedHex.endsWith("5b0063036f7264010118746578742f706c61696e3b636861727365743d7574662d3800377b2270223a226272632d3230222c226f70223a227472616e73666572222c227469636b223a226f616466222c22616d74223a223230227d6821c00f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000")) - - val signedTransaction = output.transaction - assert(signedTransaction.isInitialized) - } - - @Test - fun testSignNftInscriptionCommit() { - // Successfully broadcasted: https://www.blockchain.com/explorer/transactions/btc/f1e708e5c5847339e16accf8716c14b33717c14d6fe68f9db36627cecbde7117 - - val privateKeyData = (Numeric.hexStringToByteArray("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129")) - val fullAmount = 32400 - val minerFee = 1300 - val inscribeAmount = fullAmount - minerFee; - val txId = Numeric.hexStringToByteArray("992faa0d60f29d77cdae687c300d288a3b075b3c7e1e3b42ad537222c3909557") - val payload = Numeric.hexStringToByteArray(nftInscriptionImageData) + val dustSatoshis = 546.toLong() + val txIdCommit = Numeric.hexStringToByteArray("797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1").reversedArray() val privateKey = PrivateKey(privateKeyData) - val publicKey = privateKey.getPublicKeySecp256k1(true) - val pubKeyHash = Hash.ripemd(Hash.sha256(publicKey.data())) - - val p2wpkh = BitcoinScript.buildPayToWitnessPubkeyHash(pubKeyHash) - val outputInscribe = BitcoinScript.buildOrdinalNftInscription("image/png", payload, publicKey.data()) - val outputInscribeProto = Bitcoin.TransactionOutput.parseFrom(outputInscribe) - - val input = Bitcoin.SigningInput.newBuilder() - .setIsItBrcOperation(true) - .addPrivateKey(ByteString.copyFrom(privateKeyData)) - - val unspentOutputPoint = Bitcoin.OutPoint.newBuilder() - .setHash(ByteString.copyFrom(txId)) - .setIndex(0) - .build() - - val utxo = Bitcoin.UnspentTransaction.newBuilder() - .setScript(ByteString.copyFrom(p2wpkh.data())) - .setAmount(fullAmount.toLong()) - .setVariant(Bitcoin.TransactionVariant.P2WPKH) - .setOutPoint(unspentOutputPoint) - .build() - input.addUtxo(utxo) - - val utxoPlan = Bitcoin.UnspentTransaction.newBuilder() - .setScript(outputInscribeProto.script) - .setAmount(inscribeAmount.toLong()) - .setVariant(Bitcoin.TransactionVariant.NFTINSCRIPTION) - .build() - - val plan = Bitcoin.TransactionPlan.newBuilder() - .addUtxos(utxoPlan) - .build() - input.plan = plan - - val output = AnySigner.sign(input.build(), BITCOIN, SigningOutput.parser()) + val publicKey = ByteString.copyFrom(privateKey.getPublicKeySecp256k1(true).data()) + + val utxo0 = BitcoinV2.Input.newBuilder() + .setOutPoint(BitcoinV2.OutPoint.newBuilder().apply { + hash = ByteString.copyFrom(txIdCommit) + vout = 0 + }) + .setValue(7_000) + .setSighashType(BitcoinSigHashType.ALL.value()) + .setScriptBuilder(BitcoinV2.Input.InputBuilder.newBuilder().apply { + brc20Inscribe = BitcoinV2.Input.InputBrc20Inscription.newBuilder().apply { + inscribeTo = publicKey + ticker = "oadf" + transferAmount = "20" + }.build() + }) + + val out0 = BitcoinV2.Output.newBuilder() + .setValue(dustSatoshis) + .setBuilder(BitcoinV2.Output.OutputBuilder.newBuilder().apply { + p2Wpkh = BitcoinV2.PublicKeyOrHash.newBuilder().setPubkey(publicKey).build() + }) + + val signingInput = BitcoinV2.SigningInput.newBuilder() + .setVersion(BitcoinV2.TransactionVersion.V2) + .addPrivateKeys(ByteString.copyFrom(privateKeyData)) + .addInputs(utxo0) + .addOutputs(out0) + .setInputSelector(BitcoinV2.InputSelector.UseAll) + .setChainInfo(BitcoinV2.ChainInfo.newBuilder().apply { + p2PkhPrefix = 0 + p2ShPrefix = 5 + }) + .setDangerousUseFixedSchnorrRng(true) + .setFixedDustThreshold(dustSatoshis) + .build() + + val legacySigningInput = Bitcoin.SigningInput.newBuilder().apply { + signingV2 = signingInput + } + + val output = AnySigner.sign(legacySigningInput.build(), BITCOIN, SigningOutput.parser()) assertEquals(output.error, SigningError.OK) - assertEquals(output.transactionId, "f1e708e5c5847339e16accf8716c14b33717c14d6fe68f9db36627cecbde7117") - assertEquals(Numeric.toHexString(output.encoded.toByteArray()), "0x02000000000101992faa0d60f29d77cdae687c300d288a3b075b3c7e1e3b42ad537222c39095570000000000ffffffff017c790000000000002251202ac69a7e9dba801e9fcba826055917b84ca6fba4d51a29e47d478de603eedab602473044022054212984443ed4c66fc103d825bfd2da7baf2ab65d286e3c629b36b98cd7debd022050214cfe5d3b12a17aaaf1a196bfeb2f0ad15ffb320c4717eb7614162453e4fe0121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000") - - val signedTransaction = output.transaction - assert(signedTransaction.isInitialized) + assertEquals(output.signingResultV2.error, SigningError.OK) + assertEquals(Numeric.toHexString(output.signingResultV2.encoded.toByteArray()), "0x02000000000101b11f1782607a1fe5f033ccf9dc17404db020a0dedff94183596ee67ad4177d790000000000ffffffff012202000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d03406a35548b8fa4620028e021a944c1d3dc6e947243a7bfc901bf63fefae0d2460efa149a6440cab51966aa4f09faef2d1e5efcba23ab4ca6e669da598022dbcfe35b0063036f7264010118746578742f706c61696e3b636861727365743d7574662d3800377b2270223a226272632d3230222c226f70223a227472616e73666572222c227469636b223a226f616466222c22616d74223a223230227d6821c00f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000") + assertEquals(Numeric.toHexString(output.signingResultV2.txid.toByteArray()), "0x7046dc2689a27e143ea2ad1039710885147e9485ab6453fa7e87464aa7dd3eca") } @Test - fun testSignNftInscriptionReveal() { - // Successfully broadcasted: https://www.blockchain.com/explorer/transactions/btc/173f8350b722243d44cc8db5584de76b432eb6d0888d9e66e662db51584f44ac - + fun testSignBrc20Transfer() { + // Successfully broadcasted: https://www.blockchain.com/explorer/transactions/btc/3e3576eb02667fac284a5ecfcb25768969680cc4c597784602d0a33ba7c654b7 val privateKeyData = (Numeric.hexStringToByteArray("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129")) - val inscribeAmount = 31100 - val dustSatoshis = 546 - val txId = Numeric.hexStringToByteArray("1771decbce2766b39d8fe66f4dc11737b3146c71f8cc6ae1397384c5e508e7f1") - val payload = Numeric.hexStringToByteArray(nftInscriptionImageData) + val dustSatoshis = 546.toLong() + val txIdInscription = Numeric.hexStringToByteArray("7046dc2689a27e143ea2ad1039710885147e9485ab6453fa7e87464aa7dd3eca").reversedArray() + val txIdForFees = Numeric.hexStringToByteArray("797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1").reversedArray() val privateKey = PrivateKey(privateKeyData) - val publicKey = privateKey.getPublicKeySecp256k1(true) - val pubKeyHash = Hash.ripemd(Hash.sha256(publicKey.data())) - val p2wpkh = BitcoinScript.buildPayToWitnessPubkeyHash(pubKeyHash) - val outputInscribe = BitcoinScript.buildOrdinalNftInscription("image/png", payload, publicKey.data()) - val outputInscribeProto = Bitcoin.TransactionOutput.parseFrom(outputInscribe) - - val input = Bitcoin.SigningInput.newBuilder() - .setIsItBrcOperation(true) - .addPrivateKey(ByteString.copyFrom(privateKeyData)) - - val unspentOutputPoint0 = Bitcoin.OutPoint.newBuilder() - .setHash(ByteString.copyFrom(txId)) - .setIndex(0) - .build() - - val utxo = Bitcoin.UnspentTransaction.newBuilder() - .setScript(outputInscribeProto.script) - .setAmount(inscribeAmount.toLong()) - .setVariant(Bitcoin.TransactionVariant.NFTINSCRIPTION) - .setSpendingScript(outputInscribeProto.spendingScript) - .setOutPoint(unspentOutputPoint0) - .build() - - input.addUtxo(utxo) - - val utxoPlan = Bitcoin.UnspentTransaction.newBuilder() - .setScript(ByteString.copyFrom(p2wpkh.data())) - .setAmount(dustSatoshis.toLong()) - .setVariant(Bitcoin.TransactionVariant.P2WPKH) - .build() - - val plan = Bitcoin.TransactionPlan.newBuilder() - .addUtxos(utxoPlan) - .build() - input.plan = plan - - val output = AnySigner.sign(input.build(), BITCOIN, SigningOutput.parser()) + val publicKey = ByteString.copyFrom(privateKey.getPublicKeySecp256k1(true).data()) + val bobAddress = "bc1qazgc2zhu2kmy42py0vs8d7yff67l3zgpwfzlpk" + + val utxo0 = BitcoinV2.Input.newBuilder() + .setOutPoint(BitcoinV2.OutPoint.newBuilder().apply { + hash = ByteString.copyFrom(txIdInscription) + vout = 0 + }) + .setValue(dustSatoshis) + .setSighashType(BitcoinSigHashType.ALL.value()) + .setScriptBuilder(BitcoinV2.Input.InputBuilder.newBuilder().apply { + p2Wpkh = BitcoinV2.PublicKeyOrHash.newBuilder().setPubkey(publicKey).build() + }) + + val utxo1 = BitcoinV2.Input.newBuilder() + .setOutPoint(BitcoinV2.OutPoint.newBuilder().apply { + hash = ByteString.copyFrom(txIdForFees) + vout = 1 + }) + .setValue(16_400) + .setSighashType(BitcoinSigHashType.ALL.value()) + .setScriptBuilder(BitcoinV2.Input.InputBuilder.newBuilder().apply { + p2Wpkh = BitcoinV2.PublicKeyOrHash.newBuilder().setPubkey(publicKey).build() + }) + + val out0 = BitcoinV2.Output.newBuilder() + .setValue(dustSatoshis) + .setToAddress(bobAddress) + + val changeOutput = BitcoinV2.Output.newBuilder() + .setValue(13_400) + .setBuilder(BitcoinV2.Output.OutputBuilder.newBuilder().apply { + p2Wpkh = BitcoinV2.PublicKeyOrHash.newBuilder().setPubkey(publicKey).build() + }) + + val signingInput = BitcoinV2.SigningInput.newBuilder() + .setVersion(BitcoinV2.TransactionVersion.V2) + .addPrivateKeys(ByteString.copyFrom(privateKeyData)) + .addInputs(utxo0) + .addInputs(utxo1) + .addOutputs(out0) + .addOutputs(changeOutput) + .setInputSelector(BitcoinV2.InputSelector.UseAll) + .setChainInfo(BitcoinV2.ChainInfo.newBuilder().apply { + p2PkhPrefix = 0 + p2ShPrefix = 5 + }) + .setDangerousUseFixedSchnorrRng(true) + .setFixedDustThreshold(dustSatoshis) + .build() + + val legacySigningInput = Bitcoin.SigningInput.newBuilder().apply { + signingV2 = signingInput + } + + val output = AnySigner.sign(legacySigningInput.build(), BITCOIN, SigningOutput.parser()) assertEquals(output.error, SigningError.OK) - assertEquals(output.transactionId, "173f8350b722243d44cc8db5584de76b432eb6d0888d9e66e662db51584f44ac") - - val encodedHex = Numeric.toHexString(output.encoded.toByteArray()) - val expectedHex = nftInscriptionRawHex - - // Offset is the 0x prefix. - val offset = 2; - assertEquals(encodedHex.length, 15658 + offset) - assertEquals(encodedHex.substring(0 + offset, 164 + offset), expectedHex.substring(0, 164)) - assertEquals(encodedHex.substring(292 + offset, 15658 + offset), expectedHex.substring(292, 15658)) - - val signedTransaction = output.transaction - assert(signedTransaction.isInitialized) + assertEquals(output.signingResultV2.error, SigningError.OK) + assertEquals(Numeric.toHexString(output.signingResultV2.encoded.toByteArray()), "0x02000000000102ca3edda74a46877efa5364ab85947e148508713910ada23e147ea28926dc46700000000000ffffffffb11f1782607a1fe5f033ccf9dc17404db020a0dedff94183596ee67ad4177d790100000000ffffffff022202000000000000160014e891850afc55b64aa8247b2076f8894ebdf889015834000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d024830450221008798393eb0b7390217591a8c33abe18dd2f7ea7009766e0d833edeaec63f2ec302200cf876ff52e68dbaf108a3f6da250713a9b04949a8f1dcd1fb867b24052236950121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb0248304502210096bbb9d1f0596d69875646689e46f29485e8ceccacde9d0025db87fd96d3066902206d6de2dd69d965d28df3441b94c76e812384ab9297e69afe3480ee4031e1b2060121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000") + assertEquals(Numeric.toHexString(output.signingResultV2.txid.toByteArray()), "0x3e3576eb02667fac284a5ecfcb25768969680cc4c597784602d0a33ba7c654b7") } } - -const val nftInscriptionImageData = -"89504e470d0a1a0a0000000d4948445200000360000002be0803000000f3" + -"0f8d7d000000d8504c54450000003070bf3070af3173bd3078b73870b730" + -"70b73575ba3075ba3075b53070ba3078bb3474bb3474b73074bb3074b733" + -"76b93076b93373bc3373b93073b93376bc3275ba3075ba3572ba3272ba33" + -"75bc3276b83474bb3274bb3274b83276bd3276bb3474bd3474bb3276b934" + -"74bb3474b93274bb3274b93476bb3276bb3375ba3275ba3373bc3273ba32" + -"77bc3375bc3375ba3373bc3374ba3174b93376bb3375bc3375bb3375b933" + -"75bb3374bb3376bb3475bb3375bb3375ba3374bb3276bc3276ba3375bc32" + -"75bc3375bb3275bb3374bc3374bb3375bb7edf10e10000004774524e5300" + -"10101f20202030303030404040404050505050505f606060606f70707070" + -"7f7f7f7f80808080808f8f909090909f9f9f9fa0a0afafafb0bfbfcfcfcf" + -"cfcfdfdfdfdfefefefef6a89059600001c294944415478daeddd6b7bd3d6" + -"ba2ee0d8350eb00c81ec5533cbac69d6da99383334dbe510904b66129383" + -"feff3fda17506888751892255b52eefb634b1259d2a3f1ead5d0f0d61600" + -"000000000000000000000000000000000000000000000000000000000000" + -"000000000000000000000000000000000000000000000000000000000000" + -"000000000000000000000000000000000000000000000000000000000000" + -"000000000000000000000000000000000084eaef3c9b4e274f87f604546e" + -"27ba8abf3a19db1b50a96114dfb01031a8d0cf57f18f0e7a760a54e4b778" + -"c9c2ad1854a27f1c27586cdb3350c1edd7224eb667dfc0aa9e5fc569666e" + -"c460b5f2f0df71063762b08afb8b389b3211ca978771aec82006a5fcf870" + -"39cd8587ce5066f8ba8ac3cc0c6250d0cec738d8c53fed2f2820bb79a89d" + -"08ab185fc545a91321b03a8ce21216ea44c8372c152f2fb1400d375fb722" + -"b6630f427abcf6aee2d544220675c54bc420adb5318b2be25e0c6ec72b8a" + -"2bb4186bdac3dfb5e1495cb5d903fb15bed48657711d4ec67d3b973b6eb8" + -"574fbafe1ac6343cb8cba5e1248a6bb69031eeead8557bba648c3b3a74ed" + -"1c9cc56b74f54e5b913b13aebde82a5ebf9399af8da0ebe17a7ab09170fd" + -"1db2b1ee3dddbce57a3a79b77a5978383e5fb95c8c0e9eba29a33ba3d683" + -"6793d94925e3d6f9686b6bf0a69ab12c3a98ec2819696baa86c39d67cfa6" + -"b377671556846fbe3e385e7d10fbdb59f4ee60fa6c6767e899340d8dd2b3" + -"835974d3c9d9592db759974fbefdc98a06b1a5b09d9dfdf041a2d94429c9" + -"66ed44ebea4f1cde1c63aa1cc4b29fa1ed2923d9dce8b5b6787dbefbbaa9" + -"a641ccbb2f3448eec2f1955587fbcbb748bbeb1ac4e203479a8de46b5d4f" + -"b4e683c4bfbf7fbda6bf3f73ac59bfe16233d5e106ea446318eb77bc9eea" + -"f0d7ac6d189cac27618f1c6ed66cbca99baf5b9bb1965bb185e3cd9a9dae" + -"bb35bfd18819c258af876be82d0c4207d3fa231639e2acd5a431f15a4bc4" + -"2e7cdd3a6bf57ec3f75eeb8e98b75ce8cc2d58f1787d89d8bcce80fde290" + -"b34ef5bdf13f2f3d37a9cee762024617027679385a65ab06b5558a0246fb" + -"03369facfe5ad6e88d802160950f5e3787b1530143c07e48d7eb51955b37" + -"383c1730da2caa345d35ac22bf5d6dc6048c7606ecf27054d77a18db93ea" + -"6ac5270e39ed0bd87c7f54ef660ec6c7972623721703f6e970773d4b398d" + -"267301e34e05ecf470cddfdd359acc2f058c3b10b04faf27a3cd2c42b8bd" + -"7b583e6502c65a9598ec7b393fdc54b66ea66c7a7c2a6034dddb2283d6e9" + -"ebe978bb496be76eef4e0ee7a702467b03f6e9d3fcf8f574321edd6bee87" + -"e88f46e3e9e1f1fcf4536ee9e875159a12b07fddbb77af7d1fa87fefde76" + -"c6f4949f1c721a12b0f64e7a1030044cc0b8038ed2cfc51702062b9aa69f" + -"8bd3d67ea86b0143c0ea93d14574c411300143c0040c5609d82b01831565" + -"7cf7c31f020602b66490b174b6238e80d51730df5f848009187721601f04" + -"0c56f43863259bb67ea68cef3cfbd31147c07c2604cc6782554ec63f050c" + -"3404040c015ba72e764611b03604ec0f479ca604ec42c060551d9c183b11" + -"3004ac3e9d7cc70d011330b8ad830bc40818cdd1c125ce8e040c01ab4f27" + -"175345c0040c6e3bedde57fdbc17301a23ea5ec03af891e862c09e0818a8" + -"a7040c1d81353af3059708d84602e6cb5558b30e3e95f5ed45344707e715" + -"59391b011330ee84eebd3cd513309aa37bafff5ad8977604ec8380c18a76" + -"3bb7c65917977aa4b51edfa580591691757bd8b9824ac070c7e2ae923b1f" + -"b08bce05ccaa6dac5bf79e1a4d058c06b94b01b3e60d6bd7b9a9b11695a2" + -"493af77247c60b382f1c6e1a14b076be9e68cd1b9aa4732fd877709511ba" + -"19b05f5c31a0be5b967606ec4cc0d013d848c0ac18c0da75aeab9df160af" + -"e770b36e19cf655f752d608e366bd7b5a97b0301a325016be5e4f3875e68" + -"a649baf64ab3179a69cb09b9e8d8e7f1be258dba676965c0bc6f89a6408d" + -"265e07a3513af6dcc8eb60344bc75e087b2b60344ac7de57f1b60acdd2b1" + -"af41f7b60aad39237f71bd80fa6e5ada389dde17c8d22c1d9b4e1f7b5b85" + -"46e9d63a82be1d8c86e9d674fa879d5ba998ee06ac8593f71e9b4c8f53b2" + -"3ebbe6fad22cddfafa8789b9beb4a72dd0bec988befa81a6e95463db5444" + -"9aa6538f66232bd3d330a75d9a2b652a224df3be4b177debfae2b6c50d25" + -"7748971a6f5645a471ba3495e3b1995234cd6e87a672ec5a1591a6e9d2fc" + -"d8899952b4e9bea56d53398e4ce4a071e2ee3c697e6f22078d73d69d87b3" + -"9135a568d359d9b627cdd79e33d3386fbbf31d7cb1256fd019a8cd43df1f" + -"4bf38c3bf3f0c873661a68b733a7a5c7603450771e841d593080e6e975a6" + -"35f0be3bed1a3ae4ac2b4f8f4ebdcf4cbbcecb76cd7f883d06a381de76a4" + -"4fffd063309a68da913efd63af5bd244e38ef4e927de06a36da5559b56b2" + -"38f2188c26caead3b7693e7da44b4f235d77a3bd7dad4b4f239d76a28dd8" + -"d3a5a799de76a23bf0d8a28834d3a4136dc489b9f434d36e27aefdef3511" + -"69a64127da88a7e6d2d350d75d6870c79a883454d4818bff634d449aeaa8" + -"03fd8189053968aa7107ba1c334d449a2a6b36625bdeb93cd544a4a9b226" + -"41bcf211604519ab0644edef71f87a66362c63b2d4453b3a0453af33d35c" + -"93d6f7b8233d0edad9e568c7c237b11e070d76ddf29bb0c7adbf42d06951" + -"cb6fc2a6e671d064472d3f4123f33868b2dd763f46ca7a0a6645299ca177" + -"fbfa40f79db57a3ae2cc6366da7b13d6fcd7a916195b3f7070d9bcac09f5" + -"8d6fd43f760b468b6fc21adf879bb905a3e94e5b5c232edc82d1e69bb086" + -"d7888f634fc168ba517bcfd299898834df756ba7f35d5b508ae6cb78272c" + -"fed8e40dcf6a807a178c569ca74d9e8f98350ff1dc71a521321bf50d6e73" + -"0c634d7ada206a679b63167b558536c85a37a0b96d8ecc014c85484b6ac4" + -"c6be76993980cd1c555a5223367408cb1cc0f4106992710b87b0990a91d6" + -"d488d7ad1bc2b207b0b1634a931c659eaec3b60d605e05a35932e72336f1" + -"59d8fdcc0df6c596b4a9cdd1c0373f162ddb5eeeb8cc4761f1a2697d8edf" + -"622d0ebad3e668da63a5ec0e871607cdf332fb9cfd6793b6b59f5d206a71" + -"d0c0212cfb9cbd68522731bb836816074df4266ecb6d58f60d98018c461a" + -"e59cb68de9d5ff1c1bc068a1a81d27eefd2b03185d1cc2e2bd56e4cb0046" + -"3befc21a91b0dc7c19c068aac175dcf4d161e72a3680d1562ff3cedef8dd" + -"667b893fe76ee0b9018cc6eae50e61f16293cfc37ecbddbcf8574791e69a" + -"e49fc18b8d4da4ed1fe76f9d5988345a947f0e6faad5717f11b06ddb0e21" + -"edee73c4717cb28932f179c086e970d0fe3ec7e73271edd3d5872143ab0e" + -"07dd2812e378b6de41ecf955d0568d1c3e3a5124c6f1628defaf840d5f71" + -"fc2f478fe69b849dcd6b6bd8f7f7023748079156f83df0848e0fd611b19d" + -"45e0d65cba01a3157a27a109abbfd9713f0add16eb04d0b1dbb02f11dba9" + -"b53afc77f086b801a33db6c3cfeb1afb89fdbdabf0cd78e3a8d11ee378f3" + -"117bb228b00dffe93b68b4c87e91842d6a983cb51315d9024f98e972c22a" + -"ef760c0bc54bbee87ac22a8d5891de867c713712164715dd8a15ea6dc817" + -"77266195743b0ac74bbeb833095b3d62e3452c5fdc1593c2095bada158ac" + -"75f8c55c7f9ef61a5d178f58e96ec7fde2f18a0f1d23da6c705efca42fb7" + -"6a47d1d6a1f951742261c725cefbe2b762c57b1b9fe7cf7bc192bbd8ea28" + -"1eb1e2bd8dcfd3a3b437e8c48d588932b1d01bcf257a1b6ebfe85099f8a6" + -"4c0042bb1dc35999dfae3ca44326d775d589a56ebe74e7e9da2056aa8a8b" + -"f7f296b2df59941abe2c904de706b1f3eaebc461b9d8ce75377027f6edfb" + -"5886155787e74f1c0b3a697c5eae4eacb23a8c0fdd7dd159a59e8925d689" + -"fde372d5e17d070175626e3ff179a9eaf0d2d26ca813f307b172cd8dcb7d" + -"d5212296ffc2f35ea951f058ef903b5227febe4ab363e763a99b2f333770" + -"2b96ff7511a5de4a89cfc58b3b16b179a941ac546fdec40ddc8ad5466f03" + -"11ab8f07cbdc5dfb7547ccb443743bea8b97de06225657bcb40ea1744351" + -"eb1036d6edd03a84fa2236132ff8a14edcd7db8016743bf43620d1fdf32a" + -"6ebeec47a8eb56ccbc0dc8aa137f77f3057546ecbd275fd0bc3a517508b5" + -"d589aa43088fd8b9ea106a54e8eb22ac67034507b1379e2cc3e69b1d9a1b" + -"50db2066f882fa0631c317d4368819bea0be41ccf005b50d62862fa844e2" + -"33b163c3175434889d9bba0135dab79e28ace94eecc4175542e5111b1f9f" + -"7efaf4e9f850730300000000000000000000000000000000000000000000" + -"000000000000000000000000000000000000000000000000000000000000" + -"00000000000000000068bff16c49effbfffc9fe5ff698f41016fe3253f7d" + -"ff9fd1f2ffb4c740c040c040c0046c15b3aa0ceedcae1b26ec8583b01f7d" + -"b2f483e3b01f7cbef483ff2b608d1657e5d19ddb75bdebd2bb61f9bcbd08" + -"fb9bcb7f7226600276778aab781a34f695dd7fbb35ec770113b0861a25ec" + -"8628e407c7093ff82aa8a05ffab173f7600276a76ac49f4a55888135620d" + -"15a28009585dfefbd992b5d488c3b23b30a1427cd2fa80f5968fc28e8075" + -"22602b9f3d256bc471e20e0ca81167657b234d0ed860f94ffc216002965a" + -"23f64a55884159a9a3421430016b6cc0126bc417e52ac4803d584b852860" + -"02d6dc808dcbd488e3943d987bf7564b85286002d6dc8095aa11a3943db8" + -"c8fb6b8b3a2a440113b0e6062c312c2fca5588b9bbf0f1f24f8c054cc03a" + -"1db01235e23875174e8b5688010d150113b036072ca946bce895aa107393" + -"b95c217ed8123001eb74c012e3f24bb90a316716484d15a2800958930356" + -"b8461c67ecc3e9062a4401abd728d5ff4938030ed3ff79ff6e06ac708d78" + -"9a11b0680315a2806dcaa068f17327039658233e2a5721660e4975558802" + -"d6ad800dd734d2f587c3dc3f55c9d933890b9d2093cc80bd2854210e56fb" + -"f8b5062cfcef0b5858c03e1fd21ffdb083fb3b93d9c9591cc7f3bf767fd6" + -"3fcefdd5c3f403fbec60767276f56d83cfa2e860b2d3af3160bdb8508d78" + -"9a19b0a84885384fd8530f6e7ffc77d3a70fd619b0efc7f9b3ab93e820ef" + -"cf0b5860c01e67eca6fe24baba795a14dca7d3c0226c78e3cfdc9a24f16e" + -"b25353c08ad588c39c5651af7c85387c363b4bfea557efc6c3b5042cf900" + -"2c664f05acce80ed1c5fddbaeed611b0fe5e9473f2465f4fb383e89b8493" + -"21baa5648df8aa5c85985123e654883b7b27d9bff8ddd3ba03d69fa41f80" + -"abd952c2bfefe2840d5fdc3a0a0702961eb061b45cd8541fb0fede55c8b3" + -"84689cf5a0372e35a2f58abc7a729ab781652ac47e5ebabe9eb5e33a0396" + -"7b00a25b254491a33017b0d4803dbf8aeb0f5860bcbe9c66412763a192b1" + -"408d38ccfd83bdc215e2f020f4c32f86b505ec79c036fc388a09581501eb" + -"1f27eeac8a03b6b388eb52b68ff8aa5c8598ba87532bc4e1acc8c7d9ab27" + -"603b1f8bff7901ab2060c38ff11a02f63c8e371ab00235e269ee1f8c8a55" + -"88e143f75727c31a02167e006e8ca102b67ac0fa8b780d01fb2dde70c0c2" + -"6bc4fc0a31a5c39f52210e3f16fe40cb65e2aa01eb17b9a95d6c0b587501" + -"3b8ed710b0c2f95a541eb049e8b4c2e57f781db68b8f922bc4de79bc7ac2" + -"560cd8b0607dfeb3805515b0ab780d012b9caf595479c0926ac4455085f8" + -"e751d88e48ab10c72506e545afca800d0bdffffe2c6015052c5e43c08685" + -"cfaf41f5010bad119737f6c528a8467c98da432c3184dd5e6760a5800d4b" + -"f4971e09587b02b6287e76d510b0c01a719250e75d8724f328f529739921" + -"ecd65254ab04ac4cbee28ba180b52560c5cfaf411d01eb85b503972bc4a4" + -"ecfc115221fef9ed7f9519c27e1c245709585426dff1494fc05a12b01203" + -"581d014bfc9d3fe55788e3a4c5812f422ac4ef53aa4665cef06945012bdb" + -"c03d10b076042cf96f5ebe9eec6edffb6c7bb43b3d3ebdf57375042ca946" + -"7c1152216e85d488e9156272043e7d9acf4f3f850e61e5039676037c7a38" + -"feb2ff479337e7a9b76102d682801d25fdfed1d2839addefc779be554fc0" + -"826ac4d3c4f3e6287f16484685786b08bb3c9eecdefbf67fb677d3ceef1f" + -"c25f3e6089f5c3e5e1f6cd6d1f9fa73c4e17b016042c6162c4bf927fc5e8" + -"cdf7dedbd1fc9b84b7fde7b784eeaca4d4f6022ac4901af1617640a2bf4f" + -"eda56b4bcaf9fd43f84b072c7102c77c10b409e3adbff77142486f1d05b3" + -"e9730336ff7665bd77afc28005e7ebf3dfdc3fbffd6d7515be0fff32bf46" + -"7c995ce7f5726bc4495685f83da1cb23f7d7b1f5f7c4e3d15b3d60891dc4" + -"5f93f6fc49e66342ef83ad1cb0cbfd7eeeaf2e13b0e54b7bf6f73dee3fa9" + -"2d6083fc1a719152f8bccdab114fb32ac4bf3e464abcbe7cea9c1ab16cc0" + -"923a1cff480ef971ea733c01ab2060fbfd805f5d2660cb7fb2d83a4b55ae" + -"e8925b233e4c3bc7f26ac48456c28f091cc5e7a3ac4d4b1ac3fe583d608b" + -"b0f1eb4bc24e32ae3e02b65ac0920e7e6d01fb636301cbad118fd2eabcbc" + -"1a31a142bcb5ccc5ef39cbcb649768250336ce9d2272f380677c46015b29" + -"60e783ad3506eccf8d052cb7465ca4b6c6a2ece754a7050be1840e4fe6e8" + -"5a326051d8a14e8de32b01ab22609783ad7506ecfb5cedb5072ce997dd7c" + -"dcf430fd2e649c35bee4578801aeb306c172011b66dd5685ec9e85805511" + -"b0f1568d014bdac27f6e2a602fb377d7517a2730bb46ccaf10f31d656d5a" + -"b9808d836629670d613f09d8ea017bbf5567c0121f562e0e1e6c24603935" + -"e222e3e169668db87a859878bff462c580bd2f7007967c15792160ab07ec" + -"51bd014b5d073075add1fa0296b4317f5fd41f668dede38c64565121263d" + -"aa9eae18b045ce1cfd25efd38eb880950fd89f5bf506ec28e3d1f655c692" + -"beb504ec65d605e628eb59712fe3141f075fb58a1dca57ab05ac97fdf03b" + -"c124ede410b0f2017b5173c0f2a7929f640d66d5066c94f5b8699139bd2e" + -"a3468c8a5588fd074f9f3d9bde7694b5bf4b052ce1684fb31da73ded13b0" + -"f2017b5073c002df863a3978daaf3f60893562488598d4c888d22bc4b47b" + -"9de1647612bec6d48a012bf5a267f25f11b0d201bbd8aa3b6093e0a3194d" + -"8675072ca3463cca2ea87aa9cfa9422bc49d8382ebb7ad18b06905017b20" + -"602b066c5e7bc00abd7b727bf1e6aa03965423be0aa91093b6e445810ab1" + -"3f39297c76af18b0a30a02f648c0560cd887fa033628f4cafcadaf20a8fa" + -"dbe5526bc48779cf07d36ac4a00af149998531560cd8db0a02f68b80ad18" + -"b069fd01dbda2e94b08b719d014bad118ff25a6e6935624085d83f2e7576" + -"0b98800505ace018f6c3f2e855072cb546ccab10536bc4fc0a7158725d7e" + -"0113b0b0806d0dde944d58e55f407c9d5c23e656886935627e8558365f4d" + -"08d813016b48c08e720aa571a141ec9ff505ec28b946ccad10d36ac4dc0a" + -"b174be9a10304d8ea604ec7dee9d48a1883daa2d604935e234a4424cda96" + -"5f022ac459bca18069d3b72d6059ef229f063c0c1abd09ce58545bc0926a" + -"c445488598d42089f22bc4151ef7ae18b0490501f3a0799d01cb7a553270" + -"16f1f6f8f8bcd010567dc0926ac407b390697bcbbbe4a2975b21a61488a7" + -"afa7d3f14d93ca03b6bbfcdfffefb8a02d015b63c02e0afde6d409af83db" + -"2b8d665dbdab0f58628d18522126d68851ce5e4a1cc0e6a37eb192bc54c0" + -"06e1134f4b9c6702b662c07a71d6410d6c1d64d8de9dbe9e5fa63f0dab2d" + -"604935e2554885985823e6558809ff2079f99bea0396f04123016b4cc092" + -"8abef40be0a270c0be8d6687f3ccdbeb1a0216348568103af8a575b6532f" + -"5329cb62d410b0f7a97b55c01a10b0b338fc02388e4b06eccb8748ec2ffe" + -"525bc04262320fde27d91562caf7caae27609390495c02b6a98025dd260d" + -"0b0c60455e3adc4fddb01a0296542306a6e065c05cca9c87d3e75b6b0bd8" + -"60b5832260f506ec6d1c7c01fc2d5e31600927c9abfa027654b2420c1afc" + -"9ee4fca90feb0b58d2ff59f4caecb15eb16736021612b0497068eec72b07" + -"6c9c7676bd2fd26aa9ac469c17a89b333badef8377f86e0d017b19172e12" + -"9f6f07de902f046cc580259e868b8422f1fe555ec0a271de07495d00f86d" + -"75bde60235e2b8f4e0f72170640e18c3570f58efba60c2faffefaf2f8f0d" + -"d8633d015b2d608947272161cf73a73b8de378b193fd4126691b7654a0d5" + -"525d8d38283df88df346b03fc2db442b072cf9839ea4dd496f0d3fa65c43" + -"9346eea980ad16b0b43791f77ef8473b51e07cc245e6287692364e8d736f" + -"736aa811e7e507bf5ede8d6cf2d3fac419c1ab076c90b88d2987a2bf7795" + -"5aa52454ea1743015b2d606973d916b3bf1681eaefec4501b39d1e7fffb9" + -"d4a730bfa5fef4c3a4fb9c9bf7093b653acfd7252bc4dcc1ef43fe3f4fba" + -"3cf43fc6b5046cebf7383462dfe295dc0839ca1b099f8f05ac68c07a1927" + -"d2d5d9d959de0dffb780cd12b2f9e3e53be985dfcc8dd81b7e8f57a99b81" + -"b7252bc4dcc16f9c5ff9250c10c38f714d01eb5da75e259fded88e0793e8" + -"e68df4492fa0057323a6fdff8e2a28dcef5cc08a7d53726ac06ecf373f39" + -"98ecfc7d6cfb3b9328732848de88ab683a9d1ebcbb2a7933302a5b21e60d" + -"7ebd8003b494b09d455c57c09293f17d2746b3d96c169de53742522eb657" + -"ef0ea6d3d9d7703e10b0a2011b5512b0e4f7a1cecea2283a39bbca1d0af2" + -"9fed7e2cf15ce7ba64859853237e08cbe3cd857dfacfa29a5e57f9ea4da9" + -"63372b7eb19d0a58d180ad38843d4a1cc0c29c0715aae56727bc2d320c85" + -"5f7596a399727d880e9eedececec3c3b88aef2df28582960bdf35207602f" + -"f0863ce85d0b01ab65087b94318015e876e7a7bcf8372d647eb20fe51b24" + -"83a02314aa9280155d71282561bdeb3aae73773d60694da802011b5e97f9" + -"d179a1945f146f73649d2fe3d2835fd2cddbf1a603b6b55dea10dcfe16b7" + -"fc523d12b0c2012b595fdc08d86fa50ac441b142b5c4dc8eb7252bc4ccc0" + -"27457370bde980954cd83f0a0f613d012b7c6bba7dbd62c0ca2ca97479bf" + -"e0295ae2da392a5b21669d6983228f1397ae2ad77505ac5495f88fe29f63" + -"2a60c5774ae86dd87f0e13035666c997cb27f90f9352cfb6d56bc471e9c1" + -"2fa5bd1fd8c71b9fd516b0ad41d185f12fff51e273440256e2aab31b3486" + -"cdfbd3c480bd2c9eaff3fb4bdbb05fc3b5f37de9426754309abda0b3fb70" + -"abc68005ecc2bc6310f2391e0958895333a4be384c5a85efcbee1e15ad4e" + -"e6497556dee951a2453c2e5921660c7e6913407a018d8e375bf506ac5099" + -"78dc2f77a578256065aefdb9eb5e5ffeba951ab0adadf1bc4869f26bf236" + -"4c2abf76a6c524604e5d547802c87e40be6a0ed8d6d6243062e74fca7695" + -"2f7a0256aab8ca5e94f7eb98334d3fe907a10b8e5eeef7cb8da39725a69a" + -"46a55b61e3e2d1ccd9fc2f9795ba03b635d83f5fe918e49e09ff190858c9" + -"bb97f41d3bff6b11b269e6a8b21b90b193cc439b756c67fd12fb6e5cb242" + -"4c1dfc06652f52b3af3f597bc002ca89f92467570ef62fd3a3a9c9b1427b" + -"20f1d05cbeffbec6df34af6c1b4d3396418ce7fba3fc4d382e7ec92d1a93" + -"71e9c16f9edb2e4adefcc36fc15c47c03eafe1355fe5187cfe05a7a5a2d9" + -"4abd84d58e0785fef9fdf03fd6df3dbc1991cbd3c39b0bd4de0fd990edcf" + -"8bfade8ad9e5e9ebc9a85f6a13e24fc79351e99db79bb45674d05dc420e9" + -"27ef17dffccb1f4ee9a5edf9fb2df0ffcadad0ff4a5df13ab50f3ab97d14" + -"3e1d1fee86076430fe7169e64faf3b99ae8de86f8fbe1cc2d1bd157ec7bd" + -"d1d75f32de1ddd2b7e64faa36f9bd0caa3fa7df3b7fb9b3e92bb7f6d48a9" + -"63f9d741dc5de54c00000000000000000000000000000000000000000000" + -"000000000000000000000000000000000000000000000000000000000000" + -"000000000000000000000000000000000000000000000000000000000000" + -"00000000000000000000000000000000000000000000008066fbffddd184" + -"8d4adc88950000000049454e44ae426082" - -const val nftInscriptionRawHex = -"020000000001011771decbce2766b39d8fe66f4dc11737b3146c71f8cc6a" + -"e1397384c5e508e7f10000000000ffffffff012202000000000000160014" + -"e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d0340cc1e7b0b5fa18b28" + -"dce702e4e8ed2e91069d682b8daa3a773774bfc7d0e6f737d403016a9016" + -"b58a92592ad0b41682e6209167444eb56605532b28e9be922d3afdda1d00" + -"63036f7264010109696d6167652f706e67004d080289504e470d0a1a0a00" + -"00000d4948445200000360000002be0803000000f30f8d7d000000d8504c" + -"54450000003070bf3070af3173bd3078b73870b73070b73575ba3075ba30" + -"75b53070ba3078bb3474bb3474b73074bb3074b73376b93076b93373bc33" + -"73b93073b93376bc3275ba3075ba3572ba3272ba3375bc3276b83474bb32" + -"74bb3274b83276bd3276bb3474bd3474bb3276b93474bb3474b93274bb32" + -"74b93476bb3276bb3375ba3275ba3373bc3273ba3277bc3375bc3375ba33" + -"73bc3374ba3174b93376bb3375bc3375bb3375b93375bb3374bb3376bb34" + -"75bb3375bb3375ba3374bb3276bc3276ba3375bc3275bc3375bb3275bb33" + -"74bc3374bb3375bb7edf10e10000004774524e530010101f202020303030" + -"30404040404050505050505f606060606f707070707f7f7f7f8080808080" + -"8f8f909090909f9f9f9fa0a0afafafb0bfbfcfcfcfcfcfdfdfdfdfefefef" + -"ef6a89059600001c294944415478daeddd6b7bd3d6ba2ee0d8350eb00c81" + -"ec5533cbac69d6da99383334dbe510904b66129383feff3fda1750688875" + -"1892255b52eefb634b1259d2a3f1ead5d0f0d61600000000000000000000" + -"000000000000000000000000000000000000000000000000000000000000" + -"000000000000000000000000000000000000000000000000000000000000" + -"000000000000000000000000000000000000000000000000000000000000" + -"004d08020000000000000084eaef3c9b4e274f87f604546e27ba8abf3a19" + -"db1b50a96114dfb01031a8d0cf57f18f0e7a760a54e4b778c9c2ad1854a2" + -"7f1c27586cdb3350c1edd7224eb667dfc0aa9e5fc569666ec460b5f2f0df" + -"71063762b08afb8b389b3211ca978771aec82006a5fcf87039cd8587ce50" + -"66f8ba8ac3cc0c6250d0cec738d8c53fed2f2820bb79a89d08ab185fc545" + -"a91321b03a8ce21216ea44c8372c152f2fb1400d375fb722b6630f427abc" + -"f6aee2d544220675c54bc420adb5318b2be25e0c6ec72b8a2bb4186bdac3" + -"dfb5e1495cb5d903fb15bed48657711d4ec67d3b973b6eb8574fbafe1ac6" + -"343cb8cba5e1248a6bb69031eeead8557bba648c3b3a74ed1c9cc56b74f5" + -"4e5b913b13aebde82a5ebf9399af8da0ebe17a7ab09170fd1db2b1ee3ddd" + -"bce57a3a79b77a5978383e5fb95c8c0e9eba29a33ba3d6836793d94925e3" + -"d6f9686b6bf0a69ab12c3a98ec2819696baa86c39d67cfa6b37767155684" + -"6fbe3e385e7d10fbdb59f4ee60fa6c6767e899340d8dd2b3835974d3c9d9" + -"592db759974fbefdc98a06b1a5b09d9dfdf041a2d94429c966ed44ebea4f" + -"1cde1c63aa1cc4b29fa1ed2923d9dce8b5b6787dbefbbaa9a641ccbb2f34" + -"48eec2f1955587fbcbb748bbeb1ac4e203479a8de46b5d4fb4e683c4bfbf" + -"7fbda6bf3f73ac59bfe16233d5e106ea446318eb77bc9eeaf0d7ac6d189c" + -"ac27618f1c6ed66cbca99baf5b9b4d0802b1965bb185e3cd9a9daebb35bf" + -"d18819c258af876be82d0c4207d3fa231639e2acd5a431f15a4bc42e7cdd" + -"3a6bf57ec3f75eeb8e98b75ce8cc2d58f1787d89d8bcce80fde290b34ef5" + -"bdf13f2f3d37a9cee762024617027679385a65ab06b5558a0246fb03369f" + -"acfe5ad6e88d802160950f5e3787b1530143c07e48d7eb51955b37383c17" + -"30da2caa345d35ac22bf5d6dc6048c7606ecf27054d77a18db93ea6ac527" + -"0e39ed0bd87c7f54ef660ec6c7972623721703f6e970773d4b398d267301" + -"e34e05ecf470cddfdd359acc2f058c3b10b04faf27a3cd2c42b8bd7b583e" + -"6502c65a9598ec7b393fdc54b66ea66c7a7c2a6034dddb2283d6e9ebe978" + -"bb496be76eef4e0ee7a702467b03f6e9d3fcf8f574321edd6bee87e88f46" + -"e3e9e1f1fcf4536ee9e875159a12b07fddbb77af7d1fa87fefde76c6f494" + -"9f1c721a12b0f64e7a1030044cc0b8038ed2cfc51702062b9aa69f8bd3d6" + -"7ea86b0143c0ea93d14574c411300143c0040c5609d82b018315657cf7c3" + -"1f020602b66490b174b6238e80d51730df5f848009187721601f040c56f4" + -"3863259bb67ea68cef3cfbd31147c07c2604cc6782554ec63f050c340404" + -"0c015ba72e764611b03604ec0f479ca604ec42c060551d9c183b113004ac" + -"3e9d7cc70d011330b8ad830bc40818cdd1c125ce8e040c01ab4f27175345" + -"c0040c6e3bedde57fdbc17301a23ea5ec03af891e862c09e0818a84d0802" + -"a7040c1d81353af3059708d84602e6cb5558b30e3e95f5ed45344707e715" + -"59391b011330ee84eebd3cd513309aa37bafff5ad8977604ec8380c18a76" + -"3bb7c65917977aa4b51edfa580591691757bd8b9824ac070c7e2ae923b1f" + -"b08bce05ccaa6dac5bf79e1a4d058c06b94b01b3e60d6bd7b9a9b11695a2" + -"493af77247c60b382f1c6e1a14b076be9e68cd1b9aa4732fd877709511ba" + -"19b05f5c31a0be5b967606ec4cc0d013d848c0ac18c0da75aeab9df160af" + -"e770b36e19cf655f752d608e366bd7b5a97b0301a325016be5e4f3875e68" + -"a649baf64ab3179a69cb09b9e8d8e7f1be258dba676965c0bc6f89a6408d" + -"265e07a3513af6dcc8eb60344bc75e087b2b60344ac7de57f1b60acdd2b1" + -"af41f7b60aad39237f71bd80fa6e5ada389dde17c8d22c1d9b4e1f7b5b85" + -"46e9d63a82be1d8c86e9d674fa879d5ba998ee06ac8593f71e9b4c8f53b2" + -"3ebbe6fad22cddfafa8789b9beb4a72dd0bec988befa81a6e95463db5444" + -"9aa6538f66232bd3d330a75d9a2b652a224df3be4b177debfae2b6c50d25" + -"7748971a6f5645a471ba3495e3b1995234cd6e87a672ec5a1591a6e9d2fc" + -"d8899952b4e9bea56d53398e4ce4a071e2ee3c697e6f22078d73d69d87b3" + -"9135a568d359d9b627cdd79e33d3386fbbf31d7cb1256fd019a8cd43df1f" + -"4bf38c3bf3f0c873661a68b733a7a5c7603450771e841d593080e6e975a6" + -"35f0be3bed1a3ae4ac2b4d08024f8f4ebdcf4cbbcecb76cd7f883d06a381" + -"de76a44fffd063309a68da913efd63af5bd244e38ef4e927de06a36da555" + -"9b56b238f2188c26caead3b7693e7da44b4f235d77a3bd7dad4b4f239d76" + -"a28dd8d3a5a799de76a23bf0d8a28834d3a4136dc489b9f434d36e27aefd" + -"ef351169a64127da88a7e6d2d350d75d6870c79a883454d4818bff634d44" + -"9aeaa803fd8189053968aa7107ba1c334d449a2a6b36625bdeb93cd544a4" + -"a9b22641bcf211604519ab0644edef71f87a66362c63b2d4453b3a0453af" + -"33d35c93d6f7b8233d0edad9e568c7c237b11e070d76ddf29bb0c7adbf42" + -"d06951cb6fc2a6e671d064472d3f4123f33868b2dd763f46ca7a0a664529" + -"9ca177fbfa40f79db57a3ae2cc6366da7b13d6fcd7a916195b3f7070d9bc" + -"ac09f58d6fd43f760b468b6fc21adf879bb905a3e94e5b5c232edc82d1e6" + -"9bb086d7888f634fc168ba517bcfd299898834df756ba7f35d5b508ae6cb" + -"78272cfed8e40dcf6a807a178c569ca74d9e8f98350ff1dc71a521321bf5" + -"0d6e730c634d7ada206a679b63167b558536c85a37a0b96d8ecc014c8548" + -"4b6ac4c6be76993980cd1c555a5223367408cb1cc0f4106992710b87b099" + -"0a91d6d488d7ad1bc2b207b0b1634a931c659eaec3b60d605e05a35932e7" + -"2336f159d8fdcc0df6c596b4a9cdd1c0373f162ddb5eeeb8cc4761f1a269" + -"7d8edf622d0ebad3e668da63a5ec0e871607cdf332fb9c4d0802fd6793b6" + -"b59f5d206a71d0c0212cfb9cbd68522731bb836816074df4266ecb6d58f6" + -"0d98018c461ae59cb68de9d5ff1c1bc068a1a81d27eefd2b03185d1cc2e2" + -"bd56e4cb00463befc21a91b0dc7c19c068aac175dcf4d161e72a3680d156" + -"2ff3cedef8dd667b893fe76ee0b9018cc6eae50e61f16293cfc37ecbddbc" + -"f8574791e69ae49fc18b8d4da4ed1fe76f9d5988345a947f0e6faad5717f" + -"11b06ddb0e21edee73c4717cb28932f179c086e970d0fe3ec7e73271edd3" + -"d5872143ab0e07dd2812e378b6de41ecf955d0568d1c3e3a5124c6f1628d" + -"efaf840d5f71fc2f478fe69b849dcd6b6bd8f7f7023748079156f83df084" + -"8e0fd611b19d45e0d65cba01a3157a27a109abbfd9713f0add16eb04d0b1" + -"dbb02f11dba9b53afc77f086b801a33db6c3cfeb1afb89fdbdabf0cd78e3" + -"a8d11ee378f3117bb228b00dffe93b68b4c87e91842d6a983cb51315d902" + -"4f98e972c22aef760c0bc54bbee87ac22a8d5891de867c713712164715dd" + -"8a15ea6dc81777266195743b0ac74bbeb833095b3d62e3452c5fdc1593c2" + -"095bada158ac75f8c55c7f9ef61a5d178f58e96ec7fde2f18a0f1d23da6c" + -"705efca42fb76a47d1d6a1f951742261c725cefbe2b762c57b1b9fe7cf7b" + -"c192bbd8ea281eb1e2bd8dcfd3a3b437e8c48d588932b1d01bcf257a1b6e" + -"bfe85099f8a64c0042bb1dc35999dfae3ca44326d775d589a56ebe74e7e9" + -"da2056aa8a8b4d0802f7f296b2df59941abe2c904de706b1f3eaebc461b9" + -"d8ce75377027f6edfb5886155787e74f1c0b3a697c5eae4eacb23a8c0fdd" + -"7dd159a59e8925d689fde372d5e17d070175626e3ff179a9eaf0d2d26ca8" + -"13f307b172cd8dcb7dd5212296ffc2f35ea951f058ef903b5227febe4ab3" + -"63e763a99b2f3337702b96ff7511a5de4a89cfc58b3b16b179a941ac546f" + -"dec40ddc8ad5466f0311ab8f07cbdc5dfb7547ccb443743bea8b97de0622" + -"5657bcb40ea1744351eb1036d6edd03a84fa2236132ff8a14edcd7db8016" + -"743bf43620d1fdf32a6ebeec47a8eb56ccbc0dc8aa137f77f3057546ecbd" + -"275fd0bc3a517508b5d589aa43088fd8b9ea106a54e8eb22ac67034507b1" + -"379e2cc3e69b1d9a1b50db2066f882fa0631c317d4368819bea0be41ccf0" + -"05b50d62862fa844e233b163c3175434889d9bba0135dab79e28ace94eec" + -"c4175542e5111b1f9f7efaf4e9f850730300000000000000000000000000" + -"000000000000000000000000000000000000000000000000000000000000" + -"00000000000000000000000000000000000068bff16c49effbfffc9fe5ff" + -"698f41016fe3253f7dff9fd1f2ffb4c740c040c040c0046c15b3aa0ceedc" + -"ae1b26ec8583b01f7db2f483e3b01f7cbef483ff2b608d1657e5d19ddb75" + -"bdebd2bb61f9bcbd08fb9bcb7f7226600276778aab781a34f695dd7fbb35" + -"ec770113b0861a25ec8628e407c7093ff82aa84d0802a05ffab173f76002" + -"76a76ac49f4a55888135620d15a28009585dfefbd992b5d488c3b23b30a1" + -"427cd2fa80f5968fc28e807522602b9f3d256bc471e20e0ca81167657b23" + -"4d0ed860f94ffc216002965a23f64a55884159a9a3421430016b6cc0126b" + -"c417e52ac4803d584b85286002d6dc808dcbd488e3943d987bf7564b8528" + -"6002d6dc8095aa11a3943db8c8fb6b8b3a2a440113b0e6062c312c2fca55" + -"88b9bbf0f1f24f8c054cc03a1db01235e23875174e8b5688010d150113b0" + -"36072ca946bce895aa107393b95c217ed8123001eb74c012e3f24bb90a31" + -"6716484d15a2800958930356b8461c67ecc3e9062a4401abd728d5ff4938" + -"030ed3ff79ff6e06ac708d789a11b0680315a2806dcaa068f17327039658" + -"233e2a5721660e4975558802d6ad800dd734d2f587c3dc3f55c9d933890b" + -"9d2093cc80bd2854210e56fbf8b5062cfcef0b5858c03e1fd21ffdb083fb" + -"3b93d9c9591cc7f3bf767fd63fcefdd5c3f403fbec60767276f56d83cfa2" + -"e860b2d3af3160bdb8508d789a19b0a84885384fd8530f6e7ffc77d3a70f" + -"d619b0efc7f9b3ab93e820efcf0b5860c01e67eca6fe24baba795a14dca7" + -"d3c0226c78e3cfdc9a24f16eb25353c08ad588c39c5651af7c85387c363b" + -"4bfea557efc6c3b5042cf9002c664f05acce80ed1c5fddbaeed611b0fe5e" + -"9473f2465f4fb383e89b849321baa5648df8aa5c85985123e654883b7b27" + -"d9bf4d0802f8ddd3ba03d69fa41f80abd952c2bfefe2840d5fdc3a0a0702" + -"961eb061b45cd8541fb0fede55c8b384689cf5a0372e35a2f58abc7a729a" + -"b781652ac47e5ebabe9eb5e33a03967b00a25b254491a33017b0d4803dbf" + -"8aeb0f5860bcbe9c66412763a192b1408d38ccfd83bdc215e2f020f4c32f" + -"86b505ec79c036fc388a09581501eb1f27eeac8a03b6b388eb52b68ff8aa" + -"5c8598ba87532bc4e1acc8c7d9ab27603b1f8bff7901ab2060c38ff11a02" + -"f63c8e371ab00235e269ee1f8c8a5588e143f75727c31a02167e006e8ca1" + -"02b67ac0fa8b780d01fb2dde70c0c26bc4fc0a31a5c39f52210e3f16fe40" + -"cb65e2aa01eb17b9a95d6c0b5875013b8ed710b0c2f95a541eb049e8b4c2" + -"e57f781db68b8f922bc4de79bc7ac2560cd8b0607dfeb3805515b0ab780d" + -"012b9caf595479c0926ac4455085f8e751d88e48ab10c72506e545afca80" + -"0d0bdffffe2c6015052c5e43c08685cfaf41f5010bad119737f6c528a846" + -"7c98da432c3184dd5e6760a5800d4bf4971e09587b02b6287e76d510b0c0" + -"1a719250e75d8724f328f529739921ecd65254ab04ac4cbee28ba180b525" + -"60c5cfaf411d01eb85b503972bc4a4ecfc115221fef9ed7f9519c27e1c24" + -"5709585426dff1494fc05a12b01203581d014bfc9d3fe55788e3a4c5812f" + -"422ac4ef53aa4665cef06945012bdbc03d10b076042cf96f5ebe9eec6edf" + -"fb6c7bb43b3d3ebdf57375042ca9464d08027c1152216e85d488e9156272" + -"043e7d9acf4f3f850e61e5039676037c7a38feb2ff479337e7a9b76102d6" + -"82801d25fdfed1d2839addefc779be554fc0826ac4d3c4f3e6287f164846" + -"85786b08bb3c9eecdefbf67fb677d3ceef1fc25f3e6089f5c3e5e1f6cd6d" + -"1f9fa73c4e17b016042c6162c4bf927fc5e8cdf7dedbd1fc9b84b7fde7b7" + -"84eeaca4d4f6022ac4901af1617640a2bf4feda56b4bcaf9fd43f84b072c" + -"7102c77c10b409e3adbff77142486f1d05b3e9730336ff7665bd77afc280" + -"05e7ebf3dfdc3fbffd6d7515be0fff32bf467c995ce7f5726bc4495685f8" + -"3da1cb23f7d7b1f5f7c4e3d15b3d60891dc45f93f6fc49e66342ef83ad1c" + -"b0cbfd7eeeaf2e13b0e54b7bf6f73dee3fa92d6083fc1a719152f8bccdab" + -"114fb32ac4bf3e464abcbe7cea9c1ab16cc0923a1cff480ef971ea733c01" + -"ab2060fbfd805f5d2660cb7fb2d83a4b55aee8925b233e4c3bc7f26ac484" + -"56c28f091cc5e7a3ac4d4b1ac3fe583d608bb0f1eb4bc24e32ae3e02b65a" + -"c0920e7e6d01fb636301cbad118fd2eabcbc1a31a142bcb5ccc5ef39cbcb" + -"649768250336ce9d2272f380677c46015b2960e783ad3506eccf8d052cb7" + -"465ca4b6c6a2ece754a7050be1840e4fe6e85a326051d8a14e8de32b01ab" + -"22609783ad7506ecfb5cedb5072ce997dd7cdcf430fd2e649c35bee45788" + -"01aeb306c172011b66dd5685ec9e85805511b0f1568d014bdac27f6e4d08" + -"022a602fb377d7517a2730bb46ccaf10f31d656d5ab9808d836629670d61" + -"3f09d8ea017bbf5567c0121f562e0e1e6c24603935e222e3e169668db87a" + -"859878bff462c580bd2f7007967c15792160ab07ec51bd014b5d073075ad" + -"d1fa0296b4317f5fd41f668dede38c64565121263daa9eae18b045ce1cfd" + -"25efd38eb880950fd89f5bf506ec28e3d1f655c692beb504ec65d605e628" + -"eb59712fe3141f075fb58a1dca57ab05ac97fdf03bc124ede410b0f2017b" + -"5173c0f2a7929f640d66d5066c94f5b8699139bd2ea3468c8a5588fd074f" + -"9f3d9bde7694b5bf4b052ce1684fb31da73ded13b0f2017b5073c002df86" + -"3a3978daaf3f60893562488598d4c888d22bc4b47b9de1647612bec6d48a" + -"012bf5a267f25f11b0d201bbd8aa3b6093e0a3194d8675072ca3463cca2e" + -"a87aa9cfa9422bc49d8382ebb7ad18b06905017b20602b066c5e7bc00abd" + -"7b727bf1e6aa03965423be0aa91093b6e445810ab13f39297c76af18b0a3" + -"0a02f648c0560cd887fa033628f4cafcadaf20a8fadbe5526bc48779cf07" + -"d36ac4a00af149998531560cd8db0a02f68b80ad18b069fd01dbda2e94b0" + -"8b719d014bad118ff25a6e6935624085d83f2e75760b98800505ace018f6" + -"c3f2e855072cb546ccab10536bc4fc0a7158725d7e0113b0b0806d0dde94" + -"4d58e55f407c9d5c23e656886935627e8558365f4d08d813016b48c08e72" + -"0aa571a141ec9ff505ec284d0802b946ccad10d36ac4dc0ab174be9a1030" + -"4d8ea604ec7dee9d48a1883daa2d604935e234a4424cda965f022ac459bc" + -"a18069d3b72d6059ef229f063c0c1abd09ce58545bc0926ac445488598d4" + -"2089f22bc4151ef7ae18b0490501f3a0799d01cb7a55327016f1f6f8f8bc" + -"d010567dc0926ac407b390697bcbbbe4a2975b21a61488a7afa7d3f14d93" + -"ca03b6bbfcdfffefb8a02d015b63c02e0afde6d409af83db2b8d665dbdab" + -"0f58628d18522126d68851ce5e4a1cc0e6a37eb192bc54c006e1134f4b9c" + -"6702b662c07a71d6410d6c1d64d8de9dbe9e5fa63f0dab2d604935e25548" + -"85985823e6558809ff2079f99bea0396f04123016b4cc0928abef40be0a2" + -"70c0be8d6687f3ccdbeb1a0216348568103af8a575b6532f5329cb62d410" + -"b0f7a97b55c01a10b0b338fc02388e4b06eccb8748ec2ffe525bc0426232" + -"0fde27d91562caf7caae27609390495c02b6a98025dd260d0b0c60455e3a" + -"dc4fddb01a0296542306a6e065c05cca9c87d3e75b6b0bd860b5832260f5" + -"06ec6d1c7c01fc2d5e31600927c9abfa027654b2420c1afc9ee4fca90feb" + -"0b58d2ff59f4caecb15eb16736021612b0497068eec72b076c9c7676bd2f" + -"d26aa9ac469c17a89b333badef8377f86e0d017b19172e129f6f07de902f" + -"046cc580259e868b8422f1fe555ec0a271de07495d00f86d75bde60235e2" + -"b8f4e0f72170640e18c3570f58efba60c2faffefaf2f8f0d4d0802d8633d" + -"015b2d608947272161cf73a73b8de378b193fd4126691b7654a0d5525d8d" + -"38283df88df346b03fc2db442b072cf9839ea4dd496f0d3fa65c439346ee" + -"a980ad16b0b43791f77ef8473b51e07cc245e6287692364e8d736f736aa8" + -"11e7e507bf5ede8d6cf2d3fac419c1ab076c90b88d2987a2bf77955aa524" + -"54ea1743015b2d606973d916b3bf1681eaefec4501b39d1e7fffb9d4a730" + -"bfa5fef4c3a4fb9c9bf7093b653acfd7252bc4dcc1ef43fe3f4fba3cf43f" + -"c6b5046cebf7383462dfe295dc0839ca1b099f8f05ac68c07a1927d2d5d9" + -"d959de0dffb780cd12b2f9e3e53be985dfcc8dd81b7e8f57a99b81b7252b" + -"c4dcc16f9c5ff9250c10c38f714d01eb5da75e259fded88e0793e8e68df4" + -"492fa0057323a6fdff8e2a28dcef5cc08a7d53726ac06ecf373f3998ecfc" + -"7d6cfb3b9328732848de88ab683a9d1ebcbb2a7933302a5b21e60d7ebd80" + -"03b494b09d455c57c09293f17d2746b3d96c169de53742522eb657ef0ea6" + -"d3d9d7703e10b0a2011b5512b0e4f7a1cecea2283a39bbca1d0af29fed7e" + -"2cf15ce7ba64859853237e08cbe3cd857dfacfa29a5e57f9ea4da963372b" + -"7eb19d0a58d180ad38843d4a1cc0c29c0715aae56727bc2d320c855f7596" + -"a399727d880e9eedececec3c3b88aef2df28582960bdf35207602ff0863c" + -"e85d0b01ab65087b94318015e876e7a7bcf8372d647eb20fe51b2483a023" + -"14aa9280155d714d0802282561bdeb3aae73773d60694da802011b5e97f9" + -"d179a1945f146f73649d2fe3d2835fd2cddbf1a603b6b55dea10dcfe16b7" + -"fc523d12b0c2012b595fdc08d86fa50ac441b142b5c4dc8eb7252bc4ccc0" + -"27457370bde980954cd83f0a0f613d012b7c6bba7dbd62c0ca2ca97479bf" + -"e0295ae2da392a5b21669d6983228f1397ae2ad77505ac5495f88fe29f63" + -"2a60c5774ae86dd87f0e13035666c997cb27f90f9352cfb6d56bc471e9c1" + -"2fa5bd1fd8c71b9fd516b0ad41d185f12fff51e273440256e2aab31b3486" + -"cdfbd3c480bd2c9eaff3fb4bdbb05fc3b5f37de9426754309abda0b3fb70" + -"abc68005ecc2bc6310f2391e0958895333a4be384c5a85efcbee1e15ad4e" + -"e6497556dee951a2453c2e5921660c7e6913407a018d8e375bf506ac5099" + -"78dc2f77a578256065aefdb9eb5e5ffeba951ab0adadf1bc4869f26bf236" + -"4c2abf76a6c524604e5d547802c87e40be6a0ed8d6d6243062e74fca7695" + -"2f7a0256aab8ca5e94f7eb98334d3fe907a10b8e5eeef7cb8da39725a69a" + -"46a55b61e3e2d1ccd9fc2f9795ba03b635d83f5fe918e49e09ff190858c9" + -"bb97f41d3bff6b11b269e6a8b21b90b193cc439b756c67fd12fb6e5cb242" + -"4c1dfc06652f52b3af3f597bc002ca89f92467570ef62fd3a3a9c9b1427b" + -"20f1d05cbeffbec6df34af6c1b4d3396418ce7fba3fc4d382e7ec92d1a93" + -"71e9c16f9edb2e4adefcc36fc15c47c03eafe1354d29015fe5187cfe05a7" + -"a5a2d94abd84d58e0785fef9fdf03fd6df3dbc1991cbd3c39b0bd4de0fd9" + -"90edcf8bfade8ad9e5e9ebc9a85f6a13e24fc79351e99db79bb45674d05d" + -"c420e927ef17dffccb1f4ee9a5edf9fb2df0ffcadad0ff4a5df13ab50f3a" + -"b97d143e1d1fee86076430fe7169e64faf3b99ae8de86f8fbe1cc2d1bd15" + -"7ec7bdd1d75f32de1ddd2b7e64faa36f9bd0caa3fa7df3b7fb9b3e92bb7f" + -"6d48a963f9d741dc5de54c00000000000000000000000000000000000000" + -"000000000000000000000000000000000000000000000000000000000000" + -"000000000000000000000000000000000000000000000000000000000000" + -"00000000000000000000000000000000000000000000000000008066fbff" + -"ddd1848d4adc88950000000049454e44ae4260826821c00f209b6ada5edb" + -"42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000" diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestAnyAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestAnyAddress.kt index ae4b47b4bd0..40f9fe147c9 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestAnyAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestAnyAddress.kt @@ -53,10 +53,10 @@ class TestAnyAddress { fun testCreateWithPublicKeyDerivation() { val coin = CoinType.BITCOIN val pubkey = PublicKey(any_address_test_pubkey.toHexByteArray(), PublicKeyType.SECP256K1) - val address1 = AnyAddress(pubkey, coin, Derivation.BITCOINSEGWIT) + val address1 = AnyAddress(pubkey, coin, Derivation.SEGWIT) assertEquals(address1.description(), any_address_test_address) - val address2 = AnyAddress(pubkey, coin, Derivation.BITCOINLEGACY) + val address2 = AnyAddress(pubkey, coin, Derivation.LEGACY) assertEquals(address2.description(), "1JvRfEQFv5q5qy9uTSAezH7kVQf4hqnHXx") } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt index e3ec6f29b12..6e6d2e995b9 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt @@ -113,13 +113,13 @@ class TestHDWallet { val coin = CoinType.BITCOIN val wallet = HDWallet(words, password) - val key1 = wallet.getKeyDerivation(coin, Derivation.BITCOINSEGWIT) + val key1 = wallet.getKeyDerivation(coin, Derivation.SEGWIT) assertEquals(key1.data().toHex(), "0x1901b5994f075af71397f65bd68a9fff8d3025d65f5a2c731cf90f5e259d6aac") - val key2 = wallet.getKeyDerivation(coin, Derivation.BITCOINLEGACY) + val key2 = wallet.getKeyDerivation(coin, Derivation.LEGACY) assertEquals(key2.data().toHex(), "0x28071bf4e2b0340db41b807ed8a5514139e5d6427ff9d58dbd22b7ed187103a4") - val key3 = wallet.getKeyDerivation(coin, Derivation.BITCOINTESTNET) + val key3 = wallet.getKeyDerivation(coin, Derivation.TESTNET) assertEquals(key3.data().toHex(), "0xca5845e1b43e3adf577b7f110b60596479425695005a594c88f9901c3afe864f") } @@ -137,13 +137,13 @@ class TestHDWallet { val coin = CoinType.BITCOIN val wallet = HDWallet(words, password) - val address1 = wallet.getAddressDerivation(coin, Derivation.BITCOINSEGWIT) + val address1 = wallet.getAddressDerivation(coin, Derivation.SEGWIT) assertEquals(address1, "bc1qumwjg8danv2vm29lp5swdux4r60ezptzz7ce85") - val address2 = wallet.getAddressDerivation(coin, Derivation.BITCOINLEGACY) + val address2 = wallet.getAddressDerivation(coin, Derivation.LEGACY) assertEquals(address2, "1PeUvjuxyf31aJKX6kCXuaqxhmG78ZUdL1") - val address3 = wallet.getAddressDerivation(coin, Derivation.BITCOINTESTNET) + val address3 = wallet.getAddressDerivation(coin, Derivation.TESTNET) assertEquals(address3, "tb1qwgpxgwn33z3ke9s7q65l976pseh4edrzfmyvl0") } diff --git a/codegen/bin/coins b/codegen/bin/coins index a5ea5909e05..4c25b67fc50 100755 --- a/codegen/bin/coins +++ b/codegen/bin/coins @@ -31,9 +31,9 @@ def self.derivation_name(deriv) deriv['name'].downcase end -def self.derivation_enum_name(deriv, coin) +def self.derivation_enum_name(deriv) return "TWDerivationDefault" if deriv['name'].nil? - "TWDerivation" + format_name(coin['name']) + camel_case(deriv['name']) + "TWDerivation" + camel_case(deriv['name']) end def self.coin_img(coin) @@ -53,6 +53,7 @@ coins = JSON.parse(json_string).sort_by { |x| x['coinId'] } # used in some cases for numbering enum values enum_count = 0 +derivations = {} erbs = [ {'template' => 'TWDerivation.h.erb', 'folder' => 'include/TrustWalletCore', 'file' => 'TWDerivation.h'}, diff --git a/codegen/lib/templates/CoinInfoData.cpp.erb b/codegen/lib/templates/CoinInfoData.cpp.erb index d2bbb5d6b71..6e0d29a074e 100644 --- a/codegen/lib/templates/CoinInfoData.cpp.erb +++ b/codegen/lib/templates/CoinInfoData.cpp.erb @@ -51,7 +51,7 @@ const CoinInfo getCoinInfo(TWCoinType coin) { TWCurve<%= format_name(coin['curve']) %>, { <% coin['derivation'].each do |deriv| -%>{ - <%= derivation_enum_name(deriv, coin) %>, + <%= derivation_enum_name(deriv) %>, "<%= deriv['path'] %>", "<%= derivation_name(deriv) %>", TWHDVersion<% if deriv['xpub'].nil? -%>None<% else -%><%= format_name(deriv['xpub']) %><% end -%>, diff --git a/codegen/lib/templates/TWDerivation.h.erb b/codegen/lib/templates/TWDerivation.h.erb index eb7759a55c2..6ef63d074b3 100644 --- a/codegen/lib/templates/TWDerivation.h.erb +++ b/codegen/lib/templates/TWDerivation.h.erb @@ -18,11 +18,10 @@ enum TWDerivation { TWDerivationCustom = 1, // custom, for any coin <% enum_count += 1 -%> <% coins.each do |coin| -%> -<% if coin['derivation'].count > 1 -%> <% coin['derivation'].each_with_index do |deriv, index| -%> -<% if index > 0 or !deriv['name'].nil? -%> - <%= derivation_enum_name(deriv, coin) %> = <% enum_count += 1 -%><%= enum_count %>, -<% end -%> +<% if !deriv['name'].nil? and !derivations.has_key?(deriv['name']) -%> +<% derivations[deriv['name']] = true -%> + <%= derivation_enum_name(deriv) %> = <% enum_count += 1 -%><%= enum_count %>, <% end -%> <% end -%> <% end -%> diff --git a/include/TrustWalletCore/TWBitcoinFee.h b/include/TrustWalletCore/TWBitcoinFee.h deleted file mode 100644 index be4f116d644..00000000000 --- a/include/TrustWalletCore/TWBitcoinFee.h +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "TWData.h" -#include "TWString.h" - -TW_EXTERN_C_BEGIN - -TW_EXPORT_CLASS -struct TWBitcoinFee; - -/// Calculates the fee of any Bitcoin transaction. -/// -/// \param data: the signed transaction in its final form. -/// \param satVb: the satoshis per vbyte amount. The passed on string is interpreted as a unit64_t. -/// \returns the fee denominated in satoshis or nullptr if the transaction failed to be decoded. -TW_EXPORT_STATIC_METHOD -TWString* _Nullable TWBitcoinFeeCalculateFee(TWData* _Nonnull data, TWString* _Nonnull satVb); - -TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWBitcoinScript.h b/include/TrustWalletCore/TWBitcoinScript.h index f1a7844bfb9..b49e65ae1f8 100644 --- a/include/TrustWalletCore/TWBitcoinScript.h +++ b/include/TrustWalletCore/TWBitcoinScript.h @@ -191,26 +191,6 @@ struct TWBitcoinScript* _Nonnull TWBitcoinScriptBuildPayToWitnessPubkeyHash(TWDa TW_EXPORT_STATIC_METHOD struct TWBitcoinScript* _Nonnull TWBitcoinScriptBuildPayToWitnessScriptHash(TWData* _Nonnull scriptHash); -/// Builds the Ordinals inscripton for BRC20 transfer. -/// -/// \param ticker ticker of the brc20 -/// \param amount uint64 transfer amount -/// \param pubkey Non-null pointer to a pubkey -/// \note Must be deleted with \TWBitcoinScriptDelete -/// \return A pointer to the built script -TW_EXPORT_STATIC_METHOD -TWData* _Nonnull TWBitcoinScriptBuildBRC20InscribeTransfer(TWString* _Nonnull ticker, TWString* _Nonnull amount, TWData* _Nonnull pubkey); - -/// Builds the Ordinals inscripton for NFT construction. -/// -/// \param mimeType the MIME type of the payload -/// \param payload the payload to inscribe -/// \param pubkey Non-null pointer to a pubkey -/// \note Must be deleted with \TWBitcoinScriptDelete -/// \return A pointer to the built script -TW_EXPORT_STATIC_METHOD -TWData* _Nonnull TWBitcoinScriptBuildOrdinalNftInscription(TWString* _Nonnull mimeType, TWData* _Nonnull payload, TWData* _Nonnull pubkey); - /// Builds a appropriate lock script for the given address.. /// /// \param address Non-null pointer to an address diff --git a/registry.json b/registry.json index f3a8dca671d..39d399fac92 100644 --- a/registry.json +++ b/registry.json @@ -56,6 +56,7 @@ "blockchain": "Bitcoin", "derivation": [ { + "name": "segwit", "path": "m/84'/2'/0'/0/0", "xpub": "zpub", "xprv": "zprv" @@ -159,9 +160,16 @@ "blockchain": "Bitcoin", "derivation": [ { + "name": "segwit", "path": "m/84'/14'/0'/0/0", "xpub": "zpub", "xprv": "zprv" + }, + { + "name": "legacy", + "path": "m/44'/14'/0'/0/0", + "xpub": "xpub", + "xprv": "xprv" } ], "curve": "secp256k1", @@ -225,6 +233,7 @@ "blockchain": "Bitcoin", "derivation": [ { + "name": "segwit", "path": "m/84'/20'/0'/0/0", "xpub": "zpub", "xprv": "zprv" @@ -258,9 +267,16 @@ "blockchain": "Bitcoin", "derivation": [ { + "name": "legacy", "path": "m/44'/22'/0'/0/0", "xpub": "xpub", "xprv": "xprv" + }, + { + "name": "segwit", + "path": "m/84'/22'/0'/0/0", + "xpub": "zpub", + "xprv": "zprv" } ], "curve": "secp256k1", @@ -324,9 +340,16 @@ "blockchain": "Bitcoin", "derivation": [ { + "name": "segwit", "path": "m/84'/57'/0'/0/0", "xpub": "zpub", "xprv": "zprv" + }, + { + "name": "legacy", + "path": "m/44'/57'/0'/0/0", + "xpub": "xpub", + "xprv": "xprv" } ], "curve": "secp256k1", @@ -1517,9 +1540,16 @@ "blockchain": "Bitcoin", "derivation": [ { + "name": "segwit", "path": "m/84'/156'/0'/0/0", "xpub": "zpub", "xprv": "zprv" + }, + { + "name": "legacy", + "path": "m/44'/156'/0'/0/0", + "xpub": "xpub", + "xprv": "xprv" } ], "curve": "secp256k1", @@ -2842,9 +2872,16 @@ "blockchain": "Bitcoin", "derivation": [ { + "name": "legacy", "path": "m/44'/2301'/0'/0/0", "xpub": "xpub", "xprv": "xprv" + }, + { + "name": "segwit", + "path": "m/84'/2301'/0'/0/0", + "xpub": "zpub", + "xprv": "zprv" } ], "curve": "secp256k1", @@ -4243,6 +4280,7 @@ "blockchain": "Bitcoin", "derivation": [ { + "name": "segwit", "path": "m/44'/105105'/0'/0/0", "xpub": "xpub", "xprv": "xprv" diff --git a/rust/Cargo.lock b/rust/Cargo.lock index e94bd038f53..0eb28d98085 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -476,6 +476,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "derivation-path" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0" + [[package]] name = "derivative" version = "2.2.0" @@ -1633,6 +1639,7 @@ dependencies = [ "tw_misc", "tw_number", "tw_proto", + "tw_utxo", ] [[package]] @@ -1652,6 +1659,16 @@ dependencies = [ "tw_proto", ] +[[package]] +name = "tw_base58_address" +version = "0.1.0" +dependencies = [ + "serde", + "tw_coin_entry", + "tw_encoding", + "tw_hash", +] + [[package]] name = "tw_bech32_address" version = "0.1.0" @@ -1693,8 +1710,11 @@ dependencies = [ "secp256k1", "serde", "serde_json", + "tw_base58_address", + "tw_bech32_address", "tw_coin_entry", "tw_encoding", + "tw_hash", "tw_keypair", "tw_memory", "tw_misc", @@ -1707,6 +1727,8 @@ dependencies = [ name = "tw_coin_entry" version = "0.1.0" dependencies = [ + "derivation-path", + "serde", "serde_json", "tw_encoding", "tw_hash", @@ -1884,6 +1906,7 @@ name = "tw_keypair" version = "0.1.0" dependencies = [ "arbitrary 1.3.0", + "bitcoin", "blake2", "curve25519-dalek", "der", @@ -1895,6 +1918,7 @@ dependencies = [ "pkcs8", "rfc6979", "ring", + "secp256k1", "serde", "serde_json", "sha2", @@ -1902,6 +1926,7 @@ dependencies = [ "starknet-ff", "tw_encoding", "tw_hash", + "tw_keypair", "tw_memory", "tw_misc", "zeroize", @@ -2027,12 +2052,20 @@ dependencies = [ name = "tw_utxo" version = "0.1.0" dependencies = [ + "bech32", "bitcoin", + "byteorder", + "itertools", "secp256k1", + "strum_macros", + "tw_base58_address", + "tw_bech32_address", "tw_coin_entry", "tw_encoding", + "tw_hash", "tw_keypair", "tw_memory", + "tw_misc", "tw_proto", ] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 125780d2be9..2b0bfa58765 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -12,7 +12,9 @@ members = [ "chains/tw_solana", "chains/tw_sui", "chains/tw_thorchain", + "frameworks/tw_utxo", "tw_any_coin", + "tw_base58_address", "tw_bech32_address", "tw_bitcoin", "tw_coin_entry", @@ -26,7 +28,6 @@ members = [ "tw_misc", "tw_number", "tw_proto", - "tw_utxo", "wallet_core_bin", "wallet_core_rs", ] diff --git a/rust/chains/tw_solana/src/address.rs b/rust/chains/tw_solana/src/address.rs index 9832509cdfa..cb2051be84c 100644 --- a/rust/chains/tw_solana/src/address.rs +++ b/rust/chains/tw_solana/src/address.rs @@ -140,7 +140,7 @@ impl FromStr for SolanaAddress { fn from_str(s: &str) -> Result { let bytes = - base58::decode(s, &SOLANA_ALPHABET).map_err(|_| AddressError::FromBase58Error)?; + base58::decode(s, SOLANA_ALPHABET).map_err(|_| AddressError::FromBase58Error)?; let bytes = H256::try_from(bytes.as_slice()).map_err(|_| AddressError::InvalidInput)?; Ok(SolanaAddress { bytes }) } @@ -160,7 +160,7 @@ impl fmt::Debug for SolanaAddress { impl fmt::Display for SolanaAddress { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let encoded = base58::encode(self.bytes.as_slice(), &SOLANA_ALPHABET); + let encoded = base58::encode(self.bytes.as_slice(), SOLANA_ALPHABET); write!(f, "{}", encoded) } } diff --git a/rust/chains/tw_solana/src/blockhash.rs b/rust/chains/tw_solana/src/blockhash.rs index 8c7844dae26..1cfe0a92fa5 100644 --- a/rust/chains/tw_solana/src/blockhash.rs +++ b/rust/chains/tw_solana/src/blockhash.rs @@ -25,7 +25,7 @@ impl Blockhash { impl fmt::Display for Blockhash { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", base58::encode(self.0.as_slice(), &SOLANA_ALPHABET)) + write!(f, "{}", base58::encode(self.0.as_slice(), SOLANA_ALPHABET)) } } @@ -33,7 +33,7 @@ impl FromStr for Blockhash { type Err = EncodingError; fn from_str(s: &str) -> Result { - let bytes = base58::decode(s, &SOLANA_ALPHABET)?; + let bytes = base58::decode(s, SOLANA_ALPHABET)?; let bytes = H256::try_from(bytes.as_slice()).map_err(|_| EncodingError::InvalidInput)?; Ok(Blockhash(bytes)) } diff --git a/rust/chains/tw_solana/src/compiler.rs b/rust/chains/tw_solana/src/compiler.rs index 483ddac98cb..f3341bbd907 100644 --- a/rust/chains/tw_solana/src/compiler.rs +++ b/rust/chains/tw_solana/src/compiler.rs @@ -68,7 +68,7 @@ impl SolanaCompiler { public_keys: Vec, ) -> SigningResult> { let encode = move |data| match input.tx_encoding { - Proto::Encoding::Base58 => base58::encode(data, &SOLANA_ALPHABET), + Proto::Encoding::Base58 => base58::encode(data, SOLANA_ALPHABET), Proto::Encoding::Base64 => base64::encode(data, false), }; diff --git a/rust/chains/tw_solana/src/lib.rs b/rust/chains/tw_solana/src/lib.rs index 7e945d862f0..5bf3b779386 100644 --- a/rust/chains/tw_solana/src/lib.rs +++ b/rust/chains/tw_solana/src/lib.rs @@ -16,4 +16,4 @@ pub mod signer; pub mod transaction; // cbindgen:ignore -pub const SOLANA_ALPHABET: Alphabet = *Alphabet::BITCOIN; +pub const SOLANA_ALPHABET: Alphabet = Alphabet::Bitcoin; diff --git a/rust/chains/tw_solana/src/modules/compiled_instructions.rs b/rust/chains/tw_solana/src/modules/compiled_instructions.rs index 4e0e02a0c9c..28b0f30cd6b 100644 --- a/rust/chains/tw_solana/src/modules/compiled_instructions.rs +++ b/rust/chains/tw_solana/src/modules/compiled_instructions.rs @@ -54,7 +54,7 @@ mod tests { fn test_compile_instruction() { let public_0 = base58::decode( "GymAh18wHuFTytfSJWi8eYTA9x5S3sNb9CJSGBWoPRE3", - &SOLANA_ALPHABET, + SOLANA_ALPHABET, ) .unwrap(); let public_0 = ed25519::sha512::PublicKey::try_from(public_0.as_slice()).unwrap(); @@ -62,7 +62,7 @@ mod tests { let public_1 = base58::decode( "2oKoYSAHgveX91917v4DUEuN8BNKXDg8KJWpaGyEay9V", - &SOLANA_ALPHABET, + SOLANA_ALPHABET, ) .unwrap(); let public_1 = ed25519::sha512::PublicKey::try_from(public_1.as_slice()).unwrap(); diff --git a/rust/chains/tw_solana/src/modules/utils.rs b/rust/chains/tw_solana/src/modules/utils.rs index 0bf4655d161..cc9a1ebf4ec 100644 --- a/rust/chains/tw_solana/src/modules/utils.rs +++ b/rust/chains/tw_solana/src/modules/utils.rs @@ -40,7 +40,7 @@ impl SolanaTransaction { bincode::deserialize(&tx_bytes).map_err(|_| SigningErrorType::Error_input_parse)?; let mut msg_to_sign = tx_to_sign.message; - let new_blockchain_hash = base58::decode(recent_blockhash, &SOLANA_ALPHABET)?; + let new_blockchain_hash = base58::decode(recent_blockhash, SOLANA_ALPHABET)?; let new_blockchain_hash = H256::try_from(new_blockchain_hash.as_slice()) .tw_err(|_| SigningErrorType::Error_invalid_params)?; diff --git a/rust/chains/tw_solana/src/signer.rs b/rust/chains/tw_solana/src/signer.rs index 11b3e8bd2f9..4d3db06c69a 100644 --- a/rust/chains/tw_solana/src/signer.rs +++ b/rust/chains/tw_solana/src/signer.rs @@ -29,7 +29,7 @@ impl SolanaSigner { input: Proto::SigningInput<'_>, ) -> SigningResult> { let encode = move |data| match input.tx_encoding { - Proto::Encoding::Base58 => base58::encode(data, &SOLANA_ALPHABET), + Proto::Encoding::Base58 => base58::encode(data, SOLANA_ALPHABET), Proto::Encoding::Base64 => base64::encode(data, false), }; diff --git a/rust/chains/tw_solana/src/transaction/mod.rs b/rust/chains/tw_solana/src/transaction/mod.rs index 7ba4da1ac63..b9875f15c74 100644 --- a/rust/chains/tw_solana/src/transaction/mod.rs +++ b/rust/chains/tw_solana/src/transaction/mod.rs @@ -53,7 +53,7 @@ impl FromStr for Signature { type Err = SigningError; fn from_str(s: &str) -> Result { - let data = base58::decode(s, &SOLANA_ALPHABET) + let data = base58::decode(s, SOLANA_ALPHABET) .tw_err(|_| SigningErrorType::Error_input_parse) .context("Error decoding Solana Signature from base58")?; H512::try_from(data.as_slice()) @@ -65,7 +65,7 @@ impl FromStr for Signature { impl fmt::Display for Signature { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", base58::encode(self.0.as_slice(), &SOLANA_ALPHABET)) + write!(f, "{}", base58::encode(self.0.as_slice(), SOLANA_ALPHABET)) } } @@ -82,11 +82,11 @@ mod tests { use tw_memory::Data; fn base58_decode(s: &'static str) -> Data { - base58::decode(s, &SOLANA_ALPHABET).unwrap() + base58::decode(s, SOLANA_ALPHABET).unwrap() } fn base58_decode_h256(s: &'static str) -> H256 { - let bytes = base58::decode(s, &SOLANA_ALPHABET).unwrap(); + let bytes = base58::decode(s, SOLANA_ALPHABET).unwrap(); H256::try_from(bytes.as_slice()).unwrap() } diff --git a/rust/chains/tw_solana/tests/update_blockhash_and_sign.rs b/rust/chains/tw_solana/tests/update_blockhash_and_sign.rs index 6dffaa32a63..5521e7069af 100644 --- a/rust/chains/tw_solana/tests/update_blockhash_and_sign.rs +++ b/rust/chains/tw_solana/tests/update_blockhash_and_sign.rs @@ -15,7 +15,7 @@ fn test_update_recent_blockhash_and_sign() { let new_blockhash = "CyPYVsYWrsJNfVpi8aazu7WsrswNFuDd385z6GNoBGUg"; let private_key = base58::decode( "A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr", - &SOLANA_ALPHABET, + SOLANA_ALPHABET, ) .unwrap(); diff --git a/rust/chains/tw_sui/src/transaction/sui_types.rs b/rust/chains/tw_sui/src/transaction/sui_types.rs index 625e6bde69e..1948789adf9 100644 --- a/rust/chains/tw_sui/src/transaction/sui_types.rs +++ b/rust/chains/tw_sui/src/transaction/sui_types.rs @@ -39,7 +39,7 @@ impl FromStr for ObjectDigest { type Err = SigningError; fn from_str(s: &str) -> Result { - let bytes = base58::decode(s, Alphabet::BITCOIN) + let bytes = base58::decode(s, Alphabet::Bitcoin) .tw_err(|_| SigningErrorType::Error_invalid_params) .context("Invalid Object Digest: expected valid base58 string")?; H256::try_from(bytes.as_slice()) diff --git a/rust/frameworks/tw_utxo/Cargo.toml b/rust/frameworks/tw_utxo/Cargo.toml new file mode 100644 index 00000000000..08009365f7f --- /dev/null +++ b/rust/frameworks/tw_utxo/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "tw_utxo" +version = "0.1.0" +edition = "2021" + +[dependencies] +bech32 = "0.9.1" +bitcoin = { version = "0.30.0", features = ["rand-std"] } +byteorder = "1.4" +itertools = "0.10.5" +secp256k1 = { version = "0.27.0", features = ["rand-std"] } +strum_macros = "0.25" +tw_base58_address = { path = "../../tw_base58_address" } +tw_bech32_address = { path = "../../tw_bech32_address" } +tw_coin_entry = { path = "../../tw_coin_entry" } +tw_encoding = { path = "../../tw_encoding" } +tw_hash = { path = "../../tw_hash" } +tw_keypair = { path = "../../tw_keypair" } +tw_memory = { path = "../../tw_memory" } +tw_misc = { path = "../../tw_misc" } +tw_proto = { path = "../../tw_proto" } + +[dev-dependencies] +tw_coin_entry = { path = "../../tw_coin_entry", features = ["test-utils"] } diff --git a/rust/frameworks/tw_utxo/src/address/derivation.rs b/rust/frameworks/tw_utxo/src/address/derivation.rs new file mode 100644 index 00000000000..bd6cb3cfaf0 --- /dev/null +++ b/rust/frameworks/tw_utxo/src/address/derivation.rs @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::derivation::Derivation; + +pub enum BitcoinDerivation { + Legacy, + Segwit, +} + +impl BitcoinDerivation { + /// TrustWallet derivation inherited from: + /// https://github.com/trustwallet/wallet-core/blob/b65adc4c86e49eb905f659ade025185a62e87ca9/src/Bitcoin/Entry.cpp#L67 + pub fn tw_derivation(coin: &dyn CoinContext, derivation: Derivation) -> BitcoinDerivation { + match derivation { + Derivation::Default + // Please note that testnet derivation is no longer supported. Instead, use address prefix. + | Derivation::Testnet => { + let Some(default_derivation) = coin.derivations().first() else { + return BitcoinDerivation::Segwit; + }; + if default_derivation.name == Derivation::Segwit { + return BitcoinDerivation::Segwit; + } + BitcoinDerivation::Legacy + }, + Derivation::Segwit => BitcoinDerivation::Segwit, + Derivation::Legacy => BitcoinDerivation::Legacy, + } + } + + /// TrustWallet behaviour inherited from: + /// https://github.com/trustwallet/wallet-core/blob/b65adc4c86e49eb905f659ade025185a62e87ca9/src/Bitcoin/Entry.cpp#L14 + pub fn tw_supports_segwit(coin: &dyn CoinContext) -> bool { + coin.derivations() + .iter() + .any(|der| der.name == Derivation::Segwit) + } +} diff --git a/rust/frameworks/tw_utxo/src/address/legacy.rs b/rust/frameworks/tw_utxo/src/address/legacy.rs new file mode 100644 index 00000000000..8efbb1f9c43 --- /dev/null +++ b/rust/frameworks/tw_utxo/src/address/legacy.rs @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::script::Script; +use std::fmt; +use std::str::FromStr; +use tw_base58_address::Base58Address; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::prelude::*; +use tw_coin_entry::prefix::BitcoinBase58Prefix; +use tw_encoding::base58::Alphabet; +use tw_hash::hasher::{sha256_ripemd, Hasher}; +use tw_hash::H160; +use tw_keypair::{ecdsa, tw}; + +pub const BITCOIN_ADDRESS_SIZE: usize = 21; +pub const BITCOIN_ADDRESS_CHECKSUM_SIZE: usize = 4; + +type BitcoinBase58Address = Base58Address; + +#[derive(Debug, Eq, PartialEq)] +pub struct LegacyAddress(BitcoinBase58Address); + +impl LegacyAddress { + pub fn new(prefix: u8, data: &[u8]) -> AddressResult { + let mut bytes = Vec::with_capacity(data.len() + 1); + // Insert the prefix to the beginning of the address bytes array. + bytes.push(prefix); + bytes.extend_from_slice(data); + + BitcoinBase58Address::new(&bytes, Alphabet::Bitcoin, Hasher::Sha256d).map(LegacyAddress) + } + + pub fn p2pkh_with_public_key( + p2pkh_prefix: u8, + public_key: &ecdsa::secp256k1::PublicKey, + ) -> AddressResult { + let public_key_hash = sha256_ripemd(public_key.compressed().as_slice()); + LegacyAddress::new(p2pkh_prefix, &public_key_hash) + } + + /// Tries to parse a `LegacyAddress` and check if + pub fn p2pkh_with_coin_and_prefix( + coin: &dyn CoinContext, + public_key: &tw::PublicKey, + prefix: Option, + ) -> AddressResult { + let p2pkh_prefix = match prefix { + Some(prefix) => prefix.p2pkh, + None => coin.p2pkh_prefix().ok_or(AddressError::InvalidRegistry)?, + }; + + let ecdsa_public_key = public_key + .to_secp256k1() + .ok_or(AddressError::PublicKeyTypeMismatch)?; + + LegacyAddress::p2pkh_with_public_key(p2pkh_prefix, ecdsa_public_key) + } + + pub fn p2sh_with_prefix_byte( + redeem_script: &Script, + p2sh_prefix: u8, + ) -> AddressResult { + let script_hash = sha256_ripemd(redeem_script.as_slice()); + LegacyAddress::new(p2sh_prefix, &script_hash) + } + + pub fn from_str_with_coin_and_prefix( + coin: &dyn CoinContext, + s: &str, + prefix: Option, + ) -> AddressResult { + let base58_prefix = match prefix { + Some(base58_prefix) => base58_prefix, + None => { + let p2pkh = coin.p2pkh_prefix().ok_or(AddressError::InvalidRegistry)?; + let p2sh = coin.p2sh_prefix().ok_or(AddressError::InvalidRegistry)?; + BitcoinBase58Prefix { p2pkh, p2sh } + }, + }; + + LegacyAddress::from_str_checked(s, base58_prefix.p2pkh, base58_prefix.p2sh) + } + + pub fn from_str_checked( + s: &str, + p2pkh_prefix: u8, + p2sh_prefix: u8, + ) -> AddressResult { + let addr = LegacyAddress::from_str(s)?; + if addr.prefix() == p2pkh_prefix || addr.prefix() == p2sh_prefix { + Ok(addr) + } else { + Err(AddressError::UnexpectedAddressPrefix) + } + } + + pub fn prefix(&self) -> u8 { + self.bytes()[0] + } + + pub fn bytes(&self) -> &[u8] { + self.0.as_ref() + } + + pub fn payload(&self) -> H160 { + debug_assert_eq!(self.bytes().len(), H160::LEN + 1); + H160::try_from(&self.0.as_ref()[1..]).expect("Legacy address must be exactly 21 bytes") + } +} + +impl FromStr for LegacyAddress { + type Err = AddressError; + + fn from_str(s: &str) -> Result { + BitcoinBase58Address::from_str_with_alphabet(s, Alphabet::Bitcoin, Hasher::Sha256d) + .map(LegacyAddress) + } +} + +impl<'a> TryFrom<&'a [u8]> for LegacyAddress { + type Error = AddressError; + + fn try_from(bytes: &'a [u8]) -> Result { + BitcoinBase58Address::new(bytes, Alphabet::Bitcoin, Hasher::Sha256d).map(LegacyAddress) + } +} + +impl fmt::Display for LegacyAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} diff --git a/rust/frameworks/tw_utxo/src/address/mod.rs b/rust/frameworks/tw_utxo/src/address/mod.rs new file mode 100644 index 00000000000..c7c4539b421 --- /dev/null +++ b/rust/frameworks/tw_utxo/src/address/mod.rs @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +pub mod derivation; +pub mod legacy; +pub mod segwit; +pub mod standard_bitcoin; +pub mod taproot; +pub mod witness_program; + +type Bech32Prefix = tw_bech32_address::bech32_prefix::Bech32Prefix; diff --git a/rust/frameworks/tw_utxo/src/address/segwit.rs b/rust/frameworks/tw_utxo/src/address/segwit.rs new file mode 100644 index 00000000000..8098fc4ad93 --- /dev/null +++ b/rust/frameworks/tw_utxo/src/address/segwit.rs @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use super::Bech32Prefix; +use crate::address::witness_program::{WitnessProgram, WITNESS_V0}; +use crate::script::Script; +use core::fmt; +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::prelude::*; +use tw_hash::hasher::sha256_ripemd; +use tw_hash::sha2::sha256; +use tw_hash::{H160, H256}; +use tw_keypair::tw; +use tw_memory::Data; + +/// Witness program sizes valid for V0 (Segwit). +const WITNESS_V0_VALID_PROGRAM_SIZES: [usize; 2] = [H160::LEN, H256::LEN]; + +#[derive(Debug, Eq, PartialEq)] +pub struct SegwitAddress { + inner: WitnessProgram, +} + +impl SegwitAddress { + pub fn new(hrp: String, witness_program: Data) -> AddressResult { + // Specific segwit v0 check. These addresses can never spend funds sent to them. + if !WITNESS_V0_VALID_PROGRAM_SIZES.contains(&witness_program.len()) { + return Err(AddressError::InvalidInput); + } + + let inner = WitnessProgram::new(hrp, WITNESS_V0, witness_program, bech32::Variant::Bech32)?; + Ok(SegwitAddress { inner }) + } + + pub fn p2wpkh_with_coin_and_prefix( + coin: &dyn CoinContext, + public_key: &tw::PublicKey, + prefix: Option, + ) -> AddressResult { + let hrp = match prefix { + Some(Bech32Prefix { hrp }) => hrp, + None => coin.hrp().ok_or(AddressError::InvalidRegistry)?, + }; + + let public_key_bytes = public_key + .to_secp256k1() + .ok_or(AddressError::PublicKeyTypeMismatch)? + .compressed(); + let public_key_hash = sha256_ripemd(public_key_bytes.as_slice()); + + Self::new(hrp, public_key_hash.to_vec()) + } + + pub fn p2wsh_with_hrp(redeem_script: &Script, hrp: String) -> AddressResult { + let script_hash = sha256(redeem_script.as_slice()); + Self::new(hrp, script_hash) + } + + pub fn from_str_checked(s: &str, expected_hrp: &str) -> AddressResult { + let address = Self::from_str(s)?; + if address.inner.hrp() != expected_hrp { + return Err(AddressError::InvalidHrp); + } + Ok(address) + } + + pub fn from_str_with_coin_and_prefix( + coin: &dyn CoinContext, + s: &str, + prefix: Option, + ) -> AddressResult { + let hrp = match prefix { + Some(Bech32Prefix { hrp }) => hrp, + None => coin.hrp().ok_or(AddressError::InvalidRegistry)?, + }; + SegwitAddress::from_str_checked(s, &hrp) + } + + pub fn witness_program(&self) -> &[u8] { + self.inner.witness_program() + } +} + +impl FromStr for SegwitAddress { + type Err = AddressError; + + fn from_str(s: &str) -> Result { + let inner = WitnessProgram::from_str_checked( + s, + WITNESS_V0, + bech32::Variant::Bech32, + &WITNESS_V0_VALID_PROGRAM_SIZES, + )?; + Ok(SegwitAddress { inner }) + } +} + +impl fmt::Display for SegwitAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.inner) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tw_encoding::hex::DecodeHex; + + struct TestInputValid { + str: &'static str, + normalized: &'static str, + expected: SegwitAddress, + } + + #[track_caller] + fn segwit_addr(hrp: &str, program: &str) -> SegwitAddress { + SegwitAddress::new(hrp.to_string(), program.decode_hex().unwrap()) + .expect("Cannot construct a SegwitAddress from the input") + } + + /// Tests if the given `s` string representation is converted from and to `expected` segwit address. + #[track_caller] + fn test_to_from_str_valid(input: TestInputValid) { + let actual = SegwitAddress::from_str(input.str).expect("Expected a valid address"); + assert_eq!(actual, input.expected, "String -> SegwitAddress"); + + let actual_str = actual.to_string(); + assert_eq!(actual_str, input.normalized, "SegwitAddress -> String"); + } + + #[track_caller] + fn test_from_str_invalid(str: &str) { + let _ = SegwitAddress::from_str(str).expect_err("Expected an invalid Segwit address"); + } + + #[test] + fn test_segwit_address_to_from_str() { + test_to_from_str_valid(TestInputValid { + str: "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", + normalized: "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", + expected: segwit_addr("bc", "751e76e8199196d454941c45d1b3a323f1433bd6"), + }); + + test_to_from_str_valid(TestInputValid { + str: "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", + normalized: "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", + expected: segwit_addr( + "tb", + "1863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262", + ), + }); + + test_to_from_str_valid(TestInputValid { + str: "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", + normalized: "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", + expected: segwit_addr( + "tb", + "000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", + ), + }); + + test_to_from_str_valid(TestInputValid { + str: "bc1qpjult34k9spjfym8hss2jrwjgf0xjf40ze0pp8", + normalized: "bc1qpjult34k9spjfym8hss2jrwjgf0xjf40ze0pp8", + expected: segwit_addr("bc", "0cb9f5c6b62c03249367bc20a90dd2425e6926af"), + }); + + test_to_from_str_valid(TestInputValid { + str: "bc1qm9jzmujvdqjj6y28hptk859zs3yyv78hpqqjfj", + normalized: "bc1qm9jzmujvdqjj6y28hptk859zs3yyv78hpqqjfj", + expected: segwit_addr("bc", "d9642df24c68252d1147b85763d0a284484678f7"), + }); + + test_to_from_str_valid(TestInputValid { + str: "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", + normalized: "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", + expected: segwit_addr( + "tb", + "000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", + ), + }); + } + + #[test] + fn test_segwit_address_from_str_invalid() { + // witness program size 38 + test_from_str_invalid( + "bc1q0xlxvlhemja6c4dqv22uapctqupfhlxm0xlxvlhemja6c4dqv22uapctqupfkpvgusg", + ); + + // version 1 + test_from_str_invalid("bc1ptmsk7c2yut2xah4pgflpygh2s7fh0cpfkrza9cjj29awapv53mrslgd5cf"); + + // version 1 + test_from_str_invalid( + "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y", + ); + + // version 2 + test_from_str_invalid("bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs"); + + // version 16 + test_from_str_invalid("BC1SW50QGDZ25J"); + } +} diff --git a/rust/frameworks/tw_utxo/src/address/standard_bitcoin.rs b/rust/frameworks/tw_utxo/src/address/standard_bitcoin.rs new file mode 100644 index 00000000000..6f582b0976b --- /dev/null +++ b/rust/frameworks/tw_utxo/src/address/standard_bitcoin.rs @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +//! This module contains a standard Bitcoin address enumeration each of these exist on the mainnet network. +//! TODO consider moving the file to `tw_bitcoin`. + +use crate::address::derivation::BitcoinDerivation; +use crate::address::legacy::LegacyAddress; +use crate::address::segwit::SegwitAddress; +use crate::address::taproot::TaprootAddress; +use crate::address::Bech32Prefix; +use std::fmt; +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::CoinAddress; +use tw_coin_entry::derivation::Derivation; +use tw_coin_entry::error::prelude::*; +use tw_coin_entry::prefix::{AddressPrefix, BitcoinBase58Prefix}; +use tw_keypair::tw; +use tw_memory::Data; + +/// A standard set of Bitcoin address prefixes. +/// The set of address prefixes can differ for Bitcoin forks. +/// TODO add `TaprootBech32` enum variant. +pub enum StandardBitcoinPrefix { + Base58(BitcoinBase58Prefix), + Bech32(Bech32Prefix), +} + +impl TryFrom for StandardBitcoinPrefix { + type Error = AddressError; + + fn try_from(prefix: AddressPrefix) -> Result { + match prefix { + AddressPrefix::BitcoinBase58(base58) => Ok(StandardBitcoinPrefix::Base58(base58)), + AddressPrefix::Hrp(hrp) => Ok(StandardBitcoinPrefix::Bech32(Bech32Prefix { hrp })), + } + } +} + +/// A standard set of Bitcoin address types. +/// +/// The set of address types can differ for Bitcoin forks. +/// For example, Zcash does not support segwit addresses. +#[derive(Debug, Eq, PartialEq)] +pub enum StandardBitcoinAddress { + Legacy(LegacyAddress), + Segwit(SegwitAddress), + Taproot(TaprootAddress), +} + +impl StandardBitcoinAddress { + /// Tries to parse one of the `BitcoinAddress` variants + /// and validates if the result address matches the given `prefix` address or belongs to the `coin` network. + pub fn from_str_with_coin_and_prefix( + coin: &dyn CoinContext, + s: &str, + prefix: Option, + ) -> AddressResult { + match prefix { + Some(StandardBitcoinPrefix::Base58(base58)) => { + LegacyAddress::from_str_with_coin_and_prefix(coin, s, Some(base58)) + .map(StandardBitcoinAddress::Legacy) + }, + Some(StandardBitcoinPrefix::Bech32(bech32)) => { + SegwitAddress::from_str_with_coin_and_prefix(coin, s, Some(bech32)) + .map(StandardBitcoinAddress::Segwit) + }, + None => StandardBitcoinAddress::from_str_checked(coin, s), + } + } + + /// Tries to parse one of the `BitcoinAddress` variants + /// and validates if the result address belongs to the `coin` network. + pub fn from_str_checked( + coin: &dyn CoinContext, + s: &str, + ) -> AddressResult { + // Try to parse a Segwit address if the coin supports it. + if BitcoinDerivation::tw_supports_segwit(coin) { + if let Ok(segwit) = SegwitAddress::from_str_with_coin_and_prefix(coin, s, None) { + return Ok(StandardBitcoinAddress::Segwit(segwit)); + } + + // TODO use `BitcoinDerivation::tw_supports_taproot` based on `registry.json`. + if let Ok(taproot) = TaprootAddress::from_str_with_coin_and_prefix(coin, s, None) { + return Ok(StandardBitcoinAddress::Taproot(taproot)); + } + } + + // Otherwise, try to parse a Legacy address. + if let Ok(legacy) = LegacyAddress::from_str_with_coin_and_prefix(coin, s, None) { + return Ok(StandardBitcoinAddress::Legacy(legacy)); + } + + Err(AddressError::InvalidInput) + } + + /// TrustWallet derivation inherited from: + /// https://github.com/trustwallet/wallet-core/blob/b65adc4c86e49eb905f659ade025185a62e87ca9/src/Bitcoin/Entry.cpp#L67 + pub fn derive_as_tw( + coin: &dyn CoinContext, + public_key: &tw::PublicKey, + derivation: Derivation, + maybe_prefix: Option, + ) -> AddressResult { + match maybe_prefix { + Some(StandardBitcoinPrefix::Base58(prefix)) => { + return LegacyAddress::p2pkh_with_coin_and_prefix(coin, public_key, Some(prefix)) + .map(StandardBitcoinAddress::Legacy); + }, + Some(StandardBitcoinPrefix::Bech32(prefix)) => { + return SegwitAddress::p2wpkh_with_coin_and_prefix(coin, public_key, Some(prefix)) + .map(StandardBitcoinAddress::Segwit); + }, + // Derive an address as declared in registry.json. + None => (), + } + + match BitcoinDerivation::tw_derivation(coin, derivation) { + BitcoinDerivation::Legacy => { + LegacyAddress::p2pkh_with_coin_and_prefix(coin, public_key, None) + .map(StandardBitcoinAddress::Legacy) + }, + BitcoinDerivation::Segwit => { + SegwitAddress::p2wpkh_with_coin_and_prefix(coin, public_key, None) + .map(StandardBitcoinAddress::Segwit) + }, + } + } +} + +impl FromStr for StandardBitcoinAddress { + type Err = AddressError; + + fn from_str(s: &str) -> Result { + if let Ok(legacy) = LegacyAddress::from_str(s) { + return Ok(StandardBitcoinAddress::Legacy(legacy)); + } + if let Ok(segwit) = SegwitAddress::from_str(s) { + return Ok(StandardBitcoinAddress::Segwit(segwit)); + } + if let Ok(taproot) = TaprootAddress::from_str(s) { + return Ok(StandardBitcoinAddress::Taproot(taproot)); + } + Err(AddressError::InvalidInput) + } +} + +impl fmt::Display for StandardBitcoinAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + StandardBitcoinAddress::Legacy(legacy) => write!(f, "{legacy}"), + StandardBitcoinAddress::Segwit(segwit) => write!(f, "{segwit}"), + StandardBitcoinAddress::Taproot(taproot) => write!(f, "{taproot}"), + } + } +} + +impl CoinAddress for StandardBitcoinAddress { + fn data(&self) -> Data { + match self { + StandardBitcoinAddress::Legacy(legacy) => legacy.bytes().to_vec(), + StandardBitcoinAddress::Segwit(segwit) => segwit.witness_program().to_vec(), + StandardBitcoinAddress::Taproot(taproot) => taproot.witness_program().to_vec(), + } + } +} diff --git a/rust/frameworks/tw_utxo/src/address/taproot.rs b/rust/frameworks/tw_utxo/src/address/taproot.rs new file mode 100644 index 00000000000..d0f18810a20 --- /dev/null +++ b/rust/frameworks/tw_utxo/src/address/taproot.rs @@ -0,0 +1,250 @@ +use super::Bech32Prefix; +use crate::address::witness_program::WitnessProgram; +use bitcoin::key::TapTweak; +use core::fmt; +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::prelude::*; +use tw_hash::{H256, H264}; +use tw_keypair::tw; +use tw_memory::Data; +use tw_misc::traits::ToBytesVec; + +/// cbindgen:ignore +pub const WITNESS_V1: u8 = 1; +/// Witness program sizes valid for V1 (Taproot). +/// cbindgen:ignore +pub const WITNESS_V1_VALID_PROGRAM_SIZES: [usize; 1] = [H256::LEN]; + +#[derive(Debug, Eq, PartialEq)] +pub struct TaprootAddress { + inner: WitnessProgram, +} + +impl TaprootAddress { + pub fn new(hrp: String, witness_program: Data) -> AddressResult { + // Specific Taproot V1 check. These addresses can never spend funds sent to them. + if !WITNESS_V1_VALID_PROGRAM_SIZES.contains(&witness_program.len()) { + return Err(AddressError::InvalidInput); + } + + let inner = + WitnessProgram::new(hrp, WITNESS_V1, witness_program, bech32::Variant::Bech32m)?; + Ok(TaprootAddress { inner }) + } + + /// Create a Taproot address from a public key and an optional merkle root. + /// Taproot transactions come in two variants: + /// + /// * P2TR key-path: which is used for "normal" balance transfers and is + /// internally _tweaked_ with an empty (None) merkle root. + /// * P2TR script-path: which is used for complex scripts, such as + /// Ordinals/BRC20, and is internally _tweaked_ with a merkle root of all + /// possible spending conditions. + pub fn p2tr_with_public_key( + hrp: String, + internal_pubkey: &H264, + merkle_root: Option<&H256>, + ) -> AddressResult { + // We're relying on the `bitcoin` crate to generate anything Taproot related. + + // Convert the native `H256` to `TapNodeHash` from the `bitcoin` crate. + let merkle_root = merkle_root.map(|hash| { + let tap_hash = + as bitcoin::hashes::Hash>::from_slice( + hash.as_slice(), + ) + .expect("merkle_root length is 32 bytes"); + + bitcoin::taproot::TapNodeHash::from_raw_hash(tap_hash) + }); + + // Tweak the public key with the (empty) merkle root. + let pubkey = bitcoin::PublicKey::from_slice(internal_pubkey.as_slice()).unwrap(); + let internal_key = bitcoin::secp256k1::XOnlyPublicKey::from(pubkey.inner); + let (output_key, _parity) = + internal_key.tap_tweak(&bitcoin::secp256k1::Secp256k1::new(), merkle_root); + + Self::new(hrp, output_key.serialize().to_vec()) + } + + /// Create a Taproot address from a public key and an optional merkle root. + /// Taproot transactions come in two variants: + /// + /// * P2TR key-path: which is used for "normal" balance transfers and is + /// internally _tweaked_ with an empty (None) merkle root. + /// * P2TR script-path: which is used for complex scripts, such as + /// Ordinals/BRC20, and is internally _tweaked_ with a merkle root of all + /// possible spending conditions. + pub fn p2tr_with_coin_and_prefix( + coin: &dyn CoinContext, + public_key: &tw::PublicKey, + prefix: Option, + merkle_root: Option<&H256>, + ) -> AddressResult { + let hrp = match prefix { + Some(Bech32Prefix { hrp }) => hrp, + None => coin.hrp().ok_or(AddressError::InvalidRegistry)?, + }; + + let public_key_bytes = public_key + .to_secp256k1() + .ok_or(AddressError::PublicKeyTypeMismatch)? + .compressed(); + + Self::p2tr_with_public_key(hrp, &public_key_bytes, merkle_root) + } + + pub fn from_str_checked(s: &str, expected_hrp: &str) -> AddressResult { + let address = Self::from_str(s)?; + if address.inner.hrp() != expected_hrp { + return Err(AddressError::InvalidHrp); + } + Ok(address) + } + + pub fn from_str_with_coin_and_prefix( + coin: &dyn CoinContext, + s: &str, + prefix: Option, + ) -> AddressResult { + let hrp = match prefix { + Some(Bech32Prefix { hrp }) => hrp, + None => coin.hrp().ok_or(AddressError::InvalidRegistry)?, + }; + TaprootAddress::from_str_checked(s, &hrp) + } + + pub fn witness_program(&self) -> &[u8] { + self.inner.witness_program() + } +} + +impl FromStr for TaprootAddress { + type Err = AddressError; + + fn from_str(s: &str) -> Result { + let inner = WitnessProgram::from_str_checked( + s, + WITNESS_V1, + bech32::Variant::Bech32m, + &WITNESS_V1_VALID_PROGRAM_SIZES, + )?; + Ok(TaprootAddress { inner }) + } +} + +impl fmt::Display for TaprootAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.inner) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tw_coin_entry::test_utils::test_context::TestCoinContext; + use tw_encoding::hex::DecodeHex; + + struct TestInputValid { + str: &'static str, + normalized: &'static str, + expected: TaprootAddress, + } + + #[track_caller] + fn taproot_addr(hrp: &str, program: &str) -> TaprootAddress { + TaprootAddress::new(hrp.to_string(), program.decode_hex().unwrap()) + .expect("Cannot construct a TaprootAddress from the input") + } + + /// Tests if the given `s` string representation is converted from and to `expected` segwit address. + #[track_caller] + fn test_to_from_str_valid(input: TestInputValid) { + let actual = TaprootAddress::from_str(input.str).expect("Expected a valid address"); + assert_eq!(actual, input.expected, "String -> TaprootAddress"); + + let actual_str = actual.to_string(); + assert_eq!(actual_str, input.normalized, "TaprootAddress -> String"); + } + + #[track_caller] + fn test_from_str_invalid(str: &str) { + let _ = TaprootAddress::from_str(str).expect_err("Expected an invalid Taproot address"); + } + + #[test] + fn test_segwit_address_to_from_str() { + test_to_from_str_valid(TestInputValid { + str: "bc1ptmsk7c2yut2xah4pgflpygh2s7fh0cpfkrza9cjj29awapv53mrslgd5cf", + normalized: "bc1ptmsk7c2yut2xah4pgflpygh2s7fh0cpfkrza9cjj29awapv53mrslgd5cf", + expected: taproot_addr( + "bc", + "5ee16f6144e2d46edea1427e1222ea879377e029b0c5d2e252517aee85948ec7", + ), + }); + + test_to_from_str_valid(TestInputValid { + str: "tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c", + normalized: "tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c", + expected: taproot_addr( + "tb", + "000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", + ), + }); + + test_to_from_str_valid(TestInputValid { + str: "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0", + normalized: "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0", + expected: taproot_addr( + "bc", + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + ), + }); + } + + #[test] + fn test_taproot_address_from_str_invalid() { + // version 0 + test_from_str_invalid("tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy"); + + // version 2 + test_from_str_invalid("bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs"); + + // version 2 + test_from_str_invalid("bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs"); + + // version 16 + test_from_str_invalid("BC1SW50QGDZ25J"); + + // program size 40 + test_from_str_invalid( + "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y", + ); + } + + #[test] + fn test_taproot_address_create_with_coin_and_prefix() { + let coin = TestCoinContext::default(); + let hrp = "bc".to_string(); + let merkle_root = None; + + let public_key_bytes = "03cdf7e208a0146c3a35c181944a96a15b2a58256be69adad640a9a97d408b9b44" + .decode_hex() + .unwrap(); + let public_key = + tw::PublicKey::new(public_key_bytes.clone(), tw::PublicKeyType::Secp256k1).unwrap(); + + let addr = TaprootAddress::p2tr_with_coin_and_prefix( + &coin, + &public_key, + Some(Bech32Prefix { hrp }), + merkle_root, + ) + .unwrap(); + assert_eq!( + addr.to_string(), + "bc1purekytqrqzfzdulufmll8a335jhvw9x4glhzp8fv76yxlsxeyptsfylq9h" + ); + } +} diff --git a/rust/frameworks/tw_utxo/src/address/witness_program.rs b/rust/frameworks/tw_utxo/src/address/witness_program.rs new file mode 100644 index 00000000000..0edb5ea65be --- /dev/null +++ b/rust/frameworks/tw_utxo/src/address/witness_program.rs @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use bech32::FromBase32; +use std::fmt; +use std::ops::RangeInclusive; +use tw_coin_entry::error::prelude::*; +use tw_memory::Data; + +/// cbindgen:ignore +pub const WITNESS_V0: u8 = 0; +/// cbindgen:ignore +pub const MAX_WITNESS_VERSION: u8 = 16; +/// cbindgen:ignore +pub const WITNESS_VERSIONS: RangeInclusive = WITNESS_V0..=MAX_WITNESS_VERSION; +/// Witness program sizes valid for most of the witness versions. +/// Please note that V0 is more constraint. +/// cbindgen:ignore +pub const WITNESS_VALID_PROGRAM_SIZES: RangeInclusive = 2..=40; + +/// A segwit address implementation that supports various program versions. +/// For example: +/// * witness V0 is Segwit address +/// * witness V1 is Taproot address +#[derive(Debug, Eq, PartialEq)] +pub struct WitnessProgram { + hrp: String, + witness_version: u8, + witness_program: Data, + /// An address string created from this `hrp`, `witness_version` and `witness_program`. + address_str: String, + bech32_variant: bech32::Variant, +} + +impl WitnessProgram { + pub fn new( + hrp: String, + witness_version: u8, + witness_program: Data, + bech32_variant: bech32::Variant, + ) -> AddressResult { + if !WITNESS_VERSIONS.contains(&witness_version) { + return Err(AddressError::Unsupported); + } + + if !WITNESS_VALID_PROGRAM_SIZES.contains(&witness_program.len()) { + return Err(AddressError::InvalidInput); + } + + let address_str = + Self::fmt_internal(&hrp, witness_version, &witness_program, bech32_variant)?; + Ok(WitnessProgram { + hrp, + witness_version, + witness_program, + address_str, + bech32_variant, + }) + } + + pub fn witness_version(&self) -> u8 { + self.witness_version + } + + pub fn witness_program(&self) -> &[u8] { + &self.witness_program + } + + pub fn hrp(&self) -> &str { + &self.hrp + } + + pub fn from_str_checked( + s: &str, + expected_version: u8, + expected_checksum_type: bech32::Variant, + valid_program_sizes: &[usize], + ) -> AddressResult { + let (hrp, payload_u5, checksum_variant) = + bech32::decode(s).map_err(|_| AddressError::FromBech32Error)?; + + if payload_u5.is_empty() { + return Err(AddressError::InvalidInput); + } + + // Get the script version and program (converted from 5-bit to 8-bit) + let (version, program) = payload_u5.split_at(1); + let version = version[0].to_u8(); + let program = Data::from_base32(program).map_err(|_| AddressError::FromBech32Error)?; + + // Check witness version. + if version != expected_version { + return Err(AddressError::Unsupported); + } + + // Check encoding. + if checksum_variant != expected_checksum_type { + return Err(AddressError::InvalidInput); + } + + // Check witness program sizes. + if !valid_program_sizes.contains(&program.len()) { + return Err(AddressError::InvalidInput); + } + + WitnessProgram::new(hrp, version, program, checksum_variant) + } + + fn fmt_internal( + hrp: &str, + witness_version: u8, + witness_program: &[u8], + bech32_variant: bech32::Variant, + ) -> AddressResult { + const STRING_CAPACITY: usize = 100; + + let mut result_addr = String::with_capacity(STRING_CAPACITY); + + let version_u5 = + bech32::u5::try_from_u8(witness_version).expect("WitnessVersion must be 0..=16"); + + { + let mut bech32_writer = + bech32::Bech32Writer::new(hrp, bech32_variant, &mut result_addr) + .map_err(|_| AddressError::FromBech32Error)?; + bech32::WriteBase32::write_u5(&mut bech32_writer, version_u5) + .map_err(|_| AddressError::FromBech32Error)?; + bech32::ToBase32::write_base32(&witness_program, &mut bech32_writer) + .map_err(|_| AddressError::FromBech32Error)?; + } + + Ok(result_addr) + } +} + +impl fmt::Display for WitnessProgram { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.address_str) + } +} diff --git a/rust/frameworks/tw_utxo/src/constants.rs b/rust/frameworks/tw_utxo/src/constants.rs new file mode 100644 index 00000000000..0a99f519fac --- /dev/null +++ b/rust/frameworks/tw_utxo/src/constants.rs @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +/// A standard transaction is limited to 400k weight units (WU). +/// https://bitcoin.stackexchange.com/questions/35570/what-is-the-maximum-number-of-inputs-outputs-a-transaction-can-have +pub const MAX_TRANSACTION_WEIGHT: usize = 400_000; diff --git a/rust/frameworks/tw_utxo/src/dust/dust_filter.rs b/rust/frameworks/tw_utxo/src/dust/dust_filter.rs new file mode 100644 index 00000000000..0d68ee106dc --- /dev/null +++ b/rust/frameworks/tw_utxo/src/dust/dust_filter.rs @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::dust::DustPolicy; +use crate::script::standard_script::conditions; +use crate::transaction::transaction_interface::{TransactionInterface, TxOutputInterface}; +use crate::transaction::unsigned_transaction::UnsignedTransaction; +use std::marker::PhantomData; +use tw_coin_entry::error::prelude::*; + +pub struct DustFilter { + dust_policy: DustPolicy, + _phantom: PhantomData, +} + +impl DustFilter { + pub fn new(dust_policy: DustPolicy) -> Self { + DustFilter { + dust_policy, + _phantom: PhantomData, + } + } + + /// Filter dust UTXOs out. + /// Returns an error if there are no valid UTXOs. + pub fn filter_inputs( + &self, + mut transaction: UnsignedTransaction, + ) -> SigningResult> { + let dust_threshold = self.dust_policy.dust_threshold(); + + transaction.retain_inputs(|_utxo, utxo_args| utxo_args.amount >= dust_threshold)?; + + Ok(transaction) + } + + /// Checks if all transaction output amounts are greater or equal to a dust threshold. + pub fn check_outputs( + &self, + transaction: &UnsignedTransaction, + ) -> SigningResult<()> { + let dust_threshold = self.dust_policy.dust_threshold(); + + let has_dust_output = transaction.transaction().outputs().iter().any(|output| { + if conditions::is_op_return(output.script_pubkey()) { + // Ignore the OP_RETURN output value. It can (or even should) be 0. + return false; + } + output.value() < dust_threshold + }); + + if has_dust_output { + return SigningError::err(SigningErrorType::Error_dust_amount_requested); + } + Ok(()) + } +} diff --git a/rust/frameworks/tw_utxo/src/dust/mod.rs b/rust/frameworks/tw_utxo/src/dust/mod.rs new file mode 100644 index 00000000000..5e605ae241f --- /dev/null +++ b/rust/frameworks/tw_utxo/src/dust/mod.rs @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::transaction::transaction_parts::Amount; + +pub mod dust_filter; + +/// Transaction dust amount calculator. +/// Later, we plan to add support for `DynamicDust` policy with a `min_relay_fee` amount. +#[derive(Clone, Copy)] +pub enum DustPolicy { + FixedAmount(Amount), +} + +impl DustPolicy { + pub fn dust_threshold(&self) -> Amount { + match self { + DustPolicy::FixedAmount(amount) => *amount, + } + } +} diff --git a/rust/frameworks/tw_utxo/src/encode/compact_integer.rs b/rust/frameworks/tw_utxo/src/encode/compact_integer.rs new file mode 100644 index 00000000000..4a95a378699 --- /dev/null +++ b/rust/frameworks/tw_utxo/src/encode/compact_integer.rs @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::encode::stream::Stream; +use crate::encode::Encodable; +use std::ops::RangeInclusive; + +const ONE_BYTE_RANGE: RangeInclusive = 0..=0xFC; +const TWO_BYTES_RANGE: RangeInclusive = 0xFD..=0xFFFF; +const FOUR_BYTES_RANGE: RangeInclusive = 0x10000..=0xFFFF_FFFF; + +const TWO_BYTES_FLAG: u8 = 0xFD_u8; +const FOUR_BYTES_FLAG: u8 = 0xFE_u8; +const EIGHT_BYTES_FLAG: u8 = 0xFF_u8; + +/// A type of variable-length integer commonly used in the Bitcoin P2P protocol and Bitcoin serialized data structures. +#[derive(Default, Debug, Clone, Copy, PartialEq)] +pub struct CompactInteger(u64); + +impl From for CompactInteger { + fn from(value: usize) -> Self { + CompactInteger(value as u64) + } +} + +impl Encodable for CompactInteger { + fn encode(&self, stream: &mut Stream) { + let v = self.0; + + if ONE_BYTE_RANGE.contains(&v) { + stream.append(&(v as u8)); + } else if TWO_BYTES_RANGE.contains(&v) { + stream.append(&TWO_BYTES_FLAG).append(&(v as u16)); + } else if FOUR_BYTES_RANGE.contains(&v) { + stream.append(&FOUR_BYTES_FLAG).append(&(v as u32)); + } else { + stream.append(&EIGHT_BYTES_FLAG).append(&v); + } + } + + fn encoded_size(&self) -> usize { + const BYTE_FLAG: usize = 1; + + let v = self.0; + if ONE_BYTE_RANGE.contains(&v) { + BYTE_FLAG + } else if TWO_BYTES_RANGE.contains(&v) { + BYTE_FLAG + 2 + } else if FOUR_BYTES_RANGE.contains(&v) { + BYTE_FLAG + 4 + } else { + BYTE_FLAG + 8 + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_compact_integer_stream() { + let mut stream = Stream::default(); + + stream + .append(&CompactInteger::from(0_usize)) + .append(&CompactInteger::from(0xfc_usize)) + .append(&CompactInteger::from(0xfd_usize)) + .append(&CompactInteger::from(0xffff_usize)) + .append(&CompactInteger::from(0x10000_usize)) + .append(&CompactInteger::from(0xffff_ffff_usize)) + .append(&CompactInteger(0x1_0000_0000_u64)); + + let expected = vec![ + 0_u8, 0xfc, 0xfd, 0xfd, 0x00, 0xfd, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x01, 0x00, 0xfe, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + ]; + + assert_eq!(stream.out(), expected); + } +} diff --git a/rust/frameworks/tw_utxo/src/encode/impls.rs b/rust/frameworks/tw_utxo/src/encode/impls.rs new file mode 100644 index 00000000000..b21b1567cc1 --- /dev/null +++ b/rust/frameworks/tw_utxo/src/encode/impls.rs @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::encode::compact_integer::CompactInteger; +use crate::encode::stream::Stream; +use crate::encode::Encodable; +use byteorder::{LittleEndian, WriteBytesExt}; +use tw_hash::Hash; +use tw_memory::Data; + +impl Encodable for Data { + fn encode(&self, stream: &mut Stream) { + stream + .append(&CompactInteger::from(self.len())) + .append_raw_slice(self.as_slice()); + } + + fn encoded_size(&self) -> usize { + CompactInteger::from(self.len()).encoded_size() + self.len() + } +} + +impl Encodable for Hash { + #[inline] + fn encode(&self, stream: &mut Stream) { + stream.append_raw_slice(self.as_slice()); + } + + #[inline] + fn encoded_size(&self) -> usize { + N + } +} + +impl Encodable for u8 { + #[inline] + fn encode(&self, s: &mut Stream) { + s.write_u8(*self).unwrap(); + } + + #[inline] + fn encoded_size(&self) -> usize { + 1 + } +} + +macro_rules! impl_encodable_for_int { + ($int:ty, $size:literal, $write_fn:tt) => { + impl Encodable for $int { + #[inline] + fn encode(&self, s: &mut Stream) { + s.$write_fn::(*self).unwrap(); + } + + #[inline] + fn encoded_size(&self) -> usize { + $size + } + } + }; +} + +impl_encodable_for_int!(i32, 4, write_i32); +impl_encodable_for_int!(i64, 8, write_i64); +impl_encodable_for_int!(u16, 2, write_u16); +impl_encodable_for_int!(u32, 4, write_u32); +impl_encodable_for_int!(u64, 8, write_u64); + +#[cfg(test)] +mod tests { + use super::*; + use crate::encode::encode; + use tw_encoding::hex::{DecodeHex, ToHex}; + + #[test] + fn test_stream_append() { + let mut stream = Stream::default(); + + stream + .append(&1u8) + .append(&2u16) + .append(&3u32) + .append(&4u64); + + let expected = vec![1_u8, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0]; + assert_eq!(stream.out(), expected); + } + + #[test] + fn test_bytes_serialize() { + let expected = "020145".decode_hex().unwrap(); + let bytes = "0145".decode_hex().unwrap(); + assert_eq!(expected, encode(&bytes)); + } + + #[test] + fn test_steam_append_slice() { + let mut slice = [0u8; 4]; + slice[0] = 0x64; + let mut stream = Stream::default(); + stream.append_raw_slice(&slice); + assert_eq!(stream.out().to_hex(), "64000000"); + } +} diff --git a/rust/frameworks/tw_utxo/src/encode/mod.rs b/rust/frameworks/tw_utxo/src/encode/mod.rs new file mode 100644 index 00000000000..14eaad21574 --- /dev/null +++ b/rust/frameworks/tw_utxo/src/encode/mod.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::encode::stream::Stream; +use tw_memory::Data; + +pub mod compact_integer; +pub mod impls; +pub mod stream; + +pub fn encode(t: &T) -> Data +where + T: Encodable, +{ + let mut stream = Stream::default(); + stream.append(t); + stream.out() +} + +pub trait Encodable { + /// Serialize the struct and appends it to the end of stream. + fn encode(&self, stream: &mut Stream); + + /// Hint about the size of serialized struct. + fn encoded_size(&self) -> usize; +} diff --git a/rust/frameworks/tw_utxo/src/encode/stream.rs b/rust/frameworks/tw_utxo/src/encode/stream.rs new file mode 100644 index 00000000000..566debfd62b --- /dev/null +++ b/rust/frameworks/tw_utxo/src/encode/stream.rs @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::encode::compact_integer::CompactInteger; +use crate::encode::Encodable; +use std::io; +use std::io::Write; +use tw_memory::Data; + +/// Stream used for serialization of Bitcoin structures +#[derive(Default)] +pub struct Stream { + buffer: Data, +} + +impl Stream { + /// New stream + pub fn new() -> Self { + Stream { + buffer: Data::default(), + } + } + + /// Serializes the struct and appends it to the end of stream. + pub fn append(&mut self, t: &T) -> &mut Self + where + T: Encodable, + { + t.encode(self); + self + } + + /// Appends raw bytes to the end of the stream. + pub fn append_raw_slice(&mut self, bytes: &[u8]) -> &mut Self { + // discard error for now, since we write to simple vector + self.buffer.write_all(bytes).unwrap(); + self + } + + /// Appends a list of serializable structs to the end of the stream. + pub fn append_list(&mut self, t: &[T]) -> &mut Self { + CompactInteger::from(t.len()).encode(self); + for i in t { + i.encode(self); + } + self + } + + /// Full stream. + pub fn out(self) -> Data { + self.buffer + } +} + +impl Write for Stream { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + self.buffer.write(buf) + } + + #[inline] + fn flush(&mut self) -> Result<(), io::Error> { + self.buffer.flush() + } +} diff --git a/rust/frameworks/tw_utxo/src/lib.rs b/rust/frameworks/tw_utxo/src/lib.rs new file mode 100644 index 00000000000..84748580ded --- /dev/null +++ b/rust/frameworks/tw_utxo/src/lib.rs @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +pub mod address; +pub mod constants; +pub mod dust; +pub mod encode; +pub mod modules; +pub mod script; +pub mod sighash; +pub mod signature; +pub mod signing_mode; +pub mod spending_data; +pub mod transaction; diff --git a/rust/frameworks/tw_utxo/src/modules/fee_estimator.rs b/rust/frameworks/tw_utxo/src/modules/fee_estimator.rs new file mode 100644 index 00000000000..f6fc0242ffb --- /dev/null +++ b/rust/frameworks/tw_utxo/src/modules/fee_estimator.rs @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::transaction::transaction_interface::TransactionInterface; +use crate::transaction::transaction_parts::Amount; +use std::marker::PhantomData; +use tw_coin_entry::error::prelude::*; + +pub struct FeeEstimator { + _phantom: PhantomData, +} + +impl FeeEstimator { + pub fn estimate_fee(tx: &Transaction, fee_rate: Amount) -> SigningResult { + let vsize = tx.vsize(); + Amount::try_from(vsize) + .ok() + .and_then(|vsize| vsize.checked_mul(fee_rate)) + .or_tw_err(SigningErrorType::Error_wrong_fee) + .with_context(|| format!("feePerVByte is too large: '{vsize} * {fee_rate}' overflow")) + } +} diff --git a/rust/frameworks/tw_utxo/src/modules/keys_manager.rs b/rust/frameworks/tw_utxo/src/modules/keys_manager.rs new file mode 100644 index 00000000000..cb272d37529 --- /dev/null +++ b/rust/frameworks/tw_utxo/src/modules/keys_manager.rs @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::modules::sighash_computer::TaprootTweak; +use std::collections::HashMap; +use tw_coin_entry::error::prelude::*; +use tw_hash::H264; +use tw_keypair::{ecdsa, schnorr}; + +/// Standard Bitcoin keys manager. +/// Supports ecdsa and schnorr private keys. +#[derive(Default)] +pub struct KeysManager { + /// Ecdsa public to private keys. + ecdsa_public_private_map: HashMap, + /// Schnorr private keys. + schnorr_private_keys: Vec, +} + +impl KeysManager { + pub fn add_ecdsa_private(&mut self, private: ecdsa::secp256k1::PrivateKey) -> &mut Self { + self.ecdsa_public_private_map + .insert(private.public().compressed(), private); + self + } + + pub fn add_schnorr_private(&mut self, private: schnorr::PrivateKey) -> &mut Self { + self.schnorr_private_keys.push(private); + self + } + + pub fn get_ecdsa_private( + &self, + public: &ecdsa::secp256k1::PublicKey, + ) -> SigningResult<&ecdsa::secp256k1::PrivateKey> { + let pubkey_bytes = public.compressed(); + + self.ecdsa_public_private_map + .get(&pubkey_bytes) + .or_tw_err(SigningErrorType::Error_missing_private_key) + .with_context(|| format!("Cannot find a private key corresponding to the ecdsa public key: {pubkey_bytes}")) + } + + /// Gets a schnorr private key by an either tweaked or untweaked x-only public key. + /// The function iterates over the private keys, tweaks them if specified in `taproot_tweak`, + /// and returns `Ok(schnorr::PrivateKey)` if found. + pub fn get_schnorr_private( + &self, + public: &schnorr::XOnlyPublicKey, + taproot_tweak: &Option, + ) -> SigningResult { + let pubkey_bytes = public.bytes(); + + for private_key in self.schnorr_private_keys.iter() { + match taproot_tweak { + Some(ref tweak) => { + let tweaked_private = private_key.clone().tweak(tweak.merkle_root); + if tweaked_private.public().x_only().bytes() == pubkey_bytes { + return Ok(tweaked_private); + } + // Otherwise, continue searching for a private key. + }, + None => { + if private_key.public().x_only().bytes() == pubkey_bytes { + return Ok(private_key.clone()); + } + // Otherwise, continue searching for a private key. + }, + } + } + + SigningError::err(SigningErrorType::Error_missing_private_key) + .context(format!("Cannot find a private key corresponding to the x-only schnorr public key: {pubkey_bytes}")) + } +} diff --git a/rust/frameworks/tw_utxo/src/modules/mod.rs b/rust/frameworks/tw_utxo/src/modules/mod.rs new file mode 100644 index 00000000000..9d59f9124d1 --- /dev/null +++ b/rust/frameworks/tw_utxo/src/modules/mod.rs @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +pub mod fee_estimator; +pub mod keys_manager; +pub mod sighash_computer; +pub mod sighash_verifier; +pub mod tx_compiler; +pub mod tx_planner; +pub mod tx_signer; +pub mod utxo_selector; diff --git a/rust/frameworks/tw_utxo/src/modules/sighash_computer.rs b/rust/frameworks/tw_utxo/src/modules/sighash_computer.rs new file mode 100644 index 00000000000..0bf50529664 --- /dev/null +++ b/rust/frameworks/tw_utxo/src/modules/sighash_computer.rs @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::script::Script; +use crate::signing_mode::SigningMethod; +use crate::transaction::transaction_interface::TransactionInterface; +use crate::transaction::transaction_parts::Amount; +use crate::transaction::unsigned_transaction::UnsignedTransaction; +use crate::transaction::{ + TransactionPreimage, UtxoPreimageArgs, UtxoTaprootPreimageArgs, UtxoToSign, +}; +use std::marker::PhantomData; +use tw_coin_entry::coin_entry::PublicKeyBytes; +use tw_coin_entry::error::prelude::SigningResult; +use tw_hash::H256; + +#[derive(Debug, Clone)] +pub struct TxPreimage { + /// Transaction signatures in the same order as the transaction UTXOs. + pub sighashes: Vec, +} + +#[derive(Debug, Clone)] +pub struct UtxoSighash { + /// The signing method needs to be used for this sighash. + pub signing_method: SigningMethod, + pub sighash: H256, + pub signer_pubkey: PublicKeyBytes, + /// Taproot tweak if [`SigningMethod::Taproot`] signing method is used. + /// Empty if there is no need to tweak the private to sign the sighash. + pub taproot_tweak: Option, +} + +#[derive(Debug, Clone)] +pub struct TaprootTweak { + /// 32 bytes merkle root of the script tree. + /// `None` if there are no scripts, and the private key should be tweaked without a merkle root. + pub merkle_root: Option, +} + +/// Sighash Computer with a standard Bitcoin behaviour. +/// +/// # Important +/// +/// If needed to implement a custom logic, consider adding a different Sighash Computer. +pub struct SighashComputer { + _phantom: PhantomData, +} + +impl SighashComputer +where + Transaction: TransactionPreimage + TransactionInterface, +{ + /// Computes sighashes of [`SighashComputer::transaction`]. + pub fn preimage_tx( + unsigned_tx: &UnsignedTransaction, + ) -> SigningResult { + unsigned_tx + .input_args() + .iter() + .enumerate() + .map(|(input_index, utxo)| { + let signing_method = utxo.signing_method; + + let utxo_args = UtxoPreimageArgs { + input_index, + script_pubkey: utxo.script_pubkey.clone(), + amount: utxo.amount, + // TODO move `leaf_hash_code_separator` to `UtxoTaprootPreimageArgs`. + leaf_hash_code_separator: utxo.leaf_hash_code_separator, + sighash_ty: utxo.sighash_ty, + tx_hasher: utxo.tx_hasher, + signing_method, + }; + + let (sighash, taproot_tweak) = match signing_method { + SigningMethod::Legacy | SigningMethod::Segwit => { + let sighash = unsigned_tx.transaction().preimage_tx(&utxo_args)?; + (sighash, None) + }, + SigningMethod::Taproot => { + let tr_spent_amounts: Vec = unsigned_tx + .input_args() + .iter() + .map(|utxo| utxo.amount) + .collect(); + + let tr_spent_script_pubkeys: Vec

wf2~Y%uu$?6xGFFS2UI1YE8aU z+>@>TqGXpC=ao?<;Pklg&d>uPa^~qMM!EBGGvp04`yEQ}V4kn;OSJlE1%M6Hfz3Mg17L=4qP2zKlDao=OY1d7PuqF`ixjAwoy=M`t+^Xld#Pys?gK&$VN5W9BE4Gf$a?<9c5qk|R@@2hHBW z&BRo$X$u)P8TaZ9y9_0Oc@*np!}u0=Mxp86gOkyW);4k!lN}fk}@f8wg zGPv=4PzyG_M5*z+SLIk-6>kIw3HYTo4I#kIa@>B3@0Do zHTdAnDBedPUILDhca&;gDg?fD>@zMiB4K+pD3yY-adI>+tvC1tqkB{XMV5XK+9aNMX2(6d)JoOy!|Ys% zblr4e9i$4ZqE5n!@!6PRdF_M5dJUVD5(=ZT=>*jE)pu$fnP1B?S0Njbc#t!UqYiz- zwzGY?T?KMoW29K?-(^s$ckKm`FBf;Ly(h5s2$?C36ttwBkXhB;uZ|o4REqWiM2r0} zKR#{nIONkgT_gx{)hPp9B}wlTl5H?_fzI-gtg;}maJ$O~fqwM8#rTCDzwo^#Cj`hU zn@tl7utq}L`m(g`1kW>lQ|dh|de8Klo)Q5rEtNnmZ6Ez*{aX^upYo6(4RP4JqLP=e z^fWm#&a`0_QBuMUDvqYba0qE^<*hx4=bBSAh6fHU{c`5ip%a|p?+bi6<*=?@^?O!c z)!v`+l9f7T=N4Gth28-?k8MTHyk5ZT;P;-L3=X`(Z{J_wa~#tdP+`ce(g&MDQH9wh zu}{DhiRwH{rHa|+QiP@ZkUy@E{*m#FRo$#jrtQgkc@}nubf}7|0!5$Fv`?0XY-=ya zia<&;3}uv|55m_StXwp-lK^zYJnxcd*Dl;>)TadvyY42kG)tfOGmDb)+dH6^WFa4a zCfLXgMEwZ6h_iTlth|Wk=I_%SzQXXT=zud=hL0y-*SP;qi%-?^gMf83LVh^7`@{K) z_H&pK(JeB|BV@tJ^Gdi#PgBp)GDe`MiymM{b4bmR)7e!HAG)!XPDxvF#wfL*f&Hn~<(__G+(2xTmV)i87J6m@zE%1OS}Z9= zliBiHuyTZ2Brk&3So=(#>-MXcs>hW->P|j#-y6$g8U}%G>~vVWK|P0kMJQxX0tZe}BlGBc8N`SeAVP;B70N{P z^XE$T>{VfBqyh3cp)*_hUS*@K0h@wY)sq5ZV*+o&ZmmiaaH>CI5Rh7+Z*s7838iH( z#JC8cjg~V=1L+~wS`b#z;&vawTJi_S`B!k7#q3eDyd=xd3}8(HO(LclzI?z1QeOxkMHh@?+mQx{1N7{;=wi&)% zn{)&nzCy?2-6O}OMmOJj7nQ(yn$Ec+!BgTiC31Ja z?Ay}>sEca?ffQmV9bMMz&h-@W3Te+DahpgcphNdJ-f8mT5M=TiWA5%4HP<#UBbDwZ zuIqpR6Hm3H^_Z->@P2r>H%(!qI5QH`*(L~$ss{P46|tEU?g8hk7_qn55H4ZnJJ!>@ z5RT9-ZLIy1Aw&z1_Mw`vb#%T_q>6!XWV+(hwi^%TPSfQ<6w;FkIDRb;2;KMW(w{V; zA$d_vpQno@#$7_$c^20vd}!n~rh0idJ-MM3Wa8+|G{ONA-*bVz7t5OGT0nXH?u$cU zjA3!NGh#r_2Y4J_Z$Uw(q-GWmX<}Ze?A7P?q06c9G@b%>3;pD~EBAx(@8~=p;H#!1 z;UJXee0=I@fI0%&DB{%Wd!o?ZY2PUMn7k~<9ucL z-%aIGJM`Eo@abST)6(C|m4fP4>}sz>CJ51!6z(hVpPa5H36*3#j(r6yAi<92+O39T z=&Be_)r=IN)EFxf)Pd*vQK1C^PxUd5dtUwmVrH2U2C}7n!Rw*-V;DWE$e&?)(<)xB zQYk{bFp+2TwCRw?n4f7BHC?mFYdWwPBs2(tu8e<|C4_^_H!9Lh+E?8YDcWR`)lQSl z<73*Ea0Uw>?4HG@U6TCBEll9Tt^D@12*!Rl+dLvx7DVvfWI>F)$~%BUH<*!f?>lHI zOiW6els$X>7UolL7EfTE<(%S&Dx$;XdGHs(7gnUHFL3dzzRK+E;JfcS38-_d_>0jj zK@06oGU)leEGXX1yo34f{<{&P+bY|_l+=8&+=A!}8@Cv>d29&RXH93Q39!Z4+j%V~ zCVbBtfTGN7cZ`vQ1l}=@w3MpYL7h&L+-5`hoGQ{&<2#lfdg^@-ouZ-4(#`Qta5|HzBFzVx3c{zb0OPe}ap zlpl5`raw}C7}(iwr2MclF#VD8!wOXS_&w!^jhXqcGk#dac!qjFdJ&Lf zWKY6!4XvhAv{AA)Gq5o<0!aD|cXi|If6qPvav!fjaatrd-n|L^6wtFb`X)o)iTEDH z5)>D@dgF?E#&6{|u0IF7tY{-DE+nM~ru$ANosbFu83N`QcvHdE-oeO9+}hX%i1l2< zeXcJ1gW)7s0VMz=r2>G&e*>D|Anhh}@;a>&NJOLh{x#GUy{4uXaCPd4HzHGAF{^CzZ{T*Kg2B=e?Z?|=ga&F-9SbL0}%WL5*!$T=qV5b z1mJkg931~4*)9?`)_M-6de)M{;&k>7dUh7pMh>)g_CKNaM{&A|75tM3Bg;3S_l*c6 z&vfi`XW4ea@S}woN!OOw`Vfh|O0kw4p&y=RDSdz=k8h1#02#zQf4#6JH})lSKQXT! zj-+J;iLM!pZ9@je71~EV#k`G z7njEJSdnIU@dO+8+^KzxOz2qDBasqsH_VIOy+CvZOd3YUh+44)&RA|`8g*a)Y|l2=!w-ZXgZ0*y1i5_+EY_ToH(E`5Wk?`;EeN$Bhohz;$Us;#qah^fb?ky;WG zqF7{57{zpk^KkK2e-*6qc7fd`It;&-YzcPX#oS-r&FhNL54=4G{hjO5p zB_fOh#DqZvx8HYXujnKyDGgkNURtRsTLJOK&U$8S#)KGUTa19LH1L9%-}&>{TExT) z9g_Qu=#*(Ow}nF_yJj2&p+Lz2as=K)x9{8{Y;kzVIU)oj;8&q)s1*~P89i9`bUuj- zhRt@$m3^)xIJK$>my=?-^qiMjx2stxr1|`X)uyO(A?d1riOYWd?d_(v?XZZnaA-&A za%O2Rg%fB}kBZ$opo=s#DFwODCMb@ZJQ+!ZGP$!LJM@IxB`{kWE0lM5ZLam0%TC4@ zN4}P8nZ+B;W^fE)o5!+AdLf2g(gX|cEZNJTOkSXU7W>g#p#VlXoh|R-C|~dxo_|GT z_vHx6>IHonl#JKs)(^P1c%Ed}WmBTXu}6O%4$s`5lCmsO?aL&ls*xiZHZ6Uj?nvJM z)h`ky2p-2a#i8SV8$aIeVmy(lEDV{i3X%=N310V%IqhJH7pQR%dSq%gkyCoaCa;xw z$#$GD4E1So?d+{u-%^a`Ju{Oc2a<>Psr_tt1Qvu#w^XBZ(}P-t-zGVCz&esW#$Fd8 zl2FmG^{yu6yyKdYsKdfQE^UBmYy3gd?*aZZC)Vty2+zQb;rzpP+_>EBk6)}>)D9Hx z1`fdUJwvd5n6>N4d>Xy8=7n_oE(%5_SwyG0h!2!8B*w_hNqM85zMc(pl%@5I+^{NM zU0zGbvnAy7*yR2P%LC51hC`KeuVeBZR^jzBN=v3a%zVaPLHe#cQDqMuGU||2d3o+{ z-KqSVy?={g2qddFXU^)PUXZrwdp?m=#Addy%Nf0pK3zvSZH)unjRN3 z{E>W-jI9M7$WA@Zoc!q?^2?#buDdhZ3uCn1!L5^RmSDm6cNWFQO=-Z7lHRvD$U`kK zgs!>qK2>&oNKbvA&v^TZ4Xt&d#&qIo&0XH|Cu@j2+90`|%)Fi3;V!UKsN_Ye3lY;Z zQ}D8ov{Z|=ewof|oB9M75kcAEXkqZJ>3t-n#cQA*O>r@(48BUwsAbAD;nNDJfcVDB zECf%D#Tq~MB2BhaqlU)2Jap-p`f#>KDl90-`W*pAqk< zJo%bY?#SGEYo&d|zsrLRW7I7owOaE-l+LG5wZ*RVntd@nQ~s&ZkF`e|V-yQ_!=uSDttar`n= zQ%#3HZZUb1frvD%?!xUhNNjlN3U?}dsNMqJhlPYFbQKRDNIrZP^7Ua0fseGp30Ce~ zRPM&dUwnyinbxC3%f@|SX%&3LoQvx1V(jvTMAW{pQaEHhA5mgbA-B|CEiOnno{ti# zso1dg#Ejx+6-hY@8*}LX_DG%D)0O_0`t~zI*(db+>t!*@M?8G3L8bmJcyMnP% z?y34Mp=n^L&PEj|$pviJ4e+sQ7o}lR}dA$kNqMJ-*E)aF|m7EI2C@C{f*mwmSq7dWXZ>U*eB=+NwBgT z5UFe{9+7kKDaSOYOt0^8On#~d--Wmps&h(z+awp3sTM@>P&f_mSxl!>i`iTAPNq)B zSX>0>*pRz`iu?&1$ns!Jq?V|F0$(hf&5aS>rtr2D>-wdX-i#e&o%l5Vw){J_i( zx`@^i(OVBsNOh7`ZBXf%FDc{3g`H3(Vows*ns4qJr68oDW~O%Q+$)9@V^Y+RW>(iH znt?|jc3BKM;0cI@TL_;JQ$gBVUdX%+H!7y7h?fwY3TC8B9bdSx*m3Uhm$I(j8@-z`FCt&^%jXDtYmJND32$RSx1 zw=(JUEV|6t4xv5#LFT@=Najb?(%stbyqC!v4GR=;p)$zr$rK|mjVVl#Or;5&DJhS7 zWC){?;}?+}a314)s&W}9$mV=nI$C#GUm}noQ(1!0| zW+AD+=6f_h9dQeB$_|5mqjc*ldTU%V@?A&V&#>+(2y5}mJZO%5Sh*Fw!i0_GJaAc& zk)}E%8-TGYF)A19P9!^MDG7!NGr0|?iK}^!fPHx6aWHh^mN6j;!TO4WIc3eVZ9l2o z3pTAxhOMkqch$g_01Zi}F2(^xWN| z8Qj3*q8V^H(*{@%Jej|AyFK+Kb?+X$Fkzf+Lf+D|dt)P&-4qXvmY}}&F~3u#h#Qpa z5?|U{ZXe_b5MkxxRu~aybpr~wbO+&Q@02eF?SGOBMvPc}!Z|aGO6*9mKJS2sk~I$J zc1L#Term2+7pcGhnErJQPT}IpPTzy^gx8ET9Kn--oU|C>$F;Q>v3G|M@2wV3?`nd>IbQ}l_G!Kv zKOQrTcw!NVQ=SgbiP*f znG~M6I1;wUvYfw(xlnyKX^l3V)dA$h})C`g- zQYRj17h_ovfp+2DhJ@Ys&!IAzUerb^)fFOKvQLeEAdcX*G$Yxx#s_D_eU7WWq_Tx{ zl@Qb^b;qFSXLafFqr-k&>LkfF9!W<2#n=Gtu|hr5+N{3Jnwy_AIU&Lt)Mo_fGl9n+ z0?wZ6_3?lb(<90+NFFc9FOu%Aw%|{})Z_K?qZH>x-|7cQ~w7 zk6Vv>Y@ET=E{;T|l`o$T8&nRtseP7tc?gSoq)N_vLL>)O%1#EPlN62HI;9$FTU0$I+JDR{k)0xfginV`CDVX>8&p5jJmnES5IWO)WhH zzbd~&smzbDuHw5bgEzMJiCJC-pU3u2fb`TY(VVh7a4>c8$|oy_O%>o}t2y=>+dGne zl#k9<-IUKZ7u*E~Dv8TiA9|Vq6p~1OphP0tU(AsB;%f^Oa>h6{kZi48df!&X7;bj1 z0!V)C_{l|Av*L2-2O-K76Ir>kUU_s>N{(ltLJa4aNAtZ3LxCST6qgfU+*8dSeS1eU%LQYK{&0NnBP?|aZdN|xiaiw#=&{_}-CcXC)9Ty>qc3vm8?Ohsg5CDY zM=_JAfIsSnqH@U>#Xb9JGYTe@O@=Qfa9-FMcc0YP*`mac8NKY2Kk$K4xI6t^TUfi( zeM(cE?6$EMt%~zNW_1%)m9U6~>_85yT7F7v4xyyyw#IH{1^%im$LojjBQOdLlnNZ| z@g&v|7j7^SE#{okjB$ZS9ZRmaH3Rdlk*g7eZtyepF2>FXTIa2pTjh zot!G|5x$#BEQm~VcwuQGhpL~N6=W8x@T?YI3YKsV_K*jP zufaC7$}*5=VyI|}!!50{wbglvy&qWH>Uk$~wSR@8cF+Eb+n@lj7<-ACfJP}G-3 zo=-5&zUmVCwncC2NG*k7gghwyb*aglR4kIJ($e#@@NL3C5qd%L!)L(NIXP9ma? zKSLk_5j$#8`~#Ko{)k(kNUTJjrG?rUi?)~(PW@QIUM`yQL6hEY8LbkIWtUf$-0;ka z(@fbGOkCFL)-46;vNe^;`;b+fB_wy#MHElhtTisIUekQ7e}$&vX#%er-AaZ*ShVRJ zL6cQX3s+L1v|LcaBRywFbk#Ej@#sE*;21|md`~yXy^vN5Sx1YVlnWN2 z3X9+2Lp9y}O8&FtI z>F{yel+TT#^&_dK?wmYzwVlFfg~tq4X~*Zz-0{PYgZrhs9WbJL<4H30*@lIqn>5j+ z{p82UUCvnGH=@pScSg^KNhWE=!`|z#6};1*2Ib-Jq*7x(CSDElk8O?%fs>3O!~RGT zPg1mGbvUj(BRIa8RSog5EPErZ3YrzGu(2XKWypuk@6{Msq~XmJ@2zOHeI320KGhSruQhR=snq_I=007u5dnS}E!=*rS9ymHVO4q} za=y6)Xj(+^)%}4WaV^8$r4&m~KHd?8S21B(_4xiDqFEeq?`j^?YqPC>lxlUwpfOTS z>4Rp_^vITnUpyfldQL^BV|mUtYPe{maG#3Oea=f_#podcy;7G{h%v1b2g=o9$GWdCdDRV| zhH}S}acJ-*lZ##XG8y2L@z|>47%f9nO~UmN*eX{FYUhpppieJbrL*fZtqELCRdJpP zj7BZnj0)$rks8R)1bpEC7+`=@CxdLxjnEE#a_37Z#A;IZbE-T8;rlq2$-_iCY@d8y zvvnAFfpau2>BKRxBlD=E8_drOUd4nGv37Ke%h0k0mwsfcX5~<3%!7NpSs$x&XWXq2 zX*HhP6JagIh*}zjKwpw3M|xD%U1CitvpZHa(xh}>UMLwbq0 zt|JcLowVPpqOe|NCx7=y|6hdne`y`b>*_||x%}T7;k_2Pe@H01E}-#Le=@$`O7H-`fEYD1Bfuzum;y5{hRUU>(5-%a#iH^DztwSYjFL_cS?W9 z8vLB1@7nG^2Oh9}t26Qszyn}S{suhw)~4j4{v93j9q{m@p!~q0ABp>O<}fgEFy4TOurb_# zh;Xp6{sBi~VZYIlh=H9MC>{P+5D}JdU5f;+TjbDQ!H{Sf0Z{|MSL`Ify#c*-b&KCB z5Pxg6^TU{LVt#&&Utk2V7FU;jKN`EFc4%1H0O-w25Z_n5M}*y3+MmxrsKStMmOCcy zgURNx;j)Rb)M6d0UMS{bz|=k(^`1;7C6}O3;~K3qLkJ@HY?q!q={mV+3~uOI9T{Rb zd2~+CKCnM@VrJ19$jYldHNTlM6{?m7fVJ+YbQqkefw`*?Yr@Nv~c1}HB z+s6$b1nz!x)RpI`HBq-ScyB(ueX&QVao{e4=U_LvjjSz^P}XbqhBDdbQS4jY*JaGx zdnmUW@HkoPw~t{Y$B~LT+X|~JS+X-K5;ErBY#bM3BwY4@zK(4-LKr{7EX`nJV?@-% zO0A5UIi+AAA|8Dh1n)x>ZCP$4?BFe$;8PVNZ!?!RQ zdnuM$LqK;MP6#Wl-r1O1|ATSUZNX2bjI9Q+#|6;oq-y5j#+pmB=1}7Xd5;_3bRu31 z%#nS;j7+g9I^(EwtnWdauiK4o9}0)0C|hX$>TmUw$JM>(eOL_^D=f~54h8m(6ZkOM zT%en&2F?ys99!rq2DzYY5j->lex=ZtIHPpB?J4Ms-WA4qaw#?Q2d>siT!z{^o#dz^;MH(TlO?INTc{)wvt=~&VCXd<&J0k;@7@?SN`3u zYgvCBcPv;PhKSSR#1F#zy|PwPVpoih8SJ^@+Kb38k7GV})_d(~bqh{^P- z?y#8a7%lC6Lqz}zXBojDQ%!`nTdFBdB)i+=oS zy&&bQV0dJZ5Dk&0%Kf`|W+WqI&s1;2KnFTQg&+`-z0k109v|~o(PW1(Q`@rW6+Cew z1zOuwY*B&owHOLnYhz@oRrfNPXmH>IbNP0FS~*GqYmt8U4w>)V$$tGQ6xJa7c94E{ zICdeT^Vey~gZBexo2P~83QJGjDFRoMgX9y=X01iUMbsAanw^Zp%go%o*ixi&bWI-L zsi%Z6up@c+{v;h4)L?LI0I^K&5@2*EuF4n!vL0f<=5`Hsi@g#Wt3q`EzB!(S3^Ste z3?ar2>Hf6JTB8Yl`BeBE89U~YalXFDZr$g$AT; z3lvWW?ZU|`nNQ0gdZVW* zNc<*y^VwIFcfpEJzWO(|^RI3X42+OvG`Pp77{2c>ONqAV&U#*3A~H8x;P%|e^-03C zy9o+G`?$J^$)=K9&U0{;YPs>-@RjHJ_zh_esNtxjV&tXl=0WsAXOf+`VH1V$hk>?_ zdBknGV2>G-Jz9y;mvZLNw?f6m+gjjL7(9t%klSAr1xj%vKNu`MqP5(^m#0`iq^#A* zcI9t*eykz8!Nb{Or(dkviAYX?__^=H+kmqeRvgM%>7Fq}>B7eu5uF}! z!l|z?v^%^4!!u1K1k00zm*5|8BEI6TckVU!VCWPY|VS0BIR5Ml-&r?TE_ulc$30HPIlUjnsA~8b!H{Hl6MYN8FYXkhS3Pm< zuHFs7!LO9n3e0DKri464fFltYW_cMpsxD=)Db~*QJeZH4?o}WxEK*c+B z!IV`>3MR|Q2a6$Afr)6TsDk+y(Miz5tmAFJM;1+k zyv`McvsQ&#Tk?fdi|zFr?{vPRhIVAHU>B@(_-IE}9U+AFP;tR7v|~3shf32#^#8}+ zJ3wc)ZR?`3ZQHggwr$(CDn`XNDz>dk#da#TQ?ZSkRao6mubsF5v_EaO ze`?G=`P4u^z zmQOBO7SoshE`$p3^Lm|fP5%R6AATG3L^>kvGpbE*Jx-Z)=kDRM9-=~Sar*=;DaZ=M z7{D?2CBB9k=()We_^|b9O+3u16iXh-Rg`1!mM$hT2Evq; zIyQ;DL=BLfzV)sJ5sH#6erNunCUT|lA=H~={vr|qrZP3l^{K01l>B(hl8>dmzMdu) zLMU#KaF`j#WoF?X*-1ro&Eu*(q@_L-$)Th?F%GMbRp+_uw@&)PNjzQo)aU&1~6oGWPvw5uJWhY3( zp{XAfCBEX^vDnrlHuD3Oy2f||u%lK_(J>$ar zrux}+HmlawwwpB*jSl7P9q>D*{!!+8`|w*}g)z%|??B%Zn54f7%(-XL7mW^`(BNi= z07*ZApP0vxM)WG%VQDUfUciR*%0^()r*pyB&0cYj-g@i2qdBK`Ks{HZ30i&2_Tl-u z)|bR2A2kFis^7ZTwfxwSK9sdRb|Hl`U z`6U|tmnqACFA(_4;r;;#{3V(Fm*)MNJN;EsnO;zaUe?ah#=x3hNZ{|&-hatw|J`1H zR^0yURP2|s_Mb|%e&B{|^x`HKX6DWWEUX{16+3G?MX3s${@lle~(caG5z}bYr)WF)wgkDL;z{%>PV&|WyWY!<+=|AR5&L)4V zxkN8*Vr%AX{-G27E2;W#vy0PiKv)0(fS#OtIU6|koR7tSfBtQOe_P+SNNc~6Kr8hCv0t3C=1|5hUHCl62ZvI@C&j2 z-MeDWMs6MyBh;X!)gN0N7*NP8BPl4ZDXo6I=Rh~jbZRF=G34fzPY`Iy^7!`M`kI5c z`@FyPI6WO`v+u^7CEefu+k~y>(>a)#=>d7dFc8e4i{rJ^&a@t8^gseLb-C3sLrLRY z5;JstF{W$``6zZuwY5&LfQ^lAXH6XgURGw$5;HdbM$0!Rbg)LJ=j+M{vjbV`OubI; zowpscc7_tO4epM|t=8eOODQ}VRdzeRH-~pn7|Zu!ay^?$`b=dGW{s6)kEZt(LYSky z>ou>H1N1?1RJsbiZ|%?97l-f|Q`1}B>#wKxZ#o7%;mzaaf#>JGWrB)S1;v7%|Ni%H z8~oc3{{0yI>sP_O9xC2kzI!_kF5by&eotfNr;XF^-k^a`ua9TnPT5gqRoTCLUtM1Z z>KX8Z)BAX|Io-bWj5!47KPJK3@_M%21H)LJuTM8ws+0!be)>N(Y4g===x_r0)TpqDA3nf+`F=~a4PS=w|XI(*0KOPm0 zf8`Le^B7!M=%%kO6>(k9T#Sfk#*vQMdtoYSN5p-GfTVQ6cS*h`ke~C1-PEu0FaO8G zStjPc4QHAD)1z6AzmT5)*=UxP>F=TP({4t<>cm# zKF;J@U;68}?ii=N<+Mm`jGaXcqwk8@s8k%eH7qJ~5y??wRG5!6a+<9S)VIkS-FwX$ zRfV5BX%=vNKVr@qGEdo*@v0(j;K#395H!mmv|2KFwT3?i+NHdKU3qBQ_g0%5@q{mT zAWvL1Z!^7@u|zMgK~H>&RE2+phiNh@P?K5q3sJHB9eV}ynYrFc7W!cl27xRGJ%mxB%7AaPs30Dvx3%if z09y11n3PD!m1@Wq?q>lQtY1zGOpC7UXu?izz~ zYWzQ5Od8MRbwolJ!`>)rk2#+Pf{5tZZ0h8-T;w`g1+P~lux7)({iYbNWSS*vXAfQg zTfMyJ?TA2e+i}dQb-=HFT%p@oKipa+AGtcQ&KtYG2&$A&?f@bpW#-2rq*nN_p8I%? ziuwS&y%0y3a{{fe@ErfgPt5;WT#}WE;g{g^-)_~1a&ruJYpoCa=KEBa2PUY3-1U5l zKBFJ~ai0X+@uE{5yePsDb2{`qu?!2=GX{q*#XD04(UPSEO?=*ReR~s)i`qg`N_E-N z$t-&L?djZ!q0Y-i@6U)^Pge)#?=-yGAEC8(56`z#AD<^%*U#;#m3Ao+v+yxr=j{*W zu9x<_U0hW=ykE;6?yt>Y>8a@=L>oYN8sBswR3$Yk_9wr-UbP!uzGxs#k6s^4%5K!; zQBPE7^BIX0zMFwssZgToy7?4CzCGm9RpDpzmEZK|O?}M^lq(FMW6eJMGJF31@Uk?R zV0bmT{WAG+u#(_cT-4|G=bpBvibxVdjZ&f3HbqgV|J}4z8y@^gIjZ^dhzKPV>8K}4jOov=Wvu1EiqTBfyRUJHhwd6?)QrjG zTq<>Ba*(f$NL=CQ_;T*7(Oeyhq?31$_!5w+Ngbmqf{}$mRKX8XTHt0fOQxYa^fAb) zu%?UvWDx2;SuCD-v+{*gvwEL0UoQ?ldlrY;IUb&YqC0~(B7CxG#F%82ZI(>E9&o8% zO2u>-==CBCQV}$@eZCsg-VIN`kSr^j!Xe_z>GJ=W7V7tkJZpUW9C)GJ9IT3;#jwI# zW3$k$5u5 z1EWn7a|M9}S#`t8Srj}7nT`*t-2zp<(JX6J{eWtGb1n8kMQCTxDov+$>_MgBNjjZq z=QY|6-z4nCX2OrjHcYMNA`61M{5PAUV?_~FI?*^3^`Z(RP)JATKnDiOk~rAvY6mQ5 z*S^vS*N#2cJXtCkZZ%j#sj`}ovn0}H7_j!@foa{AHeqNx0E)~uyVW0amWMBl8lI&4 z6=@s;M{IBBa(pfpdkuxIl<#$pkq-_S)yx4Nc#?9j@_NNi*D!y0x}|3~HG6w@_r$(ncV)40*I-{4+D?i+Q=~^-j97 z+3yo*3;o01jISH;3BXn)9r{SeY1wtMDk*EaDhD#U>=(lpxB4ndj<)2Ay{s1T9QQk( z+*{IO1yzt+@Wdn2Yy9$}Pm;!;rcDNgQ>vEcu)dYOrmsC3kI32XFVCgz*tOHw20o@I zK7f&|3-&a&WQsqZYWjKs)FkmipICN-81n`pQCRu-w{FpT>j_#>pQ$C3%7UlpNni}Rs|;Bi zLbzpMn^fD_?AeGx2U~c#>5#wt#xeE|U$E{wUp6+Su$kg=E{BZz()t6Sn@itTl?{z3OSDj9w!%OzTv1%^ZhLze!?J<;^yVwJS!){v zY=V`*&pM0n3xnlp_q^MwWwyk_%T-#I{UVvntrG?Q{M5DM>U;g4W;X1L(Ka|dZ;)6S zKaHp{!uQptoK@NQ4y}vR?`=n_!A{Gn0r3uB*Vaf1sWR9UqV1rzH!PsaTt*V}V;*Fm zC^YJ(BHbH?Htr z;5@7Q*`Xe_>7Y6oQv_Dc%7`y*lf}R^>o|MEy;6^#vw`?T+8pHS6D!bg_bVn;>3Kn{ zzKIIHyA!JInI#>UufTF&^~IZcYM7(~=ESML#R zb!DxmA0W3K-K>S}vC;RP#*hom04Sb)Q?JzavK--pESnWH;7P`v*7qf=9V->WXhbu0XSh{u1&;n-!Wf6Hfs8 z_oID+oH)sS!_BX#c!LyOgs{ki!V%Dg2~N~st#k^AdQgj2W6;YhQUFD%w&-@3wc`*N zL`}jeJwdollP*0^Sfxqfxs1`tjPH*n%;(kn zR$FcxgA7xS&1EWQ-x8w-C^!;wz)tBPb%>{?Yn;aCW1f?u^ZTg$(GS2ON5XU*)~aVJ zTJdT)68W{Rx3tsj@xWc2)+3L!K&06R(`R8$IE-Xh{7obLpf#Fs$GaNcoYvVMXrY#B z>Tt`ZoORlz0^nFt*l@H!Bpdy31i6vKGuauB+)dlJxXcs|vgk{$CluZASQp70G{MAO zFT%>SE}|$eM(bA2a#pe;10o zP^|8h72~@`V6UE+*@2EAg-+K#mx&UCc6c?q^sTl&QTy*YCLIC{;TC~3X!K)*+#I~J zc_s3;l`F4x%jO2!n8D{qWpNu$SJ^aFtIo2Y-y`bBrqotkqqCyAxA`Zbe5$++EkoLt zKJrCb@I9_Co!T%dfA8eV=y6!;YOS{bX5nHi2YKn1a(7}g!BPM5fGt~kWWC&DqUakc zg<1zxb+osntiiOTUZ-YeFYdCB@-dEZ5*eo7^i!z9(_Y4`Dq%3*-?m-`gw0F{S;b>I zDLXQ;QMWgC5CvEPxkq9(vZvn`d5;QvZf*Yr5S&I$p_B2nx18i;P08-V946Tu!<;+& zaV)YanN@vk;#t8Ab!fXVwCt{MWj%|&+-swE^B3kKR;LafWHI+ak+h0hp%oZ}9|~GMsOKQInU>NHBB-yaz@Up4fsT;zr1& z*YMTZ4n$08PMGJP98XGCa)5_P-tXM>{O;*qaH}Zi6M*54lv5CQRF+%|QO1x8!yM5g z@g0&Rn_IW4KMcHe0vbhS4M6;-fu>5WPKCJyo4SMeY#>Q`lvHD!p&v#l3r}+1(v1al zJdFKLY{{T@v>g0%Qz?wFSRSA!&dkje&CsyNbl)_JkVxr?(03v5X_N`q^d5~lrBu(K zY;j5);tdgG)VOd-s;YY~Akrc3BDEykFQC62w8kVS)c zBxfB*5^s6qE%>n}ya3BUb|Qi-vS__RUuL4pR~*QUQ({y42vVBIGh%=eJ8x;2sQ|Cf zs|E~GqU2nDt{FwQmPW|c`};MyDW*q&P4X5h4*zCak96`@GZW?LP=ZxmWO41ZDW)^Y zGmQ`#w>G~DWsK6aF=h~-p+-njD`gaz7qm*M^cLzUQxgma+31ieaD62390$uNg0jg4 z$C^ws;}j3`Xw4OS|D3*4jwFl3n4x`?`P8{S z{*9SrHDoprPwDULSP2Bky6DOomOs1{=q?w~!te9QxUev7 z?1ait&xR0cc&|ae92QP;T=8ztSf2gjhBg2kJtEP#!&V|iZIHeh}HU@dH3z!sCR9((GZkEb_? z5uo|6UIk=NEnE-X+(PB?uCJgcaeJSoK1|g|4b?2HRyU@C;SqqldIbq55XWtRX6sQY z9jK+@NW1EZgpIpOM7-2n>1o`JCj3|#89+fiyEYbc1BeNxAfT&&z{;Xef{;`i3#t%G zg#xZ1ith%_x@~<_+e2cifIwPkx9r{Ca&*tqe!74^7BT7Wi*=^5v12NyXSaVbnb*Zy z#r$lD#kvX`6(j-a>BZK99NtSJd}!nP!h~Hl{-3QyKWJ zjrW^v96hibX79IC(AEM%Im9ZSQn5RKqSjHM$d?ALl5iScIOYgL=ALrszTMF|D0ZnP zxy0M&%I!f%mDRB)83(=$bok{{is}e0?}&)*moIdDm8R4XGtJ}Mdr3>n;hp!HW>|{_ zi(9novYn#lnvBZ9=n)|v(_jradi=ZWo59qq@8KCA)X3Z0`bsLPgt94fdO1SUl{zkG zn9F>w&Q@$m8sbKhZj%B+HaZ)8x>v&FxRAvgk3sua;Mz#%UhFEq$ zBLyV>^_e90WVayxOcq~E!RNw6dtup9JB$gE6ysb93``ld$u%Rb(W*uht^Kpq906{2 zC7FjN?97nrMp3MD){r$UYH`hYt8$ z0B}x)sg+a&w&{4mZ(I|X#MM>U4JTytHKmURtQ`_L+JCJTOJntOs?uvqds*Jcce~|N z*TCMq3eXu`%}=NARDf=3AInu`Agj3h#^20$7W4OCvxtCD=I;Q48!d$p7b%IP8=yOr z7QxVS6WOjVd_H{5bAblm(+tveXoZ|3Na8w3WApT*?{zmDZftq}=zD8c=p#ecJbGY` z?naLWl1*|N^wd!#vC}Mq6-87$Ki-P|4I6$m1X;yjdZ)2P932|szN%9c-rY!l411=7 z)C^E5_&hq8r0`a&pdR+KMcxOp4SC!~Ay2?`)!&_~Z8LOMP?4IXEjW((TNbqU$Z5-=)J z#fSwc7n0}fwLpfF2ZbljB2PI!T?Uc&)uHc}+TF8o6Rw9)Af3lHWRp?QMvbUtR}A`i z(W));aOGenv^eDLYNlD;n#ENGNr~kzQdW}wUz;;%>Nm;HY2 z`WH+7wW)tQ_ODa@B7*%x+y0PUMa#%UFW_YKTjn9>-zo4i{9f&uor&Xb^eUDQLe*c@ zo*6klm{m`p--&Rz~&@rWFGR0V6Xj=Z6~Y2QT^W zuJu3UQT_dG`Fo+~U)THloA58Vmz=*PQYrqPd-=ii`Ugw@+UA#~wFo$Wg{yzhz5Mmd zU+%+SxtD)w!(XU*|9Q6MhpzB1SNZVAzuja4&OgN?KV+VneqG7$t@dk=zqIT>#kTx) zmcJb7fA4D_w*NyXWB6kiWBgFGHn9J5hU`CgF(x)P*8hZyeeC_4S(}#ripIq$M z(f&;4WBip>`*-I1KMR^MF?0M8nU4eM4Ugd6AOBQHD59VwMMxn>X4%$Oi)H1ZiUZ_X zpceJhiu%>Cqa)`kew(icvyehEa=D9%WxGy^H{x(8GkhpBvD-}>g`Hiz#ISBB&NsSI zVCeMNdR#_4F)y0z?P#V2}$V!6B7HyR~G zWX1p{MIiT|RSO2i^xk^vcJjaJdVVw)>l-qS=}yKDaX0q^aq1VAd)6py`pw|`6u^wi z=ooH{3Jh+QNV!T4#{9FE!sqMfo&0h@?6-*l-%q@r&=IeqT|V`oh+?bE)RcPmxM#}) z_j=3UVEMw`2{bSu&K?^Q*09ZcdFbYQ6ZC6(#2!sB;N!Q#uvTH^yxsc(QLOgrzqyE% zCr$1FntzpQ{S?1R(0cmgp3w8gs3)HXZv@VrPC^M;c?U~M1Zvr+m@~QfA;;$qGvoZS zQmcFo{9yUC4g7xDCA2Tf*jv3173aZMS!;36LY{3T@Vjk zi=dJ;Nne2~5k{a^2gAHpAtK2)A;qNAoc-qfs>2%V=`*6;s4`Vm>Yc}?a9$R}7Ycsf z2f*{5x_3?>L@7LHuq{cX+I^xEFre^kAX?cfuo)*{G0*hT38uJceu$XS7HWt+sGxnx zS7TRk#H*5eE*~BE;Dwg{DMrH3y<>(TFBol9um!yz9j_&gI8co4eFNw)T9dWebN3`} z4V-%-+($H9&_LcgUOMjdayWoWP|3$WWFo?5GijO1eXr4E+m;ttjj~9FUnSa^OD2bT zJ{Uz)Tqvk~U$B*_YEC<=WDOwP>M`}YUNK*HjrOF5+p&GPkYOf;RlMH{WX04Q2R18+ z7kx2YszUrZFLoD9!Sb=)5yV_V$q^yloZT!Yg$^25YI!bO{625CnnVldZ_&;9s7=41 zTQ@M12wqVfhLebrM)h3?!?R!n#P!98Mj@3&S7}v}KF#?i>wK{K_Rgrs^A8Ww7)~Ma z+{7FNeugiRb-4sd0`28bln{wgpkM}ktt7l4>NukqQru|i*`}szGZa`k6yaSG-5OEN z*m)~1J@ksZkJ(cbk_}VBW7-PlR^1%As!1C!Nq4B8LaiP60!bz_i9Sq&iuY+W?Kl&l zLrG9*fQ*{04FI#641fZ~o`_}EU4t47fdLvJsAm;_#Kvu#CRWSpM+}0mf zJ*?Lgs!;`;RO-^ZFvQtXf zScl&ElQ3nHPqxqmbK-B~esOJW*c~sAXXucUBqI65 zM;vRHo50M4BACWAQS$Xee0OF$j>w&hL)AIt4%UHVCBIK*z{ZQEP6kVjcK%dZm4C6UU0Wfe9 zHIy|DfVv-PW!uu|AK8}sfZRK+$jYtJ26~(}nVgMBj14WRXXFteD=)$Avbe}$!acK3`|A#ZFTFE~7}jL4#`Q%CMF_c4_d*`= zWR&lMwhyQ+Pk4LIuxQ+B%yJC z{SuK2R)++>9~o^YxRS=KoE*pcO?^=#rSO|VPGB%DU3XjlvC$^)RCqZ9X@D11;GM{*_58czb?=0^rjWa90{ zZxR|Nk`1E~E9nWoE+KM3j6NZj-OZuM^j?nJg`rDPaDG1s{chl3J;4w__;bfe`vCW$ zSs1{8zE15UWj84j+{|3}9L~8ZJV*CKL`Oxp<-59H1&Lw4ki~}x>KSzeMZ2o&Fx)Z{ z04FCJqitb1in#-RXzXV4JQO}}r9^K(f!uXf=ivLeh@58Y$&j=^f`Z-f9nO+uNO9!H zlIMOeI$_l=On5r8ev%X(ndPQF{saYkoz?!p559_Qzxq8n0onc8}qq5C>up5ICsY6vNbPG6Bu|Yj9y?z9(nJT zeG{EdiM2*tg4Tfdu*B1UW^=dKs@kfW&B@5u59`)kSIT?Y>|pphEC$%)&)lNO zn`2(8--xwk6NOjRcoFCTO9&O>GXQecZ@5V3?XTgIY2xA3QC+zIfHJ^Wy+sv;cp6*A zE`JIq{;h2)up}}{s`|Y9af#J2V+H>|zzR73zk?P0a=3q>4s!k;EBF@_8)mj&@>ah~s?*St zBW6eOiAj9l984>_eBd&Yf(RQGw20`7cfJQa6=K5_R8GwuHlD6}3VovYiY8|lCkoi1 z;iq|CoOtxM<>e(2%eQo2IaS%+B;YJ7vpuIfPsk`x;`Nc>+Q7fwE&GIlwq~i0CgF9} zC^%#1(fl&D-O=lR@^qRgfCNMIf!-}$Lj^rA?eljhe@+DR!^BLLZ4i-h% zT1fMsTMBb(6{7Pk_pCe38p}gic9gz90I;6 zz%3J#liocqws<&3?b@|ia?GA7*`GdT6Lz@v5<(JVf=UZ^`+qmaGl`*>gLt(6*+Z5O z<4c3y^(mOG-Vj&Na@LH%EKZqUI4zF^BD|t^GnEuO@5MnwWv_Ri-&Zwx?nBrD?b0a? zN`iDcyhwoLDA+#(N*pm zKfk?b@GU5zkpGnOh+7j}H?w8O+~pw2zV%PE-o8Fk8sCp13`VhK2rX}8SEmOhDq|iu zxMAOs3UoJHQa2Jx9w2^~Q33qACe-g#VBwhj0^S z;nwJq;9p}I6~io7^VQNNdZ)qFJ!Junb%5bw6L$|@jsOYk@N6d1;Ogk0iB^;|6Zkb5 z1Su+{U71>M`URSi3j>L+MB;Oty0T)=PI5F0NPw_@45307!<0*TV^_zMS${BYtnBRP z(o)8A^p6@OKc4bi9QVSzxBampk^mCe*GsGkP-Z{_?&lGV5y$X@BYrgJ)=)^a(xXL2 zjZvUudz7f_v_H!!O>|nzYAL<1Z^1OonEn@z7-G@!A42 znH@Fbr&s~il>l=IzA;KvRgmuB0gQ0C2a#dXTS))_sRGVei?-+?Fi~0dSdV7fAA}Du zIkCV>tO6t1>(@W34Vf9q*QHkah>9n9GztCqbX1)IZ#q^~4%l@nm_%a1=L(%Tzc<`Z zX)yeC&5%E@!~AtFdPAKUF^F1iQd?=>fU=QVB@52e43ql4wK~C!eo%cdjah1sY~uJx zf?l}W4Y7Y8NDZnqTbI_}pF-F|{JLwwKD3eg` zks>q_vu zNtzFI-a8;n6iELc4-$R@^8Nx^{aa}Cf2hzX=kKB6|K5NC=bw?|--C!B_3VD{g%4&zH=|?AQT99nNpNUFu{l@toGB@q}ZW%JQyg^nmra6ZhUsT6jIqs5Mea=N?$eF z&i9cMe;ytY^XT0=QEt-O*|NN*Z2wayPA+2nexqK^PPd+KauR9lk;qsgmWj^LV*=q8j`{;g8Sh^Z#0>HLqRx-UZC{x1Y_{ZIc_&04A50k9aKFRs5n`iWlpaOed=$+> z@Z5&5ZCAvbknm1ijeO!2jWGy$5$;U+0{EhxTSS&KfSYLpRmyYM7IaOCbK!LdNw?9f zGpgARKhgNf8Ro3}S2`dlM=kdVPh%epwQ(FW}h& z4EQ~Z*i55u2VcUZ-7odYv?YilxU*r51hb#~e{Anwi$B;dBKquxbu%GGr*3wV*X2rs zRHc6DAb1$47rN+^0x#c5SxbGUr3dqe1f$U#2)i+2QZ|@*EwnigRV!ya$QNsQ2xi>5}{AToc4c)XUCw1oWNTp-&w_+_86oM9x8mD?usNbBiDO(&Gl$9GA3@ zH`n#x>Kdq5i4$GX!)0vlrK{E747VeBPs4gK#b8Zj1ITiVby8n_hR_YAT}kO2+~d$+ zV-m{?E1-|+$<2GLZkV3n*y@qm$;~v*DS+nn!HgoZfS?1(e6&{>iiuveL4o{I{!Z6! z4^m9E3Qt5Y^F0@G)Q}!~tWQjsdSAxpSUjvJ6q7HKT0OuAYa3~|N?MQr^J%^;+?WIK zVwsklK-I z#rdaxkl5K#6<+$5$@RpskK(WArMtnb8P-evV)oW6VY?JtGXkPCKq14~;8LNk+h|69RJC!YhX0WtD;!^|B=QCwK-m>zM02+<_T{D<5bhPT=S>{|mUF z209WY%k6K8%ytqg5ZiK$oxwXGK8iDb^Bl()SH#+)5~E88mJt38KC4_os0<^vj>Eu6 zbVUtd1)iggyr6HF6Q5cTTdUP6<;)&f%h9eny2RFc`CU^Fn))1oks-4krKUd9Mx#p` zfLS`CKJ5Tys{%z)O136uvy-$|t^I7)1)d>&$SdD~GS!HlgCpk9_wxjFWH;wcf(!1pu|I0s-*tnCHdJgTnI3FaXCYl|2u1vijZsi`dC8Mc`~4UVhhjY2&-G%WLc*@Fh|_Hko(vW zq6;fs;toMzTb;ZSVrFQwSd;AXxW}W(`~-G1EgEP~spZ8^simHP2qwp(iv<9L!9%eB zyWxJcaUY-<(`clii%I=zT%=OWiZ~aF>9&`JDd8;Cx=O%|JNOrTMM;AxsZxxuis`h{ z`9cT5#fMT|gLKYCT|-f|-bJ$`WND>cj)NodJ8{B!%@pxKLVG9xCXHAT-wB>?kY3wl%5y|gP95GDhd9{%MDju7r zYaZXaVmb)wrq2uvXOTV9)KhyV`~}&kw>rtqjQF^U{LRR*+;_bc7Baml1{S9&MMu}5 z_REFHv56wh30J`ld7zqn3?;ysh>im~Q&53pLr%VaVkR<_>tjnl?1Hv&9aire%)r8N z0Oz?PxkADwh6B$m&`n)Bln+p4;xXp#`VpAZ0aP5QTM}^nBX(HioBbMi!@wR~Hn?6~ zWj{GFzcvBzF1}g~NSFO+j(PL2ooxm-%C?$*8!!g-E({DoJLo?ANZ&Dc$*HH#GhrNm zyAnC4*L*9oW2z8TQlx6DaT12`{59^{pvm2UDwhS76=Xdo4D@I3=)kY?PpEMxcDPT_ zaI^hPl5R6FFH?bSX=%|gL2Mi)04@@g;t|LdwSh5_MI-Dcs`t=4HHKW;6jHZ{~#q}!WQ1+j4s2Z57SVD*0`B5R)YfS&hG`))BCU{m;qrk{>&BB?pEJZ}7hH%Iz zFG(95DYBIulTsaR+YhUd;TfV!_h;QmMKZRSGYRpQ&#rJLl$oO#j>Ug~zWT1eP<*DC z#&B6N;&{f|lb8`uU1r>fhJ6s6{~FN+RN<@A*6|?Xg*Jp*yVe?}EuIV-m{}~0u0bnF zD~@yyhl^qnqmU)mhV3EON|`E33Isx$Lk5OdP8q%hD|Q3xR#E=d!@>|*_A9G53WY5_ zOpwDoF$c4wGm7*Bh?v5Wh)(oW8{Kyp7+Kg|rk`&@(45kjuPlW)@2)?iOKz%aqbOKh zgMYX+Q;hn9b?x$jn1-Nr-znTG094>2!WEh@wcMe-xNEs(dds-+8xfV^>>ScBYb$-> zoWl!)iDv?lIsybLk6zq+)g6A-Sm-gx09(C5e>sEmpzV%fDbh>QK^*lI2tv3`H)IAi z)mM=nD7%yU62>?NvpNUPblAoY{hg!*qARx!?3EAhm2% zSHu#hecDP zfHEjXVvI`AC%OYwBd1FW)zZ-uUfF`P#Y3>ac4 zm9{I@y+OFO7XQ?>->}w$Dy>0~F^^V>4Tqv63ZXp9;ct<}L~qWr`e@wxz?7kno5xM$ zujwBw`_-qE7`weviK-hN8D{H{V)Z9v^ftzF~zcNmm!$?jLQVGl)7Fj(_Khg?Ar!7LTR*KolJ5d^;|SOoV;10kh0QX zI8tS$sQ$H+fi+$S6*=2wo*Op6N}@TiOf}ffHGSItfh>;*X8Ib~fm~D>hTOAaoESE^V^>!d5T-M~TP9Uw7r!7Zz13?fI~=rf`yEmi$%X zNQ+vw#~nkkwQ9$>mey45uMTTIkp|J8qiuigb5N!-lcN*C%BZ-)?ZpWn{_sBcwWLWgsh2+S^<;!VuDU$0P(;* z|LT#u7F?gvVFS91?PEWE4<0w4;sOK#soNi`Zsu>;mP^k`I>4Ce5N#Yr!oq1w z6}?6n@BT&mvp@^f$z|Yr6~Dw!ZT7o3VJ+C>62wf?&`NA5sEqWdPZ?Y~Qd(`jxLGsZ zMNBa|s%-+KoYv#DtlOd2A?WtX4g3*G=@moZYu7_; zP-$giz~pi1AGszGL@3Q^viJ_|>*8g7lD( zP>D^HUE8Ls#aL%2z$Mxu;jKK%*<7jQxHsL=h@WHSFzSd*Xpas;Y8pMYe~qp~UGF8} zE;eX;l>)4G*o@EG(z=~W^VKt|Nfrb5nj|PCUX#PgJS{=EO8mp7(AUED3qE>8*T8#WEzg0)hAL{# z+@X4A;0j(3O`+VwSUZd1e|tMYawB2M%KMT2j7R14oF##AgOsTuI~Zzr;TXwMq`%ef zbW;ix{D~QEt1#H#ziH-1Qh4GLR{%}0Xzsz+%caEb51~6ohTqcc{*B#b^5aK;)GXcl zABx>EGW=f2<-Zra`;*K4Lx_)&;qSCnES#-PD3nY-YV(*72-z8%5Ga1=cbd3S63`2{ zIGfu!QYaeOIGK15h*;A~nHzYT*nX@Lax^h;{#f<)4j=|$i{U)8X`!#o*(|MdP!7nL)#RC2bVmnC3jVE9up ziH}M!3?Bs<{wW~J#>v6-+d=;pHa*pmsU~f9SOXvOZ5cJbO~>zt?_$u`Vk;0&2Ezi> zz|f5>K)1-7<~FziS$yX`sEPReyk-&qIm|evU;29Oj0!EHn0LrD!E`uf&oJ`am;Qd8 zv2BNbKFF4lemckn`&$2){s?AoIvwpzr^juF$;G{G;Ou%Z>x)d#b_m^hoq)a1zL1PN zR4Ir2F&lA2GlfzkU<=;Ec;0J{!<^gU8)$$+NNJ!rS^3KPKe@|6<-~Xa?{R&D z`v&6Tw__Var3bb+@ddWJrR2*mA5>;~p3#wq=63YNHw6FI|0`&C~{Q$;SAf>%8pq)dmQNT(T1{w_15(w7bKmfI50JMf3F_YK}?_1Ll>%*90&$x-s^f@H9>f@aq8Z-jy|Dw2RU1vm_H04 zuAV1nV7(t<8w7yKBu$98$+^E1h!Aw^XQGnnYF>p#`KZZgQ2PnX5dPw!9zlMO--~O?~oXY*UgnHWNu&0fK z4-wymscC2~z^0;ZouT7YHj@W^Yy6~0254yLpbVm)3>jEJLaA;YG;`iL{_1tN5N-QqS7Jn#WfF*rCd zeogFry~mAWMCenA+y(yuSKFvaSO2sbP4_jY`Y{shSK!MX%>auOywr$&8t88|$%eHOXwrzLWwsmXox9_|AoqNt3=Zu^qGFD{F zjJYE7M@D>eesgwENT3C;4Ne%hsy0+{d^Ry{vDYMson>+r>DT~ru$EBmnq7>MgDfGy z9{vwS6lPQ=A7hePe>*7@)8%Y=@7A1R-R4hRj{c+XR4F$Qdf}V#jV4l})eyrVYpiC9 zuSj69h4eSoEec#7(#Q@G21&X*$99IM+cHNG#C~iT0|pqLQMJ+D0XYUJBI`7*pi(^o z0CS8eduRn;&@K^esh%8gf*d_?gmFICzMtb5I~ePKg&+eE6K_TnYox`&s;7}iuOoV* zkQkN(BqVwy!mwlh)PntrfZA8TZw|7M>RCA!I@Aq%{uzqEi_tVojn#u`b-b_ZlIH04 z%1s_I4NV$@G|6kfE!7h28Ihg!(>|6k%ZbG}z(oo{I>r>`*Ei_}khLTnNv#N-@4J*c zkgE$NA{LbCPh&0t{5_0f&mTU?66v_o1(OkW3`PZ@17cL1zz=;j#H4j(LC&vQ$XSmhI&G-%>8}!hUURR5XIZYLUUV55VJmptmtwi%{a5; zb&!CHsC=gx)qShQ8T2*$f_N&~7~bw0)?zTqSN;Fc?K6`G8a5P>4vR`4rcTmyj2HNi z#uqz_4^6}!iE>#OA=ELmn~0&TQfIN@V*2ya^0`aNhez+GYR1$v#A?J7SVX#@tiEL> z;p8)H8Q}iJpWxB>QRX#T!cPyy(j&uU6TJ7jLS92l5QW(kt)O_O6BV>l*+ zucJxCfyBDAT!ZwuX{aQr@PAAXa8A%+Q7D#)Q8u_0v6Pujz7H-%au$^qqxoesz{Rw7 zWj;^(?xy8H*Qt+04mi$o$x0pnt)&T|d1}~~(Z4buP3`KLw_x^IkRI(Kil@0ne*9jZ z-vqy?NzM^TSFj5Wn>54_$8&1&$bdV~s4?G##(f>?oEp3~)Etn)Gt2B_yrUn^-20Af z!ANJ3zF!%SPT@$)?3g)c-B{&b^3=mbP-tM+3%-)+D>VRFzr|6BX-N@vV`vz4hB`Ck zYAF2*eE>Y#QbFRsu`{}A&nV-0`x65JVoB*qmN1xM;|V#S4&5qFg?r-Yx&iD-=br`&7~XF0A9kgwCfrjU+%05MYg&!orVb*bXJ84+ zC&_?nLOJ|)|I*%&81;9VvApnNS`b?eH)TW6}rDLw!`ncRtMKo^p{J4JI zS?p*3dOx_?dAJMS>hk!!Yah-2>PY^`la^zr2_k>3i_w|`lt5#rveA03I=3;%PlN(NX>sH zcFZCF5ak^sa73H`OT57m{~>=u{DAqBV`TLqD9H)&rC3&{^)D-p+p+g-Jxob@PH1QFgYX&Ok3yy;h zbI8%%$m+`;j}Y8)u7UuausQV+kHe+Ih-LbiU$XL;d7yn&5NG;3Eq58B!}qQvYE5JY zb+Y}&6UQ%WWzB6zDb6{Ssh5eB%aWiG#xR_Dv8lxImP-$ux~5VZGp@o6wT8Mw9*6fR zix8ar5k}nol)I7EVZ!g5|EN+P8~h9u z@sjZGJKL_?%}jaSWA)YnHE{n#lvxN{v6&-xgO|#~PiyT(ZuC-4I|wqh-b1VUQ$m1B z6Y!0_ZHv9FL^-<-M|oVP@dCwnw(1fp^XAhr6OG36koOYyCA7rG++o$N60!!By-Cd% z7n@mn;zhUiu_Ik=R_tWM#T&fE3R)^^!}U(4-8eGfOBOYE)&PQCo+g|rU+;THXH+-i}?(Wc>+_`Ye#zJ_5g)p@w>7H(`H;wpZ2 z*VhrEp_P+DWTrk2wwuBhl1_Vk^Y7iD33HL9I8OPo3U5RH-gm`=TKalUwo6=HX){Nw zZR1kb2dGwBXB}`Q{PdZqD#{1O+h8SeSfHDTQLmfp;M)h(FD?~drg?LBRTosBp|Obs z>ons%p4PIQqk`s}eQ{nDdse5pc5^Ij%c@1EIWB^l+Q_n8TEO6&SOXnoZ6qTrrsoi1qg>vmRcjs_i2`{Sz?(YF$>%W(n&q4qr0S<(pWm_;sQMp6X{ZXkZq$Nyn1jn z0T4sjaXFuyZ<=nYSnGCc+KVjJ6)zC{_57&itUQArYmTr^71lrGA+jXvAD+R1##KFD zKzm)&y#|&u)rCtf6|T+gw@S&RD5A@i+A>&x&$02sRV=hMGewY`h`rcM8G0tV`C<}`o|H? z&6VMYN|mEryXTqm+nY~G;NlL%ODq<6KE#LQBhUBCgWuqQ~-=CIN7`v^R+7}}&C?!9PPM`Zx z+UD3hcK(#@K)$JTF;d|uu*PKrtM*ets6-~12rM$}fB#?uSAq3}FKJN}^{;6wO`^28 zo*UIFzFU?<=WDM(Sim$YhAzyg>UM4eUpa_cUMm8R4z&GKDNhPp16zV+fUUsV!cn z2h`nVuu&7}f`_%y6EQs*-T!Z2nLbAbgf=egT%h>vQKbjxy|USF!_~>~oZdk51`DLjynE;%8N+0;_VM^)*hE zdS^Rswfh!W66a9OQ+|=F-9dW`6D&x}l3PabGhBryzS>S3@_1&V6olGGVA?_)m&o;ZJDbBfATO|W@eyyaN8+`ryd?qAi~$e6c>R-$;A7VBQ`C|2@L(G z%T`hruK`sVDocm0Ch}PLBUbOaVPnCu(3#*oIVIA?7R_rxP`IFsvaIltF zq?^I7XC&Phz5hA9=_R<-)-~Qb89J=hzFf|PZt?;4Xiwviapyr)_n5BF0rr%IhViiG zfwJ+;IUFd~~1nr@@Mvl6LwLmoDftMUgv;^S7EJ@JWJVV%=QqAf?cWn^$!eBnjK zuM;k6bB=PuNNZD4f62X|daR`tZ0xA*+9-9EH`QXvW_{tkdR+j7%rYE z{@Gvi!h&Zh{epvf-C51yykdyeB_r{ItK9CNRi<)8wk7YXwQ8=?xG2Da7JaHyQ&H6D z*+}`}GHYy^V7>dW1$g7Af9mexIAx}IMP>Ki!NS7d-yBnD@?9ov@fIvzn?CHQm!;zq z2&=+GtyP*fo)RoXmO_<+EBdKqZOZ{IUTQAm#@DJj5hGem!IG(qV)`=cTi4hAtbO(( zY|mZ$da2ISFsoOoH;M;c^p1G3kzMqy+GdKm!s5rX!%)LZ_4$@k8%(*e3#Y5jRPfx7arzsjXpn{9%sWogGlvG(v;-W@}?lh!$)6}viJMcW|xhET`KJy z8$nb_u|!ar8j&sPi+EmZU$QrJzWBv`YCxFV*}!2%Ou&tRGI@oO28sj z-Z~TbckP*!SVzTTPxVq+l{!-tx_1!e#mdN5DU>!S>*c1$!dvgqa>68ondAFtHN|ftYz5!U=$oTa}|JK_e(+02B%U3Kal_ z27p2bKw$u&Fac0l04QviZ<*4UG3=N7&qUU+|63-(;wi0K{x5k(V>{h`k1xLRetWt3 zxU(0~x)aG~o)c>xW^EI$o0b2Jun9MCYMMI+K#Q5uM%rjtIX%v4929$SV{9DsX&jg) zX4{Y$R@~0ZLq~c8Y%*>gAI_W)juNt?{zgoZVd^HEIgyw;4GkSnrHYBWh?$B;c=wq( z6_`5h_8u?yh>3fP1wV?R7h&Q;V&U>&f55FDJC)Dwl-eXr+^Ij5X;ZDXjh5SpS6tLS zQ2(l2w(wFro3|$W?Ov?+z^MyY*uIijf4W?LyC^K`gx~0Z@!_uOE%_8V=enh~Wvlf; zH3BG{mnX=$e7n47hHhTidRPbdn%{}=b}o3HyHXuD-#REe>fy*ta&Zm~+MT1uulDS? zy(zLOD!n>J&DOg)`A1PIy)}L^ccyyMsNeAzzU+MyCSL7!|Bqq;^Vz}Z!hMl3Z!Y|w zhh*WrH~NKmELVSy(^_;MzDl^<))_`wMtz%JqxVL$oDuFNMdxF^8Graci>!6Rwk5%P zW%SwVQIT#kWG`8QS3I42OQzYUojm>c&|NVU_PiFv!N`SWnAg69;-V_`eD!kmV)bhE zS<4yA1Ai-ac=>FH^AiJokIpx!t`#o^*bG?7Up~bPDFaKV-|} zd%r%OoD^G%Y}0ptK0iOpP-f$Qz8<#2%eQxb-4lOqeSD0rvtMO-uY9k9H`cH4U8k!a zwrlbEI$nOi8SGztFNA~T&!YDAKOgU^F|d<`O~S)zg2&K~>N1ZHnm{VvUG(CuhLHm= zXtG*xdE%Rt)ovdoVokUO{D%+dSV66WiLZP9VjaIX-F&zcJZizL!yWv^sRK`Ku!fif zsbOWY>#bdwJZO$2*|F8(4ePb`KB`^MjnBB8N{RCw0goaA=S zt;+<@c8Hsc%dWk5%EM$I_PbJakL=A$D))AMQ|7C-RjOMTQd%F9EO>*P9uz2>Jb9)YYI_`!IvD3ym$->*% z*FhIu=00;rheh#e7P2#W$k&DZg{%63>z4T6O504=WgDhWzwOV6wnWKv-1+wTQ#(gl z`s&lcYIP4sRW8ve@1KqBYw6{Gb@nDz4^)OPEY<3C`zKY;RDYeA0=0j)H{v;yw^4Yy zF1jy79^W3e3AE6ySB9IPAuYEFIk_&f=pyD@;+`q>o~g#>j-Q?8HMdz+&>faZSDf}0 z&oY>S3474o8HoJ>m>K`aqcAi5JEZ)d zjv{RCU~Fab@BfCG`QIZ=|GP@xj^h8vN8!J#^i4wZKUQM>C;8L=Xy#bi{>L-N_D#X` ze@y%TqP9%`!fY9t|9=o$M&|zj*viiIey31}5t;QM;4J~Q?2H4fjj&pMS^pyBkC`79 zYEh6jPDGH*^?`O2HRO+*8V} z4>{{5!jxwZbJ}f)IqT;A!JRhkJ|{h^!E8P^&n-0hU$WY5;tfQdtYr6}zV2-QWO(}D z)A?T`(9DegAtJ{lZrsM70YUWX6N-LbG~jnpEP@hV*+3rr_eXvYo|5#S#*pUqQzI{l zwURUe?%`%Cw6If2bYonSm@Qx*R%1xm)h-7&qkza>EO6f?*X~HTTylS6l1CH6 zVllda#{B*=ES{BF_hMkB7bH|K5{P&)w2BWta__L>fgL7?#-yOEN=6_Ja?!#n-aZKp64JT_Zo2|}y2cKFg8uNBVC*dkIeXO&* zEVGy8vpII8hBD0$?P9I9UllgjgU#0HbdJ!lnd4j$eu5ej{Jb5|IY= za|sbz>+e6~`Cx+XMOnh-zK#?-qZN?xme&{wh(@EqtlvOx$O`ZmAv>3W3cCrZnZ z`ZY?(s7^e4m@6-vf~ozi9*;W-^whoXlaoWg<@vf-{g{z%Nj(4SSH9D4;G7}GjpSJk zY_Gz}%gfmOgc7P}g#UZtAT)a32t^*nnROP8c*FyOUjHU~K=l%$w_ zR5!7mB{%#t5qFI$Wu-w~AO!8M3zbH-cvybHrr3jL9sYT6*(2GoP6oGwI@C%+{EAN*&n z25h9m{D@}7v>XADF^Knz76<8T>xsc&K60RC!w9Jv2LrGn86aBVD%0s1@JE?z?*;!k ztcY&C1Q6-|d!+k%v39#$PS5SeIndfKJIr{f#c`t2$2fS{_ z-H-;>Xdk+o&QBvggDVZIQ;0QdJPpv2J zWZYZ6&O7a*R2h;FH)-MQ@!e+^mHO9P*%@K7FzV};x~}wmg8uxE0}@s${TX3JCocl}(}6ASQR&=0u{A1u z(G7?k=|5y^W%~p^NlkUpX5|Y)AOR6h5?>w_mAAt=!iwD-GWx=+Mrj1=1W(`}5?SJ8 z{?SY`$&z}Y5rjuyjUPGF{Eobz;5XhCv{H-1Yqy zeEW2LMSuJ7k$U>t64(?>&NlVgc;s)8``1kR{48ML64KZ25rKnbkC7=>jFF~lam&cH z!4jP}N3ATvz5jVp1h*!SUzf|1-EDMTrf89tmY-BMc{C!knDL(h&$NyvyxOR{mj zha9~hcX;YH>k*jFIq&5Z4mv!9#pWaGHrOwj{bn>?i>oa(IoPAaum3r&_S%iuAw zSZp{QxAdH^E~)f4|Ab5OzJjrNsV;KcOolo~9__tC|LBy^gT48pR$KEJTsxp0Phz8Q zb-2k3gg%^3=Pou^)15q6oG&BORXeA}ayl+@Y*e@6ec;?WyidaOlj$*=u4WqFa^B>bWcqlupTK?617AIXNy1Dqr zFX#^HloeI<0Da8NqLZ#jkK!b7KchSLK8U2VGj5^?P08?)pmCzmVpv+&;*V^$4#;Pd zQnLJFo0E6mlgNd=7KL%8qr@_-a`o|s+w#Mg@l)wpUG0Y76_!@UvX z^(+pFX0>>_0Yj(#W9SN+$l>(3@B!W`E7c4I-Z1r2>MYGxE*dgv|R8TtM#nF7MVx=6PRHAl=U<1K}LCjwU2N7MIi%sccJa861k)5t8eJS^PPm)o~T zGu;g&=4386vo*9GgjPEgv7|rc%hH7&P?dF2+0tf|ZBMgmWv-Ou_mk{FuA z>h2hZFXHr|iz_Zr(NeL($xL^KbGRSQ|IC6`FG#0oRjr=VLl*Km0uK3CC=ht4wNCce z0fwxNidoQi`Sc_DIa!1Bf`+S;)MZ6)ABWJV+ZMUN!@-a|S!EVXi?vv?{ZkWt@&s-L z0y)!FzYXhZg@8WMsh~{h5gLMj;|sW%KIS-pa&>qAElhel`+)T|IHO6naC~{XBY%ni zJgj@g8-@gh|7)~7X8X{qcDGV;E=Z@i_cU!oObOlZc3~}JdON(iJ5To8lg>J6IPs*+ zyiaFMES9vs%$tToj?|j2$whS8-Vw#1XSO@vmD$^H!isDB^W%(? zL<%%S0-AJcOW552$=kcNc&P6KRe8rZZ%)U6JzKppEC|cdoLP7ISJ9CN#98ST;wM>hve1r!`>0%t8Pf)dY z0Rw`0u9wY`m!q|IbPZk~Yk#5Z@7(JTe%OnTV&Om`r-XNzFgJp^SjSG!Q_m3`o)2&0 zk+MBgbl!0IK5)1UnO8=_Dj*(BK8vA4k>v0rZXS#5Pd#qC17A z^dRKT#HK_oX_YR$Ew~RLuwf20m8bnEo9cZST_o5QGcn^!{P{@$`&nuxZM1rxhInHo zCmb&gsxJHNG4d)Tj=46wfE9V}-3`qLidsKL7;(9g*2}Ah@Xq3)11q}=U>DapPZLsy zD=F6&zX#Aen!FP9)jMhT*{zt>i_9IPKMl3H!_y!hML(4jf2>?Pv)!mVMlL>tSeD7s zsy`Ik)DS}uN1$`!b3s9hnLvJ1C;I$dGm_WNWIS6(`Y;V8C$XCISfoePGKmgR`~t3D z$iGSWp%OM)PdWeK2nC1}QW($xXZHTtd%=#tSz% zc}iHRNLU@$DBNtvAzbiNi0sunXoIn8J4U0BOOK}fVnVSM8+%qp8OPu>4^9xlijfYo z3sn!hIzF1(@YaY6mDKr;&dKN{MzU-kIzUVR9h2Sbkst5V*Dl1`QBF)o=jw3-`U!Ud zj0Z0Dj>}_v*L(u*EwzGUj%dey&2SBR4Kvdtzw;YU`knaUPgy~KI01J+Lc=++EM%l;SoKUIZ;#wPp#xrbmD?h_dd|cgu`TGjU~(jvtI*%gyk2DA z4dkl6!bsase4EwX%sz8MMQFpf<%Wg-63IjL@Zhe5KpTE*d=3P(_VqJNhMCo{_DRAe zuE564X{~gPvxkZYu_dh$Ez=q`cuVJ+C77jClS&9pV42ymj!g7l;_k|tXN*g2>5WY_ zYV4MiMwK5L2UYEj3IorJjiI10n?^LWH@5(KQI;?^?sIm z3HO;}`aND?XrPS2<0ZC(t{+ig;SDn36gK~1Li-LUJf`5{41+U@Mf;wlXrO5ze*ut! zH=!F@cdT$02o2B-$PVD|H)Lj!6J)=Eq+%COmoe8#PUA*WAQgXb9$lVn`u?$&(+Y2$ zbdC%ne9|DE$q$n!J1+hv3l-lq44UDrYr0b3)26Uwm%^KSi$sWh3#TZ3ty|<6puN5U zkvS-NPB#{_^V^D~RTIq&xbgU9XP*I*D+1?Taptd!IBVKqQ#%AFd=18|MfRQau=!Co z4lvxK@UY+!{=%P$wajku(fqdzdQLX3>`BNJ1&0w_0|E$@?bO*zwEQ$)isriKz0EoSx8oS;;0 zLgxY$+&#JQr(*(=#g5Mn<}bi()7&F1gBr#ezi4yk9qi=kNjQtD}Nqa^j$=sMwY<5ul?j-CZ}=>TmPx)c^&d#Y<9+v9$bRDM_(hTXch zbPVm&bx@VP$RV%rSJJDhNU3sGwX7+!>Lcn@OOba?bxo|%M3vRLakFUdadvaoq$)`1 zJE`iJ;R||1W36k2VdB=TX5q0_3WlZ5r>n|Q_^G{7g(st07lvhqYX%?MNtK@FJRlEg zYh?}LVtW?QV$FMbLps`Y$k+xSJtfn*=?$cwoQZOAV~(aC3IGc$JKsGwQVgslEi)Q0 zP24G4I1@KrgRjZlp~4Q^(;?m0?8-9%D@>ZChy7>(QfTlDWN@Q+Xz;|(sgk*4OK74Y zFPj0EtDFhDzFh~f4iI-G#D=(Bsa%IRmDNfrFzeu0oJJ)(45`v2h~mqZoI!oR9oN6Rmd91@UVLc zV?b{jyk&rG6p2s5i@*0{NGe`78h@~Hc67I}3^Oi3RDwg@yW|07!TvtbS=7gFhT1cc zrd`ESZ!>2Pm5OH-PR~ykj|dNd3W4VngDGWQl&KW2LAHbaT&Cf@DNex-@Chxz`y?9T zUFu&N(&6Ug&uO$Saz7R%chyv-o@OlC;+OUcGNR%lS-_(GZn-tJYT_M^`7#opX6;Ax zaZ8Z0^Ta0Ycs`fDh%M%yG7Y^)5nm2K^fJh zJ{u$Yde;{XzWA?%ZsH9}exk3bl|P!(IfG8vyi(cqZfZSb_0%goO=*>;{6_05_K!qw zG3a+xT`}3Qhmz8T`eUSSMPszLSSFbc;XD18X_v)qKIti7SxvJ4QAQefoMI)gM`hYbRmu6o=KV2ic>Fwj+(HEkW((AbdO9#fuYyGHPl1{%lPuxtJKR|XsaG6qVqF7JN z9LpbzAJg<7bVH_tnfAH+CM-JIJTJP0-Oz4ZZ|C;6RYqDuFh$thfy(s)$pX{3qP=e6 zb&At8Xm?_Zi8M^^V`5(Y(UhPIBE30r3E*FL_KCptOZ@Da5-_E9J?4Tx?Eej!9bgJ> zyDM$q-0tssoBX04rx=!GR7hf;!y;D}pH^@%CuJU9AUQ2@?xBs6%2u97g0NE{j+;@TtI*PeyGK1yZLP8-=N^(0^KsW#5+$aaV{WWk@L%dfA)Zt_mE zJ<7hBofA>(Et>nm$Fh)4sJIEHM1fyO_kwWv_`|(x$iQ&^pW3{@Fuuj}P zN-uHKNbH5rU6!pLWoZyUiE0O|J7JiDHGy(NI9gJA)wv-#88Nk+Z3BwGE8;>(r^KlB z9GWr&eRq6Y^kuHSAqef3UkUy32D~kNGU;gUB4Jo<9^xLsWf15lO_2fbF8N7dJ*p5uXM(3YmPDz87MjYlFB;o|f^L1YW_wkvzL z`rGM+0yf0z?cR7JHNE{1>0+Z4^qx#QXf^0qjk|1T_qY26$(iRnwu#b2f{%~H0DxMuQUnf^lVVQNg^wOE*M$yShshm+ezJtXz z8fL=m1n+QFuuxQdz4@C#DC`BPq@$NX0y{1JO7K-_2%ljPUO=I?`s7Z4X)C230II*&!Wr*MR{xq1B$Jh~ zFRLdoR~4Zi|2sUt5MDpFYhp@$rg8LdcHPhvzN9-%YW3)K`CnVTz3u5z`KP1d`0R!| zkP~_G+ZSRulDij5+20+M!Kz1#QQHT<=QBNx96~eUCGk)8Z9MoBm0euK9u4YK&qqhS z;uiLQsqz|3$+4u@Z-7`?t^A2lRBG_j#b1ti4LA(Ig;bLBWA`QR*fdq9l7_uL(Gy=w zi7>IiUnL{pwz)94yjZyHF#|6hJaG$rYoFrv#Z<4N=Qmu8)oC6G*6LbO(%85w-rlZp&@|1 z2%uj53rtz;6&qC@S1(FO4X@q=NgRAopG;P$Nult5d=uKHsqQNZQWl|j6bjRx1 zo`EZ9WyAc8y&5q*9Bv8c&A} zfK8g4P%9!7dqjElG{OWj_AN4()f2Auw*=-|%VPwsvD1O8^I8JV6Qh)Xom9y|Pax>? zjc{-XhS`Ude32LT>QWt7Qj#X>VV6`K)t)`2* z#`M`ba6YuG+^eMqo3@L85ke;wrz!b;`ToSiJ?!rBh)Vq3X}?HGtGFq%TtlcBk!KaM zGzefhMU3bxPoaBub6x&Q4cg)nMX8gP~EX-Ne9hIU8#xdf>LWvIA!5eyHi_o!dhTSCr02|pISWD2a{K1 zV!*?Ira|cW!pnjh4}IRhY=X_BC?47|rAGaSG}DP7=R5*qyREi|g6g-u`xcSs7iQ?Y!lD zNTVl#=E>xP>-kQLbFbe&JV7JorV-rgddM5mI;^$OpCa6J%o@gCuZ2{+EA#^16>7^o z+8v{bf{8*WN;%QD%2;osTdzTGw3}??yIm@m4jYB}(|bR1eq%AW-7?~`swRnDmcuxD z_+&HLMzMn<7`u$uOW^^Ps8?qzC zz$IIs17^opvn#Q!O2%YbT8u#o02P~xPR^9?Mr_q;n+YiqrdCloKtL?0WYTURhpe9n zkb<}kksHrRD(bwpYp3WC#(YSct1x9hh6r~^iFBgeY6N|^2~2x)qk}(#jfy?&F~oTg z8703x&x9nJdfbky8~z6oce2>`Wu;vEtz8VcT`PiIY#!n+3cE6b4MznI8xAX*k@t)t zs`;5ll@Xe@-dJt0T51KuhvKvI{^{Iwo;EliUw#`CkUna_aVR10#4#ru4d{OyMd zSo!hZ8zpH^L)WA91jQ>!vDDi#=-KPSp56StAGN-rWo;wL87e^C#KS#eOOj|+(PJ69 zt{u>PP5KT_cL&me&$(K}<`ut0clc#h`k7k1$Mq#4fgHz>pWg#IXI_vA(o$L3R=)HCHgncY|+)$$n7bN z`iBe^-A~EXOHU4!X?ps382+XIVb^QTOdjX6|NPk9l4zpFZ?%-j`KUc>do$ z)Q)MEeAP+MxA)AJ-ECJE-2-z1a|O+Q7r2kC)%c{k_UD1ToZNE;yOl}2<%;Mq0Ytz zoYj1G#oneX56!|^#n_l4h69L0d`g^RLZe+OtlWwR<4B6}w&9hqvX3JDT%Z=}hxE76 zZ;oC>-+vDac#g|BhmI|()J89;?SVx`9I-NGEsw11E=|L+6E{yzGM>rfqhD;K*DHfm zN_=@dOSIf^WoxHmeE z3rS>?1^F_%%QDkBma;Ye$dPi*kS{U1xU9Qz@;=&!{rJ~Mce60`W|r318TxQ5Z6)<( z)_565o5S=`HocwS^Cgr(JHE}j(`Ekj4e9D&S!da8ev-Z1BRh4S&*PBSpV|BOTkK_b z7yTyuJR-2QTQgI9B6q+ME|K(gdCV$zd1cTwaNd0eNe~`c*^||YVM12@;7q^BLlzrv z#4HLL+Xwz!7bsuZ>E>6K<7L-$=Eal@z=3@HmocQI2KcUVMF>1%D3mXdat)LQu!()C zhIGTvu#%F*GSzVIC8ck7p4noFQp?IN3*9m`ef-_IqG&1~3tyQ{&ZAB4)DZ8ikN5bA z&TfyRDcz};kFKpPhp7Q{(CkdnTDJ&s**to?uAVo)eQ3PsgP|tv`bFr4_66}pbk$3k z?^H#|Xqp&x7>VLP$f+fItV+!33E2V=!dbw}u&8s$cj8y<0}Q0)02*lceH|qAop!S} zQSpwWw`i-yoU0F|CD-hyI-{KiR&vM=sEM>frtS?jqkjg(P-z$X1dzE91aA#*F==p) z3A26M@AYb8%Qp2xuLLzN8Fm8H?s6n0;f9~YwLn&BVELA#%?udpFoS%}ql@-f&%3lV zCy;IT6Ao$0FtS9gm{z6YS2YlC7+Yh~lMTSt_28%5PHe7O7x9V+XY=cj zqFGkL(_E030~Iy1rLfb|({<=z3&i~o1sq$L{`>&4xzvZgYQ1PZYJ<-2eGgWczM03T z`Wqbk_V`JPfe^Bseuy{JL8TB)a(otY04=z9W<8GXSGHV;hL%MTbm_N^4AWCt77GA1 zY0H2L9;Xqi3+t^bWs-%6Ffc|BXq!>V`6GE84TQ(|>mB3cjd27q!m^e^7TbxF)mzj- z*un8ScDs6!ebE&;52fMZl>=(bkQ&2Nl3mE1p#U3es8uIMhpX6jHn$49mEqdPxVhJF z84}!x>D!YC7VIz`J>7C5U3Tex2?L-EQ*V!AxHj|1II+2}A@4gI0ciy`h`TQM$|C6P{+;AdYvIx&pt1sgKoC4ww_($lPiX2E<&_*e!dMuQ=0~Es|nSFy< zg-KB!nPMiz!6ZdwW1gPD{TOz2O81Q_#g^&iG%_ky7YR5SWb1Xo3W;eHYrm%76#5i8 zeZ!P19U?t#yPA&rW6`r12?0j|yrs_1ZBsQQQ#!!{7BBz5YU$uNk)%Cm1Lzl|5hq(D z1b+w5%kc8}7fD~0b-z#LGG+W^1~M?J&#Q13WPHu7<`_dI{|je8n7?sdDc|T)J`ATY zYCcKNlLFbCHZeLsdU143l#QC+0k}0Ntm^I22su?WLhd=*3#+;silLLsm8JUD=MpC+ zOx$2a<$8iRVBIxNrIYUUwTqm9aA!wi7XX~B)l!|(v_zQqQeaFj9=+@MZ1x!MGT zObv~ic!98^Mcl8-3MOEVC0q$YCAx-=b=kwFENMS^=J(*qPc825zx=he|5Z3}&-I^d zd%u6|^qbCH`QZImuKopkj(%a|w8nq?+k59NDE#C18~T0(rh*@WN1xrg@A&)qUv%`| zd&lFC<6b%!^JRp41h@SSG&`a1*8q$EpvX#?R0uFNLcmI@whAFgx@9``Y7vYZ;wtGc z%yg`k^C6muR~LgdIJS%Fo&l32>WbEBhx^VnkC2#$WE+ULi#-I_wZ-OIK;|X-l7uT~#w{5(=U=@z_zPk;42A;p0LCiVW zYGT-GqD&>z2%j6Yn{Jg!q(%@L!|@HAIbwvBPVroA%F__r>LV#wFW<47-U#pC2=Cs= z0h;$Pe46#HPxfKqc&>u%B$p+h3{&h6(QgD)2-05^;sHQoU4L9le3s05-Dg#H*FH>N@Sl=x+nh1ud0H*^G*>v}FV+ zY0M#Zohq^GlyBEP?WtAINI*K-f{U4klA{|+j&3MHfreE^&cP^P0LCy4X!L?xx|?j@ z^ljhtZ9fTOuiB0P>V=EDs{Dhh{DZ3eq)OGQupg>B;anJoH;sDU=Nd{dGSr=}(5fl~@ z3ErxuuwZF?n!*BK0t;EA`3Aezs)U`Wp*J-g&2R zeiqEQ zVC|3RpSOPP@quZZra#Ia|Uw?yOnax?$rUz?MJ9u5|XF9V3R~i zEzwImKov7U^Cba0FswnTO&NN*K3zw;9%9-6ph2K!2KbqPxEd8C_$IBrzaig55x0pp zIM!KWL?+>{_kUB&GnCxKpu3c-r~Xoa+Tu4{b?CLea;cG}7s{PBK7K|dS2;H_t8x)q z8d(-!T)8@aRcd4WhSXh=N8?Y$KZ_i6jyS=SBKJnNMbP-V^LbdCny$|$epMN{2zb5C zWy5ZA+47TXN_4-xMEA>m-6t~0GD^vD%D|E0aZ1T?3K*9Q40X0P4I^B;3v;b+1=YS4 zRQpy?y(&<&fH?$p1*(JeKE`S6;-if;vIW-$Z&5(QTE<=Y}ZJolWd+Qxt} zPcG{@4ut1!?z?jJ-|zqBAK}XnUGjsSkN)tg`@lKo>Wj`;^Y(JpZeIe#w?6>pU4{QH zd|vpt(DkclP|MGH_TRk`(;wFFCm05-V>7gNc_w8*2geG$AVI#BMXi8mW!Q?{ZU&MZ z;eDc4b67#Rtk0yChAf4I#4e5%{UX3bw+zr6CN;DtUU}se)bYwI#~&fpzkz;+;W}`H zDlj_SK(2Ul5=R7ImZX`E7iFnrKUX5NzFZP`@@z2zRn@1xj~sQ?1|P;N%G2Iw5Bocz z>hA=DJ9*iUTO)pw)emVDKYW&DKSuTiN%wEV-y^ttcDDoFFlos6dABEgNs$5)0_~vO zM|q5+%aLZIxmdhFT3}v>HkmJS&+&WBL#85f9iSbynHMPA%`en1v@djtRauSIks?bR z%c`0#@`9jZS>#m#FbpZB8Z?3H2xFpY`S)fvp7UH_wU#D?_bZk zYwRUW-L9N7b$jZpIj-f+ncdAANEG%A93o|7mtj_t(B_+e5<5h`7mdoPOGS|ZSWiV2 zKzu*V6DQKtD%RFY&SCln#dyY;9PZcJ? zIbib>m*02;wwcY?d;ApZa)8Dwe{w5QSiy32q#nbvlPlXR7ges3)=K>MD9oh&KLgq4-(5 z1WRp%L=kwA+$m30j}H;GSt#t+7tvtxI z+K_LUFwZVl67sG6B++sv-Ja%V``n+w#4>*2{%tj}*G(8SK+6AsCjMyEA+b}k>ap{^ zYpG=c#!VJ8B1pRaI4p5UNEtGaF3H>Juo9%3+`Mt7GL7FGOW>Qxn_*__%c~YGzV_x> zozHG8+y+j%dfe$#Ctq`K;a#xoyO~qwjGuA)#=+## z)(zHO=uZ8vvaR4jxHa$xJ;;QZxEV6zEGez-tj$ta#i`Aj1~#BXFr}hIij~ZaaXOPB zqetT9QNPopey2yhPM6!G8Ak*dm+7X~E>cYqW2y7zxg$0j^0WhSUt+-vI|EU7phlil z?r?0mGLe%puCaz`s%)YcHFYn=aaj-xJC~}+?JKwbPZO{p%CnUfRk%;As^%s(Dr5+W z!f?nUG|G1GJE`#NkNOI4{p@jY%D(r&unEsJ@4M~M|DLz(;JOF?4B@E%Irc2L==UFi znL9pwdH9xF?=Sq%El(Cc-S8A)&3iG2&B1nW;C^vg({jqeDWYqg0W)PVVswZV4Xu^> zR#Z+JMgrxuC?HXjkYrjrw&@;fRFom&wNd@UH>xjvqe^{^ zQF)vBA(k|%@sulaV^Bg6d65%Yk>z7{+=jd&V+@cHAC80~!3g3LD4GVAju*CAmIe_y zkY*Z4g|8m}Tn+Mx9gO+)rsi2$G!l(i;Sj_&R-JD0iZeCXvhD@{d~D8-Ixbmt=IUEs zxwfzaWN&$B)Tz^cy7bI#h2L^}!pSqfS9o>*BZb1F=QeE{GwRe&A3pex`V<*Aa6eWY zGWJ(t`e=WJc`hZ2g1{h_beSxr6h;(?j)zREMVNt3cVtI{a$I92Smby=4_*W_>0bad zN%{s%Acj)Cyt zt8hYV$P?y$V{066GbqFpAJP)LX-j;i39PTSz*hAB@sHs4{xj)5N^Yo1+E3cZ5DAE~BLr%)$Xfh#*}Kuywb?4;k=$s(vGGmZ@8 zBB;tV(3E4D^X3i#)oEt_h}V)PZA#+u6mv1SImuutS`f>UtQ5BKNo-}JEM68XL%fu5-2EkMTs5L_Q!9Tv-38j-{W$=)W*$#}ujk@x_g$VIw;sS-est*H z;KW~o{ts8KdIEg+?Ki=y?!~=7X4sR0ctz8)yP9w@FJ>~2>aZ~VsIoQ!YkTNt0{8D9w(L_8>RG~j4$+5&C4 z#!l$48}ciP12>A?;HpzYtE)|P2vs&ek`e^Kt~Qc!ujaI+2r&S=+ad?4K~5a>wMiKv zjbD{``QScybl<*yp4-#^Fr0IAGVJP~hM(pc3`|$!Hb_{fZv@strhZxRs@Xk#P;@t8g=^NYY zv%mU%_BX%Jj^s=?h@*XWKl^4!30a>qZ}!ek9I53yR;+C8FNQ#*?nqsyV*JyE!4Z06 z+DPj^vyVhGxShuie2mT|5e$e?{4*EA#r!4kQvN#ax&SXx)azCV==FklH=8mfX^4tT zB`Pirs<@PqlIl9rO-%oe1hvm#>!F$Zp_%!G0!kG7CntZ?a^x z^M^UfO1B_~EU-J?rDiR%w+nBJ-kK%rBwg?AvLa;vpK_?F3{w!eeJ zK0%TtQRWcKI-DHhIJ|`si4S2=l4XX2EWoCP?N&xm0Swy<$LEYj4s7AJb9=cDIhH$J zB>R*`0XR71-!34b7q092MufuDq&WX-$oKi6bF`JD6B-8n(f<63K9YeV#L`OOr?r*5 zFt(7K0vS1k*ETJ*3!>R7wt}hK?Q^DXPYh-dNRa;axDGd$OEwO5sR7cohjLLY@TMMU z5l!7}k%%Ol*rdIAZygPThDD0=0jUDF@vvByC9jG^78~`SdLmeiWO>pBij@^BLRmH! z%91YGRgJ}Pwj?*vLCy?TtjsqsB#Dv0r{sAS;jW#9{jApy1K~L)A9BXQe9L5#LUGpqC@gI@R3~tETSs% zI}a6oI`pl5+#@H%7W~)676Sfr(r; z5bG5e&>;*y-GYXRy`VI;)@M@IbB5=phl+nk{yi2+cQ6rB+zC@Vb0Akzb& zqdgRWOoj{-@t*uk|H+Sc4;jHrDq9ox?loU~Z7&&A)ZnQIov|1bw&yg@{pS7dqNim@ovh*T$ip<`!zSfEOumFkTq8*2)-cczLogw-m#OR2 z7jRRmQ`9L2s$;9QVft(|m%U8;fxcc7708KMZHzu0PDPW1oH$MUjxOH;??ju0&Ei({ zh`?LW(Dg2EN>(%j8XOywDpx>8w61O~(xNjLqsz zW2v}nINs}x3u@?Cxgw9zgcKtf(q9_EbB=tq05rVBC;4@~0UV)6$BYGO?2ds-5V!+W zfw_&5*k~{xa8DN6A1}=2_8j~CmVa;i89IJ+GW+te(d>uE2qN5#Ij)?(&UQ=j9aGU(BeZOc`8wj&k1GR#@5~fcx`fzY5=2ZxA`}o3B^E+S5LtxXh+{=J z0+qNC$lt#UV-(MGzWc-#U24v?b?oWp;?&^sjshH|O?HQA67RTAu@(H{Ld9Yv#d0y@cDV0v(RG#8)E z&6Z}FGXwL3bE5O@_V}#C4beNH8A~B#r4%VV#Mca$C`s7&HGA5_88Ut8>5|C^0@g*+ zy@Ky>3uKr9=~RJulcZk-zpyKi4yT|j#5X_@p}FSV5UZ#u4CGAIcJoEn zkPi9UA;MklRz`dhcaK};(Az~?Nf1qs6c6Vt(oSOBi39u>w>7`THNVBRqLZTitBI!* z({Qm;UuCemu)^&UuRHua91V6!Dr1MP(gc*QlLVCg^$Dn^XbR$fNv8u?E+#W!-L2D> z-rDiE!i$CLz?Dzkn?GaJ+QRkR9^G2lv+T)2fB!EL*tllikHZ?tInN%riTfM2pDM#pE);LeCn$>h12m9sM!b629( z+(xvKyPdfUJ;*$U{=obp@)7e<^dmc7#x*eY+ysuzbGO=?Gk?gi)sgy4OC+0_VoxbM zwfxk|shM_hc3@_BPT8Dfd-*J9R>k+ZMd2lxD>FBh-IRIPelHVKY!Jp=v@?-q$cWa) zM3%Kfc0D(qVvL0qhOqaWP{^v1#;4?Ny1@ z4chCVxlZ#!6W>~C-NMVJMYBwx*?iG_G4B;Jn3aT-SFB9r!W_d0hMmb&rD`LQDg)*a z5~`?e3zCtdwrzN}-Rw?)8Cf#Xk<45m^A~IQl0+CWeY|&!XY&C90An*X?EhQ8GJEem z5B%o&!c&iL2dBPBaNwf;gIkw9hRy$-!kxqHF9KfVLb-}7wY zp+D{_d~oB){M{hC6Ues}-YVdO3%{$G5F`3{A7=k;7!hoy0I;f5SiJ*(?y}-7czvj=qNXK4wv#n2rnfe{HGco? zu4Kl2A0KaeYw~_>3KpvM)5}hG&Qa!-Eh}3o{XoCMxK_T-_^I}&(QAC7e{7f-%bkD` z3IvRRp-NT)rsENrx5(Tb&X%M|G#*PupB~s-Qn%UbX6B+%CS5^s&c>Rki>V=cJ9PM0 zF(s19=y&t}+(6z(CYtVx(ZnB5tFL*dYI#*>6{@PR;R$2Cig0ItTe#!D(Ko3if%S=b zc8`H#2W+qQLmXKT^>FNsH?kw?OdWTk4p*EvS@J_120C3bR>34F_H?-DNAE91{m1u02S0QJbZQYiC-zr zMkCQ+B^m)S!d6nWrGvxL_rVSOf4lmn*QeFaJY(SSzL^)z8lIl|U*NuLH=p^_2MUec zp6M@Karc|a>Z&s@Evx{e)@~f92>q9$=CN0tb^)1_H*er$_AlJ)Od~p(j#&vXH6`Pa zG9_|xBronS)9b;QFT}BMaydOtE2;IXC9+Z?mzBr~U&gVy?I{HG1>g#~+V=C%dF(25 z3CmX3j7HhAQ_vLQjO3~1ld2}yoP#=qdC6I|*9Y}VQg|TBQ{@ZQzL4>S8egcSdgUJM z3e~=l@r4?ashmutS}jurt58k#7^9_fQuV1L=Q!<^Gpm;>7ivrNMWKcE70PODwQ-es zY1OLgb!dZfy|%%)(Y&_m$JMuLn~lxklovJ+PiL$|CN5>_K!#!J;uhO9D#I+q(4!5% zB5{2JCaNRa@KjATsOBPEu~f>XhD)hb1X0(ClnwH3eS@s>bk@(vKKD0~8(v+dYYLYx zOQsT{z_SSQpt`C8@8h{tVt719Z0Kg}d-O$^;j{om0}2y3pbabl%fTkVgI=&brw&g! z!Qgjhk_&Uhq&2cdZUsNV>C%wlp+i%q#o?h+hN%N}#O>5|IJ1sC4>jSscvD(EfwBBi zpSX=c#v+jq`IO}&fK?p*W6dBy5gX+dh4Rx5&}67rZTFdsmfXqILbE^LaDXfhla|6p zPTYGkVxohjNmmToI4Iqq7N6J+5@2{DGMu9+o#9F(MZKX2@+*?qT;0KLDcIcP6?Lm> z=rB(@C(5hQheOc_8>RM4=q)pEw>JNUt1kYeY8LBqkF;4 zW~js12+NfQ#Aef z&S!RgZy)`31M=@^xXEO`A3TL>FgMJfwC7U;iv^Ez#3A*VqeYn+5)zlzX+AGfj^CJ2 zFWP}L@L7@dL|^W1mqVKNw0GWv{!UQkjah-dAjkb|5jo5o%?Yaf>K+zK61zEz)c0v* zDKnfTasYB8-+#q?|CK;LFEe)HW5e zP!XXwgj)itLi5fOC(^-(_|vi^IU*MlMUKJ72e}Z0oP;m+sUtI@v`_>Kp-78%wb?ci zbc&lqybVB3gSp!5d81)pgXzGXjDeN#?>i+6kd+uB`E7rc{nYKX_~(2F-3Vuo=LU`(OAyxN1as#c;6kx&D3Jo?~xy zF8{#~*g9G_#36ui8S(z%KipZShOb+=@2~}WDx#NYnK95`JOT|l!q+!ra1<`f7wZru zisve{&xXhs@b!GJO2?=0boj{dGb=FLnq`}Jh<6w;V3RVMNtd3d(rkiwtRdXOwWyQ0N$S+_3~q)xH+(*K zzPcoQ33rKlW!T`tBs*_mC)R+p%_q9avnX{+i1wur%W;qwu=dFqek4sd3^f$AEb?!r zY#dj&c5#gDkhN+B$T~MCEJ_Z;k?(K9I1Ct@6U9{64u!+ErAksNY+=y~sD|N~fskni zEJ+pZFlPiz4957DIAoiKAxT&b@IBd<6$mh5JQ|IgCrjWg#=$Li78Ay=9K(ULdK@y+ zFBaAG*S7Nxmv* zKVG!%_odPWR&xWZd7#87ESXHha7~~ZzgJ(HKc|-9CEfMGH@egumm}Zt0cg2qWu7Uf zJ4-k*r>Pdk0^DT)hDC40FiD-ewi0x0C1~MQuo+YWGIAJzdka@S|3OuJoD8C${eF67 z+3;Vtar(nwp zqS-XkP#CkMTw(ZfcYB~XdR-s18>_fe931@4@kP_|-_-`AiJ#U;x3^@Y}@>}rvO4VPP&2iLJ1gbniT zY_GW6dX9Zje2aZsd`ExN`k4Jh{6s%!4ddw-n5Y5lr$@+A5y=t*=9gVW@N8C5nQ+Lo zV-3B!U zsX)geh7=P}3Y0g;W(`dO--npr)xRBaRG%Ft#*D&8ZOM*!V;IE3TE+dPE7!vIcTjq8^P5GRppm;mY6!6`AW^%W$XaA*a3ULYS1g$= zn8=D1i7feoww-09F10sTUX~5w2p{2>rbnW!K`RnHNtCdJSS%@|E-}Kw=d`klnoOSr zm}I(Dk%S&e`x3w?%52^GYu}gNx1>8s zcM{T(VA3H3qbXDtMRw^CB^nh`5S1Vzj;LW&&>2S==Q-*u7hEpx!;-KFI_Awd-s{2x zXVm*-uF8z4xNzmpW4I%d(08)16)_Rp&p;ch33#y9cM8_=Z+aYn@p+ zga{kA8-J;EH^g5G=Y}^b7JNy7=1T(Hkf=0GqVgqgh(<(ZCEf-W2ci>#ZhUsa7oQ#X zWs>Ds&F~s9yyQjwel&sm$Y#j@19N#eHMk07y43_i%w$H!< zTaRx7comBca+A(pgRbGPQ9k5ZvOaiGka%9=8A(=2Pjg0ALb9yz3@)>TT^kW#7Q z;G~kr)^V_-bMRHyGUlIS@%2N9m1I_w<=Tp>LcNT>!KqdTu{bPY<$5JmK;(vKPanQ# zi53OPv-mAsrNGmQT#0R>yc-eDI2K>pBqfXyZ5&{quXZX(N$7~;_*qn3BY7kmdB)*+(ME0IdZWX{K0AV znW05L_yK-y9{VhMoBJ*PgdX?=g&7x(7iinC3Zz!uhaZpvu$_p+3p3G7hPIiR6wQ#` z0Zc@rwOH6m&)JIBFgxk)K040!yoj18=QP5&#b6gcnZVEJs$wtgvH77IrUkwIlh6JW zA480ysq=>qu_^W}s-9|Ns^MrFB~5CGYl((YT0=v-Ha6Ur9N%zWLzmXouvlBv&^_jd z+L}>6kNhI>j25oJRAa^iSFd#~ zydDGly{)M0DJHs;ezq^>B;N#|Z;*G?)j1UanTIsBd@?DjIKzGCg^?a&EPSyLF(KG05YXh-c) z!e&A~JSuvfHql(Hy2&H(Y4Vb?CdaVYRG zfc@rUP!j=O0Um0$ybkpdXmaOIA6ITmCX{jxhMCWsc)0@$;NcQ719jZ|C!Y{B=3u__ zFn)N@;ZrNUxyM0>Jx(YJ76GuOP(8^V6qC=Zsy=Eba~di0rs`y-8DvI@MWd;Z!viIh zy45>jUd~57h8J|&8S%w0VmcPQxOme`lW&~T zdhPzJQOm@8?zp95bL{$k_uljDOjC+hzm$qz@#+opo3C4R&EvHdcg~&k+}+bxPY>x@ zBAb)1KkdwUJ+Yo2O|>sSfAq>D-`;)ZIP^h%%B-K(G^P8J8E4*1>UV4KIwsvaCRITP zU>)CzIMv8r8J>)9G||swH)HYH501njatRh;$?_h+L5961%jEjIPL~iN)d) z?Hc3y$n}YV^!w`m=>GU$f?q|yiho>jAU&LpXSgP#DKv)bG;HnyVGJ_>wWS0*FfLbB zyvXEedH|tyXam}ejvzLTI?)WoAOgseMT-b&MFm-22(%jkiirY5yAfzNNl*I8vVxaJ z$RVQ`44c4ECW=>1ZW{qO62)ESOmx6=2eI-Gj$9Bu9-XkHXBk%Lek7ZE*cnb& zSUw5E>Ac`jAm`gX;I{^A@Yt5-N;+&(HPv-YDC%QcIPIzaWm~S;)MF3*_2rkYr3-U^ zvf}xFzG=nt+^)gDJuu^eH*OsIdg#4hp`X1n_eXo)-1piZ?89abA7Tz;zmcFXccPK# z@DVS*mAxTu$rB7uFg<}>B0nNn(C;viLCoh&YAN=NY#^lwu@tKyJuHaiTnlin1zP zUc@ZsZc>+Oz1l`~o3vftrm7KjjruXo=+&1QHyC#qjDfHluxrOauYWf_taa1|>Hu{F zC-sJ*P~~Gx;X?;n)G|2csku=!vbqTVraDPMx8hNsZ}^YW%c!i9PviI(A!O@$rwnMj z`iyM%tf8@v70%$NK+kMYvMHci2~e%`Qen?{hdtE}yT-e!7|!k$P+I5|XhDa5D4BeK z0A5+}D82;MT|UC;$-=9pvV)_Bz5$YD?k1qX0XA+P{^_!#q;q1KH;#l&r`dH7|AT51 zR@iwZVo);s8NePQz{WshaY_^b)2j%ji8tTuC-4UqfT6WiRsw40jx7~m|9bz>e=Ym` z-WUEoy(xaj#rHh>_rtwejd@QH$9fTcFFJl_}*`SfGy=DthpaKQ{HqC zEafVhX0@DF(9YGk)=+Eed-R3!?9iOl)${`HD(T8lcWNO04)<>GgZL-GPeNZuzl?tZ z7BP}e=M%(?PE8P-DvYMH+UUr5x>cJ>Pt+!b&P#nyE^1e6pYWeWj-#WxiNcJomtcdH$vq|NQi=D<50)PxpQM#CpX1aMlD` zKR$^Ly!FQad~N?*uM*!p75nB&Y%9Z9<07DO(KM9`yqXwS25ReH%M10VwgmK zaH_Fj&n8ku3V6x_?{mjP#}e%6f${OvQxgKy5))Fh0`uduQOI)5>$*&9_qmRW* zDuN6x8l4#-1vDm-GS-|&5Zf^_FP7-=c_gYr1E z==o_gE7;kFf~PN@nz?4m&KF4|eo2K=phAvOOM&vh5FAmVk*>BY53Ml=@pD+6jsd*n z75K=|mCFs&G1$}L@}ao&0x;K+v@0lZQN7q3aARCyfZ9i}Bi4)%9n+SWlsU$vgN!M& zLD#&=Nic7+;ElFlc4=2r-fC&;>hYO4r^_=WR6*$wRRNJ2&s@5z@jqVteCTTw`uBH{ zj!qnsx88l_eS`byS?ajry{n!<#po0LD2-zj71a-YIP{H~*|h5#^w64fuGvVigCJJJ zUha=n6kY0cdxj)r#GB${;&yy#{1@skwP!Rjq19`f;{$Ov4nSWbU8oQ>Mm1703e)*e zkY#vEULQiC;h=M|b`*m)Tc!u_3_{eMV^?r0!BPzUAc7X`zSGA+dTZxX>B2e+#cg7O z;(dtySHU0$@K^5~J`Qwx)DyAzOK2BWMIA#j z<$*(`YffMyoD_qhIGjJ+<={kwthn8B#$-dL#Y+M&;;hD$0!hl^jU>vWe8cM1D349Z zvg9^Ojs!_j{j~li3@;ndy(GIk7qm1x{r5GkEv!usf%#=5g=&Z@hhZe8PsMqzB#*ioI0 zlaxtS6GvTGb>XN*!j)B5j_PjgZQNgXsOmpzzOJ*P5k9<=-qK&63JMVEm>Fse#5uj7 z&^XnlZ?jDjrc0k za~r`6MZpV2OI|1nUWjlQ!n0pEUWmMka2mQ^DC*4VU;#VK<31a8-2G0q7+r3l995n6 z6f*58WZG58Xfd6A#n@|nWDFZ@+UPW9;26&{D~9W-Mi&h*D@KB-oly;hc`0&44lAhv zp2>*k8<$rR=aipbwk$nv_l@cIPvD@y;v6G`|8(T~w}1T7_cvbkrwzaP;?a$_t$O-}@2`CNqQtCR^MZ@pHvb59eDDZD z_dU{kV)5Viu6&MZczfWLw|@89?}$BFOHs@r=oWe1nW2v0^&O59*y?}{nSG9JWhOGa zG!~u;N8^R4XsK3+;Sgn{I3c9S>WFwo^=Xyr#Wr?bQO!wq%hEK@5{kIM7##ZYdtPmY!wwn@Y*_rN^PpuL+1l|(_EclMJT=6N(oKI#; zFkf3y8cVM83~8OTLE0=0NFPZ@B!QCB(o(5cTJJt}KpK|hw1g8rfu$LVXLb({xS!Ba zWNt?k&+)9x3ptKr*Rvbg&FlbsfaM3+BP>m^8Fn9j#Ig>~K+k1Ms>XtF@7nzudd`+b~=E?S6_Letc*a8V~vsKx-U*DNU(*f?8_!@&w)A9Wt_CMD+x%^cru` zGM=D%g64*oxgyKuxHH%mZVkspMUE3#mS(vig*1g`LMm%N7UPxKSIvfK< zqu38| z1PSVxZj$nE57jx106%#k^e9~stEFYFDLN!Rx@a1;qA4elqzg&PS@9<|aVNJ}0$%mD zw8SzL|E0>i*9tkT_M~l%a6Yf;AG)S`Y`Sf1f6Ii2&tpG-`|WRTeN;d1L3aMP8(y8Z z0Gm6!?wP-nnRq7P%nRzJs;2Z^U1zXJO8IOloG($nMjAYbG!mKa0(Kin~% zkv!HjXRbi@1j!S~B%%#-33Hj7WjerTf>7Q((if;B<<2Ah_&&hF6VT3Z#J&qZ!?P^M z@@>*&tPcEX@+C%{5Yvoo1x8P=kXT_^Q7C@TewH1*Z4oM z@9_uu&xQZuzY)U$S>_mqrO80PB;p5>DCV3oe1>In&S<`jmpMy%)L4!*1}O?9vpbPt zOB@Sjk!q1V3TFVHF`ePGbvThK&)|D%tf(#>p^~FY%crFFbcsod&LMTuAX@giM}mMjTP;ZSW(eIChoUZ zkiq+Rwr1dNOO;EN5A(1+l-tO_4-aguf}zQ+5pwt8Rujhc@f|!;;ckoK&C?PTihNbz z0~U#)2>!NEr~_`uB*)emdGl9WlFrLf*F2|$P;Qcj;YmZrR-2_*5xZ2DcU-B{0_IKh zLpy?iEg*XK^P$D)l@Es=yMx34X*lRTa8Q%~`0JBl2*FJM7w8|Sd06k478F{eBC&gOmtb`HmU2AAg6b02Z+ z419BhW76DGu9q9;SbR!knsI_8@<|XZg>iPYoTRyqMcZO3wIq8!VKDoljmYVX(NuQkX$*QjfSo6ucPd0pV(3h^6 zJ~`l%EuS3p1?c`@J>ZiqpRBoqdZIV6NBT_cX^K|Ju?N|M(x0QBWVm;^V;MRsW@@BZ zG9xieO=XG?lMG8hd`%*5%KLI?U2a2;&S593=hj)sVga{=IYA35rhr?9fD{?Fdm?IL z(STlpkF}syYI%d9WejsC>gtPmpi_Ai%PkIR#aM1#5+&iAlBI7-!Z#%e>4ZhTDG4Eb z60q|mv8s?%BvtaANv}Se#9y_YqFZXbFRAg&bB$}BL&cgL+D9Q$R-n@qG13ee>55Z~ zGz1Y;#LXN|co@i0I|M0&qyD;Mx3jsOXl38YL4{Lbf;`k$cIxsIZIZ`>&~w(a%pqdw zgl$8~45W`wOjkp}+K_4`Q9uiO35JJ|{lgH^mQ6Wl!v0H*j;lgFAjE?)6)`t~>e>Dj)T`DZQtAN?0CxM1~ow)Uav zmtAquu1(tq>*!xCxorGHPYymzZ(X@^=FfjJ_`a9Deu~$11bqr9OORvuApNwt)BKqE zEO>-D7UWqHA5>$#y~RY2nEPS}V#6^uBZl-)B!IJ5#78t)(^Y*$3ak58zFtaUQ$;-+ zgN!u>*{cG1s{(neQp#Huuy;x|yaI}v5HLdCiXVUD7CB|N`gzP5gH<3kRuKNDOpig& z2+3h%M`HBS*oN5V*g%YpF?35f;^|(*)5eGwcSXQLAM3X)m;U8DQ&;(<)YbB(uB>a1 z2kgMfsqpkDkZ+g%ILYi$NM}d3;;zv0>`H$KJIKD=q-(rL}N z4cZ)0$q{WvFo_&qy^@6f$Jg*VmG6( z#BJg+H02d0%bP~3ZmEenJwf1o zcdtmlCYe3?E*zeAjnprXfq|5SplNIlH%GdHyFy|~mKmFQitLd#Y2H1kYzs>ya{HfQK`W|Ue}(Hw5BIJ;tLW?e=c zA03}OKYD(0o_L8mKRQ3TSiDwUWL_7&HaU>_W9Wm}2Z=vc9t<6HwKPYiwgqYK6le#<<$=hZjgee;cf!&)d}@4M<%UYG5G*7+E6)v{o17Dzle{u`WwN`nxAGnS-N2{(=jsaFERp!Qqhv&XL^z{vYy#4n*j}KNozw*Y7 zn^xTP#LyyIJYzZcdk#B?UC1tHdC3wbQPP5zq*08B6fg#qEY+_QkyxDxq99#;^6tI4m{ZS5izR+d-Fc5VR~TltKS8CA2hp;E+aI-uYsj-|AY1x{^Cc zP>EwnWhXP%7fbr%NT(DhV4r{Y<7X}EyyVie&OZB$OGA}x?PEPt#y?dzxwCuO;5+0T zb`Bq6w%~I(hOLBbyj0#Sk0J^4ClUv2sNIN^@IvC8xp8&zgVoxsWO|$ z)P`AQbVY)c9;@Sx(54dajL(Q)7T*-#8|RI9I(|d^qd1$6KM<$mFJmJg#Q^DCBA&=sk$z&Z`J0F{60!Qr3UL86On zYP6D0phPxq2V;e1^7dvZPRE>EVkY9S+jl%e-Wku3cf|pri$j|M`RN47@tpJ}<+0m7 zY;YRz=WR>1?`uE}X8L(F-0y#n=>>vl3am{0-n8_O8C=K{UjErk@kCPMZlXz+jXZ^)^MT5TFVTWL6hODwM>$d>V+iYPQy2qA^fmPuSrtXHJU1pl9Q;u zPLlaNo21fa1<48=hHAKhWV9i_di83`XS+!yqpSP`a*|UDp$et8wqmriDePz^o=Ai% zApQ%Nn}Nwr!zb?uTUT2*nr#CKlxu3rF=!^;D8h$e8 zwwEuqHmf%-TD3S5X-eMp%EQG)uiduyeRO8(+GSUrduB~6*L>dU>634%Pv@uHdUb5} z{Ml_asfwVSZJDrY{>AIR_dK>%+2Oy^4cwzt6kQJpRb~Vtq>LC4FUx{Y=6$kEc#vxe zHa=iyvCtbw6jC)AF;v8q@`j9~Mn*BrYN{G(-x0pKWR2B0d{fb|AWoDfb_+{|USXZU zQaF9sAZ!)}gna@J1Z^%;+fi5t0^vb`E>8!kaRryV?KtQ(NlZwvNlF85e&Qrx!Y+C- z6+>gUEIbMP!vWo4w|sh#IP1e?oP;=QtEI(!-A8nj+`>eFt%*?xi+QsxEkQWquyk1h z-!1d7nO}2rlbZ$x(LHx{}*v}kbYw=<4a4P5t(l`~47U)bcLTHbU*zrK9kPo7)7>uYW z7*VjBw($9|FJXdsHB(-Xi6L7;}jkp#Uou_3WJF`Qr%r7~NofaezH`bZtW6gWtT zk}}dh>43yaUf>{=0tdH*FGJxT=F6a14_Jc)WpZhHd<4+z*1}H(6`UXfI;T6@-F+Id znMtswt{EDc&L(?bFucjCNlFtfr+C)Tu-X}w#GxEkehc6#Mv3sC2Ay6@wbLh9R|_?> zXqi-+>0I^hrBBQ-m43y#e%7q}&*=YU|CH-ywBATRIN0~&(a? z1=a$YzVeirE*~`m_1)AAbfg63`-5?@+=)&2HT+%HQBI^}kw<)qb|DKCn@xE?`vQ}cf>Rc*D)#A|C-yA%ne3L?3ov8(FC_sG4M3C1f`Ru!mvd;@u&RGlkO5qyIm?YD2Q zA6=j__{LC2QT0--+)lO1Q>e*u5f$lq;ziOzw2)pTE|OMKH=~>BTf~*p&GK5bmR`f$ zE8HXgQ2G`1i1ZWrdFpZbWon18MSh+7y}X}#SN@9nSpJqeDmUU^M~+buxt^+(+vFLP zElZpoh!i-i#s#u@-3=iApupdea{Q?aC0okSHmX*>_ybYr!-@D{*I-v7cKBeIyA@lhvSzi9Isa1)*OLE&0vCTsoHj>cye!+1kOouD;>I74$uzm1GKb2iD2}_ti^Svq~CC z(461+8gw+b*640u<5ifrQZEHv(obeYsi~yFrZ+UpmxpF0UwfBc9-1}EuL$^FA(mf3 zyn=UlBUX`fc;3`J&-y4&PK*F--S-%3=@kJVmx@?^g;0Kl?4zh)JsBrT)xK98@>Re~ zGx=7=ORlQ%Z>GC1{m|E@Qj$G7>;TD|!8tmEWW0;Hywk605ZE5V2{h^D&Zy202KsfU zIb^_YB9djpW7%R5rQ&=D@dnmbO@+NQy27Oe)BPgp`<6D)cOs6B@+4UZ7fq@!}c!xy)qV7N&_)m2-^A*7<=;jM;%} zg$3f(fm`_R3(Lh9`CZ2Lz~A_9rFzAxr|Pvjz22w`G=;`dZGoG`HR2=8!|GG$Y5Hkp zqq>dS&hOIym3@zYUpmAdGCm6&<&R4#1t|PfxHWlaWY7tZ0p)egN>bJhHb7aTAm#)k zrxWN(7Z?qxIc?|gdv+UfV;Z&&4S-K+C=}#n#j2I_)`jeBdA_y8T4nvvk}a9Vwt`#^ z=fa%KDAJTa+T_r9nFq;VCv?XDl6HszrV)XYWLZ>HRW>aPM-Wr{I4Xb>f%EKzvY}^w zX9;3PumXWRCxkdo(D8N7X?jT0bkQ=5yex+B+X?-MYsn}Y2?16#ELGRw@B%mnAiFFP z(-trYKdc-&W@?CRFVd?qSeu`+<;)Bj-5}pVX1wXSwlu>+H&}OAgeq#Tt(Y9@hVn3j zjo(w-(6QjLg^*OlrycF;is7UH|071QEB4*gBd%4n;P(H6c|_374)TAkJ2E_Vb9zo` z+*`}28TzH+12~~PfY<-Nerk-733yvz!~FNu&4oE-_7QP--xgsEg6FH|Ox@h#rzR1H z4{Q-K&JP14w>TsuiTk$WlnZ|$_B8vp3S-FEY^BE0yPU5tePikEQUBYm;RAhghRsmq z=We3}`OtT^2imE|0PwSJ36^Klok}-Zp+FlxGTZq_e~5bk?+}av{|Qsaps7Qjxisa}T}n+x_&aZ724UsMf&o%zt9fVbXag zp3%yd*wo9!BqNBZ`~gbXKnl;&5>N9QUJ3>z;6_s(Rs(Dym)v0lkWn3X_G6eCZ@<|1 z8T&KwQT^w}05`x72yYsaVMp2%Oi&7I39}WAS5~9@6|pJsJ$9ZjPq|2c7(F6CqU@k| zs{g9Ip}%GBXWo_G*8XIEA_oFxK9PWB#5B|66CsiTd?K<;^QZEOEaZ8{VHDvd;1n?o zlTeBnhGv%dL`<1CXhSw%qh6C}Ggo31c}+uF&QB`Bn?Nce&j_HvdD`u2wQO9@OSjuH z4jFdX{7k+VHu*fq)-%lQboC5;>d&)Q0fg3d^uKxg_V}i%tkJ#pIZ005C-0<5)1-Mf z(6mY0v{2gcYD>#gq0mBkNL$+iRw%EwAOb>pN<~yq9C6S=9R~%9R2)Z<&iDlNa~wWr z0R4W0jx$aj?_9=lMsm1&pQMGs`F{8N=iW3ukL2W>z1MrKz4uxvD}opi{OA1D!5`!g z$qhH8*`#4TpGqL2)%J)vLk3b?vXhB`^AnOgO&hE`LjbhWxu*JbQ7 zZBg&g?b7cuY&Y&#Jfr+M|BCLk@lT3B82_eD^8YYicR2M1t41@N4$Ev%I_#`=g7!Ww zqqPlpA{CU?59hmzYqct#XcuA&+l&T-Pp>eN6RnD<51&$DR4Nq)y7*ryr8hth-hm^I z*Bsc{=0y#W!PXlx_?1*v*Xv(W0WX$~5k;`^`sJ76C5r5LiH3-Ylco|~I^}I0^+Rb1 zm^A(0WTS`e$85AtN5 zD1~KjTncArg@4~CEFJ63TUT2k-1#gY^xBtbGudGBz^aYwR^uh#zxBeny4xw21c_w+ zHQ^Esd@XVb{II%1LQ|{n$8U3357AWCnDYraIe;>!IFs^?L^U}G;h;Rimq3YP5>(&{ zZjyW&KM!W&nOwbmI=>Va-SVBVh1)Lw2VBK=8|Q}*7nYZBPjeSQMy19n z-c*2z@{-f!;vRho;8Ah~<`fDaU}Aq@phb}JB1xF^U9pHTDfQ9hqB#}TC~!Y$`-n9n zk-mcGB7|ggU_l(w&Q>4N0MaBh3pMv@zR^g;#1CHjm1ZSU+yn3ef@!E7T}KQOGlCcw4 zMW>+0QCO0*H%?I~Qc$K!=`F7l^U6@W;qq~p`9N}Sp!F@A!1fx*mbglN{d zCs|rLoJND`*l9y4|23VaOeGf5(#&<`=@C%f&mR|N$Tod<54wImwGLA92KJ*WLXt8x z9$g}c^B==>ePBXITyHtV zUJ-*)Gh=aI4rGP=dY{IReNMmGukcCz8NOIfVHS$}$VR6%x=|K5++dNDBb$sI6$GPM zP~Zosc014ReDPxA%WJm0=vw1kHLc^J-FMe-lYDemc<7gB zh5c{72`y)T2}{ndKP-Is)M4S{BS#>7_$i1SPV(OM4_$fZzO#!rEX%B(_SAs`kGD`M zx0LxKjFG%e)bMtZI{Y8WelNqvWM^gguN*wcJ;~t?ZWD)RaZMb?9B@hoaX*#y!}Np` zn12w*Mu|!RVWt$xin&0F)@`cCJBMkV&Jif336qFaAZQ$<+7G==yx|qg$qSY*xA$>K zb}vo*lkuKt-O$PPMwwI=C@PAtJChZjJEEwNVZZzE>8+z@geuJoZbu-LTz`}ChJ}?dV;T^X(B;Ow7zsQq&e%mRak}`Gp&{Kr_o-9K ziody0+HGiDHz5v#p-fSd9%fUKtaMzqG+FwESY|_Pnj#g8O3$>zC#DfV82fgxxhNO+h1U$Xu9a6&7S5$wwgfjoj)rI?*cpu~S$`mH!yxRkQ3 zSV}b&Gp3^UW%7`$gj|*&);)ksWEdS`k_>(@HJmRiBp8nApr*O2Y=2}azJzmTxVnvP zW0E@BJ{$R98)=^d{a9?Doncj0yvj=JbzEaug#tROFDZNrA0c#k-SPJOH!9kf0MG|PFi&iMwfw7 z0fV~q{3rWKwK_wQLE&;-p%eRaB}xOH6tu+r@W#`#5h&@>X_%CXr82k4CJmK?%kHa~ zFm9~rk<2|?AK5*6MaJ{(t*d9vtSRmo#CH)Vh z7sUSSJREQE@Kdx}vjhTpRQr%_N=VN`1kS2VTVO#(dj>AgfDGDyGwHuA6SRj6Ay|Na|^{hv=r z7Tx#_p#SbSpWk>65>7+Kjo)zpgWquI@29z&e#8F{e#4``oaR%%;ZZZHXXmn0k&Nhk z3(@)k6hU!Rjw;bKG#f2IcM>yh4cdU-N-Ss&Djb~PHwJ0yBS3;pDT%+`{0~db|4brKRCyqBb-K#&e6l0|HbPdlG^j< zQ`g^e@ei&i5HNbO3kzb=KzeUT?=9*5(D$-i&Trk{`u(lvzFY1;^gQO*c?AV|`{?HH zvHV!RmmUf6D0%%XmY*NPx6y4fgMNTF4_})+npY5wiWkFM^gCf5-Ts|kypJ9+4@apr znjaG`#A5lMk`s7@9MsaAb(0Oe8jTbttH{wqd3gocmA*)jk%KSk&ps*0E661Wl#;|1 zghYpFQESRd#n-<~|wJZ9zM0$En8kq(uyl!&Y10(6mFoR0FrsMxrNCr!vM%9>(U z^o=?@wb5`uGu6|^&ZDyardK|pap4?lgBY`f*d=z9^%4rHq#+~!b0X>#$V#>>iVR*H zgvj7!$^`UDE?q&qDCT0s^kOn`HUWJw#3oI9OgKk25IbnhUbHCNnA*Zu#~PQyy<{WY zyE49DLGem)OP~Eqv<^zivlU2<#wMIHsXVkr4bAG+YOKb7wur?nC{+^aYz7!*KVEbK zSda zSe8BtQjiJ%AU4ehq|fVgj)b zNy{SyVrD`#QxjT?E+lb~_JT6xK;R^8O=x5iS+DGn3==y*#@JpV?bXs&A2VoCcLoKkiR$>Bgmx_(db-sORAAB9q%T`U=-DhCmS-K&@VoiyJ2n3ZL@FR zAiN7@?KNe>s%>R6_H4s1$fo?JqHMz2RaFyU8SE>I3Cnsa#%?CC{+i&}Kw!-wVxeFp zK_Qm7&ZMQsSVS8{koexjN6^vQ2?@Y7B9?5rDt#)C@hD>*9S+1y|G7fJ6BiK%$Rx)j1=B%pPGQ3_io;}3H-7Qb0)#(_|Dap z6NCqarDX-Mx2I<_%>+X06Is|X$UrEtDluY-9_sb5LCa@NJAS^FQD1qj2i68rt_7`uwiJnDRI|u$=VZZN0)b= z=vZ@NO=)@O@s0=g_CE5!-Urw*HTR#tZ}a>2*VNqq{^tA6-(PdB?1MMo`r!Sy-~2$F zFG-p^kBt-Q;ya~yM=k+Oya`$rM5^d#a*lzDSpOmsK;(Rkw8ibwNz;}xo_BmgU&q0Z zuCbB0Dk2dapo!L2o38?N(ibOC=s7aK~GOVBzq!wC?7QpzuayD%Xk@6 zSdfdU6e?q4X+X;~jLgma@!Eo{+DE(EH%&C!OX|!1X6lzsTy@tje3}+hYCGxSHpG{G&9hMRuUp} zm2~*sE?2JKFX^yawPm>-#5U=;M+?)mGyxu?jg;p?Zf+Qrx}B~}EuY6jCZEY0!p;Ih zWG4(xg(eNI6np4mgLGt$K#f4IL1cXbG_t4GG?feuH-l~H1x{v&C3A5dQW^epqPfynv*l##q91ooR-Vl0U7J`sePm8eYa%1zGmc$TU*s*HncuO)(5Iii z>fzaQ544YIY=no^iTP_QnvQghAGLJ#jqFJM}wrz4R zxPL)j;liHVIvyHHIWb@8W=FFzLf;xxnXs#6N*0j(omHAG8r)1=Gb!7qf-0iKI705n zl~Oq&{ytz;n82o-XwZT380ll=T$Jd~a|8T9G%E6dN0(BgZk-3%(ZA|T-i=?}_bXwm zMDPjSu;nMr?rRIiYoOX*`NX zs))xZ5pzV*itdnzL{Zz`z*%Y$4Dw`q14Gjwhcz&SdwO32UikB8F9}youbLmeYsYg( zcRWA;xn6uB*$>~$6NFDb5zfE=u4d=<_aAxm_--Tq>rn#fJBZ8+NzM>E--Gri?kw<3 z@Z9HN{2u7ZhSk~7{7!hb+sB#l*3F}qYO7Pa61FV z06GE!A|vet8a_9&bkJguI=$0Lybr6^Y5x zrE+z-#fXUM;eB0iGPEZ24HnYmLP@B!!4xI z$dnZrxCt#ct2>4RgYdsTx*%NLzv1GSKY#733-32=-`UjMv$Ju-b1$yn{M0jyZI19? zuL^+9?0nzCj{oAx-+uPQe@+>{aZzK>mU+uIB<)Xa-u%q--5Z`Klsi*s6t%S#c~Nho zp~zL?!qrYFal-aY2%90p401E9G(xix>U1!df@qTlDl|}_focV`aWI#>*(EG?s5(%u z6|7dN-$1B0yF;os*qn%AvP@2`Cd(-wQt!hgI}D!~wJ-k-WTdIAP-0>^n)}r+Ix%{k zqQ#jpGBxgCwU!9X~sEnKAMQo*^<|Z|8BfKPyD15Ayy_{ zVG_@4uYbXs#dEWG9wpxgC8x=`#X59Hx8x^u#8~{EN{JkDM=7$I#4!bOJ(3(D=NW&Z z=eLvZZQ}jxXz2Gg@p=cM^1SW(CCM_$N8~w`s5>!b=S1k52(1&KDFL+!XdVT#LvUvh zW(A?r2@^7*+6EyW&{BHLczsBu~InCyAcBh{i=Vaw_Vtf-*Cr!Hl zikaBJVDuK;xIKPRPk7z^5vdda47o4~D zKvU=C}on+ysW2ry8DVUH8;A>zXc^PDir3B*h{`H_ovVV5hEWu}GQ zw2CYRRv@Q~0NL(wY8kszA|DnFqOc~?S`-XKQ#GU{-4BSLLfG#YlcI^U^Ytc^+sV-J zRdCax3JsRDyOtk3yW+{@3k?g7es|!3FAmN6{#99Z_p>dBE(fn|WL6n|^Gf5BYbL>m zYfi08w7m_Um!AU7nYPw9gs+~jy!Tf<6~8zI)n9!(dhJOnwSR(;WFCQz0~IIStL0ne zcnb%kSg^Zj^*`j)DqU)w)M;ik7^w)w3=oM;or>rUTivZoQvzM995cm+$bo${vY>a- zre_xiUVn+pTG-V)b?sXZOlPWlUhas_e&Dq`uYD>xeIYh`tS_j9Zl-!N%N`>=I1WhL$MG%^OD@P zuxk&&Lhp3R>1(I9onAqo-@>^5S@Cgnc7ovHfA=wSC?2RBe3Rx%Lg9o(DI;zY<8sM+ z4UkHPU94P+WipUr20;qf1R^CoX;gB6*n&M_9-Zl-MS@{@%t{zA zMo+^%-1-3!+0X3;igO*fL9$S|n$*AX2L2RKQirc5Rg%-m$+#~$nE-s@^@UOkp-U@@ zpbLrJn zFvkiL&0sNAnsBogT8T1cVJiZs*^+6=^xKiu;c$fAR=>q!@FO?xcDWh1J3s7q$v`X1 zl3|(OsbVr6PP>hesMU-xkukB@)S?UM#Wk-@SZd zwNp{_cnRUN#A=j*2cmH%X1O8lsbpuDKlJL8Lb88x%W1%mHJ^HH{hY|kJHhg&&dpIFZWEIbYFjb`ii$ebMPd%-dd91u=UxuSM&*&8im(kWIklRSo7PR zdoJytTD|`dk8k*ohi@12UH^gklvpPw6hccA)$w3u5XZACvvIMr%884uRaQ)#>N%Pg z4W7fbaM*4)`M_vQt*C>r&znkbwm``$O;VRb&&$jXMyjNCDOGpmJrru7W@qe%qu?f7 zLyeD=hKoflvBDS(yL73pGoL=g=I?E3dV0;+v8$eLdbN@56UL3YbIJ(+q-7PAZIzzL z^wyr~(`P*RUiXIcy;C3PyjGjH=)uJc9&aC0y7b}t<^xSJs@1m(EifjPgAjQVMi%>| zbojs}E=^@LG6|iscljJ`=|GZl7NJqPbZ2{CU!Tw-dG(*;rCZ-iX9oFBhw9Pe1r6p{78$Uw12 zCt(h8Tz>7EN>+?FO%-OceS}85s2D9yl*i+haU9RD%*Vwet488FWvdc5W0-}(?lAhi zIXQZ-cT|Bli!0|aM>xlkr8P(+PF^kZIe`&dMc?J3BPr6x0HKZKd&7Fd=edb4%#<)v z(ssDzzFX-e#|hCqd6OAhVmPFW?|b<6f%?0 zpp#h39f`@a@q16P9I=E8>tCU-mxKRmfq&-qB&FymTaN;^*(sdxD>FD)> z*ISU=;UYdw$SEaOa+cFT96nT@c43CYltjGJAluDG9MAw-QBmxcxMe)f+)5qGcQo&6 zC^6(7Xk2*kuF|nxKU=oy)IH;hRvcS5Z&F$7lo2DQwvHKp*R*i%^ktIMd)gjdvnZN7 zZrqw9jm^h4R@dx)_ui&A-n(?~!hfEL)OOWOTRFuSntXRf)%v*w6bi({$RxVTMXadh ziAnV_D2YvqQ4^}!0&NCp(LlF)ry4I+!#3j2qKuB-Jjl!Ai|%y6EEo7);3A;#cer@q z!%mv?&+RlxM54L@f*9?6L(0$n7a+LfWs>;KQZyY}lo`@}9}1X3R?mc%sUbyr9 z7aeRb9pC-pvQ;P7B_eIlubWp`);c*HoYYh{e#PyP-0AJRD_1U& zZmO=?{>Iv-cYgi(hWV26$gC6;f|J{;s@4%G{J*nc{y)k&;ti&A)e>zmS@Z_Ui(Sqx zCtAaVjwa@7H3o&z#IOdoSh}AOGdb`{q(+H!k_3t+Ad`SZVrVu%z%bi@4VaTFWS{^a zk{QVzP?v&Eid5i$0^|xCF~Tt8;7kUE!eAn9oK!|!_dW@m))NCnqND48Q;+#bfwcpzqAa+v_JPGtqhUwIbJ zo}YTR#>lIrtVF3cXzqdv;WWEExsoXJ!f;N8BRsOuFZ_;1d-jw5he`j9$c=K*+(aJA z<-K-^H|Vkh3i3g0588vuJX5X@DP2meM5gfFBcXTXp^c84dOgM|wTvqx7MhiI15##+g`sq(z9rTW`N@ ziBu&2JXkPqVjwWCE_N04Q<}%v?BkZrxQ_5bzPP$JclP?}es+0JucvBUNv_6QmX}xJ z|MV@>-V` zYqb`W4{2}sEk8e)`6P)p@L z1TJD;%EEFvNWz$sks=JkWHdNLLll%T0$QR0i62T!*NgwG=h`afF5*t~Bs=kk?D9jx zyN86Q(obj3lBdgw-$#^~iv^ZdVu_EgKgTj0W?3nOX{`R3bVvfH9~PqqES-ZQK7tS> zb2hmLXC{Atnh7%>3U3|~IC9?=0+B(Y2$+Py*+2q>A;Bbw)V3>d7f;q*YP>4{G?qmj2t`E&NBG6~`$xx85nMz;L{9`85gZFA5HCOf$i(RA;Q`^WkrE#r9!^s! z?iV;t4&+;=8OUY4IegT{ z(xE&!588u4L_Uu>oqT?8K+b@1k>ddVy||G34@(Kmfg_|}FWbA)W0+h#8z49{Aus3g zfL(sNzhUp8Lv+%Q{lWC`P5leOiN8XR>5mlc9Pf7zDxn}i!0zG2irb1IM2PzdixlH0 zI|x+6tnTwaOlPt4E~W@@JW>Eo5VfOV<`yODQleTV(kPKKFff*7lq?g-u9R(-(eq`f zQHH8zs2D`!>np@XUyn5&s0)Ilf2lv+0pg=sxxWYN#RSFDbcm-{EPl_QmNH8G%Xvma z^g|Lc`la^@w_8Yli(hhte{$D>X_l)w%+(gsZa6`cyJsjEjt!ak6$y`&y`KFK-_*aC z-HX6Fl*1*oEHVUzo@Qy@M}B_ zAVj4ro(O>K+AXi9w=H^Wvrf0^?M2Jpy}eSu^N(WsbQyeY^HU$W9Am?criLfgUtP?P-;R!+=8H0LG*P2 zYXWG`7)l|BTuR|L+{Ztb^5gtyKmSCT%-1{A6`}@(1HyqK+DSOL8wI)jCSVM!9%Y0ThtXM~6GQ2-L6d{%$x4)?L`4kBVo(VYcr?Xv_*(TS-bW@g`J);T zISa|ZM@%Cecxv$lZSq$+nNg!M`Ix8>6tW@)b;3}1bYf{_Z6qDZg@*^q5`BDD`6IJ` zmp?trA5Hd0x&9~tK#i6lnFQ=u0#jT{gmh52%uSw3==(Hh7QUrE%XRSu%IUOOV1Q}D zJ=DS5y}3`1G7h1UcZW6yaaX;0;j9mf+S@y{+(noa`K2? z41P(B3unT^y+;g_#0t(fNU0o_=;bBl5@jS1Nl73g5mQLNoMw`Syve9)ZYf|X0(W^((3u4jg7_^i_ zCJyO2v?2<1g`owZs6mdZN~MRI3WjP$3l+Cy(0@$Hc{@j*o~550}JH zsaz_ZI+i@VNKw9wZ{~<_DncGXM?|Pbs6v?VOq_;}2oG_lp@QCp$GnB-FkCmGas<0M z9dP9xR5Z$!jfS{Cje&2J2uWaEGUG~JnSAN$#o2R8;=)U=n=`wo1J2RAjEj#fEn**K zv!~CWP|!3fet5;r_J)l!6UVjRU2nX9K?%!5)wc#EjE#*>i;qz#`SOn1N!L$H%QmgA z_Q{`>7a7JECdB3P2_Zq_D(c4<+2M>@yI3p!)obQUazcwJkb0q5h_P&11mjHhX-2DqWLU}iWOxXEf+mfq#Os-Vc}jx&gob6rjT>6AU0OG8pDc-2U{+U z&TGEIx^P!Zb|~|NhaxtA=7h$synrXsXBl>D{bW^|)@*G~q&N1pC+a7wQ^HcG-%v(x z0IDUhH|7G>{*Y&mtwE?7P9QR0PnMxQX*L$W96~b;?dwk=Uj(ibDJfKmHx62gkPRUt zLWuTfDTeG5T1eR7R5_m9EF%ZYfy?av{Wj;s->9da#npB8PCRHkYNERqT zlypF@_-Pe#Ye~59(c3GBthg5uk&yHWzetZiaJRodapyRAVYvu`!AT%ujh~%J%JG+u z?*I60^lpFM2j40qWs=dz`)GeHN*SkD%JciLr}cD1|Do{YK;L{++xLY#zkbxXf=JI; z57wJSez6?24?Hdsuk(W#8ODz|9fn*B4kS;e^qrW6U90>Aeijs8)ECJC<*>&7xWoNf zAD@fh1AG%l{RT2`?~g(!=?(q2B$kbgn3#>)F!m_$ZYKeI05!EBM()M3EP97UzJN1U z=BtD>C?J5`uT)4Nsb%4`jL$s2v^)`CK0KY4hJQo>f_HbOoAUzF@l$5e83@uj^6IA_ z?G??OyqHJVhK))L>}zMteVdi>u?qU^F8bccgyg`Up1$(nq?Wi>gz)@YJ_~BU{u13nHeyDCCqB@_z>pb0yR(Sqn+Yx){D$$2hARF=?8ih zeHMB7W}~W0D=6t>FcI8pMh3n<*8&heq0ca0)ESEE8%cRRN?}gP7``OEij4i>91C}j zMFZ3o^jR1?3uCc(EI}rYg}}P?^clc74rwnfBNz!#GCBfImrLmrFgl)$!ph;!mB}!+ zmaNP}v&5DDE?PAJ*Fsk2qZz_V7p8^1#su3XvxgaOg zuh45fUKQ}tb!1e*I7icAbP*Yawc==iYjgyRt|Fr#UvbpiHA4Cd{r%N6N_c{f7PSRu^qNa2*zJ(tBvIq3$<%WNmX7ggxi?CVh|YN`VMz4rPRk5bnj#!3@WkxjQ&CjEkMk0NrG13vq ztj%>xIR2)b7T(Gc|3DIX20slikqeKFQTV!vnDS<34J0%;A;gtFp1;)q6`mc?cN-rCptWt$$=8Vbki&Ty8`x&K1WoKnYBoy((bBSGprq_Es zDcCh#DeNTL-42$LQLt;U1F%j%TC`3L{V8oBV_><$80#Dx?XoVgTo@yk%QAxHl7E?E z*+N|q3LW8AjP%X)U+ACNc=kn4Z?6)`Jn1g)Pi1PK@BL=`ZFVpIeF4b5&VgevCdbV&TZVnCnKx#q}qYjh4Gg>gZ0@jHFwW zf8Y~Ro72XpZyd8FV_)WN+1WWBxo5{o#;50%=g-Q&G@-gMy(qJIN$Fjh4<@&i-3VpR zA1JT>>t)KPe?}?KbrtKsQJ!#>PyT4BP*pr!Su|9J%24?~T4w(#Wx-GxD*u6HW97q@ ze;q1AWvC35p)yp4%1{|9LuIH8m7(%S%G5Upigv_M`Hw22hssd-UtOBSa<2~Qrt22z zzSd9pe=M8z&sCLIdsUZKKUGsyv#2(-_O9ApwU5*utbMljXzlB@@7I1-`(^E!X&a_T zO>dZfYR2dp)iWNQ*);Q`nf1QbXt|flqG>kk!gX@U$CM|}%hw4Y)A=VkHh`v#*vu<-dsA2T;V%-zweRwb`_%^XF z$WW*YG8F2*e#-ERr0(rTzYNosC?|Cvw|+ldPvl5lj%iEeNnHW;z{EUKSGvs)B0LK9 zV7LBIvbK`chZ9@^%m}wRQEv4ZGGCDAXi{$_^(0a!@|3vIFLA@=}fD4yuc4Cq0GGvOyhx8lc}yW+cOinrel@QCjG0g!K-}PFg1T zHo@%q@M$D7eW8>>s}6ciR44RQ5a<@b>%>zA(7OP32f*O~t`%UKsd}i_L%kiwY%V;U zi+Y|)huSDtD~nPQ8VmrT9p-X?*8q4hjC!g?oI43xP0))+JAg*Jizh4xGvTB4cj;*$ zvf!u*(5{0K+-o3`7|f#p$0~Bl5iA`rT2J`Jtp?O3wjL}r+%Ho%D`Z6u$ZNRe|cNBZs1gXP!m(%=9ec-#S0m|>+Iq@X4<1&*9t z7zj<+0*nMBrn!Z1*YIyy&*d|72VuoF)ClruC3rY09_E_~2UeHd5~yk-E4z!IEP#t` zU;xq-pzQyzoqLm?AzT07V(VAh!GM;Eh$VEwTnoqp>#+ffS>!2+$leO{m*)cOSGcz+YGc&VeX67+7GoACk z@9n<5y83tPZm*`gS}mz1wOZXZHB&83h?T*LB_F$G*=%n@n)F4r;0G_+#|PSSL#0Th_*Y53R4vo?o|6Q&}_C69hMV z`GBB~K`ACJRN1pi^{8=tC;mj$9;G_x;8_CJ=O^^CE@Gewaf2E1Z3Q9MN*^211KtT1 zoZy>JM!LhdMTORbZb&#bgwNLfz#0HJGtva@y`iTMjT#CA(t5ehsPz*f(cvM;(q~sa zg263Oc~dlV()AzP=f?>>)@DBiKbey?4gD6)kvJycwIzvM@DgdOOfIt>?`Uz9Q{P)l ztLb!OPDt65UVC(yW{xt4KS}Wx-I>h}d2prbp58P`UHt~}OwBLg;KhQZ4Ow#hnVOVd z*1pN^@Jt*6w;%|+F;;#0D5tx*^3%#ReqxEMA4Zd1B$KbX3m;0lV1)U50bcqPh@mgK z3I;_<|qiji1nBq^8 z$r5j)ViIobgI?!Sbm5Y01&DT`Nz_k_c0i{uQ}Xb^0{L{6A$@G10q8s<+1%j|O(-Em z=Bp`i*CU|l8mba=C|~+PBt;BNpid|!RiQTefn04lNG>H=;m4@K7=VlTUdpYyEM(mr zzGA#5c8oyZ;%THXso?@Xao7v-ei`Pjc_gPKkFZX^*%5WSgrK;R;4nHpxLPD~N+Rl2 zRmhEaI%$!MQs@b=`dz<36if^!3Q~a9=n!FL57)O$6Kja$&ofF;`d(Db(fkzI27Dg= z7wXV)x(}$@^3sHYkY(Qp(~g2}LWGjFEk;C5y{P!*{QYpgDSY!KERy!l45nE!9xCJu zorho6TAGWx^!^3rX>wT`C0gNA2Vo9~NyzwkJ5VC1ZUT*;s9XLuL0Q6}7NJ%#6}7q`11Ip3mWQGu=Dh&d!<|c4kWd zVXDL`ptS$!z=YM>Us1Jn&cG_EJV8%tZF$&uEL;+Svygd7vf+_bmtp@{)ug00nWH(x zAyz1IAr7mh3>TqdZrP|}|ET;_ouw_Hck|KC0`N90Ypo(+>WZplQZ=i3teWK*Q$N%b z?|^xV8QU>rwy(0b#iVS$3Sp~W+PpqF9E_aDy4xtGjA>}*PXRjPainHJ4 zW^|*L+`O(=ojPFS5F5-`KbIUP92;$l=E9ykq+!X}s0#&+kUOMJ*SH)z$HGVe1_w2+ z&4?*!4y6ifX#U`S$i`{wAFm>(D^l7$&@WBDuu_&(=VuZ-7BQtqa?O>@By{d02%)6M zXqQ4At|lY^{&SGTTE#SdsV6@5IJK-Csx_pPiOp=!rPg}}>vN<74Ko?%n7?qlX_=BBRFwkOLd-uqk)VDVe7-rzgDf+7)>zi=JnnA#nOOlR{ zrEj_^>(cTl!Y8lNW5Q{>;Zaa<0+-aU@i_C6gq%3WsKwd6aXi z+`p*1k@3DZ|gtD^k&e%FI$$jB9;TDMwFa=Zr_%*s-eHFUU*! zHk7`HZreI!#6qlU9!}8|55~A36p#sWL}H*l=%-Dmv{9_IqHP(>ID8S-Se=ww-i3-D zZro9f=75iUzu6up#BRj;{G_O}yBK^6zCi$nT4qv*#m69wwxH#k4m~fop;yHWHlaK~ zKs8DJZNJREiild79119z=5ROI+y#Mk;y`us#SCBA)LjI}sE@$R#g z+RzHLA7L)^GdMq@Qu?5M3a(_+%Gj!WPP6t=dT+Twl^iH*fIjLMN-cua&$dOta{ifjiO~ZBRS)PwQw7r=hHSsj_GmvVNp8>-<)vm!NE42lr$cITbPm#f!o?Mn#i79#c@_6C)Ek6^P{~Tm zNPiRseTDUE!2O+;FaGSx<*DOprK1*NbIIj9aSre zJLm7V)i(uqS~+Aq#9o0q!76M+O3$#8^g22-T6=K~VVmUaG?|{odwe!CS$~&pdMdE6 z{JvjV4|^{jSkd@?-nLF-WO6$%S8zc7(XQ-eib}9J8>0#7+3F^X)O&a4x0d+ahfevt) z`QTV;DmrCN_B=Q0GAUyN4%=e-JI&^RX(WMU$(DJD~E`>Dgq#XrW z?bbwz;wZQCGw^~v;Bfc&FQKH*1*Q9P&c0z+b8~o5R@xjxZ(udErD2PRL0Z=-5(<7v zy0+TNUGu6QzGo#57y=Ju=6PpvIp}xC$4hEOW)<=J8`FES#I=_>6@-9byz14o%B+*?&1tTYNN&lVd`-WZ@#;;orT%|Iq32KSV zF0xCKB>O?b?dv^7vl!}( z*K0)6vR8lal{q>5jTV+Uye-r_ji(LG5%w3#`jgLc373(aHIDcSVPkCb?tM%wm!s7K zx~3SoF1wrUtQKuWEBCK$N(qYXwscG9t5^M~JF1eFB}KcPl4;7$JiXTy2a~amhtw%G zb>|3`=i%ew9XnR5QHi#hhm}N%9adSTeWtI8DTBO-Yz|x59mPZYIp+yb8>Ve@y zBVTsSifpV!p?f7s|eBE{ZjuI)&lW@-YR6jVQ zlF@d(IzoGE*~;(1LFD@M+23A3GBdv=@B z(SgQohoc0oG_|VZQ8Q7JzMlvw6yZThr z<5!p4$KwQf5BFfUY7NKYp|0Syhr*^1Cy%(Q!`#)ua8fBl3u0DRzUl`LJIYJ_Uv3U2 zCfJDjVVfgRDQ(-*+0tJ=a^k#Xy5uUDBJ{Uy+Fv*HtMg-sjI|r7=0BaTa;GYpQ?ffv zT(XzS+04WeS^m0MxoypDnc7~!MRW~uZr)kJXneEr4u7z2zRyQaz~yC$@T9j`#JH5< zt!^wn?&P#*GdeZyZwQmFUAdjF-Z-U5xJn_KSRJ*zW#@U%*F0(b{S!uC!Y-`)mJp1r6+8Dw8IN6N>P<5ilL(uBRSmTVQmAc*4 z|E{N9C@*g!H+W&(beJtFA-?}OTi5qw<{_aTFcif^T4*6Lom6*ol6P`5T1QL-(FrA> zqB4*a@WfA3HLLTIGrfI!_DP_oMwn~;u4@GM=Ct!1tnw(k?9>4W*{$~Rr6i==tNL}k z8~=x!B1)#DT2}WMq~c{%X;X6-&@-g#5VF#{r~6r}2ko=Ej&Vkd87Jc!Qx2DJE>DGX z$JOHRkaRhWHqR%wjZ*2(=hY?6K&8xiTt?{|r^fpSN6*eC`+-wiu$t}6YjVXGbJc~C zLTtRJhat*WU&jE(ksD-Md8)rPD6ufJv!m9nCDq z{#O?w!foF?Z+$Hu&%*HUBG?>kM=MBJ-O(}LoCW$ml1z#-kH!%tN<{czi@3WGbm--s{cIELAv6wvXOeuM7tLK z=z+7)`LZTX`(-(e;uODO{!_NiZQ}8n zMaZu?CWrfvH{Q7&ae|3+_iNUslW{kLrTdC)yobRjw}~;*Cz-aE+9Tqy8XNC=BV*#K zO3QdJdmXTjnV(&0_j4}u8@Prr7HHQx_lsQ-Xx-f9Zrc~;@DS6K?-g&7L$_Vz^gHu* zu+B$n#>+=}mFu!|L>y)qS?Ax}>a6p(wp$x+@Kjw_IWBi>hZ@t@->-Q(PFoGez%?yX zJWq^&SiUc2-YwU1;g@@G-=vdyww&(9Z}HMt2%~vob@aV0t}qJk?9U@So6X&ax2HVI z@dURUznrjk;K3y2?l@Ou>h+UqZ0AEd?Xo)z0Jh1`DFtoBk-melcNGM2!O;jmmJ#J?vEY&@=npPPG} zlpc(+W_XHUA`bDZr{3{h2DFM7|3Jk71EOH#ExtUiWQdcVzSu9lf}+MxpS32YSEx)z zHVDa!CCJ%y?A&xqizIL@rRH@$NB@RAnjKea@MLi~Ea!x~*+%YorhTh#P}a&N{U-NI~t3`?RX#bKS)Gnv7@VmNe^xh4VtjCT^_D*@~P&CHH>5 zD9dI1_n(s&NP#XeuCr8E?@k&g*bShWIOz5ZD2nyy^BeOJ(2^m-n!i| z-w&h4Of`~*0QwmZPhA<~k($RT9FV8d4x_14d$~>r^@ix{Zsd>YUFoJPM+kG1@9Lw; zg96PB)~j#=aFpWg2`8M46Jej3@AP8o5qgLRQp$dbL{t-V{?IinsPUUwjHM&i=bjg1 zzoS+ql@}Y~v_n{zI=>ryX7^m5xNgq8Z8C@OHmqX}inSm%y%g>hFWc(~Zx(L0V1FD?D%YE0s=H|(v=!MsSo!be;F0JZtk z+A`V8#NrVt`4`hu6K8-+2_8J1Ev5Zu5%ZwRZlTlFb-XUGuMw7{>hRBWX> z9@LNkJcVvMCT_aT3kXBlop>*Fr68q=%vNI+$vH(SmG)gN!U)5o!RmJr^)|E13HR@A zB5dr{qiK$99?hsHobOLmuA%X_S)lmF7erhlxOdl}3oPTRncj4yUyz`xpHU%CezXRNR4u)%powEXRGxrX)__as zexlwFDaLQVzwGtEwWEbuF|=xO$Y1`>rW7hYU_UNipl0cEH{YUduj)bQ!7=4dQx2FnQ~mY23qSzCAi;YI`_l( zrfQm*U$k{xO>mO}6EgHo32dHN^Rg0HN%g7kp|O*Gto1oZS{JPgWa6l)On0Ea5f*Wg z$DI4C{Q9z|P$vU%4g>AsKzUg7q7E6F;A)~7^@?^=RAXaPb4BaEIa4XB~yH?*jnO9%=uL) z`l_{B9am1_QQ;8?6nn?)de z746myocDO5Bf0 zxTNBkicFHOhubmkb+>49)I>z8iQ4W<>x|Ae$V0lUVLI9rjo8A@u9Hf1qA#~R`6a)~ z!kK7vlJ*Sx=CJuFpnf&u@QH>F{P-(+%Z%zKopm0C744+p|^Qz6kX{LHk_thI8 zn!YgpcUxUGqjy`?8$Ui-v;?{KI~xnTdMMH=#_yEb9WC%!IaSNMGke(g>8!Vq{8#eb zzI4@68Uf{yARP_LfvEpH`~34eBkpZaFQtQZI+Ctpysc6t^xb)khCkh$!4<&X zC@4{F@60)hK++B<4Xr$4q@7+=0wI)@_OwSkJ~f{6DKjRf@pAN9AnDKQt5HuX^BZ6| zjxhDeKd9&eODax&7v^%`hK!s69SetI#JPJ*Pj>8zXPLkq0CxJ)H;ml|#1jzpLT{|4 zX<~ISBf1HJk)G|G>TfzhoWna!{tVONF|2uJk~Z#wQVPFG%3sXtYFkCRxibymKkM>{ z3X4%oq|bCAFDq+bIB?PXZTNCNIC*c>1%!S492i!tu2&19NWpA#vwQS33@RJ2UeEHgFtpAa~_~|IRGv5vXW*Wa?JfY8w|Mt zvflqlbm-0Xq?xG@#PK{H#p@BG?giXekBH#~6^AQC5f*V@t6KQkzGM&}3}|cqOrgaq zi`iHGw4)$1l;H4 zN%ZU1te8d6IYgoRQ+Gyye9Ml-kRkR#y$Tb_jr{h%q>A0uHtndBn!cT_D4BZX+FP!D6LZN^Z5ETPZ87Ls>9m z4%+CKMt+MCfQv^Hs_7uNH-acF!iY(a<}NUuRl^)Tsws}y?I+~QtT4IdLr9>BgCUK* zS4B|3om_vQtcUT-HwpFuNUVct@$x-qR6m(-44G9;sI`LWPIGH^yzG}*9{izFV2Rce z`513b67_vB7_^2T&eSpnm;w_$#ak$*C^IDvvILo;Z@;xGYihb0r9h|bK)6; zGOb60-^x7lTR~c6JB_S4Q`CAtgeqn7>jNA`UJBhe4fuoI;wNA+GLj-|Se{+{T8$&A z#PxR*C$Ggy8ca$U+gNce$D{n#D?kphjvoC*kXEtnfwD&e-C_1b7*z=vSlK=R zc2xov7S<1dp7}2n0V5;hAJSiqnVE&)f71RXijn0nS%2gaure_G)&9UN^z;O*9}@rI z{zzt~qx*~Jk7TwFZGZ88;QwU@^M`4FF#j?0-!MB9)4ytp_h-7IW>LI1Mz zL-ODD{-x>P_A~s^{L%hs{?q@_`ZwR-{hxLJMgL>rpLrj=f9&|v|GWKh*B>vj{P7?? z{fD6+jDJAJ4==JZ{^dnBh7Se?26X~;f`9G-{eSMkAH)7^$-h2-miagQVcZAp|K$e! zL(0G0`$zI$uKifVe>n3){vX_bvkf1z{+liMhqgb?`cJq2<*L8k{a{(W};j+=k^`(HcsPnmz>=AYw#!vA;L-*NGG z|Nj&p|Biu=!~4%e`bX;jJU0Gt|FQSa74Xmgk3D~If7-v+^@sWgVq;_f56T}Z9RcG9 z<`4DnIe*|k*U4X!|LZCH>uj>J@{BSJu4F(4-Y)d-YxV%YDUufp9{$8M9euQAcGLz(Y8dLS<%B0{XU1vJKVDCE69n_VSs!xw7 z!5%YgH`tLD=iVFVZl5PoxvshPxpy<3)?@L95C^by9o!C-&3B7JupnQ}8X3=$yD&X- zY-atgN>fUKSQDdK5)^4%?OL1lk@IW5ZM8p38$5of9}Asp_|=f9`@A3?DK$Df)_2H^ zE;hho&hSpowpa}fIL>_yqgEeLcGJ=x8Ir72JR{dRL7%O5pIlX5xH#>L?KPSRxr1Wu zMfNlt@W`Wz8aL6HZgA*h+Xl_!)iImGr5-stzQTT5A6;zsC+D&Kv9>(#W_y?V+fvy1 zJZ7MwrBrI8A;nsgVcPIpx$3mjns?&mFp0KZI!y^yZybkDkR zSh@`=-aRy`tu|(hvn3bx*-S0Oa-lSURJtqNKgiz>Tq7p+p9gbmYP~r<^nG(8Ry8x%C?xpqWLAdZQ790KNv}pzNdz_O-MmSE+h77*j^2zliLX3^Qyy`nYhdk`^J{y&hvh>vOlM>0 zM2ZT0dwq}QHvIX*jiBVQO`RYY3J<=C6P5ZH+B#k3tztl_BCDhPykb_nRA}?6=DN}N z@dU>hx+ttX+rq~3kgbT7BzbeZ04JJ32u37)=~BEs{1$~=DHYW!k7oFu?yicewt(|u zQT?tu@zdz|rNfymmE49w@m}apYJ_g$_i7o6shY@6p&zCo5yQp57p-m-*d zp}9GB9J#d6QF|HgW~n?@tp0Q3TT?}Kuci$DHA^nXmol%<^Fo6vOTU!8uI%eE`v~db zb-MkBoSE*Lno zuO5cc3+=i{R65K#_*-!{-^8T!MeGIS7UhmDy57vL6kFZ5wBQRW2Q=4|i4=z{5%1mc zBd?96XHB%t2ICX_`+tWoky_RR6;1n0y0^-E0zGG~^cJhtRAkEzini8G&J1cWGf=3% z&eER}WE&(#BL6BWQsEgmExn9>6-`{7;yJACEx7|8HWsm2Xf4araFYiGD-4M@fqip+2bU8GtPvb zg??fADI)U>pVwZozF~Pvonbo(u$%aka@6g20(AZt*DA`*gprbb5z|AWA^Ji6$oM6X zsmh3m0I33l(DIYBos`MMOgYqyFGZJu7!WT!Qxk1(>7)j?V@1@1zskQiT`SE!wmVoV zh^Q-l&G)p7@=Ilbc+`^(Rte>=O;{E`+`@t{dr9z1-odkuj>x385jY zT%WtRUW`2heK&tCj}goOWVu+I4+UDqXRcNmmWWzhMZAf%bR*l8y))oxx;6hjO?kfY zRLm(kh1;o!+d(t=eC}7SNGLoG4xh=gBV5IRyL#z3OcJ=($+<9YzxyBgjr=E_x6EU> z^~{7WRe%!(w`;olNGXZYT9@TWy+0%z2ijD9ryma=63I|6!6g!gh9v)n;FAMf#xZ&daFo;#)XAokE);b zMW&;8s+zvG6gYy>)mvRB+ zBWfjRVQW*l!h;@L(uCm+9GPN+wTqV$oNU=>@Zo`~M&cbo>`!L8lJNE#;^x8VUA3p$ zur6^kx|F$SbezAjsh)h!sSirXpNfnOOfXXSG(#sTILhnw1RdCTT+MLTE#x=AB0B{Y zS%L|Sf*Yy>+l1+DsP=HbW`EQWeIM$;3t0TR?xFKee^2v8UU9~{jfjggYGK2!ja9?e z(Jl%2?f{uWEBAstpC;ze?nLF)A24&5+9=yb`MS*!{{u9s6&#mSd^39RX$h>eHG zQXrh%pGJC0X9*D<$PE*`P&e-s=$<5TX%vE9OVoC;sCON$7Y{n^C+7?5s|7#1~jI&l3)7qvhlFIoPVDDJ=za5rx2}ocRTz zLh7fyi|HNm9yS*{>-LPr__3t(7MwWz%T-jb-b^txEtn`HKYoE)-JHI_Us1@oL2o zX4$d?op^w`Y+0O6GoW1dTdYnoKs?VV8c0lmE?zAfVnBf|HbpFy3xJoM6|d$C;h-?d zPonMTqSzCo7YSLC9gWEm4l$vSiqk0oe5EiDrB@FzmX(Ot2?50BClU7xQjm($YlK+K zhQ;fI0dn(;Vsd0dU?_(3F-iJAiek#f{hkJSbemw_;XAKGy}1pXQ@ z>3|YI2tW-69j#3@q*wM!3@{6@A#TkVa-R2~7}6+wKb3J;?G71=hD z{#4nvul>ieZKVCBvTcO@BeHG8{h_jL1pU{tC-Fdbii~(57DYw^a6y)fq(4@c>udkE z>`63GNtTPWKUbEEu)kN9i@4ucmW!bORrVx7ClR17yGqvIF1t$9FGb-XiX5FI7g7aq z6t_+UIEq;Z0#@>y)I&7ItWyDvV%DJmR&ncCKxY(Cn8G4TCl##Of z#Cv?2|3$)onzOQX)zG_Z9yqdskl2VzW67zu?$FeBYURALwae*uAUhs(gT2+A&OSW? z&53l#C98`AL+I9ds0ojx>inhcZT%B67PY zYl1Lkmh(%HS#VB}3}uWM26>DiMT#lhh;j;$Gb9)rM2aFwfGy2L1H^;H7H1*_;(fxF zlt;6U{G=efipqy3xGR`TZb>``ixMx>jx0!q1Ya1_8x$aWE1QFo^HtC{IHb6mFo%D3 zm@fxZ(0^7LREB46gy={bKyV~7n@FBmgvgvO5GQGFfczNjA~(ZBNIQ3pbW1o)JWODN znlCa<<~<#x7JW06C7%PZ;RucyN*0V0-O1wOj1EtO7Q7Tx9Ii!54hE7>2Gx z-9*vHz!y9w0n-dxI#&`1-?U6g5Cu{bc{8lx`UGQ9P-Bs{B z<{CF=1F2o|1|7{gR@M@!9q$J>@{J0yJ?Vi$j?(W(hADADT9kG^p$xKR@$vnj3=)@s z17pf7)11QJkHk}`f|01FU!Tz8-NIxeQMkX~(Cu;y-Vm_k6SQO+u};zC&`>(#=bw?Z z1R9Y|MTm-Fk)!-bhCd>PL0yxy!IYvGRZaT@ddQNAi{N42~5-HvfeQm~Vt zCHep=$6RnhPIp0uY5_%Ez*z}ZUC54P3Qc~?MoeAUj$(==&XK;}26@Uq&JlF#QE(sG zoP;A$EJL6eA*VKYoN)Pj9XqfLJRQrL1|*%ojsj=~mM&$d(uW0BFOv3o;AJ+*md=Q1 z3aX?d<0$=m9SzV7Bwb=|0j2z_iez0#QMeHabGuIoEtyF;+cE!0QFXq&4YUmG)DOYX zV04-Gk7%5&B`sMOo#i7r{zowSW{7jA3NWQYafhZWt zHWM2#36ip)?IUiHMLZ|@DA@^sNZ?dqY<)}L2!;!foNxTz1VK&8@MSbjzI{Zn41$b9 z-&X^Q(BIPLcLgl&vq_06mq@ocF3Eyef)+V;IdwtqG{4cRC&2TCPp_) ztZZh%IM^%)p#%Yg%jBoOlZ@?z5iuZR1;fe9N|V^{$Oi)mrTTo<8X)n!dmCIPzN-4} z6WrW)fo^g>boigscCmIXbY*V6qh6q0bqGGeZrW}#YVp2gf@Kn%M~ZsFxk0>uKG11r z@83g@WgvQOfObOOb>H(maBr$_;^ujRd*C?1I-xody%A3wUxIU0dTWMTUbdlRZaZT+ zK|l3BcfG* zZLaR$P$8(Q)aA%-g=Ixy1#Lxdg?ywQbVoxceU)aJal$^)Hd#C8R=cluU7o=>XEuRl z5^RV{dzpr3f|%aW&TVI6Bw)0C2mf^h%3^7AozA0|0;j>!$yI=EN=pM@1%pHXxn*)F zn>o9FWx>yZD)m>aje(Vu(e0hW*R_%z1@qEb-b&z#(q)K3n-!YUBIPsXIpy3kx(c#N zv%64Gc~VV|yO5K=g|busGZmo0C$+T&f2q1>trX^qS|{PUMm<;Ex7il=Uq;3=F_SuW zRx(39Kd#wh{v=lf!e;u@%ua~cXQ)5Hydot1P7DlN8^&Q+ct6NmZ!VQ zntYzjCyk^1;+8xulZ_Y31Rd$if+8vUhPYtH5+<5b0=%DP;d8OTiQ*de=CMO-%J@vq zM}qx)f$p{SF z8!8J=8Dti;zROJuj}9XVd=$z6l$np&ijWTVGbp3iVHcAYB^_iEoCFvt7^>H07n{~M zIw(wddC+)1$e##dV8UShS#X%3X&`W)0zd`4WwRhKKOTjJcoS+N(IH`i0Kg#mc%nYt zd;;U+&7zHhy#clHG9!S!0B;Ay--P7iqa{H74YJC|5e3!cB~Jip1vbQo67`u4gi{L~ z#mlJ+Mj3<==~EI2krv2}mxUfHj{+YkTbD9;U?e^i0_Z32b8ql2$F9aMO)pEYN^f3p zPp}TK4u}pAo=-gBciR#p~Gn*vr)0)T`9H)QifS%IhN=jhDE$xL2rms28j^tk<>owO4spa~C_` zCo2{!^eV_jxU?Z`;Rf3@&xq>@>o@js-oeLxw)ZdlncFY_2c!~65a<_rFCr+bK9C5^ zE@W^v9S{+?&Hs8&kyVr_VY~Wj|!`8&E9owsFE=!ox zet|DbrtTR{>r+`oX^l_QZYj;3@|I+;ZbOo;|)2Ii4* z4^)6ZK1=eOKmY1r+2jq=1$cLZ}{xc zkpA&cOKwXcVMpj%ak*NOea+{UZU-HbbN%~jnz46XXU~yNqNB(!mnS-{ORtMx5vxUz zjPZICZ@4~h^vay$jfor%E>=dN=ab(;xS}8*8Q;OVCU8&SiI`O0JpyEDs}dJSPVDgz zZwa3>gI9oeq_PsvJ-{xELxaoe>?uaaEmUV}gkCgAJ0(8pJL(x$$|jp8i=5)la~0&oP*@8UDf{Y5C(FWBs6rja-=>ws3O%68kbhqDGW;cmrU-Hi5EyN>O z58~mJbk;A-o^O_xdaoJ7OQy1pBnMW%=DI{$7N5K(Ohq3`3`sC0qcF;)B_nKSH{L>j z>X;^T)2uLkd_$R|Gn4w2;NriOcK{bqp;1xi(d(vbxEyoBAs(|9t;0adQPzxRi(u|x z5X8ceCS`IwDLAl%yY>kVbo9;YE+y$UtKC8H_%lgI63z#z~dQ%M?PWRzLDdJad-`bL-Dlv&LXV08^^W@X2@NYm1k~{uX@bg0I~*P}22H zvfHp~;8*Y&tHl{Vbwu%X^sD-2We2kuG%m!nGVdbr4>0w3bH3!00pPHAFqyKfXc8_h z8@4!>KciEt(s=hypvq@IS#@p))ryvB(2~R>0+kuQrU!UYEX5`qi8D|Hekw*cG^nXn zsu5!6N=c1rH>RaNC>Hh<+|r=m*c z3mTpn+S%Wc{p{6&{7R)ADs=8}=eT!kZGU;DrFQ-RW~-rbPFO3-u6zy*Xaj_%B+o=G znOV-Wj_d+Ln=Q2n6KJaIW;-mia_i*bx*goCn{5UwQ@bzT)fxeX9aBj=l9Jb?*zvvl z2QSTwr`AVXRA{<6tkh$%Mx)mCPEXHOM(*}*S6H>@8c~{u0c+xc!L|fF z;#nMDQUTnlAVZoxE)mGd@pmq%l2;5!n6k-nh^0mMpYC+P&nb`GMz$IItVGTZVCz0JQnKTc`qkq zDy)Un)f$Pkb3DLEG0FVya3I5!a;$UZVKEGTO_7+KnOTR6BiTDP7{$|Xvk45Icjzp; z0=nd6ai{psOG<5Csh(kYfJ`FG3?q?)%T6OzhlUc%G$8yu03^l0^ zv&zRQApWpLANz91`Biz244xl!#)P>%navhhFb-*cpGn_RqgIW%EmRi7idT~60la`b z_C+gX&4^m8bORaRif?+|DB%frZ~Vm>Sl-Vyh#ubCZ~*Y`Tr{q|hHfy*YOuew^bP)& z|S4A#hy|L zBk#thufIPz=9&zS9l-KSf&-UlmQf;s%}gxRcxE$%hhD&Ls*f|Dy8bf=!ZTGZo+jjR%+qP{R)3$Bf#=mXbwr%X}y}P&ee%P(8I+aRtDqoVS zB=4K^dkzy#d|T80hX)(G3cdi3xtn`R(NKB&UeXb0ZA(izO`%tj@&*Z%q$Z){JWTOn zh?ZiC7NqW00b0~`sG^O)?dit1Uo##AqQY|V8!i>Qh|t!pLZj3|H_1sVd|HZCQ!xca z=?1*nQOxud_5Es9H^i}V^pqNZ-S7pHyEr-=Wm=cH`_lMWKo)LJEytO%irG9tPEPI( z9rcTu3fc$A5HWr$<;*R{lWodK#bB7*qpX?w5o*w~^Ft2*gx5w^w`Mj?Y|Uw^7fQ3j z@C(z81y{xoKz*c-xMrUE{53LSx{O1^S~iq!;6iS0!-CPm*RP^dFGhf>FV%3-VM_`@ zEl|O3R!h4*J=AWX&e_m9Vgd%|YD|XhOgiMRa*4gu3&$tNyGNVIv&otv?#pT87V4GC z=_`-d+>Y1Nnh;%dObMW|Re~dODeJU}0?!ZNIZJuXffX(e$-(u7WeZqHLP|}!xO;Tj z6I)+77?X)SsFYT%udE_>X=R(M;84d~zLD|)ZXMc3c=h(MK$p_Y348b_9)0L^d1%H@ znf2llv(VQ)R+hYJUj4`iL}GGV?U3w@zrdIw>874G_{hb|z9>ZcmMx@W+t`{#Y}oE1 zh$yOtbaD{eYfGw{ipyGRYg@{So2p9UGN5C?pb<#6F4-<$(6L_#*_jfg4(*>Orl)6` zvX>qe%$$_TjH|q>@R==Tv#j%DH@`(b1VcwFit$S~dv`AMjGink4q+WTc%o zU@10Z_1GDew>>->fLlY+ z7!|G=yAo&4pzoKSuw5$i3wZ_6X3uXj7Zw{|z3*K(X>Gi>dRXB!U`rd3t5ekJ2iNrL zswZhLHuO{*2#YBw4E+shd7>MRI_Ij1a-dO_)8w<<8m@>2#bCheV)a?>(tSWFosv#3 zhr=_#G`LR9jRSZd%zLm%9G=9caoL>>FAZmvfOk8o_c?*ja4AZ`j@rL?+wR8t*(;FT z-INo_8jT4o1N0G`(29P#brw_Bb~@Y4XM6b-WFs6clT3Q<+8*z`y?dCBR-K<6t|m^( zYk#4mq3IIye>0o+<^gN`ENr{=vbi)P?qvSuLsJlqi!AdGu}2k@KMzA99A}0b!b<>( zr%kN0ZuWaB0z0t5$We%W|px;X2v=>%Y3v1JDEX+yJ0U1>xzg$5ur;S*}It2x_zXEf<*)n?jA=FD5 zL~>zt#pk_TujCQh(w**_Iftv7+lK0zYC=-0L-^5S;6v-xv1YM-qsK#=f(g>(M%jm7>9SN^CtG~$mQ+`y z&GK3J9+&U3F7jFdBf=T|tb8}v96BuIKI#3B4sW|{7bu5@ZPEtInp~}}E*1yoE;y|Y zmS=~X^K%eCcq7$WTfJq8Dq5*!s-jkA6C#s|s#7{z95+T6hIRvOmcX)jyPF1mr%$es zsM?;)8Y#51HB2$N4sPJ28QngU_U6~u#uf%Ullgs#S(o`_>v^qBtSIVdUx=zKm_A= zFVBCp{}0!HRrmaW@c)hf`2l&?Y624HiTW0}!kRY!8~$xOgZ!kQ8Nk)RK4(L|oYNd> z+bC^M%gC{e)?s78Y#V&kpgU({Rh2k|*Z)$mYUaFw?__f&(~4J>R^2&kBT)s~YhcJZ0RgqL zDJ;@T#iL|1KCPVDz_MhTzANuqvYtYIkd$4{Zems;vzBhD^sncCHLrG0#hr^clCoFy z!G;HZM@0q)sN#AGr=tHOK;9+>ob{=Lc{X4@d9yHK#s{Uq9Qb4LmjDUs6EmiSn*?y- zosadF!s(v`x>5By_ZkY);6;tr@%qODm5l*K8&?4}L(!tk(*rp>AxVInn-Kqjx-Ar+ zfmp!yKoU>?Lkrc!jqR0>iF1bm3WM!01=M5M3ppRBzs71yFgpQKXwMEgTFP>s7?mSm zB7)*AodahK3337dex?r*6A_Pweg1n+VIc!0ObQ__na~G*c?#63*_;$o|4;l96k|^C z@IE6^9JHoH%yN00&cK`}R)3sCNU^|z_80`sUu-zpVyOTuLob8FoNp2 zKN@iyyJJ!KW?3laCuJXuq_89b)4tGbPYP&6Tm|N?kS2IuDx{|8A>`|2r}L~TVS3Xj zy1d@2b+`1o;x84x`SAU6VeCI~Mbv|0p`WCpx??~jhL6@9bo23oxk(I%zbVGixqqQ? z3#v$u&^)Gv;B(k3fN;YY6Ts6I!$eUct3{L`XjJHej4A{LJXz%6oW~JSQUj?ek3Y`l z`6tQ{>>9`fv>Ps60)!NqYHDpoy`>13xND!7~~IW8o|2B_akqB zSYeqw$=D;1e$RObI7l4vMt|}+VnL|Lh&G`)Mjqz4r~;Ue5|H>Fsw@=_PMIPr+HVL- zA&7*Oai?F^C5FnO``SUyrB(Xsklh&}?(*p+KpGhhQQN0Bpe;lUzfU*e-MDbpfc7m; z%NVB?u#MHwvI!`unL8LtoEqRvesRsmCD7xI8QeM=#h#s^DgR@vV~#PN_kI^i3#x!q zHI(Pn@GpKBy*5O9V_OND%<;beDc2yV*TAnX$Gu_&Usqb3{+WHYhgZ?~1nTh%g0juv zCK~`)X)SySskjB!%y=Gh{R!e;QxAHtSXHmFPsH@kknfN$1sADV#FoQXRQ6J#j13d0 zphEN7hfokZ0%iM>wGeA|n)S`UPyDsS#Fec@$&I8G(C{;2lr+9iz0UHq2+t#G>sT== z{3a{Afo19fnZ#hygb;!n(l`E3jug#HB5p%v%BjaT?G)`6s8`rUDB{75m`!;&`Y~H+ z$Ou7~1kDKN6u>%V5banh(Cu~6DE4L1OE{%hE}N?k4;3rUY6#B?C1=DsK!L2}%Pb1- z!mTi?QpDf!Q`L`EaDSkJ;7LE%=HyInhCIHb{A<))S9w@<&ex12yV?1PDT}7eNrC*z zOaxcSp*Vkr=NBX!w_9XE>dciz;yy?@{B8 zqb1eOTXbjEwC~5dD_bq&1IonvaVSj9iB3|yQ$cmWEf*F${3Y$Gi zGK>?OG!m2#IgdbO<{sFFr~vXP^s`W&XPeUO7abMB8kAxPD~Z2oeHo;LQm+|tuaxlT z+ix?;b@w8XUumVz$b`^@W(d_Z!H`#pk~4w?LT`q^i&jGt>|sLliyI6e-sB)-L|^8N znNifDAnI7*tTMvOsI%-r>%#@Q-Kt5Y|F+iIidl&GJUmF>g6J01d^bt-cNP1o72jviIWZW>q&kiUE}EmAD|YHQ{{(}TuL9cRs6vhelXq?+S*J(_4l!F{tce$jbRG_uuAW<% zdLV`r4Hh(46J(&N34?*4y6?J*x=2lPCJw00HrCThnN4W z5(erIf;|-(2Wu`2rUnf}hxrk=xk9@7pF?v(96=`3sMEtjpblO5U*o~l;n2H4WJ9nJ zv7}N};DKXJxbd_s5=6_`2NjtCE*zMMMCHArv*0?MU3m~CD^^2_>55fTA)bV(*d#2d z>e`^8WR`h7lJpA>hEAluhS>tQ#K_eul}{R;-Qp{BjPeyJr<{@lk_i7|dKqTRSegxG zRE6?5@dmL(!P6pe!_`ysRe^&*t2|9RaHGYEAOk>8I7KtKA=Rt=M}dioLi=Ds^NC1% zn?12Ig4Q^Ujv^gT9_Ak`pEIsqzuL>*2HIq2K{Xo(i~OQ2n#nNE5ZI9MPt zWnGMLIbg@dvszmiyeI%TLIkwh-WXVbYZv4hOqxuy&8g^|H(8i2g}o^!jTtSBHV?s` z!20l%jS%*?iM&i$BAJpow&t%xGNjI5+|FURSBbP99omQwi6YEMTq5vpu)osT*)o4) zT=-X6CNn8vX6;Ih1WFH$C7MmJF_&vkDp;|A8ZaQ~BNRGC2-q{gz&1e=rk5n@D~(-% zMI*y9$qKW?%7bR-S@h~E`VWRoZZXOTE3yAhLO=+g<>GGg@ZatQW3Gb%W3P)!k})268K#o&?1VGT`u=d5?(}!qv&gnf_S9CX^moQ&AplT7-A8OJ4iKzGe57G3}Y{t z31vAN(ypBYEMPK9n1P!YM-OYGF38OXX{(EeTT}>UjtTW^WlTRp)#^}L{Hv^NF$9vf zTt-9-8#+F^48s3|PF#?bOqA^9 zBogc|WBBi1WJNF};PHPM1fU;%)g74z3g&7GI zNPkNbqlo@xq-Rp41TyM>4O>n6ZQ#v1hf@_P2jrpyD4p2I&Yj+*_f6k(exnffz+eG#vU5NH0H1Tr|e8L^u$JP!=IS zR??fes7%oH@gR(=T0o=+FEMEHRsM>| z_1*@!nPG<`cZJ~QDj<2p!sboRUq)c&P`3v`e#m3}f8EQ(MTNZ^pq$qPr4$+*Ec^@- z{AF&%`bo{>C>1~m5vQP4NePMdb-MfuKxAZCZ=nPOzLH3Vx@qGpS6j}9crWwI6eKkfU#7S+G| z&sQ~YNVnSnxxP#{<|D^F(QFhb5}`6~hf6yR3rE;|e9yF+fs z%~kx>OaKj_!4fv3??c@a3EK;qi}?u)j@}Dx!VHLh6hZKsDIn%o-5uaA!Dxvs_o~G1HuTV&)Y*p-&;0f z18Tnp)JoHP3ObyKy+VhZj)m1}1cprxgub&~3@ z)D_3Ea-rp6*?jWzR18Ra${93NKLFZLbnlF8eHEhd_%KQo%UY)bc0(Nu@)bpEM9C;7uvTm-OnMx(uzREB;&-*jCo zt@ZEuZJ2~6x52Ay1`q9f_t!OtXN@6^p}y5Zi5D3@#^&Lk;q5+bw>pzRHFr~ZZ<7!b zk(_R2nQmN6ILHah1K7%%mbyW_s@Q&v2y2ne0iT&TYUOn2%)-L;^17*&h=nyUy-MZ! z@Gy|-KpD}A#kEr%*zIQK*${w!C)x^o5+ z%U(OE?GPoyGcIHZ3>N2fGl>l?9XAg%)-^gNRZG*Ab$H|LzC6S1{xiViV1D>#w@G#uJu<8 z%O+zg>7LCI7t-AQLNpjO@zNE_l|`E^MUc4lE)F*L%k(RmkAV-;7_IMb#_fveaYYz3 zY@&u>xz-!w0UN=Wk|B;FBK^XF5P`O7e^O~H-nB@b2(Na#vSDfHi4+E($av(pS}LI0 zA-3E2{L+3{e|3oL0;g}gSZBH-uw}B7S4renm z+D=eHta)^2%VjQwN_}cNw`(S{4LWA&A@~_SEa}xko5Izq6Fg;C4|JeW;1z(iveQ4i z7vQ|>ejRsu06XP0-%)ui*Pb8uf0x@G#L&XOeRsV6HiZ?h9K}e~E}Q_hHq*z6m9Dxb z;rrEcalbh2W=Rg_v(moqvmmtto>D|!N^(y<8mY zd3oG}J!sz`#B_Kz;7Fo{-f|1=dzVQmX{5Ds;c|UZF`a2CT#+?u9JV_YLGqE%e}C%O z_ZRA2H&e9I+&M@!kuJ0_;IUd(CvPr#HgYtakeUl97+2o{p1mYE7%isJaE#wLDqoJ7 z+*_PwbYj9PB0&0sBI&6JHmMbKBBzNh&RCOUJx&^v>;b|6AfS%Ua-?avGCBXebdJ^O zx#X%d7Lz%so4p)l#g#n2n2mOeHfBs3i@TFXT?M-*%Vl&%N*)81V z_7`E_q&zn3${Q9#$}~B7v+cni)#za2Bxq+5eF-e~tz{>eUH3)wMK^vZh4Z&jtIYgr z&qktd6t!g67k+WQPl;+HsD<_9P6F-cXv|{C3v+hrco?Y%dB+?yE-YZLUu=yw3GTqr zgVbN2I&;bG&({ty0pHB$=qF%OoGh`mxwbx|KCiC#NoHivu8HhY&5!oqYbs8&%UZla zB1k8=!}|2N-_sCwt)cfs8Ob$IT4>?*b?(n6+r-12`^L+aGFmK4=go^t=k>``lFE4M zX6Jj*#UghqI1$DG>)83N*9ACxk^M{!)}i+YNqcC{#c|W*hspT!Jm~U4ct=a62!8aY z*Pqy@Z14RS4Y!#?K<2aM*KNWz58!1zPPfJDr)@kPXWIJGoB7RUv^`gRx@YeBt2=&8 zqNizkNzd^b!|5E^m66;vU1ju%HMCXF+vj$;xAGu2M`QPp&F84uq53(Ct7{Nd;FjGq zdEzpU`37@4vimC4Fw>0;55MxBK>3kSTIJRFgAb##GsIdMiuN8n*_ON4*{Eu=e!0z| zlv!qeDW2u*AO`-%^Tg+PyN?3FR z@gf0XypT@zhR5+{Jo7E+7|sg)Sj~pWzP%|g&sbu3{bA#VS<$=2^=0gYhNH{~8fbe~enBqow3>P>z9mcdExG&)Z$v`1V1Rv*@t8oEsR z(^Ff@9#=W!iI1P@CjQc@tfq*rVHJwf&$k^zRS!; z@&l@sj_Kfm0@u&OP+?{n(46M%w4PA)Os27Ch45?Gt;^yHkH+KUNv*~~NJ}x4;&c7v zqweg63xhh8E_ReZ$*;=M>p7}EB7EKMy2`!#Fz_S2#;zzdiLa{GkEzA+E5<~?+t=Fm z{9XYxhBa1JVDD>p%Iu@fbq3Vyx7y!|P>SbGY%Z4kP22GrCeR$b(JV@Sfcth&kWy?} zA%mKePNtWA=|8yYKl0tFFdauJzLk&V$HM#oDjz;ZOfPDWl~Pe9 zH&>a?j4kM@a2(AsmmA zN28C=_OJ3KLXW`NWV2B+!|tfvlhejU&GsH3ayRBAIP##jt~kO5n%~#F^yd)qbssa? z`9CVYdW!jFReS_2oeLOiS<9(zy6qi)z5=;}PXc=Ok#;xEKdCB!!J3ss(Z){I(!`TC z7^#iNoNj|{{e}ZB+K=TEo*S74N6+igj2HWPg<2A!D++qgPl*!9kEs(5Dc+mSC_UBs z5sI_hCbd8#_K{Fq-kx#}tFwvZh+1a9rw=}Ai-o@jD_;eiJs0GpMPZki(ORPI z2$s1^aB!h|ySW9=312>7GGnf=y%5VSyZQi0NVT@koH;qh(^1Faf3AdKGKwq}Im$eN zJ&8VNKGL`?MFK0HGyv6G`fhaKDjn_rs`se!KH z#O)g`@KCm;&-jBbLu0+yR^#qotGv^2fv3Ig++m%`N^!M*4>RU?w}YQEG!1^CrTR#v zSI@7?v5QE95fdd{6cHE)=2FI6kR_0RQkokXy>N>Y%a~jk7-y}BWH?I6JYPik_bZu& zX^|wb;641rK;P=5VT>&(Jzj@?^V`*D`z;steP}Nk_iI#0(kr!D;O5nZSgMqI zq4miOH=IHnE}!R=fWqKw~J4Y-5qIrye0S`+==d(@9Xj-3Rnd=fI}}P*S<;byjmmx$8*B;#i`qkfp+^K^Hb+2>^hO!acHN4 zT3hw{oBZ9t>1kt(cj(xy`3phzi7TnzDgI>eSW*1NKiYz&MdzWX$7I1^to3aSz4SF} z(4{z2^{}>@Iydv+d*d>kp6d$4qq17%D5%7BRp(oO)8K~1ZG#+F(_YX0jEi1Lt^vnK z-uE`-e7d3`xBdGD?*$Rto4rP6XiY63(#ug%)0Dtb<2i-41zQYVs|ZIj;9wH9>=z@B z9>lNYPrC9niG;j@0=(b0{x1pJHrysV_-U_G>=TS`DYu~vR+k&PP*dOaZyKGU z23{vxw%n^vtlNPbABUu&AZLS2#RD5O?DXiqK0H>Z;p@SFGHuq_aQw6uJO(H;qXE-0 zJiMD*`>x{GbU9^4551XI)u^#5VzzjWt^}d!jqDd=$VZ8`K{T4*$WH^sjAz`*tfYP> z?SoXi#~F+y+%u!VZQp6unFIOu>r{%XH3WWPCLZ89=b2D7Bl`&lf}H=1%5R) zk*s&IBbDx6`Wy8TN_^Lz^=3=6OeHEGJJ)Lajw_PQy%xHA*uG{ywsnQnCXS7j>zE9{ zPoHk=_sNKCCk6$nT6MOrm)&eav>m+Qhg@|S)Z&asjc2?ZoPH-97QdNL z?B6_x6ZYh$jyBHk*!E0U(PWzA>^A2TJYNdJ7xv*}%RAJF7^t ze4K7%On`yTR~}`i?wvVk2U8mhNspZYvftZu1?Z-pH#vABX!$;bUvP#zzS_21;4Q<~ zJmjTTomHPHn{E;uNl?h0d)XvB8rz9#Q11|VuN|g z+;3cR4c3%P3;mH^Er_qm3EEdRC_$EX#VWcubLTAD?u1Jd(q6)#je96LTJ#`9{(zUo z(0s4+MK?wmK{*LO9#PZJ7`V_|>vLh+hr7RM5ANDy~kL#m91WNWLZ6eLYueoPcBz5B2Gzptq8Gw-@2_;xo` zpS`=^Pw$Dms3p;#3DU6%NXo8#%nhyyH~P~qTfwECFO{h!a8b^*amC5L2kA_0h$A&F zFgaz>r`}3o)fNB3 z)U+PcnNzJG7}}L}4Bxq3?_!#KX!1L_HtmnB&G($V$eJ#h^6kpLKJB-LuFOJFl<4!&logH!%LBW_6qPyFj$w!#PNa#z^4)cx@7b0}*)$BH!L$q!c6t|ZCE9+>W=~AR74Aq6 zDbo4E6TxNVm{zyXo@I#Xv=?#Le#L$j5*6RX$LQglyXoDEQPNQlj&0&MdGBm7`gnzG{~*A;Kipgzc>$;Ge9)Z8%0=EV792FJ zg9nS8KxUmXD=!;P*{N~FSd@1S*tux??RCEg$cWtx#ePJ6HYA$w8rFV9RR9@S6up#x zFkDp)x+%d7t7Ak_EwsVWq{81f-i~wDz3C4ZD7_3}ytU71?dbBlec9Y~UL-Ub321L= z9~17l86IXy$T^V!08O>Kscr%cJEpCFt>T^eTsNL?x)no|d9mEQo`)=W*w@guTr?I7 zC;r`=V-fa#9KGJo9wsVmvw7?U)=hfXwl9?`t^n_$v7H+m`JPoOfQhY~Dw)VFUM>v3 zI)~bygDkYfb{)T5^zbi&_}z8e|Db{WZ<5zPQsRG*!q}PF{=dLrER0P59Ry~hAR{-x z2-kh0cF2jKlu3_AsFEyhpor)l){l6K;wD;PkyH};?WO4T>u*?+=*=77MnhsnPi-|c zug6`GCE@L~`jr7Ld98o$t~D(x){1yAY`@x5wb36|jt9)VS{shKg55DIxR|~fZ>QM- z=o0H1qC~y0`~aSn6LCJtQmTN(9vsPxKWFtWp!;&_buc@smhLNoV`< zJ6OwsiUrc!`>hy<-6Rdf_aUPsWJMVtFk`$F370Et#j8JGFzvAov~u-&M2KO3NtKZ+ zd|x^z^);5b_1QcK%pyMq^XU&*(U+4u`{-(pe1X?)bBX^i6aPQ4f&Z}v|4$+t3lkd) z>wgj1*qJ$**#G-NOnQ0zBeFI4Ew#5UWKSBFTgQ-3gqKh!Mkwh2R}4j% zFoJTFcvr9hfgTuK8!5OnDzA`u9lGp~U;wg&7^b#?c=`zhcVSQr!f908=FUsjHNv0S zQozp1*IlPrw$ru7#bSAdk_!(KssblUHZN+a@ykVmh8GF4T476gLm^H$RUOh05rs@v{zc$lHj_yI3S_ zZC?ezI}Va>=^iP~d6N-`D|LV{1v?+~8DjP!#wg-pztmy(1j_!Z?yP+iJIv>H^ z_6~Ue9xI}4&FGx|5}S9f$d@Qf!XL^(HypIREJ@if=TKSD)u&>c6VC|rDN$rnng1~4 z^cl9Dl=k*#S3no98VXZtf}prZxhf3GBM+%5oHDNzyMXRUHJxX~4pbq?bNckRfA~5@ zg)Zy+bS$PyJ7c~a<|JVK5Sm8!PxoybOcWflGbh=}D^s=J`^t)RDG+K)5SM_BzxWPYDIaE8S;`U`^{^AdOL9f!StV3sktmAkk zugBJ=8R86g%!%ei4p-_G3(pfafOq8t{q)R9G^gnnyBj1A0xV;9O`Fv$OVS?HF!cSch{0F?LX z&dCLb0W=N9Bh}1PvYG%qoTg-GR#JmNut}t6)0v_SQ4D}2iGn^l;@c88()Rc35Nk(? z;ehX5*1{4^)mzWP0;`S4<&l4u3DMCBj8)k%*x0eKIpKJJarwbFW12g9-=Zn!IRfgp zB!2QNCq3AegwTs~mH`pn30x98Xe!O08emo`u5B6FTY&`I0_iGZs<>rC++=KwGx2et z&A<3}d7~0y@MWXQ&PfV#o2(t&oo8{^>a(5DU7c8tY@i;j+m1jq znRPRFtP!~W#FO7(TatXI>`@(%g~fax(C7vPPaHkN;njm-!Rj6~tX&K$k@GZ!ss`vq z@AP}iw#B7yn8qK-G5Og|L3aD~oLiQ<5sm8-q55pgLO&hzqDg>4f-}GwlV@^HzEKls zX%|8@xq=$7lwEX zK+7Ii&%50DmyQZxvtgAt4OpSMmvB!I9Rdv(;@@sM%FdM+@cZPV*!SKz0^Sz(l&kP2 zc+~m#nG-&a(t)C0o%2kw*DF!3N1Ss;-}xwg$v#lyU+$-?U;Pklnpq9^#*?Lj>|oH_{1s=m$aXP?H(&+{l;X= zy<73dWa)vR=M8L@?X!DD#XGU`z@p@pg%m8rY4FA%WlZP^X(9mX5=R{H-JEU&W&lOF z9r&Cxv2Aq_l8iWXYDj?GpYvCI$MEU>8TR;sR+-P~w+##lb*CZN<9I{5+^vk;BzF~J z*?+U7pIx54u>36iL=l*-y^RD)4TJ+1$H4`LTnFfd#Gk@jcMAB?wE%d0VP^WN$9PEW zc|J8Sy`7pyB#+!)<>v`kUelSfrskZ1p|qeraN&766!(OCwWCti;l{3^H>htqAXL6@v9cApogNStmJm>Pyw4C=f+4Zwvh%Fc`=1kbk3Qx(zBIv`gIeITbt;8!2Fu%D=3~ z`L3@jFFwgH5A#yM4skF$V>#No3Pp<&s(!rOoUKuh+Nk91B)QGL4mL^tZotuG$L%gE z{p0~B=*Qs#N$*T;Kw9t?QVFA^1)@2v7u1^qqFD|+c67}oz`W%;8VAO?FTcaWyNqq3TMFCrq(Z#ZOfu%> z_@_Era)#80*XOtr-YIX3MW2JQ`-{&=!P77^y3Y%n~#s9s^ziV6N$)cLfV_tBvx3*M)UP*A=lAI&ViUqDd zsIdtfC+0U`GkP11)a(!mg|Qdb%1aHSEmJu}xJd~a*WnQjb%(Bas6dXH{egZArfar>C7GbFC5+Ah0ueQ_hCx$j*jx zm)9lZ!}a{lHQfGYaXnlM)$a)@Xa-^0I_eNt@xoMiXaY}h z$KJ*W$I&aeLr>IKxHpAj&+;^&LpS3II=!dh4~g6du{nN!=-tqO+hr?EdLI$7W*4xC zD_`t`*_!zcx;ED9h4APN`+1)~?kheX28&)tagCN<8z@drQV>T&w(m^l8svAA&xMH* zojpFsmgYpzc8P949EBMd;8D8ZUEE&I3WVM9XG`1R_QOQfHW*(f)OP~PmgLfwQgGXl ze3d%fY~z}TVn00CH;!+g3S8G+FtX_R4_@z7sraaiE-=C2I|?Ya11xWiw7D42ku-=C zU6AQ&9ZC68qCRA`q+TIft{}$eCB<7)Snx`7u$-gafz}nJq*fE|>rt(6kM3XEEO8kk zK%_I|d+xF6KSff?kH5kikjJp!QRde&vHpO!t95JCV{LEly(oV0Ru}G)tLVQhXt2c9 zazGn?EOD5K=7A=8+z*RyXQB3?~Grlb&t#%J}vlT@j1 zvxWDSqX+^!WL7UWWoNIM5~4_LA+I*_K=JYyIFX0V93-`VY4Z5MBsq4r@>%t2|krMOjklvcKRzcF0hr9R(VE!#pWUKn5E zL*wCr@e9&#OwoPT$!}d_)%DIrblxA7ecJl7>q+52#()^>LdTKp&GmMeG!;@XmTDRI zQuBgo!1LZ`8e(5+A^6Y(pb2w)qGo{34Jbl^nO`}7wh-t0V~$wrO1Z+2jBDMIuX80N z&lm>@%Hu@e(1iNQJ>#)Fok3J3w{=@V+(g#1HboqMpgZ7i!DK+i?%IM6qg@aRyUaHt zT7%7{LZ;sS$`ZfF9IC4yaH3_;gi`p%ADr^Nqvt68!njl64a>$A_EXWUnd4c6>D+>R z?qCoMqMPxDCxD2&hA7E*!94DOx%UAknJ5F#X~%2TX1esoJvWxo(^!zDD=1<|<#&*97Loo#z7+d%5i;YHP2t;Sj>t|{-{QwSVC4zVQc zBO=AmSh{7*X)#NA=enzv|0Ja`wSZ z?$bGFj%O(^Qmk9dMD1-AXpZ}dd64~0ca-w>>4yDIve5d>9GI;C%%5A98Nv6GIl>|< zhXQ09y|Kn5+GUZHidjF1X=A!+eW+vh%A43H%x>U$rX&(1@RuVGAov{N`qP04V5dF2 z&NGtNe7l8@ve=MIQ733Q`FWO*PC`vUuhcKNU2iIlPsHD=4o3n-Lhle2dMuI-o>SS? zaHjV{Wr*Bb?CiknD^jsvWabhKtM^bY7=I88z&yYt=R-2o;VOL!}5D$)z&-n8O zoH0*ew}!ThYv@IOhHhmflWXbhm^`e;w0beNwkQ1#W4V+p0611pC7LyNK4K@y+VEN( zDDo5KZS|H*)eTwvVjh@Xp`!N0kpjR~z~05ZGIO8UfZphT=k&|&vhDD=+3rqNf&RG& za+uS1H0B8i?KIEr^frWdZ|*~A+tW|rqX#nh&Hc!<;|4iRTxLs)dB}j|PLr5+_Jg2u z3j7+7E|VG|qG(28{u~KqOr!;`cdD*36#Zw4-!93gh!*LJ2Bfd4%{I(gCaBMjP@)0L zxVono!#wY)A|tH?=e>Q_APJ`y3vWJB6RmP72>_o@&Ic`3Ao!+yAVRc1MwEzr6%Mu~ zjJ}t^eW#TDR@Lp+;t=s32F3*IX1r%HG&-5dRgYtLM) z1iYW^Mbv1NFIPJ-BIV!uE9fC?U{guZf|R(akCH6U7J5tKM;;KayddI@`S24XaSJ?p zOT7B@K}F!OBwAc6wVRzf=aLnL`6gS%nfwhC>kd2ot?w-?P+&qTd&{%=&XR94SEp{3 zw>QU%#IegQ&j5fMZ*yU^4*RRVtLx$h#~#z7ao;_z3z_3Tr9w1>xxPT7TkpK)g4?P_#oHik7;{ z@RdB9N<_s6^Bbw;!kCXot3Tvb|FfAQVgpn8OmiES3j- z#;?DuQH!1j(lJ*^IZIn>dkY&XuDIr&v-qcLbT@v0QHMalkd)`yFP<_PjiBj>p^~G^ zW$*@<$Y$zlvKt*&>51(9Ocj0s;6Hwzoq6Aim85vvNkTe9kKpi_-FN-Jzuuq6uCtTR z&X-t7_~@EL8#@Pb=IPq46b*&FwXLO{Q_Fs9iMp$shq0|1A~2t2=#SGn$56L=EsS@2 zW;jiz(Y{?*35tnzMmYZC`xbIP->pxmaCnX6;6G;Vl$VaIEG#T1+PHGGH!@!Z%XYrF z2P|xlvXCbic)JBOPG0ksjvuIaBvOawU)t;YcQf{M^j;r1dgZ${E zHckE)2*}(nUIcrA?tY+}8xHu47%RM&x-s9$C|W`r)-y`2b#ZZ9?dBm*Zn1M2ZK{HE z(y#iBr=rUNBV`AK8d~^%Fm)t17oTO*<0>LA}U3pPMH!?@M z>|t?FEg@o9P&)RSh5~!#LRKsr%E2+Fp{g+3%%5>iUb~*TTTkI^JvLs|D)x@4MS#D@ z@HeEHLAklPJmH*PN-S}Mb0UHGL>=nyimnCZzu}h_VbT>L@;XOI&I3xrm`B;6sO`fz zS$eu=6Y)=uT`NhJPU{!+NTj_U*93T0)SqlZJE!^W?svIl|jufOGxr+CgwPe*-dVlF!qzqhQQjLB>kE!*%=6;#Y6=4-W z_jfo7*hNZmcfGBLHZ{LVZPULMYbM;LiYAn35+yaRb5Eqs%h%>a_z6jtov>xl#Yw6R zJS*sGyjw46cfp2QUJz9ni|XLE3T;uS z6LfJY%z9REIdVaZ7QfOi-8!zanxAiK>KF<>3P81C%~+xEkg%Z~Ag|zpoToN7?oPbP zQoh;aDV?OWY3PgE(NJ6m8leGr8*BI=HYKyK8Mvra$2B)7_N*SkqA`JW3#lvr6pr4Mas0W zCVr0TvAyG4u-!{ZZcGFzGMg~Y?wgdpn4U;V@IiRkIoBu#bJyavfOmH#>r9-VUjk7a z*Ji1nOZE9O%p{e+my%Zzh)&m}bxLD<*j;H$%X3uPhAaT%Bt&U=t025#Z#PR%`8%zy zqm2tfVMs-ZtmlW6fAK|+Ix4qtbV&d-(?kE3cM<&$07F2$zqo&HUbc3K76YFHo&f4J z2WgGJMxzv@xjT_LCQQ2>2FI)-t207?u)|7mRm=Vn_|3z}~hujGox9 zNfidWRbgA8X4_uWq~s-N+dgU_9!mHPAP+O&p#DF^e)wW*l;IhF3vN|0_;=^T|#RB4TBtx@G0 zRQU!~&Q;}He_5v4?a~yLM#xl~tWANxl%h@2#;LqmD}-;HqD{oR%qM7LRX$!Dt@5i- zACJ5U{i2YM)6{o&Z7kjkkdMK;%tvdZ8VXYSr;lUgpw0KC^;Zlrc?AoT%yfW^Rapb7J2Vq7NZ%EStpS{C)dTn@1TQBI0x_va~M zz5K01>n7GWbW8E%1&e)D3@isO2G)vw4W_7wylC{3eY1fRfpdU0z(;}40WFm4Yw?7L zT#>6y6caTa(%o~f+ucv){RjE-uF1YUG_hYq-i=yMu&F0K3e-SyPmtUbTMfLW00l%R zoely|06zjshNpuW>0m}WHkgj_>8e+d>h}%M0BR7Tbj;tek4cSA0cN+GMb>7ZCIfF7 z7?Xj%8L0gTDXS5(y%@L=c+lV0T?un{C7j(cS9egGjWk!4B9NwN-5W%3L?igiIT3le z;QK@%8pYk<=H1}i-O`i@sl*W~&Gwf!ptOMkBQ^q`0|seLfS!PKKn9>YzyYuWaH3i_ zoaQFLM!-FQy8#;j>v1~K&$$k|#GHXQ46GTrap0o^pBs2^pn1Qj0F;YzkCkF$;R{4X zS`zbYLZ?|2#y_fbk4kS;sYj)8p2S&UXJ&=HJ}c~@Sz!;%3M-uzHepuSs99mzv%(sA zt|!hF_Mt0mlPheRD{PP}Y@jQwzbmY#D=aUP%Xm74(aS0wsnUKb?XJ>fp572f!3TII zIV{isPT%LaCFQK6QRjw~+Z>G+WVia>X8N*WvhszLK8^(`nZBAdU)IC%l8zZ@DnCPJ zc6l<*ZRD0W3|d~Vc;$f2VOej+LNr>@ISysP~SC`tBiDVkF1eLA_!+fNpZ*HwG$NVulR3C@=<16}f z`xSjsf3i%>8#kBD4$RIc#$CaaK>^^$9i-t!a9dt~!yxz--J)*iXmXwPW1mXdmirG-Y8!8=#^UJ+u2 z0GGp08yRa`JVHtHFh~5_q-LG?NeV%*5{ke#3@Dqj^0M+|6SPb=h0B=O-xR-gm?No~ zpY%7`P#x(UN%4i(6yV>|rT)UN{I^s-%dTE}waV4MrOTEBribUCA!?6}-7 zIP%YkT$qL=k^WiqFNpMeDG7NuZ7yzbGCuO-r}BSc$oAG2!w{!npi2DcdrANgsI-K~ z`*NB`@2S7Hco22{`A_r|c_;$)?~%sDrR1jj=q7rfrW)U()AaTQ@`};dScFkishpP6Z$LpU zJwnI1ObizXjl0zUYp6o)i}WVDAW-G-@Ft`8Z)qzv(aUs#{!C|?If65|mfz*~Ow@9u z^@uUnm}}fXg*1VRsTQrtoXUA(hBianqn&E`to37~8|E&d6||CWrj6?Ve4L`c(1)xE zt0)muwLO$X!`1&r*bJUM0#3d`AF+i8a1ML;c78^z(6yE$xbbz00SCt@?r)}T;OXP^ z937#z=xxmTt@@QU0Y^WTXYm?d$M^94{5U_u&+=EoB>t>v+AaDY^{-k_8dl?B<0-70 zL|w@a-!~Iya23w;IDLuz_2Nv<<#&WjWNNI3wzRhPH%1#Z#;e9Da#A|_4u_vBzcNk- z)vM?>+E0JP*yHpk`ka0Mmo&C=6nJ50Cr{=nyc|^Q;cvJ_#Nzyhi0edycu#X_$Mxy@ zvn~5tV_F+pziBm$-Goi1RFAWi|TW4H+W z-HQ1?;!6-Gi&!h35eB^BP1+lJg1)tNLhGW|t*v{F0mgX9mIfy*fd&9_AWic3wWaE} z@SW=KvGziGPSV#L&)v8WkL77x%H>?mH}GO!!Z-78z}2UCAMfXr{2_mh=s8HlfKM(_ zDQ*?_iGAXTI4RC(q)kCgvqZaDyHDGvy`}w4x9ORBAAP)DuCLNpo5-XE#afPCio3L^ zWp2yEEw8lpZY^kC*t)*;aO=s|PmK`cpm7$iX&=y7MhigW8tngex`!Txd^`;rKcO$^ zE1dav;DyG)oCu0j)Vbw@?(v{}I$WmtfNH)L{IBKR+`tF;5Fh3@_)UI?PxCi|L49w) zFlfP4F&}$)SnL*F@fW}a@gFS>Q9wVfKcb9s?B)*bPVDDF?X-4Q7kZ4|SD&KS=&zYH z(>&7_(>Bu))9a=$gKR-F{WjvV1;L{o6NmK?+I6%8(SoLZDc%uoUL!8@??o~n#%jrk zzlue^7zW>HKcsvSMVq$;If5J_nr!BBIfr;iWNFj&G%b{t$-l%BGsNwpoSxtVbWx0f zq^;17iydN)woSiRAHk;(ZyeT1gz@*3M|nH~XZJ2G!3kw)&*}1SvB_l7E}0gIFyjvW z3zN{^fgK#qLOaSc_#79DSa55YxQCp0xA8gTW1)e6f#f&A?HrPVDE$a4tijk2i8LZ`bV1l=CzR1MYNvlD3e?G5FHbnX%JUwFJDS%$q;<$9M@}OsGcwCZ|HBp z`MwBFCPQ*9aJWB#OmD*~e+AI8`fqSIU4+%Vo$jTF=?}O~cH!Pi2OqBjH)p~2T!TBJ5A~yg*xd;E zYYmM>TQN<8JuQcooKM%&5?J2i`hyx?y@Wq)4%BH5=Mz3 zvCrB@uysW8vk0TiY(5~y;&y1|2^89!(7a0{NQbo4Y*}E#;HGD)bndtYn%&;dW+Qk#` zA>DiTc3Ll4hPyJ|!+T}9Wqf26zt;}y@2I~oF0T(aHHzCkR<;Jq|HW!O72GU#QHXfi z6Kan<7}Jt4s!#0yk0KGAN{-)T)uOG!!N&wV+unB95_*5nJ^rNy4~ zo}s;L!W0+Y#TpeG6lAi+#de7q(Zw`AIJnD>@E$}q9DPF9qs@?kcurU+PjmM8mh*1g zIU6{Og9KBDO6E$|&gKD8QGYjufFXkFm^a+r?zLX2u@5835mL- zI5;XOF4krW0<~5vhz7k85Ens-$-A22_xb7lUebF81S@hoP#Z0TIY=G);6YIX2Z(f^ zM8w5L#fqArr1smTtw!sf?x6{w_6;7h>v0})6x2cGz^$xGWmAmkemq;vHs`AbnQ#iA{^TyPfl=fEGGnOLuhF5C>|R; zGk9U}3SJp}m*uYDE&NdMi8cjMzD5Av(j$1SrWlOhwWVrNKn zcw~a9$B(6{2@#<&rYGaV6Cy%zOlPt0znP?Wlg)efMhUC4+4zn$<9tJwrDt9+${EIa zN;iI>SYV9t!wX%*gTpQ1qS^R2*^KWRlEbs)h+f8b9%oNe*YK3^?oo>@-MU6mZ%#Lb zb$5n4hDT)%H$|CDVTr?`U&mhP+hcfmLZ9C?a}dNMbDfX9w(r5=TuAge2!^y1LwypG zzsfU1^tPqNC&VShCd4E}Czyh|CM73zOG-)7gVNJ7(t4)#O4EZvLam{}p_Wi{s3}NG z>mJ#|L-sC-9(Dz#dMHcJ_OLU;;o+nNBx#|U9_lTSsK7^sI=%3?ZlNLmhljL(5T$uy zBD-}-$c^q68J8O=lh|(EqW%wQ-vZ}Eb@qMEWRgtolgUgnllx3=n`C#BY?9ptcF6z& z6-3aAfDl0tDTu;~md{INd2L0h7ZfkASGHBudUu!0vQ+VH(V|qLuh1&hg1(5p*0r=R z`f71^zvoP{7qIyIe!q`R&TS@;ob!MF&;NPO$x>3f2TrOgG!neu35Ab^LUKUiv6mvG zNbqhOw4l)OaGx&7SxR+gDBPqK;3b*-C7-JvXgGLJh`cIPqRgywtcMi%U&Q$2ZO_31 ztJzH=4v%7y)lA;whKI2LBZ+uKp#*`4zQ>(!;EK9W*Dii=%1!lwi4G66rv6)f@GRDS z!Gx4lGivhf9TU=G&8Q#T)92tsUh3o+U-!V*s>5kOjLE0^W^glnw8_y!{R&y}L#m0j zS{>{wCWay-dKlrbUQgjynCfP}k@N9A5KB6#CEJnS#8@SY-$|_nF85Gxt7f#s>BLLG z|FADpD|0Js9Pa;uH#sCTBhg=CboN4nA2icUsS!8D%;)dYcCvjA6^TrgUHD9VZM zSU4`oqKaG*r;0rxUd3D#imK7AU0YcO_=DPTTY65DTvY==&e5i^RIvakHW&?62T`Rp zgTaW5ebmoHScGp6VDyi+3qsFdf92DX!DMBfo~1i3dE}$`+#mP9+jD_T*6x}cj(xG> zN7HMTKXGGOG@3nZ_VJ4ktsi~Ks;m3o{{^huZwEf2VXhFsp=)_FM%}<=`G$s~b9D6V z=#0c42@aUMN-E_)27e;P})30aW7qtNXI z_9P^#83H_<6)N=?iqV*hr}eQyM8z(>pyFsTu410%RD`r;Sqp!lYA&}mqvkg3u84>D zkXz0-G{UmPD+`kMXgzI_1}&$uMmpEk*L~T&{L^}ehh_PPp1b?COSYARUP<~v+r|fP zn00q8>$EtGun%X9`;v-v7zGEX%Q?W7YPRQ#bsOSQuVE_u>sL1>WRgYg%XS z`soGtfyeqIu`9Q5c0cWT90iD1YU7F1g;w|P&vE(6(KCkODG+v z3>_th!Z7jMbvjxv2W>%UcOpBuB=BMZKd}Tia=@5d&~;NcsSd2SF9`%f$PvU~4Z$Aj zN+jdCss+plOA1X3R`6l}3Cj^3T8_49AQIbJKq2T=C1$CL5{-nsl$CT;Cwg1Im}!w`irVqd?vqQN=laLPH^)r;DmT!eSm&Bso_=D}NR2QSogAg8`vuP(F5Mg`!$`$w$F`MZH zE+-JyWFj|+UQzVxO@2LibBg(0BJQ4u$@*bIe-1gK{vtUSN?3%HpS0YrVop3ra8@l!Nh}fv-ryRJ9lBG0X&dDl?P5 zh%v-qg1caT!_`{R>PR8lJ#auZ6ZU{tM;Ti;ymAdq(+p4ZOq`B0Dc5M%RM%YBGS{`P zTUS-FcC zkV34J3%mG`5OfDT5-Zvi6t!7nmbe*bGgN&oIF+O^ozi;@c3U(DHnMqZU8_!K@^$0C zsP0fJuTQj^Y~qK8R+M5>;Z!z7r(UMsf~Y!*qsU4u@?aht6QNS!|S{mQJZS>{7b%v#NmNW>9#s zXbl5{X?>lFWk-lyvEZ03p`xTQp}~RxgKg3D9pSY$&EIGms^WRlco-@gQo!pFtW>cR zQINocs2YY2UViT8#W(MN<;g2vZk$lcb{xFnf@aa}v~kJS-}dj4;*YIZxv69EB^R|& zu2okae*C^~ZvOdm|M}=GOE)c691`a-^ZhSK)bj(Wvpwb_;kh!+GTfW)wREP@s3SWGRkn4J#ROtV%?4_G3mp4#Fu zN$$@_i0Y%$G&xk#hzEGbqgfL%rgirEw4E@}DXtrWh@xDXh$1xbAffBl_B}zkM5pV2 zo^jhb(LgVcYi6bmMU!X6nw3iMD>AG3z z#gyJ+r$wKg7uSpT;0KupEgKV?(ht}D3O|WD#zD>(CPF? zg53ijbY>&t6>;erm5H<_lZHU6OP^^q z*dpEdJy2pvHydx&eduj1xu?4+cQ@`;E%|UO`(bUXNoqJP7y*c$AhAB7qGK9R2~IU= zX6tgkkW-+UAQx7Vp9dbQH$o(l)q@)iIf0RU0u*&jMnyRYJBC9H;#j{MU!XEBx1qKP zWZN=5@M#ys2=GBZ?SjyO)M^$Y2fYYAy$CH#T3FP^cxy=z;dX(H31mzlW2XcRbB6pk zaA4CQ6>6D*Ce3X_fB@KbjcDs!OD{jt(Q)MRW%E*_58ikGfze6ZFRr=z7n^>(dXwkb z_V#C=TeIdl>gV|<7ucRv=2u zF*o-y_f6z~QYY&5yAaM1mL#w0CX4Qd_5Tbtz&zE#s|@_s-0s{vIr?J87DaKpHDReS zr64ARmMsngHrf$Qwwm>X&s1{;2+b_Fx^3|u;4B;UgldkqI=n^ihsM_0m#L>vVVJtu z6MZ1Gz5-}`j4Eh2TB<}DPAy)o5?3NouS96UX1CfYgA?S8i{)9`ppT_YfRP#tU?dS& zgeYMu7p|vC2+Aa3&<;g}m0^4moKRvRATFlcK=T3uC-wyv639^vy) zv5<%lFK<($Zr8Z3i)THyU{}Xgzb;G|72i1T##=5L4R|FP`BzB|@E_i^WsXWW-5DXi%9SX?;&N$TCb8@JqkYnQ5~VV$)Mif$pY zK_r+{O^!~th3P~FZYOpFC(ziv)nbt(|8PpBWg1%vBuHWfpORQ{ohi*ST<8|gXc}`a zdG_5oBKI;IAv#0fjf$94z4JpW3~LNJr^TM;xPV3Rhl-I%z^^kIx(8nEas*4HR%;zn z$#5Y>1x&oxlXkfR-a@@27s^q&bg>wyPt*|)N2Sv7c%Tkk%yPBGOX0XB5{-L{pnHM{ zS-g~mQQ{6i{+oZmPx;5`;>d(&m^w`RO-D?}O?p!?9j=UI=%q_U{~0l##*6caZpp@2QU9nbgf5m&^0*C4}?a+DNHeMMnCbz?%92 zl_^ri{?OOtQvVhrI(46s8~P8Ek+=G%&eyJdLCX2CN*{xU|8H2O5c-o^m!a$R5o_2M z=EFk9p9zlD=dD>jE0p}D;JNwl?MuH^7J-=_iCnryy|7fEbEPTwD?=4 zSpxOmd&V5X_hlDw`Ma5g!A516y6d#;!Vywa_h@Q!?6>Yg@m`rn@+7&IX9f<_FTlLT z(LdB1Zq%)DUB}-_-KM+KwVwal&6p?)ZxM74QjZv)Hh#i>%zx}Q(ClUGHg+3NH!$&V zq=--;EQS1k7R5lw=-@0CIvk?77;O?gL5z`VbC!^5WlKj8h;)p+HIB>dL54As3r2FG z9i+XKcw`4YSTW)K&sH>o6Ey zI!_&D4csW?rntI_q6fIQq=EWSTN#uat^hLOtiV%8R9RDFWAU_^)xB`^(fQk#6HMZ1 z_fI+h?54T>+ev^#T~?XgKJNoZW-Y>v8YKTQd2&tgj`OHvL(`1O?@FW}fLR_-U8OCcEe;-Pppu8XG? z;s^psMIKjB8|l(`C5Jc^loei<6(r{rI*haNR3x5CCZ(LBgu!0GU_`39DL%GTVi-2A zniz1N*KLty(bUk5XKxRWAD=_x;|)FdDWp)>sUCGkZb5EkP6tjPHzTJzk~DAq$L76h8Y?!AR`Y^XU!w^|B@l4zhTKOrRajC zWaO7W-Lek9*}wj~MALVY(hs)#7o~2zdfag>8MAa>if~XnSjTLz4jC<1o58gfiEEX8 zLBcIQaIJr?deS2Cq*UDEBhJxb!X|G7R^7!(%I0fFY)Bp}gLA7$fnonONAPPcqU`9SZP<> z%ES}R&4FMr(9{@&fO;PeCFUnqBwk4DO6U`6B3VeNT+xxZGqFE$Byk+Zx~UIUArQjz zF?A>2N50`p`~5UU(SdI2TGj0e>u8=1PIv8d9d`ZAMZ2U?zo`nJX~d?z65A^}M`bFH zavOBZAah0bm{&`kkx>@hOJBw0NmU|jp>--m06T~BJ-VR(W%eP>6TL~w$wgAWjmP3qZxbsm zR1$o9gFdijj%4yqmj->c)B#PMjTk4#h5^;D0IJjYdNpB0Mlp-d5vQaV1ny?{2fh)% zNt>TSPX(y7Xkr1kT1i2;xy9rhKNCY^D`{AH4Ph*s7g&}T0#?DTq{xsT#aG5D6ldcL z;``%tJe~w~oJt3jG_vT(4dMzTb{Ip(6-J%Wn52wG9Tl)pSeD4evjc&=7zjvWKo&g$ zKqLs$0&%LC2AFz8-s2HH?o?cn;xZq%>f#nfmaSF`MZSj)svw>fXNWI~$3j4{x*a9z05P+3YUQFYwI< zytYH+p(EnvO0CjkO@h@bm@4JBZrx`i``SVAzJ2st{Sk=8Y+Hv0}5N>e|yh^Tst}V;OYKDdc@gq{y zWE-;L@~X_Ci)+`ak-Tp9_Wk!iedJH0Z<*fSzVM~6iS?N6iyoe_sdFV^jn~Hf^z7|R z&Ru=g6+0JQ```mBuG{8dZ<%=6D6_~p%?@wskwtw6G{^o+Cp&%2`RDv-)_n49_&Qko zS@b8!k7{uAC7X+^m4(glEbHPGo8L|PE-52Ol3P&xL8A^^!f|Wa(v25wjmxI6jG?8b zbbg5Oj3!G!cEHS225%~I0kVb#UUisC4t@oHnAh=A^5Q#2E(cizEs`L|EkG7X>`@U6 zR5~git)y8-eczVu)EBMQ#ZWI;X2MxZHafZH!kSBKex+=VZpS+;zYcCo?AGsP4$$v2 zNA;gEPB)#y4fC;ds^JFoYD`J3JS$6w#9Z7+}21kIjJAAGP0p4A9&Dp+O;9p5UOEsm1VJ@A!U z3yt3i@5kOt90?tf{~G%&VT=k1_gUd-v1y5!;d1Pv#4^V+X=&_M$?7IawN);Dj_X3< z^4MjGue^E#xLv`UWRqOXyPkcBy-&Q?yII%__eSEJ(;@LbjC71$@_QUMq;p!(ElxRU zv~4WU;h_Anx+Rn2Ke6jUsre1q;aE&TJioF2;X z!aX~G^T($b?mb^%ou0*y?b+LZ5?l8EhtB3FR`N>7>+wx)|Lp$94*XyS?{TKbUygOZ z+l#Hl7H)*ueukWVhxz_++u5lfr6>}1JzojCGFsTBFhP+FvA&GQ=Mz0j(Ck)fOl32e zL063o^9|+@R%AYitQOu#zR2SVnZoVlR5-@o+L+u9QBXI&tt*{suS^pARarmcU2&i3poA?xJ`@D)^!X6gLs*Ap_bI>}LMAe5xHZys-#+F3 z_*WOdJozZ;u(NKPl)Fbn(M*bdC`KWl&f z{Ij`=f?fRRnYjYjePV1KRZ_)?SW)B2i);%n%m9DHSX9l8s1}F4`!s0YtJ*SxDy$Ut z3$!2=ui1frU(NnZ)6$eJ>@iJLwUCIsPutELb?WAJ`Lqm>J(c#sg4Tvm+LsJrBJ0+! z{chH5SVdRU*~9yC20<81rDd?jqkSt#xpx~Ww>^8u>UDWR7{Ue%HG(kd^^8fOP%xbn!>lI^ zYiSQ|%*kPMT1Fe=MrI6Uzy@3s2;;EX%$hn(FPd}?({$5((+j43CcVj9te4v(8zLQ% z7bEnp$iB#t$ngjrkqW6xP64aN79@&>#UN*GOGgQ%R??WKGDi6{SGg1U22LRFz=_Tj z)6hNeb!U))F!n@eEt4fx(wZlOb?ywfy^%-aD#`^Hhk_g4Xs05>r$U+vl`w2biIr>su6*ByNB(d(zF`r%vVXx6&F-dZwV(N?Bkn=DMoY)$+hWLv5nS;v& z1C_HD!y0wSbSpa6c&5M@pFd;c+=aK!yBLCxQ2*bE`?&a~Yvzs1EFZa9pjkHPqLZ^G zPrP$_-~SHTuWs&jb>Y>0pAVhH8(TStfnEkQxb;q?0|mQAO({}?q)O*Yi=?ZipGroT zjh)Sdjx|_Kv-Nt#>h??b3ZP?kuTkCjo^5`E&1xpUb8Wu%f2bT$?KG_q3)6YbOM&y& z3~v#zecCFul)macEmbulWmU#Pr0Dw2PC#`6s0}x)!BfcR?i00K)s!zun6G!f_g?>b z-~M9+Q-PKtoWBb`*+#&5k?P)olCY3&TPaC)vUsajtJ674X^HCz7qz>9Q+z5`pG*~M zild^XSZlIWSjI1lST1wnh^x^>rTOW}_hRoAK97A~I2k)xU`EGA7nVhr6`$dsi5Q~A zNCZ`g%reAceggX~D1<{H@{d~C5~)=dv}b5uBotC2ia&zt@|wM5vy%$hY`&n>6^c%a z_7~YRX1m>DR-7cE1u;Y=n`O<_|O8ia`rCaG~JeuVsgPb7yh zoSY`d|H;mrmQMf>XgfvKgo2il)W13iRH>zgO6{rB>dxV` zG$wiJb@poN>icsMr`7U~w!&z_={7`m#Fx@?rFq4Ne zXulp2F1>~qr!{l25!1P(zZ-A5L`#w#C!=Hf@5evxUq2#v`X{a>21MHZU-ZuzIu*AH zENUlM6b}|9qOw|8D6A5062SdhXA{)~I%zi1NSr9#>vSq20%?SB*vYcf*d)AYU9;@eWS6#KhZeRKhvnsGEJ?TeI6c-M^73%dUWep zrAcs*p9`?A^P%-OncT?_fLq@*dO}S*9#k%a!}V1yZJZgI2fjcwJdFc~;NLm~?)#lV^@S^E zhK%GLc*kld8lWccq!a4OR|Lu~LCYy=aon(rjyNkvc2Dh(@YSaq>8F=3AGc6$UNw4d z)rIpUG90!yy2b9kOlsDsbYSTf(L5A9G*v(LnF`b4^M%QL+H&Tm?mok_7 zbPxG98>s6;U3d$n^M{s&D1>Q>3WB{>RGknKC}EN$2%@BLr>nY(OR^(7woj^hrHZO! zQH+gIr%5|oaZ*LvHH>LC;2wMgg(0SQ1!UR?Dv#rwW|KMWJuG2~I22YBcpI`EAn+uj z?uJD}Rohj-*>?h*#sAZ0!~d7E<9&WTV`K~rW$^0(a6TqbQFkd#-3@}RGsMG7|MHTL zufVH~&MW`h6+gVLZKS@dVr9*o zvy#D|pWXMjVSP8}`UyYl`}=7C0eyGZz+t)t5Mn_d`~$U_bJK3#@8{{;dDN zc+g_F+_=<9Ev6RJOPQtSWwzzc#jeXdj3DbAvPow#8LcwXP5?sO(m7EV!;hq(SU50&zBZlJ!y`dX_)FlE14JvnVZoPfwHX@n_zsID7 zS(c$N%MD=`KX76T%iDQR&%j6EaX;#^1)ag+h>DzY(2fGC7B|oON<1l?B*Eu$1WOhk z3dRhDkrYmH+%W)(&&XRixCn)tcY4P1q;T;Lp4_op<)C3UgIHroLFpW!7Mw<>eW7R$ zCfUou48_PiPfLIAZ+8D*_PzwXsbX#Toa{-PHv7``Bq?o5Lz|{4ZAqo+N(M4B#4l&m9Ha`;5NZ4@t27bzeGQ5hy8f{Kfbt%{u<>y72aYAvDL z&vpiyxt{^aOqFqd%b=M8eDzATM^_mkW);x5A-LU4yoBYRoyZQ%q z_w$fHp_e=y8(t83mLEI5Z29qH^rx3NH?X@QGAQkexe=sESS6YvnxQ& z6Xi)DaKgGBE#IR>k^JxYEPm7&LO~@&8mbNScE@)Hoz*6k<#c$wFR4k7$)9lEj`PCQWOj*`POsUd30rhY3{jZCU$y~xak#|A!S_k%YT zk>A^wDDqjj_4-&4Ie{=Iy}B$jqH=DwRy#Lq2GaNgS)w z5jK8Zj;<vEYJPn<_lQN%VQs)GhaP)~=8GYV1JF7rO#f*}VQjb^06SnA_ zcruHEi>v^$syt0Rq1KGQZd`0G%zII`-0b+V@q{8@ikDsZbQ!)dN2c+77|{Mso5CuJ zd2!$@p&s~#D1t|x@ro=6xxIQ-Zp`2=7(To`4X?zqAs!21iE2~8B^yO&jYJ7KxD=sq zT%>>iT%>@<EcI%oeA6$DoG-FrrgN`)rRzemuEMTME=%{E> zSXh)Wo)ATc#}o8!G;`=M;Neom@D2UM%%Qu}DkUrp@u{Q!?55Q)P@oLwrGQAI`x zB84~tho=d`!-WEUB>mbcEV`0;?UbZL`7!7NI3-jJcW)OMo|QzR-yH(Fj*p{1jqO!A8kh(hi^yb=F*3>bNd2k?&!#y^M@9wfpBXV zk#-bpsUe$=GC4XMFXu?(2^mj8OP+Q^Ipc==2qn0m3MNk3z!%K>75ut9DMb8OH(7(( zz}xagn_rt_uc3DhS}Koh!fN;yFFEHtTsWnR6^E5^uVcoGG)V8dAsyJz_g;Yx4CF$4 z?j~}RY_ptAiKs9N9aN5>A}Ltq+sJI$IC&GRS?W=E4BG(d8M(|JPa1xAs74YvR0DuW zg4CoG6Jm)tP0SWoO2uM{R47zl!rD-?3MLM|f-5wlemEwPE%Hj$8LuqpPCGwfk}~(kR6wCWc$c%U{Rw;9lZ&LHonf_lQeka3;7oMpO<^hLp0Nd z?_3Sd4zHdTdWMEzPSFh6BFY$E!rFdc|HjF96UBzEcazppWa>S*AQ$a0*RjR;s5(;k z1o~+ru(OgqFu-{LbZul;+P523N$N%HXH-wB_pqKaVI#RsAZ# z2G(}gF&10E5hyhrjnc?U<`|Vp>TFK7vXoP*tmo7#rYNUqrx=^anVc47v$|Q^Y@EZn zUAbEIp!zSYO`NBc8`Qg4dpUcQ+ts_ZyN$1_4y!*@olyTnbxNHQrixajuu@bh>c!f{ z#?7j|szcmEijP$1$aCsnSeI45sHH{?I-Et|D>raxixW7sMW5J+kOYrxD3rP+RAlN% zAc+Jj!JfeW4nZ3djwi4?5*8$|Bnj0Ctb~L$#)O0PdXDCBYV;amQ7#TFok!N=@wIO^ec;rzL6wAN==rt0i@B0h2*!puS`j~_J;DbX}A9FeA|@@gZ?@AguBZq&! zTt2Lfy`<&&JuO?4k-MHnmzk@2U3>1Ifp$aBpoO!MWX1HIB13$*iKXcKhP5`-kKGWn zXMi6UvR2qv#Y^L5ELpa+UdoC_&-C&7TgWzGv=yX<=jkHDDAe_o= zGMmmu*`m^I7F&YNU>jXvE3{?WtTvmyurMz>JI`Q9N=;45o65EBAyao!g=_O9=;cH- zNpiz=I#qZ$mr#)^RV=wSlIsQYh$^-~eXn6{k__YN)+SAjj7?)6Te-2?B9SOcG@57Q zoq2)e2fq#=u$Vrjy{I{_mB0n9r?nNQG@!L9z|#* z=i%S53wJT|B11Ot26}NJV_saqeRHqEi0(i4$_(iK1=>r2`zJf1bH_7_S!PZ^+ZqPR zSZFUXL1PIzi7ydBGf5n@k;K8GS)vcN!p$P#AH96oCDn){tr-yGcEFuEJ&)fxV8${A zzOV~rVR_QHFj-y(`oQ!=0Hh+7db|`S*71e;ak4xTT?(^eqZPE*6D0db|X!-7RRb4tE)IFWfWmO0PtLc3NMtivcj?!bOKJSNN_LBDkO@ z>64xzl)VF#CDD>DT(+yrwr$()F5Bv|ZQHhO+qP}%l+}g52X|)f%$xVGw{qvny(8j8 zyhJZ$Bv4h9FU%Yjz& z`2209sIFD^se#>=E56*bmiJW98Hc^#^W!ZZsSLBZ6Q`0?!>=zU@AaMxgx^TtM7?3x ziMw@tI(@>u!oS?l`QKpQMc!YYkUgRq8}erSYQb`9h|iAW_)svAi>8PZHIE(#DCKG8 zVQaz)p6p={#@g84+c>-VHEkuOBx(tNO~2m#vPNtyDk;%=q(X0x<4+zwuF0P@hL$3h z_BSJI){v`MJhE&Su8B?15~s*pXelaU75VuyKuBOCFI9^+NvM_)^YQ1cO@J~IGnbHN zo-jEXv=FWux#(r-nRzfBH4JV}@QMO-%lvsuQqnxKsJ1h1CCN==8(?m*wmCxvMH@Qx zz{O7B&}a16z8R}fug^c{OZK@*i_DCW8{(NsSXFUqzI8Z2`QyNr39q#lt>*izf;ui4 zZuA6}v^_xE;kJz#>op^}GGnjK)sb8E_qUa`c9MEIcOXaZn8$)Y##ZTJA!)=?kL^w0 zGgcsmp;y>0bN$aSP&c$ojg6^3AckJ-wbX&q)N#-(k}B@M=x68M*=biJoiC-B#@Va-dGcu{I>raTiHdYsr{zSI5&d zN}WAyZFsq1fM7AUH;_*BjMZ!7hc_o9Cm@odcKL{Z!E|gDrvyex-IdUrU{7rGcZ4lP zDHBDpB+ii}UI?Qlj!pCP<+0+PmvsxbG339?=&mnr?r3=~p=zC-p1Hook!x*3nUEO- z7jr@QvaiFWCR$C3)1NR=2(QmDST6}L&vMJ0U7b_@$!PnmF7pR2eS#@{80T-HEz-|v zjv=vaM$Zv=j;XY1n}tYX@zN6Vi_EYlg_xrvJT4zF4JA#@jH6fNabybwzXTE}xX(d1 zV{GqK+~T`XqHhF(pnS%&c?D)NCRZ*;HI^4aK3{d8S8un^+mAAXN6^oDg^X&|2hcMI zDiS^tK476w4fNMmG7>(%z+=JH9!llD4wA6V)@7?^bw=hl?25}?XdMQkBS@qnA0&Fy zv8UeL`1mg>rCg0nI`F>p|I1grJVXc5Oi+Pxzn@VVEo65GSbDLb$*NroS=OC5P;y z;b>#_>?7yg!5?FPOj0m2N(E6}V5N!OqP;LNX6F5<#7sjC7$h4&>%>fx5T(>L)#r~# z8$w3HetUwW1tOzZEC1mDuXsV;kzWM!%Qn;p z^fL+cNkgy`_DoT56_%Axl%y{j$W<_#VGR-OXh+rCdX6HY3099Pq!5Tc zc#oX%*5$BfA{yC_&rNwJk2W!U9J7*zbEJMw1YvDNk(xEyvLHmT7v@Xg5}Npf;{g6! zt1JIUZk~uXk!7M?HNnN!nr5gL;Xx7XnaiG!1ww`$q7UtSx!cl@=S&T^Bv!Srydu!G zvZ=_Y=p+BD>FDP30XMuTaoIJJO?v@6H(94m@sNnjaUP~?gehkn{Wp6&%VobeC`q@r zrVm+IP1l~?WLEG|eV7x-<|of{r@b!N4OZcuA#((GsO?&+wb@%_x4M*$`Yh~vIz1`V z?I}sVwF#|yK1eUywkxGfRX8kQ0r#*c?R4(x!Csn8#ZA$qjQpw4>iJl#QM|q1cAqR~ zY=JCFf}!NNfbO=`68#-KLn2C>dF%@?%Rm;|X=l)r;pr)59U+~N5fsf(i>MKv`qU-} zcc|AVMXmS4pYYlH=Sm?ntRmJPiYVLaG1lKD_@Irznz_v^3HG6Odjo1qr+M0@K;lMY zTl>A@yd@;Pu(5=UhtW7F!`XuKkt8$8YF&>Lyo{4@6BLYz@Dr4*C2-cu;YQ%ql@O*| zYB`SCYV&0bobU2h#n)-v*J6)TINXKbo#;quyVBu?+FD{sf;IT2GLZCCfy^)kWuTVi zA~jIbk$I%hjFxHdq*dh}yokvlx_CK<=GX{rs=_|hG4^C&!4H%i(6==H=$2Ztvi;sa zK%23n&ik5Z@|tjOhT-rnm7vI+<0NjuaKX>NsPoafeHe^RL^BpODN6%La3@r-ZkYNs zz>jbRdy~qiAva9Y@DB){^aA!L2y@BcO5fDQ!P$OYsEMQ78bCngh>PzMHnlB*@1t^K~2o~*RnFiAX$o-sKs7F)}P%S zv#bi!wo_*nHYVGmt$J964`b30#I})}6OpZXSlocFNWCbb4M1-b zELc-A09TgO?31u23wq-fp6^bAOYsp*g59a0UcFf+^>gHw(4IWfR&2nh&83?PL>c^fmTYm1E&^`X4y|O&%#!F@+A4kmZo3DIyLf3zpNum z6D{G37muElz;i-W6$%@Z4L!hc8Jwh@JvzOjZQ9u0Ilc%A z`14t9qgMyMyuLg?e*rv_*>(M;Y+2D7Y3Vpq(4i0ILrSYG`_`>oID7rrDVH?4xDE`$ zsx;$*vAq8vXibvWwtsU~#J$zS9=nTpgl%dd1U|GM3KLhs0K}S)FMAd8Pxo@ur&yt+eY~fx`L4ml-c%Nd1-fj^JK6Ud0%&Hw==rQBk7_gv=AiB>Osm% zL1}mTeLK%5zXD0Zr#@tvdt3^(cuLi}_&5Qw)~)aWF?E$&-dJUWplqsruz z0~=A@o?0O3*z&-PB6oM0=;@}nb2hHi&(G~Td1sVsYq{W$7l0hOjTXoAbgj^{!l^WkS)7VsS68vIAWTPBSC8x-S!4X# zJ0mshb76_kW(qWtBVm+(Cq=6aibl95FiFsJAU)1`v=QSkU*EOn9UwWfFr9?7Au6|e z^vR$1Akz`x(d0v3iJAwIS4>~tv*vf=z!nw4LM*KZ;Gs)$I1!y*iv~v}_dzY{?`7LS z^#}r=XT&d{X%yq(cNArZ#Iu?fbPjL2L^Ba{oZUBrve?M>9S4U+xTo*iJVL@*&BgS; z>fm3w9h#%Mvb)(jhJtxP`Hz_}3XF|&L(m16Us%}>lwog)@UCJpD-vAvEFz8^0i*dp zEX4TG0TcSt(3MzLNLyY$;0-Hlz^h!BWoWA^Ym1T{uL|;yi!P@wr>HJ3|Mc4*tF6kG zR!LTTOa4_ZWs)pLUg0L)T-a6;QsJOzg0_5YGOVbos-do{rmV{4PwnTDtio8EBCNyAW?ZCM~aVM&8LjY62ApmNG%ISF$)@qnl7oL$&a zQG(gQ0Md|bl1zqhR@_$5*1(SD5zJiS#%x@cvG8&4u%+UIX9wu}KvPvwa88n=tS*+W zoQRCbugk;aoMfP6DCrCkLQ6SKRY@x=_=xy`9&S@XQ?%D-4e5MeFw_S7JRbZW*l4Jb zW{n!Cn9zhYmSdEbu&%T4){v_YpKwiDCiG9@tnlMSp=c@_5Y5vuVUhz6&%iD~78H8x z$T$$vjfi-d^jC$q#Er}T%gX~`~r%$4&uVC%A^nWJHs?>`!EM(mY4 z;`RfAq?z%q=fAc;-|gPEx6}P*vJ^QKAaApGETx`fEM=Z!zsP-Nj)v?PxDq*0IamAH zAnUS`KIb&EV>IZFT<)3J{f*4|_x{|bGl~XsIm#h@LhOY@W%=UYGbo10OE+p)Ur)nT zT@>x#-ImV2G&g6K4QTYJqO$d$=2#R=WPd$-t7u_vu}Ew9%a1 z7^a$$l`)X4m+CY-@b{%`*O5cuB{aUqlu{r&nT9K4;PFn`=#Q^Bg$AiCVtbqE zDd}D2Cq>_XLqq~iiBaz%nV|0bYox78a%EyP_ci}M+CqyU{kz^__r+o5*GDtO!*(|n z0l#Im)R)7`kP2e&yvyXeo^&q{3bKw%PHu80ul&QDUV5a<$ zRf1we)7zgF%XeLFZMWI|h^n-zHi;%FvsowsP0XCz`)-WEneL93gIa;Ft*6)1=J(m$ z%|9YH-W%+z>76zxr@huJEdq+S4zk`w52bUKnLh*R#f@1=v0rRg{PbI&42HtqSf!oB*fZ1xHRciEMm z^xiE;U(JYWH*WOD`>c2C(Xaxle7{n&vr>B#K4%eAcHfyJ;cXsI@qa?0>}MDW71bl- zqDc^qe_WqOE??T@FdIw_6#{%)wB8-AYS)4c;c=ringtE&z8n^m7KpfQd;elu>?B=7 zvUrkAH$xHS{4&Tfrr%^V5V(YYIz- z5>JPrJfY$RekDttq>z$tcQ0sXjpJxUKW4^ra&^@!2Wc-pUgojld?>eUgc z&beJj^p;4)YeZ$z^UnAXeCu%CwVv?4tX5h;W6-ocx&P9@INt2Fna#L#q19k_m(i%N zxv^yzF>Nuda2UvT_04qs0F6#a??@>>R4@Hgq^2I741LPgv+x^SE2>=09n?Oh&f2%<^e|8m)!g zxFdISc$_NtG<66n>c8(zHVq7MUCA}{6tm6xuAiq^Y- z$EzE#QY^?55NHA%ezE14eh5SSh}4BFJ=9MznUPm$O(W7c*Jn>-`}0lm!Sr-hx%540 z-qiZ_dDWV8Jk5af;G|`HSlFoNW|MU@l3m%ltGx%@x|TqlKic!PaQs*J>|k%rcM;$Z-(Et*P7i`Pyu&qu6JxkQ=VC-mMOFRek=hXjFAEWPMjd~b*OP^ASW zSVZlQa;~SVsgo0sNDbg!LT1BKJq zq0HXK$(qZSMBbp;^4J?b9S?Dq)&E@uzV6@h**)=CUiw{xUG%~%F*X0}?>XkM4z_plp zU3l4?Pyx+;Sb@lapTrN@G4w`L8b(v=oFLpH#QND-E7U~Db+39ZNKgT*X9Knf5>b;< z4t|5osDoYU+k?d65up`T4?O_&x7$>JxSWSNx2@>`jRLv$N7I?;0Bko4n9OR`r`G#= zj5BD&+Vw})Tj>;g`&VMvswx}6I`?IAOGSb9a$NH*m1l>_sJqL3a>+Iou(KvA%B#zN zF(hGDdZ%HFS#6^=gQuk$-2;e*KLtB9d>@^yPvujz*Sn^iKQ}{+q~FSLHs78$^4(BZ zbb1>e-W+By$&uCO_0D_KD*Rs1F^zewIpLr%*3k#ZFz)xAp;KTKABqb-EcR&vv`P05IvJ7 zFyFlO>96&^70TiQuyX!r`M%LosZEP?M z-d%9?8!NZ0KbRw6@dD<-llT>HCw8!qERd#Mq&f`oTXr>LSp+6n$OxQ8Hh<7`K!452 zts9A_--%ByN4#`b?%4HBtBXI@7d_(ogtl=xE8b>&it$v?sWhU zSifQ>@LA_HeAdqLt2r0&=U`H8Rok{~$KlaP&P4bqxRt#}rDxc-q)6{!w_2x|)|0S9 ze|Tk?BOUcyeu|gFY?njkEk2c)v$9v~-SlIZ83n!ed*9G8>b#Z9EOp4ox#fN~w%e-A z{cy(@od6ZT*M0Nu8a!*%_fzI_;^ZZoAbH73&*`J7o|Mhvp`e0W;+3l9{65NLa_TzL zH_KA@d)6Jmx8y1RGS{WIlk!curn7jmrqOjZ|I_dwh}`0*!{RT*Y>&`ZyMx2topvQ| zA)+hy$C(G`)|bTJMwQ^c#H1$)51j%`Nmt$`ve$*12R5?`03XNg(iR?7{uATt2sD8| zo5jvNV}K}Ap72^lE#Hx%$W9VrtDk718-;;sRm`-34_3Yw2V`3gQk-5 zNR^-sblWmwreRalF?Koyk?Y$}zkfL97|4-p;Zo$oggM0%|6R_S2?f)!1cY)f<`GXW z=OJ@oDo30LC9P4>6cm%^B*aZDjS7rx-|d7PKnPV9R>lzqyQKr@z6teyJ~Ylrcc0`s z<@k0ynZ|O4m7Iv;94&byE!0SOisVg@$ywaB$pKS+l(Si&GW?SJB&=lfKq$k}?}7&> zwL_UBCl14ow-o51+c`%ls%k@<=mqKbLPT-`hS`1w5<0K73U9put>?Q$vdvu@F)6z4s^Dq18ey|kPPjFEu2@CY-kcml<9>s(bm(eI z3;P~*&16utnEM2rmww>5!?D!5vp|q9$0xCD9dpa7pJZPzwf!m#UMe|r-zV@eC>1uK zX7-oOe)yu+BgW(!0WfpxX!&Tm-XI#&MXQrfKZuz657heKKqO^m)148L zP)zRGW|iCeb~vt7NXz>?i-;Q6h7W6(_#U+XH2KVTJxjsi#WX7~&T^sx?_ktDrB5E@ z4+t_Tfoy<}MN(lQK(Aw4bQkN7a1Ox(F1SL;mla4K0b4sGVPR7IbKYS!BavsLDKA}s28ChWNjQzv5Pt@;ZRVxy# zwE%66^stGhORZF-SZb+{e3JwONd9BF|F<%tkLqJ1dc_jGQRqmP@960EHYMiox`XVr?{}6(+O4Jup~{fa9#= z8=d8b=zkw;OOaUf8+xK^@!;wc=^Et zqkaI<+H3P&2-Bzcr6tUS3)F)5^FjjGK@F*oa!oM+G-6_Nj`O`48nh+jyG@_S?|Ta2 z7M-W>hl`?Djj=mPTrz=&Z4D}ltdM5pdM*KNOl}7y(-BeFQ z>G;aFnVZG9naJs03Ch)dBN^OCY(}Gu1s?!BUlD3Su4w@3U zXey{f?6R*;E`H(e_YgvMY*NPduIv%iZ+T^ncTPH^L27e-qVz!MEPVmpNGD7UZP6h; zFPENsFVbC>oebG`vUrGeboOJy$NuNospswQo4A!=<{G)~Io5Z^lU6J28mMNpmW^F3t*b zX;5tLBGJ)vb?i_cGm=D2xpi|;T77A&N-aHVM}EphdAI`^(R_$dx>vR47Adbex}f7Td5Td>k4fl1 zknsBuwq@NUv+&rgbmQmkDMl#9lWF|VVB7@oV0G86NtzkTQR%na(iAKgI)j7+TXgRY zG9weC@%JUctDsk5W+V&#VRy|1jxEEY&>)4Ob7MV*KJgnRV6T=oP7j{%7 zG5Wo8df3ay8R%=8M`|v{qO5byY=3IsqxG`=g3!1k&@rKsNtBgh(^oRkGzh~+w+elb zrE?v7S~pc8NyY-O>~#A+tjHcWo@%GcHB~3=0VD0eqWzA0Vp=~G&M8k`QNr)>32yGc zzG!j+C$x}K3BV5R*C<(`4s1=V$9Vm0tJ?_9XSrCgX}^9hnTfv!p_lkYJnE~Zqqk44 z6DrF;rEu~V^reNLE_^X*lU^b@GFcxneWW4Z>N*W?kkgMB|K50Y3yYQMnh-1r&R2q+ z`56>s7zk^W!iq_=#E<@Tvfdg`U#bP6j(K6(YU%P9_?`K{saqhwaWliqtE)pckn`Cn zieLbW^%%&%;PQQ z32LFAG?x--LD!YRDBgKw9$a2}0~4z5L@zcZ zh<>I+sER+s?B49UAUNr?Kp~!D=Ff^pL`G1mI{Evn(CFQa9UqDZKS%W%W+=wHD)qzk zHf>lS3t8)amB=)h&D<52mom`Zs^!LXN_fVd2W76K4`dDbj$)}PW5Ar;_cM99;qfPW z*-fAnBHsA#n`-7GuP7h&`zAXDX_5D+n`(<)&#};VfYtTq zwIaU0##PdEm&;(ni31+KraDk93u8>U5+F)S%Dx{~U&iqe%E$^jD@B0ntd2l+*6i&x zJ8+RN!t@p^v3Opjfe`PX6!c&z-ip@<3XfML`US6G#}jL$su8c4(z{DUp4d8fwsdOx zxs4S2M4(#-H|=a_dtA)3M3rn zB)6dE)hWYetNG=Ek(>2S9pfHjJC{bVpZP`{$MxCMQmdEZ4Jm7q6B*de_T@pJ6u81j zJGxx1c}OBw3eL9sZWougHyv|@r#}%l$bI+vL%2-h@|!AMMl+la$cR)I3j!Ord&CAg zhn1cpv?pL+IU4#i$^RE5ZjOH-acdDWY7w$CGU^Z#vizgN#PqMde4?^2Bj>K9iIja?zBFLw{L+vt@i~To_pv=9LP$ z&ETW2a~`&+=Se#k0p5ttnv0IMmzSwyz_!SGvc`Q9UJ@Z!07X|VRY+R7mk$u+A6tt5 z^8?B9FBj}zZ?XTYx7Zn3SQunYY|Na@nFyJf7&$rl`Qc$4og7RItl?qYvZlFV-ISNp zzuRNe$4QA2<6NQI6Ek0o$f0M0B*_^BL5WC#S^N^lr{qla6Vhj7QGP8EM;MA`s0!FG zS2V9gsCNU$=d0vfTDz*X+7Mi|Y^*?D7Shn|(qCxGn*RN}dIElS`o3=Ksc^&f{hj;$ z0dTzO1{DO-$9Dlyt^(Alt`mGm0{gYacZ{zCZd1KGtw1O+NUR$jDM^#N+xOZ;Q}lgEAp6c&b7}+F&eZa%5N~xa#Dxa}KTIfT5btgj;AKIfSJ1_H@5+8BeTU9WqiNm^e)tdW4tMJn?aJK!_?q7 z-;?VlZvFy{KG6D6?Eqjyh~4)F-19!Z{0?@VZvQv~)uRDoqSE?dLe^*crScv-lN6op$KH8a4pI#y73uYF2F34!`&o5mDZ}y|_YU78~ytm0JRPDXD z3hTE9jBhZj@9&m<6pYeqKjXb~LFT)a{wsJ;jBDfw*aM7JC3}ne3$C>h26{ka?e88K zqAQfX5N}*eM&W_=t;Wlljv0s<{76xtnGCbKpw{N+MA$;(M(364t|}KXxu<&*8FYq< zimE!ziAiahh)5XN`-da##-v6@Pa#!PaSu&P>HCi&(O&~yuWjl^Q9`w-;5j6UE3~y2 zDN6x1DwJ`nRaB*ISAqdX==oa7aZQ%=vDD5i>=yK;@LZk@dre|o){9v~q|3tA?&(Xk zG8Z&*4L`^;|K^mvJQJ-_mssF*NA>gli0;DDrrU%q zImUCTAgTZH#9nTpCy>n_Sa4f3Qln)$YkF5WeU2YqP*~V1c@c=yVLF>(VcS{=r#*RG zdx5VLT=8P?LyMbqY2>=zwcHr7BB_jGsj|#BVoR24dy|Y!+$L_jaI=$%BfXHiDK-t! z%yc70V#jaF)S{_ThP~2RqhWi#NuL)>d{ZLEc&#X^o#oi%uw=+wfAo~hTu}SNN-#ag z2+62aQRa`6F?6$J4=#;xau$l_0$HKLq(v!hsM4u}@!+JqUGl|1l-p9;$c@skzf(w_ zD$;Bdisuo5{{pl>^lL|m6rSD75rBYOWXrB%&nWa;T9j-46qXU)4dW8SQDgR!a$i?W zt4#eQhlYeBXfm+Ed&Z^-*|jQx?xC{^#1~!C!RaVy1LM;O?Kh=suGHqp=Hg;YY>1z^ z%E(C9-Jvrg6BuT9jIg9k`;uWSp0RaVEhBw$EJ;4|k_p{vFmOy=;@zkoZYg#|K+o3W z=PMPu_!yz*k6a|Ak2GskPtqz)r&>TOg?W1|H4^(Lxt=_;i-LypW7dM6;6J)ND?iOV%1T46=%<0Lz(qR_Y)*k#J^LnSjmiV)a)eX1J z07qwcwoWM50GkWP+USE_&1GAV1Vy34qH=`Pk3jSQaL+WBy7H<+H&K9LBM1rO78ZG* zc?cCYuvuE_jk!JT?Uf*gUlmxps6^N+gU}OEiPF=XH*rmlTR9#ZE{kS|(-|-Mwe0 zrtL_TULKd&udQ#bv-K>Nu;SIUE^|!AkpHnaC*wOL`*T{>=`6i%Pf5gwAp@6)Pu&n? zX|?SO9sTFriDtPi*K$jNtz|D3Z;PzN2yKIYc?pmrP~~ z#_|10Ljc8(7pC6rd3fKE72lRyMORN(p|no_Y>D?hY`J8iR?lGO9obzulbMK1$;NM? ze*w0Ju$!=ZeNoi5>B#MfwDr>Nr@Tse_m82w%4J7EkCxMRt0a@;{ci+~`i4~IjX zR1G+l1Hn}wNE6M0P!y3UM^Sqw=OA51IxYn(P7EBYGG4UxYOV2SL_nt)=3wx)`N(;i?&x+zHjod9}dfkHkTpPL{M@3>`X zL!~xdeY(-kKQkNL#w33{(vG@iann0Lf!&jic)z=ccsKhXaOyk$jZpao(@*3UB(yIt zgyut%g*F47NF2t@9(jgDkmY9HVB#QCMA$&QfZEQ-9CFPUbtn8-0t7Rk%RW_s>QV3T zvDOn}4QA8|$=3mUGrBf^#s_cU22uIwJ_V4urT#$p{KMA;PB2J;iF=_3!<6XoGyJYQ zs%oSK>w#6YhOBeU#{qG~Jqvf(C7~-uSH2)Llat4ljblh>a(f1E@8&qhk)r)qS~yhg zpls1Y7uqgSXIK-cNr}$9|3i%q zm*P#E$yN9p40=$yJEr#JHym^r-&Ak3sS75`SZ-f`Z}6W}xh<{hEfP2KE)m_;T8T^Y zSBm&kZgnaA9ND12g6vCj*tXE|jc^=_uk2oqd!X^X9gsB1tg=(=`U7@x{KK5U-ah|v zg&>YSt{b+su+osk>#XmAZjLC5Y4X>k$kERw|I;vn*%JPbuB$eGYI@`HR;AX(EQVj3>dN-N9W-mAFI z?W+aOM9-uAyl@evqs z45Qsc#@*Yyy*TIbu2|+L_G*nzrjo-KxQuuAuwkRnBM0j~ZO)6w)(fQd@6VKqno#RG5)@*F7Wz!mX0m5^9m(-XQrt^wT3T8*A`DP% z>yvgIwj*2B7tRF}Dl7_p-#0WrDy#e|$5f2Jh=~)$9>iGoclpmU+*bJRj9!Rt;=?l~ z@q7cezE(?zniGXihX&5ndT&%7=U{r9lWSXaE$|w4tk6)7f|=Uex$Xy|wzeGH4Ge7P zDHvFfCdl0lq-$u$VPPGP?T)lY7y77+wW?Ma@7Ap>&c`h*&dVtq8R^jamz3f1*rqe+ zi<2EwSO?6CN=xik1_Cg>78}1HxEH7w7nsRp+znzL9YFTWkP4}Y3JdN{96y!30xeC7 z164#WOEoM!AZgC<^6JZ~i@i13#3B#5a@ zrqD}iYZFe&Oa-O=I@0c@KMv3sIF-wqoUpA-qpes=O5Nz-!keu{9J6Sc^f1xA2>4*X z92$?HIbI4!4BjhU)lU7DklU*cb1Rbn{P9o=4fp2cl+gwFsGg7TN!*R0kYu6_6xtl9 zO^rKP0CzJ_$r~B=QvE2nPoTx;tS9DBkcUETq~mX%5Tvd+TC;@vshxHfOSeJetsbXD zc5qou*RrJTAXeNtlCf~ZiD74+d;%qjPX89XcBW$5UL+8g2bWvBJ z&vA%W(1KuahZ=e^E;hR41;P%ol#+8wYb7+z5L{Ai<94G(on22CPZ1LzOT&>F-pc77 z>pzQtmR5zH@2x|C-se>9&7T?XZXVml6Vv-mnB&@O?FH_p5-$^*V5a?(q9*5*hOaNJ z(2(bD=C{$ticph}L(9-* zW93i7V0SW$g`J^T!&fF@=7^yB0!oZVO)g5O6|7Ea4f*q;)s>45M@AamUM zWI9?_a*QL2qf_)LhOtJv2 z0B${5G5r{|$X~T!uZQrc_+u{Z1X+ESfu2WvPj#@)K>9mqP`5H;Ag3g-U-yf(*uqTIoiKSK#hL3SrwUc3y-;wD#7{%44a{sCSRDZ0#r9&ki#{L8e`iAVV0-SXPr=K5 zUv!|A_>`6a(jp)nU$@o^`+0M4wb<0thDi=s9C9HJ9ieVk^g(+j1OkC5#eyBL#woiC{`+3e4)7KjCH!I|`T^&F+q-0v<5GofW*X&auB|C%SSF9L2>cN zmhW69Z}55FC@Na21xV{l+RNQ7J4HRdymtMZjzfX^xg$N<08?(BJ&JV;?%kb#;8JWm zcL`;+P?D-nOh3BI%whu2I1DtyF1WyB8N>Y|2)t%GR z?}tM6!Wn>HdPIP07Xne36XNA9Jys1?WS}rc$E6s)TFJSdRI7I3x`*q6Uphzw&gTuP z#N<8Uhawo^izI@sT*MxG3$&r>@v}2Ez(X~q;8yei21`g zr;<4*GeP?x#&>#!Ie>Fi5);NJaBPai1tIs98pvmkBGm9x6r*i-+A}aUEJXlHh$5&* zc?dK2&l)Vp58&M2b6C`g=J5v`%pIoEz!1OJuI)R_G}xo0`9S3Xc{29X>HwVE7)h}l zL#cCske2D1(!i<4uG}Wh5DL8N0@-Ytn?K^Hw z`@Ey5NU%xx&8xciJA}wrf85c}vdpB>YPU~E63h4tNO4w|45S}nclMqhyyg$57%;er>z}!oeT7Xh(PhJI?W4*pE(N-Zw(nIy!@|AZ@17He{NuaMVcD1IzeL z?0zglU7kwPOp9}nfo&P$mW$w(se2bNNlaRk+>bYgy#>fm`BgxFtXxUHG_+wNF^+M> zUlRiTJVA-L8y2oo=aE`LC@9&k<(pBgm_cB@Bs z{IKF#v$V_(t$t}ho9B*4df1n~5ID|PVleXHqvZ#d`}*OEc$ojZK#eI{fog&*kGmADyH_LHGQ;da#X|wS1oh@D7w0@}R*&;_lL!=pw5l;zUpmFVQ zox8*Jkl$te9Fq-!=aHrvD>Yy^Y(2WFsVIZOvcFk*a;>M6z=ew3($Jh*)FP)C6I+eY zb440?F1!Uch32os8fA>gEjr4U(mOIM0)*reLd9q}T}x*xeq3Z`y6=yA8w|5gWq^~- zRLew~Mke3A6w=c?6CK$zPEw)=SvywM%RcxtbDM*Eu<4qlq(r{ZKY7P}x?PyW=U($n zS7Z-UXLKUtLN?=u($|+3D7iLJyVh;wH3JO2d|cS07$=m`KgZ77^#$Zj7&8n6DrWXtI>}4(X z>QiCFf@HR-gCpb4r=LtXE4)v2-2gE@`jgXQLxoj0Eajk&VNU9*gaZ5aq{lLNJ@yus z1)W7tPLF_K^%K*XOc{5^;!nd))u{^L3#p}4q(zP=*~0t6o{qI}?ml_dQ8N$hYQE8| ziin&P{_0#V0Sjf3D<@c{w+Scq(xbNZuRC9xXw9_;7s1J?sK;`=gZTp4%g~L@L1Mh= z>(t%rUyduV)V`*%vt9d~n6p2=$UP60PP%}p9C!YP6ewtaPzR$MG}~c4FOOaKoz1oV zn!Jc7>6@8Rd=!wnpCCum9)A5_ zV*O12i1q)M}gI(PY;h&v(rf<8|hi;nVSfq1!0ysx|eYa_|=101?I!dAb0j@f54^wS+k1 zFp-~dOR&gAaScV&r%hIw)8t&6N9(N7=k!BzQW;hk+D-zS53@kv_(02$pYi_tCJUZE+5%Azu8<+(e|S>YzG3uzFkq?>Ub}mIJ1D$tGBOX%Yd}bBfX8&x9)NVr)-azj8a$a?_6dWz9 zwDq8$4;%Py$?SNWfeNB6*D@X9J-_taVH8rrJk5WxqQnd*PVNC~n?1&t2{^DSx#M8J zS^y3Af;6ibtlhI+rjL5S!GA-ev(4QXb71f)j1u^8X*%k*)#*4&e=^IfMCG94I!e#_ zV!f#Q=)TrzI8t~1f9;)lJk{I#=yMuKRLYb&8bsKiX%9k$ltLLYQ-*EGW*fE)X+}{J zrHN7*N+qETA!W$WU@FI$L{U*BnpAh~Iz#qn{qFsp@9W;*{pWCAoxLB=8lLB~KI^@n zwVwUi@k^3ZCEu;H7LZ8{=&y^YkFrd%D7i-|9$Lx}65OCFXV&8PE#XnM&AH?+@wRuK zD^EQn?xfN9P`oqymSH$Dd;X=+(Xv@j(~`{2Ne8(|2?VL!TCXt8Z;kVf=TT!SAKu{!R}Q;YvlC{t-CT6r=IybB#)Jj=wP7cRwN=rq{Qc@S%H_-3 zHM7>Mv)|^l9bA6h_4m$drQ>*@O{Vxs)$-Crzve)$no7c$y zt~BRWrq4ioQE$Y_jCSqg@rx(LH(6Z_Q8koY%&9bffQlOJdQ*j-SkhV;didN*_2U~v zlyr@xENW7pYM<=AS5Yjt@mAQ3+O6q3X37_Y?WG>q*e^F4qW8 zq5DWkKBuNwV)~+88Ua%^dp~7*CyGSaIgL!wml|?wZWxNXy6ePkM%w6RwpY4SsnFXQ zAGLAQ{Z-8ey-uZH-QMhZfK}G8u+Q+F@tUno4$HF@ng`W&AIwpg7Syx-{i)QekxMLl z>zkbeE>D8whN9nDzul}-Sj>-2_c4}ac7CzsET1-sG0&yVT&GUqu}A&7Okd&XsL4Cr zYF4H*7Nzz}vC?Bu=TN=H{_HpYQ(0HnNuPHR7-Cwzv(5C+)}J?by2QqY*Ls8IDm`Tj zzj`*>E!guJuX#9=x`ICddDH)%TGVtAzvEVGV5RlIC!Y|rT#&Gl`9(B=imY6CskZmG z8V~Ilucmvdw(Ujs55(O{?h}EX!FkpG;@!=8OHYZ;H$MOB%EIR<&Bl4nF&PT{=;;ee zfx3mP&2hMKUFt(o8@Jg$**AjcM2RQ1#1AbfHm}KcO!TV7DI=|9@@%hNzM-f+&$W^<|XN@IZ=P2Ib-ip z$~VCqP2FiOB6j`4Xas+kqV(&RhWZYgMU;jw)@C^~XWd>T%09PxchK>|J&BF#Io8+` zy{x<#&!vLdwlC*1D9^(kld4^$i_8K+5^m<9y7Dvry}$eTumAq>~!Ylb>ZG!{#1abC)gI-g@jtebXLw_e0iw$41|; zO&Be8mVL$Yd^Kknd`)RV9N+nMLiL+#&tEtx%gJX_LsdfVHW|HDv)CJN-57PCG2w~_JPD+fOBZTU2L>M@7idb_PAXYq$`S#Hz2_}lG| zdGXJzyB&-xn@w3VL5hqArL>I4n&}_EpU|pZXFjeG+vD3W`bkSC`F*_(!&ql|#zVg= z&GBEVeZunMs#l_S?|yadN>vygmu=+_A5i*WV@{Jqun|S$PrY zlxp!lEtAl0d~u1t{^4(P5=V0HsmLU2@3C8aA-3!749%AQHzqN!m%aQr@bxl#YsXIg zb)HEHtGB$CS$$~EOKi1RqtYyu1%2f+#2HDmoIg;U7m&wt{o7|%!ekS{_oA|NpQ?}GMHZRx27^`tOnqx<;G{*+S zQ#*^UH%jKMcPipf4ER0Wq)Pp0j(mO>?dnk-ZS^(V{P_EVV>%J4iAnr6!{5Zq6|bxL z6<=%Vw)fTz=WmzHD{paS3Fe&am>Jr*QgdbOxi1kTmN(j*Y&_L3VToQ3(qBKAR%B!4 z(tL%Ydig#jB(-{fw+=%wlcy$s{h7B}mHH5%%u3+7juQb;nWZ%i(GFWRF@Mzx-WtXD}kNZjTzZ1kJCIna_6=_63mqK>J8h|lTPF~Y&rd`a%-W%mV}Mo z`9a3B-_4^rGkv~`-sux``YyU}Z-(c{po4kp3H4Yzlg-C=b-bEaS{GAy#>+KlQ(m`2 z#NN5d(v@F7-f3+h?!*xvmDRH6%R`M?1Q{Cs>^J*~uxCXx26K zU+u5*2@4k0I-J-MuXSvk_$2;yXM37rMYtr(P)AUsJYY!6=#Evl;Q-(2U5lUYwi#ME z$%p@{+};bmJEq9Fc&)ysJ$2{lc!M_zdwXXrF!E8loK^2G)GpLKsYCZlK>rtdvkN7< zer1brkjq!v9Cj&u$b~mN&k&v!Q&}-03dELUzGIC4S=09^dy4&DMX4v8YFw#Uk0WoVsS| zT{~2_uc~G_GG0EEzqI!BQtD&o4Eypibi$FT^>6Ek z%;P%tC4UyIUonFHHhXR!Fo$KB(!sFzd|8XG&a!j! zt(v(bzWu@0`@2mp7_OS4ww^xQ;+0J15KiB%Q=xvJc*J+>ip=C4Lh0QyL(|5@x|<%i zmD`%wJ>K5z__(vgSaIR1yQA)=()`}_^siq%Ev9U|cth2?ZqQNA5eXQU9E`A1zqKm5 zD`elO@*9y!X>vr(;yqYUZWQOm;f;&T67~Gknzf`~tnJp?*16VsHd8IBM8 zapVr$A_{4K90pNd+Tv1?%uLX)r%J?&(~B&j_qGqwYGQ8s;lR3UyD4bSL1ji zKx9fm#H$@9Yx}~7%hsDu;PWD^LjP1#@a|U*p zeD9{6+1dRmxny|YLUQMxkQ)-SnrD>7Y~J<6eTwPjO^M4Aw0iFU-v3&m>O{F`xJ<&C zq2#7zqvkaM51sFw*DNu0*>I9leL_CO_HjX*r^T1TqYEWWq&E#Xx#4SX&(?6>FOqv+ zYE!|&^UkN7>j&Jnoi2&o);-tQWarb%v4Z)sEs6rM4j=9x*BDRPu=SvJxNTD)7B;-z zw8ps9YWwq7392_OvmSem=39;3-mvCsoF=|4dA8Rjt49Z9`AQ3P zWCW{LEA7Z$A`=sy@+6odb4k={mT=VIo!u+67Ug|$`PRR+G%JTud}GN|*+apVn~mP{ z_wenscpq*0eFa7Q!|Ja(h}`Js@$x7AmWYv+oAH;dn+cLaQObq`LQ{K`oy|L>KUC?n ztF0o?GFI%Zp*6~K!k=9d2EXl&o6p8o-3w1Veo5Vl8&p2FwEVsE+8sx$BApfq;i|H) zeh76=l+aYu;GJ~QEYZ$x5x}kPDaJ{)IQ}R~zx-C*t>B0? zozcZ&M;GVvg}SU(XpuOP);hBTmwY8HHS8pm)3#4fFU2{;H=|$vP1@DE_>}OjR5hg! zQ}rfG7D$HPh@IBmI4#3BmDzb)YlB>c!?SNrQrRA}gGM8Q!XwKZyPiK8Nw<#}uAQc? zH7E99#j51wQ-+?ueLGkET&~h+MofB*=ZXyqpBK*_NNe3Nef=&00zCTlw^t&sc1_ah zZ%XzKpC7I^GiA2ZiA71PMAAznhF2PxMyXzQ61XfdxLMS`^X!Yg$ zq{|l_=PyDke~-#lXbRtwJ$=4LVr9$2`4yIFqXKm~DYM*_3f7JaqOaDzNZ_lOi3l&h zTIbJ_|Ip-ZscZE2sD{$NsW{k>-|TyC1$ z3s`1XN1US#?zTJGG0FMjUF6~ld+8(MOx2YyJ{h;fQkJK_PqpdZv+QK7nf0rDzN0G# zg%m0;F;W`hB2Jxrxa@@ZWO~;w>S(U)SEGHGha=-o`8PTkn9e#CLYTHg|*(;svZVOH)1bjO4P6)Xu^YVDk z7f;ErafN$z)V0&fvcjsDoIRltym>D^k(gMFP`yqFvXXQsQSkGA65qEbZ#PgiY4E5ev! z?5oH0@LUt>&omD;v2Y3Xa$&eCF4xft)(rOc^#;u-1bef50yKlQ6`fq!&P+}4nk&{& zRQRF7@zPer)Dblr1yR%y)be+A(=^jJ_@fYbr>*G0;rME5XaofXsRv=|Y=3tRgu!5F zP*4pNRRbDo0Uh=AlUY`K< zA01P7VS8(UKWHH8mn{OQ--*N2K`AJvhM;OF z#R9=KQCt(lR4Irig`%awqxqwxG27MCE#$vb@)ynjbbx>k7#siZ-Qw;2r)P|fHTBsp zf!v{EsILnbnKpP7GHu8_W{I9$Ib=N=gz&}#{cXa*hu7Bi# zf292H==x{w`iXt8z6XoWzXAlEzXJ@W4~Qy)K=|-qL}%P^=+8Jt-!s6M~#c`fC`l z!17X`=>nGFe}-EKj^PtCV~CcJVhg5h)$k`rTx?;!euKWgsYt*|UqfN<25^q>pz*r@ zO8wNwLLIfmQ`*j-RQ2hfx=!TqQPqoTThymMi#(>*D=u|5of#3dy7s&8z*} zS7eWwRBDb^zb`3lNzLu2Nn+8)u}i7#^0Ooo-|pUa;M+C{yAD&KmZ`bXYWpN6ONiUW zlr>eJO&PM!9;Dn?o0b)2SUIL4s%N6SwO=LD(Lrcgsie*%ns||S%&D6)kBaEQA%4|q zmFK>Q?HfE)lwCj%Tl!{+LFhxJL#hUh9P{I17c^81VpShXnjU<)*F735IiGDM>MmSy zD>zM(cAdU$8GZloi_abT2d39a%&og~x-ueAEH%Ix@eD&ks+?63hE!Ms{x9izrTf3OElS{5iSyu61o7P-m+aGfyd*z=1IW9pW1k(@^#v2WoL!~Ox`t%D-pB1@lU1y5f$ z98^3t#;Z80dD+HcseARdSyoj3&>PzIg4ds{$UHKlQM@FPyJ0i>fjRtc1nIjd%rh$&b4$&4EJr~#J3qg-=2~(j!8E#Psu=% zhqDN4FcYx)K}8ye(J<^TFa!I z7im*wJ)-GK8^@Fx*eVspReNj^r8Y{n0-Xa&*G*dl+;GBH<{bFzcBShLkCa*0kzZ=? z*m9lTCg~Y70D4jRXH}*L>kXVzZi%#gd@ovJta?31I_=<(%1v98Sqm7>ppydzTT`xy zG_CQ%iBM_J7=hI~O)6KOfDT+YZSdFDdt31R|G(*_NpBfM?U5C)Cc zUK)x}$TAe7N0947sW_<(jDnE*haoswhSJC~Do*MPMkDtHW03odQ@G2Q34W&GIL_-o z8cxv2G8)2r&1nRM0k*a$)<>Wy*cj%K5j2wT2^x1fG*ORA!AWgU5gPA!p-~CaxS~<1 z7^%-x29+cOi!D-pbjUSlAmlN`KuPx&je#*p*N8!&yfQjYBVAuQfs)1~oxn)_ql0aD zUOVUn9pUveonYX+eg-m<@99(oWGG7N3)j&T^$-e$!8@)H z3W9PEaei$B$f(>CDjpewca8uK#d*g)m?UX*p8kQS`jGA!Aj5g>1u_u-@U{!)aUTBx zJqAJQADuzAmx1tJUxdOydFNdq!+CjxAP9+X2!i4i-u@vdLFM%yf>S7D8EMWzz-&+O z`U+r>dp0-0_XvZ)dB#2H9u2GoCg_1T2xymgeSm;IlVvoBj1G}8$TGkHQhi{Yl4U4F zhCyWHH4OshHFA9rI}nH+U~VFph1dbsFC;w_Vh1=2CF?=#0QVqS4`K)40$C4Y2MVzR zh1dZewMD8AgV+J?BeEXE4lv)7^&oZtHj?!qc3==Y0H;Z1!5mALLF@qcG+7T~2M)0V zhu8s_Os)@N2Ux?A^&obDdzz$2KbT>>wa^fVq=g7Geir8A%V! zlVlmh4l2YBD#Q*jcarOa*a6o3WIc!-V2wo51H2^5Aa(%mll35W&>(iuAa(%8lk0=n z0a#1cgV+JMO46f4>;UU4vL3_^I>Zh-#10TQlIw%m0r*SSgV@1<*a6lhq_SW=NtQwE z0PzP|4`K&cTafi2c7T|ZtVgy31j-~m?)sUxEI9Iq$jEk}kk}rD@EnEk90hS6xm~gy zD2TC1dJvwY5T2t5#0~^v2Ur)A+aO;L?mCRD2eAXfa}-<$ zZd^+)3$X*lR%AWM^?>jkh437O@SGbrlG?x^c0hQ}UGtO6LhJy01!O&l9T1*_#WJ}p zi1SGGL3oaW^)6WtGOi#zM!j@Uk0h0FYy13I(M?#ubF;U{j0SKM2pcu^-8&5T2t{$n}8m9EI>4SVC?C z!gG`c8CMXVqY$2R_u)u(fIT;|3}Oe^k09wmc#cANjzV~j(jj*6vYUH-N&6+>`a*b) z(jj(0c#hH`c0hQJLU@isc#bk4c0hQJG9dF6gy$#&Vh7k8p5S}n7cOggV`mJ)bFfJ} zu`DQrL3oaVeas1M{Lmxc9~gw^7=-5-gy-CSbaER!wsG5q@En8i9E0#2gYX>Vjm5eC zBkj3y+a=#07$iOiS-@mFAUww)JjWnB$9UNd`p5Mt>H0!=jzM@19-mI`AB5)^BtFL= zJO|HUC)WqzIrnUWbbTQ_#~?h%AUwx-+0FF}-~!1nkoX*f@En8i9E0#2gYX=K@En80 z=U{`E>;nkTF$m8wNPLb#c#iS18~6nW_X5c;5T0WYo?{T6V-TKW5T0X@_#A`qoO|6! zV*?VObI;YtdXRAi;W-B3ImXLwuCI9aJGpZXgy$H9=NN?N7=-5-gy$H9=NN?N7=-5- zgy$H9=im^H>_13+jzM^iL3oZqc#c7MjzM^iLE>|am+@TRLwF7jK*)ALc#c7M4h{{; zWg$GrAUww)JjWnB#~?iCo)wYCE`;Y8BtGY!|MHdvoA3}B`ToEmJO_uQkg{Z+;}D*6 z&j`qUAzu$1!gCxFpM!%da(xhr(gdVk$hd;=96T$HTo%G}91@@75T4@@o^#nv@*gkbxnl^z za~#5R9Kv%P!gCzLa~#5R9Kv&uG(qk&gy-P!9ij)h9uS`65T4@@p5qXn;}D+X5T4@@ zp5qXn;}D+X5T4@@p5qXn;}D*6&!S1=0K#(|!gCzLa~#5RZf*d%4aoI?@EnKmoPhA0 zfW+qngy#f==LBTGoq+J1fbg7v#OK_6Bwl+#auitx;W+`}IRW800pU3}4~5h&gy#f= z=O7UUQWjzdgy#f==LCf3AQ_C@288F}v5pWu@_fb3t0LJ8;W+_`&j|?62?);#2+s)! z&j|?6x!IW$`Uhera!w$~5P|TVfbg7v@EjzFO>6_q*#v~=1cc`Vgy-D+k_mnOQ6J=b zK;m-(!gB(`a{|J10>X0w!gB(`a{>~dgVb2E9T1)q5T0{C=T7nggy#e#J|`eNCm=j0 zAUr1^JSQML=jItr=pT1n0sewZ1!l&-bAJg4&j|?62?)=*^-XAlI}RW`Cm=lM=690I zLaqmd=LCf31cc`VBt9n~Jm==FlKKMSIY^*{=t1m&@SK3~oPh8gBu_)yfXurPo)ZwB z6A+#g5T1hsZF0L1o`Zx@h#t>a z1&PBD8QBghgy&QU&p|Rfq(1VvqC$AieI^I*_yY;I5E;Y{2+yeyo>L(_2Z_?8Hn_9H zguZ}z17sLaoHr0~$^_1jqm@Cg_1A z3eX*rjJr0Qs0Vib0jGIo+&Gk11~zkfWgx$pq{kqAjsRpUbMqc}>f=ZafMi zSa5GmEDItC-n~-bWRP0=w;spe$&dy^gY^IR9~%7msX!-x&d<-7fHYo^ z#xE?maN(-;2H;ya|M=huko^DYp2h}$=2nHDz0%-*w9=g84c>A+$J=u%N~Ixx^!$GT Dio)aD literal 0 HcmV?d00001 From 8f8a266f4ebf44c6fa17288577d9f25a2da2bc30 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 10 Jan 2024 13:31:05 +0700 Subject: [PATCH 037/128] [WalletConnect/BNB]: Add support for WalletConnect signing requests (#3632) * [WalletConnect/BNB]: Add `WalletConnectSigner` optional module * [WalletConnect/BNB]: Implement `cosmos_signAmino` handler * TODO fix compilation error * [WalletConnect/BNB]: POC * [WalletConnect/BNB]: Move `TxBuilder` implementation * [WalletConnect/BNB]: Finalize `WalletConnector` in `CoinEntry` * [WalletConnect/BNB]: Add `tw_wallet_connect_request_parse` FFI * [WalletConnect/BNB]: Add `signature` and `signature_json` to Binance::SigningOutput * [WalletConnect/BNB]: Add `TWWalletConnectRequestParse` C FFI * Improve WalletConnect.proto interface * [WalletConnect/BNB]: Add Android test * [WalletConnect/BNB]: Add iOS test * [WalletConnect/BNB]: Update the year in Copyright * [WalletConnect/BNB]: Fix codegen-v2 template * [WalletConnect/BNB]: Minor changes * [CI] Trigger CI * [WalletConnect/BNB]: Fix C++ tests --------- Co-authored-by: Sztergbaum Roman --- .github/workflows/codegen-v2.yml | 1 + .../TestBinanceWalletConnectSigning.kt | 46 ++ .../rust/templates/blockchain_crate/entry.rs | 2 + .../src/tests/samples/class.output.swift | 2 +- .../src/tests/samples/enum.output.swift | 2 +- .../tests/samples/enum_extension.output.swift | 2 +- .../tests/samples/enum_private.output.swift | 2 +- .../tests/samples/non-associated.output.swift | 2 +- .../src/tests/samples/optional.output.swift | 2 +- .../tests/samples/private_class.output.swift | 2 +- .../src/tests/samples/struct.output.swift | 2 +- .../TrustWalletCore/TWWalletConnectRequest.h | 27 + rust/chains/tw_binance/Cargo.toml | 2 +- rust/chains/tw_binance/src/address.rs | 17 +- rust/chains/tw_binance/src/amino.rs | 2 +- rust/chains/tw_binance/src/compiler.rs | 22 +- rust/chains/tw_binance/src/context.rs | 23 + rust/chains/tw_binance/src/entry.rs | 9 +- rust/chains/tw_binance/src/lib.rs | 3 +- rust/chains/tw_binance/src/modules/mod.rs | 3 +- .../tw_binance/src/modules/preimager.rs | 2 +- .../tw_binance/src/modules/serializer.rs | 10 +- .../tw_binance/src/modules/tx_builder.rs | 496 +----------------- .../src/modules/wallet_connect/connector.rs | 56 ++ .../src/modules/wallet_connect/mod.rs | 8 + .../src/modules/wallet_connect/types.rs | 14 + rust/chains/tw_binance/src/signature.rs | 2 +- rust/chains/tw_binance/src/signer.rs | 20 +- .../src/transaction/message/htlt_order.rs | 164 ++++-- .../tw_binance/src/transaction/message/mod.rs | 256 +++++++-- .../src/transaction/message/send_order.rs | 67 ++- .../message/side_chain_delegate.rs | 206 +++++--- .../transaction/message/time_lock_order.rs | 126 +++-- .../src/transaction/message/token_order.rs | 175 +++--- .../src/transaction/message/trade_order.rs | 86 ++- .../transaction/message/tranfer_out_order.rs | 59 ++- rust/chains/tw_binance/src/transaction/mod.rs | 20 +- rust/chains/tw_cosmos/src/entry.rs | 2 + rust/chains/tw_greenfield/src/entry.rs | 2 + rust/chains/tw_native_evmos/src/entry.rs | 2 + rust/chains/tw_native_injective/src/entry.rs | 2 + rust/chains/tw_thorchain/src/entry.rs | 2 + rust/tw_any_coin/src/ffi/mod.rs | 1 + .../src/ffi/tw_wallet_connect_request.rs | 31 ++ rust/tw_any_coin/src/lib.rs | 1 + rust/tw_any_coin/src/test_utils/mod.rs | 1 + .../src/test_utils/wallet_connect_utils.rs | 34 ++ .../tw_any_coin/src/wallet_connect_request.rs | 25 + .../tests/chains/binance/binance_address.rs | 2 +- .../tests/chains/binance/binance_compile.rs | 17 +- .../tests/chains/binance/binance_sign.rs | 112 +++- .../chains/binance/binance_wallet_connect.rs | 71 +++ .../binance/data/wc_sign_request_case_1.json | 37 ++ rust/tw_any_coin/tests/chains/binance/mod.rs | 12 +- rust/tw_aptos/src/entry.rs | 2 + rust/tw_bech32_address/src/lib.rs | 15 +- rust/tw_bitcoin/src/entry.rs | 2 + rust/tw_coin_entry/src/coin_entry.rs | 12 + rust/tw_coin_entry/src/coin_entry_ext.rs | 23 + rust/tw_coin_entry/src/modules/mod.rs | 1 + .../src/modules/wallet_connector.rs | 30 ++ rust/tw_cosmos_sdk/Cargo.toml | 2 +- .../tw_cosmos_sdk/src/public_key/secp256k1.rs | 11 + rust/tw_ethereum/src/entry.rs | 2 + rust/tw_internet_computer/src/entry.rs | 3 + rust/tw_misc/src/serde.rs | 41 +- rust/tw_ronin/src/entry.rs | 2 + src/interface/TWWalletConnectRequest.cpp | 20 + src/proto/Binance.proto | 6 + src/proto/WalletConnect.proto | 44 ++ .../Tests/Blockchains/BinanceChainTests.swift | 31 ++ .../chains/Binance/TWWalletConnectSigning.cpp | 54 ++ .../Binance/TransactionCompilerTests.cpp | 1 - .../interface/TWTransactionCompilerTests.cpp | 1 - 74 files changed, 1734 insertions(+), 863 deletions(-) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/binance/TestBinanceWalletConnectSigning.kt create mode 100644 include/TrustWalletCore/TWWalletConnectRequest.h create mode 100644 rust/chains/tw_binance/src/context.rs create mode 100644 rust/chains/tw_binance/src/modules/wallet_connect/connector.rs create mode 100644 rust/chains/tw_binance/src/modules/wallet_connect/mod.rs create mode 100644 rust/chains/tw_binance/src/modules/wallet_connect/types.rs create mode 100644 rust/tw_any_coin/src/ffi/tw_wallet_connect_request.rs create mode 100644 rust/tw_any_coin/src/test_utils/wallet_connect_utils.rs create mode 100644 rust/tw_any_coin/src/wallet_connect_request.rs create mode 100644 rust/tw_any_coin/tests/chains/binance/binance_wallet_connect.rs create mode 100644 rust/tw_any_coin/tests/chains/binance/data/wc_sign_request_case_1.json create mode 100644 rust/tw_coin_entry/src/modules/wallet_connector.rs create mode 100644 src/interface/TWWalletConnectRequest.cpp create mode 100644 src/proto/WalletConnect.proto create mode 100644 tests/chains/Binance/TWWalletConnectSigning.cpp diff --git a/.github/workflows/codegen-v2.yml b/.github/workflows/codegen-v2.yml index 108b8e42448..f8ced68b019 100644 --- a/.github/workflows/codegen-v2.yml +++ b/.github/workflows/codegen-v2.yml @@ -13,6 +13,7 @@ on: jobs: test: runs-on: ubuntu-latest + if: github.event.pull_request.draft == false steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/binance/TestBinanceWalletConnectSigning.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/binance/TestBinanceWalletConnectSigning.kt new file mode 100644 index 00000000000..a66c2dc9776 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/binance/TestBinanceWalletConnectSigning.kt @@ -0,0 +1,46 @@ +package com.trustwallet.core.app.blockchains.binance + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.toHexBytes +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.proto.Binance.SigningOutput +import wallet.core.jni.proto.WalletConnect +import wallet.core.jni.* +import wallet.core.jni.CoinType.BINANCE +import wallet.core.java.AnySigner +import wallet.core.jni.proto.Common + +class TestBinanceWalletConnectSigning { + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testSignBinanceTransactionFromWalletConnectRequest() { + // Step 1: Parse a signing request received through WalletConnect. + + val parsingInput = WalletConnect.ParseRequestInput.newBuilder().apply { + method = WalletConnect.Method.CosmosSignAmino + payload = "{\"signerAddress\":\"bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2\",\"signDoc\":{\"account_number\":\"19\",\"chain_id\":\"chain-bnb\",\"memo\":\"\",\"data\":null,\"msgs\":[{\"inputs\":[{\"address\":\"bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2\",\"coins\":[{\"amount\":1001000000,\"denom\":\"BNB\"}]}],\"outputs\":[{\"address\":\"bnb13zeh6hs97d5eu2s5qerguhv8ewwue6u4ywa6yf\",\"coins\":[{\"amount\":1001000000,\"denom\":\"BNB\"}]}]}],\"sequence\":\"23\",\"source\":\"1\"}}" + }.build() + + val parsingOutputBytes = WalletConnectRequest.parse(BINANCE, parsingInput.toByteArray()) + val parsingOutput = WalletConnect.ParseRequestOutput.parseFrom(parsingOutputBytes) + + assertEquals(parsingOutput.error, Common.SigningError.OK) + + // Step 2: Set missing fields. + + val signingInput = parsingOutput.binance.toBuilder().apply { + privateKey = ByteString.copyFrom("95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832".toHexBytes()) + }.build() + + // Step 3: Sign the transaction. + + val output = AnySigner.sign(signingInput, BINANCE, SigningOutput.parser()) + + assertEquals(output.error, Common.SigningError.OK) + assertEquals(output.signatureJson, "{\"pub_key\":{\"type\":\"tendermint/PubKeySecp256k1\",\"value\":\"Amo1kgCI2Yw4iMpoxT38k/RWRgJgbLuH8P5e5TPbOOUC\"},\"signature\":\"PCTHhMa7+Z1U/6uxU+3LbTxKd0k231xypdMolyVvjgYvMg+0dTMC+wqW8IxHWXTSDt/Ronu+7ac1h/WN3JWJdQ==\"}") + } +} \ No newline at end of file diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs index afbada1d43e..a33b00322b5 100644 --- a/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs @@ -15,6 +15,7 @@ use tw_coin_entry::error::AddressResult; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_coin_entry::prefix::NoPrefix; use tw_keypair::tw::PublicKey; use tw_proto::{BLOCKCHAIN}::Proto; @@ -33,6 +34,7 @@ impl CoinEntry for {BLOCKCHAIN}Entry { type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; + type WalletConnector = NoWalletConnector; #[inline] fn parse_address( diff --git a/codegen-v2/src/tests/samples/class.output.swift b/codegen-v2/src/tests/samples/class.output.swift index 0b5b7fbb3a1..bd9024b04df 100644 --- a/codegen-v2/src/tests/samples/class.output.swift +++ b/codegen-v2/src/tests/samples/class.output.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/codegen-v2/src/tests/samples/enum.output.swift b/codegen-v2/src/tests/samples/enum.output.swift index ebc0cd0b491..d8d9a3d3e72 100644 --- a/codegen-v2/src/tests/samples/enum.output.swift +++ b/codegen-v2/src/tests/samples/enum.output.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/codegen-v2/src/tests/samples/enum_extension.output.swift b/codegen-v2/src/tests/samples/enum_extension.output.swift index afdd1d794cc..77a42623f8c 100644 --- a/codegen-v2/src/tests/samples/enum_extension.output.swift +++ b/codegen-v2/src/tests/samples/enum_extension.output.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/codegen-v2/src/tests/samples/enum_private.output.swift b/codegen-v2/src/tests/samples/enum_private.output.swift index b1f199054fe..bf583ef1ce5 100644 --- a/codegen-v2/src/tests/samples/enum_private.output.swift +++ b/codegen-v2/src/tests/samples/enum_private.output.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/codegen-v2/src/tests/samples/non-associated.output.swift b/codegen-v2/src/tests/samples/non-associated.output.swift index d4e8ee82477..89c0a9dbb63 100644 --- a/codegen-v2/src/tests/samples/non-associated.output.swift +++ b/codegen-v2/src/tests/samples/non-associated.output.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/codegen-v2/src/tests/samples/optional.output.swift b/codegen-v2/src/tests/samples/optional.output.swift index e2cef180240..504a396ebd3 100644 --- a/codegen-v2/src/tests/samples/optional.output.swift +++ b/codegen-v2/src/tests/samples/optional.output.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/codegen-v2/src/tests/samples/private_class.output.swift b/codegen-v2/src/tests/samples/private_class.output.swift index 0ce8618172f..31897047db4 100644 --- a/codegen-v2/src/tests/samples/private_class.output.swift +++ b/codegen-v2/src/tests/samples/private_class.output.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/codegen-v2/src/tests/samples/struct.output.swift b/codegen-v2/src/tests/samples/struct.output.swift index 927ffd1eac1..9cf7a2fbd19 100644 --- a/codegen-v2/src/tests/samples/struct.output.swift +++ b/codegen-v2/src/tests/samples/struct.output.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/include/TrustWalletCore/TWWalletConnectRequest.h b/include/TrustWalletCore/TWWalletConnectRequest.h new file mode 100644 index 00000000000..a65e7d9ed3d --- /dev/null +++ b/include/TrustWalletCore/TWWalletConnectRequest.h @@ -0,0 +1,27 @@ +// Copyright © 2017-2024 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "TWBase.h" +#include "TWCoinType.h" +#include "TWData.h" + +TW_EXTERN_C_BEGIN + +/// Represents a WalletConnect signing request. +TW_EXPORT_CLASS +struct TWWalletConnectRequest; + +/// Parses the WalletConnect signing request as a `SigningInput`. +/// +/// \param coin The given coin type to plan the transaction for. +/// \param input The serialized data of a `WalletConnect::Proto::ParseRequestInput` proto object. +/// \return The serialized data of `WalletConnect::Proto::ParseRequestOutput` proto object. +TW_EXPORT_STATIC_METHOD +TWData* _Nonnull TWWalletConnectRequestParse(enum TWCoinType coin, TWData* _Nonnull input); + +TW_EXTERN_C_END diff --git a/rust/chains/tw_binance/Cargo.toml b/rust/chains/tw_binance/Cargo.toml index 9a1d5003651..e5c9a10aa2c 100644 --- a/rust/chains/tw_binance/Cargo.toml +++ b/rust/chains/tw_binance/Cargo.toml @@ -17,5 +17,5 @@ tw_evm = { path = "../../tw_evm" } tw_hash = { path = "../../tw_hash" } tw_keypair = { path = "../../tw_keypair" } tw_memory = { path = "../../tw_memory" } -tw_misc = { path = "../../tw_misc" } +tw_misc = { path = "../../tw_misc", features = ["serde"] } tw_proto = { path = "../../tw_proto" } diff --git a/rust/chains/tw_binance/src/address.rs b/rust/chains/tw_binance/src/address.rs index 9bc164e0395..6fecbd6f3ac 100644 --- a/rust/chains/tw_binance/src/address.rs +++ b/rust/chains/tw_binance/src/address.rs @@ -1,10 +1,10 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use serde::Serialize; +use serde::{Deserialize, Serialize}; use std::fmt; use std::str::FromStr; use tw_bech32_address::bech32_prefix::Bech32Prefix; @@ -12,6 +12,7 @@ use tw_bech32_address::Bech32Address; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_cosmos_sdk::address::CosmosAddress; use tw_keypair::tw::PublicKey; use tw_memory::Data; @@ -21,7 +22,7 @@ const BNB_KNOWN_HRPS: [&str; 2] = [ "bca", ]; -#[derive(Serialize)] +#[derive(Deserialize, PartialEq, Serialize)] pub struct BinanceAddress(Bech32Address); impl CoinAddress for BinanceAddress { @@ -31,6 +32,16 @@ impl CoinAddress for BinanceAddress { } } +impl CosmosAddress for BinanceAddress { + fn from_str_with_coin(coin: &dyn CoinContext, addr: &str) -> AddressResult + where + Self: Sized, + { + let prefix = None; + Self::from_str_with_coin_and_prefix(coin, addr.to_string(), prefix) + } +} + impl BinanceAddress { pub const VALIDATOR_HRP: &'static str = "bva"; diff --git a/rust/chains/tw_binance/src/amino.rs b/rust/chains/tw_binance/src/amino.rs index 0c891586b9e..048b0bdedc1 100644 --- a/rust/chains/tw_binance/src/amino.rs +++ b/rust/chains/tw_binance/src/amino.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/rust/chains/tw_binance/src/compiler.rs b/rust/chains/tw_binance/src/compiler.rs index 1ee1e23dd15..0a289ec1c3f 100644 --- a/rust/chains/tw_binance/src/compiler.rs +++ b/rust/chains/tw_binance/src/compiler.rs @@ -1,9 +1,10 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use crate::context::BinanceContext; use crate::modules::preimager::{JsonPreimager, JsonTxPreimage}; use crate::modules::serializer::BinanceAminoSerializer; use crate::modules::tx_builder::TxBuilder; @@ -12,9 +13,12 @@ use crate::transaction::SignerInfo; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; use tw_coin_entry::common::compile_input::SingleSignaturePubkey; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_coin_entry::signing_output_error; -use tw_keypair::ecdsa::secp256k1; +use tw_cosmos_sdk::modules::serializer::json_serializer::JsonSerializer; +use tw_cosmos_sdk::public_key::secp256k1::Secp256PublicKey; +use tw_cosmos_sdk::public_key::CosmosPublicKey; +use tw_misc::traits::ToBytesVec; use tw_proto::Binance::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; @@ -69,7 +73,13 @@ impl BinanceCompiler { public_key, } = SingleSignaturePubkey::from_sign_pubkey_list(signatures, public_keys)?; let signature = BinanceSignature::try_from(signature.as_slice())?; - let public_key = secp256k1::PublicKey::try_from(public_key.as_slice())?; + let public_key = Secp256PublicKey::from_bytes(coin, public_key.as_slice())?; + + let signature_bytes = signature.to_vec(); + let signature_json = JsonSerializer::::serialize_signature( + &public_key, + signature_bytes.clone(), + ); let unsigned_tx = TxBuilder::unsigned_tx_from_proto(coin, &input)?; let signed_tx = unsigned_tx.into_signed(SignerInfo { @@ -79,8 +89,12 @@ impl BinanceCompiler { let encoded_tx = BinanceAminoSerializer::serialize_signed_tx(&signed_tx)?; + let signature_json = serde_json::to_string(&signature_json) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?; Ok(Proto::SigningOutput { encoded: encoded_tx.into(), + signature: signature_bytes.into(), + signature_json: signature_json.into(), ..Proto::SigningOutput::default() }) } diff --git a/rust/chains/tw_binance/src/context.rs b/rust/chains/tw_binance/src/context.rs new file mode 100644 index 00000000000..a70977bcd46 --- /dev/null +++ b/rust/chains/tw_binance/src/context.rs @@ -0,0 +1,23 @@ +// Copyright © 2017-2024 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::address::BinanceAddress; +use tw_cosmos_sdk::context::CosmosContext; +use tw_cosmos_sdk::hasher::sha256_hasher::Sha256Hasher; +use tw_cosmos_sdk::private_key::secp256k1::Secp256PrivateKey; +use tw_cosmos_sdk::public_key::secp256k1::Secp256PublicKey; +use tw_cosmos_sdk::signature::secp256k1::Secp256k1Signature; + +pub struct BinanceContext; + +impl CosmosContext for BinanceContext { + type Address = BinanceAddress; + /// Binance Beacon chain uses `sha256` hash. + type TxHasher = Sha256Hasher; + type PrivateKey = Secp256PrivateKey; + type PublicKey = Secp256PublicKey; + type Signature = Secp256k1Signature; +} diff --git a/rust/chains/tw_binance/src/entry.rs b/rust/chains/tw_binance/src/entry.rs index bda948c9900..4589e2d31a8 100644 --- a/rust/chains/tw_binance/src/entry.rs +++ b/rust/chains/tw_binance/src/entry.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,6 +6,7 @@ use crate::address::BinanceAddress; use crate::compiler::BinanceCompiler; +use crate::modules::wallet_connect::connector::BinanceWalletConnector; use crate::signer::BinanceSigner; use std::str::FromStr; use tw_bech32_address::bech32_prefix::Bech32Prefix; @@ -33,6 +34,7 @@ impl CoinEntry for BinanceEntry { type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; + type WalletConnector = BinanceWalletConnector; #[inline] fn parse_address( @@ -88,4 +90,9 @@ impl CoinEntry for BinanceEntry { ) -> Self::SigningOutput { BinanceCompiler::compile(coin, input, signatures, public_keys) } + + #[inline] + fn wallet_connector(&self) -> Option { + Some(BinanceWalletConnector) + } } diff --git a/rust/chains/tw_binance/src/lib.rs b/rust/chains/tw_binance/src/lib.rs index 0bd07a91bbf..606d6f3000d 100644 --- a/rust/chains/tw_binance/src/lib.rs +++ b/rust/chains/tw_binance/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,6 +7,7 @@ pub mod address; pub mod amino; pub mod compiler; +pub mod context; pub mod entry; pub mod modules; pub mod signature; diff --git a/rust/chains/tw_binance/src/modules/mod.rs b/rust/chains/tw_binance/src/modules/mod.rs index f27e84a2506..5300755a36f 100644 --- a/rust/chains/tw_binance/src/modules/mod.rs +++ b/rust/chains/tw_binance/src/modules/mod.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,3 +7,4 @@ pub mod preimager; pub mod serializer; pub mod tx_builder; +pub mod wallet_connect; diff --git a/rust/chains/tw_binance/src/modules/preimager.rs b/rust/chains/tw_binance/src/modules/preimager.rs index 18e2d47b5b5..cbb4969d139 100644 --- a/rust/chains/tw_binance/src/modules/preimager.rs +++ b/rust/chains/tw_binance/src/modules/preimager.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/rust/chains/tw_binance/src/modules/serializer.rs b/rust/chains/tw_binance/src/modules/serializer.rs index 4716ffd9ab9..d77e85d05f3 100644 --- a/rust/chains/tw_binance/src/modules/serializer.rs +++ b/rust/chains/tw_binance/src/modules/serializer.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,7 +8,7 @@ use crate::amino::AminoEncoder; use crate::transaction::SignedTransaction; use std::borrow::Cow; use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; -use tw_hash::H264; +use tw_cosmos_sdk::public_key::CosmosPublicKey; use tw_memory::Data; use tw_misc::traits::ToBytesVec; use tw_proto::serialize; @@ -27,7 +27,7 @@ impl BinanceAminoSerializer { .unsigned .msgs .iter() - .map(|msg| msg.to_amino_protobuf().map(Cow::from)) + .map(|msg| msg.as_ref().to_amino_protobuf().map(Cow::from)) .collect::>>()?; let signature = Self::serialize_signature(tx)?; @@ -43,7 +43,7 @@ impl BinanceAminoSerializer { .encode_size_prefixed()?) } - pub fn serialize_public_key(public_key: H264) -> Data { + pub fn serialize_public_key(public_key: Data) -> Data { let public_key_len = public_key.len() as u8; AminoEncoder::new(&PUBLIC_KEY_PREFIX) // Push the length of the public key. @@ -54,7 +54,7 @@ impl BinanceAminoSerializer { pub fn serialize_signature(signed: &SignedTransaction) -> SigningResult { let sign_msg = Proto::Signature { - pub_key: Self::serialize_public_key(signed.signer.public_key.compressed()).into(), + pub_key: Self::serialize_public_key(signed.signer.public_key.to_bytes()).into(), signature: signed.signer.signature.to_vec().into(), account_number: signed.unsigned.account_number, sequence: signed.unsigned.sequence, diff --git a/rust/chains/tw_binance/src/modules/tx_builder.rs b/rust/chains/tw_binance/src/modules/tx_builder.rs index a355df60dc4..e5b47fbedf5 100644 --- a/rust/chains/tw_binance/src/modules/tx_builder.rs +++ b/rust/chains/tw_binance/src/modules/tx_builder.rs @@ -1,17 +1,14 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use crate::address::BinanceAddress; -use crate::transaction::message::Token; -use crate::transaction::message::{BinanceMessage, BinanceMessageBox}; +use crate::transaction::message::{BinanceMessageEnum, TWBinanceProto}; use crate::transaction::UnsignedTransaction; +use std::borrow::Cow; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; -use tw_evm::address::Address as EthereumAddress; -use tw_hash::H160; use tw_proto::Binance::Proto; pub struct TxBuilder; @@ -21,8 +18,7 @@ impl TxBuilder { coin: &dyn CoinContext, input: &Proto::SigningInput<'_>, ) -> SigningResult { - let msg = Self::msg_from_proto(coin, input)?; - + let msg = BinanceMessageEnum::from_tw_proto(coin, &input.order_oneof)?; Ok(UnsignedTransaction { account_number: input.account_number, chain_id: input.chain_id.to_string(), @@ -34,468 +30,26 @@ impl TxBuilder { }) } - pub fn msg_from_proto( - coin: &dyn CoinContext, - input: &Proto::SigningInput<'_>, - ) -> SigningResult { - use Proto::mod_SigningInput::OneOforder_oneof as OrderEnum; - - match input.order_oneof { - OrderEnum::trade_order(ref new_order) => Self::trade_order_from_proto(coin, new_order), - OrderEnum::cancel_trade_order(ref cancel_order) => { - Self::cancel_order_from_proto(coin, cancel_order) - }, - OrderEnum::send_order(ref send_order) => Self::send_order_from_proto(coin, send_order), - OrderEnum::freeze_order(ref freeze_order) => { - Self::freeze_order_from_proto(coin, freeze_order) - }, - OrderEnum::unfreeze_order(ref unfreeze_order) => { - Self::unfreeze_order_from_proto(coin, unfreeze_order) - }, - OrderEnum::htlt_order(ref htlt_order) => Self::htlt_order_from_proto(coin, htlt_order), - OrderEnum::depositHTLT_order(ref deposit_htlt) => { - Self::deposit_htlt_order_from_proto(coin, deposit_htlt) - }, - OrderEnum::claimHTLT_order(ref claim_htlt) => { - Self::claim_htlt_order_from_proto(coin, claim_htlt) - }, - OrderEnum::refundHTLT_order(ref refund_htlt) => { - Self::refund_htlt_order_from_proto(coin, refund_htlt) - }, - OrderEnum::issue_order(ref issue_order) => { - Self::issue_order_from_proto(coin, issue_order) - }, - OrderEnum::mint_order(ref mint_order) => Self::mint_order_from_proto(coin, mint_order), - OrderEnum::burn_order(ref burn_order) => Self::burn_order_from_proto(coin, burn_order), - OrderEnum::transfer_out_order(ref transfer_out) => { - Self::transfer_out_order_from_proto(coin, transfer_out) - }, - OrderEnum::side_delegate_order(ref side_delegate) => { - Self::side_delegate_order_from_proto(coin, side_delegate) - }, - OrderEnum::side_redelegate_order(ref side_redelegate) => { - Self::side_redelegate_order_from_proto(coin, side_redelegate) - }, - OrderEnum::side_undelegate_order(ref side_undelegate) => { - Self::side_undelegate_order_from_proto(coin, side_undelegate) - }, - OrderEnum::time_lock_order(ref time_lock) => { - Self::time_lock_order_from_proto(coin, time_lock) - }, - OrderEnum::time_relock_order(ref time_relock) => { - Self::time_relock_order_from_proto(coin, time_relock) - }, - OrderEnum::time_unlock_order(ref time_unlock) => { - Self::time_unlock_order_from_proto(coin, time_unlock) - }, - OrderEnum::None => Err(SigningError(SigningErrorType::Error_invalid_params)), - } - } - - pub fn trade_order_from_proto( - coin: &dyn CoinContext, - new_order: &Proto::TradeOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::trade_order::NewTradeOrder; - use crate::transaction::message::trade_order::OrderType; - - let order_type = OrderType::from_repr(new_order.ordertype) - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; - let sender = BinanceAddress::from_key_hash_with_coin(coin, new_order.sender.to_vec())?; - - Ok(NewTradeOrder { - id: new_order.id.to_string(), - order_type, - price: new_order.price, - quantity: new_order.quantity, - sender, - side: new_order.side, - symbol: new_order.symbol.to_string(), - time_in_force: new_order.timeinforce, - } - .into_boxed()) - } - - pub fn cancel_order_from_proto( - coin: &dyn CoinContext, - cancel_order: &Proto::CancelTradeOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::trade_order::CancelTradeOrder; - - let sender = BinanceAddress::from_key_hash_with_coin(coin, cancel_order.sender.to_vec())?; - Ok(CancelTradeOrder { - sender, - symbol: cancel_order.symbol.to_string(), - refid: cancel_order.refid.to_string(), - } - .into_boxed()) - } - - pub fn send_order_from_proto( - coin: &dyn CoinContext, - send_order: &Proto::SendOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::send_order::{InOut, SendOrder}; - - fn in_out_from_proto( - coin: &dyn CoinContext, - address_key_hash: &[u8], - coins: &[Proto::mod_SendOrder::Token], - ) -> SigningResult { - let address = BinanceAddress::from_key_hash_with_coin(coin, address_key_hash.to_vec())?; - let coins = coins.iter().map(TxBuilder::token_from_proto).collect(); - - Ok(InOut { address, coins }) - } - - let inputs = send_order - .inputs - .iter() - .map(|input| in_out_from_proto(coin, &input.address, &input.coins)) - .collect::>>()?; - - let outputs = send_order - .outputs - .iter() - .map(|output| in_out_from_proto(coin, &output.address, &output.coins)) - .collect::>>()?; - - Ok(SendOrder { inputs, outputs }.into_boxed()) - } - - pub fn freeze_order_from_proto( - coin: &dyn CoinContext, - freeze_order: &Proto::TokenFreezeOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::token_order::TokenFreezeOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, freeze_order.from.to_vec())?; - Ok(TokenFreezeOrder { - from, - symbol: freeze_order.symbol.to_string(), - amount: freeze_order.amount, - } - .into_boxed()) - } - - pub fn unfreeze_order_from_proto( - coin: &dyn CoinContext, - unfreeze_order: &Proto::TokenUnfreezeOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::token_order::TokenUnfreezeOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, unfreeze_order.from.to_vec())?; - Ok(TokenUnfreezeOrder { - from, - symbol: unfreeze_order.symbol.to_string(), - amount: unfreeze_order.amount, - } - .into_boxed()) - } - - pub fn htlt_order_from_proto( - coin: &dyn CoinContext, - htlt_order: &Proto::HTLTOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::htlt_order::HTLTOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, htlt_order.from.to_vec())?; - let to = BinanceAddress::from_key_hash_with_coin(coin, htlt_order.to.to_vec())?; - - let amount = htlt_order - .amount - .iter() - .map(Self::token_from_proto) - .collect(); - - Ok(HTLTOrder { - from, - to, - recipient_other_chain: htlt_order.recipient_other_chain.to_string(), - sender_other_chain: htlt_order.sender_other_chain.to_string(), - random_number_hash: htlt_order.random_number_hash.to_vec(), - timestamp: htlt_order.timestamp, - amount, - expected_income: htlt_order.expected_income.to_string(), - height_span: htlt_order.height_span, - cross_chain: htlt_order.cross_chain, - } - .into_boxed()) - } - - pub fn deposit_htlt_order_from_proto( - coin: &dyn CoinContext, - deposit_htlt: &Proto::DepositHTLTOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::htlt_order::DepositHTLTOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, deposit_htlt.from.to_vec())?; - - let amount = deposit_htlt - .amount - .iter() - .map(Self::token_from_proto) - .collect(); - - Ok(DepositHTLTOrder { - from, - amount, - swap_id: deposit_htlt.swap_id.to_vec(), - } - .into_boxed()) - } - - pub fn claim_htlt_order_from_proto( - coin: &dyn CoinContext, - claim_htlt: &Proto::ClaimHTLOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::htlt_order::ClaimHTLTOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, claim_htlt.from.to_vec())?; - - Ok(ClaimHTLTOrder { - from, - swap_id: claim_htlt.swap_id.to_vec(), - random_number: claim_htlt.random_number.to_vec(), - } - .into_boxed()) - } - - pub fn refund_htlt_order_from_proto( - coin: &dyn CoinContext, - refund_htlt: &Proto::RefundHTLTOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::htlt_order::RefundHTLTOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, refund_htlt.from.to_vec())?; - let swap_id = refund_htlt.swap_id.to_vec(); - - Ok(RefundHTLTOrder { from, swap_id }.into_boxed()) - } - - pub fn issue_order_from_proto( - coin: &dyn CoinContext, - issue_order: &Proto::TokenIssueOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::token_order::TokenIssueOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, issue_order.from.to_vec())?; - Ok(TokenIssueOrder { - from, - name: issue_order.name.to_string(), - symbol: issue_order.symbol.to_string(), - total_supply: issue_order.total_supply, - mintable: issue_order.mintable, - } - .into_boxed()) - } - - pub fn mint_order_from_proto( - coin: &dyn CoinContext, - mint_order: &Proto::TokenMintOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::token_order::TokenMintOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, mint_order.from.to_vec())?; - Ok(TokenMintOrder { - from, - symbol: mint_order.symbol.to_string(), - amount: mint_order.amount, - } - .into_boxed()) - } - - pub fn burn_order_from_proto( - coin: &dyn CoinContext, - burn_order: &Proto::TokenBurnOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::token_order::TokenBurnOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, burn_order.from.to_vec())?; - Ok(TokenBurnOrder { - from, - symbol: burn_order.symbol.to_string(), - amount: burn_order.amount, - } - .into_boxed()) - } - - pub fn transfer_out_order_from_proto( - coin: &dyn CoinContext, - transfer_out: &Proto::TransferOut<'_>, - ) -> SigningResult { - use crate::transaction::message::tranfer_out_order::TransferOutOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, transfer_out.from.to_vec())?; - - let to_bytes = H160::try_from(transfer_out.to.as_ref()) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_address))?; - let to = EthereumAddress::from_bytes(to_bytes); - - let amount_proto = transfer_out - .amount - .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; - - Ok(TransferOutOrder { - from, - to, - amount: Self::token_from_proto(amount_proto), - expire_time: transfer_out.expire_time, - } - .into_boxed()) - } - - pub fn side_delegate_order_from_proto( - coin: &dyn CoinContext, - side_delegate: &Proto::SideChainDelegate<'_>, - ) -> SigningResult { - use crate::transaction::message::side_chain_delegate::SideDelegateOrder; - - let delegator_addr = - BinanceAddress::from_key_hash_with_coin(coin, side_delegate.delegator_addr.to_vec())?; - let validator_addr = - BinanceAddress::new_validator_addr(side_delegate.validator_addr.to_vec())?; - - let delegation = side_delegate - .delegation - .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; - - Ok(SideDelegateOrder { - delegator_addr, - validator_addr, - delegation: Self::token_from_proto(delegation), - side_chain_id: side_delegate.chain_id.to_string(), - } - .into_boxed()) - } - - pub fn side_redelegate_order_from_proto( - coin: &dyn CoinContext, - side_redelegate: &Proto::SideChainRedelegate<'_>, - ) -> SigningResult { - use crate::transaction::message::side_chain_delegate::SideRedelegateOrder; - - let delegator_addr = - BinanceAddress::from_key_hash_with_coin(coin, side_redelegate.delegator_addr.to_vec())?; - let validator_src_addr = - BinanceAddress::new_validator_addr(side_redelegate.validator_src_addr.to_vec())?; - let validator_dst_addr = - BinanceAddress::new_validator_addr(side_redelegate.validator_dst_addr.to_vec())?; - - let amount = side_redelegate - .amount - .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; - - Ok(SideRedelegateOrder { - delegator_addr, - validator_src_addr, - validator_dst_addr, - amount: Self::token_from_proto(amount), - side_chain_id: side_redelegate.chain_id.to_string(), - } - .into_boxed()) - } - - pub fn side_undelegate_order_from_proto( - coin: &dyn CoinContext, - side_undelegate: &Proto::SideChainUndelegate<'_>, - ) -> SigningResult { - use crate::transaction::message::side_chain_delegate::SideUndelegateOrder; - - let delegator_addr = - BinanceAddress::from_key_hash_with_coin(coin, side_undelegate.delegator_addr.to_vec())?; - let validator_addr = - BinanceAddress::new_validator_addr(side_undelegate.validator_addr.to_vec())?; - - let amount = side_undelegate - .amount - .as_ref() - .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; - - Ok(SideUndelegateOrder { - delegator_addr, - validator_addr, - amount: Self::token_from_proto(amount), - side_chain_id: side_undelegate.chain_id.to_string(), - } - .into_boxed()) - } - - pub fn time_lock_order_from_proto( - coin: &dyn CoinContext, - time_lock: &Proto::TimeLockOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::time_lock_order::TimeLockOrder; - - let from = BinanceAddress::from_key_hash_with_coin(coin, time_lock.from_address.to_vec())?; - let amount = time_lock - .amount - .iter() - .map(Self::token_from_proto) - .collect(); - - Ok(TimeLockOrder { - from, - description: time_lock.description.to_string(), - amount, - lock_time: time_lock.lock_time, - } - .into_boxed()) - } - - pub fn time_relock_order_from_proto( - coin: &dyn CoinContext, - time_relock: &Proto::TimeRelockOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::time_lock_order::TimeRelockOrder; - - let from = - BinanceAddress::from_key_hash_with_coin(coin, time_relock.from_address.to_vec())?; - - let amount = if time_relock.amount.is_empty() { - None - } else { - Some( - time_relock - .amount - .iter() - .map(Self::token_from_proto) - .collect(), - ) - }; - - Ok(TimeRelockOrder { - from, - time_lock_id: time_relock.id, - description: time_relock.description.to_string(), - amount, - lock_time: time_relock.lock_time, - } - .into_boxed()) - } - - pub fn time_unlock_order_from_proto( - coin: &dyn CoinContext, - time_unlock: &Proto::TimeUnlockOrder<'_>, - ) -> SigningResult { - use crate::transaction::message::time_lock_order::TimeUnlockOrder; - - let from = - BinanceAddress::from_key_hash_with_coin(coin, time_unlock.from_address.to_vec())?; - Ok(TimeUnlockOrder { - from, - time_lock_id: time_unlock.id, - } - .into_boxed()) - } - - fn token_from_proto(token: &Proto::mod_SendOrder::Token) -> Token { - Token { - denom: token.denom.to_string(), - amount: token.amount, - } + pub fn unsigned_tx_to_proto( + unsigned: &UnsignedTransaction, + ) -> SigningResult> { + if unsigned.msgs.len() != 1 { + return Err(SigningError(SigningErrorType::Error_invalid_params)); + } + let msg = unsigned + .msgs + .first() + .expect("There should be exactly one message") + .to_tw_proto(); + + Ok(Proto::SigningInput { + chain_id: unsigned.chain_id.clone().into(), + account_number: unsigned.account_number, + sequence: unsigned.sequence, + source: unsigned.source, + memo: unsigned.memo.clone().into(), + private_key: Cow::default(), + order_oneof: msg, + }) } } diff --git a/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs b/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs new file mode 100644 index 00000000000..893b408488b --- /dev/null +++ b/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs @@ -0,0 +1,56 @@ +// Copyright © 2017-2024 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::modules::tx_builder::TxBuilder; +use crate::modules::wallet_connect::types::SignAminoRequest; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::modules::wallet_connector::WalletConnector; +use tw_coin_entry::signing_output_error; +use tw_proto::WalletConnect::Proto::{ + self as WCProto, mod_ParseRequestOutput::OneOfsigning_input_oneof as SigningInputEnum, +}; + +pub struct BinanceWalletConnector; + +impl WalletConnector for BinanceWalletConnector { + fn parse_request( + &self, + coin: &dyn CoinContext, + request: WCProto::ParseRequestInput<'_>, + ) -> WCProto::ParseRequestOutput<'static> { + Self::parse_request_impl(coin, request) + .unwrap_or_else(|e| signing_output_error!(WCProto::ParseRequestOutput, e)) + } +} + +impl BinanceWalletConnector { + fn parse_request_impl( + coin: &dyn CoinContext, + request: WCProto::ParseRequestInput<'_>, + ) -> SigningResult> { + match request.method { + WCProto::Method::CosmosSignAmino => Self::parse_sign_amino_request(coin, request), + _ => Err(SigningError(SigningErrorType::Error_not_supported)), + } + } + + pub fn parse_sign_amino_request( + _coin: &dyn CoinContext, + request: WCProto::ParseRequestInput<'_>, + ) -> SigningResult> { + let amino_req: SignAminoRequest = serde_json::from_str(&request.payload) + .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; + + // Parse a `SigningInput` from the given `signDoc`. + let signing_input = TxBuilder::unsigned_tx_to_proto(&amino_req.sign_doc)?; + + Ok(WCProto::ParseRequestOutput { + signing_input_oneof: SigningInputEnum::binance(signing_input), + ..WCProto::ParseRequestOutput::default() + }) + } +} diff --git a/rust/chains/tw_binance/src/modules/wallet_connect/mod.rs b/rust/chains/tw_binance/src/modules/wallet_connect/mod.rs new file mode 100644 index 00000000000..9e2233feaa6 --- /dev/null +++ b/rust/chains/tw_binance/src/modules/wallet_connect/mod.rs @@ -0,0 +1,8 @@ +// Copyright © 2017-2024 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +pub mod connector; +pub mod types; diff --git a/rust/chains/tw_binance/src/modules/wallet_connect/types.rs b/rust/chains/tw_binance/src/modules/wallet_connect/types.rs new file mode 100644 index 00000000000..e2b2f599b6e --- /dev/null +++ b/rust/chains/tw_binance/src/modules/wallet_connect/types.rs @@ -0,0 +1,14 @@ +// Copyright © 2017-2024 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::transaction::UnsignedTransaction; +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct SignAminoRequest { + #[serde(rename = "signDoc")] + pub sign_doc: UnsignedTransaction, +} diff --git a/rust/chains/tw_binance/src/signature.rs b/rust/chains/tw_binance/src/signature.rs index 5e58a2967a1..eda4adbf4c7 100644 --- a/rust/chains/tw_binance/src/signature.rs +++ b/rust/chains/tw_binance/src/signature.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/rust/chains/tw_binance/src/signer.rs b/rust/chains/tw_binance/src/signer.rs index b8b1c02739e..8913a9c5e6a 100644 --- a/rust/chains/tw_binance/src/signer.rs +++ b/rust/chains/tw_binance/src/signer.rs @@ -1,19 +1,23 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +use crate::context::BinanceContext; use crate::modules::preimager::{JsonPreimager, JsonTxPreimage}; use crate::modules::serializer::BinanceAminoSerializer; use crate::modules::tx_builder::TxBuilder; use crate::signature::BinanceSignature; use crate::transaction::SignerInfo; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_coin_entry::signing_output_error; +use tw_cosmos_sdk::modules::serializer::json_serializer::JsonSerializer; +use tw_cosmos_sdk::public_key::secp256k1::Secp256PublicKey; use tw_keypair::ecdsa::secp256k1; use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; +use tw_misc::traits::ToBytesVec; use tw_proto::Binance::Proto; pub struct BinanceSigner; @@ -37,7 +41,13 @@ impl BinanceSigner { let key_pair = secp256k1::KeyPair::try_from(input.private_key.as_ref())?; let signature = BinanceSignature::from(key_pair.sign(tx_hash)?); - let public_key = key_pair.public().clone(); + let public_key = Secp256PublicKey::from_secp256k1_public_key(coin, key_pair.public())?; + + let signature_bytes = signature.to_vec(); + let signature_json = JsonSerializer::::serialize_signature( + &public_key, + signature_bytes.clone(), + ); let signed_tx = unsigned_tx.into_signed(SignerInfo { public_key, @@ -45,8 +55,12 @@ impl BinanceSigner { }); let encoded_tx = BinanceAminoSerializer::serialize_signed_tx(&signed_tx)?; + let signature_json = serde_json::to_string(&signature_json) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?; Ok(Proto::SigningOutput { encoded: encoded_tx.into(), + signature: signature_bytes.into(), + signature_json: signature_json.into(), ..Proto::SigningOutput::default() }) } diff --git a/rust/chains/tw_binance/src/transaction/message/htlt_order.rs b/rust/chains/tw_binance/src/transaction/message/htlt_order.rs index c0969bb305d..2d5677ccaa5 100644 --- a/rust/chains/tw_binance/src/transaction/message/htlt_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/htlt_order.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,28 +6,28 @@ use crate::address::BinanceAddress; use crate::amino::AminoEncoder; -use crate::transaction::message::{message_to_json, BinanceMessage, Token}; -use serde::Serialize; -use serde_json::Value as Json; +use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; +use serde::{Deserialize, Serialize}; +use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; use tw_coin_entry::error::SigningResult; use tw_encoding::hex::as_hex; use tw_memory::Data; use tw_proto::Binance::Proto; -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct HTLTOrder { + pub amount: Vec, + pub cross_chain: bool, + pub expected_income: String, pub from: BinanceAddress, - pub to: BinanceAddress, - pub recipient_other_chain: String, - pub sender_other_chain: String, + pub height_span: i64, #[serde(serialize_with = "as_hex")] pub random_number_hash: Data, + pub recipient_other_chain: String, + pub sender_other_chain: String, pub timestamp: i64, - pub amount: Vec, - pub expected_income: String, - pub height_span: i64, - pub cross_chain: bool, + pub to: BinanceAddress, } impl HTLTOrder { @@ -36,34 +36,56 @@ impl HTLTOrder { } impl BinanceMessage for HTLTOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::HTLTOrder { +impl TWBinanceProto for HTLTOrder { + type Proto<'a> = Proto::HTLTOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + let to = BinanceAddress::from_key_hash_with_coin(coin, msg.to.to_vec())?; + + let amount = msg.amount.iter().map(Token::from_tw_proto).collect(); + + Ok(HTLTOrder { + from, + to, + recipient_other_chain: msg.recipient_other_chain.to_string(), + sender_other_chain: msg.sender_other_chain.to_string(), + random_number_hash: msg.random_number_hash.to_vec(), + timestamp: msg.timestamp, + amount, + expected_income: msg.expected_income.to_string(), + height_span: msg.height_span, + cross_chain: msg.cross_chain, + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::HTLTOrder { from: self.from.data().into(), to: self.to.data().into(), recipient_other_chain: self.recipient_other_chain.clone().into(), sender_other_chain: self.sender_other_chain.clone().into(), random_number_hash: self.random_number_hash.clone().into(), timestamp: self.timestamp, - amount: self.amount.iter().map(Token::to_proto).collect(), + amount: self.amount.iter().map(Token::to_tw_proto).collect(), expected_income: self.expected_income.clone().into(), height_span: self.height_span, cross_chain: self.cross_chain, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct DepositHTLTOrder { - pub from: BinanceAddress, pub amount: Vec, + pub from: BinanceAddress, #[serde(serialize_with = "as_hex")] pub swap_id: Data, } @@ -74,30 +96,43 @@ impl DepositHTLTOrder { } impl BinanceMessage for DepositHTLTOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::DepositHTLTOrder { +impl TWBinanceProto for DepositHTLTOrder { + type Proto<'a> = Proto::DepositHTLTOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + let amount = msg.amount.iter().map(Token::from_tw_proto).collect(); + + Ok(DepositHTLTOrder { + from, + amount, + swap_id: msg.swap_id.to_vec(), + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::DepositHTLTOrder { from: self.from.data().into(), - amount: self.amount.iter().map(Token::to_proto).collect(), + amount: self.amount.iter().map(Token::to_tw_proto).collect(), swap_id: self.swap_id.clone().into(), - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct ClaimHTLTOrder { pub from: BinanceAddress, #[serde(serialize_with = "as_hex")] - pub swap_id: Data, - #[serde(serialize_with = "as_hex")] pub random_number: Data, + #[serde(serialize_with = "as_hex")] + pub swap_id: Data, } impl ClaimHTLTOrder { @@ -106,24 +141,36 @@ impl ClaimHTLTOrder { } impl BinanceMessage for ClaimHTLTOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::ClaimHTLOrder { +impl TWBinanceProto for ClaimHTLTOrder { + type Proto<'a> = Proto::ClaimHTLOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + + Ok(ClaimHTLTOrder { + from, + swap_id: msg.swap_id.to_vec(), + random_number: msg.random_number.to_vec(), + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::ClaimHTLOrder { from: self.from.data().into(), swap_id: self.swap_id.clone().into(), random_number: self.random_number.clone().into(), - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct RefundHTLTOrder { pub from: BinanceAddress, #[serde(serialize_with = "as_hex")] @@ -135,19 +182,28 @@ impl RefundHTLTOrder { pub const PREFIX: [u8; 4] = [0x34, 0x54, 0xA2, 0x7C]; } -impl BinanceMessage for RefundHTLTOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) +impl TWBinanceProto for RefundHTLTOrder { + type Proto<'a> = Proto::RefundHTLTOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + let swap_id = msg.swap_id.to_vec(); + + Ok(RefundHTLTOrder { from, swap_id }) } - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::RefundHTLTOrder { + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::RefundHTLTOrder { from: self.from.data().into(), swap_id: self.swap_id.clone().into(), - }; + } + } +} +impl BinanceMessage for RefundHTLTOrder { + fn to_amino_protobuf(&self) -> SigningResult { Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? + .extend_with_msg(&self.to_tw_proto())? .encode()) } } diff --git a/rust/chains/tw_binance/src/transaction/message/mod.rs b/rust/chains/tw_binance/src/transaction/message/mod.rs index 6729a4276e4..78518de6b15 100644 --- a/rust/chains/tw_binance/src/transaction/message/mod.rs +++ b/rust/chains/tw_binance/src/transaction/message/mod.rs @@ -1,15 +1,14 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use serde::ser::Error as SerError; -use serde::{Serialize, Serializer}; -use serde_json::Value as Json; +use serde::{Deserialize, Serialize, Serializer}; +use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_memory::Data; -use tw_proto::Binance::Proto; +use tw_proto::Binance::Proto::{self, mod_SigningInput::OneOforder_oneof as BinanceMessageProto}; pub mod htlt_order; pub mod send_order; @@ -19,66 +18,249 @@ pub mod token_order; pub mod trade_order; pub mod tranfer_out_order; -pub type BinanceMessageBox = Box; - pub trait BinanceMessage { - fn into_boxed(self) -> BinanceMessageBox - where - Self: 'static + Sized, - { - Box::new(self) - } + fn to_amino_protobuf(&self) -> SigningResult; +} - fn to_json(&self) -> SigningResult; +/// A Binance message represented as a Trust Wallet Core Protobuf message. +pub trait TWBinanceProto: Sized { + type Proto<'a>; - fn to_amino_protobuf(&self) -> SigningResult; + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult; + + fn to_tw_proto(&self) -> Self::Proto<'static>; } -impl Serialize for dyn BinanceMessage { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - self.to_json() - .map_err(|e| SerError::custom(format!("{e:?}")))? - .serialize(serializer) +/// Please note that some of the fields are typped such as `SideDelegateOrder`. +#[derive(Deserialize, Serialize)] +#[serde(untagged)] +pub enum BinanceMessageEnum { + HTLTOrder(htlt_order::HTLTOrder), + DepositHTLTOrder(htlt_order::DepositHTLTOrder), + ClaimHTLTOrder(htlt_order::ClaimHTLTOrder), + RefundHTLTOrder(htlt_order::RefundHTLTOrder), + SendOrder(send_order::SendOrder), + SideDelegateOrder(side_chain_delegate::SideDelegateOrder), + SideRedelegateOrder(side_chain_delegate::SideRedelegateOrder), + SideUndelegateOrder(side_chain_delegate::SideUndelegateOrder), + TimeLockOrder(time_lock_order::TimeLockOrder), + TimeRelockOrder(time_lock_order::TimeRelockOrder), + TimeUnlockOrder(time_lock_order::TimeUnlockOrder), + TokenFreezeOrder(token_order::TokenFreezeOrder), + TokenUnfreezeOrder(token_order::TokenUnfreezeOrder), + TokenIssueOrder(token_order::TokenIssueOrder), + TokenMintOrder(token_order::TokenMintOrder), + TokenBurnOrder(token_order::TokenBurnOrder), + NewTradeOrder(trade_order::NewTradeOrder), + CancelTradeOrder(trade_order::CancelTradeOrder), + TransferOutOrder(tranfer_out_order::TransferOutOrder), +} + +impl TWBinanceProto for BinanceMessageEnum { + type Proto<'a> = BinanceMessageProto<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + match msg { + BinanceMessageProto::trade_order(ref order) => { + trade_order::NewTradeOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::NewTradeOrder) + }, + BinanceMessageProto::cancel_trade_order(ref order) => { + trade_order::CancelTradeOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::CancelTradeOrder) + }, + BinanceMessageProto::send_order(ref order) => { + send_order::SendOrder::from_tw_proto(coin, order).map(BinanceMessageEnum::SendOrder) + }, + BinanceMessageProto::freeze_order(ref order) => { + token_order::TokenFreezeOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::TokenFreezeOrder) + }, + BinanceMessageProto::unfreeze_order(ref order) => { + token_order::TokenUnfreezeOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::TokenUnfreezeOrder) + }, + BinanceMessageProto::htlt_order(ref order) => { + htlt_order::HTLTOrder::from_tw_proto(coin, order).map(BinanceMessageEnum::HTLTOrder) + }, + BinanceMessageProto::depositHTLT_order(ref order) => { + htlt_order::DepositHTLTOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::DepositHTLTOrder) + }, + BinanceMessageProto::claimHTLT_order(ref order) => { + htlt_order::ClaimHTLTOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::ClaimHTLTOrder) + }, + BinanceMessageProto::refundHTLT_order(ref order) => { + htlt_order::RefundHTLTOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::RefundHTLTOrder) + }, + BinanceMessageProto::issue_order(ref order) => { + token_order::TokenIssueOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::TokenIssueOrder) + }, + BinanceMessageProto::mint_order(ref order) => { + token_order::TokenMintOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::TokenMintOrder) + }, + BinanceMessageProto::burn_order(ref order) => { + token_order::TokenBurnOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::TokenBurnOrder) + }, + BinanceMessageProto::transfer_out_order(ref order) => { + tranfer_out_order::TransferOutOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::TransferOutOrder) + }, + BinanceMessageProto::side_delegate_order(ref order) => { + side_chain_delegate::SideDelegateOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::SideDelegateOrder) + }, + BinanceMessageProto::side_redelegate_order(ref order) => { + side_chain_delegate::SideRedelegateOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::SideRedelegateOrder) + }, + BinanceMessageProto::side_undelegate_order(ref order) => { + side_chain_delegate::SideUndelegateOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::SideUndelegateOrder) + }, + BinanceMessageProto::time_lock_order(ref order) => { + time_lock_order::TimeLockOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::TimeLockOrder) + }, + BinanceMessageProto::time_relock_order(ref order) => { + time_lock_order::TimeRelockOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::TimeRelockOrder) + }, + BinanceMessageProto::time_unlock_order(ref order) => { + time_lock_order::TimeUnlockOrder::from_tw_proto(coin, order) + .map(BinanceMessageEnum::TimeUnlockOrder) + }, + BinanceMessageProto::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + } + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + match self { + BinanceMessageEnum::HTLTOrder(m) => BinanceMessageProto::htlt_order(m.to_tw_proto()), + BinanceMessageEnum::DepositHTLTOrder(m) => { + BinanceMessageProto::depositHTLT_order(m.to_tw_proto()) + }, + BinanceMessageEnum::ClaimHTLTOrder(m) => { + BinanceMessageProto::claimHTLT_order(m.to_tw_proto()) + }, + BinanceMessageEnum::RefundHTLTOrder(m) => { + BinanceMessageProto::refundHTLT_order(m.to_tw_proto()) + }, + BinanceMessageEnum::SendOrder(m) => BinanceMessageProto::send_order(m.to_tw_proto()), + BinanceMessageEnum::SideDelegateOrder(m) => { + BinanceMessageProto::side_delegate_order(m.to_tw_proto()) + }, + BinanceMessageEnum::SideRedelegateOrder(m) => { + BinanceMessageProto::side_redelegate_order(m.to_tw_proto()) + }, + BinanceMessageEnum::SideUndelegateOrder(m) => { + BinanceMessageProto::side_undelegate_order(m.to_tw_proto()) + }, + BinanceMessageEnum::TimeLockOrder(m) => { + BinanceMessageProto::time_lock_order(m.to_tw_proto()) + }, + BinanceMessageEnum::TimeRelockOrder(m) => { + BinanceMessageProto::time_relock_order(m.to_tw_proto()) + }, + BinanceMessageEnum::TimeUnlockOrder(m) => { + BinanceMessageProto::time_unlock_order(m.to_tw_proto()) + }, + BinanceMessageEnum::TokenFreezeOrder(m) => { + BinanceMessageProto::freeze_order(m.to_tw_proto()) + }, + BinanceMessageEnum::TokenUnfreezeOrder(m) => { + BinanceMessageProto::unfreeze_order(m.to_tw_proto()) + }, + BinanceMessageEnum::TokenIssueOrder(m) => { + BinanceMessageProto::issue_order(m.to_tw_proto()) + }, + BinanceMessageEnum::TokenMintOrder(m) => { + BinanceMessageProto::mint_order(m.to_tw_proto()) + }, + BinanceMessageEnum::TokenBurnOrder(m) => { + BinanceMessageProto::burn_order(m.to_tw_proto()) + }, + BinanceMessageEnum::NewTradeOrder(m) => { + BinanceMessageProto::trade_order(m.to_tw_proto()) + }, + BinanceMessageEnum::CancelTradeOrder(m) => { + BinanceMessageProto::cancel_trade_order(m.to_tw_proto()) + }, + BinanceMessageEnum::TransferOutOrder(m) => { + BinanceMessageProto::transfer_out_order(m.to_tw_proto()) + }, + } } } -pub fn message_to_json(msg: T) -> SigningResult { - serde_json::to_value(msg).map_err(|_| SigningError(SigningErrorType::Error_internal)) +impl<'a> AsRef for BinanceMessageEnum { + fn as_ref(&self) -> &(dyn BinanceMessage + 'a) { + match self { + BinanceMessageEnum::HTLTOrder(m) => m, + BinanceMessageEnum::DepositHTLTOrder(m) => m, + BinanceMessageEnum::ClaimHTLTOrder(m) => m, + BinanceMessageEnum::RefundHTLTOrder(m) => m, + BinanceMessageEnum::SendOrder(m) => m, + BinanceMessageEnum::SideDelegateOrder(m) => m, + BinanceMessageEnum::SideRedelegateOrder(m) => m, + BinanceMessageEnum::SideUndelegateOrder(m) => m, + BinanceMessageEnum::TimeLockOrder(m) => m, + BinanceMessageEnum::TimeRelockOrder(m) => m, + BinanceMessageEnum::TimeUnlockOrder(m) => m, + BinanceMessageEnum::TokenFreezeOrder(m) => m, + BinanceMessageEnum::TokenUnfreezeOrder(m) => m, + BinanceMessageEnum::TokenIssueOrder(m) => m, + BinanceMessageEnum::TokenMintOrder(m) => m, + BinanceMessageEnum::TokenBurnOrder(m) => m, + BinanceMessageEnum::NewTradeOrder(m) => m, + BinanceMessageEnum::CancelTradeOrder(m) => m, + BinanceMessageEnum::TransferOutOrder(m) => m, + } + } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct Token { - /// Token ID. - pub denom: String, /// Amount. pub amount: i64, + /// Token ID. + pub denom: String, } impl Token { - pub fn to_proto(&self) -> Proto::mod_SendOrder::Token { - Proto::mod_SendOrder::Token { - denom: self.denom.clone().into(), - amount: self.amount, - } - } - pub fn serialize_with_string_amount(&self, serializer: S) -> Result where S: Serializer, { #[derive(Serialize)] struct TokenWithStringAmount<'a> { - denom: &'a str, amount: String, + denom: &'a str, } TokenWithStringAmount { - denom: &self.denom, amount: self.amount.to_string(), + denom: &self.denom, } .serialize(serializer) } + + fn from_tw_proto(msg: &Proto::mod_SendOrder::Token<'_>) -> Self { + Token { + denom: msg.denom.to_string(), + amount: msg.amount, + } + } + + fn to_tw_proto(&self) -> Proto::mod_SendOrder::Token<'static> { + Proto::mod_SendOrder::Token { + denom: self.denom.clone().into(), + amount: self.amount, + } + } } diff --git a/rust/chains/tw_binance/src/transaction/message/send_order.rs b/rust/chains/tw_binance/src/transaction/message/send_order.rs index f8e5632a85d..23f9460c160 100644 --- a/rust/chains/tw_binance/src/transaction/message/send_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/send_order.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,16 +6,16 @@ use crate::address::BinanceAddress; use crate::amino::AminoEncoder; -use crate::transaction::message::{message_to_json, BinanceMessage, Token}; -use serde::Serialize; -use serde_json::Value as Json; +use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; +use serde::{Deserialize, Serialize}; +use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; use tw_coin_entry::error::SigningResult; use tw_memory::Data; use tw_proto::Binance::Proto; /// Either an input or output of a `SendOrder`. -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct InOut { /// Source address. pub address: BinanceAddress, @@ -25,22 +25,22 @@ pub struct InOut { } impl InOut { - pub fn to_input_proto(&self) -> Proto::mod_SendOrder::Input { + pub fn to_input_proto(&self) -> Proto::mod_SendOrder::Input<'static> { Proto::mod_SendOrder::Input { address: self.address.data().into(), - coins: self.coins.iter().map(Token::to_proto).collect(), + coins: self.coins.iter().map(Token::to_tw_proto).collect(), } } - pub fn to_output_proto(&self) -> Proto::mod_SendOrder::Output { + pub fn to_output_proto(&self) -> Proto::mod_SendOrder::Output<'static> { Proto::mod_SendOrder::Output { address: self.address.data().into(), - coins: self.coins.iter().map(Token::to_proto).collect(), + coins: self.coins.iter().map(Token::to_tw_proto).collect(), } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct SendOrder { pub inputs: Vec, pub outputs: Vec, @@ -52,18 +52,47 @@ impl SendOrder { } impl BinanceMessage for SendOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::SendOrder { +impl TWBinanceProto for SendOrder { + type Proto<'a> = Proto::SendOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + fn in_out_from_proto( + coin: &dyn CoinContext, + address_key_hash: &[u8], + coins: &[Proto::mod_SendOrder::Token], + ) -> SigningResult { + let address = BinanceAddress::from_key_hash_with_coin(coin, address_key_hash.to_vec())?; + let coins = coins.iter().map(Token::from_tw_proto).collect(); + + Ok(InOut { address, coins }) + } + + let inputs = msg + .inputs + .iter() + .map(|input| in_out_from_proto(coin, &input.address, &input.coins)) + .collect::>>()?; + + let outputs = msg + .outputs + .iter() + .map(|output| in_out_from_proto(coin, &output.address, &output.coins)) + .collect::>>()?; + + Ok(SendOrder { inputs, outputs }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::SendOrder { inputs: self.inputs.iter().map(InOut::to_input_proto).collect(), outputs: self.outputs.iter().map(InOut::to_output_proto).collect(), - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } diff --git a/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs b/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs index d376e530eee..39008818681 100644 --- a/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs +++ b/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,26 +6,28 @@ use crate::address::BinanceAddress; use crate::amino::AminoEncoder; -use crate::transaction::message::{message_to_json, BinanceMessage, Token}; -use serde::Serialize; -use serde_json::Value as Json; +use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; +use serde::{Deserialize, Serialize}; +use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::SigningResult; -use tw_cosmos_sdk::modules::serializer::json_serializer::AnyMsg; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_memory::Data; +use tw_misc::serde::Typed; use tw_proto::Binance::Proto; +pub type SideDelegateOrder = Typed; + /// cosmos-sdk/MsgSideChainDelegate -#[derive(Serialize)] -pub struct SideDelegateOrder { - pub delegator_addr: BinanceAddress, - pub validator_addr: BinanceAddress, +#[derive(Deserialize, Serialize)] +pub struct SideDelegateOrderValue { #[serde(serialize_with = "Token::serialize_with_string_amount")] pub delegation: Token, + pub delegator_addr: BinanceAddress, pub side_chain_id: String, + pub validator_addr: BinanceAddress, } -impl SideDelegateOrder { +impl SideDelegateOrderValue { /// cbindgen:ignore pub const PREFIX: [u8; 4] = [0xE3, 0xA0, 0x7F, 0xD2]; /// cbindgen:ignore @@ -33,40 +35,62 @@ impl SideDelegateOrder { } impl BinanceMessage for SideDelegateOrder { - fn to_json(&self) -> SigningResult { - let any_msg = AnyMsg { - msg_type: Self::MESSAGE_TYPE.to_string(), - value: message_to_json(self)?, - }; - message_to_json(any_msg) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&SideDelegateOrderValue::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::SideChainDelegate { - delegator_addr: self.delegator_addr.data().into(), - validator_addr: self.validator_addr.data().into(), - delegation: Some(self.delegation.to_proto()), - chain_id: self.side_chain_id.clone().into(), +impl TWBinanceProto for SideDelegateOrder { + type Proto<'a> = Proto::SideChainDelegate<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let delegator_addr = + BinanceAddress::from_key_hash_with_coin(coin, msg.delegator_addr.to_vec())?; + let validator_addr = BinanceAddress::new_validator_addr(msg.validator_addr.to_vec())?; + + let delegation = msg + .delegation + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + + let value = SideDelegateOrderValue { + delegator_addr, + validator_addr, + delegation: Token::from_tw_proto(delegation), + side_chain_id: msg.chain_id.to_string(), }; + Ok(Typed { + ty: SideDelegateOrderValue::MESSAGE_TYPE.to_string(), + value, + }) + } - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::SideChainDelegate { + delegator_addr: self.value.delegator_addr.data().into(), + validator_addr: self.value.validator_addr.data().into(), + delegation: Some(self.value.delegation.to_tw_proto()), + chain_id: self.value.side_chain_id.clone().into(), + } } } +pub type SideRedelegateOrder = Typed; + /// cosmos-sdk/MsgSideChainRedelegate -#[derive(Serialize)] -pub struct SideRedelegateOrder { - pub delegator_addr: BinanceAddress, - pub validator_src_addr: BinanceAddress, - pub validator_dst_addr: BinanceAddress, +#[derive(Deserialize, Serialize)] +pub struct SideRedelegateOrderValue { #[serde(serialize_with = "Token::serialize_with_string_amount")] pub amount: Token, + pub delegator_addr: BinanceAddress, pub side_chain_id: String, + pub validator_dst_addr: BinanceAddress, + pub validator_src_addr: BinanceAddress, } -impl SideRedelegateOrder { +impl SideRedelegateOrderValue { /// cbindgen:ignore pub const PREFIX: [u8; 4] = [0xE3, 0xCE, 0xD3, 0x64]; /// cbindgen:ignore @@ -74,40 +98,66 @@ impl SideRedelegateOrder { } impl BinanceMessage for SideRedelegateOrder { - fn to_json(&self) -> SigningResult { - let any_msg = AnyMsg { - msg_type: Self::MESSAGE_TYPE.to_string(), - value: message_to_json(self)?, - }; - message_to_json(any_msg) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&SideRedelegateOrderValue::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::SideChainRedelegate { - delegator_addr: self.delegator_addr.data().into(), - validator_src_addr: self.validator_src_addr.data().into(), - validator_dst_addr: self.validator_dst_addr.data().into(), - amount: Some(self.amount.to_proto()), - chain_id: self.side_chain_id.clone().into(), +impl TWBinanceProto for SideRedelegateOrder { + type Proto<'a> = Proto::SideChainRedelegate<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let delegator_addr = + BinanceAddress::from_key_hash_with_coin(coin, msg.delegator_addr.to_vec())?; + let validator_src_addr = + BinanceAddress::new_validator_addr(msg.validator_src_addr.to_vec())?; + let validator_dst_addr = + BinanceAddress::new_validator_addr(msg.validator_dst_addr.to_vec())?; + + let amount = msg + .amount + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + + let value = SideRedelegateOrderValue { + delegator_addr, + validator_src_addr, + validator_dst_addr, + amount: Token::from_tw_proto(amount), + side_chain_id: msg.chain_id.to_string(), }; + Ok(Typed { + ty: SideRedelegateOrderValue::MESSAGE_TYPE.to_string(), + value, + }) + } - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::SideChainRedelegate { + delegator_addr: self.value.delegator_addr.data().into(), + validator_src_addr: self.value.validator_src_addr.data().into(), + validator_dst_addr: self.value.validator_dst_addr.data().into(), + amount: Some(self.value.amount.to_tw_proto()), + chain_id: self.value.side_chain_id.clone().into(), + } } } +pub type SideUndelegateOrder = Typed; + /// cosmos-sdk/MsgSideChainUndelegate -#[derive(Serialize)] -pub struct SideUndelegateOrder { - pub delegator_addr: BinanceAddress, - pub validator_addr: BinanceAddress, +#[derive(Deserialize, Serialize)] +pub struct SideUndelegateOrderValue { #[serde(serialize_with = "Token::serialize_with_string_amount")] pub amount: Token, + pub delegator_addr: BinanceAddress, pub side_chain_id: String, + pub validator_addr: BinanceAddress, } -impl SideUndelegateOrder { +impl SideUndelegateOrderValue { /// cbindgen:ignore pub const PREFIX: [u8; 4] = [0x51, 0x4F, 0x7E, 0x0E]; /// cbindgen:ignore @@ -115,24 +165,44 @@ impl SideUndelegateOrder { } impl BinanceMessage for SideUndelegateOrder { - fn to_json(&self) -> SigningResult { - let any_msg = AnyMsg { - msg_type: Self::MESSAGE_TYPE.to_string(), - value: message_to_json(self)?, - }; - message_to_json(any_msg) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&SideUndelegateOrderValue::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::SideChainUndelegate { - delegator_addr: self.delegator_addr.data().into(), - validator_addr: self.validator_addr.data().into(), - amount: Some(self.amount.to_proto()), - chain_id: self.side_chain_id.clone().into(), +impl TWBinanceProto for SideUndelegateOrder { + type Proto<'a> = Proto::SideChainUndelegate<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let delegator_addr = + BinanceAddress::from_key_hash_with_coin(coin, msg.delegator_addr.to_vec())?; + let validator_addr = BinanceAddress::new_validator_addr(msg.validator_addr.to_vec())?; + + let amount = msg + .amount + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + + let value = SideUndelegateOrderValue { + delegator_addr, + validator_addr, + amount: Token::from_tw_proto(amount), + side_chain_id: msg.chain_id.to_string(), }; + Ok(Typed { + ty: SideUndelegateOrderValue::MESSAGE_TYPE.to_string(), + value, + }) + } - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::SideChainUndelegate { + delegator_addr: self.value.delegator_addr.data().into(), + validator_addr: self.value.validator_addr.data().into(), + amount: Some(self.value.amount.to_tw_proto()), + chain_id: self.value.side_chain_id.clone().into(), + } } } diff --git a/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs b/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs index 83dbdd6cf24..0581dba133c 100644 --- a/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,19 +6,19 @@ use crate::address::BinanceAddress; use crate::amino::AminoEncoder; -use crate::transaction::message::{message_to_json, BinanceMessage, Token}; -use serde::Serialize; -use serde_json::Value as Json; +use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; +use serde::{Deserialize, Serialize}; +use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; use tw_coin_entry::error::SigningResult; use tw_memory::Data; use tw_proto::Binance::Proto; -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct TimeLockOrder { - pub from: BinanceAddress, - pub description: String, pub amount: Vec, + pub description: String, + pub from: BinanceAddress, pub lock_time: i64, } @@ -28,32 +28,46 @@ impl TimeLockOrder { } impl BinanceMessage for TimeLockOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::TimeLockOrder { +impl TWBinanceProto for TimeLockOrder { + type Proto<'a> = Proto::TimeLockOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from_address.to_vec())?; + let amount = msg.amount.iter().map(Token::from_tw_proto).collect(); + + Ok(TimeLockOrder { + from, + description: msg.description.to_string(), + amount, + lock_time: msg.lock_time, + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::TimeLockOrder { from_address: self.from.data().into(), description: self.description.clone().into(), - amount: self.amount.iter().map(Token::to_proto).collect(), + amount: self.amount.iter().map(Token::to_tw_proto).collect(), lock_time: self.lock_time, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct TimeRelockOrder { - pub from: BinanceAddress, - pub time_lock_id: i64, - pub description: String, /// If the amount is empty or omitted, set null to avoid signature verification error. pub amount: Option>, + pub description: String, + pub from: BinanceAddress, pub lock_time: i64, + pub time_lock_id: i64, } impl TimeRelockOrder { @@ -62,31 +76,51 @@ impl TimeRelockOrder { } impl BinanceMessage for TimeRelockOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { +impl TWBinanceProto for TimeRelockOrder { + type Proto<'a> = Proto::TimeRelockOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from_address.to_vec())?; + + let amount = if msg.amount.is_empty() { + None + } else { + Some(msg.amount.iter().map(Token::from_tw_proto).collect()) + }; + + Ok(TimeRelockOrder { + from, + time_lock_id: msg.id, + description: msg.description.to_string(), + amount, + lock_time: msg.lock_time, + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { let amount = match self.amount { - Some(ref tokens) => tokens.iter().map(Token::to_proto).collect(), + Some(ref tokens) => tokens.iter().map(Token::to_tw_proto).collect(), None => Vec::default(), }; - let msg = Proto::TimeRelockOrder { + Proto::TimeRelockOrder { from_address: self.from.data().into(), id: self.time_lock_id, description: self.description.clone().into(), amount, lock_time: self.lock_time, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct TimeUnlockOrder { pub from: BinanceAddress, pub time_lock_id: i64, @@ -98,18 +132,28 @@ impl TimeUnlockOrder { } impl BinanceMessage for TimeUnlockOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::TimeUnlockOrder { +impl TWBinanceProto for TimeUnlockOrder { + type Proto<'a> = Proto::TimeUnlockOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from_address.to_vec())?; + Ok(TimeUnlockOrder { + from, + time_lock_id: msg.id, + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::TimeUnlockOrder { from_address: self.from.data().into(), id: self.time_lock_id, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } diff --git a/rust/chains/tw_binance/src/transaction/message/token_order.rs b/rust/chains/tw_binance/src/transaction/message/token_order.rs index 16f2594fbcb..1d2c442cd9a 100644 --- a/rust/chains/tw_binance/src/transaction/message/token_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/token_order.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,19 +6,19 @@ use crate::address::BinanceAddress; use crate::amino::AminoEncoder; -use crate::transaction::message::{message_to_json, BinanceMessage}; -use serde::Serialize; -use serde_json::Value as Json; +use crate::transaction::message::{BinanceMessage, TWBinanceProto}; +use serde::{Deserialize, Serialize}; +use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; use tw_coin_entry::error::SigningResult; use tw_memory::Data; use tw_proto::Binance::Proto; -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct TokenFreezeOrder { + pub amount: i64, pub from: BinanceAddress, pub symbol: String, - pub amount: i64, } impl TokenFreezeOrder { @@ -27,28 +27,39 @@ impl TokenFreezeOrder { } impl BinanceMessage for TokenFreezeOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::TokenFreezeOrder { +impl TWBinanceProto for TokenFreezeOrder { + type Proto<'a> = Proto::TokenFreezeOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + Ok(TokenFreezeOrder { + from, + symbol: msg.symbol.to_string(), + amount: msg.amount, + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::TokenFreezeOrder { from: self.from.data().into(), symbol: self.symbol.clone().into(), amount: self.amount, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct TokenUnfreezeOrder { + pub amount: i64, pub from: BinanceAddress, pub symbol: String, - pub amount: i64, } impl TokenUnfreezeOrder { @@ -57,30 +68,41 @@ impl TokenUnfreezeOrder { } impl BinanceMessage for TokenUnfreezeOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::TokenUnfreezeOrder { +impl TWBinanceProto for TokenUnfreezeOrder { + type Proto<'a> = Proto::TokenUnfreezeOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + Ok(TokenUnfreezeOrder { + from, + symbol: msg.symbol.to_string(), + amount: msg.amount, + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::TokenUnfreezeOrder { from: self.from.data().into(), symbol: self.symbol.clone().into(), amount: self.amount, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct TokenIssueOrder { pub from: BinanceAddress, + pub mintable: bool, pub name: String, pub symbol: String, pub total_supply: i64, - pub mintable: bool, } impl TokenIssueOrder { @@ -89,30 +111,43 @@ impl TokenIssueOrder { } impl BinanceMessage for TokenIssueOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) + } +} + +impl TWBinanceProto for TokenIssueOrder { + type Proto<'a> = Proto::TokenIssueOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + Ok(TokenIssueOrder { + from, + name: msg.name.to_string(), + symbol: msg.symbol.to_string(), + total_supply: msg.total_supply, + mintable: msg.mintable, + }) } - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::TokenIssueOrder { + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::TokenIssueOrder { from: self.from.data().into(), name: self.name.clone().into(), symbol: self.symbol.clone().into(), total_supply: self.total_supply, mintable: self.mintable, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct TokenMintOrder { + pub amount: i64, pub from: BinanceAddress, pub symbol: String, - pub amount: i64, } impl TokenMintOrder { @@ -121,28 +156,39 @@ impl TokenMintOrder { } impl BinanceMessage for TokenMintOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) + } +} + +impl TWBinanceProto for TokenMintOrder { + type Proto<'a> = Proto::TokenMintOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + Ok(TokenMintOrder { + from, + symbol: msg.symbol.to_string(), + amount: msg.amount, + }) } - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::TokenMintOrder { + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::TokenMintOrder { from: self.from.data().into(), symbol: self.symbol.clone().into(), amount: self.amount, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct TokenBurnOrder { + pub amount: i64, pub from: BinanceAddress, pub symbol: String, - pub amount: i64, } impl TokenBurnOrder { @@ -151,19 +197,30 @@ impl TokenBurnOrder { } impl BinanceMessage for TokenBurnOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::TokenBurnOrder { +impl TWBinanceProto for TokenBurnOrder { + type Proto<'a> = Proto::TokenBurnOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + Ok(TokenBurnOrder { + from, + symbol: msg.symbol.to_string(), + amount: msg.amount, + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::TokenBurnOrder { from: self.from.data().into(), symbol: self.symbol.clone().into(), amount: self.amount, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } diff --git a/rust/chains/tw_binance/src/transaction/message/trade_order.rs b/rust/chains/tw_binance/src/transaction/message/trade_order.rs index 8e02780e214..9020bb00f9d 100644 --- a/rust/chains/tw_binance/src/transaction/message/trade_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/trade_order.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,22 +6,24 @@ use crate::address::BinanceAddress; use crate::amino::AminoEncoder; -use crate::transaction::message::{message_to_json, BinanceMessage}; -use serde::Serialize; -use serde_json::Value as Json; +use crate::transaction::message::{BinanceMessage, TWBinanceProto}; +use serde::{Deserialize, Serialize}; +use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_memory::Data; use tw_proto::Binance::Proto; #[repr(i64)] -#[derive(Clone, Copy, serde_repr::Serialize_repr, strum_macros::FromRepr)] +#[derive( + Clone, Copy, serde_repr::Deserialize_repr, serde_repr::Serialize_repr, strum_macros::FromRepr, +)] pub enum OrderType { /// https://github.com/bnb-chain/python-sdk/blob/0f6b8a6077f486b26eda3e448f3e84ef35bfff75/binance_chain/constants.py#L62 Limit = 2, } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct NewTradeOrder { /// Order id, optional. pub id: String, @@ -49,12 +51,35 @@ impl NewTradeOrder { } impl BinanceMessage for NewTradeOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::TradeOrder { +impl TWBinanceProto for NewTradeOrder { + type Proto<'a> = Proto::TradeOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let order_type = OrderType::from_repr(msg.ordertype) + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + let sender = BinanceAddress::from_key_hash_with_coin(coin, msg.sender.to_vec())?; + + Ok(NewTradeOrder { + id: msg.id.to_string(), + order_type, + price: msg.price, + quantity: msg.quantity, + sender, + side: msg.side, + symbol: msg.symbol.to_string(), + time_in_force: msg.timeinforce, + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::TradeOrder { id: self.id.clone().into(), ordertype: self.order_type as i64, price: self.price, @@ -63,21 +88,18 @@ impl BinanceMessage for NewTradeOrder { side: self.side, symbol: self.symbol.clone().into(), timeinforce: self.time_in_force, - }; - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct CancelTradeOrder { + /// Order id to cancel. + pub refid: String, /// Originating address. pub sender: BinanceAddress, /// Symbol for trading pair in full name of the tokens. pub symbol: String, - /// Order id to cancel. - pub refid: String, } impl CancelTradeOrder { @@ -86,18 +108,30 @@ impl CancelTradeOrder { } impl BinanceMessage for CancelTradeOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) + } +} + +impl TWBinanceProto for CancelTradeOrder { + type Proto<'a> = Proto::CancelTradeOrder<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let sender = BinanceAddress::from_key_hash_with_coin(coin, msg.sender.to_vec())?; + Ok(CancelTradeOrder { + sender, + symbol: msg.symbol.to_string(), + refid: msg.refid.to_string(), + }) } - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::CancelTradeOrder { + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::CancelTradeOrder { sender: self.sender.data().into(), symbol: self.symbol.clone().into(), refid: self.refid.clone().into(), - }; - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } diff --git a/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs b/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs index 3f838793e11..68030deb104 100644 --- a/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,21 +6,22 @@ use crate::address::BinanceAddress; use crate::amino::AminoEncoder; -use crate::transaction::message::{message_to_json, BinanceMessage, Token}; -use serde::Serialize; -use serde_json::Value as Json; +use crate::transaction::message::{BinanceMessage, TWBinanceProto, Token}; +use serde::{Deserialize, Serialize}; +use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_evm::address::Address as EthereumAddress; +use tw_hash::H160; use tw_memory::Data; use tw_proto::Binance::Proto; -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct TransferOutOrder { - pub from: BinanceAddress, - pub to: EthereumAddress, pub amount: Token, pub expire_time: i64, + pub from: BinanceAddress, + pub to: EthereumAddress, } impl TransferOutOrder { @@ -29,20 +30,42 @@ impl TransferOutOrder { } impl BinanceMessage for TransferOutOrder { - fn to_json(&self) -> SigningResult { - message_to_json(self) + fn to_amino_protobuf(&self) -> SigningResult { + Ok(AminoEncoder::new(&Self::PREFIX) + .extend_with_msg(&self.to_tw_proto())? + .encode()) } +} - fn to_amino_protobuf(&self) -> SigningResult { - let msg = Proto::TransferOut { +impl TWBinanceProto for TransferOutOrder { + type Proto<'a> = Proto::TransferOut<'a>; + + fn from_tw_proto(coin: &dyn CoinContext, msg: &Self::Proto<'_>) -> SigningResult { + let from = BinanceAddress::from_key_hash_with_coin(coin, msg.from.to_vec())?; + + let to_bytes = H160::try_from(msg.to.as_ref()) + .map_err(|_| SigningError(SigningErrorType::Error_invalid_address))?; + let to = EthereumAddress::from_bytes(to_bytes); + + let amount_proto = msg + .amount + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + + Ok(TransferOutOrder { + from, + to, + amount: Token::from_tw_proto(amount_proto), + expire_time: msg.expire_time, + }) + } + + fn to_tw_proto(&self) -> Self::Proto<'static> { + Proto::TransferOut { from: self.from.data().into(), to: self.to.data().into(), - amount: Some(self.amount.to_proto()), + amount: Some(self.amount.to_tw_proto()), expire_time: self.expire_time, - }; - - Ok(AminoEncoder::new(&Self::PREFIX) - .extend_with_msg(&msg)? - .encode()) + } } } diff --git a/rust/chains/tw_binance/src/transaction/mod.rs b/rust/chains/tw_binance/src/transaction/mod.rs index 74e96f2595b..021f7180203 100644 --- a/rust/chains/tw_binance/src/transaction/mod.rs +++ b/rust/chains/tw_binance/src/transaction/mod.rs @@ -1,29 +1,29 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. use crate::signature::BinanceSignature; -use crate::transaction::message::BinanceMessageBox; -use serde::Serialize; -use tw_keypair::ecdsa::secp256k1; +use crate::transaction::message::BinanceMessageEnum; +use serde::{Deserialize, Serialize}; +use tw_cosmos_sdk::public_key::secp256k1::Secp256PublicKey; use tw_memory::Data; use tw_misc::serde::as_string; pub mod message; -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct UnsignedTransaction { - #[serde(serialize_with = "as_string")] + #[serde(with = "as_string")] pub account_number: i64, pub chain_id: String, pub data: Option, pub memo: String, - pub msgs: Vec, - #[serde(serialize_with = "as_string")] + pub msgs: Vec, + #[serde(with = "as_string")] pub sequence: i64, - #[serde(serialize_with = "as_string")] + #[serde(with = "as_string")] pub source: i64, } @@ -37,7 +37,7 @@ impl UnsignedTransaction { } pub struct SignerInfo { - pub public_key: secp256k1::PublicKey, + pub public_key: Secp256PublicKey, pub signature: BinanceSignature, } diff --git a/rust/chains/tw_cosmos/src/entry.rs b/rust/chains/tw_cosmos/src/entry.rs index ca2d84efcf7..fad4cfb1735 100644 --- a/rust/chains/tw_cosmos/src/entry.rs +++ b/rust/chains/tw_cosmos/src/entry.rs @@ -12,6 +12,7 @@ use tw_coin_entry::error::AddressResult; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_cosmos_sdk::address::{Address, Bech32Prefix}; use tw_cosmos_sdk::context::StandardCosmosContext; use tw_cosmos_sdk::modules::compiler::tw_compiler::TWTransactionCompiler; @@ -31,6 +32,7 @@ impl CoinEntry for CosmosEntry { type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; + type WalletConnector = NoWalletConnector; #[inline] fn parse_address( diff --git a/rust/chains/tw_greenfield/src/entry.rs b/rust/chains/tw_greenfield/src/entry.rs index c78823b9b27..c3aa6fd68d0 100644 --- a/rust/chains/tw_greenfield/src/entry.rs +++ b/rust/chains/tw_greenfield/src/entry.rs @@ -15,6 +15,7 @@ use tw_coin_entry::error::{AddressError, AddressResult}; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_coin_entry::prefix::NoPrefix; use tw_keypair::tw::PublicKey; use tw_proto::Greenfield::Proto; @@ -33,6 +34,7 @@ impl CoinEntry for GreenfieldEntry { type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; + type WalletConnector = NoWalletConnector; #[inline] fn parse_address( diff --git a/rust/chains/tw_native_evmos/src/entry.rs b/rust/chains/tw_native_evmos/src/entry.rs index eb25d7ea816..9f1d0c9820a 100644 --- a/rust/chains/tw_native_evmos/src/entry.rs +++ b/rust/chains/tw_native_evmos/src/entry.rs @@ -13,6 +13,7 @@ use tw_coin_entry::error::AddressResult; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_cosmos_sdk::address::{Address, Bech32Prefix}; use tw_cosmos_sdk::modules::compiler::tw_compiler::TWTransactionCompiler; use tw_cosmos_sdk::modules::signer::tw_signer::TWSigner; @@ -31,6 +32,7 @@ impl CoinEntry for NativeEvmosEntry { type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; + type WalletConnector = NoWalletConnector; #[inline] fn parse_address( diff --git a/rust/chains/tw_native_injective/src/entry.rs b/rust/chains/tw_native_injective/src/entry.rs index b11d0036c87..ed1759554ec 100644 --- a/rust/chains/tw_native_injective/src/entry.rs +++ b/rust/chains/tw_native_injective/src/entry.rs @@ -13,6 +13,7 @@ use tw_coin_entry::error::AddressResult; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_cosmos_sdk::address::{Address, Bech32Prefix, CosmosAddress}; use tw_cosmos_sdk::modules::compiler::tw_compiler::TWTransactionCompiler; use tw_cosmos_sdk::modules::signer::tw_signer::TWSigner; @@ -31,6 +32,7 @@ impl CoinEntry for NativeInjectiveEntry { type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; + type WalletConnector = NoWalletConnector; #[inline] fn parse_address( diff --git a/rust/chains/tw_thorchain/src/entry.rs b/rust/chains/tw_thorchain/src/entry.rs index 81f8bf562f1..4cd52da3965 100644 --- a/rust/chains/tw_thorchain/src/entry.rs +++ b/rust/chains/tw_thorchain/src/entry.rs @@ -14,6 +14,7 @@ use tw_coin_entry::error::AddressResult; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_cosmos_sdk::address::{Address, Bech32Prefix, CosmosAddress}; use tw_keypair::tw; use tw_proto::Cosmos::Proto; @@ -30,6 +31,7 @@ impl CoinEntry for ThorchainEntry { type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; + type WalletConnector = NoWalletConnector; #[inline] fn parse_address( diff --git a/rust/tw_any_coin/src/ffi/mod.rs b/rust/tw_any_coin/src/ffi/mod.rs index 62c144645d8..854c6cd5e98 100644 --- a/rust/tw_any_coin/src/ffi/mod.rs +++ b/rust/tw_any_coin/src/ffi/mod.rs @@ -8,3 +8,4 @@ pub mod tw_any_address; pub mod tw_any_signer; pub mod tw_message_signer; pub mod tw_transaction_compiler; +pub mod tw_wallet_connect_request; diff --git a/rust/tw_any_coin/src/ffi/tw_wallet_connect_request.rs b/rust/tw_any_coin/src/ffi/tw_wallet_connect_request.rs new file mode 100644 index 00000000000..fbbb5da4247 --- /dev/null +++ b/rust/tw_any_coin/src/ffi/tw_wallet_connect_request.rs @@ -0,0 +1,31 @@ +// Copyright © 2017-2024 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#![allow(clippy::missing_safety_doc)] + +use crate::wallet_connect_request::WalletConnectRequest; +use tw_coin_registry::coin_type::CoinType; +use tw_memory::ffi::tw_data::TWData; +use tw_memory::ffi::RawPtrTrait; +use tw_misc::try_or_else; + +/// Parses the WalletConnect signing request as a `SigningInput`. +/// +/// \param coin The given coin type to plan the transaction for. +/// \param input The serialized data of a `WalletConnect::Proto::ParseRequestInput` proto object. +/// \return The serialized data of `WalletConnect::Proto::ParseRequestOutput` proto object. +#[no_mangle] +pub unsafe extern "C" fn tw_wallet_connect_request_parse( + coin: u32, + input: *const TWData, +) -> *mut TWData { + let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut); + let input = try_or_else!(TWData::from_ptr_as_ref(input), std::ptr::null_mut); + + WalletConnectRequest::parse(coin, input.as_slice()) + .map(|output| TWData::from(output).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} diff --git a/rust/tw_any_coin/src/lib.rs b/rust/tw_any_coin/src/lib.rs index ebe47631a8e..5788831c45a 100644 --- a/rust/tw_any_coin/src/lib.rs +++ b/rust/tw_any_coin/src/lib.rs @@ -9,6 +9,7 @@ pub mod any_signer; pub mod ffi; pub mod message_signer; pub mod transaction_compiler; +pub mod wallet_connect_request; #[cfg(feature = "test-utils")] pub mod test_utils; diff --git a/rust/tw_any_coin/src/test_utils/mod.rs b/rust/tw_any_coin/src/test_utils/mod.rs index 098e098ea64..2c5b412f804 100644 --- a/rust/tw_any_coin/src/test_utils/mod.rs +++ b/rust/tw_any_coin/src/test_utils/mod.rs @@ -6,3 +6,4 @@ pub mod address_utils; pub mod sign_utils; +pub mod wallet_connect_utils; diff --git a/rust/tw_any_coin/src/test_utils/wallet_connect_utils.rs b/rust/tw_any_coin/src/test_utils/wallet_connect_utils.rs new file mode 100644 index 00000000000..d270a3422f2 --- /dev/null +++ b/rust/tw_any_coin/src/test_utils/wallet_connect_utils.rs @@ -0,0 +1,34 @@ +// Copyright © 2017-2024 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::ffi::tw_wallet_connect_request::tw_wallet_connect_request_parse; +use tw_coin_registry::coin_type::CoinType; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_memory::Data; +use tw_proto::{deserialize, serialize, WalletConnect::Proto as WCProto}; + +#[derive(Default)] +pub struct WalletConnectRequestHelper { + output_data: Data, +} + +impl WalletConnectRequestHelper { + pub fn parse<'a>( + &'a mut self, + coin_type: CoinType, + input: &WCProto::ParseRequestInput, + ) -> WCProto::ParseRequestOutput<'a> { + let input_data = TWDataHelper::create(serialize(input).unwrap()); + + self.output_data = TWDataHelper::wrap(unsafe { + tw_wallet_connect_request_parse(coin_type as u32, input_data.ptr()) + }) + .to_vec() + .expect("!tw_wallet_connect_request_parse returned nullptr"); + + deserialize(&self.output_data).unwrap() + } +} diff --git a/rust/tw_any_coin/src/wallet_connect_request.rs b/rust/tw_any_coin/src/wallet_connect_request.rs new file mode 100644 index 00000000000..97a5f672ea5 --- /dev/null +++ b/rust/tw_any_coin/src/wallet_connect_request.rs @@ -0,0 +1,25 @@ +// Copyright © 2017-2024 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use tw_coin_entry::error::{SigningError, SigningResult}; +use tw_coin_registry::coin_type::CoinType; +use tw_coin_registry::dispatcher::coin_dispatcher; +use tw_memory::Data; + +/// Represents a signer to sign transactions for any blockchain. +pub struct WalletConnectRequest; + +impl WalletConnectRequest { + /// Parses the WalletConnect signing request as a `SigningInput`. + /// It is optional. Returns an error if the chain does not support WalletConnect signing. + #[inline] + pub fn parse(coin: CoinType, input: &[u8]) -> SigningResult { + let (ctx, entry) = coin_dispatcher(coin)?; + entry + .wallet_connect_parse_request(&ctx, input) + .map_err(SigningError::from) + } +} diff --git a/rust/tw_any_coin/tests/chains/binance/binance_address.rs b/rust/tw_any_coin/tests/chains/binance/binance_address.rs index d915ef496a8..076bcf9f61f 100644 --- a/rust/tw_any_coin/tests/chains/binance/binance_address.rs +++ b/rust/tw_any_coin/tests/chains/binance/binance_address.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/rust/tw_any_coin/tests/chains/binance/binance_compile.rs b/rust/tw_any_coin/tests/chains/binance/binance_compile.rs index f1104b9657e..6f3b0766e02 100644 --- a/rust/tw_any_coin/tests/chains/binance/binance_compile.rs +++ b/rust/tw_any_coin/tests/chains/binance/binance_compile.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -55,15 +55,19 @@ fn test_binance_compile() { // Step 3: Compile transaction info // Simulate signature, normally obtained from signature server. - let signature = "1b1181faec30b60a2ddaa2804c253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a504f9e8310679" - .decode_hex() - .unwrap(); + let signature = "1b1181faec30b60a2ddaa2804c253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a504f9e8310679"; + let signature_bytes = signature.decode_hex().unwrap(); let public_key = "026a35920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e502" .decode_hex() .unwrap(); let mut compiler = CompilerHelper::::default(); - let output = compiler.compile(CoinType::Binance, &input, vec![signature], vec![public_key]); + let output = compiler.compile( + CoinType::Binance, + &input, + vec![signature_bytes], + vec![public_key], + ); assert_eq!(output.error, SigningError::OK); let expected_tx = concat!( @@ -74,4 +78,7 @@ fn test_binance_compile() { "04f9e8310679", ); assert_eq!(output.encoded.to_hex(), expected_tx); + assert_eq!(output.signature.to_hex(), signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"Amo1kgCI2Yw4iMpoxT38k/RWRgJgbLuH8P5e5TPbOOUC"},"signature":"GxGB+uwwtgot2qKATCU88mTGkYDsMYFJKbXeYgiMDFpF6KgW0SCPxTZruLBBeBpncSSFUNBAlMPXpQT56DEGeQ=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } diff --git a/rust/tw_any_coin/tests/chains/binance/binance_sign.rs b/rust/tw_any_coin/tests/chains/binance/binance_sign.rs index de227f4efc5..ecddd3e6f92 100644 --- a/rust/tw_any_coin/tests/chains/binance/binance_sign.rs +++ b/rust/tw_any_coin/tests/chains/binance/binance_sign.rs @@ -1,10 +1,13 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use crate::chains::binance::make_token; +use crate::chains::binance::{ + make_token, ACCOUNT_12_PRIVATE_KEY, ACCOUNT_15_PRIVATE_KEY, ACCOUNT_16_PRIVATE_KEY, + ACCOUNT_19_PRIVATE_KEY, +}; use tw_any_coin::test_utils::sign_utils::AnySignerHelper; use tw_coin_registry::coin_type::CoinType; use tw_encoding::hex::{DecodeHex, ToHex}; @@ -12,15 +15,6 @@ use tw_proto::Binance::Proto; use tw_proto::Binance::Proto::mod_SigningInput::OneOforder_oneof as OrderEnum; use tw_proto::Common::Proto::SigningError; -const ACCOUNT_12_PRIVATE_KEY: &str = - "90335b9d2153ad1a9799a3ccc070bd64b4164e9642ee1dd48053c33f9a3a05e9"; -const ACCOUNT_19_PRIVATE_KEY: &str = - "95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832"; -const ACCOUNT_15_PRIVATE_KEY: &str = - "eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d"; -const ACCOUNT_16_PRIVATE_KEY: &str = - "851fab89c14f4bbec0cc06f5e445ec065efc641068d78b308c67217d9bd5c88a"; - #[test] fn test_binance_sign_trade_order() { // bnb1hgm0p7khfk85zpz5v0j8wnej3a90w709vhkdfu @@ -57,6 +51,11 @@ fn test_binance_sign_trade_order() { output.encoded.to_hex(), "dc01f0625dee0a64ce6dc0430a14ba36f0fad74d8f41045463e4774f328f4af779e5122b424133364630464144373444384634313034353436334534373734463332384634414637373945352d33361a0b4e4e422d3333385f424e422002280130b09282413880c2d72f4001126e0a26eb5ae98721029729a52e4e3c2b4a4e52aa74033eedaf8ba1df5ab6d1f518fd69e67bbd309b0e12409123cb6906bb20aeb753f4a121d4d88ff0e9750ba75b0c4e10d76caee1e7d2481290fa3b9887a6225d6997f5f939ef834ea61d596a314237c48e560da9e17b5a180c20232001" ); + + let expected_signature = "9123cb6906bb20aeb753f4a121d4d88ff0e9750ba75b0c4e10d76caee1e7d2481290fa3b9887a6225d6997f5f939ef834ea61d596a314237c48e560da9e17b5a"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"ApcppS5OPCtKTlKqdAM+7a+Lod9attH1GP1p5nu9MJsO"},"signature":"kSPLaQa7IK63U/ShIdTYj/DpdQunWwxOENdsruHn0kgSkPo7mIemIl1pl/X5Oe+DTqYdWWoxQjfEjlYNqeF7Wg=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -90,6 +89,11 @@ fn test_binance_sign_cancel_trade_order() { output.encoded.to_hex(), "cc01f0625dee0a54166e681b0a14ba36f0fad74d8f41045463e4774f328f4af779e5120b4e4e422d3333385f424e421a2b424133364630464144373444384634313034353436334534373734463332384634414637373945352d3336126e0a26eb5ae98721029729a52e4e3c2b4a4e52aa74033eedaf8ba1df5ab6d1f518fd69e67bbd309b0e12403df6603426b991f7040bce22ce0137c12137df01e1d4d425cf3d9104103aec6335ac05c825e08ba26b9f72aa4cc45aa75cacfb6082df86b00692fef9701eb0f5180c20242001" ); + + let expected_signature = "3df6603426b991f7040bce22ce0137c12137df01e1d4d425cf3d9104103aec6335ac05c825e08ba26b9f72aa4cc45aa75cacfb6082df86b00692fef9701eb0f5"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"ApcppS5OPCtKTlKqdAM+7a+Lod9attH1GP1p5nu9MJsO"},"signature":"PfZgNCa5kfcEC84izgE3wSE33wHh1NQlzz2RBBA67GM1rAXIJeCLomufcqpMxFqnXKz7YILfhrAGkv75cB6w9Q=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -97,6 +101,7 @@ fn test_binance_sign_send_order() { let amount = 1_001_000_000; // bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2 let from_address_key_hash = "40c2979694bbc961023d1d27be6fc4d21a9febe6"; + // bnb13zeh6hs97d5eu2s5qerguhv8ewwue6u4ywa6yf let to_address_key_hash = "88b37d5e05f3699e2a1406468e5d87cb9dcceb95"; let send_order = Proto::SendOrder { @@ -143,6 +148,11 @@ fn test_binance_sign_send_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "c65a13440f18a155bd971ee40b9e0dd58586f5bf344e12ec4c76c439aebca8c7789bab7bfbfb4ce89aadc4a02df225b6b6efc861c13bbeb5f7a3eea2d7ffc80f"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"Amo1kgCI2Yw4iMpoxT38k/RWRgJgbLuH8P5e5TPbOOUC"},"signature":"xloTRA8YoVW9lx7kC54N1YWG9b80ThLsTHbEOa68qMd4m6t7+/tM6JqtxKAt8iW2tu/IYcE7vrX3o+6i1//IDw=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -178,6 +188,11 @@ fn test_binance_sign_token_freeze_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"4wIgadiXv1v0hG01T80sDoWAcFO+ZDyLjIWWMGAD9zQNQ6FiciZz64SCWLBDWx9JmT0OddSuQ9A0U6OuV/5pkQ=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -213,6 +228,11 @@ fn test_binance_sign_token_unfreeze_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"4wIgadiXv1v0hG01T80sDoWAcFO+ZDyLjIWWMGAD9zQNQ6FiciZz64SCWLBDWx9JmT0OddSuQ9A0U6OuV/5pkQ=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -250,6 +270,11 @@ fn test_binance_sign_token_issue_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "1fbb993d643f03b3e8e757a502035f58c4c45aaaa6e107a3059ab7c6164283c10f1254e87feee21477c64c87b1a27d8481048533ae2f685b3ac0dc66e4edbc0b"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"H7uZPWQ/A7Po51elAgNfWMTEWqqm4QejBZq3xhZCg8EPElTof+7iFHfGTIexon2EgQSFM64vaFs6wNxm5O28Cw=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -283,6 +308,11 @@ fn test_binance_sign_token_mint_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"4wIgadiXv1v0hG01T80sDoWAcFO+ZDyLjIWWMGAD9zQNQ6FiciZz64SCWLBDWx9JmT0OddSuQ9A0U6OuV/5pkQ=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -316,6 +346,11 @@ fn test_binance_sign_token_burn_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "e3022069d897bf5bf4846d354fcd2c0e85807053be643c8b8c8596306003f7340d43a162722673eb848258b0435b1f49993d0e75d4ae43d03453a3ae57fe6991"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"4wIgadiXv1v0hG01T80sDoWAcFO+ZDyLjIWWMGAD9zQNQ6FiciZz64SCWLBDWx9JmT0OddSuQ9A0U6OuV/5pkQ=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -358,6 +393,11 @@ fn test_binance_sign_htlt_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "51439de2da19fe9fd22137c903cfc5dc87553bf05dca0bb202c0e07c47f9b51269efa27243eb7b55888f5384a84ac1eac6d325c830d1be0ed042838e2dc0f6a9"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"UUOd4toZ/p/SITfJA8/F3IdVO/BdyguyAsDgfEf5tRJp76JyQ+t7VYiPU4SoSsHqxtMlyDDRvg7QQoOOLcD2qQ=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -392,6 +432,11 @@ fn test_binance_sign_deposit_htlt_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "0ca4144c6818e2836d09b4faf3161781d85f9adfc00badb2eaa0953174610a233b81413dafcf84716abad48a4ed3aeb9884d90eb8416eec5d5c0c6930ab60bd0"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A432lgCE4gstB9UOFCL5QQXGJB2fFIKk63nOi/1GDxnk"},"signature":"DKQUTGgY4oNtCbT68xYXgdhfmt/AC62y6qCVMXRhCiM7gUE9r8+EcWq61IpO0665iE2Q64QW7sXVwMaTCrYL0A=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -427,6 +472,11 @@ fn test_binance_sign_claim_htlt_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "fa30ba50111aa31d8329dacb6d044c1c7d54f1cb782bc9aa2a50c3fabce02a4579d75b76ca69a9fab11b676d9da66b5af7aa4c9ad3d18e24fffeb16433be39fb"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"+jC6UBEaox2DKdrLbQRMHH1U8ct4K8mqKlDD+rzgKkV511t2ymmp+rEbZ22dpmta96pMmtPRjiT//rFkM745+w=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -460,6 +510,11 @@ fn test_binance_sign_refund_htlt_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "c9f36142534d16ec8ce656f8eb7370b32206a2d15198b7165acf1e2a18952c9e4570b0f862e1ab7bb868c30781a42c9e3ec0ae08982e8d6c91c55b83c71b7b1e"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"yfNhQlNNFuyM5lb463NwsyIGotFRmLcWWs8eKhiVLJ5FcLD4YuGre7howweBpCyePsCuCJgujWyRxVuDxxt7Hg=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -495,6 +550,11 @@ fn test_binance_sign_transfer_out_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "7eda148e1167b1be1271a788ccf4e3eade1c7e1773e9d2093982d7f802f8f85f35ef550049011728206e4eda1a272f9e96fd95ef3983cad85a29cd14262c22e0"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"ftoUjhFnsb4ScaeIzPTj6t4cfhdz6dIJOYLX+AL4+F8171UASQEXKCBuTtoaJy+elv2V7zmDythaKc0UJiwi4A=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -531,6 +591,11 @@ fn test_binance_sign_side_chain_delegate_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "39302c9975fb2a09ac2b6b6fb1d3b9fb5b4c03630d3d7a7da42b1c6736d6127142a3fcdca0b70a3d065da8d4f4df8b5d9d8f46aeb3627a7d7aa901fe186af34c"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"OTAsmXX7KgmsK2tvsdO5+1tMA2MNPXp9pCscZzbWEnFCo/zcoLcKPQZdqNT034tdnY9GrrNien16qQH+GGrzTA=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -570,6 +635,11 @@ fn test_binance_sign_side_chain_redelegate_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "114c6927423e95ecc831ec763b629b3a40db8feeb330528a941fd74843c0d63b4271b23916770d4901988c1f56b20086e5768a12290ebec265e30a80f8f3d88e"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"EUxpJ0I+lezIMex2O2KbOkDbj+6zMFKKlB/XSEPA1jtCcbI5FncNSQGYjB9WsgCG5XaKEikOvsJl4wqA+PPYjg=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -606,6 +676,11 @@ fn test_binance_sign_side_chain_undelegate_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "a622b7ca7a2875e5eaa675a5ed976b2ec3b8ca055a2b05e7fb471d328bd04df854789437dd06407e41ebb1e5a345604c93663dfb660e223800636c0b94c2e798"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"piK3ynoodeXqpnWl7ZdrLsO4ygVaKwXn+0cdMovQTfhUeJQ33QZAfkHrseWjRWBMk2Y9+2YOIjgAY2wLlMLnmA=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -638,6 +713,11 @@ fn test_binance_sign_time_lock_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "c270822b9515ba486c6a6b3472d388a5aea872ed960c0b53de0fafdc8682ef473a126f01e7dd2c00f04a0138a601b9540f54b14026846de362f7ab7f9fed948b"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"wnCCK5UVukhsams0ctOIpa6ocu2WDAtT3g+v3IaC70c6Em8B590sAPBKATimAblUD1SxQCaEbeNi96t/n+2Uiw=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -673,6 +753,11 @@ fn test_binance_sign_time_relock_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "86ddaa077c8ae551d402fa409cf7e91663982b0542200967c03c0b5876b181353250f689d342f2217624a077b671ce7d09649187e29879f40abbbee9de7ab27c"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"ht2qB3yK5VHUAvpAnPfpFmOYKwVCIAlnwDwLWHaxgTUyUPaJ00LyIXYkoHe2cc59CWSRh+KYefQKu77p3nqyfA=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } #[test] @@ -704,4 +789,9 @@ fn test_binance_sign_time_unlock_order() { ); assert_eq!(output.error, SigningError::OK); assert_eq!(output.encoded.to_hex(), expected_encoded); + + let expected_signature = "da777bfd2032834f59ec9fe69fd6eaa4aca24242dfbc5ec4ef8c435cb9da7eb05ab78e1b8ca9f109657cb77996898f1b59137b3d8f1e00f842e409e18033b347"; + assert_eq!(output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6mlXAQMjrgSDz0bMhkyUIQcCK9E6lYarJk9vg9rao/H"},"signature":"2nd7/SAyg09Z7J/mn9bqpKyiQkLfvF7E74xDXLnafrBat44bjKnxCWV8t3mWiY8bWRN7PY8eAPhC5AnhgDOzRw=="}"#; + assert_eq!(output.signature_json, expected_signature_json); } diff --git a/rust/tw_any_coin/tests/chains/binance/binance_wallet_connect.rs b/rust/tw_any_coin/tests/chains/binance/binance_wallet_connect.rs new file mode 100644 index 00000000000..60430cd5a99 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/binance/binance_wallet_connect.rs @@ -0,0 +1,71 @@ +// Copyright © 2017-2024 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::chains::binance::{make_token, ACCOUNT_19_PRIVATE_KEY}; +use std::borrow::Cow; +use tw_any_coin::test_utils::sign_utils::AnySignerHelper; +use tw_any_coin::test_utils::wallet_connect_utils::WalletConnectRequestHelper; +use tw_coin_registry::coin_type::CoinType; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_proto::Binance::Proto::{self, mod_SigningInput::OneOforder_oneof as MessageEnum}; +use tw_proto::Common::Proto::SigningError; +use tw_proto::WalletConnect::Proto as WCProto; + +const WC_SIGN_REQUEST_CASE_1: &str = include_str!("data/wc_sign_request_case_1.json"); + +#[test] +fn test_binance_sign_wallet_connect_case_1() { + let input = WCProto::ParseRequestInput { + protocol: WCProto::Protocol::V2, + method: WCProto::Method::CosmosSignAmino, + payload: WC_SIGN_REQUEST_CASE_1.to_string().into(), + }; + + let mut parser = WalletConnectRequestHelper::default(); + let parsing_output = parser.parse(CoinType::Binance, &input); + + let mut signing_input = match parsing_output.signing_input_oneof { + WCProto::mod_ParseRequestOutput::OneOfsigning_input_oneof::binance(input) => input, + _ => unreachable!(), + }; + + // bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2 + let expected_from_addr_key_hash = "40c2979694bbc961023d1d27be6fc4d21a9febe6"; + // bnb13zeh6hs97d5eu2s5qerguhv8ewwue6u4ywa6yf + let expected_to_addr_key_hash = "88b37d5e05f3699e2a1406468e5d87cb9dcceb95"; + let expected_send_order = Proto::SendOrder { + inputs: vec![Proto::mod_SendOrder::Input { + address: expected_from_addr_key_hash.decode_hex().unwrap().into(), + coins: vec![make_token("BNB", 1_001_000_000)], + }], + outputs: vec![Proto::mod_SendOrder::Output { + address: expected_to_addr_key_hash.decode_hex().unwrap().into(), + coins: vec![make_token("BNB", 1_001_000_000)], + }], + }; + let expected_signing_input = Proto::SigningInput { + chain_id: "chain-bnb".into(), + account_number: 19, + sequence: 23, + source: 1, + memo: "".into(), + private_key: Cow::default(), + order_oneof: MessageEnum::send_order(expected_send_order), + }; + assert_eq!(signing_input, expected_signing_input); + + // Set missing private key. + signing_input.private_key = ACCOUNT_19_PRIVATE_KEY.decode_hex().unwrap().into(); + + let mut signer = AnySignerHelper::::default(); + let signing_output = signer.sign(CoinType::Binance, signing_input); + + assert_eq!(signing_output.error, SigningError::OK); + let expected_signature = "3c24c784c6bbf99d54ffabb153edcb6d3c4a774936df5c72a5d32897256f8e062f320fb4753302fb0a96f08c475974d20edfd1a27bbeeda73587f58ddc958975"; + assert_eq!(signing_output.signature.to_hex(), expected_signature); + let expected_signature_json = r#"{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"Amo1kgCI2Yw4iMpoxT38k/RWRgJgbLuH8P5e5TPbOOUC"},"signature":"PCTHhMa7+Z1U/6uxU+3LbTxKd0k231xypdMolyVvjgYvMg+0dTMC+wqW8IxHWXTSDt/Ronu+7ac1h/WN3JWJdQ=="}"#; + assert_eq!(signing_output.signature_json, expected_signature_json); +} diff --git a/rust/tw_any_coin/tests/chains/binance/data/wc_sign_request_case_1.json b/rust/tw_any_coin/tests/chains/binance/data/wc_sign_request_case_1.json new file mode 100644 index 00000000000..3e070de9aef --- /dev/null +++ b/rust/tw_any_coin/tests/chains/binance/data/wc_sign_request_case_1.json @@ -0,0 +1,37 @@ +{ + "signerAddress": "bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2", + "signDoc": { + "account_number": "19", + "chain_id": "chain-bnb", + "memo": "", + "data": null, + "msgs": [ + { + "inputs": [ + { + "address": "bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2", + "coins": [ + { + "amount": 1001000000, + "denom": "BNB" + } + ] + } + ], + "outputs": [ + { + "address": "bnb13zeh6hs97d5eu2s5qerguhv8ewwue6u4ywa6yf", + "coins": [ + { + "amount": 1001000000, + "denom": "BNB" + } + ] + } + ] + } + ], + "sequence": "23", + "source": "1" + } +} \ No newline at end of file diff --git a/rust/tw_any_coin/tests/chains/binance/mod.rs b/rust/tw_any_coin/tests/chains/binance/mod.rs index 5dd8c0fa9fb..a38fee8ce8a 100644 --- a/rust/tw_any_coin/tests/chains/binance/mod.rs +++ b/rust/tw_any_coin/tests/chains/binance/mod.rs @@ -1,4 +1,4 @@ -// Copyright © 2017-2023 Trust Wallet. +// Copyright © 2017-2024 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,6 +9,16 @@ use tw_proto::Binance::Proto; mod binance_address; mod binance_compile; mod binance_sign; +mod binance_wallet_connect; + +const ACCOUNT_12_PRIVATE_KEY: &str = + "90335b9d2153ad1a9799a3ccc070bd64b4164e9642ee1dd48053c33f9a3a05e9"; +const ACCOUNT_19_PRIVATE_KEY: &str = + "95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832"; +const ACCOUNT_15_PRIVATE_KEY: &str = + "eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d"; +const ACCOUNT_16_PRIVATE_KEY: &str = + "851fab89c14f4bbec0cc06f5e445ec065efc641068d78b308c67217d9bd5c88a"; fn make_token(denom: &str, amount: i64) -> Proto::mod_SendOrder::Token { Proto::mod_SendOrder::Token { diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs index e2a16cd0ac8..bed1d3c8d47 100644 --- a/rust/tw_aptos/src/entry.rs +++ b/rust/tw_aptos/src/entry.rs @@ -15,6 +15,7 @@ use tw_coin_entry::error::{AddressError, AddressResult}; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_coin_entry::prefix::NoPrefix; use tw_keypair::tw::PublicKey; use tw_proto::Aptos::Proto; @@ -33,6 +34,7 @@ impl CoinEntry for AptosEntry { type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; + type WalletConnector = NoWalletConnector; #[inline] fn parse_address( diff --git a/rust/tw_bech32_address/src/lib.rs b/rust/tw_bech32_address/src/lib.rs index d8493362219..338e26f6b16 100644 --- a/rust/tw_bech32_address/src/lib.rs +++ b/rust/tw_bech32_address/src/lib.rs @@ -5,7 +5,7 @@ // file LICENSE at the root of the source code distribution tree. use crate::bech32_prefix::Bech32Prefix; -use serde::{Serialize, Serializer}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; @@ -19,6 +19,7 @@ use tw_memory::Data; pub mod bech32_prefix; +#[derive(PartialEq)] pub struct Bech32Address { hrp: String, key_hash: Data, @@ -178,6 +179,18 @@ impl fmt::Debug for Bech32Address { } } +impl<'de> Deserialize<'de> for Bech32Address { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de::Error as DeError; + + let address_str = String::deserialize(deserializer)?; + Bech32Address::from_str(&address_str).map_err(|e| DeError::custom(format!("{e:?}"))) + } +} + impl Serialize for Bech32Address { fn serialize(&self, serializer: S) -> Result where diff --git a/rust/tw_bitcoin/src/entry.rs b/rust/tw_bitcoin/src/entry.rs index a847c4a0690..0ba49752e50 100644 --- a/rust/tw_bitcoin/src/entry.rs +++ b/rust/tw_bitcoin/src/entry.rs @@ -11,6 +11,7 @@ use tw_coin_entry::derivation::Derivation; use tw_coin_entry::error::{AddressError, AddressResult}; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; +use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_coin_entry::prefix::NoPrefix; use tw_coin_entry::signing_output_error; use tw_keypair::tw::PublicKey; @@ -45,6 +46,7 @@ impl CoinEntry for BitcoinEntry { type JsonSigner = NoJsonSigner; type PlanBuilder = BitcoinPlanBuilder; type MessageSigner = NoMessageSigner; + type WalletConnector = NoWalletConnector; #[inline] fn parse_address( diff --git a/rust/tw_coin_entry/src/coin_entry.rs b/rust/tw_coin_entry/src/coin_entry.rs index 1ec5e590f48..83ff059e43d 100644 --- a/rust/tw_coin_entry/src/coin_entry.rs +++ b/rust/tw_coin_entry/src/coin_entry.rs @@ -16,6 +16,7 @@ use tw_memory::Data; use tw_proto::{MessageRead, MessageWrite}; use crate::modules::message_signer::MessageSigner; +use crate::modules::wallet_connector::WalletConnector; pub use tw_proto::{ProtoError, ProtoResult}; pub type PrivateKeyBytes = Data; @@ -60,6 +61,10 @@ pub trait CoinEntry { /// /// **Optional**. Use `NoMessageSigner` if the blockchain does not support message signing. type MessageSigner: MessageSigner; + /// WalletConnect Connector - the module allows to parse transactions received in WalletConnect format. + /// + /// **Optional**. Use `NoWalletConnector` if the blockchain does not support WalletConnect transactions. + type WalletConnector: WalletConnector; /// Tries to parse `Self::Address` from the given `address` string by `coin` type and address `prefix`. fn parse_address( @@ -125,4 +130,11 @@ pub trait CoinEntry { fn message_signer(&self) -> Option { None } + + /// It is optional, Parsing signing requests received through WalletConnect. + /// Returns `Ok(None)` if the blockchain does not support WalletConnect transactions. + #[inline] + fn wallet_connector(&self) -> Option { + None + } } diff --git a/rust/tw_coin_entry/src/coin_entry_ext.rs b/rust/tw_coin_entry/src/coin_entry_ext.rs index d3daf007c4a..cc0b39549d3 100644 --- a/rust/tw_coin_entry/src/coin_entry_ext.rs +++ b/rust/tw_coin_entry/src/coin_entry_ext.rs @@ -12,9 +12,11 @@ use crate::error::{AddressResult, SigningError, SigningErrorType}; use crate::modules::json_signer::JsonSigner; use crate::modules::message_signer::MessageSigner; use crate::modules::plan_builder::PlanBuilder; +use crate::modules::wallet_connector::WalletConnector; use crate::prefix::AddressPrefix; use tw_keypair::tw::{PrivateKey, PublicKey}; use tw_memory::Data; +use tw_proto::WalletConnect::Proto as WCProto; use tw_proto::{deserialize, serialize, ProtoResult}; pub type PrivateKeyBytes = Data; @@ -84,6 +86,13 @@ pub trait CoinEntryExt { /// Verifies a signature for a message. fn verify_message(&self, coin: &dyn CoinContext, input: &[u8]) -> SigningResult; + + /// Signs a transaction in WalletConnect format. + fn wallet_connect_parse_request( + &self, + coin: &dyn CoinContext, + input: &[u8], + ) -> SigningResult; } impl CoinEntryExt for T @@ -209,4 +218,18 @@ where deserialize(input)?; Ok(message_signer.verify_message(coin, input)) } + + fn wallet_connect_parse_request( + &self, + coin: &dyn CoinContext, + input: &[u8], + ) -> SigningResult { + let Some(wc_connector) = self.wallet_connector() else { + return Err(SigningError(SigningErrorType::Error_not_supported)); + }; + + let input: WCProto::ParseRequestInput = deserialize(input)?; + let output = wc_connector.parse_request(coin, input); + serialize(&output).map_err(SigningError::from) + } } diff --git a/rust/tw_coin_entry/src/modules/mod.rs b/rust/tw_coin_entry/src/modules/mod.rs index 6f8b8b891bf..f49968d2816 100644 --- a/rust/tw_coin_entry/src/modules/mod.rs +++ b/rust/tw_coin_entry/src/modules/mod.rs @@ -9,3 +9,4 @@ pub mod json_signer; pub mod message_signer; pub mod plan_builder; +pub mod wallet_connector; diff --git a/rust/tw_coin_entry/src/modules/wallet_connector.rs b/rust/tw_coin_entry/src/modules/wallet_connector.rs new file mode 100644 index 00000000000..25db0008587 --- /dev/null +++ b/rust/tw_coin_entry/src/modules/wallet_connector.rs @@ -0,0 +1,30 @@ +// Copyright © 2017-2023 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +use crate::coin_context::CoinContext; +use tw_proto::WalletConnect::Proto as WCProto; + +pub trait WalletConnector { + /// Parses a signing request received through Wallet Connect. + fn parse_request( + &self, + coin: &dyn CoinContext, + request: WCProto::ParseRequestInput<'_>, + ) -> WCProto::ParseRequestOutput<'static>; +} + +/// `NoWalletConnector` can't be created since there are no enum variants. +pub enum NoWalletConnector {} + +impl WalletConnector for NoWalletConnector { + fn parse_request( + &self, + _coin: &dyn CoinContext, + _request: WCProto::ParseRequestInput<'_>, + ) -> WCProto::ParseRequestOutput<'static> { + panic!("`NoWalletConnector` should never be constructed and used") + } +} diff --git a/rust/tw_cosmos_sdk/Cargo.toml b/rust/tw_cosmos_sdk/Cargo.toml index 3e209b4f6df..ffd6abf455b 100644 --- a/rust/tw_cosmos_sdk/Cargo.toml +++ b/rust/tw_cosmos_sdk/Cargo.toml @@ -17,7 +17,7 @@ tw_hash = { path = "../tw_hash" } tw_keypair = { path = "../tw_keypair" } tw_memory = { path = "../tw_memory" } tw_misc = { path = "../tw_misc" } -tw_number = { path = "../tw_number", features = ["serde"] } +tw_number = { path = "../tw_number" } tw_proto = { path = "../tw_proto" } [dev-dependencies] diff --git a/rust/tw_cosmos_sdk/src/public_key/secp256k1.rs b/rust/tw_cosmos_sdk/src/public_key/secp256k1.rs index 3558aba8626..230582a8472 100644 --- a/rust/tw_cosmos_sdk/src/public_key/secp256k1.rs +++ b/rust/tw_cosmos_sdk/src/public_key/secp256k1.rs @@ -14,10 +14,21 @@ use tw_memory::Data; use tw_misc::traits::ToBytesVec; use tw_proto::{google, to_any}; +#[derive(Clone)] pub struct Secp256PublicKey { public_key: Data, } +impl Secp256PublicKey { + pub fn from_secp256k1_public_key( + coin: &dyn CoinContext, + public_key: &secp256k1::PublicKey, + ) -> KeyPairResult { + let public_key = prepare_secp256k1_public_key(coin, public_key.compressed().as_slice())?; + Ok(Secp256PublicKey { public_key }) + } +} + impl CosmosPublicKey for Secp256PublicKey { fn from_private_key(coin: &dyn CoinContext, private_key: &tw::PrivateKey) -> KeyPairResult where diff --git a/rust/tw_ethereum/src/entry.rs b/rust/tw_ethereum/src/entry.rs index f69893fd63f..807e6ba3d58 100644 --- a/rust/tw_ethereum/src/entry.rs +++ b/rust/tw_ethereum/src/entry.rs @@ -10,6 +10,7 @@ use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; use tw_coin_entry::error::{AddressError, AddressResult}; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_coin_entry::prefix::NoPrefix; use tw_evm::address::Address; use tw_evm::evm_context::StandardEvmContext; @@ -35,6 +36,7 @@ impl CoinEntry for EthereumEntry { type JsonSigner = EthJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = EthMessageSigner; + type WalletConnector = NoWalletConnector; #[inline] fn parse_address( diff --git a/rust/tw_internet_computer/src/entry.rs b/rust/tw_internet_computer/src/entry.rs index 6fb47ba161c..99a76913646 100644 --- a/rust/tw_internet_computer/src/entry.rs +++ b/rust/tw_internet_computer/src/entry.rs @@ -12,6 +12,7 @@ use tw_coin_entry::{ error::{AddressError, AddressResult, SigningError}, modules::{ json_signer::NoJsonSigner, message_signer::NoMessageSigner, plan_builder::NoPlanBuilder, + wallet_connector::NoWalletConnector, }, prefix::NoPrefix, signing_output_error, @@ -43,6 +44,8 @@ impl CoinEntry for InternetComputerEntry { type MessageSigner = NoMessageSigner; + type WalletConnector = NoWalletConnector; + #[inline] fn parse_address( &self, diff --git a/rust/tw_misc/src/serde.rs b/rust/tw_misc/src/serde.rs index 0e1e85d5256..869da0e1dc1 100644 --- a/rust/tw_misc/src/serde.rs +++ b/rust/tw_misc/src/serde.rs @@ -4,13 +4,38 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -use serde::{Serialize, Serializer}; +use serde::{Deserialize, Serialize}; -/// Serializes the `value` as a string. -pub fn as_string(value: &T, serializer: S) -> Result -where - T: ToString, - S: Serializer, -{ - value.to_string().serialize(serializer) +pub mod as_string { + use serde::de::Error as DeError; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use std::fmt; + use std::str::FromStr; + + /// Serializes the `value` as a string. + pub fn serialize(value: &T, serializer: S) -> Result + where + T: ToString, + S: Serializer, + { + value.to_string().serialize(serializer) + } + + /// Deserialize a value from a string. + pub fn deserialize<'de, T, D>(deserializer: D) -> Result + where + T: FromStr, + T::Err: fmt::Display, + D: Deserializer<'de>, + { + let str = String::deserialize(deserializer)?; + T::from_str(&str).map_err(DeError::custom) + } +} + +#[derive(Deserialize, Serialize)] +pub struct Typed { + #[serde(rename = "type")] + pub ty: String, + pub value: Value, } diff --git a/rust/tw_ronin/src/entry.rs b/rust/tw_ronin/src/entry.rs index 36bb8fb96b2..79020884ff9 100644 --- a/rust/tw_ronin/src/entry.rs +++ b/rust/tw_ronin/src/entry.rs @@ -12,6 +12,7 @@ use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; use tw_coin_entry::error::{AddressError, AddressResult}; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_coin_entry::prefix::NoPrefix; use tw_evm::evm_entry::EvmEntry; use tw_evm::modules::compiler::Compiler; @@ -35,6 +36,7 @@ impl CoinEntry for RoninEntry { type JsonSigner = EthJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = EthMessageSigner; + type WalletConnector = NoWalletConnector; #[inline] fn parse_address( diff --git a/src/interface/TWWalletConnectRequest.cpp b/src/interface/TWWalletConnectRequest.cpp new file mode 100644 index 00000000000..621f3c9cbcd --- /dev/null +++ b/src/interface/TWWalletConnectRequest.cpp @@ -0,0 +1,20 @@ +// Copyright © 2017-2024 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include + +#include "rust/Wrapper.h" + +using namespace TW; + +TWData* _Nonnull TWWalletConnectRequestParse(enum TWCoinType coin, TWData* _Nonnull input) { + const Data& inputData = *reinterpret_cast(input); + Rust::TWDataWrapper twInputData = inputData; + + Rust::TWDataWrapper twOutputData = Rust::tw_wallet_connect_request_parse(static_cast(coin), twInputData.get()); + auto outputData = twOutputData.toDataOrDefault(); + return TWDataCreateWithBytes(outputData.data(), outputData.size()); +} diff --git a/src/proto/Binance.proto b/src/proto/Binance.proto index 00df9f21fd2..01aa30a3698 100644 --- a/src/proto/Binance.proto +++ b/src/proto/Binance.proto @@ -382,4 +382,10 @@ message SigningOutput { // error description in case of error string error_message = 3; + + // Signature bytes. + bytes signature = 4; + + // Signature JSON string. + string signature_json = 5; } diff --git a/src/proto/WalletConnect.proto b/src/proto/WalletConnect.proto new file mode 100644 index 00000000000..37b8a9796c0 --- /dev/null +++ b/src/proto/WalletConnect.proto @@ -0,0 +1,44 @@ +syntax = "proto3"; + +package TW.WalletConnect.Proto; +option java_package = "wallet.core.jni.proto"; + +import "Binance.proto"; +import "Common.proto"; + +// The transaction protocol may differ from version to version. +enum Protocol { + V2 = 0; +} + +// WalletConnect request method. +enum Method { + Unknown = 0; + // cosmos_signAmino + CosmosSignAmino = 1; +} + +message ParseRequestInput { + // A protocol version. + Protocol protocol = 1; + + // A signing method like "cosmos_signAmino" or "eth_signTransaction". + Method method = 2; + + // Transaction payload to sign. + // Basically, a JSON object. + string payload = 3; +} + +message ParseRequestOutput { + // OK (=0) or other codes in case of error + Common.Proto.SigningError error = 1; + + // error description in case of error + string error_message = 2; + + // Prepared unsigned transaction input, on the source chain. Some fields must be completed, and it has to be signed. + oneof signing_input_oneof { + Binance.Proto.SigningInput binance = 3; + } +} diff --git a/swift/Tests/Blockchains/BinanceChainTests.swift b/swift/Tests/Blockchains/BinanceChainTests.swift index 90df7dd3573..78ad5dd1bc6 100644 --- a/swift/Tests/Blockchains/BinanceChainTests.swift +++ b/swift/Tests/Blockchains/BinanceChainTests.swift @@ -238,4 +238,35 @@ class BinanceChainTests: XCTestCase { } queue.waitUntilAllOperationsAreFinished() } + + func testSignFromWalletConnectRequest() throws { + // Step 1: Parse a signing request received through WalletConnect. + + let requestPayload = """ + {"signerAddress":"bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2","signDoc":{"account_number":"19","chain_id":"chain-bnb","memo":"","data":null,"msgs":[{"inputs":[{"address":"bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2","coins":[{"amount":1001000000,"denom":"BNB"}]}],"outputs":[{"address":"bnb13zeh6hs97d5eu2s5qerguhv8ewwue6u4ywa6yf","coins":[{"amount":1001000000,"denom":"BNB"}]}]}],"sequence":"23","source":"1"}} + """ + let parsingInput = WalletConnectParseRequestInput.with { + $0.method = .cosmosSignAmino + $0.payload = requestPayload + } + let parsingInputBytes = try parsingInput.serializedData() + + let parsingOutputBytes = WalletConnectRequest.parse(coin: .binance, input: parsingInputBytes) + let parsingOutput = try WalletConnectParseRequestOutput(serializedData: parsingOutputBytes) + + var signingInput = parsingOutput.binance + + // Step 2: Set missing fields. + + signingInput.privateKey = Data(hexString: "95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832")! + + // Step 3: Sign the transaction. + + let output: BinanceSigningOutput = AnySigner.sign(input: signingInput, coin: .binance) + + let expected = """ + {"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"Amo1kgCI2Yw4iMpoxT38k/RWRgJgbLuH8P5e5TPbOOUC"},"signature":"PCTHhMa7+Z1U/6uxU+3LbTxKd0k231xypdMolyVvjgYvMg+0dTMC+wqW8IxHWXTSDt/Ronu+7ac1h/WN3JWJdQ=="} + """ + XCTAssertEqual(output.signatureJson, expected) + } } diff --git a/tests/chains/Binance/TWWalletConnectSigning.cpp b/tests/chains/Binance/TWWalletConnectSigning.cpp new file mode 100644 index 00000000000..8a463f99470 --- /dev/null +++ b/tests/chains/Binance/TWWalletConnectSigning.cpp @@ -0,0 +1,54 @@ +// Copyright © 2017-2024 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "proto/Binance.pb.h" +#include "proto/WalletConnect.pb.h" +#include "Coin.h" +#include +#include + +#include "TestUtilities.h" +#include + +namespace TW::Binance { + +TEST(TWWalletConnectSign, SendOrder) { + auto private_key = parse_hex("95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832"); + const auto payload = R"({"signerAddress":"bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2","signDoc":{"account_number":"19","chain_id":"chain-bnb","memo":"","data":null,"msgs":[{"inputs":[{"address":"bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2","coins":[{"amount":1001000000,"denom":"BNB"}]}],"outputs":[{"address":"bnb13zeh6hs97d5eu2s5qerguhv8ewwue6u4ywa6yf","coins":[{"amount":1001000000,"denom":"BNB"}]}]}],"sequence":"23","source":"1"}})"; + + WalletConnect::Proto::ParseRequestInput parsingInput; + parsingInput.set_method(WalletConnect::Proto::Method::CosmosSignAmino); + parsingInput.set_payload(payload); + + const auto parsinginputData = parsingInput.SerializeAsString(); + const auto parsingInputDataPtr = WRAPD(TWDataCreateWithBytes(reinterpret_cast(parsinginputData.c_str()), parsinginputData.size())); + + const auto outputDataPtr = WRAPD(TWWalletConnectRequestParse(TWCoinTypeBinance, parsingInputDataPtr.get())); + + WalletConnect::Proto::ParseRequestOutput parsingOutput; + parsingOutput.ParseFromArray( + TWDataBytes(outputDataPtr.get()), + static_cast(TWDataSize(outputDataPtr.get())) + ); + + EXPECT_EQ(parsingOutput.error(), Common::Proto::SigningError::OK); + + // Step 2: Set missing fields. + ASSERT_TRUE(parsingOutput.has_binance()); + Proto::SigningInput signingInput = parsingOutput.binance(); + + signingInput.set_private_key(private_key.data(), private_key.size()); + + Proto::SigningOutput output; + ANY_SIGN(signingInput, TWCoinTypeBinance); + + EXPECT_EQ(output.error(), Common::Proto::SigningError::OK); + EXPECT_EQ(hex(output.signature()), "3c24c784c6bbf99d54ffabb153edcb6d3c4a774936df5c72a5d32897256f8e062f320fb4753302fb0a96f08c475974d20edfd1a27bbeeda73587f58ddc958975"); + EXPECT_EQ(output.signature_json(), R"({"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"Amo1kgCI2Yw4iMpoxT38k/RWRgJgbLuH8P5e5TPbOOUC"},"signature":"PCTHhMa7+Z1U/6uxU+3LbTxKd0k231xypdMolyVvjgYvMg+0dTMC+wqW8IxHWXTSDt/Ronu+7ac1h/WN3JWJdQ=="})"); +} + +} // namespace TW::Binance diff --git a/tests/chains/Binance/TransactionCompilerTests.cpp b/tests/chains/Binance/TransactionCompilerTests.cpp index 5b5a4e7ef19..efcb375ac30 100644 --- a/tests/chains/Binance/TransactionCompilerTests.cpp +++ b/tests/chains/Binance/TransactionCompilerTests.cpp @@ -82,7 +82,6 @@ TEST(BinanceCompiler, CompileWithSignatures) { "253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a5" "04f9e8310679"; { - EXPECT_EQ(outputData.size(), 189ul); Binance::Proto::SigningOutput output; ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); diff --git a/tests/interface/TWTransactionCompilerTests.cpp b/tests/interface/TWTransactionCompilerTests.cpp index f2be40920e9..9746a4e9b8f 100644 --- a/tests/interface/TWTransactionCompilerTests.cpp +++ b/tests/interface/TWTransactionCompilerTests.cpp @@ -111,7 +111,6 @@ TEST(TWTransactionCompiler, ExternalSignatureSignBinance) { "253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a5" "04f9e8310679"; { - EXPECT_EQ(TWDataSize(outputData.get()), 189ul); Binance::Proto::SigningOutput output; ASSERT_TRUE(output.ParseFromArray(TWDataBytes(outputData.get()), (int)TWDataSize(outputData.get()))); From 408ca6aedbdeea04d79b2d317302189bc0b242c0 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Fri, 12 Jan 2024 17:23:30 +0700 Subject: [PATCH 038/128] [Explorer]: Update blockchain explorers for Bitcoin and Nano (#3656) * [Nano]: Update block explorer for Nano * [Bitcoin]: Update block explorer * [Nano]: Fix `txPath` and `accountPath` * [KMP]: Fix KMP CI * [NEAR]: Update blockchain explorer * [Misc]: Add a Python script to validate `registry.json` * Fix sampleTx and Explorer URL for `BandChain`, `Oasis`, `OKXChain`, `Stellar` --- registry.json | 30 ++++++------ samples/kmp/shared/build.gradle.kts | 2 +- tests/chains/Bitcoin/TWCoinTypeTests.cpp | 4 +- .../Cosmos/BandChain/TWCoinTypeTests.cpp | 8 ++-- tests/chains/NEAR/TWAnySignerTests.cpp | 6 +-- tests/chains/NEAR/TWCoinTypeTests.cpp | 4 +- tests/chains/Nano/TWCoinTypeTests.cpp | 4 +- tests/chains/OKXChain/TWCoinTypeTests.cpp | 4 +- tests/chains/Oasis/TWCoinTypeTests.cpp | 4 +- tests/chains/Stellar/TWCoinTypeTests.cpp | 4 +- tools/registry | 46 +++++++++++++++++++ 11 files changed, 81 insertions(+), 35 deletions(-) create mode 100755 tools/registry diff --git a/registry.json b/registry.json index 87f01e776f9..1e33dfc115b 100644 --- a/registry.json +++ b/registry.json @@ -34,9 +34,9 @@ "publicKeyHasher": "sha256ripemd", "base58Hasher": "sha256d", "explorer": { - "url": "https://blockchair.com", - "txPath": "/bitcoin/transaction/", - "accountPath": "/bitcoin/address/", + "url": "https://mempool.space", + "txPath": "/tx/", + "accountPath": "/address/", "sampleTx": "0607f62530b68cfcc91c57a1702841dd399a899d0eecda8e31ecca3f52f01df2", "sampleAccount": "17A16QmavnUfCW11DAApiJxp7ARnxN5pGX" }, @@ -1498,7 +1498,7 @@ "url": "https://blockchair.com/stellar", "txPath": "/transaction/", "accountPath": "/account/", - "sampleTx": "d9aeabfa9d24df8c5755125f8af243b74cd3ff878656cfa72c566a8824bf6e84", + "sampleTx": "8a7ff7261e8b3f31af7f6ed257c2e9fe7c47afcd9b1ce1be1bfc1bc5f6a3ad9e", "sampleAccount": "GCILJZQ3CKBKBUJWW4TAM6Q37LJA5MQX6GMSFSQN75BPLWIZ33OPRG52" }, "info": { @@ -1559,9 +1559,9 @@ "publicKeyType": "ed25519Blake2b", "url": "https://nano.org", "explorer": { - "url": "https://nanocrawler.cc", - "txPath": "/explorer/block/", - "accountPath": "/explorer/account/", + "url": "https://www.nanolooker.com", + "txPath": "/block/", + "accountPath": "/account/", "sampleTx": "C264DB7BF40738F0CEFF19B606746CB925B713E4B8699A055699E0DC8ABBC70F", "sampleAccount": "nano_1wpj616kwhe1y38y1mspd8aub8i334cwybqco511iyuxm55zx8d67ptf1tsf" }, @@ -2034,9 +2034,9 @@ "curve": "ed25519", "publicKeyType": "ed25519", "explorer": { - "url": "https://explorer.near.org", - "txPath": "/transactions/", - "accountPath": "/accounts/", + "url": "https://nearblocks.io", + "txPath": "/txns/", + "accountPath": "/address/", "sampleTx": "FPQAMaVnvFHNwNBJWnTttXfdJhp5FvMGGDJEesB8gvbL", "sampleAccount": "test-trust.vlad.near" }, @@ -2297,11 +2297,11 @@ "hrp": "band", "addressHasher": "sha256ripemd", "explorer": { - "url": "https://scan-wenchang-testnet2.bandchain.org/", + "url": "https://www.mintscan.io/band", "txPath": "/tx/", "accountPath": "/account/", - "sampleTx": "473264551D3063A9EC64EC251C61BE92DDDFCF6CC46D026D1E574D83D5447173", - "sampleAccount": "band12nmsm9khdsv0tywge43q3zwj8kkj3hvup9rltp" + "sampleTx": "74AF38C2183B06EB6274DA4AAC0D2334E6E283643D436852F5E088AEA2CD0B17", + "sampleAccount": "band16gpgu994g2gdrzvwp9047le3pcq9wz6mcgtd4w" }, "info": { "url": "https://bandprotocol.com/", @@ -2713,7 +2713,7 @@ "url": "https://oasisscan.com", "txPath": "/transactions/", "accountPath": "/accounts/detail/", - "sampleTx": "0b9bd4983f1c88a1c71bf33562b6ba02b3064e01697d15a0de4bfe1922ec74b8", + "sampleTx": "73dc977fdd8596d4a57e6feb891b21f5da3652d26815dc94f15f7420c298e29e", "sampleAccount": "oasis1qrx376dmwuckmruzn9vq64n49clw72lywctvxdf4" }, "info": { @@ -4130,7 +4130,7 @@ "publicKeyType": "secp256k1Extended", "addressHasher": "keccak256", "explorer": { - "url": "https://www.oklink.com/en/okc", + "url": "https://www.oklink.com/oktc", "txPath": "/tx/", "accountPath": "/address/", "sampleTx": "0x46C3A947E8248570FBD28E4FE456CC8F80DFD90716533878FB67857B95FA3D37", diff --git a/samples/kmp/shared/build.gradle.kts b/samples/kmp/shared/build.gradle.kts index 34b9cbe9377..8de33b05a29 100644 --- a/samples/kmp/shared/build.gradle.kts +++ b/samples/kmp/shared/build.gradle.kts @@ -35,7 +35,7 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation("com.trustwallet:wallet-core-kotlin:4.0.14") + implementation("com.trustwallet:wallet-core-kotlin:4.0.16") } } val commonTest by getting { diff --git a/tests/chains/Bitcoin/TWCoinTypeTests.cpp b/tests/chains/Bitcoin/TWCoinTypeTests.cpp index 88d4a71ec1b..b11ad81225f 100644 --- a/tests/chains/Bitcoin/TWCoinTypeTests.cpp +++ b/tests/chains/Bitcoin/TWCoinTypeTests.cpp @@ -27,8 +27,8 @@ TEST(TWBitcoinCoinType, TWCoinType) { ASSERT_EQ(0x5, TWCoinTypeP2shPrefix(TWCoinTypeBitcoin)); ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeBitcoin)); assertStringsEqual(symbol, "BTC"); - assertStringsEqual(txUrl, "https://blockchair.com/bitcoin/transaction/0607f62530b68cfcc91c57a1702841dd399a899d0eecda8e31ecca3f52f01df2"); - assertStringsEqual(accUrl, "https://blockchair.com/bitcoin/address/17A16QmavnUfCW11DAApiJxp7ARnxN5pGX"); + assertStringsEqual(txUrl, "https://mempool.space/tx/0607f62530b68cfcc91c57a1702841dd399a899d0eecda8e31ecca3f52f01df2"); + assertStringsEqual(accUrl, "https://mempool.space/address/17A16QmavnUfCW11DAApiJxp7ARnxN5pGX"); assertStringsEqual(id, "bitcoin"); assertStringsEqual(name, "Bitcoin"); } diff --git a/tests/chains/Cosmos/BandChain/TWCoinTypeTests.cpp b/tests/chains/Cosmos/BandChain/TWCoinTypeTests.cpp index e124952c713..91b3138682b 100644 --- a/tests/chains/Cosmos/BandChain/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/BandChain/TWCoinTypeTests.cpp @@ -15,9 +15,9 @@ TEST(TWBandChainCoinType, TWCoinType) { auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeBandChain)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("473264551D3063A9EC64EC251C61BE92DDDFCF6CC46D026D1E574D83D5447173")); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("74AF38C2183B06EB6274DA4AAC0D2334E6E283643D436852F5E088AEA2CD0B17")); auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeBandChain, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("band12nmsm9khdsv0tywge43q3zwj8kkj3hvup9rltp")); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("band16gpgu994g2gdrzvwp9047le3pcq9wz6mcgtd4w")); auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeBandChain, accId.get())); auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeBandChain)); auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeBandChain)); @@ -27,8 +27,8 @@ TEST(TWBandChainCoinType, TWCoinType) { ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeBandChain)); ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeBandChain)); assertStringsEqual(symbol, "BAND"); - assertStringsEqual(txUrl, "https://scan-wenchang-testnet2.bandchain.org//tx/473264551D3063A9EC64EC251C61BE92DDDFCF6CC46D026D1E574D83D5447173"); - assertStringsEqual(accUrl, "https://scan-wenchang-testnet2.bandchain.org//account/band12nmsm9khdsv0tywge43q3zwj8kkj3hvup9rltp"); + assertStringsEqual(txUrl, "https://www.mintscan.io/band/tx/74AF38C2183B06EB6274DA4AAC0D2334E6E283643D436852F5E088AEA2CD0B17"); + assertStringsEqual(accUrl, "https://www.mintscan.io/band/account/band16gpgu994g2gdrzvwp9047le3pcq9wz6mcgtd4w"); assertStringsEqual(id, "band"); assertStringsEqual(name, "BandChain"); } diff --git a/tests/chains/NEAR/TWAnySignerTests.cpp b/tests/chains/NEAR/TWAnySignerTests.cpp index cc9192f58f2..cfbda747473 100644 --- a/tests/chains/NEAR/TWAnySignerTests.cpp +++ b/tests/chains/NEAR/TWAnySignerTests.cpp @@ -94,7 +94,7 @@ TEST(TWAnySignerNEAR, SignStakeMainnetReplication) { Proto::SigningOutput output; ANY_SIGN(input, TWCoinTypeNEAR); - // https://explorer.near.org/transactions/kd7ajFw1CfXB8LiJXvhz5NDS7QpQXkuQraAbhb5MMMq + // https://nearblocks.io/txns/kd7ajFw1CfXB8LiJXvhz5NDS7QpQXkuQraAbhb5MMMq ASSERT_EQ(Base58::encode(data(output.hash())), "kd7ajFw1CfXB8LiJXvhz5NDS7QpQXkuQraAbhb5MMMq"); ASSERT_EQ(Base64::encode(data(output.signed_transaction())), "QAAAAGI4ZDVkZjI1MDQ3ODQxMzY1MDA4ZjMwZmI2YjMwZGQ4MjBlOWE4NGQ4NjlmMDU2MjNkMTE0ZTk2ODMxZjJmYmYAzgCT6NK76nb1mB7pToefgkGUHfUe5BKvvr3gW/nq+MgEuu1Mq0YAABEAAABhdmFkby5wb29sdjEubmVhcueGgJlhJ7eg8/I0NQLkQvJDZsul95y3L4vG0N67Js4kAQAAAAIRAAAAZGVwb3NpdF9hbmRfc3Rha2UCAAAAe30A0JjUr3EAAAAAgPZK4ccCLRUAAAAAAAAALNrorr8qTL6u1nlxLpuPa45nFdYmjU96i7CmJP08mVHVzHUaw/bGN30Z3u3o1F2o2yefCBNqO9Ogn9fM25NGCg=="); } @@ -122,7 +122,7 @@ TEST(TWAnySignerNEAR, SignUnstakeMainnetReplication) { Proto::SigningOutput output; ANY_SIGN(input, TWCoinTypeNEAR); - // https://explorer.near.org/transactions/DH6QAX3TkY6XtkteorvKBoGT5hA5ADkURZdzrbbKRs8P + // https://nearblocks.io/txns/DH6QAX3TkY6XtkteorvKBoGT5hA5ADkURZdzrbbKRs8P ASSERT_EQ(Base58::encode(data(output.hash())), "DH6QAX3TkY6XtkteorvKBoGT5hA5ADkURZdzrbbKRs8P"); ASSERT_EQ(Base64::encode(data(output.signed_transaction())), "QAAAAGI4ZDVkZjI1MDQ3ODQxMzY1MDA4ZjMwZmI2YjMwZGQ4MjBlOWE4NGQ4NjlmMDU2MjNkMTE0ZTk2ODMxZjJmYmYAzgCT6NK76nb1mB7pToefgkGUHfUe5BKvvr3gW/nq+MgGuu1Mq0YAABEAAABhdmFkby5wb29sdjEubmVhcq0YnhRlt+TTtagkoy0qKn56zAfGhE+jkTJW6PR5k5r8AQAAAAILAAAAdW5zdGFrZV9hbGwCAAAAe30A0JjUr3EAAAAAAAAAAAAAAAAAAAAAAAAABaFP0EkfJU3VQZ4QAiTwq9ebWDJ7jx7TxbA+VGH4hwKX3gWnmDHVve+LK7/UbbffjF/y8vn0KrPxdh3ONAG0Ag=="); } @@ -131,7 +131,7 @@ TEST(TWAnySignerNEAR, SignUnstakeMainnetReplication) { /// https://nomicon.io/Standards/Tokens/FungibleToken/Core /// /// Successfully broadcasted tx: -/// https://explorer.near.org/transactions/ABQY6nfLdNrRVynHYNjYkfUM6Up5pDHHpuhRJe6FCMRu +/// https://nearblocks.io/txns/ABQY6nfLdNrRVynHYNjYkfUM6Up5pDHHpuhRJe6FCMRu TEST(TWAnySignerNEAR, SignTokenTransfer) { auto privateKey = parse_hex("77006e227658c18da47546413926a26b839204b1b19e807c4a13d994d661c72e"); diff --git a/tests/chains/NEAR/TWCoinTypeTests.cpp b/tests/chains/NEAR/TWCoinTypeTests.cpp index b6e310a641e..e423b2ec3b4 100644 --- a/tests/chains/NEAR/TWCoinTypeTests.cpp +++ b/tests/chains/NEAR/TWCoinTypeTests.cpp @@ -27,8 +27,8 @@ TEST(TWNEARCoinType, TWCoinType) { ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeNEAR)); ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeNEAR)); assertStringsEqual(symbol, "NEAR"); - assertStringsEqual(txUrl, "https://explorer.near.org/transactions/FPQAMaVnvFHNwNBJWnTttXfdJhp5FvMGGDJEesB8gvbL"); - assertStringsEqual(accUrl, "https://explorer.near.org/accounts/test-trust.vlad.near"); + assertStringsEqual(txUrl, "https://nearblocks.io/txns/FPQAMaVnvFHNwNBJWnTttXfdJhp5FvMGGDJEesB8gvbL"); + assertStringsEqual(accUrl, "https://nearblocks.io/address/test-trust.vlad.near"); assertStringsEqual(id, "near"); assertStringsEqual(name, "NEAR"); } diff --git a/tests/chains/Nano/TWCoinTypeTests.cpp b/tests/chains/Nano/TWCoinTypeTests.cpp index 87a8ee6ef1e..ce32e618915 100644 --- a/tests/chains/Nano/TWCoinTypeTests.cpp +++ b/tests/chains/Nano/TWCoinTypeTests.cpp @@ -27,8 +27,8 @@ TEST(TWNanoCoinType, TWCoinType) { ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeNano)); ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeNano)); assertStringsEqual(symbol, "XNO"); - assertStringsEqual(txUrl, "https://nanocrawler.cc/explorer/block/C264DB7BF40738F0CEFF19B606746CB925B713E4B8699A055699E0DC8ABBC70F"); - assertStringsEqual(accUrl, "https://nanocrawler.cc/explorer/account/nano_1wpj616kwhe1y38y1mspd8aub8i334cwybqco511iyuxm55zx8d67ptf1tsf"); + assertStringsEqual(txUrl, "https://www.nanolooker.com/block/C264DB7BF40738F0CEFF19B606746CB925B713E4B8699A055699E0DC8ABBC70F"); + assertStringsEqual(accUrl, "https://www.nanolooker.com/account/nano_1wpj616kwhe1y38y1mspd8aub8i334cwybqco511iyuxm55zx8d67ptf1tsf"); assertStringsEqual(id, "nano"); assertStringsEqual(name, "Nano"); } diff --git a/tests/chains/OKXChain/TWCoinTypeTests.cpp b/tests/chains/OKXChain/TWCoinTypeTests.cpp index 4741a154cc5..e047d47b13a 100644 --- a/tests/chains/OKXChain/TWCoinTypeTests.cpp +++ b/tests/chains/OKXChain/TWCoinTypeTests.cpp @@ -27,8 +27,8 @@ TEST(TWCoinTypeOKXChain, TWCoinType) { ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeOKXChain)); ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeOKXChain)); assertStringsEqual(symbol, "OKT"); - assertStringsEqual(txUrl, "https://www.oklink.com/en/okc/tx/0x46C3A947E8248570FBD28E4FE456CC8F80DFD90716533878FB67857B95FA3D37"); - assertStringsEqual(accUrl, "https://www.oklink.com/en/okc/address/0x074faafd0b20fad2efa115b8ed7e75993e580b85"); + assertStringsEqual(txUrl, "https://www.oklink.com/oktc/tx/0x46C3A947E8248570FBD28E4FE456CC8F80DFD90716533878FB67857B95FA3D37"); + assertStringsEqual(accUrl, "https://www.oklink.com/oktc/address/0x074faafd0b20fad2efa115b8ed7e75993e580b85"); assertStringsEqual(id, "okc"); assertStringsEqual(name, "OKX Chain"); } diff --git a/tests/chains/Oasis/TWCoinTypeTests.cpp b/tests/chains/Oasis/TWCoinTypeTests.cpp index 19bb42436cf..a2a1738b374 100644 --- a/tests/chains/Oasis/TWCoinTypeTests.cpp +++ b/tests/chains/Oasis/TWCoinTypeTests.cpp @@ -15,7 +15,7 @@ TEST(TWOasisCoinType, TWCoinType) { auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeOasis)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0b9bd4983f1c88a1c71bf33562b6ba02b3064e01697d15a0de4bfe1922ec74b8")); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("73dc977fdd8596d4a57e6feb891b21f5da3652d26815dc94f15f7420c298e29e")); auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeOasis, txId.get())); auto accId = WRAPS(TWStringCreateWithUTF8Bytes("oasis1qrx376dmwuckmruzn9vq64n49clw72lywctvxdf4")); auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeOasis, accId.get())); @@ -27,7 +27,7 @@ TEST(TWOasisCoinType, TWCoinType) { ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeOasis)); ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeOasis)); assertStringsEqual(symbol, "ROSE"); - assertStringsEqual(txUrl, "https://oasisscan.com/transactions/0b9bd4983f1c88a1c71bf33562b6ba02b3064e01697d15a0de4bfe1922ec74b8"); + assertStringsEqual(txUrl, "https://oasisscan.com/transactions/73dc977fdd8596d4a57e6feb891b21f5da3652d26815dc94f15f7420c298e29e"); assertStringsEqual(accUrl, "https://oasisscan.com/accounts/detail/oasis1qrx376dmwuckmruzn9vq64n49clw72lywctvxdf4"); assertStringsEqual(id, "oasis"); assertStringsEqual(name, "Oasis"); diff --git a/tests/chains/Stellar/TWCoinTypeTests.cpp b/tests/chains/Stellar/TWCoinTypeTests.cpp index f9a31043da0..113a6366e72 100644 --- a/tests/chains/Stellar/TWCoinTypeTests.cpp +++ b/tests/chains/Stellar/TWCoinTypeTests.cpp @@ -15,7 +15,7 @@ TEST(TWStellarCoinType, TWCoinType) { auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeStellar)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("d9aeabfa9d24df8c5755125f8af243b74cd3ff878656cfa72c566a8824bf6e84")); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("8a7ff7261e8b3f31af7f6ed257c2e9fe7c47afcd9b1ce1be1bfc1bc5f6a3ad9e")); auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeStellar, txId.get())); auto accId = WRAPS(TWStringCreateWithUTF8Bytes("GCILJZQ3CKBKBUJWW4TAM6Q37LJA5MQX6GMSFSQN75BPLWIZ33OPRG52")); auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeStellar, accId.get())); @@ -27,7 +27,7 @@ TEST(TWStellarCoinType, TWCoinType) { ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeStellar)); ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeStellar)); assertStringsEqual(symbol, "XLM"); - assertStringsEqual(txUrl, "https://blockchair.com/stellar/transaction/d9aeabfa9d24df8c5755125f8af243b74cd3ff878656cfa72c566a8824bf6e84"); + assertStringsEqual(txUrl, "https://blockchair.com/stellar/transaction/8a7ff7261e8b3f31af7f6ed257c2e9fe7c47afcd9b1ce1be1bfc1bc5f6a3ad9e"); assertStringsEqual(accUrl, "https://blockchair.com/stellar/account/GCILJZQ3CKBKBUJWW4TAM6Q37LJA5MQX6GMSFSQN75BPLWIZ33OPRG52"); assertStringsEqual(id, "stellar"); assertStringsEqual(name, "Stellar"); diff --git a/tools/registry b/tools/registry new file mode 100755 index 00000000000..c044a37394c --- /dev/null +++ b/tools/registry @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 + +import argparse +import json +import sys +import requests + +PATH_TO_REGISTRY_JSON = "registry.json" + + +def ping_url(url): + response = requests.get(url) + if response.status_code != 200: + raise Exception(str(response.status_code)) + + +def ping_explorers(_args): + """Pings blockchain explorers inside 'registry.json'""" + registry_file = open(PATH_TO_REGISTRY_JSON, 'r') + registry_json = json.load(registry_file) + + for chain_json in registry_json: + explorer_json = chain_json["explorer"] + sample_url = explorer_json["url"] + + # Not all chains have `sampleTx` parameter. + if "sampleTx" in explorer_json: + sample_url += explorer_json["txPath"] + explorer_json["sampleTx"] + + try: + ping_url(sample_url) + except Exception as exception: + print(chain_json["name"], ":", sample_url, " NOT WORKING") + print("\tError: ", exception) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Operations over registry.json") + subparsers = parser.add_subparsers() + + ping_explorers_parser = subparsers.add_parser('ping-explorers', + help="Ping blockchain explorers inside 'registry.json'") + ping_explorers_parser.set_defaults(func=ping_explorers) + + args = parser.parse_args() + args.func(args) From cd5a27481d2181e63362cb57e2b2160506cce163 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Fri, 12 Jan 2024 18:26:30 +0700 Subject: [PATCH 039/128] [Copyright]: Update Copyright to 2024 (#3650) * [Misc]: Update Copyright to 2024 * [Copyright]: Change Copyright Disclamer format * [Copyright]: Change Copyright disclaimer format * [Copyright]: Change Copyright disclaimer format * [Copyright]: Change Copyright disclaimer format * [CI] Trigger CI --- CMakeLists.txt | 6 ++---- .../core/app/blockchains/acala/TestAcalaAddress.kt | 6 ++---- .../core/app/blockchains/acala/TestAcalaSigner.kt | 6 ++---- .../core/app/blockchains/acalaevm/TestAcalaEVMAddress.kt | 6 ++---- .../core/app/blockchains/agoric/TestAgoricAddress.kt | 6 ++---- .../core/app/blockchains/agoric/TestAgoricSigner.kt | 6 ++---- .../core/app/blockchains/aptos/TestAptosAddress.kt | 6 ++---- .../core/app/blockchains/aptos/TestAptosSigner.kt | 6 ++---- .../app/blockchains/bandchain/TestBandChainAddress.kt | 6 ++---- .../app/blockchains/bandchain/TestBandChainSigner.kt | 6 ++---- .../bitcoindiamond/TestBitcoinDiamondAddress.kt | 6 ++---- .../bitcoindiamond/TestBitcoinDiamondSigner.kt | 6 ++---- .../core/app/blockchains/bluzelle/TestBluzelleAddress.kt | 6 ++---- .../core/app/blockchains/bluzelle/TestBluzelleSigner.kt | 6 ++---- .../core/app/blockchains/cardano/TestCardanoAddress.kt | 6 ++---- .../core/app/blockchains/cardano/TestCardanoSigning.kt | 6 ++---- .../confluxespace/TestConfluxeSpaceAddress.kt | 6 ++---- .../app/blockchains/cryptoorg/TestCryptoorgAddress.kt | 6 ++---- .../app/blockchains/cryptoorg/TestCryptoorgSigner.kt | 6 ++---- .../app/blockchains/everscale/TestEverscaleAddress.kt | 6 ++---- .../app/blockchains/everscale/TestEverscaleSigner.kt | 6 ++---- .../core/app/blockchains/fio/TestFIOAddress.kt | 6 ++---- .../core/app/blockchains/fio/TestFIOSigner.kt | 6 ++---- .../app/blockchains/greenfield/TestGreenfieldSigner.kt | 6 ++---- .../core/app/blockchains/hedera/TestHederaAddress.kt | 6 ++---- .../core/app/blockchains/hedera/TestHederaSigner.kt | 6 ++---- .../internetcomputer/TestInternetComputerAddress.kt | 6 ++---- .../internetcomputer/TestInternetComputerSigner.kt | 6 ++---- .../core/app/blockchains/juno/TestJunoAddress.kt | 6 ++---- .../blockchains/kcc/TestKuCoinCommunityChainAddress.kt | 6 ++---- .../core/app/blockchains/kusama/TestKusamaAddress.kt | 6 ++---- .../core/app/blockchains/kusama/TestKusamaSigner.kt | 6 ++---- .../app/blockchains/multiversx/TestMultiversXAddress.kt | 6 ++---- .../app/blockchains/multiversx/TestMultiversXSigner.kt | 6 ++---- .../nativeinjective/TestNativeInjectiveAddress.kt | 6 ++---- .../nativeinjective/TestNativeInjectiveSigner.kt | 6 ++---- .../core/app/blockchains/neo/TestsNEOAddress.kt | 6 ++---- .../core/app/blockchains/nervos/TestNervosAddress.kt | 6 ++---- .../core/app/blockchains/nervos/TestNervosSigner.kt | 6 ++---- .../core/app/blockchains/oasis/TestOasisAddress.kt | 6 ++---- .../core/app/blockchains/oasis/TestOasisSigner.kt | 6 ++---- .../core/app/blockchains/osmosis/TestOsmosisAddress.kt | 6 ++---- .../core/app/blockchains/osmosis/TestOsmosisSigner.kt | 6 ++---- .../core/app/blockchains/polkadot/TestPolkadotAddress.kt | 6 ++---- .../core/app/blockchains/polkadot/TestPolkadotSigner.kt | 6 ++---- .../core/app/blockchains/polygon/TestPolygonAddress.kt | 6 ++---- .../core/app/blockchains/scroll/TestScrollAddress.kt | 6 ++---- .../core/app/blockchains/secret/TestSecretAddress.kt | 6 ++---- .../core/app/blockchains/secret/TestSecretSigner.kt | 6 ++---- .../smartbitcoincash/TestSmartBitcoinCashAddress.kt | 6 ++---- .../smartchain/TestBinanceSmartChainAddress.kt | 6 ++---- .../core/app/blockchains/stargaze/TestStargazeAddress.kt | 6 ++---- .../core/app/blockchains/stargaze/TestStargazeSigner.kt | 6 ++---- .../core/app/blockchains/sui/TestSuiAddress.kt | 6 ++---- .../core/app/blockchains/sui/TestSuiSigner.kt | 6 ++---- .../theopennetwork/TestTheOpenNetworkAddress.kt | 6 ++---- .../theopennetwork/TestTheOpenNetworkSigner.kt | 6 ++---- .../app/blockchains/thetafuel/TestThetaFuelAddress.kt | 6 ++---- .../app/blockchains/thorchain/TestTHORChainAddress.kt | 6 ++---- .../app/blockchains/thorchain/TestTHORChainSigner.kt | 6 ++---- .../core/app/blockchains/zen/TestZenAddress.kt | 6 ++---- .../core/app/blockchains/zen/TestZenSigner.kt | 6 ++---- .../com/trustwallet/core/app/utils/TestAnyAddress.kt | 6 ++---- .../java/com/trustwallet/core/app/utils/TestAsnParser.kt | 6 ++---- .../java/com/trustwallet/core/app/utils/TestHDWallet.kt | 6 ++---- .../com/trustwallet/core/app/utils/TestLiquidStaking.kt | 6 ++---- cmake/Protobuf.cmake | 6 ++---- .../src/codegen/cpp/blockchain_dispatcher_generator.rs | 6 ++---- codegen-v2/src/codegen/cpp/entry_generator.rs | 6 ++---- codegen-v2/src/codegen/cpp/mod.rs | 6 ++---- codegen-v2/src/codegen/cpp/new_blockchain.rs | 6 ++---- codegen-v2/src/codegen/cpp/new_evmchain.rs | 6 ++---- codegen-v2/src/codegen/cpp/templates/Entry.h | 6 ++---- .../src/codegen/cpp/templates/TWAnyAddressTests.cpp | 6 ++---- .../src/codegen/cpp/templates/TWAnySignerTests.cpp | 6 ++---- codegen-v2/src/codegen/cpp/templates/TWCoinTypeTests.cpp | 6 ++---- .../src/codegen/cpp/tw_any_address_tests_generator.rs | 6 ++---- .../src/codegen/cpp/tw_any_signer_tests_generator.rs | 6 ++---- codegen-v2/src/codegen/cpp/tw_blockchain.rs | 6 ++---- .../cpp/tw_coin_address_derivation_tests_generator.rs | 6 ++---- codegen-v2/src/codegen/cpp/tw_coin_type_generator.rs | 6 ++---- .../src/codegen/cpp/tw_coin_type_tests_generator.rs | 6 ++---- codegen-v2/src/codegen/mod.rs | 6 ++---- codegen-v2/src/codegen/proto/mod.rs | 6 ++---- codegen-v2/src/codegen/proto/new_blockchain.rs | 6 ++---- codegen-v2/src/codegen/proto/proto_generator.rs | 6 ++---- codegen-v2/src/codegen/proto/templates/Blockchain.proto | 6 ++---- .../src/codegen/rust/blockchain_dispatcher_generator.rs | 6 ++---- codegen-v2/src/codegen/rust/blockchain_type_generator.rs | 6 ++---- .../rust/coin_address_derivation_test_generator.rs | 6 ++---- codegen-v2/src/codegen/rust/coin_crate.rs | 6 ++---- codegen-v2/src/codegen/rust/coin_integration_tests.rs | 6 ++---- .../src/codegen/rust/coin_registry_manifest_generator.rs | 6 ++---- codegen-v2/src/codegen/rust/mod.rs | 6 ++---- codegen-v2/src/codegen/rust/new_blockchain.rs | 6 ++---- codegen-v2/src/codegen/rust/new_evmchain.rs | 6 ++---- .../codegen/rust/templates/blockchain_crate/address.rs | 6 ++---- .../codegen/rust/templates/blockchain_crate/compiler.rs | 6 ++---- .../src/codegen/rust/templates/blockchain_crate/entry.rs | 6 ++---- .../src/codegen/rust/templates/blockchain_crate/lib.rs | 6 ++---- .../codegen/rust/templates/blockchain_crate/signer.rs | 6 ++---- .../rust/templates/integration_tests/address_tests.rs | 6 ++---- .../rust/templates/integration_tests/compile_tests.rs | 6 ++---- .../src/codegen/rust/templates/integration_tests/mod.rs | 6 ++---- .../rust/templates/integration_tests/sign_tests.rs | 6 ++---- codegen-v2/src/codegen/rust/toml_editor.rs | 6 ++---- codegen-v2/src/codegen/swift/functions.rs | 6 ++---- codegen-v2/src/codegen/swift/inits.rs | 6 ++---- codegen-v2/src/codegen/swift/mod.rs | 6 ++---- codegen-v2/src/codegen/swift/properties.rs | 6 ++---- codegen-v2/src/codegen/swift/render.rs | 6 ++---- codegen-v2/src/codegen/swift/templates/WalletCore.h | 6 ++---- codegen-v2/src/codegen/swift/templates/enum.hbs | 6 ++---- codegen-v2/src/codegen/swift/templates/extension.hbs | 6 ++---- codegen-v2/src/codegen/swift/templates/proto.hbs | 6 ++---- codegen-v2/src/codegen/swift/templates/struct.hbs | 6 ++---- codegen-v2/src/codegen/template_generator.rs | 6 ++---- codegen-v2/src/coin_id.rs | 6 ++---- codegen-v2/src/lib.rs | 6 ++---- codegen-v2/src/main.rs | 6 ++---- codegen-v2/src/manifest.rs | 6 ++---- codegen-v2/src/registry.rs | 6 ++---- codegen-v2/src/tests/mod.rs | 6 ++---- codegen-v2/src/tests/samples/class.output.swift | 6 ++---- codegen-v2/src/tests/samples/enum.output.swift | 6 ++---- codegen-v2/src/tests/samples/enum_extension.output.swift | 6 ++---- codegen-v2/src/tests/samples/enum_private.output.swift | 6 ++---- codegen-v2/src/tests/samples/non-associated.output.swift | 6 ++---- codegen-v2/src/tests/samples/optional.output.swift | 6 ++---- codegen-v2/src/tests/samples/private_class.output.swift | 6 ++---- codegen-v2/src/tests/samples/struct.output.swift | 6 ++---- codegen-v2/src/utils.rs | 6 ++---- codegen/lib/templates/CoinInfoData.cpp.erb | 6 ++---- codegen/lib/templates/TWCoinTypeTests.cpp.erb | 6 ++---- codegen/lib/templates/TWDerivation.h.erb | 6 ++---- codegen/lib/templates/TWEthereumChainID.h.erb | 6 ++---- codegen/lib/templates/copyright_header.erb | 6 ++---- codegen/lib/templates/hrp.cpp.erb | 6 ++---- codegen/lib/templates/hrp.h.erb | 6 ++---- codegen/lib/templates/java/header.erb | 6 ++---- codegen/lib/templates/newcoin/Address.cpp.erb | 6 ++---- codegen/lib/templates/newcoin/Address.h.erb | 6 ++---- codegen/lib/templates/newcoin/AddressTests.cpp.erb | 6 ++---- codegen/lib/templates/newcoin/AddressTests.kt.erb | 6 ++---- codegen/lib/templates/newcoin/Entry.cpp.erb | 6 ++---- codegen/lib/templates/newcoin/Entry.h.erb | 6 ++---- codegen/lib/templates/newcoin/Proto.erb | 6 ++---- codegen/lib/templates/newcoin/Signer.cpp.erb | 6 ++---- codegen/lib/templates/newcoin/Signer.h.erb | 6 ++---- codegen/lib/templates/newcoin/SignerTests.cpp.erb | 6 ++---- codegen/lib/templates/newcoin/SignerTests.kt.erb | 6 ++---- codegen/lib/templates/newcoin/TWAddressTests.cpp.erb | 6 ++---- codegen/lib/templates/newcoin/TWSignerTests.cpp.erb | 6 ++---- codegen/lib/templates/newcoin/Tests.swift.erb | 6 ++---- .../templates/newcoin/TransactionCompilerTests.cpp.erb | 6 ++---- codegen/lib/templates/swift/TrustWalletCore.h.erb | 6 ++---- include/TrustWalletCore/TWAES.h | 6 ++---- include/TrustWalletCore/TWAESPaddingMode.h | 6 ++---- include/TrustWalletCore/TWAccount.h | 6 ++---- include/TrustWalletCore/TWAnyAddress.h | 6 ++---- include/TrustWalletCore/TWAnySigner.h | 6 ++---- include/TrustWalletCore/TWAsnParser.h | 6 ++---- include/TrustWalletCore/TWBarz.h | 6 ++---- include/TrustWalletCore/TWBase.h | 6 ++---- include/TrustWalletCore/TWBase32.h | 6 ++---- include/TrustWalletCore/TWBase58.h | 6 ++---- include/TrustWalletCore/TWBase64.h | 6 ++---- include/TrustWalletCore/TWBitcoinAddress.h | 6 ++---- include/TrustWalletCore/TWBitcoinFee.h | 6 ++---- include/TrustWalletCore/TWBitcoinMessageSigner.h | 6 ++---- include/TrustWalletCore/TWBitcoinScript.h | 6 ++---- include/TrustWalletCore/TWBitcoinSigHashType.h | 6 ++---- include/TrustWalletCore/TWBlockchain.h | 6 ++---- include/TrustWalletCore/TWCardano.h | 6 ++---- include/TrustWalletCore/TWCoinType.h | 6 ++---- include/TrustWalletCore/TWCoinTypeConfiguration.h | 6 ++---- include/TrustWalletCore/TWCurve.h | 6 ++---- include/TrustWalletCore/TWData.h | 6 ++---- include/TrustWalletCore/TWDataVector.h | 6 ++---- include/TrustWalletCore/TWDerivationPath.h | 6 ++---- include/TrustWalletCore/TWDerivationPathIndex.h | 6 ++---- include/TrustWalletCore/TWEthereum.h | 6 ++---- include/TrustWalletCore/TWEthereumAbi.h | 6 ++---- include/TrustWalletCore/TWEthereumAbiFunction.h | 6 ++---- include/TrustWalletCore/TWEthereumAbiValue.h | 6 ++---- include/TrustWalletCore/TWEthereumMessageSigner.h | 6 ++---- include/TrustWalletCore/TWEthereumRlp.h | 6 ++---- include/TrustWalletCore/TWFIOAccount.h | 6 ++---- include/TrustWalletCore/TWFilecoinAddressConverter.h | 6 ++---- include/TrustWalletCore/TWFilecoinAddressType.h | 6 ++---- include/TrustWalletCore/TWGroestlcoinAddress.h | 6 ++---- include/TrustWalletCore/TWHDVersion.h | 6 ++---- include/TrustWalletCore/TWHDWallet.h | 6 ++---- include/TrustWalletCore/TWHash.h | 6 ++---- include/TrustWalletCore/TWLiquidStaking.h | 6 ++---- include/TrustWalletCore/TWMnemonic.h | 6 ++---- include/TrustWalletCore/TWNEARAccount.h | 6 ++---- include/TrustWalletCore/TWNervosAddress.h | 6 ++---- include/TrustWalletCore/TWPBKDF2.h | 6 ++---- include/TrustWalletCore/TWPrivateKey.h | 6 ++---- include/TrustWalletCore/TWPrivateKeyType.h | 6 ++---- include/TrustWalletCore/TWPublicKey.h | 6 ++---- include/TrustWalletCore/TWPublicKeyType.h | 6 ++---- include/TrustWalletCore/TWPurpose.h | 6 ++---- include/TrustWalletCore/TWRippleXAddress.h | 6 ++---- include/TrustWalletCore/TWSS58AddressType.h | 6 ++---- include/TrustWalletCore/TWSegwitAddress.h | 6 ++---- include/TrustWalletCore/TWSolanaAddress.h | 6 ++---- include/TrustWalletCore/TWStarkExMessageSigner.h | 6 ++---- include/TrustWalletCore/TWStarkWare.h | 6 ++---- include/TrustWalletCore/TWStellarMemoType.h | 6 ++---- include/TrustWalletCore/TWStellarPassphrase.h | 6 ++---- include/TrustWalletCore/TWStellarVersionByte.h | 6 ++---- include/TrustWalletCore/TWStoredKey.h | 6 ++---- include/TrustWalletCore/TWStoredKeyEncryption.h | 6 ++---- include/TrustWalletCore/TWStoredKeyEncryptionLevel.h | 6 ++---- include/TrustWalletCore/TWString.h | 6 ++---- include/TrustWalletCore/TWTHORChainSwap.h | 6 ++---- include/TrustWalletCore/TWTezosMessageSigner.h | 6 ++---- include/TrustWalletCore/TWTransactionCompiler.h | 6 ++---- include/TrustWalletCore/TWTronMessageSigner.h | 6 ++---- include/TrustWalletCore/TWWalletConnectRequest.h | 6 ++---- include/TrustWalletCore/TWWebAuthn.h | 6 ++---- jni/android/AnySigner.c | 6 ++---- jni/android/AnySigner.h | 6 ++---- jni/cpp/Random.cpp | 6 ++---- jni/cpp/TWJNI.h | 6 ++---- jni/cpp/TWJNIData.cpp | 6 ++---- jni/cpp/TWJNIData.h | 6 ++---- jni/cpp/TWJNIString.cpp | 6 ++---- jni/cpp/TWJNIString.h | 6 ++---- jni/java/wallet/core/java/AnySigner.java | 6 ++---- jni/kotlin/AnySigner.c | 6 ++---- jni/kotlin/AnySigner.h | 6 ++---- .../kotlin/com/trustwallet/core/AnySigner.kt | 6 ++---- .../commonMain/kotlin/com/trustwallet/core/AnySigner.kt | 6 ++---- .../src/iosMain/kotlin/com/trustwallet/core/AnySigner.kt | 6 ++---- .../iosMain/kotlin/com/trustwallet/core/ByteArrayExt.kt | 6 ++---- .../src/iosMain/kotlin/com/trustwallet/core/StringExt.kt | 6 ++---- .../wallet-core-kotlin/src/jsMain/kotlin/WalletCore.kt | 6 ++---- .../src/jsMain/kotlin/com/trustwallet/core/AnySigner.kt | 6 ++---- .../src/jsMain/kotlin/com/trustwallet/core/ByteArray.kt | 6 ++---- .../jsMain/kotlin/com/trustwallet/core/JsAnySigner.kt | 6 ++---- .../jsMain/kotlin/com/trustwallet/core/JsWalletCore.kt | 6 ++---- protobuf-plugin/CMakeLists.txt | 6 ++---- protobuf-plugin/c_typedef.cc | 6 ++---- protobuf-plugin/swift_typealias.cc | 6 ++---- rust/chains/tw_binance/src/address.rs | 6 ++---- rust/chains/tw_binance/src/amino.rs | 6 ++---- rust/chains/tw_binance/src/compiler.rs | 6 ++---- rust/chains/tw_binance/src/context.rs | 6 ++---- rust/chains/tw_binance/src/entry.rs | 6 ++---- rust/chains/tw_binance/src/lib.rs | 6 ++---- rust/chains/tw_binance/src/modules/mod.rs | 6 ++---- rust/chains/tw_binance/src/modules/preimager.rs | 6 ++---- rust/chains/tw_binance/src/modules/serializer.rs | 6 ++---- rust/chains/tw_binance/src/modules/tx_builder.rs | 6 ++---- .../tw_binance/src/modules/wallet_connect/connector.rs | 6 ++---- rust/chains/tw_binance/src/modules/wallet_connect/mod.rs | 6 ++---- .../tw_binance/src/modules/wallet_connect/types.rs | 6 ++---- rust/chains/tw_binance/src/signature.rs | 6 ++---- rust/chains/tw_binance/src/signer.rs | 6 ++---- .../tw_binance/src/transaction/message/htlt_order.rs | 6 ++---- rust/chains/tw_binance/src/transaction/message/mod.rs | 6 ++---- .../tw_binance/src/transaction/message/send_order.rs | 6 ++---- .../src/transaction/message/side_chain_delegate.rs | 6 ++---- .../src/transaction/message/time_lock_order.rs | 6 ++---- .../tw_binance/src/transaction/message/token_order.rs | 6 ++---- .../tw_binance/src/transaction/message/trade_order.rs | 6 ++---- .../src/transaction/message/tranfer_out_order.rs | 6 ++---- rust/chains/tw_binance/src/transaction/mod.rs | 6 ++---- rust/chains/tw_cosmos/src/entry.rs | 6 ++---- rust/chains/tw_cosmos/src/lib.rs | 6 ++---- rust/chains/tw_greenfield/src/address.rs | 6 ++---- rust/chains/tw_greenfield/src/compiler.rs | 6 ++---- rust/chains/tw_greenfield/src/context.rs | 6 ++---- rust/chains/tw_greenfield/src/eip712_types.rs | 6 ++---- rust/chains/tw_greenfield/src/entry.rs | 6 ++---- rust/chains/tw_greenfield/src/lib.rs | 6 ++---- rust/chains/tw_greenfield/src/modules/eip712_signer.rs | 6 ++---- rust/chains/tw_greenfield/src/modules/mod.rs | 6 ++---- rust/chains/tw_greenfield/src/modules/tx_builder.rs | 6 ++---- rust/chains/tw_greenfield/src/public_key.rs | 6 ++---- rust/chains/tw_greenfield/src/signature.rs | 6 ++---- rust/chains/tw_greenfield/src/signer.rs | 6 ++---- rust/chains/tw_greenfield/src/transaction/message/mod.rs | 6 ++---- .../tw_greenfield/src/transaction/message/send_order.rs | 6 ++---- .../src/transaction/message/transfer_out.rs | 6 ++---- .../src/transaction/message/type_msg_amount.rs | 6 ++---- rust/chains/tw_greenfield/src/transaction/mod.rs | 6 ++---- rust/chains/tw_greenfield/tests/eip712_signer.rs | 6 ++---- rust/chains/tw_native_evmos/src/context.rs | 6 ++---- rust/chains/tw_native_evmos/src/entry.rs | 6 ++---- rust/chains/tw_native_evmos/src/ethermint_public_key.rs | 6 ++---- rust/chains/tw_native_evmos/src/lib.rs | 6 ++---- rust/chains/tw_native_injective/src/context.rs | 6 ++---- rust/chains/tw_native_injective/src/entry.rs | 6 ++---- .../tw_native_injective/src/injective_public_key.rs | 6 ++---- rust/chains/tw_native_injective/src/lib.rs | 6 ++---- rust/chains/tw_thorchain/src/compiler.rs | 6 ++---- rust/chains/tw_thorchain/src/entry.rs | 6 ++---- rust/chains/tw_thorchain/src/lib.rs | 6 ++---- rust/chains/tw_thorchain/src/signer.rs | 6 ++---- rust/chains/tw_thorchain/src/signing_input.rs | 6 ++---- rust/tw_any_coin/src/any_address.rs | 6 ++---- rust/tw_any_coin/src/any_signer.rs | 6 ++---- rust/tw_any_coin/src/ffi/mod.rs | 6 ++---- rust/tw_any_coin/src/ffi/tw_any_address.rs | 6 ++---- rust/tw_any_coin/src/ffi/tw_any_signer.rs | 6 ++---- rust/tw_any_coin/src/ffi/tw_message_signer.rs | 6 ++---- rust/tw_any_coin/src/ffi/tw_transaction_compiler.rs | 6 ++---- rust/tw_any_coin/src/ffi/tw_wallet_connect_request.rs | 6 ++---- rust/tw_any_coin/src/lib.rs | 6 ++---- rust/tw_any_coin/src/message_signer.rs | 6 ++---- rust/tw_any_coin/src/test_utils/address_utils.rs | 6 ++---- rust/tw_any_coin/src/test_utils/mod.rs | 6 ++---- rust/tw_any_coin/src/test_utils/sign_utils.rs | 6 ++---- rust/tw_any_coin/src/test_utils/wallet_connect_utils.rs | 6 ++---- rust/tw_any_coin/src/transaction_compiler.rs | 6 ++---- rust/tw_any_coin/src/wallet_connect_request.rs | 6 ++---- rust/tw_any_coin/tests/chain_tests.rs | 6 ++---- rust/tw_any_coin/tests/chains/aptos/aptos_address.rs | 6 ++---- rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs | 6 ++---- rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs | 6 ++---- rust/tw_any_coin/tests/chains/aptos/mod.rs | 6 ++---- rust/tw_any_coin/tests/chains/aptos/test_cases.rs | 6 ++---- rust/tw_any_coin/tests/chains/binance/binance_address.rs | 6 ++---- rust/tw_any_coin/tests/chains/binance/binance_compile.rs | 6 ++---- rust/tw_any_coin/tests/chains/binance/binance_sign.rs | 6 ++---- .../tests/chains/binance/binance_wallet_connect.rs | 6 ++---- rust/tw_any_coin/tests/chains/binance/mod.rs | 6 ++---- rust/tw_any_coin/tests/chains/bitcoin/bitcoin_address.rs | 6 ++---- rust/tw_any_coin/tests/chains/bitcoin/mod.rs | 6 ++---- rust/tw_any_coin/tests/chains/cosmos/cosmos_address.rs | 6 ++---- rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs | 6 ++---- rust/tw_any_coin/tests/chains/cosmos/mod.rs | 6 ++---- .../tests/chains/ethereum/ethereum_address.rs | 6 ++---- .../tests/chains/ethereum/ethereum_compile.rs | 6 ++---- .../tests/chains/ethereum/ethereum_message_sign.rs | 6 ++---- rust/tw_any_coin/tests/chains/ethereum/ethereum_sign.rs | 6 ++---- rust/tw_any_coin/tests/chains/ethereum/mod.rs | 6 ++---- .../tests/chains/greenfield/greenfield_address.rs | 6 ++---- .../tests/chains/greenfield/greenfield_compile.rs | 6 ++---- .../tests/chains/greenfield/greenfield_sign.rs | 6 ++---- rust/tw_any_coin/tests/chains/greenfield/mod.rs | 6 ++---- .../internet_computer/internet_computer_address.rs | 6 ++---- rust/tw_any_coin/tests/chains/internet_computer/mod.rs | 6 ++---- rust/tw_any_coin/tests/chains/mod.rs | 6 ++---- rust/tw_any_coin/tests/chains/native_evmos/mod.rs | 6 ++---- .../tests/chains/native_evmos/native_evmos_address.rs | 6 ++---- .../tests/chains/native_evmos/native_evmos_sign.rs | 6 ++---- rust/tw_any_coin/tests/chains/native_injective/mod.rs | 6 ++---- .../chains/native_injective/native_injective_address.rs | 6 ++---- .../chains/native_injective/native_injective_compile.rs | 6 ++---- .../chains/native_injective/native_injective_sign.rs | 6 ++---- rust/tw_any_coin/tests/chains/tbinance/mod.rs | 6 ++---- .../tests/chains/tbinance/tbinance_address.rs | 6 ++---- rust/tw_any_coin/tests/chains/thorchain/mod.rs | 6 ++---- rust/tw_any_coin/tests/chains/thorchain/test_cases.rs | 6 ++---- .../tests/chains/thorchain/thorchain_address.rs | 6 ++---- .../tests/chains/thorchain/thorchain_compile.rs | 6 ++---- .../tw_any_coin/tests/chains/thorchain/thorchain_sign.rs | 6 ++---- rust/tw_any_coin/tests/coin_address_derivation_test.rs | 6 ++---- rust/tw_any_coin/tests/tw_any_signer_ffi_tests.rs | 6 ++---- rust/tw_aptos/src/address.rs | 6 ++---- rust/tw_aptos/src/aptos_move_packages.rs | 6 ++---- rust/tw_aptos/src/constants.rs | 6 ++---- rust/tw_aptos/src/entry.rs | 6 ++---- rust/tw_aptos/src/lib.rs | 6 ++---- rust/tw_aptos/src/liquid_staking.rs | 6 ++---- rust/tw_aptos/src/nft.rs | 6 ++---- rust/tw_aptos/src/signer.rs | 6 ++---- rust/tw_aptos/src/transaction.rs | 6 ++---- rust/tw_aptos/src/transaction_builder.rs | 6 ++---- rust/tw_aptos/src/transaction_payload.rs | 6 ++---- rust/tw_aptos/tests/signer.rs | 6 ++---- rust/tw_bech32_address/src/bech32_prefix.rs | 6 ++---- rust/tw_bech32_address/src/lib.rs | 6 ++---- rust/tw_coin_entry/src/coin_context.rs | 6 ++---- rust/tw_coin_entry/src/coin_entry.rs | 6 ++---- rust/tw_coin_entry/src/coin_entry_ext.rs | 6 ++---- rust/tw_coin_entry/src/common/compile_input.rs | 6 ++---- rust/tw_coin_entry/src/common/mod.rs | 6 ++---- rust/tw_coin_entry/src/derivation.rs | 6 ++---- rust/tw_coin_entry/src/error.rs | 6 ++---- rust/tw_coin_entry/src/lib.rs | 6 ++---- rust/tw_coin_entry/src/modules/json_signer.rs | 6 ++---- rust/tw_coin_entry/src/modules/message_signer.rs | 6 ++---- rust/tw_coin_entry/src/modules/mod.rs | 6 ++---- rust/tw_coin_entry/src/modules/plan_builder.rs | 6 ++---- rust/tw_coin_entry/src/modules/wallet_connector.rs | 6 ++---- rust/tw_coin_entry/src/prefix.rs | 6 ++---- rust/tw_coin_entry/src/test_utils/mod.rs | 6 ++---- rust/tw_coin_entry/src/test_utils/test_context.rs | 6 ++---- rust/tw_coin_registry/build.rs | 6 ++---- rust/tw_coin_registry/src/blockchain_type.rs | 6 ++---- rust/tw_coin_registry/src/coin_context.rs | 6 ++---- rust/tw_coin_registry/src/dispatcher.rs | 6 ++---- rust/tw_coin_registry/src/error.rs | 6 ++---- rust/tw_coin_registry/src/lib.rs | 6 ++---- rust/tw_coin_registry/src/registry.rs | 6 ++---- rust/tw_cosmos_sdk/build.rs | 6 ++---- rust/tw_cosmos_sdk/src/address.rs | 6 ++---- rust/tw_cosmos_sdk/src/context.rs | 6 ++---- rust/tw_cosmos_sdk/src/hasher/keccak256_hasher.rs | 6 ++---- rust/tw_cosmos_sdk/src/hasher/mod.rs | 6 ++---- rust/tw_cosmos_sdk/src/hasher/sha256_hasher.rs | 6 ++---- rust/tw_cosmos_sdk/src/lib.rs | 6 ++---- rust/tw_cosmos_sdk/src/modules/broadcast_msg.rs | 6 ++---- .../tw_cosmos_sdk/src/modules/compiler/json_preimager.rs | 6 ++---- rust/tw_cosmos_sdk/src/modules/compiler/mod.rs | 6 ++---- .../src/modules/compiler/protobuf_preimager.rs | 6 ++---- rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs | 6 ++---- rust/tw_cosmos_sdk/src/modules/mod.rs | 6 ++---- .../src/modules/serializer/json_serializer.rs | 6 ++---- rust/tw_cosmos_sdk/src/modules/serializer/mod.rs | 6 ++---- .../src/modules/serializer/protobuf_serializer.rs | 6 ++---- rust/tw_cosmos_sdk/src/modules/signer/mod.rs | 6 ++---- rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs | 6 ++---- rust/tw_cosmos_sdk/src/modules/tx_builder.rs | 6 ++---- rust/tw_cosmos_sdk/src/private_key/mod.rs | 6 ++---- rust/tw_cosmos_sdk/src/private_key/secp256k1.rs | 6 ++---- rust/tw_cosmos_sdk/src/public_key/mod.rs | 6 ++---- rust/tw_cosmos_sdk/src/public_key/secp256k1.rs | 6 ++---- rust/tw_cosmos_sdk/src/signature/mod.rs | 6 ++---- rust/tw_cosmos_sdk/src/signature/secp256k1.rs | 6 ++---- rust/tw_cosmos_sdk/src/test_utils/mod.rs | 6 ++---- rust/tw_cosmos_sdk/src/test_utils/proto_utils.rs | 6 ++---- rust/tw_cosmos_sdk/src/test_utils/sign_utils.rs | 6 ++---- .../src/transaction/message/cosmos_auth_message.rs | 6 ++---- .../src/transaction/message/cosmos_bank_message.rs | 6 ++---- .../src/transaction/message/cosmos_generic_message.rs | 6 ++---- .../src/transaction/message/cosmos_gov_message.rs | 6 ++---- .../src/transaction/message/cosmos_staking_message.rs | 6 ++---- .../tw_cosmos_sdk/src/transaction/message/ibc_message.rs | 6 ++---- rust/tw_cosmos_sdk/src/transaction/message/mod.rs | 6 ++---- .../src/transaction/message/stride_message.rs | 6 ++---- .../src/transaction/message/terra_wasm_message.rs | 6 ++---- .../src/transaction/message/thorchain_message.rs | 6 ++---- .../src/transaction/message/wasm_message.rs | 6 ++---- rust/tw_cosmos_sdk/src/transaction/mod.rs | 6 ++---- rust/tw_cosmos_sdk/tests/compile.rs | 6 ++---- rust/tw_cosmos_sdk/tests/sign.rs | 6 ++---- rust/tw_cosmos_sdk/tests/sign_staking.rs | 6 ++---- rust/tw_cosmos_sdk/tests/sign_stride.rs | 6 ++---- rust/tw_cosmos_sdk/tests/sign_terra_wasm.rs | 6 ++---- rust/tw_cosmos_sdk/tests/sign_thorchain.rs | 6 ++---- rust/tw_cosmos_sdk/tests/sign_wasm_contract.rs | 6 ++---- rust/tw_encoding/src/base32.rs | 6 ++---- rust/tw_encoding/src/base58.rs | 6 ++---- rust/tw_encoding/src/base64.rs | 6 ++---- rust/tw_encoding/src/bcs.rs | 6 ++---- rust/tw_encoding/src/bech32.rs | 6 ++---- rust/tw_encoding/src/ffi.rs | 6 ++---- rust/tw_encoding/src/hex.rs | 6 ++---- rust/tw_encoding/src/lib.rs | 6 ++---- rust/tw_encoding/tests/base32_ffi_tests.rs | 6 ++---- rust/tw_encoding/tests/base58_ffi_tests.rs | 6 ++---- rust/tw_encoding/tests/base64_ffi_tests.rs | 6 ++---- rust/tw_encoding/tests/hex_ffi_tests.rs | 6 ++---- rust/tw_ethereum/src/entry.rs | 6 ++---- rust/tw_ethereum/src/lib.rs | 6 ++---- rust/tw_ethereum/tests/compiler.rs | 6 ++---- rust/tw_ethereum/tests/signer.rs | 6 ++---- rust/tw_evm/fuzz/fuzz_targets/abi_decode_value.rs | 6 ++---- rust/tw_evm/fuzz/fuzz_targets/abi_encode_function.rs | 6 ++---- .../fuzz/fuzz_targets/abi_function_decode_input.rs | 6 ++---- rust/tw_evm/fuzz/fuzz_targets/rlp_encode.rs | 6 ++---- rust/tw_evm/fuzz/fuzz_targets/sign.rs | 6 ++---- rust/tw_evm/src/abi/contract.rs | 6 ++---- rust/tw_evm/src/abi/decode.rs | 6 ++---- rust/tw_evm/src/abi/encode.rs | 6 ++---- rust/tw_evm/src/abi/function.rs | 6 ++---- rust/tw_evm/src/abi/mod.rs | 6 ++---- rust/tw_evm/src/abi/non_empty_array.rs | 6 ++---- rust/tw_evm/src/abi/param.rs | 6 ++---- rust/tw_evm/src/abi/param_token.rs | 6 ++---- rust/tw_evm/src/abi/param_type/constructor.rs | 6 ++---- rust/tw_evm/src/abi/param_type/mod.rs | 6 ++---- rust/tw_evm/src/abi/param_type/reader.rs | 6 ++---- rust/tw_evm/src/abi/param_type/writer.rs | 6 ++---- rust/tw_evm/src/abi/prebuild/erc1155.rs | 6 ++---- rust/tw_evm/src/abi/prebuild/erc20.rs | 6 ++---- rust/tw_evm/src/abi/prebuild/erc4337.rs | 6 ++---- rust/tw_evm/src/abi/prebuild/erc721.rs | 6 ++---- rust/tw_evm/src/abi/prebuild/mod.rs | 6 ++---- rust/tw_evm/src/abi/signature.rs | 6 ++---- rust/tw_evm/src/abi/token.rs | 6 ++---- rust/tw_evm/src/abi/uint.rs | 6 ++---- rust/tw_evm/src/address.rs | 6 ++---- rust/tw_evm/src/evm_context.rs | 6 ++---- rust/tw_evm/src/evm_entry.rs | 6 ++---- rust/tw_evm/src/lib.rs | 6 ++---- rust/tw_evm/src/message/eip191.rs | 6 ++---- rust/tw_evm/src/message/eip712/eip712_message.rs | 6 ++---- rust/tw_evm/src/message/eip712/message_types.rs | 6 ++---- rust/tw_evm/src/message/eip712/mod.rs | 6 ++---- rust/tw_evm/src/message/eip712/property.rs | 6 ++---- rust/tw_evm/src/message/mod.rs | 6 ++---- rust/tw_evm/src/message/signature.rs | 6 ++---- rust/tw_evm/src/modules/abi_encoder.rs | 6 ++---- rust/tw_evm/src/modules/compiler.rs | 6 ++---- rust/tw_evm/src/modules/json_signer.rs | 6 ++---- rust/tw_evm/src/modules/message_signer.rs | 6 ++---- rust/tw_evm/src/modules/mod.rs | 6 ++---- rust/tw_evm/src/modules/rlp_encoder.rs | 6 ++---- rust/tw_evm/src/modules/signer.rs | 6 ++---- rust/tw_evm/src/modules/tx_builder.rs | 6 ++---- rust/tw_evm/src/rlp/buffer.rs | 6 ++---- rust/tw_evm/src/rlp/impls.rs | 6 ++---- rust/tw_evm/src/rlp/list.rs | 6 ++---- rust/tw_evm/src/rlp/mod.rs | 6 ++---- rust/tw_evm/src/signature.rs | 6 ++---- rust/tw_evm/src/transaction/mod.rs | 6 ++---- rust/tw_evm/src/transaction/signature.rs | 6 ++---- rust/tw_evm/src/transaction/transaction_eip1559.rs | 6 ++---- rust/tw_evm/src/transaction/transaction_non_typed.rs | 6 ++---- rust/tw_evm/src/transaction/user_operation.rs | 6 ++---- rust/tw_evm/tests/abi_encoder.rs | 6 ++---- rust/tw_evm/tests/barz.rs | 6 ++---- rust/tw_evm/tests/message_signer.rs | 6 ++---- rust/tw_evm/tests/rlp.rs | 6 ++---- rust/tw_evm/tests/signer.rs | 6 ++---- rust/tw_hash/fuzz/fuzz_targets/hash_fuzz.rs | 6 ++---- rust/tw_hash/src/blake.rs | 6 ++---- rust/tw_hash/src/blake2.rs | 6 ++---- rust/tw_hash/src/crc32.rs | 6 ++---- rust/tw_hash/src/ffi.rs | 6 ++---- rust/tw_hash/src/groestl.rs | 6 ++---- rust/tw_hash/src/hash_array.rs | 6 ++---- rust/tw_hash/src/hash_wrapper.rs | 6 ++---- rust/tw_hash/src/hasher.rs | 6 ++---- rust/tw_hash/src/hmac.rs | 6 ++---- rust/tw_hash/src/lib.rs | 6 ++---- rust/tw_hash/src/ripemd.rs | 6 ++---- rust/tw_hash/src/sha1.rs | 6 ++---- rust/tw_hash/src/sha2.rs | 6 ++---- rust/tw_hash/src/sha3.rs | 6 ++---- rust/tw_hash/tests/hash_ffi_tests.rs | 6 ++---- rust/tw_internet_computer/build.rs | 6 ++---- rust/tw_internet_computer/src/address.rs | 6 ++---- rust/tw_internet_computer/src/context.rs | 6 ++---- rust/tw_internet_computer/src/entry.rs | 6 ++---- rust/tw_internet_computer/src/lib.rs | 6 ++---- rust/tw_internet_computer/src/protocol/envelope.rs | 6 ++---- rust/tw_internet_computer/src/protocol/identity.rs | 6 ++---- rust/tw_internet_computer/src/protocol/mod.rs | 6 ++---- rust/tw_internet_computer/src/protocol/principal.rs | 6 ++---- rust/tw_internet_computer/src/protocol/request_id.rs | 6 ++---- rust/tw_internet_computer/src/protocol/rosetta.rs | 6 ++---- rust/tw_internet_computer/src/signer.rs | 6 ++---- rust/tw_internet_computer/src/transactions/mod.rs | 6 ++---- .../src/transactions/proto/ledger.proto | 6 ++---- .../src/transactions/proto/types.proto | 6 ++---- rust/tw_internet_computer/src/transactions/transfer.rs | 6 ++---- rust/tw_keypair/fuzz/fuzz_targets/tw_private_sign.rs | 6 ++---- .../tw_keypair/fuzz/fuzz_targets/tw_private_to_public.rs | 6 ++---- rust/tw_keypair/fuzz/fuzz_targets/tw_public_verify.rs | 6 ++---- rust/tw_keypair/src/ecdsa/canonical.rs | 6 ++---- rust/tw_keypair/src/ecdsa/der.rs | 6 ++---- rust/tw_keypair/src/ecdsa/mod.rs | 6 ++---- rust/tw_keypair/src/ecdsa/nist256p1/keypair.rs | 6 ++---- rust/tw_keypair/src/ecdsa/nist256p1/mod.rs | 6 ++---- rust/tw_keypair/src/ecdsa/nist256p1/private.rs | 6 ++---- rust/tw_keypair/src/ecdsa/nist256p1/public.rs | 6 ++---- rust/tw_keypair/src/ecdsa/secp256k1/keypair.rs | 6 ++---- rust/tw_keypair/src/ecdsa/secp256k1/mod.rs | 6 ++---- rust/tw_keypair/src/ecdsa/secp256k1/private.rs | 6 ++---- rust/tw_keypair/src/ecdsa/secp256k1/public.rs | 6 ++---- rust/tw_keypair/src/ecdsa/signature.rs | 6 ++---- rust/tw_keypair/src/ed25519/keypair.rs | 6 ++---- rust/tw_keypair/src/ed25519/mangle.rs | 6 ++---- rust/tw_keypair/src/ed25519/mod.rs | 6 ++---- .../ed25519/modifications/cardano/extended_keypair.rs | 6 ++---- .../ed25519/modifications/cardano/extended_private.rs | 6 ++---- .../src/ed25519/modifications/cardano/extended_public.rs | 6 ++---- rust/tw_keypair/src/ed25519/modifications/cardano/mod.rs | 6 ++---- rust/tw_keypair/src/ed25519/modifications/mod.rs | 6 ++---- .../src/ed25519/modifications/waves/keypair.rs | 6 ++---- rust/tw_keypair/src/ed25519/modifications/waves/mod.rs | 6 ++---- .../src/ed25519/modifications/waves/private.rs | 6 ++---- .../tw_keypair/src/ed25519/modifications/waves/public.rs | 6 ++---- .../src/ed25519/modifications/waves/signature.rs | 6 ++---- rust/tw_keypair/src/ed25519/private.rs | 6 ++---- rust/tw_keypair/src/ed25519/public.rs | 6 ++---- rust/tw_keypair/src/ed25519/secret.rs | 6 ++---- rust/tw_keypair/src/ed25519/signature.rs | 6 ++---- rust/tw_keypair/src/ffi/asn.rs | 6 ++---- rust/tw_keypair/src/ffi/mod.rs | 6 ++---- rust/tw_keypair/src/ffi/privkey.rs | 6 ++---- rust/tw_keypair/src/ffi/pubkey.rs | 6 ++---- rust/tw_keypair/src/lib.rs | 6 ++---- rust/tw_keypair/src/starkex/keypair.rs | 6 ++---- rust/tw_keypair/src/starkex/mod.rs | 6 ++---- rust/tw_keypair/src/starkex/private.rs | 6 ++---- rust/tw_keypair/src/starkex/public.rs | 6 ++---- rust/tw_keypair/src/starkex/signature.rs | 6 ++---- rust/tw_keypair/src/test_utils/mod.rs | 6 ++---- rust/tw_keypair/src/test_utils/tw_private_key_helper.rs | 6 ++---- rust/tw_keypair/src/test_utils/tw_public_key_helper.rs | 6 ++---- rust/tw_keypair/src/traits.rs | 6 ++---- rust/tw_keypair/src/tw/mod.rs | 6 ++---- rust/tw_keypair/src/tw/private.rs | 6 ++---- rust/tw_keypair/src/tw/public.rs | 6 ++---- rust/tw_keypair/tests/asn_parser_ffi_tests.rs | 6 ++---- rust/tw_keypair/tests/ed25519_blake2b_tests.rs | 6 ++---- rust/tw_keypair/tests/ed25519_extended_cardano_tests.rs | 6 ++---- rust/tw_keypair/tests/ed25519_tests.rs | 6 ++---- rust/tw_keypair/tests/ed25519_waves_tests.rs | 6 ++---- rust/tw_keypair/tests/nist256p1_tests.rs | 6 ++---- rust/tw_keypair/tests/private_key_ffi_tests.rs | 6 ++---- rust/tw_keypair/tests/public_key_ffi_tests.rs | 6 ++---- rust/tw_keypair/tests/secp256k1_tests.rs | 6 ++---- rust/tw_keypair/tests/tw_keypair_starkex_tests.rs | 6 ++---- rust/tw_memory/src/ffi/c_byte_array.rs | 6 ++---- rust/tw_memory/src/ffi/c_byte_array_ref.rs | 6 ++---- rust/tw_memory/src/ffi/c_result.rs | 6 ++---- rust/tw_memory/src/ffi/mod.rs | 6 ++---- rust/tw_memory/src/ffi/tw_data.rs | 6 ++---- rust/tw_memory/src/ffi/tw_data_vector.rs | 6 ++---- rust/tw_memory/src/ffi/tw_string.rs | 6 ++---- rust/tw_memory/src/lib.rs | 6 ++---- rust/tw_memory/src/test_utils/mod.rs | 6 ++---- rust/tw_memory/src/test_utils/tw_data_helper.rs | 6 ++---- rust/tw_memory/src/test_utils/tw_data_vector_helper.rs | 6 ++---- rust/tw_memory/src/test_utils/tw_string_helper.rs | 6 ++---- rust/tw_memory/src/test_utils/tw_wrapper.rs | 6 ++---- rust/tw_memory/tests/c_byte_array_ffi_tests.rs | 6 ++---- rust/tw_memory/tests/c_result_ffi_tests.rs | 6 ++---- rust/tw_memory/tests/string_ffi_tests.rs | 6 ++---- rust/tw_misc/src/lib.rs | 6 ++---- rust/tw_misc/src/macros.rs | 6 ++---- rust/tw_misc/src/serde.rs | 6 ++---- rust/tw_misc/src/test_utils/json.rs | 6 ++---- rust/tw_misc/src/test_utils/mod.rs | 6 ++---- rust/tw_misc/src/traits.rs | 6 ++---- rust/tw_number/src/i256.rs | 6 ++---- rust/tw_number/src/lib.rs | 6 ++---- rust/tw_number/src/sign.rs | 6 ++---- rust/tw_number/src/u256.rs | 6 ++---- rust/tw_number/tests/u256.rs | 6 ++---- rust/tw_proto/build.rs | 6 ++---- rust/tw_proto/src/common/google/mod.rs | 6 ++---- rust/tw_proto/src/common/google/protobuf/mod.rs | 6 ++---- rust/tw_proto/src/common/mod.rs | 6 ++---- rust/tw_proto/src/ffi.rs | 6 ++---- rust/tw_proto/src/lib.rs | 6 ++---- rust/tw_proto/tests/proto_ffi_tests.rs | 6 ++---- rust/tw_proto/tests/proto_tests.rs | 6 ++---- rust/tw_ronin/src/address.rs | 6 ++---- rust/tw_ronin/src/entry.rs | 6 ++---- rust/tw_ronin/src/lib.rs | 6 ++---- rust/tw_ronin/src/ronin_context.rs | 6 ++---- rust/tw_ronin/tests/address.rs | 6 ++---- rust/tw_ronin/tests/compiler.rs | 6 ++---- rust/tw_ronin/tests/rlp.rs | 6 ++---- rust/tw_ronin/tests/signer.rs | 6 ++---- rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs | 6 ++---- rust/wallet_core_rs/src/ffi/bitcoin/mod.rs | 6 ++---- rust/wallet_core_rs/src/ffi/ethereum/abi.rs | 6 ++---- rust/wallet_core_rs/src/ffi/ethereum/mod.rs | 6 ++---- rust/wallet_core_rs/src/ffi/ethereum/rlp.rs | 6 ++---- rust/wallet_core_rs/src/ffi/mod.rs | 6 ++---- rust/wallet_core_rs/src/lib.rs | 6 ++---- rust/wallet_core_rs/tests/ethereum_abi.rs | 6 ++---- rust/wallet_core_rs/tests/ethereum_rlp.rs | 6 ++---- samples/cpp/CMakeLists.txt | 6 ++---- samples/cpp/sample.cpp | 6 ++---- .../osx/cocoapods/WalletCoreExample/AppDelegate.swift | 6 ++---- .../osx/cocoapods/WalletCoreExample/ViewController.swift | 6 ++---- samples/rust/src/build.rs | 6 ++---- samples/rust/src/main.rs | 6 ++---- samples/rust/src/walletcore_extra.rs | 6 ++---- samples/rust/src/walletcore_iface.rs | 6 ++---- src/Aeternity/Address.cpp | 6 ++---- src/Aeternity/Address.h | 6 ++---- src/Aeternity/Entry.cpp | 6 ++---- src/Aeternity/Entry.h | 6 ++---- src/Aeternity/Identifiers.h | 6 ++---- src/Aeternity/Signer.cpp | 6 ++---- src/Aeternity/Signer.h | 6 ++---- src/Aeternity/Transaction.cpp | 6 ++---- src/Aeternity/Transaction.h | 6 ++---- src/Aion/Address.cpp | 6 ++---- src/Aion/Address.h | 6 ++---- src/Aion/Entry.cpp | 6 ++---- src/Aion/Entry.h | 6 ++---- src/Aion/RLP.h | 6 ++---- src/Aion/Signer.cpp | 6 ++---- src/Aion/Signer.h | 6 ++---- src/Aion/Transaction.cpp | 6 ++---- src/Aion/Transaction.h | 6 ++---- src/Algorand/Address.cpp | 6 ++---- src/Algorand/Address.h | 6 ++---- src/Algorand/AssetTransfer.cpp | 6 ++---- src/Algorand/AssetTransfer.h | 6 ++---- src/Algorand/BaseTransaction.h | 6 ++---- src/Algorand/BinaryCoding.h | 6 ++---- src/Algorand/Entry.cpp | 6 ++---- src/Algorand/Entry.h | 6 ++---- src/Algorand/OptInAssetTransaction.cpp | 6 ++---- src/Algorand/OptInAssetTransaction.h | 6 ++---- src/Algorand/Signer.cpp | 6 ++---- src/Algorand/Signer.h | 6 ++---- src/Algorand/Transfer.cpp | 6 ++---- src/Algorand/Transfer.h | 6 ++---- src/AnyAddress.cpp | 6 ++---- src/AnyAddress.h | 6 ++---- src/Aptos/Entry.h | 6 ++---- src/AsnParser.h | 6 ++---- src/Base32.h | 6 ++---- src/Base58.h | 6 ++---- src/Base58Address.h | 6 ++---- src/Base64.cpp | 6 ++---- src/Base64.h | 6 ++---- src/Bech32.cpp | 6 ++---- src/Bech32.h | 6 ++---- src/Bech32Address.cpp | 6 ++---- src/Bech32Address.h | 6 ++---- src/Binance/Address.cpp | 6 ++---- src/Binance/Address.h | 6 ++---- src/Binance/Entry.cpp | 6 ++---- src/Binance/Entry.h | 6 ++---- src/BinaryCoding.cpp | 6 ++---- src/BinaryCoding.h | 6 ++---- src/Bitcoin/Address.h | 6 ++---- src/Bitcoin/Amount.h | 6 ++---- src/Bitcoin/CashAddress.cpp | 6 ++---- src/Bitcoin/CashAddress.h | 6 ++---- src/Bitcoin/Entry.cpp | 6 ++---- src/Bitcoin/Entry.h | 6 ++---- src/Bitcoin/FeeCalculator.cpp | 6 ++---- src/Bitcoin/FeeCalculator.h | 6 ++---- src/Bitcoin/InputSelector.cpp | 6 ++---- src/Bitcoin/InputSelector.h | 6 ++---- src/Bitcoin/MessageSigner.cpp | 6 ++---- src/Bitcoin/MessageSigner.h | 6 ++---- src/Bitcoin/OpCodes.h | 6 ++---- src/Bitcoin/OutPoint.cpp | 6 ++---- src/Bitcoin/OutPoint.h | 6 ++---- src/Bitcoin/Script.cpp | 6 ++---- src/Bitcoin/Script.h | 6 ++---- src/Bitcoin/SegwitAddress.cpp | 6 ++---- src/Bitcoin/SegwitAddress.h | 6 ++---- src/Bitcoin/SigHashType.h | 6 ++---- src/Bitcoin/SignatureBuilder.cpp | 6 ++---- src/Bitcoin/SignatureBuilder.h | 6 ++---- src/Bitcoin/SignatureVersion.h | 6 ++---- src/Bitcoin/Signer.cpp | 6 ++---- src/Bitcoin/Signer.h | 6 ++---- src/Bitcoin/SigningInput.cpp | 6 ++---- src/Bitcoin/SigningInput.h | 6 ++---- src/Bitcoin/Transaction.cpp | 6 ++---- src/Bitcoin/Transaction.h | 6 ++---- src/Bitcoin/TransactionBuilder.cpp | 6 ++---- src/Bitcoin/TransactionBuilder.h | 6 ++---- src/Bitcoin/TransactionInput.cpp | 6 ++---- src/Bitcoin/TransactionInput.h | 6 ++---- src/Bitcoin/TransactionOutput.cpp | 6 ++---- src/Bitcoin/TransactionOutput.h | 6 ++---- src/Bitcoin/TransactionPlan.h | 6 ++---- src/Bitcoin/TransactionSigner.cpp | 6 ++---- src/Bitcoin/TransactionSigner.h | 6 ++---- src/Bitcoin/UTXO.h | 6 ++---- src/BitcoinDiamond/Entry.cpp | 6 ++---- src/BitcoinDiamond/Entry.h | 6 ++---- src/BitcoinDiamond/Signer.cpp | 6 ++---- src/BitcoinDiamond/Signer.h | 6 ++---- src/BitcoinDiamond/Transaction.cpp | 6 ++---- src/BitcoinDiamond/Transaction.h | 6 ++---- src/BitcoinDiamond/TransactionBuilder.h | 6 ++---- src/Cardano/AddressV2.cpp | 6 ++---- src/Cardano/AddressV2.h | 6 ++---- src/Cardano/AddressV3.cpp | 6 ++---- src/Cardano/AddressV3.h | 6 ++---- src/Cardano/Entry.cpp | 6 ++---- src/Cardano/Entry.h | 6 ++---- src/Cardano/Signer.cpp | 6 ++---- src/Cardano/Signer.h | 6 ++---- src/Cardano/Transaction.cpp | 6 ++---- src/Cardano/Transaction.h | 6 ++---- src/Cbor.cpp | 6 ++---- src/Cbor.h | 6 ++---- src/Coin.cpp | 6 ++---- src/Coin.h | 6 ++---- src/CoinEntry.cpp | 6 ++---- src/CoinEntry.h | 6 ++---- src/Cosmos/Address.h | 6 ++---- src/Cosmos/Entry.cpp | 6 ++---- src/Cosmos/Entry.h | 6 ++---- src/Crc.cpp | 6 ++---- src/Crc.h | 6 ++---- src/Data.cpp | 6 ++---- src/Data.h | 6 ++---- src/Decred/Address.cpp | 6 ++---- src/Decred/Address.h | 6 ++---- src/Decred/Entry.cpp | 6 ++---- src/Decred/Entry.h | 6 ++---- src/Decred/OutPoint.cpp | 6 ++---- src/Decred/OutPoint.h | 6 ++---- src/Decred/Signer.cpp | 6 ++---- src/Decred/Signer.h | 6 ++---- src/Decred/Transaction.cpp | 6 ++---- src/Decred/Transaction.h | 6 ++---- src/Decred/TransactionBuilder.h | 6 ++---- src/Decred/TransactionInput.cpp | 6 ++---- src/Decred/TransactionInput.h | 6 ++---- src/Decred/TransactionOutput.cpp | 6 ++---- src/Decred/TransactionOutput.h | 6 ++---- src/Defer.h | 6 ++---- src/DerivationPath.cpp | 6 ++---- src/DerivationPath.h | 6 ++---- src/EOS/Action.cpp | 6 ++---- src/EOS/Action.h | 6 ++---- src/EOS/Address.cpp | 6 ++---- src/EOS/Address.h | 6 ++---- src/EOS/Asset.cpp | 6 ++---- src/EOS/Asset.h | 6 ++---- src/EOS/Entry.cpp | 6 ++---- src/EOS/Entry.h | 6 ++---- src/EOS/KeyType.h | 6 ++---- src/EOS/Name.cpp | 6 ++---- src/EOS/Name.h | 6 ++---- src/EOS/PackedTransaction.cpp | 6 ++---- src/EOS/PackedTransaction.h | 6 ++---- src/EOS/Prefixes.h | 6 ++---- src/EOS/Signer.cpp | 6 ++---- src/EOS/Signer.h | 6 ++---- src/EOS/Transaction.cpp | 6 ++---- src/EOS/Transaction.h | 6 ++---- src/Encrypt.cpp | 6 ++---- src/Encrypt.h | 6 ++---- src/Ethereum/ABI/Function.cpp | 6 ++---- src/Ethereum/ABI/Function.h | 6 ++---- src/Ethereum/ABI/ProtoParam.h | 6 ++---- src/Ethereum/ABI/ValueDecoder.cpp | 6 ++---- src/Ethereum/ABI/ValueDecoder.h | 6 ++---- src/Ethereum/ABI/ValueEncoder.cpp | 6 ++---- src/Ethereum/ABI/ValueEncoder.h | 6 ++---- src/Ethereum/Address.cpp | 6 ++---- src/Ethereum/Address.h | 6 ++---- src/Ethereum/AddressChecksum.cpp | 6 ++---- src/Ethereum/AddressChecksum.h | 6 ++---- src/Ethereum/Barz.cpp | 6 ++---- src/Ethereum/Barz.h | 6 ++---- src/Ethereum/ContractCall.cpp | 6 ++---- src/Ethereum/ContractCall.h | 6 ++---- src/Ethereum/EIP1014.cpp | 6 ++---- src/Ethereum/EIP1014.h | 6 ++---- src/Ethereum/EIP1967.cpp | 6 ++---- src/Ethereum/EIP1967.h | 6 ++---- src/Ethereum/EIP2645.cpp | 6 ++---- src/Ethereum/EIP2645.h | 6 ++---- src/Ethereum/Entry.cpp | 6 ++---- src/Ethereum/Entry.h | 6 ++---- src/Ethereum/MessageSigner.cpp | 6 ++---- src/Ethereum/MessageSigner.h | 6 ++---- src/Ethereum/RLP.cpp | 6 ++---- src/Ethereum/RLP.h | 6 ++---- src/Everscale/Address.cpp | 6 ++---- src/Everscale/Address.h | 6 ++---- src/Everscale/CommonTON/Cell.cpp | 6 ++---- src/Everscale/CommonTON/Cell.h | 6 ++---- src/Everscale/CommonTON/CellBuilder.cpp | 6 ++---- src/Everscale/CommonTON/CellBuilder.h | 6 ++---- src/Everscale/CommonTON/CellSlice.cpp | 6 ++---- src/Everscale/CommonTON/CellSlice.h | 6 ++---- src/Everscale/CommonTON/Messages.h | 6 ++---- src/Everscale/CommonTON/RawAddress.cpp | 6 ++---- src/Everscale/CommonTON/RawAddress.h | 6 ++---- src/Everscale/CommonTON/WorkchainType.h | 6 ++---- src/Everscale/Entry.cpp | 6 ++---- src/Everscale/Entry.h | 6 ++---- src/Everscale/Messages.cpp | 6 ++---- src/Everscale/Messages.h | 6 ++---- src/Everscale/Signer.cpp | 6 ++---- src/Everscale/Signer.h | 6 ++---- src/Everscale/Wallet.cpp | 6 ++---- src/Everscale/Wallet.h | 6 ++---- src/Everscale/WorkchainType.h | 6 ++---- src/FIO/Action.cpp | 6 ++---- src/FIO/Action.h | 6 ++---- src/FIO/Actor.cpp | 6 ++---- src/FIO/Actor.h | 6 ++---- src/FIO/Address.cpp | 6 ++---- src/FIO/Address.h | 6 ++---- src/FIO/Encryption.cpp | 6 ++---- src/FIO/Encryption.h | 6 ++---- src/FIO/Entry.cpp | 6 ++---- src/FIO/Entry.h | 6 ++---- src/FIO/NewFundsRequest.cpp | 6 ++---- src/FIO/NewFundsRequest.h | 6 ++---- src/FIO/Signer.cpp | 6 ++---- src/FIO/Signer.h | 6 ++---- src/FIO/Transaction.cpp | 6 ++---- src/FIO/Transaction.h | 6 ++---- src/FIO/TransactionBuilder.cpp | 6 ++---- src/FIO/TransactionBuilder.h | 6 ++---- src/Filecoin/Address.cpp | 6 ++---- src/Filecoin/Address.h | 6 ++---- src/Filecoin/AddressConverter.cpp | 6 ++---- src/Filecoin/AddressConverter.h | 6 ++---- src/Filecoin/Entry.cpp | 6 ++---- src/Filecoin/Entry.h | 6 ++---- src/Filecoin/Signer.cpp | 6 ++---- src/Filecoin/Signer.h | 6 ++---- src/Filecoin/Transaction.cpp | 6 ++---- src/Filecoin/Transaction.h | 6 ++---- src/FullSS58Address.h | 6 ++---- src/Greenfield/Entry.h | 6 ++---- src/Groestlcoin/Address.cpp | 6 ++---- src/Groestlcoin/Address.h | 6 ++---- src/Groestlcoin/Entry.cpp | 6 ++---- src/Groestlcoin/Entry.h | 6 ++---- src/Groestlcoin/Signer.cpp | 6 ++---- src/Groestlcoin/Signer.h | 6 ++---- src/Groestlcoin/Transaction.h | 6 ++---- src/HDWallet.cpp | 6 ++---- src/HDWallet.h | 6 ++---- src/Harmony/Address.cpp | 6 ++---- src/Harmony/Address.h | 6 ++---- src/Harmony/Entry.cpp | 6 ++---- src/Harmony/Entry.h | 6 ++---- src/Harmony/Signer.cpp | 6 ++---- src/Harmony/Signer.h | 6 ++---- src/Harmony/Staking.cpp | 6 ++---- src/Harmony/Staking.h | 6 ++---- src/Harmony/Transaction.h | 6 ++---- src/Hash.cpp | 6 ++---- src/Hash.h | 6 ++---- src/Hedera/Address.cpp | 6 ++---- src/Hedera/Address.h | 6 ++---- src/Hedera/DER.cpp | 6 ++---- src/Hedera/DER.h | 6 ++---- src/Hedera/Entry.cpp | 6 ++---- src/Hedera/Entry.h | 6 ++---- src/Hedera/Signer.cpp | 6 ++---- src/Hedera/Signer.h | 6 ++---- src/HexCoding.h | 6 ++---- src/IOST/Account.cpp | 6 ++---- src/IOST/Account.h | 6 ++---- src/IOST/Entry.cpp | 6 ++---- src/IOST/Entry.h | 6 ++---- src/IOST/Signer.cpp | 6 ++---- src/IOST/Signer.h | 6 ++---- src/Icon/Address.cpp | 6 ++---- src/Icon/Address.h | 6 ++---- src/Icon/AddressType.h | 6 ++---- src/Icon/Entry.cpp | 6 ++---- src/Icon/Entry.h | 6 ++---- src/Icon/Signer.cpp | 6 ++---- src/Icon/Signer.h | 6 ++---- src/ImmutableX/Constants.h | 6 ++---- src/ImmutableX/StarkKey.cpp | 6 ++---- src/ImmutableX/StarkKey.h | 6 ++---- src/InternetComputer/Entry.h | 6 ++---- src/IoTeX/Address.cpp | 6 ++---- src/IoTeX/Address.h | 6 ++---- src/IoTeX/Entry.cpp | 6 ++---- src/IoTeX/Entry.h | 6 ++---- src/IoTeX/Signer.cpp | 6 ++---- src/IoTeX/Signer.h | 6 ++---- src/IoTeX/Staking.cpp | 6 ++---- src/IoTeX/Staking.h | 6 ++---- src/KeyPair.h | 6 ++---- src/Keystore/AESParameters.cpp | 6 ++---- src/Keystore/AESParameters.h | 6 ++---- src/Keystore/Account.cpp | 6 ++---- src/Keystore/Account.h | 6 ++---- src/Keystore/EncryptionParameters.cpp | 6 ++---- src/Keystore/EncryptionParameters.h | 6 ++---- src/Keystore/PBKDF2Parameters.cpp | 6 ++---- src/Keystore/PBKDF2Parameters.h | 6 ++---- src/Keystore/ScryptParameters.cpp | 6 ++---- src/Keystore/ScryptParameters.h | 6 ++---- src/Keystore/StoredKey.cpp | 6 ++---- src/Keystore/StoredKey.h | 6 ++---- src/Kusama/Address.h | 6 ++---- src/Kusama/Entry.cpp | 6 ++---- src/Kusama/Entry.h | 6 ++---- src/LiquidStaking/LiquidStaking.cpp | 6 ++---- src/LiquidStaking/LiquidStaking.h | 6 ++---- src/LiquidStaking/TWLiquidStaking.cpp | 6 ++---- src/Mnemonic.cpp | 6 ++---- src/Mnemonic.h | 6 ++---- src/Move/Address.h | 6 ++---- src/MultiversX/Address.cpp | 6 ++---- src/MultiversX/Address.h | 6 ++---- src/MultiversX/Codec.cpp | 6 ++---- src/MultiversX/Codec.h | 6 ++---- src/MultiversX/Entry.cpp | 6 ++---- src/MultiversX/Entry.h | 6 ++---- src/MultiversX/Serialization.cpp | 6 ++---- src/MultiversX/Serialization.h | 6 ++---- src/MultiversX/Signer.cpp | 6 ++---- src/MultiversX/Signer.h | 6 ++---- src/MultiversX/Transaction.cpp | 6 ++---- src/MultiversX/Transaction.h | 6 ++---- src/MultiversX/TransactionFactory.cpp | 6 ++---- src/MultiversX/TransactionFactory.h | 6 ++---- src/MultiversX/TransactionFactoryConfig.cpp | 6 ++---- src/MultiversX/TransactionFactoryConfig.h | 6 ++---- src/NEAR/Account.cpp | 6 ++---- src/NEAR/Account.h | 6 ++---- src/NEAR/Address.cpp | 6 ++---- src/NEAR/Address.h | 6 ++---- src/NEAR/Entry.cpp | 6 ++---- src/NEAR/Entry.h | 6 ++---- src/NEAR/Serialization.cpp | 6 ++---- src/NEAR/Serialization.h | 6 ++---- src/NEAR/Signer.cpp | 6 ++---- src/NEAR/Signer.h | 6 ++---- src/NEO/Address.cpp | 6 ++---- src/NEO/Address.h | 6 ++---- src/NEO/BinaryCoding.h | 6 ++---- src/NEO/CoinReference.h | 6 ++---- src/NEO/Constants.h | 6 ++---- src/NEO/Entry.cpp | 6 ++---- src/NEO/Entry.h | 6 ++---- src/NEO/ISerializable.h | 6 ++---- src/NEO/InvocationTransaction.h | 6 ++---- src/NEO/MinerTransaction.h | 6 ++---- src/NEO/OpCode.h | 6 ++---- src/NEO/ReadData.cpp | 6 ++---- src/NEO/ReadData.h | 6 ++---- src/NEO/Script.cpp | 6 ++---- src/NEO/Script.h | 6 ++---- src/NEO/Serializable.h | 6 ++---- src/NEO/Signer.cpp | 6 ++---- src/NEO/Signer.h | 6 ++---- src/NEO/Transaction.cpp | 6 ++---- src/NEO/Transaction.h | 6 ++---- src/NEO/TransactionAttribute.h | 6 ++---- src/NEO/TransactionAttributeUsage.h | 6 ++---- src/NEO/TransactionOutput.h | 6 ++---- src/NEO/TransactionType.h | 6 ++---- src/NEO/Witness.h | 6 ++---- src/NULS/Address.cpp | 6 ++---- src/NULS/Address.h | 6 ++---- src/NULS/BinaryCoding.h | 6 ++---- src/NULS/Entry.cpp | 6 ++---- src/NULS/Entry.h | 6 ++---- src/NULS/Signer.cpp | 6 ++---- src/NULS/Signer.h | 6 ++---- src/Nano/Address.cpp | 6 ++---- src/Nano/Address.h | 6 ++---- src/Nano/Entry.cpp | 6 ++---- src/Nano/Entry.h | 6 ++---- src/Nano/Signer.cpp | 6 ++---- src/Nano/Signer.h | 6 ++---- src/NativeEvmos/Entry.h | 6 ++---- src/NativeInjective/Entry.h | 6 ++---- src/Nebulas/Address.cpp | 6 ++---- src/Nebulas/Address.h | 6 ++---- src/Nebulas/Entry.cpp | 6 ++---- src/Nebulas/Entry.h | 6 ++---- src/Nebulas/Signer.cpp | 6 ++---- src/Nebulas/Signer.h | 6 ++---- src/Nebulas/Transaction.cpp | 9 ++------- src/Nebulas/Transaction.h | 6 ++---- src/Nervos/Address.cpp | 6 ++---- src/Nervos/Address.h | 6 ++---- src/Nervos/Cell.h | 6 ++---- src/Nervos/CellDep.cpp | 6 ++---- src/Nervos/CellDep.h | 6 ++---- src/Nervos/CellInput.cpp | 6 ++---- src/Nervos/CellInput.h | 6 ++---- src/Nervos/CellOutput.cpp | 6 ++---- src/Nervos/CellOutput.h | 6 ++---- src/Nervos/Constants.h | 6 ++---- src/Nervos/Entry.cpp | 6 ++---- src/Nervos/Entry.h | 6 ++---- src/Nervos/HeaderDep.h | 6 ++---- src/Nervos/OutPoint.cpp | 6 ++---- src/Nervos/OutPoint.h | 6 ++---- src/Nervos/Script.cpp | 6 ++---- src/Nervos/Script.h | 6 ++---- src/Nervos/Serialization.h | 6 ++---- src/Nervos/Signer.cpp | 6 ++---- src/Nervos/Signer.h | 6 ++---- src/Nervos/Transaction.cpp | 6 ++---- src/Nervos/Transaction.h | 6 ++---- src/Nervos/TransactionPlan.cpp | 6 ++---- src/Nervos/TransactionPlan.h | 6 ++---- src/Nervos/Witness.cpp | 6 ++---- src/Nervos/Witness.h | 6 ++---- src/Nimiq/Address.cpp | 6 ++---- src/Nimiq/Address.h | 6 ++---- src/Nimiq/Entry.cpp | 6 ++---- src/Nimiq/Entry.h | 6 ++---- src/Nimiq/Signer.cpp | 6 ++---- src/Nimiq/Signer.h | 6 ++---- src/Nimiq/Transaction.cpp | 6 ++---- src/Nimiq/Transaction.h | 6 ++---- src/Numeric.h | 6 ++---- src/NumericLiteral.h | 6 ++---- src/Oasis/Address.cpp | 6 ++---- src/Oasis/Address.h | 6 ++---- src/Oasis/Entry.cpp | 6 ++---- src/Oasis/Entry.h | 6 ++---- src/Oasis/Signer.cpp | 6 ++---- src/Oasis/Signer.h | 6 ++---- src/Oasis/Transaction.cpp | 6 ++---- src/Oasis/Transaction.h | 6 ++---- src/Ontology/Address.cpp | 6 ++---- src/Ontology/Address.h | 6 ++---- src/Ontology/Asset.h | 6 ++---- src/Ontology/Entry.cpp | 6 ++---- src/Ontology/Entry.h | 6 ++---- src/Ontology/Oep4.cpp | 6 ++---- src/Ontology/Oep4.h | 6 ++---- src/Ontology/Oep4TxBuilder.cpp | 6 ++---- src/Ontology/Oep4TxBuilder.h | 6 ++---- src/Ontology/Ong.cpp | 6 ++---- src/Ontology/Ong.h | 6 ++---- src/Ontology/OngTxBuilder.cpp | 6 ++---- src/Ontology/OngTxBuilder.h | 6 ++---- src/Ontology/Ont.cpp | 6 ++---- src/Ontology/Ont.h | 6 ++---- src/Ontology/OntTxBuilder.cpp | 6 ++---- src/Ontology/OntTxBuilder.h | 6 ++---- src/Ontology/OpCode.h | 6 ++---- src/Ontology/ParamsBuilder.cpp | 6 ++---- src/Ontology/ParamsBuilder.h | 6 ++---- src/Ontology/SigData.cpp | 6 ++---- src/Ontology/SigData.h | 6 ++---- src/Ontology/Signer.cpp | 6 ++---- src/Ontology/Signer.h | 6 ++---- src/Ontology/Transaction.cpp | 6 ++---- src/Ontology/Transaction.h | 6 ++---- src/Polkadot/Address.h | 6 ++---- src/Polkadot/Entry.cpp | 6 ++---- src/Polkadot/Entry.h | 6 ++---- src/Polkadot/Extrinsic.cpp | 6 ++---- src/Polkadot/Extrinsic.h | 6 ++---- src/Polkadot/SS58Address.cpp | 6 ++---- src/Polkadot/SS58Address.h | 6 ++---- src/Polkadot/ScaleCodec.h | 6 ++---- src/Polkadot/Signer.cpp | 6 ++---- src/Polkadot/Signer.h | 6 ++---- src/PrivateKey.cpp | 6 ++---- src/PrivateKey.h | 6 ++---- src/PublicKey.cpp | 6 ++---- src/PublicKey.h | 6 ++---- src/Result.h | 6 ++---- src/Ronin/Entry.h | 6 ++---- src/Solana/AccountMeta.h | 6 ++---- src/Solana/Address.cpp | 6 ++---- src/Solana/Address.h | 6 ++---- src/Solana/AddressLookupTable.h | 6 ++---- src/Solana/CompiledInstruction.cpp | 6 ++---- src/Solana/CompiledInstruction.h | 6 ++---- src/Solana/Constants.h | 6 ++---- src/Solana/Encoding.h | 6 ++---- src/Solana/Entry.cpp | 6 ++---- src/Solana/Entry.h | 6 ++---- src/Solana/Instruction.h | 6 ++---- src/Solana/LegacyMessage.cpp | 6 ++---- src/Solana/LegacyMessage.h | 6 ++---- src/Solana/MessageHeader.h | 6 ++---- src/Solana/Program.cpp | 6 ++---- src/Solana/Program.h | 6 ++---- src/Solana/Signer.cpp | 6 ++---- src/Solana/Signer.h | 6 ++---- src/Solana/Transaction.cpp | 6 ++---- src/Solana/Transaction.h | 6 ++---- src/Solana/V0Message.h | 6 ++---- src/Solana/VersionedMessage.cpp | 6 ++---- src/Solana/VersionedMessage.h | 6 ++---- src/Solana/VersionedTransaction.cpp | 6 ++---- src/Solana/VersionedTransaction.h | 6 ++---- src/StarkEx/MessageSigner.cpp | 6 ++---- src/StarkEx/MessageSigner.h | 6 ++---- src/Stellar/Address.cpp | 6 ++---- src/Stellar/Address.h | 6 ++---- src/Stellar/Entry.cpp | 6 ++---- src/Stellar/Entry.h | 6 ++---- src/Stellar/Signer.cpp | 6 ++---- src/Stellar/Signer.h | 6 ++---- src/Sui/Address.cpp | 6 ++---- src/Sui/Address.h | 6 ++---- src/Sui/Entry.cpp | 6 ++---- src/Sui/Entry.h | 6 ++---- src/Sui/Signer.cpp | 6 ++---- src/Sui/Signer.h | 6 ++---- src/THORChain/Entry.h | 6 ++---- src/THORChain/Swap.cpp | 6 ++---- src/THORChain/Swap.h | 6 ++---- src/THORChain/TWSwap.cpp | 6 ++---- src/Tezos/Address.cpp | 6 ++---- src/Tezos/Address.h | 6 ++---- src/Tezos/BinaryCoding.cpp | 6 ++---- src/Tezos/BinaryCoding.h | 6 ++---- src/Tezos/Entry.cpp | 6 ++---- src/Tezos/Entry.h | 6 ++---- src/Tezos/Forging.cpp | 6 ++---- src/Tezos/Forging.h | 6 ++---- src/Tezos/MessageSigner.cpp | 6 ++---- src/Tezos/MessageSigner.h | 6 ++---- src/Tezos/Michelson.cpp | 6 ++---- src/Tezos/Michelson.h | 6 ++---- src/Tezos/OperationList.cpp | 6 ++---- src/Tezos/OperationList.h | 6 ++---- src/Tezos/Signer.cpp | 6 ++---- src/Tezos/Signer.h | 6 ++---- src/TheOpenNetwork/Address.cpp | 6 ++---- src/TheOpenNetwork/Address.h | 6 ++---- src/TheOpenNetwork/Entry.cpp | 6 ++---- src/TheOpenNetwork/Entry.h | 6 ++---- src/TheOpenNetwork/Message.cpp | 6 ++---- src/TheOpenNetwork/Message.h | 6 ++---- src/TheOpenNetwork/Payloads.cpp | 6 ++---- src/TheOpenNetwork/Payloads.h | 6 ++---- src/TheOpenNetwork/Signer.cpp | 6 ++---- src/TheOpenNetwork/Signer.h | 6 ++---- src/TheOpenNetwork/WorkchainType.h | 6 ++---- src/TheOpenNetwork/wallet/Wallet.cpp | 6 ++---- src/TheOpenNetwork/wallet/Wallet.h | 6 ++---- src/TheOpenNetwork/wallet/WalletV4R2.cpp | 6 ++---- src/TheOpenNetwork/wallet/WalletV4R2.h | 6 ++---- src/Theta/Coins.h | 6 ++---- src/Theta/Entry.cpp | 6 ++---- src/Theta/Entry.h | 6 ++---- src/Theta/Signer.cpp | 6 ++---- src/Theta/Signer.h | 6 ++---- src/Theta/Transaction.cpp | 6 ++---- src/Theta/Transaction.h | 6 ++---- src/TransactionCompiler.cpp | 6 ++---- src/TransactionCompiler.h | 6 ++---- src/Tron/Address.cpp | 6 ++---- src/Tron/Address.h | 6 ++---- src/Tron/Entry.cpp | 6 ++---- src/Tron/Entry.h | 6 ++---- src/Tron/MessageSigner.cpp | 6 ++---- src/Tron/MessageSigner.h | 6 ++---- src/Tron/Serialization.cpp | 6 ++---- src/Tron/Serialization.h | 6 ++---- src/Tron/Signer.cpp | 6 ++---- src/Tron/Signer.h | 6 ++---- src/VeChain/Clause.h | 6 ++---- src/VeChain/Entry.cpp | 6 ++---- src/VeChain/Entry.h | 6 ++---- src/VeChain/Signer.cpp | 6 ++---- src/VeChain/Signer.h | 6 ++---- src/VeChain/Transaction.cpp | 6 ++---- src/VeChain/Transaction.h | 6 ++---- src/Verge/Entry.cpp | 6 ++---- src/Verge/Entry.h | 6 ++---- src/Verge/Signer.cpp | 6 ++---- src/Verge/Signer.h | 6 ++---- src/Verge/Transaction.cpp | 6 ++---- src/Verge/Transaction.h | 6 ++---- src/Verge/TransactionBuilder.h | 6 ++---- src/Wasm.h | 6 ++---- src/Waves/Address.cpp | 6 ++---- src/Waves/Address.h | 6 ++---- src/Waves/BinaryCoding.h | 6 ++---- src/Waves/Entry.cpp | 6 ++---- src/Waves/Entry.h | 6 ++---- src/Waves/Signer.cpp | 6 ++---- src/Waves/Signer.h | 6 ++---- src/Waves/Transaction.cpp | 6 ++---- src/Waves/Transaction.h | 6 ++---- src/WebAuthn.cpp | 6 ++---- src/WebAuthn.h | 6 ++---- src/XRP/Address.cpp | 6 ++---- src/XRP/Address.h | 6 ++---- src/XRP/BinaryCoding.h | 6 ++---- src/XRP/Entry.cpp | 6 ++---- src/XRP/Entry.h | 6 ++---- src/XRP/Signer.cpp | 6 ++---- src/XRP/Signer.h | 6 ++---- src/XRP/Transaction.cpp | 6 ++---- src/XRP/Transaction.h | 6 ++---- src/XRP/XAddress.cpp | 6 ++---- src/XRP/XAddress.h | 6 ++---- src/Zcash/Entry.cpp | 6 ++---- src/Zcash/Entry.h | 6 ++---- src/Zcash/Signer.cpp | 6 ++---- src/Zcash/Signer.h | 6 ++---- src/Zcash/TAddress.h | 6 ++---- src/Zcash/Transaction.cpp | 6 ++---- src/Zcash/Transaction.h | 6 ++---- src/Zcash/TransactionBuilder.h | 6 ++---- src/Zen/Address.h | 6 ++---- src/Zen/Entry.cpp | 6 ++---- src/Zen/Entry.h | 6 ++---- src/Zen/Signer.cpp | 6 ++---- src/Zen/Signer.h | 6 ++---- src/Zen/TransactionBuilder.h | 6 ++---- src/Zilliqa/Address.cpp | 6 ++---- src/Zilliqa/Address.h | 6 ++---- src/Zilliqa/AddressChecksum.cpp | 6 ++---- src/Zilliqa/AddressChecksum.h | 6 ++---- src/Zilliqa/Entry.cpp | 6 ++---- src/Zilliqa/Entry.h | 6 ++---- src/Zilliqa/Signer.cpp | 6 ++---- src/Zilliqa/Signer.h | 6 ++---- src/algorithm/erase.h | 6 ++---- src/algorithm/sort_copy.h | 6 ++---- src/algorithm/string.hpp | 6 ++---- src/algorithm/to_array.h | 6 ++---- src/concepts/tw_concepts.h | 6 ++---- src/interface/TWAES.cpp | 6 ++---- src/interface/TWAccount.cpp | 6 ++---- src/interface/TWAnyAddress.cpp | 6 ++---- src/interface/TWAnySigner.cpp | 6 ++---- src/interface/TWAsnParser.cpp | 6 ++---- src/interface/TWBarz.cpp | 6 ++---- src/interface/TWBase32.cpp | 6 ++---- src/interface/TWBase58.cpp | 6 ++---- src/interface/TWBase64.cpp | 6 ++---- src/interface/TWBitcoinAddress.cpp | 6 ++---- src/interface/TWBitcoinFee.cpp | 6 ++---- src/interface/TWBitcoinMessageSigner.cpp | 6 ++---- src/interface/TWBitcoinScript.cpp | 6 ++---- src/interface/TWBitcoinSigHashType.cpp | 6 ++---- src/interface/TWCardano.cpp | 6 ++---- src/interface/TWCoinType.cpp | 6 ++---- src/interface/TWData.cpp | 6 ++---- src/interface/TWDataVector.cpp | 6 ++---- src/interface/TWDerivationPath.cpp | 6 ++---- src/interface/TWDerivationPathIndex.cpp | 6 ++---- src/interface/TWEthereum.cpp | 6 ++---- src/interface/TWEthereumAbi.cpp | 6 ++---- src/interface/TWEthereumAbiFunction.cpp | 6 ++---- src/interface/TWEthereumAbiValue.cpp | 6 ++---- src/interface/TWEthereumMessageSigner.cpp | 6 ++---- src/interface/TWEthereumRlp.cpp | 6 ++---- src/interface/TWFIOAccount.cpp | 6 ++---- src/interface/TWFilecoinAddressConverter.cpp | 6 ++---- src/interface/TWGroestlcoinAddress.cpp | 6 ++---- src/interface/TWHDVersion.cpp | 6 ++---- src/interface/TWHDWallet.cpp | 6 ++---- src/interface/TWHash.cpp | 6 ++---- src/interface/TWMnemonic.cpp | 6 ++---- src/interface/TWNEARAccount.cpp | 6 ++---- src/interface/TWNervosAddress.cpp | 6 ++---- src/interface/TWPBKDF2.cpp | 6 ++---- src/interface/TWPrivateKey.cpp | 6 ++---- src/interface/TWPublicKey.cpp | 6 ++---- src/interface/TWRippleXAddress.cpp | 6 ++---- src/interface/TWSegwitAddress.cpp | 6 ++---- src/interface/TWSolanaAddress.cpp | 6 ++---- src/interface/TWStarkExMessageSigner.cpp | 6 ++---- src/interface/TWStarkWare.cpp | 6 ++---- src/interface/TWStoredKey.cpp | 6 ++---- src/interface/TWString+Hex.cpp | 6 ++---- src/interface/TWString.cpp | 6 ++---- src/interface/TWTezosMessageSigner.cpp | 6 ++---- src/interface/TWTransactionCompiler.cpp | 6 ++---- src/interface/TWTronMessageSigner.cpp | 6 ++---- src/interface/TWWalletConnectRequest.cpp | 6 ++---- src/interface/TWWebAuthn.cpp | 6 ++---- src/memory/memzero_wrapper.h | 6 ++---- src/operators/equality_comparable.h | 6 ++---- src/proto/Aptos.proto | 6 ++---- src/proto/Barz.proto | 6 ++---- src/proto/Everscale.proto | 6 ++---- src/proto/Hedera.proto | 6 ++---- src/proto/InternetComputer.proto | 6 ++---- src/proto/LiquidStaking.proto | 6 ++---- src/proto/MultiversX.proto | 6 ++---- src/proto/Nervos.proto | 6 ++---- src/proto/Oasis.proto | 6 ++---- src/proto/Sui.proto | 6 ++---- src/proto/TheOpenNetwork.proto | 6 ++---- src/rust/RustCoinEntry.cpp | 6 ++---- src/rust/RustCoinEntry.h | 6 ++---- src/rust/Wrapper.h | 6 ++---- src/uint256.h | 6 ++---- swift/Example/Example/ContentView.swift | 6 ++---- swift/Example/Example/ExampleApp.swift | 6 ++---- swift/Example/ExampleMac/ContentView.swift | 6 ++---- swift/Example/ExampleMac/ExampleMacApp.swift | 6 ++---- swift/Example/ExampleTests/ExampleTests.swift | 6 ++---- swift/Playground.playground/Contents.swift | 6 ++---- swift/Sources/Dummy.cpp | 6 ++---- swift/Sources/Extensions/Account+Codable.swift | 6 ++---- swift/Sources/Extensions/AddressProtocol.swift | 6 ++---- swift/Sources/Extensions/BitcoinAddress+Extension.swift | 6 ++---- swift/Sources/Extensions/CoinType+Address.swift | 6 ++---- swift/Sources/Extensions/Data+Hex.swift | 6 ++---- swift/Sources/Extensions/DerivationPath+Extension.swift | 6 ++---- swift/Sources/Extensions/Mnemonic+Extension.swift | 6 ++---- swift/Sources/Extensions/PublicKey+Bitcoin.swift | 6 ++---- swift/Sources/TWCardano.swift | 6 ++---- swift/Sources/TWData.swift | 6 ++---- swift/Sources/TWString.swift | 6 ++---- swift/Tests/AESTests.swift | 6 ++---- swift/Tests/Addresses/BinanceAddressTests.swift | 6 ++---- swift/Tests/Addresses/BitcoinAddressTests.swift | 6 ++---- swift/Tests/Addresses/JunoAddressTests.swift | 6 ++---- swift/Tests/Addresses/NEOAddressTests.swift | 6 ++---- swift/Tests/Addresses/OntologyAddressTests.swift | 6 ++---- swift/Tests/Addresses/TronAddressTests.swift | 6 ++---- swift/Tests/AnyAddressTests.swift | 6 ++---- swift/Tests/AsnParserTests.swift | 6 ++---- swift/Tests/BarzTests.swift | 6 ++---- swift/Tests/Base32Tests.swift | 6 ++---- swift/Tests/Base64Tests.swift | 6 ++---- swift/Tests/Blockchains/AcalaEVMTests.swift | 6 ++---- swift/Tests/Blockchains/AcalaTests.swift | 6 ++---- swift/Tests/Blockchains/AeternityTests.swift | 6 ++---- swift/Tests/Blockchains/AgoricTests.swift | 6 ++---- swift/Tests/Blockchains/AionTests.swift | 6 ++---- swift/Tests/Blockchains/AlgorandTests.swift | 6 ++---- swift/Tests/Blockchains/AptosTests.swift | 6 ++---- swift/Tests/Blockchains/AvalancheTests.swift | 6 ++---- swift/Tests/Blockchains/BandChainTests.swift | 6 ++---- swift/Tests/Blockchains/BinanceChainTests.swift | 6 ++---- swift/Tests/Blockchains/BinanceSmartChainTests.swift | 6 ++---- swift/Tests/Blockchains/BitcoinDiamondTests.swift | 6 ++---- swift/Tests/Blockchains/BitcoinTests.swift | 6 ++---- swift/Tests/Blockchains/BitconCashTests.swift | 6 ++---- swift/Tests/Blockchains/BluzelleTests.swift | 6 ++---- swift/Tests/Blockchains/CardanoTests.swift | 6 ++---- swift/Tests/Blockchains/CeloTests.swift | 6 ++---- swift/Tests/Blockchains/ConfluxeSpaceTests.swift | 6 ++---- swift/Tests/Blockchains/CosmosTests.swift | 6 ++---- swift/Tests/Blockchains/CronosTests.swift | 6 ++---- swift/Tests/Blockchains/CryptoorgTests.swift | 6 ++---- swift/Tests/Blockchains/DashTests.swift | 6 ++---- swift/Tests/Blockchains/DecredTests.swift | 6 ++---- swift/Tests/Blockchains/DogeTests.swift | 6 ++---- swift/Tests/Blockchains/ECashTests.swift | 6 ++---- swift/Tests/Blockchains/EOSTests.swift | 6 ++---- swift/Tests/Blockchains/EthereumAbiTests.swift | 6 ++---- swift/Tests/Blockchains/EthereumRlpTests.swift | 6 ++---- swift/Tests/Blockchains/EthereumTests.swift | 6 ++---- swift/Tests/Blockchains/EverscaleTests.swift | 6 ++---- swift/Tests/Blockchains/EvmosTests.swift | 6 ++---- swift/Tests/Blockchains/FIOTests.swift | 6 ++---- swift/Tests/Blockchains/FilecoinTests.swift | 6 ++---- swift/Tests/Blockchains/GreenfieldTests.swift | 6 ++---- swift/Tests/Blockchains/GroestlcoinTests.swift | 6 ++---- .../Blockchains/GroestlcoinTransactionSignerTests.swift | 6 ++---- swift/Tests/Blockchains/HarmonyTests.swift | 6 ++---- swift/Tests/Blockchains/HederaTests.swift | 6 ++---- swift/Tests/Blockchains/IconTests.swift | 6 ++---- swift/Tests/Blockchains/InternetComputerTests.swift | 6 ++---- swift/Tests/Blockchains/IoTeXTests.swift | 6 ++---- swift/Tests/Blockchains/KavaTests.swift | 6 ++---- swift/Tests/Blockchains/KinTests.swift | 6 ++---- swift/Tests/Blockchains/KuCoinCommunityChainTests.swift | 6 ++---- swift/Tests/Blockchains/KusamaTests.swift | 6 ++---- swift/Tests/Blockchains/LitecoinTests.swift | 6 ++---- swift/Tests/Blockchains/MonacoinTests.swift | 6 ++---- swift/Tests/Blockchains/MultiversXTests.swift | 6 ++---- swift/Tests/Blockchains/NEARTests.swift | 6 ++---- swift/Tests/Blockchains/NEOTests.swift | 6 ++---- swift/Tests/Blockchains/NULSTests.swift | 6 ++---- swift/Tests/Blockchains/NativeInjectiveTests.swift | 6 ++---- swift/Tests/Blockchains/NervosTests.swift | 6 ++---- swift/Tests/Blockchains/NimiqTests.swift | 6 ++---- swift/Tests/Blockchains/OasisTests.swift | 6 ++---- swift/Tests/Blockchains/OntologyTests.swift | 6 ++---- swift/Tests/Blockchains/OsmosisTests.swift | 6 ++---- swift/Tests/Blockchains/PolkadotTests.swift | 6 ++---- swift/Tests/Blockchains/PolygonTests.swift | 6 ++---- swift/Tests/Blockchains/QtumTests.swift | 6 ++---- swift/Tests/Blockchains/RippleTests.swift | 6 ++---- swift/Tests/Blockchains/RoninTests.swift | 6 ++---- swift/Tests/Blockchains/ScrollTests.swift | 6 ++---- swift/Tests/Blockchains/SecretTests.swift | 6 ++---- swift/Tests/Blockchains/SmartBitcoinCashTests.swift | 6 ++---- swift/Tests/Blockchains/SolanaTests.swift | 6 ++---- swift/Tests/Blockchains/StargazeTests.swift | 6 ++---- swift/Tests/Blockchains/StarkExTests.swift | 6 ++---- swift/Tests/Blockchains/StellarTests.swift | 6 ++---- swift/Tests/Blockchains/SuiTests.swift | 6 ++---- swift/Tests/Blockchains/SyscoinTests.swift | 6 ++---- swift/Tests/Blockchains/THORChainSwapTests.swift | 6 ++---- swift/Tests/Blockchains/THORChainTests.swift | 6 ++---- swift/Tests/Blockchains/TerraClassicTests.swift | 6 ++---- swift/Tests/Blockchains/TerraTests.swift | 6 ++---- swift/Tests/Blockchains/TezosTests.swift | 6 ++---- swift/Tests/Blockchains/TheOpenNetworkTests.swift | 6 ++---- swift/Tests/Blockchains/ThetaFuelTests.swift | 6 ++---- swift/Tests/Blockchains/ThetaTests.swift | 6 ++---- swift/Tests/Blockchains/TronTests.swift | 6 ++---- swift/Tests/Blockchains/WanchainTests.swift | 6 ++---- swift/Tests/Blockchains/WavesTests.swift | 6 ++---- swift/Tests/Blockchains/ZcashTests.swift | 6 ++---- swift/Tests/Blockchains/ZcoinTests.swift | 6 ++---- swift/Tests/Blockchains/ZenTests.swift | 6 ++---- swift/Tests/Blockchains/ZilliqaTests.swift | 6 ++---- swift/Tests/CoinAddressDerivationTests.swift | 6 ++---- swift/Tests/CoinTypeTests.swift | 6 ++---- swift/Tests/DataTests.swift | 6 ++---- swift/Tests/DerivationPathTests.swift | 6 ++---- swift/Tests/HDWalletTests.swift | 6 ++---- swift/Tests/HashTests.swift | 6 ++---- swift/Tests/LiquidStakingTests.swift | 6 ++---- swift/Tests/MnemonicTests.swift | 6 ++---- swift/Tests/PBKDF2Tests.swift | 6 ++---- swift/Tests/PrivateKeyTests.swift | 6 ++---- swift/Tests/PublicKeyTests.swift | 6 ++---- swift/Tests/TransactionCompilerTests.swift | 6 ++---- swift/Tests/UniversalAssetIDTests.swift | 6 ++---- swift/Tests/WebAuthnTests.swift | 6 ++---- swift/Tests/XCTAssert+Extension.swift | 6 ++---- swift/cpp.xcconfig.in | 6 ++---- tests/CMakeLists.txt | 6 ++---- tests/chains/Acala/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Acala/TWAnySignerTests.cpp | 6 ++---- tests/chains/Acala/TWCoinTypeTests.cpp | 6 ++---- tests/chains/AcalaEVM/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Aeternity/AddressTests.cpp | 6 ++---- tests/chains/Aeternity/SignerTests.cpp | 6 ++---- tests/chains/Aeternity/TWAeternityAddressTests.cpp | 6 ++---- tests/chains/Aeternity/TWAnySignerTests.cpp | 6 ++---- tests/chains/Aeternity/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Aeternity/TransactionTests.cpp | 6 ++---- tests/chains/Aion/AddressTests.cpp | 6 ++---- tests/chains/Aion/RLPTests.cpp | 6 ++---- tests/chains/Aion/SignerTests.cpp | 6 ++---- tests/chains/Aion/TWAnySignerTests.cpp | 6 ++---- tests/chains/Aion/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Aion/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Aion/TransactionTests.cpp | 6 ++---- tests/chains/Algorand/AddressTests.cpp | 6 ++---- tests/chains/Algorand/SignerTests.cpp | 6 ++---- tests/chains/Algorand/TWAnySignerTests.cpp | 6 ++---- tests/chains/Algorand/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Algorand/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Aptos/AddressTests.cpp | 5 ++++- tests/chains/Aptos/CompilerTests.cpp | 6 ++---- tests/chains/Aptos/TWAnySignerTests.cpp | 6 ++---- tests/chains/Aptos/TWAptosAddressTests.cpp | 6 ++---- tests/chains/Aptos/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Arbitrum/TWCoinTypeTests.cpp | 6 ++---- tests/chains/ArbitrumNova/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Aurora/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Avalanche/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Base/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Binance/AddressTests.cpp | 6 ++---- tests/chains/Binance/TWAnySignerTests.cpp | 6 ++---- tests/chains/Binance/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Binance/TWWalletConnectSigning.cpp | 6 ++---- tests/chains/Binance/TransactionCompilerTests.cpp | 6 ++---- tests/chains/BinanceSmartChain/SignerTests.cpp | 6 ++---- tests/chains/BinanceSmartChain/TWAnyAddressTests.cpp | 6 ++---- tests/chains/BinanceSmartChain/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Bitcoin/BitcoinAddressTests.cpp | 6 ++---- tests/chains/Bitcoin/BitcoinOrdinalNftData.h | 6 ++---- tests/chains/Bitcoin/BitcoinScriptTests.cpp | 6 ++---- tests/chains/Bitcoin/FeeCalculatorTests.cpp | 6 ++---- tests/chains/Bitcoin/InputSelectorTests.cpp | 6 ++---- tests/chains/Bitcoin/MessageSignerTests.cpp | 6 ++---- tests/chains/Bitcoin/SegwitAddressTests.cpp | 6 ++---- tests/chains/Bitcoin/TWBitcoinAddressTests.cpp | 6 ++---- tests/chains/Bitcoin/TWBitcoinFeeTests.cpp | 6 ++---- tests/chains/Bitcoin/TWBitcoinScriptTests.cpp | 6 ++---- tests/chains/Bitcoin/TWBitcoinSigningTests.cpp | 6 ++---- tests/chains/Bitcoin/TWBitcoinTransactionTests.cpp | 6 ++---- tests/chains/Bitcoin/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Bitcoin/TWSegwitAddressTests.cpp | 6 ++---- tests/chains/Bitcoin/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Bitcoin/TransactionPlanTests.cpp | 6 ++---- tests/chains/Bitcoin/TxComparisonHelper.cpp | 6 ++---- tests/chains/Bitcoin/TxComparisonHelper.h | 6 ++---- tests/chains/BitcoinCash/TWBitcoinCashTests.cpp | 6 ++---- tests/chains/BitcoinCash/TWCoinTypeTests.cpp | 6 ++---- tests/chains/BitcoinDiamond/AddressTests.cpp | 6 ++---- tests/chains/BitcoinDiamond/SignerTests.cpp | 6 ++---- tests/chains/BitcoinDiamond/TWAnyAddressTests.cpp | 6 ++---- tests/chains/BitcoinDiamond/TWAnySignerTests.cpp | 6 ++---- tests/chains/BitcoinDiamond/TWCoinTypeTests.cpp | 6 ++---- tests/chains/BitcoinDiamond/TWSegwitAddressTests.cpp | 6 ++---- tests/chains/BitcoinDiamond/TransactionCompilerTests.cpp | 6 ++---- tests/chains/BitcoinGold/TWAddressTests.cpp | 6 ++---- tests/chains/BitcoinGold/TWBitcoinGoldTests.cpp | 6 ++---- tests/chains/BitcoinGold/TWCoinTypeTests.cpp | 6 ++---- tests/chains/BitcoinGold/TWSegwitAddressTests.cpp | 6 ++---- tests/chains/BitcoinGold/TWSignerTests.cpp | 6 ++---- tests/chains/Boba/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Callisto/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cardano/AddressTests.cpp | 6 ++---- tests/chains/Cardano/SigningTests.cpp | 6 ++---- tests/chains/Cardano/StakingTests.cpp | 6 ++---- tests/chains/Cardano/TWCardanoAddressTests.cpp | 6 ++---- tests/chains/Cardano/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cardano/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Cardano/TransactionTests.cpp | 6 ++---- tests/chains/Celo/TWCoinTypeTests.cpp | 6 ++---- tests/chains/ConfluxeSpace/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/AddressTests.cpp | 6 ++---- tests/chains/Cosmos/Agoric/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Agoric/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/Akash/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Akash/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/Axelar/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Axelar/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/BandChain/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/Bluzelle/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/Comdex/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Comdex/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/Coreum/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Coreum/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/CosmosTestHelpers.h | 6 ++---- tests/chains/Cosmos/Crescent/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Crescent/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/CryptoOrg/SignerTests.cpp | 6 ++---- tests/chains/Cosmos/CryptoOrg/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/CryptoOrg/TWAnySignerTests.cpp | 6 ++---- tests/chains/Cosmos/CryptoOrg/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/FetchAI/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/FetchAI/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/Juno/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Juno/TWAnySignerTests.cpp | 6 ++---- tests/chains/Cosmos/Juno/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/Kava/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/Kujira/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Kujira/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/Mars/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Mars/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/NativeCanto/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/NativeCanto/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/NativeEvmos/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/NativeEvmos/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/NativeInjective/SignerTests.cpp | 6 ++---- .../chains/Cosmos/NativeInjective/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/NativeInjective/TWCoinTypeTests.cpp | 6 ++---- .../Cosmos/NativeInjective/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Cosmos/Neutron/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Neutron/TWAnySignerTests.cpp | 6 ++---- tests/chains/Cosmos/Neutron/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/Noble/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Noble/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/Osmosis/SignerTests.cpp | 6 ++---- tests/chains/Cosmos/Osmosis/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Osmosis/TWAnySignerTests.cpp | 6 ++---- tests/chains/Cosmos/Osmosis/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/Persistence/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Persistence/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/Quasar/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Quasar/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/Secret/SignerTests.cpp | 6 ++---- tests/chains/Cosmos/Secret/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Secret/TWAnySignerTests.cpp | 6 ++---- tests/chains/Cosmos/Secret/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/Sei/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Sei/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/SignerTests.cpp | 6 ++---- tests/chains/Cosmos/Sommelier/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Sommelier/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/StakingTests.cpp | 6 ++---- tests/chains/Cosmos/Stargaze/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Stargaze/TWAnySignerTests.cpp | 6 ++---- tests/chains/Cosmos/Stride/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Stride/TWAnySignerTests.cpp | 6 ++---- tests/chains/Cosmos/Stride/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/THORChain/SignerTests.cpp | 6 ++---- tests/chains/Cosmos/THORChain/SwapTests.cpp | 6 ++---- tests/chains/Cosmos/THORChain/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/THORChain/TWAnySignerTests.cpp | 6 ++---- tests/chains/Cosmos/THORChain/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/THORChain/TWSwapTests.cpp | 6 ++---- tests/chains/Cosmos/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/TWAnySignerTests.cpp | 6 ++---- tests/chains/Cosmos/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/Terra/SignerTests.cpp | 6 ++---- tests/chains/Cosmos/Terra/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/TerraV2/SignerTests.cpp | 6 ++---- tests/chains/Cosmos/TerraV2/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/Tia/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Tia/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cosmos/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Cosmos/Umee/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cosmos/Umee/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Cronos/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Cronos/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Dash/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Dash/TWDashTests.cpp | 6 ++---- tests/chains/Decred/AddressTests.cpp | 6 ++---- tests/chains/Decred/SignerTests.cpp | 6 ++---- tests/chains/Decred/TWAnySignerTests.cpp | 6 ++---- tests/chains/Decred/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Decred/TWDecredTests.cpp | 6 ++---- tests/chains/Decred/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Decred/TransactionTests.cpp | 6 ++---- tests/chains/DigiByte/TWCoinTypeTests.cpp | 6 ++---- tests/chains/DigiByte/TWDigiByteTests.cpp | 6 ++---- tests/chains/Dogecoin/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Dogecoin/TWDogeTests.cpp | 6 ++---- tests/chains/ECO/TWCoinTypeTests.cpp | 6 ++---- tests/chains/ECash/TWCoinTypeTests.cpp | 6 ++---- tests/chains/EOS/AddressTests.cpp | 6 ++---- tests/chains/EOS/AssetTests.cpp | 6 ++---- tests/chains/EOS/NameTests.cpp | 6 ++---- tests/chains/EOS/SignatureTests.cpp | 6 ++---- tests/chains/EOS/TWAnySignerTests.cpp | 6 ++---- tests/chains/EOS/TWCoinTypeTests.cpp | 6 ++---- tests/chains/EOS/TransactionCompilerTests.cpp | 6 ++---- tests/chains/EOS/TransactionTests.cpp | 6 ++---- tests/chains/Ethereum/AddressTests.cpp | 6 ++---- tests/chains/Ethereum/BarzTests.cpp | 6 ++---- tests/chains/Ethereum/ContractCallTests.cpp | 6 ++---- tests/chains/Ethereum/EIP1014Tests.cpp | 6 ++---- tests/chains/Ethereum/EIP1967Tests.cpp | 6 ++---- tests/chains/Ethereum/EthereumMessageSignerTests.cpp | 6 ++---- tests/chains/Ethereum/TWAnySignerTests.cpp | 6 ++---- tests/chains/Ethereum/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Ethereum/TWEthereumAbiTests.cpp | 6 ++---- tests/chains/Ethereum/TWEthereumAbiValueDecoderTests.cpp | 6 ++---- tests/chains/Ethereum/TWEthereumAbiValueEncodeTests.cpp | 6 ++---- tests/chains/Ethereum/TWRlpTests.cpp | 6 ++---- tests/chains/Ethereum/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Ethereum/ValueDecoderTests.cpp | 6 ++---- tests/chains/Ethereum/ValueEncoderTests.cpp | 6 ++---- tests/chains/EthereumClassic/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Everscale/AddressTests.cpp | 6 ++---- tests/chains/Everscale/CellBuilderTest.cpp | 6 ++---- tests/chains/Everscale/CellTests.cpp | 6 ++---- tests/chains/Everscale/SignerTests.cpp | 6 ++---- tests/chains/Everscale/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Everscale/TWAnySignerTests.cpp | 6 ++---- tests/chains/Everscale/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Evmos/SignerTests.cpp | 6 ++---- tests/chains/Evmos/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Evmos/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Evmos/TransactionCompilerTests.cpp | 6 ++---- tests/chains/FIO/AddressTests.cpp | 6 ++---- tests/chains/FIO/EncryptionTests.cpp | 6 ++---- tests/chains/FIO/SignerTests.cpp | 6 ++---- tests/chains/FIO/TWCoinTypeTests.cpp | 6 ++---- tests/chains/FIO/TWFIOAccountTests.cpp | 6 ++---- tests/chains/FIO/TWFIOTests.cpp | 6 ++---- tests/chains/FIO/TransactionBuilderTests.cpp | 6 ++---- tests/chains/FIO/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Fantom/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Filecoin/AddressTests.cpp | 6 ++---- tests/chains/Filecoin/SignerTests.cpp | 6 ++---- tests/chains/Filecoin/TWAddressConverterTests.cpp | 6 ++---- tests/chains/Filecoin/TWAnySignerTests.cpp | 6 ++---- tests/chains/Filecoin/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Filecoin/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Filecoin/TransactionTests.cpp | 6 ++---- tests/chains/Firo/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Firo/TWZCoinAddressTests.cpp | 6 ++---- tests/chains/GoChain/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Greenfield/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Greenfield/TWAnySignerTests.cpp | 6 ++---- tests/chains/Greenfield/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Greenfield/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Groestlcoin/AddressTests.cpp | 6 ++---- tests/chains/Groestlcoin/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Groestlcoin/TWGroestlcoinSigningTests.cpp | 6 ++---- tests/chains/Groestlcoin/TWGroestlcoinTests.cpp | 6 ++---- tests/chains/Groestlcoin/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Harmony/AddressTests.cpp | 6 ++---- tests/chains/Harmony/SignerTests.cpp | 6 ++---- tests/chains/Harmony/StakingTests.cpp | 6 ++---- tests/chains/Harmony/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Harmony/TWAnySignerTests.cpp | 6 ++---- tests/chains/Harmony/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Harmony/TWHarmonyStakingTests.cpp | 6 ++---- tests/chains/Harmony/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Hedera/AddressTests.cpp | 6 ++---- tests/chains/Hedera/SignerTests.cpp | 6 ++---- tests/chains/Hedera/TWAnySignerTests.cpp | 6 ++---- tests/chains/Hedera/TWCoinTypeTests.cpp | 6 ++---- tests/chains/ICON/AddressTests.cpp | 6 ++---- tests/chains/ICON/TWAnySignerTests.cpp | 6 ++---- tests/chains/ICON/TWCoinTypeTests.cpp | 6 ++---- tests/chains/ICON/TransactionCompilerTests.cpp | 6 ++---- tests/chains/IOST/AddressTests.cpp | 6 ++---- tests/chains/IOST/SignerTests.cpp | 6 ++---- tests/chains/IOST/TWCoinTypeTests.cpp | 6 ++---- tests/chains/IOST/TransactionCompilerTests.cpp | 6 ++---- tests/chains/ImmutableX/StarkKeyTests.cpp | 6 ++---- tests/chains/InternetComputer/TWAnyAddressTests.cpp | 6 ++---- tests/chains/InternetComputer/TWAnySignerTests.cpp | 6 ++---- tests/chains/InternetComputer/TWCoinTypeTests.cpp | 6 ++---- tests/chains/IoTeX/AddressTests.cpp | 6 ++---- tests/chains/IoTeX/SignerTests.cpp | 6 ++---- tests/chains/IoTeX/StakingTests.cpp | 6 ++---- tests/chains/IoTeX/TWAnySignerTests.cpp | 6 ++---- tests/chains/IoTeX/TWCoinTypeTests.cpp | 6 ++---- tests/chains/IoTeX/TransactionCompilerTests.cpp | 6 ++---- tests/chains/KavaEvm/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Kin/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Klaytn/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Komodo/AddressTests.cpp | 6 ++---- tests/chains/Komodo/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Komodo/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Komodo/TransactionCompilerTests.cpp | 6 ++---- tests/chains/KuCoinCommunityChain/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Kusama/AddressTests.cpp | 6 ++---- tests/chains/Kusama/SignerTests.cpp | 6 ++---- tests/chains/Kusama/TWAnySignerTests.cpp | 6 ++---- tests/chains/Kusama/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Linea/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Litecoin/LitecoinAddressTests.cpp | 6 ++---- tests/chains/Litecoin/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Litecoin/TWLitecoinTests.cpp | 6 ++---- tests/chains/MantaPacific/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Mantle/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Meter/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Metis/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Monacoin/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Monacoin/TWMonacoinAddressTests.cpp | 6 ++---- tests/chains/Monacoin/TWMonacoinTransactionTests.cpp | 6 ++---- tests/chains/Moonbeam/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Moonriver/TWCoinTypeTests.cpp | 6 ++---- tests/chains/MultiversX/AddressTests.cpp | 6 ++---- tests/chains/MultiversX/SerializationTests.cpp | 6 ++---- tests/chains/MultiversX/SignerTests.cpp | 6 ++---- tests/chains/MultiversX/TWAnySignerTests.cpp | 6 ++---- tests/chains/MultiversX/TWCoinTypeTests.cpp | 6 ++---- tests/chains/MultiversX/TestAccounts.h | 6 ++---- tests/chains/MultiversX/TransactionCompilerTests.cpp | 6 ++---- tests/chains/MultiversX/TransactionFactoryTests.cpp | 6 ++---- tests/chains/NEAR/AccountTests.cpp | 6 ++---- tests/chains/NEAR/AddressTests.cpp | 6 ++---- tests/chains/NEAR/SerializationTests.cpp | 6 ++---- tests/chains/NEAR/SignerTests.cpp | 6 ++---- tests/chains/NEAR/TWAnySignerTests.cpp | 6 ++---- tests/chains/NEAR/TWCoinTypeTests.cpp | 6 ++---- tests/chains/NEAR/TWNEARAccountTests.cpp | 6 ++---- tests/chains/NEAR/TransactionCompilerTests.cpp | 6 ++---- tests/chains/NEO/AddressTests.cpp | 6 ++---- tests/chains/NEO/BinaryCodingTests.cpp | 6 ++---- tests/chains/NEO/CoinReferenceTests.cpp | 6 ++---- tests/chains/NEO/ReadDataTests.cpp | 6 ++---- tests/chains/NEO/ScriptTests.cpp | 6 ++---- tests/chains/NEO/SignerTests.cpp | 6 ++---- tests/chains/NEO/TWAnySignerTests.cpp | 6 ++---- tests/chains/NEO/TWCoinTypeTests.cpp | 6 ++---- tests/chains/NEO/TransactionAttributeTests.cpp | 6 ++---- tests/chains/NEO/TransactionCompilerTests.cpp | 6 ++---- tests/chains/NEO/TransactionOutputTests.cpp | 6 ++---- tests/chains/NEO/TransactionTests.cpp | 6 ++---- tests/chains/NEO/WitnessTests.cpp | 6 ++---- tests/chains/NULS/AddressTests.cpp | 6 ++---- tests/chains/NULS/SignerTests.cpp | 6 ++---- tests/chains/NULS/TWAnySignerTests.cpp | 6 ++---- tests/chains/NULS/TWCoinTypeTests.cpp | 6 ++---- tests/chains/NULS/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Nano/AddressTests.cpp | 6 ++---- tests/chains/Nano/SignerTests.cpp | 6 ++---- tests/chains/Nano/TWAnySignerTests.cpp | 6 ++---- tests/chains/Nano/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Nano/TWNanoAddressTests.cpp | 6 ++---- tests/chains/Nano/TestAccounts.h | 6 ++---- tests/chains/Nano/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Nebl/AddressTests.cpp | 6 ++---- tests/chains/Nebl/SignerTests.cpp | 6 ++---- tests/chains/Nebl/TWAnySignerTests.cpp | 6 ++---- tests/chains/Nebl/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Nebl/TWNeblAddressTests.cpp | 6 ++---- tests/chains/Nebl/TransactionBuilderTests.cpp | 6 ++---- tests/chains/Nebl/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Nebulas/AddressTests.cpp | 6 ++---- tests/chains/Nebulas/SignerTests.cpp | 6 ++---- tests/chains/Nebulas/TWAnySignerTests.cpp | 6 ++---- tests/chains/Nebulas/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Nebulas/TWNebulasAddressTests.cpp | 6 ++---- tests/chains/Nebulas/TransactionTests.cpp | 6 ++---- tests/chains/Neon/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Nervos/AddressTests.cpp | 6 ++---- tests/chains/Nervos/SignerTests.cpp | 6 ++---- tests/chains/Nervos/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Nervos/TWAnySignerTests.cpp | 6 ++---- tests/chains/Nervos/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Nervos/TWNervosAddressTests.cpp | 6 ++---- tests/chains/Nimiq/AddressTests.cpp | 6 ++---- tests/chains/Nimiq/SignerTests.cpp | 6 ++---- tests/chains/Nimiq/TWAnySignerTests.cpp | 6 ++---- tests/chains/Nimiq/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Nimiq/TransactionTests.cpp | 6 ++---- tests/chains/OKXChain/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Oasis/AddressTests.cpp | 6 ++---- tests/chains/Oasis/SignerTests.cpp | 6 ++---- tests/chains/Oasis/TWAnySignerTests.cpp | 6 ++---- tests/chains/Oasis/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Oasis/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Ontology/AccountTests.cpp | 6 ++---- tests/chains/Ontology/AddressTests.cpp | 6 ++---- tests/chains/Ontology/Oep4Tests.cpp | 6 ++---- tests/chains/Ontology/OngTests.cpp | 6 ++---- tests/chains/Ontology/OntTests.cpp | 6 ++---- tests/chains/Ontology/ParamsBuilderTests.cpp | 6 ++---- tests/chains/Ontology/TWAnySignerTests.cpp | 6 ++---- tests/chains/Ontology/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Ontology/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Ontology/TransactionTests.cpp | 6 ++---- tests/chains/OpBNBtestnet/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Optimism/TWCoinTypeTests.cpp | 6 ++---- tests/chains/POANetwork/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Pivx/AddressTests.cpp | 6 ++---- tests/chains/Pivx/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Pivx/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Pivx/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Polkadot/AddressTests.cpp | 6 ++---- tests/chains/Polkadot/ExtrinsicTests.cpp | 6 ++---- tests/chains/Polkadot/SS58AddressTests.cpp | 6 ++---- tests/chains/Polkadot/ScaleCodecTests.cpp | 6 ++---- tests/chains/Polkadot/SignerTests.cpp | 6 ++---- tests/chains/Polkadot/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Polkadot/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Polygon/TWCoinTypeTests.cpp | 6 ++---- tests/chains/PolygonZkEvm/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Qtum/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Qtum/TWQtumAddressTests.cpp | 6 ++---- tests/chains/Ravencoin/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Ravencoin/TWRavencoinTransactionTests.cpp | 6 ++---- tests/chains/Ronin/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Ronin/TWAnySignerTests.cpp | 6 ++---- tests/chains/Ronin/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Rootstock/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Scroll/TWAnySignerTests.cpp | 6 ++---- tests/chains/Scroll/TWCoinTypeTests.cpp | 6 ++---- tests/chains/SmartBitcoinCash/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Solana/AddressTests.cpp | 6 ++---- tests/chains/Solana/ProgramTests.cpp | 6 ++---- tests/chains/Solana/SignerTests.cpp | 6 ++---- tests/chains/Solana/TWAnySignerTests.cpp | 6 ++---- tests/chains/Solana/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Solana/TWSolanaAddressTests.cpp | 6 ++---- tests/chains/Solana/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Solana/TransactionTests.cpp | 6 ++---- tests/chains/StarkEx/MessageSignerTests.cpp | 6 ++---- tests/chains/Stellar/AddressTests.cpp | 6 ++---- tests/chains/Stellar/TWAnySignerTests.cpp | 6 ++---- tests/chains/Stellar/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Stellar/TWStellarAddressTests.cpp | 6 ++---- tests/chains/Stellar/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Stellar/TransactionTests.cpp | 6 ++---- tests/chains/Stratis/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Stratis/TWStratisTests.cpp | 6 ++---- tests/chains/Sui/AddressTests.cpp | 6 ++---- tests/chains/Sui/CompilerTests.cpp | 6 ++---- tests/chains/Sui/SignerTests.cpp | 6 ++---- tests/chains/Sui/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Syscoin/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Syscoin/TWSyscoinTests.cpp | 6 ++---- tests/chains/TBinance/TWAnyAddressTests.cpp | 6 ++---- tests/chains/TBinance/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Tezos/AddressTests.cpp | 6 ++---- tests/chains/Tezos/ForgingTests.cpp | 6 ++---- tests/chains/Tezos/MessageSignerTests.cpp | 6 ++---- tests/chains/Tezos/OperationListTests.cpp | 6 ++---- tests/chains/Tezos/PublicKeyTests.cpp | 6 ++---- tests/chains/Tezos/SignerTests.cpp | 6 ++---- tests/chains/Tezos/TWAnySignerTests.cpp | 6 ++---- tests/chains/Tezos/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Tezos/TransactionCompilerTests.cpp | 6 ++---- tests/chains/TheOpenNetwork/AddressTests.cpp | 6 ++---- tests/chains/TheOpenNetwork/SignerTests.cpp | 6 ++---- tests/chains/TheOpenNetwork/TWAnyAddressTests.cpp | 6 ++---- tests/chains/TheOpenNetwork/TWAnySignerTests.cpp | 6 ++---- tests/chains/TheOpenNetwork/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Theta/SignerTests.cpp | 6 ++---- tests/chains/Theta/TWAnySignerTests.cpp | 6 ++---- tests/chains/Theta/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Theta/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Theta/TransactionTests.cpp | 6 ++---- tests/chains/ThetaFuel/TWAnySignerTests.cpp | 6 ++---- tests/chains/ThetaFuel/TWCoinTypeTests.cpp | 6 ++---- tests/chains/ThunderToken/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Tron/AddressTests.cpp | 6 ++---- tests/chains/Tron/SerializationTests.cpp | 6 ++---- tests/chains/Tron/SignerTests.cpp | 6 ++---- tests/chains/Tron/TWAnySignerTests.cpp | 6 ++---- tests/chains/Tron/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Tron/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Tron/TronMessageSignerTests.cpp | 6 ++---- tests/chains/VeChain/SignerTests.cpp | 6 ++---- tests/chains/VeChain/TWAnySignerTests.cpp | 6 ++---- tests/chains/VeChain/TWCoinTypeTests.cpp | 6 ++---- tests/chains/VeChain/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Verge/AddressTests.cpp | 6 ++---- tests/chains/Verge/SignerTests.cpp | 6 ++---- tests/chains/Verge/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Verge/TWAnySignerTests.cpp | 6 ++---- tests/chains/Verge/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Verge/TransactionBuilderTests.cpp | 6 ++---- tests/chains/Verge/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Viacoin/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Viacoin/TWViacoinAddressTests.cpp | 6 ++---- tests/chains/Viction/TWCoinTypeTests.cpp | 6 ++---- tests/chains/WAX/TWAnySignerTests.cpp | 6 ++---- tests/chains/WAX/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Wanchain/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Waves/AddressTests.cpp | 6 ++---- tests/chains/Waves/LeaseTests.cpp | 6 ++---- tests/chains/Waves/SignerTests.cpp | 6 ++---- tests/chains/Waves/TWAnySignerTests.cpp | 6 ++---- tests/chains/Waves/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Waves/TransactionTests.cpp | 6 ++---- tests/chains/XRP/AddressTests.cpp | 6 ++---- tests/chains/XRP/BinaryCodingTests.cpp | 6 ++---- tests/chains/XRP/TWAnySignerTests.cpp | 6 ++---- tests/chains/XRP/TWCoinTypeTests.cpp | 6 ++---- tests/chains/XRP/TWRippleAddressTests.cpp | 6 ++---- tests/chains/XRP/TransactionCompilerTests.cpp | 6 ++---- tests/chains/XRP/TransactionTests.cpp | 6 ++---- tests/chains/Zcash/AddressTests.cpp | 6 ++---- tests/chains/Zcash/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Zcash/TWZcashAddressTests.cpp | 6 ++---- tests/chains/Zcash/TWZcashTransactionTests.cpp | 6 ++---- tests/chains/Zcash/TransactionCompilerTests.cpp | 6 ++---- tests/chains/Zelcash/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Zelcash/TWZelcashAddressTests.cpp | 6 ++---- tests/chains/Zelcash/TWZelcashTransactionTests.cpp | 6 ++---- tests/chains/Zen/AddressTests.cpp | 6 ++---- tests/chains/Zen/SignerTests.cpp | 6 ++---- tests/chains/Zen/TWAnyAddressTests.cpp | 6 ++---- tests/chains/Zen/TWAnySignerTests.cpp | 6 ++---- tests/chains/Zen/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Zen/TransactionBuilderTests.cpp | 6 ++---- tests/chains/Zen/TransactionCompilerTests.cpp | 6 ++---- tests/chains/ZenEON/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Zilliqa/AddressTests.cpp | 6 ++---- tests/chains/Zilliqa/SignatureTests.cpp | 6 ++---- tests/chains/Zilliqa/SignerTests.cpp | 6 ++---- tests/chains/Zilliqa/TWAnySignerTests.cpp | 6 ++---- tests/chains/Zilliqa/TWCoinTypeTests.cpp | 6 ++---- tests/chains/Zilliqa/TWZilliqaAddressTests.cpp | 6 ++---- tests/chains/ZkSyncV2/TWCoinTypeTests.cpp | 6 ++---- tests/chains/xDai/TWCoinTypeTests.cpp | 6 ++---- tests/common/AnyAddressTests.cpp | 6 ++---- tests/common/Base64Tests.cpp | 6 ++---- tests/common/BaseEncoding.cpp | 6 ++---- tests/common/Bech32AddressTests.cpp | 6 ++---- tests/common/Bech32Tests.cpp | 6 ++---- tests/common/BinaryCodingTests.cpp | 6 ++---- tests/common/CborTests.cpp | 6 ++---- tests/common/CoinAddressDerivationTests.cpp | 6 ++---- tests/common/CoinAddressValidationTests.cpp | 6 ++---- tests/common/DataTests.cpp | 6 ++---- tests/common/EncryptTests.cpp | 6 ++---- tests/common/HDWallet/HDWalletInternalTests.cpp | 6 ++---- tests/common/HDWallet/HDWalletTests.cpp | 6 ++---- tests/common/HashTests.cpp | 6 ++---- tests/common/HexCodingTests.cpp | 6 ++---- tests/common/Keystore/DerivationPathTests.cpp | 6 ++---- tests/common/Keystore/StoredKeyTests.cpp | 6 ++---- tests/common/LiquidStaking/LiquidStakingTests.cpp | 6 ++---- tests/common/MnemonicTests.cpp | 6 ++---- tests/common/NumericLiteralTests.cpp | 6 ++---- tests/common/PrivateKeyTests.cpp | 6 ++---- tests/common/PublicKeyLegacy.h | 6 ++---- tests/common/PublicKeyTests.cpp | 6 ++---- tests/common/TestUtilities.cpp | 6 ++---- tests/common/TestUtilities.h | 6 ++---- tests/common/Uint256Tests.cpp | 6 ++---- tests/common/WalletConsoleTests.cpp | 6 ++---- tests/common/WebAuthnTests.cpp | 6 ++---- tests/common/algorithm/erase_tests.cpp | 6 ++---- tests/common/algorithm/sort_copy_tests.cpp | 6 ++---- tests/common/algorithm/string.cpp | 6 ++---- tests/common/algorithm/to_array_tests.cpp | 6 ++---- tests/common/memory/memzero_tests.cpp | 6 ++---- tests/common/operators/equality_comparable_tests.cpp | 6 ++---- tests/common/rust/bindgen/WalletCoreRsTests.cpp | 6 ++---- tests/interface/TWAESTests.cpp | 6 ++---- tests/interface/TWAccountTests.cpp | 6 ++---- tests/interface/TWAnyAddressTests.cpp | 6 ++---- tests/interface/TWAsnParserTests.cpp | 6 ++---- tests/interface/TWBase32Tests.cpp | 6 ++---- tests/interface/TWBase58Tests.cpp | 6 ++---- tests/interface/TWBase64Tests.cpp | 6 ++---- tests/interface/TWCoinTypeTests.cpp | 6 ++---- tests/interface/TWDataTests.cpp | 6 ++---- tests/interface/TWDataVectorTests.cpp | 6 ++---- tests/interface/TWDerivationPathTests.cpp | 6 ++---- tests/interface/TWHDWalletTests.cpp | 6 ++---- tests/interface/TWHRPTests.cpp | 6 ++---- tests/interface/TWHashTests.cpp | 6 ++---- tests/interface/TWMnemonicTests.cpp | 6 ++---- tests/interface/TWPBKDF2Tests.cpp | 6 ++---- tests/interface/TWPrivateKeyTests.cpp | 6 ++---- tests/interface/TWPublicKeyTests.cpp | 6 ++---- tests/interface/TWStoredKeyTests.cpp | 6 ++---- tests/interface/TWStringTests.cpp | 6 ++---- tests/interface/TWTransactionCompilerTests.cpp | 6 ++---- tests/main.cpp | 6 ++---- trezor-crypto/CMakeLists.txt | 6 ++---- trezor-crypto/crypto/tests/CMakeLists.txt | 6 ++---- trezor-crypto/include/TrezorCrypto/TrezorCrypto.h | 6 ++---- walletconsole/CMakeLists.txt | 6 ++---- walletconsole/lib/Address.cpp | 6 ++---- walletconsole/lib/Address.h | 6 ++---- walletconsole/lib/Buffer.cpp | 6 ++---- walletconsole/lib/Buffer.h | 6 ++---- walletconsole/lib/CMakeLists.txt | 6 ++---- walletconsole/lib/Coins.cpp | 6 ++---- walletconsole/lib/Coins.h | 6 ++---- walletconsole/lib/CommandExecutor.cpp | 6 ++---- walletconsole/lib/CommandExecutor.h | 6 ++---- walletconsole/lib/Keys.cpp | 6 ++---- walletconsole/lib/Keys.h | 6 ++---- walletconsole/lib/Util.cpp | 6 ++---- walletconsole/lib/Util.h | 6 ++---- walletconsole/lib/WalletConsole.cpp | 6 ++---- walletconsole/lib/WalletConsole.h | 6 ++---- walletconsole/main.cpp | 6 ++---- wasm/CMakeLists.txt | 6 ++---- wasm/index.ts | 6 ++---- wasm/src/AnySigner.cpp | 6 ++---- wasm/src/BitcoinSigHashTypeExt.cpp | 6 ++---- wasm/src/BitcoinSigHashTypeExt.h | 6 ++---- wasm/src/CoinTypeExt.cpp | 6 ++---- wasm/src/CoinTypeExt.h | 6 ++---- wasm/src/HDVersionExt.cpp | 6 ++---- wasm/src/HDVersionExt.h | 6 ++---- wasm/src/HexCoding.cpp | 6 ++---- wasm/src/Random.cpp | 6 ++---- wasm/src/WasmData.cpp | 6 ++---- wasm/src/WasmData.h | 6 ++---- wasm/src/WasmString.cpp | 6 ++---- wasm/src/WasmString.h | 6 ++---- wasm/src/enum-ext.d.ts | 6 ++---- wasm/src/keystore/default-impl.ts | 6 ++---- wasm/src/keystore/extension-storage.ts | 6 ++---- wasm/src/keystore/fs-storage.ts | 6 ++---- wasm/src/keystore/index.ts | 6 ++---- wasm/src/keystore/types.ts | 6 ++---- wasm/tests/AES.test.ts | 6 ++---- wasm/tests/AnyAddress.test.ts | 6 ++---- wasm/tests/Base32.test.ts | 6 ++---- wasm/tests/Base64.test.ts | 6 ++---- wasm/tests/BitcoinSigHashType.test.ts | 6 ++---- wasm/tests/Blockchain/Aptos.test.ts | 6 ++---- wasm/tests/Blockchain/Bitcoin.test.ts | 6 ++---- wasm/tests/Blockchain/Ethereum.test.ts | 6 ++---- wasm/tests/Blockchain/Greenfield.test.ts | 6 ++---- wasm/tests/Blockchain/Hedera.test.ts | 6 ++---- wasm/tests/Blockchain/InternetComputer.test.ts | 6 ++---- wasm/tests/Blockchain/Sui.test.ts | 6 ++---- wasm/tests/Blockchain/TheOpenNetwork.test.ts | 6 ++---- wasm/tests/CoinType.test.ts | 6 ++---- wasm/tests/HDVersion.test.ts | 6 ++---- wasm/tests/HDWallet.test.ts | 6 ++---- wasm/tests/HRP.test.ts | 6 ++---- wasm/tests/Hash.test.ts | 6 ++---- wasm/tests/HexCoding.test.ts | 6 ++---- wasm/tests/KeyStore+extension.test.ts | 6 ++---- wasm/tests/KeyStore+fs.test.ts | 6 ++---- wasm/tests/Mnemonic.test.ts | 6 ++---- wasm/tests/PBKDF2.test.ts | 6 ++---- wasm/tests/StoredKey.test.ts | 6 ++---- wasm/tests/initWasm.test.ts | 6 ++---- wasm/tests/mock.ts | 6 ++---- 2147 files changed, 4296 insertions(+), 8588 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5cba2961f0a..6fec5513326 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,6 @@ -# Copyright © 2017-2022 Trust Wallet. +# SPDX-License-Identifier: Apache-2.0 # -# This file is part of Trust. The full Trust copyright notice, including -# terms governing use, modification, and redistribution, is contained in the -# file LICENSE at the root of the source code distribution tree. +# Copyright © 2017 Trust Wallet. cmake_minimum_required(VERSION 3.18 FATAL_ERROR) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/acala/TestAcalaAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/acala/TestAcalaAddress.kt index b94158bace6..5e50f515ab4 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/acala/TestAcalaAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/acala/TestAcalaAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.acala diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/acala/TestAcalaSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/acala/TestAcalaSigner.kt index a066009144d..ad99985b4ec 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/acala/TestAcalaSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/acala/TestAcalaSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.acala diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/acalaevm/TestAcalaEVMAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/acalaevm/TestAcalaEVMAddress.kt index 81274311cdd..6845bea3e89 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/acalaevm/TestAcalaEVMAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/acalaevm/TestAcalaEVMAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.acalaevm diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/agoric/TestAgoricAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/agoric/TestAgoricAddress.kt index 9cabca65f15..1164db16e14 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/agoric/TestAgoricAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/agoric/TestAgoricAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.agoric diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/agoric/TestAgoricSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/agoric/TestAgoricSigner.kt index 7567a61f21f..ecdf4372f3a 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/agoric/TestAgoricSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/agoric/TestAgoricSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.agoric diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosAddress.kt index 180617ff5b1..c8bed69c5d8 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.aptos diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosSigner.kt index f16a3be8300..e536821658b 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.aptos diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bandchain/TestBandChainAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bandchain/TestBandChainAddress.kt index d988eed5bb1..ec361735357 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bandchain/TestBandChainAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bandchain/TestBandChainAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.bandchain diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bandchain/TestBandChainSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bandchain/TestBandChainSigner.kt index 662773bba9c..001e9d90631 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bandchain/TestBandChainSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bandchain/TestBandChainSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.bandchain diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bitcoindiamond/TestBitcoinDiamondAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bitcoindiamond/TestBitcoinDiamondAddress.kt index d8f768eeaec..497b2d6dedc 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bitcoindiamond/TestBitcoinDiamondAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bitcoindiamond/TestBitcoinDiamondAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.bitcoindiamond diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bitcoindiamond/TestBitcoinDiamondSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bitcoindiamond/TestBitcoinDiamondSigner.kt index eea39e3a269..3bbbd9a77fd 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bitcoindiamond/TestBitcoinDiamondSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bitcoindiamond/TestBitcoinDiamondSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.bitcoindiamond diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bluzelle/TestBluzelleAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bluzelle/TestBluzelleAddress.kt index 99baa611f71..d8eb3424893 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bluzelle/TestBluzelleAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bluzelle/TestBluzelleAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.bluzelle diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bluzelle/TestBluzelleSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bluzelle/TestBluzelleSigner.kt index ba4d7e99238..bf40f0dcc67 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bluzelle/TestBluzelleSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bluzelle/TestBluzelleSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.bluzelle diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cardano/TestCardanoAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cardano/TestCardanoAddress.kt index 6ac0f1c4d63..a505e201176 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cardano/TestCardanoAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cardano/TestCardanoAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.cardano diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cardano/TestCardanoSigning.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cardano/TestCardanoSigning.kt index 2e56bab0838..d9450e9881c 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cardano/TestCardanoSigning.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cardano/TestCardanoSigning.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.cardano diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/confluxespace/TestConfluxeSpaceAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/confluxespace/TestConfluxeSpaceAddress.kt index 5186e4730ea..25d5ae1c81e 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/confluxespace/TestConfluxeSpaceAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/confluxespace/TestConfluxeSpaceAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.confluxespace diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cryptoorg/TestCryptoorgAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cryptoorg/TestCryptoorgAddress.kt index 52e06a041d8..214c391624d 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cryptoorg/TestCryptoorgAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cryptoorg/TestCryptoorgAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.cryptoorg diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cryptoorg/TestCryptoorgSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cryptoorg/TestCryptoorgSigner.kt index 25197f82a53..74090e73a29 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cryptoorg/TestCryptoorgSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cryptoorg/TestCryptoorgSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.cryptoorg diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/everscale/TestEverscaleAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/everscale/TestEverscaleAddress.kt index b09cfaff057..5922fa9f191 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/everscale/TestEverscaleAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/everscale/TestEverscaleAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.everscale diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/everscale/TestEverscaleSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/everscale/TestEverscaleSigner.kt index cb1d7266f92..a07ba7c46b4 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/everscale/TestEverscaleSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/everscale/TestEverscaleSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.everscale diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/fio/TestFIOAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/fio/TestFIOAddress.kt index 6b6461e78d1..4a9bef5fedb 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/fio/TestFIOAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/fio/TestFIOAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.fio diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/fio/TestFIOSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/fio/TestFIOSigner.kt index 7037706254b..6992aa0b9f8 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/fio/TestFIOSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/fio/TestFIOSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.fio diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/greenfield/TestGreenfieldSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/greenfield/TestGreenfieldSigner.kt index 757bdaea895..cd7caf5e2ac 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/greenfield/TestGreenfieldSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/greenfield/TestGreenfieldSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.greenfield diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/hedera/TestHederaAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/hedera/TestHederaAddress.kt index be92015f825..925c36c7851 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/hedera/TestHederaAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/hedera/TestHederaAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.hedera diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/hedera/TestHederaSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/hedera/TestHederaSigner.kt index dec11a37bcb..c01f813e2d0 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/hedera/TestHederaSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/hedera/TestHederaSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.hedera diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/internetcomputer/TestInternetComputerAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/internetcomputer/TestInternetComputerAddress.kt index f4b9044e339..2861aea3a4a 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/internetcomputer/TestInternetComputerAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/internetcomputer/TestInternetComputerAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.internetcomputer diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/internetcomputer/TestInternetComputerSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/internetcomputer/TestInternetComputerSigner.kt index 87a4261a158..d3e97de972e 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/internetcomputer/TestInternetComputerSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/internetcomputer/TestInternetComputerSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.internetcomputer diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/juno/TestJunoAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/juno/TestJunoAddress.kt index eb9edcfb89d..39c7022b743 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/juno/TestJunoAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/juno/TestJunoAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.juno diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kcc/TestKuCoinCommunityChainAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kcc/TestKuCoinCommunityChainAddress.kt index 0d2fe4a7446..9a2ecfc3811 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kcc/TestKuCoinCommunityChainAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kcc/TestKuCoinCommunityChainAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.kcc diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kusama/TestKusamaAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kusama/TestKusamaAddress.kt index 23f6b258315..39fc302d0ff 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kusama/TestKusamaAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kusama/TestKusamaAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.kusama diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kusama/TestKusamaSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kusama/TestKusamaSigner.kt index 388a38bd150..7bec37b4f0e 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kusama/TestKusamaSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kusama/TestKusamaSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.polkadot diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/multiversx/TestMultiversXAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/multiversx/TestMultiversXAddress.kt index 6cb38fef175..ab3b1ec2483 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/multiversx/TestMultiversXAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/multiversx/TestMultiversXAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.multiversx diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/multiversx/TestMultiversXSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/multiversx/TestMultiversXSigner.kt index fc6bd12637f..811c8a6b76f 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/multiversx/TestMultiversXSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/multiversx/TestMultiversXSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.multiversx diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativeinjective/TestNativeInjectiveAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativeinjective/TestNativeInjectiveAddress.kt index fc0a81474eb..8c70eaf1dfe 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativeinjective/TestNativeInjectiveAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativeinjective/TestNativeInjectiveAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.nativeinjective diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativeinjective/TestNativeInjectiveSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativeinjective/TestNativeInjectiveSigner.kt index 8892bac9d7f..b1295e2d823 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativeinjective/TestNativeInjectiveSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativeinjective/TestNativeInjectiveSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.nativeinjective diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/neo/TestsNEOAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/neo/TestsNEOAddress.kt index dd1ef2e86dc..8eb3f6ac165 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/neo/TestsNEOAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/neo/TestsNEOAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.neo diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nervos/TestNervosAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nervos/TestNervosAddress.kt index 64f1c492ada..f77ce3858cd 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nervos/TestNervosAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nervos/TestNervosAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.nervos diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nervos/TestNervosSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nervos/TestNervosSigner.kt index 3d0a149e5ae..d556f883f17 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nervos/TestNervosSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nervos/TestNervosSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.nervos diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/oasis/TestOasisAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/oasis/TestOasisAddress.kt index 2cdc8dfcd4a..00b315bc40a 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/oasis/TestOasisAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/oasis/TestOasisAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.oasis diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/oasis/TestOasisSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/oasis/TestOasisSigner.kt index f9962ebb13c..ef3ad829694 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/oasis/TestOasisSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/oasis/TestOasisSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.oasis diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/osmosis/TestOsmosisAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/osmosis/TestOsmosisAddress.kt index 1d881bbdef1..88b33d1b9fd 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/osmosis/TestOsmosisAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/osmosis/TestOsmosisAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.osmosis diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/osmosis/TestOsmosisSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/osmosis/TestOsmosisSigner.kt index e8498fb0ab6..679583594f7 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/osmosis/TestOsmosisSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/osmosis/TestOsmosisSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.osmosis diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polkadot/TestPolkadotAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polkadot/TestPolkadotAddress.kt index 82477b4bbbe..7ab39ef55f5 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polkadot/TestPolkadotAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polkadot/TestPolkadotAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.Polkadot diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polkadot/TestPolkadotSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polkadot/TestPolkadotSigner.kt index 9da312ca3ea..da571e5ca1a 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polkadot/TestPolkadotSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polkadot/TestPolkadotSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.polkadot diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polygon/TestPolygonAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polygon/TestPolygonAddress.kt index f6b21b1780c..ab807796d5a 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polygon/TestPolygonAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polygon/TestPolygonAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.polygon diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/scroll/TestScrollAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/scroll/TestScrollAddress.kt index da490466d1c..23d77ff4ccb 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/scroll/TestScrollAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/scroll/TestScrollAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.scroll diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/secret/TestSecretAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/secret/TestSecretAddress.kt index 26d66ee5883..5315c57c9ad 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/secret/TestSecretAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/secret/TestSecretAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.secret diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/secret/TestSecretSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/secret/TestSecretSigner.kt index d9286ccd4f2..3f1ef6330a0 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/secret/TestSecretSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/secret/TestSecretSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.secret diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/smartbitcoincash/TestSmartBitcoinCashAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/smartbitcoincash/TestSmartBitcoinCashAddress.kt index 95acfd23453..64c33ba9c58 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/smartbitcoincash/TestSmartBitcoinCashAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/smartbitcoincash/TestSmartBitcoinCashAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.smartbitcoincash diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/smartchain/TestBinanceSmartChainAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/smartchain/TestBinanceSmartChainAddress.kt index 142cbc0babe..a9a83132c36 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/smartchain/TestBinanceSmartChainAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/smartchain/TestBinanceSmartChainAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.binancesmartchain diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/stargaze/TestStargazeAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/stargaze/TestStargazeAddress.kt index bc568dccae3..54577d7971d 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/stargaze/TestStargazeAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/stargaze/TestStargazeAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.stargaze diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/stargaze/TestStargazeSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/stargaze/TestStargazeSigner.kt index 94a2fbfc7c2..63a7a166b60 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/stargaze/TestStargazeSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/stargaze/TestStargazeSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.stargaze diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiAddress.kt index b4ec636a5eb..503e6e8c8ad 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.sui diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiSigner.kt index 327e8a2b19b..0a37390cdd5 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/sui/TestSuiSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.sui diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkAddress.kt index d2f341dc62f..c96c5ebb21a 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.theopennetwork diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkSigner.kt index 887c3516493..4374bba398c 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.theopennetwork diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thetafuel/TestThetaFuelAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thetafuel/TestThetaFuelAddress.kt index 876fdefebb4..17e60b89aeb 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thetafuel/TestThetaFuelAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thetafuel/TestThetaFuelAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.thetafuel diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORChainAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORChainAddress.kt index 8a1c5768d90..0974995d3f1 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORChainAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORChainAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.thorchain diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORChainSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORChainSigner.kt index c12ad29b2f1..fde6325529f 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORChainSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORChainSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.thorchain diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/zen/TestZenAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/zen/TestZenAddress.kt index bd7fcdfed69..8398b636928 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/zen/TestZenAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/zen/TestZenAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.zen diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/zen/TestZenSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/zen/TestZenSigner.kt index 0b42106409a..36682ac0293 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/zen/TestZenSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/zen/TestZenSigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.zen diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestAnyAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestAnyAddress.kt index 64b3bb82987..ae4b47b4bd0 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestAnyAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestAnyAddress.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.utils diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestAsnParser.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestAsnParser.kt index ecdf9db3d9c..7c896d7860c 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestAsnParser.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestAsnParser.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.utils diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt index 22af2bfc37f..e3ec6f29b12 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestHDWallet.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.utils diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestLiquidStaking.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestLiquidStaking.kt index a4bcbf35957..41adb7f1d4f 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestLiquidStaking.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestLiquidStaking.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.utils diff --git a/cmake/Protobuf.cmake b/cmake/Protobuf.cmake index 131bd7678b8..8e0a93251d2 100644 --- a/cmake/Protobuf.cmake +++ b/cmake/Protobuf.cmake @@ -1,8 +1,6 @@ -# Copyright © 2017-2022 Trust Wallet. +# SPDX-License-Identifier: Apache-2.0 # -# This file is part of Trust. The full Trust copyright notice, including -# terms governing use, modification, and redistribution, is contained in the -# file LICENSE at the root of the source code distribution tree. +# Copyright © 2017 Trust Wallet. set(protobuf_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../build/local/src/protobuf/protobuf-3.19.2) set(protobuf_source_dir ${CMAKE_CURRENT_LIST_DIR}/../build/local/src/protobuf/protobuf-3.19.2) diff --git a/codegen-v2/src/codegen/cpp/blockchain_dispatcher_generator.rs b/codegen-v2/src/codegen/cpp/blockchain_dispatcher_generator.rs index d0b4fcd552a..0871d545995 100644 --- a/codegen-v2/src/codegen/cpp/blockchain_dispatcher_generator.rs +++ b/codegen-v2/src/codegen/cpp/blockchain_dispatcher_generator.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::cpp::cpp_source_directory; use crate::registry::CoinItem; diff --git a/codegen-v2/src/codegen/cpp/entry_generator.rs b/codegen-v2/src/codegen/cpp/entry_generator.rs index 49f341ab35f..639ee6c0488 100644 --- a/codegen-v2/src/codegen/cpp/entry_generator.rs +++ b/codegen-v2/src/codegen/cpp/entry_generator.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::cpp::cpp_source_directory; use crate::codegen::template_generator::TemplateGenerator; diff --git a/codegen-v2/src/codegen/cpp/mod.rs b/codegen-v2/src/codegen/cpp/mod.rs index 87443e4ad6d..3ddf0131626 100644 --- a/codegen-v2/src/codegen/cpp/mod.rs +++ b/codegen-v2/src/codegen/cpp/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::registry::CoinItem; use std::env; diff --git a/codegen-v2/src/codegen/cpp/new_blockchain.rs b/codegen-v2/src/codegen/cpp/new_blockchain.rs index 75634f481de..df7c94c62bd 100644 --- a/codegen-v2/src/codegen/cpp/new_blockchain.rs +++ b/codegen-v2/src/codegen/cpp/new_blockchain.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::cpp::blockchain_dispatcher_generator::BlockchainDispatcherGenerator; use crate::codegen::cpp::entry_generator::EntryGenerator; diff --git a/codegen-v2/src/codegen/cpp/new_evmchain.rs b/codegen-v2/src/codegen/cpp/new_evmchain.rs index 36c29e127e2..2368c797ab3 100644 --- a/codegen-v2/src/codegen/cpp/new_evmchain.rs +++ b/codegen-v2/src/codegen/cpp/new_evmchain.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::cpp::tw_coin_address_derivation_tests_generator::CoinAddressDerivationTestsGenerator; use crate::codegen::cpp::tw_coin_type_generator::TWCoinTypeGenerator; diff --git a/codegen-v2/src/codegen/cpp/templates/Entry.h b/codegen-v2/src/codegen/cpp/templates/Entry.h index a07c98e471d..2c2520f9ccb 100644 --- a/codegen-v2/src/codegen/cpp/templates/Entry.h +++ b/codegen-v2/src/codegen/cpp/templates/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-{YEAR} Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/codegen-v2/src/codegen/cpp/templates/TWAnyAddressTests.cpp b/codegen-v2/src/codegen/cpp/templates/TWAnyAddressTests.cpp index 35516cf2597..139234a20e4 100644 --- a/codegen-v2/src/codegen/cpp/templates/TWAnyAddressTests.cpp +++ b/codegen-v2/src/codegen/cpp/templates/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-{YEAR} Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "HexCoding.h" diff --git a/codegen-v2/src/codegen/cpp/templates/TWAnySignerTests.cpp b/codegen-v2/src/codegen/cpp/templates/TWAnySignerTests.cpp index 38f1493b916..ce5b56f0467 100644 --- a/codegen-v2/src/codegen/cpp/templates/TWAnySignerTests.cpp +++ b/codegen-v2/src/codegen/cpp/templates/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-{YEAR} Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "HexCoding.h" diff --git a/codegen-v2/src/codegen/cpp/templates/TWCoinTypeTests.cpp b/codegen-v2/src/codegen/cpp/templates/TWCoinTypeTests.cpp index 59a9d1a2e3f..8e917150dd4 100644 --- a/codegen-v2/src/codegen/cpp/templates/TWCoinTypeTests.cpp +++ b/codegen-v2/src/codegen/cpp/templates/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-{YEAR} Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/codegen-v2/src/codegen/cpp/tw_any_address_tests_generator.rs b/codegen-v2/src/codegen/cpp/tw_any_address_tests_generator.rs index f6fa62ac324..599ea265235 100644 --- a/codegen-v2/src/codegen/cpp/tw_any_address_tests_generator.rs +++ b/codegen-v2/src/codegen/cpp/tw_any_address_tests_generator.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::cpp::coin_integration_tests_directory; use crate::codegen::template_generator::TemplateGenerator; diff --git a/codegen-v2/src/codegen/cpp/tw_any_signer_tests_generator.rs b/codegen-v2/src/codegen/cpp/tw_any_signer_tests_generator.rs index 677162aee84..9ea4337616a 100644 --- a/codegen-v2/src/codegen/cpp/tw_any_signer_tests_generator.rs +++ b/codegen-v2/src/codegen/cpp/tw_any_signer_tests_generator.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::cpp::coin_integration_tests_directory; use crate::codegen::template_generator::TemplateGenerator; diff --git a/codegen-v2/src/codegen/cpp/tw_blockchain.rs b/codegen-v2/src/codegen/cpp/tw_blockchain.rs index f74c1e20ea9..a76a73ab055 100644 --- a/codegen-v2/src/codegen/cpp/tw_blockchain.rs +++ b/codegen-v2/src/codegen/cpp/tw_blockchain.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::cpp::cpp_include_directory; use crate::registry::CoinItem; diff --git a/codegen-v2/src/codegen/cpp/tw_coin_address_derivation_tests_generator.rs b/codegen-v2/src/codegen/cpp/tw_coin_address_derivation_tests_generator.rs index 1cdeaf295ac..91321dffb05 100644 --- a/codegen-v2/src/codegen/cpp/tw_coin_address_derivation_tests_generator.rs +++ b/codegen-v2/src/codegen/cpp/tw_coin_address_derivation_tests_generator.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::cpp::integration_tests_directory; use crate::registry::CoinItem; diff --git a/codegen-v2/src/codegen/cpp/tw_coin_type_generator.rs b/codegen-v2/src/codegen/cpp/tw_coin_type_generator.rs index d238922ae4d..0271838af03 100644 --- a/codegen-v2/src/codegen/cpp/tw_coin_type_generator.rs +++ b/codegen-v2/src/codegen/cpp/tw_coin_type_generator.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::cpp::cpp_include_directory; use crate::registry::CoinItem; diff --git a/codegen-v2/src/codegen/cpp/tw_coin_type_tests_generator.rs b/codegen-v2/src/codegen/cpp/tw_coin_type_tests_generator.rs index 8b85c4e79f4..67edddac4ff 100644 --- a/codegen-v2/src/codegen/cpp/tw_coin_type_tests_generator.rs +++ b/codegen-v2/src/codegen/cpp/tw_coin_type_tests_generator.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::cpp::coin_integration_tests_directory; use crate::codegen::template_generator::TemplateGenerator; diff --git a/codegen-v2/src/codegen/mod.rs b/codegen-v2/src/codegen/mod.rs index 2f1b1a70839..1d8522e6b9f 100644 --- a/codegen-v2/src/codegen/mod.rs +++ b/codegen-v2/src/codegen/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod cpp; pub mod proto; diff --git a/codegen-v2/src/codegen/proto/mod.rs b/codegen-v2/src/codegen/proto/mod.rs index e760e33c8ea..47d3e1a2dd5 100644 --- a/codegen-v2/src/codegen/proto/mod.rs +++ b/codegen-v2/src/codegen/proto/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::env; use std::path::PathBuf; diff --git a/codegen-v2/src/codegen/proto/new_blockchain.rs b/codegen-v2/src/codegen/proto/new_blockchain.rs index 805e342f124..9e5f51a5387 100644 --- a/codegen-v2/src/codegen/proto/new_blockchain.rs +++ b/codegen-v2/src/codegen/proto/new_blockchain.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::proto::proto_generator::ProtoGenerator; use crate::registry::CoinItem; diff --git a/codegen-v2/src/codegen/proto/proto_generator.rs b/codegen-v2/src/codegen/proto/proto_generator.rs index f3bd016984d..f2cdfd94a86 100644 --- a/codegen-v2/src/codegen/proto/proto_generator.rs +++ b/codegen-v2/src/codegen/proto/proto_generator.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::proto::proto_source_directory; use crate::codegen::template_generator::TemplateGenerator; diff --git a/codegen-v2/src/codegen/proto/templates/Blockchain.proto b/codegen-v2/src/codegen/proto/templates/Blockchain.proto index c78772ce0a5..9c4c6e34478 100644 --- a/codegen-v2/src/codegen/proto/templates/Blockchain.proto +++ b/codegen-v2/src/codegen/proto/templates/Blockchain.proto @@ -1,8 +1,6 @@ -// Copyright © 2017-{YEAR} Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. syntax = "proto3"; diff --git a/codegen-v2/src/codegen/rust/blockchain_dispatcher_generator.rs b/codegen-v2/src/codegen/rust/blockchain_dispatcher_generator.rs index 19c83519e15..92f98bac940 100644 --- a/codegen-v2/src/codegen/rust/blockchain_dispatcher_generator.rs +++ b/codegen-v2/src/codegen/rust/blockchain_dispatcher_generator.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::rust::coin_registry_directory; use crate::registry::CoinItem; diff --git a/codegen-v2/src/codegen/rust/blockchain_type_generator.rs b/codegen-v2/src/codegen/rust/blockchain_type_generator.rs index b902e88f739..4895bf8a923 100644 --- a/codegen-v2/src/codegen/rust/blockchain_type_generator.rs +++ b/codegen-v2/src/codegen/rust/blockchain_type_generator.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::rust::coin_registry_directory; use crate::registry::CoinItem; diff --git a/codegen-v2/src/codegen/rust/coin_address_derivation_test_generator.rs b/codegen-v2/src/codegen/rust/coin_address_derivation_test_generator.rs index 4a5d3acdcc6..41091af2a76 100644 --- a/codegen-v2/src/codegen/rust/coin_address_derivation_test_generator.rs +++ b/codegen-v2/src/codegen/rust/coin_address_derivation_test_generator.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::rust::tw_any_coin_directory; use crate::registry::CoinItem; diff --git a/codegen-v2/src/codegen/rust/coin_crate.rs b/codegen-v2/src/codegen/rust/coin_crate.rs index 4586f66ae3f..0e1b133f171 100644 --- a/codegen-v2/src/codegen/rust/coin_crate.rs +++ b/codegen-v2/src/codegen/rust/coin_crate.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::rust::chains_directory; use crate::codegen::template_generator::TemplateGenerator; diff --git a/codegen-v2/src/codegen/rust/coin_integration_tests.rs b/codegen-v2/src/codegen/rust/coin_integration_tests.rs index 541e1b2ae3c..2743d546066 100644 --- a/codegen-v2/src/codegen/rust/coin_integration_tests.rs +++ b/codegen-v2/src/codegen/rust/coin_integration_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::rust::tw_any_coin_directory; use crate::codegen::template_generator::TemplateGenerator; diff --git a/codegen-v2/src/codegen/rust/coin_registry_manifest_generator.rs b/codegen-v2/src/codegen/rust/coin_registry_manifest_generator.rs index f76fed045db..2a63e90e336 100644 --- a/codegen-v2/src/codegen/rust/coin_registry_manifest_generator.rs +++ b/codegen-v2/src/codegen/rust/coin_registry_manifest_generator.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::rust::coin_registry_directory; use crate::codegen::rust::toml_editor::Dependencies; diff --git a/codegen-v2/src/codegen/rust/mod.rs b/codegen-v2/src/codegen/rust/mod.rs index 7edc77d5062..5364b415fee 100644 --- a/codegen-v2/src/codegen/rust/mod.rs +++ b/codegen-v2/src/codegen/rust/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::env; use std::path::PathBuf; diff --git a/codegen-v2/src/codegen/rust/new_blockchain.rs b/codegen-v2/src/codegen/rust/new_blockchain.rs index 2f3a4996dd2..ca27f77b08e 100644 --- a/codegen-v2/src/codegen/rust/new_blockchain.rs +++ b/codegen-v2/src/codegen/rust/new_blockchain.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::rust::blockchain_dispatcher_generator::BlockchainDispatcherGenerator; use crate::codegen::rust::blockchain_type_generator::BlockchainTypeGenerator; diff --git a/codegen-v2/src/codegen/rust/new_evmchain.rs b/codegen-v2/src/codegen/rust/new_evmchain.rs index 2222306a4de..f754fa5ba1b 100644 --- a/codegen-v2/src/codegen/rust/new_evmchain.rs +++ b/codegen-v2/src/codegen/rust/new_evmchain.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::rust::coin_address_derivation_test_generator::CoinAddressDerivationTestGenerator; use crate::registry::CoinItem; diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/address.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/address.rs index 96d862a63cd..4ca837b12fb 100644 --- a/codegen-v2/src/codegen/rust/templates/blockchain_crate/address.rs +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-{YEAR} Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::fmt; use std::str::FromStr; diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/compiler.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/compiler.rs index 2e1831cf5b6..aff08a27680 100644 --- a/codegen-v2/src/codegen/rust/templates/blockchain_crate/compiler.rs +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/compiler.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-{YEAR} Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs index a33b00322b5..643e7b053a4 100644 --- a/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-{YEAR} Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::{BLOCKCHAIN}Address; use crate::compiler::{BLOCKCHAIN}Compiler; diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/lib.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/lib.rs index c5ac4e098e5..c41edeb4471 100644 --- a/codegen-v2/src/codegen/rust/templates/blockchain_crate/lib.rs +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-{YEAR} Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod address; pub mod compiler; diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/signer.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/signer.rs index 17cb69f66e9..343b819b63f 100644 --- a/codegen-v2/src/codegen/rust/templates/blockchain_crate/signer.rs +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-{YEAR} Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::error::SigningResult; diff --git a/codegen-v2/src/codegen/rust/templates/integration_tests/address_tests.rs b/codegen-v2/src/codegen/rust/templates/integration_tests/address_tests.rs index 6a3a7e6a330..55c23efc6ee 100644 --- a/codegen-v2/src/codegen/rust/templates/integration_tests/address_tests.rs +++ b/codegen-v2/src/codegen/rust/templates/integration_tests/address_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-{YEAR} Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_any_coin::test_utils::address_utils::{ test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, diff --git a/codegen-v2/src/codegen/rust/templates/integration_tests/compile_tests.rs b/codegen-v2/src/codegen/rust/templates/integration_tests/compile_tests.rs index d403c5e154b..fad7f69330a 100644 --- a/codegen-v2/src/codegen/rust/templates/integration_tests/compile_tests.rs +++ b/codegen-v2/src/codegen/rust/templates/integration_tests/compile_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-{YEAR} Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #[test] fn test_{COIN_ID}_compile() { diff --git a/codegen-v2/src/codegen/rust/templates/integration_tests/mod.rs b/codegen-v2/src/codegen/rust/templates/integration_tests/mod.rs index 96f7f590af1..15dccad969f 100644 --- a/codegen-v2/src/codegen/rust/templates/integration_tests/mod.rs +++ b/codegen-v2/src/codegen/rust/templates/integration_tests/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-{YEAR} Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. mod {COIN_ID}_address; mod {COIN_ID}_compile; diff --git a/codegen-v2/src/codegen/rust/templates/integration_tests/sign_tests.rs b/codegen-v2/src/codegen/rust/templates/integration_tests/sign_tests.rs index 9a24b45d74b..38f01a4e0e1 100644 --- a/codegen-v2/src/codegen/rust/templates/integration_tests/sign_tests.rs +++ b/codegen-v2/src/codegen/rust/templates/integration_tests/sign_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-{YEAR} Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #[test] fn test_{COIN_ID}_sign() { diff --git a/codegen-v2/src/codegen/rust/toml_editor.rs b/codegen-v2/src/codegen/rust/toml_editor.rs index 0ac35b3c53d..0811182365d 100644 --- a/codegen-v2/src/codegen/rust/toml_editor.rs +++ b/codegen-v2/src/codegen/rust/toml_editor.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::{Error, Result}; use std::fs; diff --git a/codegen-v2/src/codegen/swift/functions.rs b/codegen-v2/src/codegen/swift/functions.rs index 08573893865..1f7e4aa8d39 100644 --- a/codegen-v2/src/codegen/swift/functions.rs +++ b/codegen-v2/src/codegen/swift/functions.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use super::*; use crate::manifest::{FunctionInfo, TypeVariant}; diff --git a/codegen-v2/src/codegen/swift/inits.rs b/codegen-v2/src/codegen/swift/inits.rs index 07de55e037d..a1f140c326e 100644 --- a/codegen-v2/src/codegen/swift/inits.rs +++ b/codegen-v2/src/codegen/swift/inits.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use super::*; use crate::manifest::InitInfo; diff --git a/codegen-v2/src/codegen/swift/mod.rs b/codegen-v2/src/codegen/swift/mod.rs index bb19c2d7478..4bc70e7e416 100644 --- a/codegen-v2/src/codegen/swift/mod.rs +++ b/codegen-v2/src/codegen/swift/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use self::functions::process_methods; use self::inits::process_inits; diff --git a/codegen-v2/src/codegen/swift/properties.rs b/codegen-v2/src/codegen/swift/properties.rs index 0f19db983ba..61d73e7171f 100644 --- a/codegen-v2/src/codegen/swift/properties.rs +++ b/codegen-v2/src/codegen/swift/properties.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use super::*; use crate::manifest::PropertyInfo; diff --git a/codegen-v2/src/codegen/swift/render.rs b/codegen-v2/src/codegen/swift/render.rs index b7f68716b72..465a7277682 100644 --- a/codegen-v2/src/codegen/swift/render.rs +++ b/codegen-v2/src/codegen/swift/render.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use super::{inits::process_deinits, *}; diff --git a/codegen-v2/src/codegen/swift/templates/WalletCore.h b/codegen-v2/src/codegen/swift/templates/WalletCore.h index 98e432e9066..76f61b9fa74 100644 --- a/codegen-v2/src/codegen/swift/templates/WalletCore.h +++ b/codegen-v2/src/codegen/swift/templates/WalletCore.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #import diff --git a/codegen-v2/src/codegen/swift/templates/enum.hbs b/codegen-v2/src/codegen/swift/templates/enum.hbs index e9fd0b59cf7..5363fb776b4 100644 --- a/codegen-v2/src/codegen/swift/templates/enum.hbs +++ b/codegen-v2/src/codegen/swift/templates/enum.hbs @@ -1,8 +1,6 @@ -// Copyright © 2017-{{current_year}} Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here WILL BE LOST. // diff --git a/codegen-v2/src/codegen/swift/templates/extension.hbs b/codegen-v2/src/codegen/swift/templates/extension.hbs index de51b918f76..ddae1f95ba6 100644 --- a/codegen-v2/src/codegen/swift/templates/extension.hbs +++ b/codegen-v2/src/codegen/swift/templates/extension.hbs @@ -1,8 +1,6 @@ -// Copyright © 2017-{{current_year}} Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here WILL BE LOST. // diff --git a/codegen-v2/src/codegen/swift/templates/proto.hbs b/codegen-v2/src/codegen/swift/templates/proto.hbs index aad05bf9191..23e1c541698 100644 --- a/codegen-v2/src/codegen/swift/templates/proto.hbs +++ b/codegen-v2/src/codegen/swift/templates/proto.hbs @@ -1,8 +1,6 @@ -// Copyright © 2017-{{current_year}} Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here WILL BE LOST. // diff --git a/codegen-v2/src/codegen/swift/templates/struct.hbs b/codegen-v2/src/codegen/swift/templates/struct.hbs index 6d7f9bb63ce..eccc0499fd7 100644 --- a/codegen-v2/src/codegen/swift/templates/struct.hbs +++ b/codegen-v2/src/codegen/swift/templates/struct.hbs @@ -1,8 +1,6 @@ -// Copyright © 2017-{{current_year}} Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here WILL BE LOST. // diff --git a/codegen-v2/src/codegen/template_generator.rs b/codegen-v2/src/codegen/template_generator.rs index add3da7fcb7..684116c514d 100644 --- a/codegen-v2/src/codegen/template_generator.rs +++ b/codegen-v2/src/codegen/template_generator.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::registry::CoinItem; use crate::{current_year, Error, Result}; diff --git a/codegen-v2/src/coin_id.rs b/codegen-v2/src/coin_id.rs index 6f0eaed7c36..87a6b9e13c4 100644 --- a/codegen-v2/src/coin_id.rs +++ b/codegen-v2/src/coin_id.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::{Error, Result}; use serde::de::Error as SerdeError; diff --git a/codegen-v2/src/lib.rs b/codegen-v2/src/lib.rs index 5227f533701..3e2c2e2e59d 100644 --- a/codegen-v2/src/lib.rs +++ b/codegen-v2/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #[macro_use] extern crate serde; diff --git a/codegen-v2/src/main.rs b/codegen-v2/src/main.rs index d708659a4c0..9bd35a8b9ee 100644 --- a/codegen-v2/src/main.rs +++ b/codegen-v2/src/main.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use libparser::codegen::swift::RenderIntput; use libparser::codegen::{cpp, proto, rust}; diff --git a/codegen-v2/src/manifest.rs b/codegen-v2/src/manifest.rs index 91ef48f1392..f70c24b2e5a 100644 --- a/codegen-v2/src/manifest.rs +++ b/codegen-v2/src/manifest.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use super::Result; use std::fs; diff --git a/codegen-v2/src/registry.rs b/codegen-v2/src/registry.rs index aaad3ff0330..17eda663c7c 100644 --- a/codegen-v2/src/registry.rs +++ b/codegen-v2/src/registry.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::coin_id::CoinId; use crate::{Error, Result}; diff --git a/codegen-v2/src/tests/mod.rs b/codegen-v2/src/tests/mod.rs index 266d3913191..6a889017da4 100644 --- a/codegen-v2/src/tests/mod.rs +++ b/codegen-v2/src/tests/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::codegen::swift::{render_to_strings, RenderIntput}; use crate::manifest::parse_str; diff --git a/codegen-v2/src/tests/samples/class.output.swift b/codegen-v2/src/tests/samples/class.output.swift index bd9024b04df..ed7c0b000ac 100644 --- a/codegen-v2/src/tests/samples/class.output.swift +++ b/codegen-v2/src/tests/samples/class.output.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here WILL BE LOST. // diff --git a/codegen-v2/src/tests/samples/enum.output.swift b/codegen-v2/src/tests/samples/enum.output.swift index d8d9a3d3e72..0e5b8346d6f 100644 --- a/codegen-v2/src/tests/samples/enum.output.swift +++ b/codegen-v2/src/tests/samples/enum.output.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here WILL BE LOST. // diff --git a/codegen-v2/src/tests/samples/enum_extension.output.swift b/codegen-v2/src/tests/samples/enum_extension.output.swift index 77a42623f8c..706368ee760 100644 --- a/codegen-v2/src/tests/samples/enum_extension.output.swift +++ b/codegen-v2/src/tests/samples/enum_extension.output.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here WILL BE LOST. // diff --git a/codegen-v2/src/tests/samples/enum_private.output.swift b/codegen-v2/src/tests/samples/enum_private.output.swift index bf583ef1ce5..b2cb22aea3a 100644 --- a/codegen-v2/src/tests/samples/enum_private.output.swift +++ b/codegen-v2/src/tests/samples/enum_private.output.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here WILL BE LOST. // diff --git a/codegen-v2/src/tests/samples/non-associated.output.swift b/codegen-v2/src/tests/samples/non-associated.output.swift index 89c0a9dbb63..478b8b1d6b5 100644 --- a/codegen-v2/src/tests/samples/non-associated.output.swift +++ b/codegen-v2/src/tests/samples/non-associated.output.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here WILL BE LOST. // diff --git a/codegen-v2/src/tests/samples/optional.output.swift b/codegen-v2/src/tests/samples/optional.output.swift index 504a396ebd3..a33b4afea58 100644 --- a/codegen-v2/src/tests/samples/optional.output.swift +++ b/codegen-v2/src/tests/samples/optional.output.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here WILL BE LOST. // diff --git a/codegen-v2/src/tests/samples/private_class.output.swift b/codegen-v2/src/tests/samples/private_class.output.swift index 31897047db4..2362367af00 100644 --- a/codegen-v2/src/tests/samples/private_class.output.swift +++ b/codegen-v2/src/tests/samples/private_class.output.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here WILL BE LOST. // diff --git a/codegen-v2/src/tests/samples/struct.output.swift b/codegen-v2/src/tests/samples/struct.output.swift index 9cf7a2fbd19..34f310c1eb8 100644 --- a/codegen-v2/src/tests/samples/struct.output.swift +++ b/codegen-v2/src/tests/samples/struct.output.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here WILL BE LOST. // diff --git a/codegen-v2/src/utils.rs b/codegen-v2/src/utils.rs index 43e1bd84f81..a61222fc3fd 100644 --- a/codegen-v2/src/utils.rs +++ b/codegen-v2/src/utils.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::{Error, Result}; use std::fs; diff --git a/codegen/lib/templates/CoinInfoData.cpp.erb b/codegen/lib/templates/CoinInfoData.cpp.erb index 4803af6dff6..d2bbb5d6b71 100644 --- a/codegen/lib/templates/CoinInfoData.cpp.erb +++ b/codegen/lib/templates/CoinInfoData.cpp.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here WILL BE LOST. // diff --git a/codegen/lib/templates/TWCoinTypeTests.cpp.erb b/codegen/lib/templates/TWCoinTypeTests.cpp.erb index 3784f589eb4..bb8cbdadb79 100644 --- a/codegen/lib/templates/TWCoinTypeTests.cpp.erb +++ b/codegen/lib/templates/TWCoinTypeTests.cpp.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/codegen/lib/templates/TWDerivation.h.erb b/codegen/lib/templates/TWDerivation.h.erb index 30ba65483da..eb7759a55c2 100644 --- a/codegen/lib/templates/TWDerivation.h.erb +++ b/codegen/lib/templates/TWDerivation.h.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE from \registry.json, changes made here WILL BE LOST. // diff --git a/codegen/lib/templates/TWEthereumChainID.h.erb b/codegen/lib/templates/TWEthereumChainID.h.erb index 7d3657b7a8c..3e6385e1b77 100644 --- a/codegen/lib/templates/TWEthereumChainID.h.erb +++ b/codegen/lib/templates/TWEthereumChainID.h.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE from \registry.json, changes made here WILL BE LOST. // diff --git a/codegen/lib/templates/copyright_header.erb b/codegen/lib/templates/copyright_header.erb index e8de6a21997..39239c27440 100644 --- a/codegen/lib/templates/copyright_header.erb +++ b/codegen/lib/templates/copyright_header.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here WILL BE LOST. // diff --git a/codegen/lib/templates/hrp.cpp.erb b/codegen/lib/templates/hrp.cpp.erb index ecca3779ced..c3705bd5a9f 100644 --- a/codegen/lib/templates/hrp.cpp.erb +++ b/codegen/lib/templates/hrp.cpp.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE from \registry.json, changes made here WILL BE LOST. // diff --git a/codegen/lib/templates/hrp.h.erb b/codegen/lib/templates/hrp.h.erb index a346bf8333c..4691fbafc5b 100644 --- a/codegen/lib/templates/hrp.h.erb +++ b/codegen/lib/templates/hrp.h.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE from \registry.json, changes made here WILL BE LOST. // diff --git a/codegen/lib/templates/java/header.erb b/codegen/lib/templates/java/header.erb index 13ea5d56c17..3c5da1f81ed 100644 --- a/codegen/lib/templates/java/header.erb +++ b/codegen/lib/templates/java/header.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here WILL BE LOST. // diff --git a/codegen/lib/templates/newcoin/Address.cpp.erb b/codegen/lib/templates/newcoin/Address.cpp.erb index 88de42de169..ea1c354db0e 100644 --- a/codegen/lib/templates/newcoin/Address.cpp.erb +++ b/codegen/lib/templates/newcoin/Address.cpp.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" diff --git a/codegen/lib/templates/newcoin/Address.h.erb b/codegen/lib/templates/newcoin/Address.h.erb index 302a491a0aa..7fe392b92df 100644 --- a/codegen/lib/templates/newcoin/Address.h.erb +++ b/codegen/lib/templates/newcoin/Address.h.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/codegen/lib/templates/newcoin/AddressTests.cpp.erb b/codegen/lib/templates/newcoin/AddressTests.cpp.erb index 4c20321c6fd..27c4c09ab89 100644 --- a/codegen/lib/templates/newcoin/AddressTests.cpp.erb +++ b/codegen/lib/templates/newcoin/AddressTests.cpp.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "<%= format_name(coin) %>/Address.h" diff --git a/codegen/lib/templates/newcoin/AddressTests.kt.erb b/codegen/lib/templates/newcoin/AddressTests.kt.erb index 4185afbb9dd..20bbd774cc9 100644 --- a/codegen/lib/templates/newcoin/AddressTests.kt.erb +++ b/codegen/lib/templates/newcoin/AddressTests.kt.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.<%= format_name_lowercase(coin) %> diff --git a/codegen/lib/templates/newcoin/Entry.cpp.erb b/codegen/lib/templates/newcoin/Entry.cpp.erb index 5e4c4aaf25d..793f5bf8f3f 100644 --- a/codegen/lib/templates/newcoin/Entry.cpp.erb +++ b/codegen/lib/templates/newcoin/Entry.cpp.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/codegen/lib/templates/newcoin/Entry.h.erb b/codegen/lib/templates/newcoin/Entry.h.erb index 29ba0783596..32b48e0e42c 100644 --- a/codegen/lib/templates/newcoin/Entry.h.erb +++ b/codegen/lib/templates/newcoin/Entry.h.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/codegen/lib/templates/newcoin/Proto.erb b/codegen/lib/templates/newcoin/Proto.erb index 9609e2d2089..bbd242d66be 100644 --- a/codegen/lib/templates/newcoin/Proto.erb +++ b/codegen/lib/templates/newcoin/Proto.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. syntax = "proto3"; diff --git a/codegen/lib/templates/newcoin/Signer.cpp.erb b/codegen/lib/templates/newcoin/Signer.cpp.erb index fd81ac314e5..aeedceda863 100644 --- a/codegen/lib/templates/newcoin/Signer.cpp.erb +++ b/codegen/lib/templates/newcoin/Signer.cpp.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Address.h" diff --git a/codegen/lib/templates/newcoin/Signer.h.erb b/codegen/lib/templates/newcoin/Signer.h.erb index 7d660796723..f77982f3ac3 100644 --- a/codegen/lib/templates/newcoin/Signer.h.erb +++ b/codegen/lib/templates/newcoin/Signer.h.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/codegen/lib/templates/newcoin/SignerTests.cpp.erb b/codegen/lib/templates/newcoin/SignerTests.cpp.erb index 26150a8face..d88ad46994f 100644 --- a/codegen/lib/templates/newcoin/SignerTests.cpp.erb +++ b/codegen/lib/templates/newcoin/SignerTests.cpp.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "<%= format_name(coin) %>/Signer.h" #include "<%= format_name(coin) %>/Address.h" diff --git a/codegen/lib/templates/newcoin/SignerTests.kt.erb b/codegen/lib/templates/newcoin/SignerTests.kt.erb index 5bf1f4ed8bb..4add9add89f 100644 --- a/codegen/lib/templates/newcoin/SignerTests.kt.erb +++ b/codegen/lib/templates/newcoin/SignerTests.kt.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core.app.blockchains.<%= format_name_lowercase(coin) %> diff --git a/codegen/lib/templates/newcoin/TWAddressTests.cpp.erb b/codegen/lib/templates/newcoin/TWAddressTests.cpp.erb index 9ad574fec94..91a0cbf0729 100644 --- a/codegen/lib/templates/newcoin/TWAddressTests.cpp.erb +++ b/codegen/lib/templates/newcoin/TWAddressTests.cpp.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "HexCoding.h" diff --git a/codegen/lib/templates/newcoin/TWSignerTests.cpp.erb b/codegen/lib/templates/newcoin/TWSignerTests.cpp.erb index 456b6f08b8d..8e8193db07c 100644 --- a/codegen/lib/templates/newcoin/TWSignerTests.cpp.erb +++ b/codegen/lib/templates/newcoin/TWSignerTests.cpp.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "HexCoding.h" diff --git a/codegen/lib/templates/newcoin/Tests.swift.erb b/codegen/lib/templates/newcoin/Tests.swift.erb index 28c2ef8339e..e28d0ec1bc2 100644 --- a/codegen/lib/templates/newcoin/Tests.swift.erb +++ b/codegen/lib/templates/newcoin/Tests.swift.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/codegen/lib/templates/newcoin/TransactionCompilerTests.cpp.erb b/codegen/lib/templates/newcoin/TransactionCompilerTests.cpp.erb index 8150081c49e..c996dd2c6ab 100644 --- a/codegen/lib/templates/newcoin/TransactionCompilerTests.cpp.erb +++ b/codegen/lib/templates/newcoin/TransactionCompilerTests.cpp.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "<%= format_name(coin) %>/Signer.h" #include "<%= format_name(coin) %>/Address.h" diff --git a/codegen/lib/templates/swift/TrustWalletCore.h.erb b/codegen/lib/templates/swift/TrustWalletCore.h.erb index f1246a2a970..5e2ac88537a 100644 --- a/codegen/lib/templates/swift/TrustWalletCore.h.erb +++ b/codegen/lib/templates/swift/TrustWalletCore.h.erb @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #import diff --git a/include/TrustWalletCore/TWAES.h b/include/TrustWalletCore/TWAES.h index 383804cedb1..e4a30c656d1 100644 --- a/include/TrustWalletCore/TWAES.h +++ b/include/TrustWalletCore/TWAES.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWAESPaddingMode.h b/include/TrustWalletCore/TWAESPaddingMode.h index b9e7c707950..da271a5b989 100644 --- a/include/TrustWalletCore/TWAESPaddingMode.h +++ b/include/TrustWalletCore/TWAESPaddingMode.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWAccount.h b/include/TrustWalletCore/TWAccount.h index adb6f8a97e3..a5cc0fc0e2f 100644 --- a/include/TrustWalletCore/TWAccount.h +++ b/include/TrustWalletCore/TWAccount.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWAnyAddress.h b/include/TrustWalletCore/TWAnyAddress.h index ce501489c22..0256ac89070 100644 --- a/include/TrustWalletCore/TWAnyAddress.h +++ b/include/TrustWalletCore/TWAnyAddress.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWAnySigner.h b/include/TrustWalletCore/TWAnySigner.h index 25cebe5c1a0..a50f1246618 100644 --- a/include/TrustWalletCore/TWAnySigner.h +++ b/include/TrustWalletCore/TWAnySigner.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once #include "TWBase.h" diff --git a/include/TrustWalletCore/TWAsnParser.h b/include/TrustWalletCore/TWAsnParser.h index 67d0c6588cd..b008a0578d8 100644 --- a/include/TrustWalletCore/TWAsnParser.h +++ b/include/TrustWalletCore/TWAsnParser.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWBarz.h b/include/TrustWalletCore/TWBarz.h index 3bc08ed31b4..bc2d0615ba0 100644 --- a/include/TrustWalletCore/TWBarz.h +++ b/include/TrustWalletCore/TWBarz.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once #include "TWBase.h" diff --git a/include/TrustWalletCore/TWBase.h b/include/TrustWalletCore/TWBase.h index f0486654eb7..1e334d18679 100644 --- a/include/TrustWalletCore/TWBase.h +++ b/include/TrustWalletCore/TWBase.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #if !defined(TW_EXTERN_C_BEGIN) #if defined(__cplusplus) diff --git a/include/TrustWalletCore/TWBase32.h b/include/TrustWalletCore/TWBase32.h index 153d348f3a3..a8f09039ec8 100644 --- a/include/TrustWalletCore/TWBase32.h +++ b/include/TrustWalletCore/TWBase32.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWBase58.h b/include/TrustWalletCore/TWBase58.h index 87a3cbe70fd..c59ddd2a2a0 100644 --- a/include/TrustWalletCore/TWBase58.h +++ b/include/TrustWalletCore/TWBase58.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWBase64.h b/include/TrustWalletCore/TWBase64.h index 7ca0c37492f..5b3cff51a73 100644 --- a/include/TrustWalletCore/TWBase64.h +++ b/include/TrustWalletCore/TWBase64.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWBitcoinAddress.h b/include/TrustWalletCore/TWBitcoinAddress.h index 1ef667bf6a4..5fd4d2172a0 100644 --- a/include/TrustWalletCore/TWBitcoinAddress.h +++ b/include/TrustWalletCore/TWBitcoinAddress.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWBitcoinFee.h b/include/TrustWalletCore/TWBitcoinFee.h index 33dc9c6c5ea..be4f116d644 100644 --- a/include/TrustWalletCore/TWBitcoinFee.h +++ b/include/TrustWalletCore/TWBitcoinFee.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWBitcoinMessageSigner.h b/include/TrustWalletCore/TWBitcoinMessageSigner.h index 6b09918f3a6..4090ff4850f 100644 --- a/include/TrustWalletCore/TWBitcoinMessageSigner.h +++ b/include/TrustWalletCore/TWBitcoinMessageSigner.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWBitcoinScript.h b/include/TrustWalletCore/TWBitcoinScript.h index 94ed5ce020b..f1a7844bfb9 100644 --- a/include/TrustWalletCore/TWBitcoinScript.h +++ b/include/TrustWalletCore/TWBitcoinScript.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWBitcoinSigHashType.h b/include/TrustWalletCore/TWBitcoinSigHashType.h index c71bde2d480..4d7e9c49a38 100644 --- a/include/TrustWalletCore/TWBitcoinSigHashType.h +++ b/include/TrustWalletCore/TWBitcoinSigHashType.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWBlockchain.h b/include/TrustWalletCore/TWBlockchain.h index 94187ca6899..faf4c575c1b 100644 --- a/include/TrustWalletCore/TWBlockchain.h +++ b/include/TrustWalletCore/TWBlockchain.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWCardano.h b/include/TrustWalletCore/TWCardano.h index 0892d616183..17bd9ebb61f 100644 --- a/include/TrustWalletCore/TWCardano.h +++ b/include/TrustWalletCore/TWCardano.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 9aeca0a9953..f18f8ff9c67 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWCoinTypeConfiguration.h b/include/TrustWalletCore/TWCoinTypeConfiguration.h index c83db7753e0..ca71f4dc56e 100644 --- a/include/TrustWalletCore/TWCoinTypeConfiguration.h +++ b/include/TrustWalletCore/TWCoinTypeConfiguration.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWCurve.h b/include/TrustWalletCore/TWCurve.h index f04256c62c2..3b7f2b003bd 100644 --- a/include/TrustWalletCore/TWCurve.h +++ b/include/TrustWalletCore/TWCurve.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWData.h b/include/TrustWalletCore/TWData.h index 8c498d81cc4..77dc7c625b6 100644 --- a/include/TrustWalletCore/TWData.h +++ b/include/TrustWalletCore/TWData.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWDataVector.h b/include/TrustWalletCore/TWDataVector.h index 3f950635aea..fbfae776ad2 100644 --- a/include/TrustWalletCore/TWDataVector.h +++ b/include/TrustWalletCore/TWDataVector.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWDerivationPath.h b/include/TrustWalletCore/TWDerivationPath.h index 0d116870e93..ccec3051138 100644 --- a/include/TrustWalletCore/TWDerivationPath.h +++ b/include/TrustWalletCore/TWDerivationPath.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWDerivationPathIndex.h b/include/TrustWalletCore/TWDerivationPathIndex.h index fe7d93ae2c0..a015f37b5f5 100644 --- a/include/TrustWalletCore/TWDerivationPathIndex.h +++ b/include/TrustWalletCore/TWDerivationPathIndex.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWEthereum.h b/include/TrustWalletCore/TWEthereum.h index 20007e4aaff..9ff49f208b2 100644 --- a/include/TrustWalletCore/TWEthereum.h +++ b/include/TrustWalletCore/TWEthereum.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWEthereumAbi.h b/include/TrustWalletCore/TWEthereumAbi.h index 33b976dc61a..f2d23a4dec5 100644 --- a/include/TrustWalletCore/TWEthereumAbi.h +++ b/include/TrustWalletCore/TWEthereumAbi.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWEthereumAbiFunction.h b/include/TrustWalletCore/TWEthereumAbiFunction.h index d07199a2844..cb5fbb9407c 100644 --- a/include/TrustWalletCore/TWEthereumAbiFunction.h +++ b/include/TrustWalletCore/TWEthereumAbiFunction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWEthereumAbiValue.h b/include/TrustWalletCore/TWEthereumAbiValue.h index 0af2f1e22ad..fcbe14de839 100644 --- a/include/TrustWalletCore/TWEthereumAbiValue.h +++ b/include/TrustWalletCore/TWEthereumAbiValue.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWEthereumMessageSigner.h b/include/TrustWalletCore/TWEthereumMessageSigner.h index cf21d8a8ddd..6d73c338f6c 100644 --- a/include/TrustWalletCore/TWEthereumMessageSigner.h +++ b/include/TrustWalletCore/TWEthereumMessageSigner.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWEthereumRlp.h b/include/TrustWalletCore/TWEthereumRlp.h index 1644c86e1e2..361ac305cbc 100644 --- a/include/TrustWalletCore/TWEthereumRlp.h +++ b/include/TrustWalletCore/TWEthereumRlp.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWFIOAccount.h b/include/TrustWalletCore/TWFIOAccount.h index a8abca53615..e11a65c4b3d 100644 --- a/include/TrustWalletCore/TWFIOAccount.h +++ b/include/TrustWalletCore/TWFIOAccount.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWFilecoinAddressConverter.h b/include/TrustWalletCore/TWFilecoinAddressConverter.h index aa5e37b8000..b6c3689984c 100644 --- a/include/TrustWalletCore/TWFilecoinAddressConverter.h +++ b/include/TrustWalletCore/TWFilecoinAddressConverter.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWFilecoinAddressType.h b/include/TrustWalletCore/TWFilecoinAddressType.h index 98f82450d3d..5bab60774c5 100644 --- a/include/TrustWalletCore/TWFilecoinAddressType.h +++ b/include/TrustWalletCore/TWFilecoinAddressType.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWGroestlcoinAddress.h b/include/TrustWalletCore/TWGroestlcoinAddress.h index e6512db8c0b..52114afd736 100644 --- a/include/TrustWalletCore/TWGroestlcoinAddress.h +++ b/include/TrustWalletCore/TWGroestlcoinAddress.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWHDVersion.h b/include/TrustWalletCore/TWHDVersion.h index 6e726bef840..ceb7733e676 100644 --- a/include/TrustWalletCore/TWHDVersion.h +++ b/include/TrustWalletCore/TWHDVersion.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWHDWallet.h b/include/TrustWalletCore/TWHDWallet.h index e48e9bd7050..9e902a55587 100644 --- a/include/TrustWalletCore/TWHDWallet.h +++ b/include/TrustWalletCore/TWHDWallet.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWHash.h b/include/TrustWalletCore/TWHash.h index f8f4a9743e2..06024bb4e4f 100644 --- a/include/TrustWalletCore/TWHash.h +++ b/include/TrustWalletCore/TWHash.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWLiquidStaking.h b/include/TrustWalletCore/TWLiquidStaking.h index 4829ef3b594..a50f3e2709b 100644 --- a/include/TrustWalletCore/TWLiquidStaking.h +++ b/include/TrustWalletCore/TWLiquidStaking.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once #include "TWBase.h" diff --git a/include/TrustWalletCore/TWMnemonic.h b/include/TrustWalletCore/TWMnemonic.h index 6baa9b1113c..2cfba1dba70 100644 --- a/include/TrustWalletCore/TWMnemonic.h +++ b/include/TrustWalletCore/TWMnemonic.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWNEARAccount.h b/include/TrustWalletCore/TWNEARAccount.h index 8daa5365cf9..cd8fbb4b97a 100644 --- a/include/TrustWalletCore/TWNEARAccount.h +++ b/include/TrustWalletCore/TWNEARAccount.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWNervosAddress.h b/include/TrustWalletCore/TWNervosAddress.h index eedceb86f08..0254c2bf5cf 100644 --- a/include/TrustWalletCore/TWNervosAddress.h +++ b/include/TrustWalletCore/TWNervosAddress.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWPBKDF2.h b/include/TrustWalletCore/TWPBKDF2.h index b7070849dbf..86c6cca6801 100644 --- a/include/TrustWalletCore/TWPBKDF2.h +++ b/include/TrustWalletCore/TWPBKDF2.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWPrivateKey.h b/include/TrustWalletCore/TWPrivateKey.h index 2e32b19f532..5fdfc61bcde 100644 --- a/include/TrustWalletCore/TWPrivateKey.h +++ b/include/TrustWalletCore/TWPrivateKey.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWPrivateKeyType.h b/include/TrustWalletCore/TWPrivateKeyType.h index a51aebae791..ee9255e0893 100644 --- a/include/TrustWalletCore/TWPrivateKeyType.h +++ b/include/TrustWalletCore/TWPrivateKeyType.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWPublicKey.h b/include/TrustWalletCore/TWPublicKey.h index 326f694d073..7187ac564af 100644 --- a/include/TrustWalletCore/TWPublicKey.h +++ b/include/TrustWalletCore/TWPublicKey.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWPublicKeyType.h b/include/TrustWalletCore/TWPublicKeyType.h index 894292ad5f4..f175fc8c471 100644 --- a/include/TrustWalletCore/TWPublicKeyType.h +++ b/include/TrustWalletCore/TWPublicKeyType.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWPurpose.h b/include/TrustWalletCore/TWPurpose.h index 142ed4f10c9..8cfa9cd91ae 100644 --- a/include/TrustWalletCore/TWPurpose.h +++ b/include/TrustWalletCore/TWPurpose.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWRippleXAddress.h b/include/TrustWalletCore/TWRippleXAddress.h index ff412711003..3c9256d2613 100644 --- a/include/TrustWalletCore/TWRippleXAddress.h +++ b/include/TrustWalletCore/TWRippleXAddress.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWSS58AddressType.h b/include/TrustWalletCore/TWSS58AddressType.h index 846c9d8c60f..9f9010c670e 100644 --- a/include/TrustWalletCore/TWSS58AddressType.h +++ b/include/TrustWalletCore/TWSS58AddressType.h @@ -1,9 +1,7 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWSegwitAddress.h b/include/TrustWalletCore/TWSegwitAddress.h index 29c81571132..452b4bc2ed0 100644 --- a/include/TrustWalletCore/TWSegwitAddress.h +++ b/include/TrustWalletCore/TWSegwitAddress.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWSolanaAddress.h b/include/TrustWalletCore/TWSolanaAddress.h index 9e9c4ede571..78ed40af7d2 100644 --- a/include/TrustWalletCore/TWSolanaAddress.h +++ b/include/TrustWalletCore/TWSolanaAddress.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWStarkExMessageSigner.h b/include/TrustWalletCore/TWStarkExMessageSigner.h index 63e0d2eb7cc..d5299f4f026 100644 --- a/include/TrustWalletCore/TWStarkExMessageSigner.h +++ b/include/TrustWalletCore/TWStarkExMessageSigner.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWStarkWare.h b/include/TrustWalletCore/TWStarkWare.h index 1f767ea6d71..1ff02199f71 100644 --- a/include/TrustWalletCore/TWStarkWare.h +++ b/include/TrustWalletCore/TWStarkWare.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWStellarMemoType.h b/include/TrustWalletCore/TWStellarMemoType.h index 812edbf4fda..8f6b66eec8f 100644 --- a/include/TrustWalletCore/TWStellarMemoType.h +++ b/include/TrustWalletCore/TWStellarMemoType.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWStellarPassphrase.h b/include/TrustWalletCore/TWStellarPassphrase.h index d56187244eb..307afb84865 100644 --- a/include/TrustWalletCore/TWStellarPassphrase.h +++ b/include/TrustWalletCore/TWStellarPassphrase.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWStellarVersionByte.h b/include/TrustWalletCore/TWStellarVersionByte.h index f2df0f65215..94f125ab94c 100644 --- a/include/TrustWalletCore/TWStellarVersionByte.h +++ b/include/TrustWalletCore/TWStellarVersionByte.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWStoredKey.h b/include/TrustWalletCore/TWStoredKey.h index 01efa96e691..e378f7cdd42 100644 --- a/include/TrustWalletCore/TWStoredKey.h +++ b/include/TrustWalletCore/TWStoredKey.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWStoredKeyEncryption.h b/include/TrustWalletCore/TWStoredKeyEncryption.h index 856b407157b..ccb0d7cfcac 100644 --- a/include/TrustWalletCore/TWStoredKeyEncryption.h +++ b/include/TrustWalletCore/TWStoredKeyEncryption.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWStoredKeyEncryptionLevel.h b/include/TrustWalletCore/TWStoredKeyEncryptionLevel.h index 071df20c97f..6a7ceb7bcbc 100644 --- a/include/TrustWalletCore/TWStoredKeyEncryptionLevel.h +++ b/include/TrustWalletCore/TWStoredKeyEncryptionLevel.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWString.h b/include/TrustWalletCore/TWString.h index 336b88922f4..9cfc8e77bfc 100644 --- a/include/TrustWalletCore/TWString.h +++ b/include/TrustWalletCore/TWString.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWTHORChainSwap.h b/include/TrustWalletCore/TWTHORChainSwap.h index bcd35a0911e..11844425bbe 100644 --- a/include/TrustWalletCore/TWTHORChainSwap.h +++ b/include/TrustWalletCore/TWTHORChainSwap.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once #include "TWBase.h" diff --git a/include/TrustWalletCore/TWTezosMessageSigner.h b/include/TrustWalletCore/TWTezosMessageSigner.h index bcb99bf8a85..be1f586f448 100644 --- a/include/TrustWalletCore/TWTezosMessageSigner.h +++ b/include/TrustWalletCore/TWTezosMessageSigner.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWTransactionCompiler.h b/include/TrustWalletCore/TWTransactionCompiler.h index f9ecf2d6c86..67dfc4f0124 100644 --- a/include/TrustWalletCore/TWTransactionCompiler.h +++ b/include/TrustWalletCore/TWTransactionCompiler.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWTronMessageSigner.h b/include/TrustWalletCore/TWTronMessageSigner.h index 56f326d49a4..d20baba14c1 100644 --- a/include/TrustWalletCore/TWTronMessageSigner.h +++ b/include/TrustWalletCore/TWTronMessageSigner.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWWalletConnectRequest.h b/include/TrustWalletCore/TWWalletConnectRequest.h index a65e7d9ed3d..41ba895457c 100644 --- a/include/TrustWalletCore/TWWalletConnectRequest.h +++ b/include/TrustWalletCore/TWWalletConnectRequest.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/include/TrustWalletCore/TWWebAuthn.h b/include/TrustWalletCore/TWWebAuthn.h index 4d988daaecf..c3f24b27a2b 100644 --- a/include/TrustWalletCore/TWWebAuthn.h +++ b/include/TrustWalletCore/TWWebAuthn.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once #include "TWBase.h" diff --git a/jni/android/AnySigner.c b/jni/android/AnySigner.c index d9ac054adc4..fd1704562f5 100644 --- a/jni/android/AnySigner.c +++ b/jni/android/AnySigner.c @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/jni/android/AnySigner.h b/jni/android/AnySigner.h index b51323a6554..566d446a7ee 100644 --- a/jni/android/AnySigner.h +++ b/jni/android/AnySigner.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #ifndef JNI_TW_ANYSIGNER_H #define JNI_TW_ANYSIGNER_H diff --git a/jni/cpp/Random.cpp b/jni/cpp/Random.cpp index 8e583ae5a92..caf305c8ef1 100644 --- a/jni/cpp/Random.cpp +++ b/jni/cpp/Random.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/jni/cpp/TWJNI.h b/jni/cpp/TWJNI.h index 4fc8d55d71f..86fc962ca29 100644 --- a/jni/cpp/TWJNI.h +++ b/jni/cpp/TWJNI.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/jni/cpp/TWJNIData.cpp b/jni/cpp/TWJNIData.cpp index 47e294f64b0..942505f593f 100644 --- a/jni/cpp/TWJNIData.cpp +++ b/jni/cpp/TWJNIData.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/jni/cpp/TWJNIData.h b/jni/cpp/TWJNIData.h index 39d3ca47aa9..9d1c6730368 100644 --- a/jni/cpp/TWJNIData.h +++ b/jni/cpp/TWJNIData.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/jni/cpp/TWJNIString.cpp b/jni/cpp/TWJNIString.cpp index 95b017fac2e..996eb0c747c 100644 --- a/jni/cpp/TWJNIString.cpp +++ b/jni/cpp/TWJNIString.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/jni/cpp/TWJNIString.h b/jni/cpp/TWJNIString.h index e6452df0134..1691fe47abb 100644 --- a/jni/cpp/TWJNIString.h +++ b/jni/cpp/TWJNIString.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/jni/java/wallet/core/java/AnySigner.java b/jni/java/wallet/core/java/AnySigner.java index 49c7770d3d1..21caaa185d4 100644 --- a/jni/java/wallet/core/java/AnySigner.java +++ b/jni/java/wallet/core/java/AnySigner.java @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package wallet.core.java; diff --git a/jni/kotlin/AnySigner.c b/jni/kotlin/AnySigner.c index 57c215eb34c..83211fb3512 100644 --- a/jni/kotlin/AnySigner.c +++ b/jni/kotlin/AnySigner.c @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/jni/kotlin/AnySigner.h b/jni/kotlin/AnySigner.h index 05742d57122..eac2bd9340b 100644 --- a/jni/kotlin/AnySigner.h +++ b/jni/kotlin/AnySigner.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #ifndef JNI_TW_ANYSIGNER_H #define JNI_TW_ANYSIGNER_H diff --git a/kotlin/wallet-core-kotlin/src/commonAndroidJvmMain/kotlin/com/trustwallet/core/AnySigner.kt b/kotlin/wallet-core-kotlin/src/commonAndroidJvmMain/kotlin/com/trustwallet/core/AnySigner.kt index 374d66218b9..e6b348433c4 100644 --- a/kotlin/wallet-core-kotlin/src/commonAndroidJvmMain/kotlin/com/trustwallet/core/AnySigner.kt +++ b/kotlin/wallet-core-kotlin/src/commonAndroidJvmMain/kotlin/com/trustwallet/core/AnySigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core diff --git a/kotlin/wallet-core-kotlin/src/commonMain/kotlin/com/trustwallet/core/AnySigner.kt b/kotlin/wallet-core-kotlin/src/commonMain/kotlin/com/trustwallet/core/AnySigner.kt index 85e195c229e..576aa1d32cb 100644 --- a/kotlin/wallet-core-kotlin/src/commonMain/kotlin/com/trustwallet/core/AnySigner.kt +++ b/kotlin/wallet-core-kotlin/src/commonMain/kotlin/com/trustwallet/core/AnySigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core diff --git a/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/AnySigner.kt b/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/AnySigner.kt index fd1cb8ed651..6de0b110bea 100644 --- a/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/AnySigner.kt +++ b/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/AnySigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core diff --git a/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/ByteArrayExt.kt b/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/ByteArrayExt.kt index 8498cb26061..ec5923272a1 100644 --- a/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/ByteArrayExt.kt +++ b/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/ByteArrayExt.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core diff --git a/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/StringExt.kt b/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/StringExt.kt index 19f224a22b7..2eb7fdd311e 100644 --- a/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/StringExt.kt +++ b/kotlin/wallet-core-kotlin/src/iosMain/kotlin/com/trustwallet/core/StringExt.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core diff --git a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/WalletCore.kt b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/WalletCore.kt index ba7c30845c5..a454889ea72 100644 --- a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/WalletCore.kt +++ b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/WalletCore.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import com.trustwallet.core.* import kotlin.js.Promise diff --git a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/AnySigner.kt b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/AnySigner.kt index b9e28700f97..d62bf4f81c3 100644 --- a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/AnySigner.kt +++ b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/AnySigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core diff --git a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/ByteArray.kt b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/ByteArray.kt index 615d8c38db9..9163d5bdca2 100644 --- a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/ByteArray.kt +++ b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/ByteArray.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core diff --git a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/JsAnySigner.kt b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/JsAnySigner.kt index f387eccbe7d..75be5fbd8d7 100644 --- a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/JsAnySigner.kt +++ b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/JsAnySigner.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. package com.trustwallet.core diff --git a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/JsWalletCore.kt b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/JsWalletCore.kt index f2d75c7c361..0865e64e531 100644 --- a/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/JsWalletCore.kt +++ b/kotlin/wallet-core-kotlin/src/jsMain/kotlin/com/trustwallet/core/JsWalletCore.kt @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. @file:Suppress("PropertyName") diff --git a/protobuf-plugin/CMakeLists.txt b/protobuf-plugin/CMakeLists.txt index 57b7ac59d83..52694a9c14e 100644 --- a/protobuf-plugin/CMakeLists.txt +++ b/protobuf-plugin/CMakeLists.txt @@ -1,8 +1,6 @@ -# Copyright © 2017-2022 Trust Wallet. +# SPDX-License-Identifier: Apache-2.0 # -# This file is part of Trust. The full Trust copyright notice, including -# terms governing use, modification, and redistribution, is contained in the -# file LICENSE at the root of the source code distribution tree. +# Copyright © 2017 Trust Wallet. cmake_minimum_required(VERSION 3.2 FATAL_ERROR) project(TrustWalletCoreProtobufPlugin) diff --git a/protobuf-plugin/c_typedef.cc b/protobuf-plugin/c_typedef.cc index 9cd48a5e798..5197185f9ac 100644 --- a/protobuf-plugin/c_typedef.cc +++ b/protobuf-plugin/c_typedef.cc @@ -19,11 +19,9 @@ class Generator : public compiler::CodeGenerator { io::Printer printer(output.get(), '$'); printer.Print( - "// Copyright © 2017-2020 Trust Wallet.\n" + "// SPDX-License-Identifier: Apache-2.0\n" "//\n" - "// This file is part of Trust. The full Trust copyright notice, including\n" - "// terms governing use, modification, and redistribution, is contained in the\n" - "// file LICENSE at the root of the source code distribution tree.\n" + "// Copyright © 2017 Trust Wallet.\n" "//\n" "// This is a GENERATED FILE, changes made here WILL BE LOST.\n" "\n" diff --git a/protobuf-plugin/swift_typealias.cc b/protobuf-plugin/swift_typealias.cc index 57a169dea03..40e84df0ee9 100644 --- a/protobuf-plugin/swift_typealias.cc +++ b/protobuf-plugin/swift_typealias.cc @@ -21,11 +21,9 @@ class Generator : public compiler::CodeGenerator { io::Printer printer(output.get(), '$'); printer.Print( - "// Copyright © 2017-2020 Trust Wallet.\n" + "// SPDX-License-Identifier: Apache-2.0\n" "//\n" - "// This file is part of Trust. The full Trust copyright notice, including\n" - "// terms governing use, modification, and redistribution, is contained in the\n" - "// file LICENSE at the root of the source code distribution tree.\n" + "// Copyright © 2017 Trust Wallet.\n" "\n" ); diff --git a/rust/chains/tw_binance/src/address.rs b/rust/chains/tw_binance/src/address.rs index 6fecbd6f3ac..ec4cffad8f3 100644 --- a/rust/chains/tw_binance/src/address.rs +++ b/rust/chains/tw_binance/src/address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde::{Deserialize, Serialize}; use std::fmt; diff --git a/rust/chains/tw_binance/src/amino.rs b/rust/chains/tw_binance/src/amino.rs index 048b0bdedc1..6bcc017dd48 100644 --- a/rust/chains/tw_binance/src/amino.rs +++ b/rust/chains/tw_binance/src/amino.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use quick_protobuf::MessageWrite; use tw_encoding::{EncodingError, EncodingResult}; diff --git a/rust/chains/tw_binance/src/compiler.rs b/rust/chains/tw_binance/src/compiler.rs index 0a289ec1c3f..8c9012d2910 100644 --- a/rust/chains/tw_binance/src/compiler.rs +++ b/rust/chains/tw_binance/src/compiler.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::context::BinanceContext; use crate::modules::preimager::{JsonPreimager, JsonTxPreimage}; diff --git a/rust/chains/tw_binance/src/context.rs b/rust/chains/tw_binance/src/context.rs index a70977bcd46..4cd308db000 100644 --- a/rust/chains/tw_binance/src/context.rs +++ b/rust/chains/tw_binance/src/context.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::BinanceAddress; use tw_cosmos_sdk::context::CosmosContext; diff --git a/rust/chains/tw_binance/src/entry.rs b/rust/chains/tw_binance/src/entry.rs index 4589e2d31a8..f2e979c3b1e 100644 --- a/rust/chains/tw_binance/src/entry.rs +++ b/rust/chains/tw_binance/src/entry.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::BinanceAddress; use crate::compiler::BinanceCompiler; diff --git a/rust/chains/tw_binance/src/lib.rs b/rust/chains/tw_binance/src/lib.rs index 606d6f3000d..6df844a203d 100644 --- a/rust/chains/tw_binance/src/lib.rs +++ b/rust/chains/tw_binance/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod address; pub mod amino; diff --git a/rust/chains/tw_binance/src/modules/mod.rs b/rust/chains/tw_binance/src/modules/mod.rs index 5300755a36f..943cd0f39ce 100644 --- a/rust/chains/tw_binance/src/modules/mod.rs +++ b/rust/chains/tw_binance/src/modules/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod preimager; pub mod serializer; diff --git a/rust/chains/tw_binance/src/modules/preimager.rs b/rust/chains/tw_binance/src/modules/preimager.rs index cbb4969d139..ba881f8beae 100644 --- a/rust/chains/tw_binance/src/modules/preimager.rs +++ b/rust/chains/tw_binance/src/modules/preimager.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::transaction::UnsignedTransaction; use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; diff --git a/rust/chains/tw_binance/src/modules/serializer.rs b/rust/chains/tw_binance/src/modules/serializer.rs index d77e85d05f3..cb1ea5ff4d6 100644 --- a/rust/chains/tw_binance/src/modules/serializer.rs +++ b/rust/chains/tw_binance/src/modules/serializer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::amino::AminoEncoder; use crate::transaction::SignedTransaction; diff --git a/rust/chains/tw_binance/src/modules/tx_builder.rs b/rust/chains/tw_binance/src/modules/tx_builder.rs index e5b47fbedf5..4b6fb2ebbea 100644 --- a/rust/chains/tw_binance/src/modules/tx_builder.rs +++ b/rust/chains/tw_binance/src/modules/tx_builder.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::transaction::message::{BinanceMessageEnum, TWBinanceProto}; use crate::transaction::UnsignedTransaction; diff --git a/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs b/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs index 893b408488b..a1e60b7da16 100644 --- a/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs +++ b/rust/chains/tw_binance/src/modules/wallet_connect/connector.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::modules::tx_builder::TxBuilder; use crate::modules::wallet_connect::types::SignAminoRequest; diff --git a/rust/chains/tw_binance/src/modules/wallet_connect/mod.rs b/rust/chains/tw_binance/src/modules/wallet_connect/mod.rs index 9e2233feaa6..1a90ae8a9cf 100644 --- a/rust/chains/tw_binance/src/modules/wallet_connect/mod.rs +++ b/rust/chains/tw_binance/src/modules/wallet_connect/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod connector; pub mod types; diff --git a/rust/chains/tw_binance/src/modules/wallet_connect/types.rs b/rust/chains/tw_binance/src/modules/wallet_connect/types.rs index e2b2f599b6e..f5ac124e47e 100644 --- a/rust/chains/tw_binance/src/modules/wallet_connect/types.rs +++ b/rust/chains/tw_binance/src/modules/wallet_connect/types.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::transaction::UnsignedTransaction; use serde::Deserialize; diff --git a/rust/chains/tw_binance/src/signature.rs b/rust/chains/tw_binance/src/signature.rs index eda4adbf4c7..45e1b5c6fca 100644 --- a/rust/chains/tw_binance/src/signature.rs +++ b/rust/chains/tw_binance/src/signature.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_keypair::ecdsa::secp256k1; diff --git a/rust/chains/tw_binance/src/signer.rs b/rust/chains/tw_binance/src/signer.rs index 8913a9c5e6a..7f6a7809e33 100644 --- a/rust/chains/tw_binance/src/signer.rs +++ b/rust/chains/tw_binance/src/signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::context::BinanceContext; use crate::modules::preimager::{JsonPreimager, JsonTxPreimage}; diff --git a/rust/chains/tw_binance/src/transaction/message/htlt_order.rs b/rust/chains/tw_binance/src/transaction/message/htlt_order.rs index 2d5677ccaa5..f677a0dfc7e 100644 --- a/rust/chains/tw_binance/src/transaction/message/htlt_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/htlt_order.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::BinanceAddress; use crate::amino::AminoEncoder; diff --git a/rust/chains/tw_binance/src/transaction/message/mod.rs b/rust/chains/tw_binance/src/transaction/message/mod.rs index 78518de6b15..bb7bcf8c74c 100644 --- a/rust/chains/tw_binance/src/transaction/message/mod.rs +++ b/rust/chains/tw_binance/src/transaction/message/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde::{Deserialize, Serialize, Serializer}; use tw_coin_entry::coin_context::CoinContext; diff --git a/rust/chains/tw_binance/src/transaction/message/send_order.rs b/rust/chains/tw_binance/src/transaction/message/send_order.rs index 23f9460c160..ef1b5107134 100644 --- a/rust/chains/tw_binance/src/transaction/message/send_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/send_order.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::BinanceAddress; use crate::amino::AminoEncoder; diff --git a/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs b/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs index 39008818681..19e4dce4a80 100644 --- a/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs +++ b/rust/chains/tw_binance/src/transaction/message/side_chain_delegate.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::BinanceAddress; use crate::amino::AminoEncoder; diff --git a/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs b/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs index 0581dba133c..2647d6553cb 100644 --- a/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/time_lock_order.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::BinanceAddress; use crate::amino::AminoEncoder; diff --git a/rust/chains/tw_binance/src/transaction/message/token_order.rs b/rust/chains/tw_binance/src/transaction/message/token_order.rs index 1d2c442cd9a..ecff5236773 100644 --- a/rust/chains/tw_binance/src/transaction/message/token_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/token_order.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::BinanceAddress; use crate::amino::AminoEncoder; diff --git a/rust/chains/tw_binance/src/transaction/message/trade_order.rs b/rust/chains/tw_binance/src/transaction/message/trade_order.rs index 9020bb00f9d..5aced178ea6 100644 --- a/rust/chains/tw_binance/src/transaction/message/trade_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/trade_order.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::BinanceAddress; use crate::amino::AminoEncoder; diff --git a/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs b/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs index 68030deb104..ae8aabff1ca 100644 --- a/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs +++ b/rust/chains/tw_binance/src/transaction/message/tranfer_out_order.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::BinanceAddress; use crate::amino::AminoEncoder; diff --git a/rust/chains/tw_binance/src/transaction/mod.rs b/rust/chains/tw_binance/src/transaction/mod.rs index 021f7180203..fbd24f33ebe 100644 --- a/rust/chains/tw_binance/src/transaction/mod.rs +++ b/rust/chains/tw_binance/src/transaction/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::signature::BinanceSignature; use crate::transaction::message::BinanceMessageEnum; diff --git a/rust/chains/tw_cosmos/src/entry.rs b/rust/chains/tw_cosmos/src/entry.rs index fad4cfb1735..53eb31c218c 100644 --- a/rust/chains/tw_cosmos/src/entry.rs +++ b/rust/chains/tw_cosmos/src/entry.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; diff --git a/rust/chains/tw_cosmos/src/lib.rs b/rust/chains/tw_cosmos/src/lib.rs index 1afc00798db..c080f3da3cf 100644 --- a/rust/chains/tw_cosmos/src/lib.rs +++ b/rust/chains/tw_cosmos/src/lib.rs @@ -1,7 +1,5 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod entry; diff --git a/rust/chains/tw_greenfield/src/address.rs b/rust/chains/tw_greenfield/src/address.rs index 679da7b4e35..9318c48cecc 100644 --- a/rust/chains/tw_greenfield/src/address.rs +++ b/rust/chains/tw_greenfield/src/address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde::Serialize; use std::fmt; diff --git a/rust/chains/tw_greenfield/src/compiler.rs b/rust/chains/tw_greenfield/src/compiler.rs index e1133c119d1..46485a5bfe1 100644 --- a/rust/chains/tw_greenfield/src/compiler.rs +++ b/rust/chains/tw_greenfield/src/compiler.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::context::GreenfieldContext; use crate::modules::eip712_signer::{Eip712Signer, Eip712TxPreimage}; diff --git a/rust/chains/tw_greenfield/src/context.rs b/rust/chains/tw_greenfield/src/context.rs index 5bc6bbfcd72..f4f9db90b25 100644 --- a/rust/chains/tw_greenfield/src/context.rs +++ b/rust/chains/tw_greenfield/src/context.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::GreenfieldAddress; use crate::public_key::GreenfieldPublicKey; diff --git a/rust/chains/tw_greenfield/src/eip712_types.rs b/rust/chains/tw_greenfield/src/eip712_types.rs index 6cb4807080f..3bf8f103d52 100644 --- a/rust/chains/tw_greenfield/src/eip712_types.rs +++ b/rust/chains/tw_greenfield/src/eip712_types.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::transaction::GreenfieldFee; use core::fmt; diff --git a/rust/chains/tw_greenfield/src/entry.rs b/rust/chains/tw_greenfield/src/entry.rs index c3aa6fd68d0..a01e6146ae3 100644 --- a/rust/chains/tw_greenfield/src/entry.rs +++ b/rust/chains/tw_greenfield/src/entry.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::GreenfieldAddress; use crate::compiler::GreenfieldCompiler; diff --git a/rust/chains/tw_greenfield/src/lib.rs b/rust/chains/tw_greenfield/src/lib.rs index 8c018a0cdba..02e2d565253 100644 --- a/rust/chains/tw_greenfield/src/lib.rs +++ b/rust/chains/tw_greenfield/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod address; pub mod compiler; diff --git a/rust/chains/tw_greenfield/src/modules/eip712_signer.rs b/rust/chains/tw_greenfield/src/modules/eip712_signer.rs index a44796c821b..7b9fcb34f05 100644 --- a/rust/chains/tw_greenfield/src/modules/eip712_signer.rs +++ b/rust/chains/tw_greenfield/src/modules/eip712_signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::eip712_types::{ Eip712Domain, Eip712Fee, Eip712Transaction, Eip712TypedMsg, MsgPropertyName, diff --git a/rust/chains/tw_greenfield/src/modules/mod.rs b/rust/chains/tw_greenfield/src/modules/mod.rs index 4ca8bc01e20..a9eed00576a 100644 --- a/rust/chains/tw_greenfield/src/modules/mod.rs +++ b/rust/chains/tw_greenfield/src/modules/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod eip712_signer; pub mod tx_builder; diff --git a/rust/chains/tw_greenfield/src/modules/tx_builder.rs b/rust/chains/tw_greenfield/src/modules/tx_builder.rs index 78ae1a73811..2fef62ccd1e 100644 --- a/rust/chains/tw_greenfield/src/modules/tx_builder.rs +++ b/rust/chains/tw_greenfield/src/modules/tx_builder.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::GreenfieldAddress; use crate::public_key::GreenfieldPublicKey; diff --git a/rust/chains/tw_greenfield/src/public_key.rs b/rust/chains/tw_greenfield/src/public_key.rs index 8486fef04e0..a10f6d32954 100644 --- a/rust/chains/tw_greenfield/src/public_key.rs +++ b/rust/chains/tw_greenfield/src/public_key.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::coin_context::CoinContext; use tw_cosmos_sdk::public_key::{CosmosPublicKey, JsonPublicKey, ProtobufPublicKey}; diff --git a/rust/chains/tw_greenfield/src/signature.rs b/rust/chains/tw_greenfield/src/signature.rs index bee96a99b9f..bc2094edb67 100644 --- a/rust/chains/tw_greenfield/src/signature.rs +++ b/rust/chains/tw_greenfield/src/signature.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_cosmos_sdk::signature::CosmosSignature; use tw_evm::message::signature::{MessageSignature, SignatureType}; diff --git a/rust/chains/tw_greenfield/src/signer.rs b/rust/chains/tw_greenfield/src/signer.rs index 43b1900c66f..6b0d2c88e28 100644 --- a/rust/chains/tw_greenfield/src/signer.rs +++ b/rust/chains/tw_greenfield/src/signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::compiler::GreenfieldCompiler; use crate::modules::eip712_signer::{Eip712Signer, Eip712TxPreimage}; diff --git a/rust/chains/tw_greenfield/src/transaction/message/mod.rs b/rust/chains/tw_greenfield/src/transaction/message/mod.rs index 2c7f8430c65..7921931c696 100644 --- a/rust/chains/tw_greenfield/src/transaction/message/mod.rs +++ b/rust/chains/tw_greenfield/src/transaction/message/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::eip712_types::MsgPropertyType; use tw_cosmos_sdk::transaction::message::{CosmosMessage, CosmosMessageBox}; diff --git a/rust/chains/tw_greenfield/src/transaction/message/send_order.rs b/rust/chains/tw_greenfield/src/transaction/message/send_order.rs index 85fb9da35f3..8d9ebc62ccf 100644 --- a/rust/chains/tw_greenfield/src/transaction/message/send_order.rs +++ b/rust/chains/tw_greenfield/src/transaction/message/send_order.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::GreenfieldAddress; use crate::transaction::message::type_msg_amount::TypeMsgAmount; diff --git a/rust/chains/tw_greenfield/src/transaction/message/transfer_out.rs b/rust/chains/tw_greenfield/src/transaction/message/transfer_out.rs index dec3e261fac..f626db7d5a7 100644 --- a/rust/chains/tw_greenfield/src/transaction/message/transfer_out.rs +++ b/rust/chains/tw_greenfield/src/transaction/message/transfer_out.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::GreenfieldAddress; use crate::transaction::message::type_msg_amount::TypeMsgAmount; diff --git a/rust/chains/tw_greenfield/src/transaction/message/type_msg_amount.rs b/rust/chains/tw_greenfield/src/transaction/message/type_msg_amount.rs index 3b338c3acb7..e635367220b 100644 --- a/rust/chains/tw_greenfield/src/transaction/message/type_msg_amount.rs +++ b/rust/chains/tw_greenfield/src/transaction/message/type_msg_amount.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_evm::message::eip712::message_types::MessageTypesBuilder; use tw_evm::message::eip712::property::PropertyType; diff --git a/rust/chains/tw_greenfield/src/transaction/mod.rs b/rust/chains/tw_greenfield/src/transaction/mod.rs index 718cb9fd0a3..80f08f72da3 100644 --- a/rust/chains/tw_greenfield/src/transaction/mod.rs +++ b/rust/chains/tw_greenfield/src/transaction/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::GreenfieldAddress; use crate::context::GreenfieldContext; diff --git a/rust/chains/tw_greenfield/tests/eip712_signer.rs b/rust/chains/tw_greenfield/tests/eip712_signer.rs index 75d77eb1ba4..e20ab3b48fb 100644 --- a/rust/chains/tw_greenfield/tests/eip712_signer.rs +++ b/rust/chains/tw_greenfield/tests/eip712_signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_encoding::hex::{DecodeHex, ToHex}; diff --git a/rust/chains/tw_native_evmos/src/context.rs b/rust/chains/tw_native_evmos/src/context.rs index edb431767ac..2f2352dde83 100644 --- a/rust/chains/tw_native_evmos/src/context.rs +++ b/rust/chains/tw_native_evmos/src/context.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ethermint_public_key::EthermintEthSecp256PublicKey; use tw_cosmos_sdk::address::Address; diff --git a/rust/chains/tw_native_evmos/src/entry.rs b/rust/chains/tw_native_evmos/src/entry.rs index 9f1d0c9820a..017daede1db 100644 --- a/rust/chains/tw_native_evmos/src/entry.rs +++ b/rust/chains/tw_native_evmos/src/entry.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::context::NativeEvmosContext; use std::str::FromStr; diff --git a/rust/chains/tw_native_evmos/src/ethermint_public_key.rs b/rust/chains/tw_native_evmos/src/ethermint_public_key.rs index 5f53f261e5b..0059dc31871 100644 --- a/rust/chains/tw_native_evmos/src/ethermint_public_key.rs +++ b/rust/chains/tw_native_evmos/src/ethermint_public_key.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::coin_context::CoinContext; use tw_cosmos_sdk::proto::ethermint; diff --git a/rust/chains/tw_native_evmos/src/lib.rs b/rust/chains/tw_native_evmos/src/lib.rs index d7aa4534c83..0c7068cba6f 100644 --- a/rust/chains/tw_native_evmos/src/lib.rs +++ b/rust/chains/tw_native_evmos/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod context; pub mod entry; diff --git a/rust/chains/tw_native_injective/src/context.rs b/rust/chains/tw_native_injective/src/context.rs index 898c034d656..a9ac9bf6bfc 100644 --- a/rust/chains/tw_native_injective/src/context.rs +++ b/rust/chains/tw_native_injective/src/context.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::injective_public_key::InjectiveEthSecp256PublicKey; use tw_cosmos_sdk::address::Address; diff --git a/rust/chains/tw_native_injective/src/entry.rs b/rust/chains/tw_native_injective/src/entry.rs index ed1759554ec..caf1cbdf3bf 100644 --- a/rust/chains/tw_native_injective/src/entry.rs +++ b/rust/chains/tw_native_injective/src/entry.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::context::NativeInjectiveContext; use std::str::FromStr; diff --git a/rust/chains/tw_native_injective/src/injective_public_key.rs b/rust/chains/tw_native_injective/src/injective_public_key.rs index 084d47e94f0..47b01c575fd 100644 --- a/rust/chains/tw_native_injective/src/injective_public_key.rs +++ b/rust/chains/tw_native_injective/src/injective_public_key.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::coin_context::CoinContext; use tw_cosmos_sdk::proto::injective; diff --git a/rust/chains/tw_native_injective/src/lib.rs b/rust/chains/tw_native_injective/src/lib.rs index a61663bd72c..fb550eba23b 100644 --- a/rust/chains/tw_native_injective/src/lib.rs +++ b/rust/chains/tw_native_injective/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod context; pub mod entry; diff --git a/rust/chains/tw_thorchain/src/compiler.rs b/rust/chains/tw_thorchain/src/compiler.rs index a6fd1b8bbc8..7e3e81ad090 100644 --- a/rust/chains/tw_thorchain/src/compiler.rs +++ b/rust/chains/tw_thorchain/src/compiler.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::signing_input::ThorchainSigningInput; use tw_coin_entry::coin_context::CoinContext; diff --git a/rust/chains/tw_thorchain/src/entry.rs b/rust/chains/tw_thorchain/src/entry.rs index 4cd52da3965..7ec28508188 100644 --- a/rust/chains/tw_thorchain/src/entry.rs +++ b/rust/chains/tw_thorchain/src/entry.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::compiler::ThorchainCompiler; use crate::signer::ThorchainSigner; diff --git a/rust/chains/tw_thorchain/src/lib.rs b/rust/chains/tw_thorchain/src/lib.rs index be313341897..9abacf4dfac 100644 --- a/rust/chains/tw_thorchain/src/lib.rs +++ b/rust/chains/tw_thorchain/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod compiler; pub mod entry; diff --git a/rust/chains/tw_thorchain/src/signer.rs b/rust/chains/tw_thorchain/src/signer.rs index d086eacd1c3..7346be163af 100644 --- a/rust/chains/tw_thorchain/src/signer.rs +++ b/rust/chains/tw_thorchain/src/signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::signing_input::ThorchainSigningInput; use tw_coin_entry::coin_context::CoinContext; diff --git a/rust/chains/tw_thorchain/src/signing_input.rs b/rust/chains/tw_thorchain/src/signing_input.rs index 66863f7288f..429a62a6434 100644 --- a/rust/chains/tw_thorchain/src/signing_input.rs +++ b/rust/chains/tw_thorchain/src/signing_input.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::borrow::Cow; use tw_proto::Cosmos::Proto; diff --git a/rust/tw_any_coin/src/any_address.rs b/rust/tw_any_coin/src/any_address.rs index 5d5098482a9..4716d920da3 100644 --- a/rust/tw_any_coin/src/any_address.rs +++ b/rust/tw_any_coin/src/any_address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::derivation::Derivation; use tw_coin_entry::error::{AddressError, AddressResult}; diff --git a/rust/tw_any_coin/src/any_signer.rs b/rust/tw_any_coin/src/any_signer.rs index eea6859517b..2e7b3692fcf 100644 --- a/rust/tw_any_coin/src/any_signer.rs +++ b/rust/tw_any_coin/src/any_signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::error::{SigningError, SigningResult}; use tw_coin_registry::coin_type::CoinType; diff --git a/rust/tw_any_coin/src/ffi/mod.rs b/rust/tw_any_coin/src/ffi/mod.rs index 854c6cd5e98..8db000e6d80 100644 --- a/rust/tw_any_coin/src/ffi/mod.rs +++ b/rust/tw_any_coin/src/ffi/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod tw_any_address; pub mod tw_any_signer; diff --git a/rust/tw_any_coin/src/ffi/tw_any_address.rs b/rust/tw_any_coin/src/ffi/tw_any_address.rs index ba8d39163c5..4555d09ad25 100644 --- a/rust/tw_any_coin/src/ffi/tw_any_address.rs +++ b/rust/tw_any_coin/src/ffi/tw_any_address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![allow(clippy::missing_safety_doc)] diff --git a/rust/tw_any_coin/src/ffi/tw_any_signer.rs b/rust/tw_any_coin/src/ffi/tw_any_signer.rs index c0bde8e921c..8f6c8c4cea6 100644 --- a/rust/tw_any_coin/src/ffi/tw_any_signer.rs +++ b/rust/tw_any_coin/src/ffi/tw_any_signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![allow(clippy::missing_safety_doc)] diff --git a/rust/tw_any_coin/src/ffi/tw_message_signer.rs b/rust/tw_any_coin/src/ffi/tw_message_signer.rs index e1188890bf1..1bc3d4227d8 100644 --- a/rust/tw_any_coin/src/ffi/tw_message_signer.rs +++ b/rust/tw_any_coin/src/ffi/tw_message_signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![allow(clippy::missing_safety_doc)] diff --git a/rust/tw_any_coin/src/ffi/tw_transaction_compiler.rs b/rust/tw_any_coin/src/ffi/tw_transaction_compiler.rs index bd227f4426f..5c18816e1d9 100644 --- a/rust/tw_any_coin/src/ffi/tw_transaction_compiler.rs +++ b/rust/tw_any_coin/src/ffi/tw_transaction_compiler.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![allow(clippy::missing_safety_doc)] diff --git a/rust/tw_any_coin/src/ffi/tw_wallet_connect_request.rs b/rust/tw_any_coin/src/ffi/tw_wallet_connect_request.rs index fbbb5da4247..1bde2cffa77 100644 --- a/rust/tw_any_coin/src/ffi/tw_wallet_connect_request.rs +++ b/rust/tw_any_coin/src/ffi/tw_wallet_connect_request.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![allow(clippy::missing_safety_doc)] diff --git a/rust/tw_any_coin/src/lib.rs b/rust/tw_any_coin/src/lib.rs index 5788831c45a..84ca053fa3b 100644 --- a/rust/tw_any_coin/src/lib.rs +++ b/rust/tw_any_coin/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod any_address; pub mod any_signer; diff --git a/rust/tw_any_coin/src/message_signer.rs b/rust/tw_any_coin/src/message_signer.rs index 0266df3eed0..5ae2f83bc8f 100644 --- a/rust/tw_any_coin/src/message_signer.rs +++ b/rust/tw_any_coin/src/message_signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::error::SigningResult; use tw_coin_registry::coin_type::CoinType; diff --git a/rust/tw_any_coin/src/test_utils/address_utils.rs b/rust/tw_any_coin/src/test_utils/address_utils.rs index 8efd7de120e..d2ec4ba4b9d 100644 --- a/rust/tw_any_coin/src/test_utils/address_utils.rs +++ b/rust/tw_any_coin/src/test_utils/address_utils.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ffi::tw_any_address::{ tw_any_address_create_bech32_with_public_key, tw_any_address_create_with_string, diff --git a/rust/tw_any_coin/src/test_utils/mod.rs b/rust/tw_any_coin/src/test_utils/mod.rs index 2c5b412f804..7c86db82edc 100644 --- a/rust/tw_any_coin/src/test_utils/mod.rs +++ b/rust/tw_any_coin/src/test_utils/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod address_utils; pub mod sign_utils; diff --git a/rust/tw_any_coin/src/test_utils/sign_utils.rs b/rust/tw_any_coin/src/test_utils/sign_utils.rs index ff61b07dcce..fa34ec0c415 100644 --- a/rust/tw_any_coin/src/test_utils/sign_utils.rs +++ b/rust/tw_any_coin/src/test_utils/sign_utils.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ffi::tw_any_signer::tw_any_signer_sign; use crate::ffi::tw_transaction_compiler::{ diff --git a/rust/tw_any_coin/src/test_utils/wallet_connect_utils.rs b/rust/tw_any_coin/src/test_utils/wallet_connect_utils.rs index d270a3422f2..a4323bf9b95 100644 --- a/rust/tw_any_coin/src/test_utils/wallet_connect_utils.rs +++ b/rust/tw_any_coin/src/test_utils/wallet_connect_utils.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ffi::tw_wallet_connect_request::tw_wallet_connect_request_parse; use tw_coin_registry::coin_type::CoinType; diff --git a/rust/tw_any_coin/src/transaction_compiler.rs b/rust/tw_any_coin/src/transaction_compiler.rs index 85e5013bdb9..b25d472f858 100644 --- a/rust/tw_any_coin/src/transaction_compiler.rs +++ b/rust/tw_any_coin/src/transaction_compiler.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; use tw_coin_entry::error::{SigningError, SigningResult}; diff --git a/rust/tw_any_coin/src/wallet_connect_request.rs b/rust/tw_any_coin/src/wallet_connect_request.rs index 97a5f672ea5..72465e79ccd 100644 --- a/rust/tw_any_coin/src/wallet_connect_request.rs +++ b/rust/tw_any_coin/src/wallet_connect_request.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::error::{SigningError, SigningResult}; use tw_coin_registry::coin_type::CoinType; diff --git a/rust/tw_any_coin/tests/chain_tests.rs b/rust/tw_any_coin/tests/chain_tests.rs index c1b24a82062..4531f6d5665 100644 --- a/rust/tw_any_coin/tests/chain_tests.rs +++ b/rust/tw_any_coin/tests/chain_tests.rs @@ -1,7 +1,5 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. mod chains; diff --git a/rust/tw_any_coin/tests/chains/aptos/aptos_address.rs b/rust/tw_any_coin/tests/chains/aptos/aptos_address.rs index 788cdc5aeec..97a52410960 100644 --- a/rust/tw_any_coin/tests/chains/aptos/aptos_address.rs +++ b/rust/tw_any_coin/tests/chains/aptos/aptos_address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_any_coin::test_utils::address_utils::{ test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, diff --git a/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs b/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs index 114d7e9a46e..b2016790f38 100644 --- a/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs +++ b/rust/tw_any_coin/tests/chains/aptos/aptos_compile.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::chains::aptos::test_cases::transfer_b4d62afd::{ aptos_sign_transfer_input, expected_json, DATA_TO_SIGN, ENCODED, PRIVATE_KEY, RAW_TXN, diff --git a/rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs b/rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs index 6021ce8bb92..44933138f42 100644 --- a/rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs +++ b/rust/tw_any_coin/tests/chains/aptos/aptos_sign.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::chains::aptos::test_cases::transfer_b4d62afd::{ aptos_sign_transfer_input, expected_json, ENCODED, PRIVATE_KEY, RAW_TXN, SIGNATURE, diff --git a/rust/tw_any_coin/tests/chains/aptos/mod.rs b/rust/tw_any_coin/tests/chains/aptos/mod.rs index ecea2c75bfd..4aee9d56923 100644 --- a/rust/tw_any_coin/tests/chains/aptos/mod.rs +++ b/rust/tw_any_coin/tests/chains/aptos/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. mod aptos_address; mod aptos_compile; diff --git a/rust/tw_any_coin/tests/chains/aptos/test_cases.rs b/rust/tw_any_coin/tests/chains/aptos/test_cases.rs index 81b166f7247..41af3a01603 100644 --- a/rust/tw_any_coin/tests/chains/aptos/test_cases.rs +++ b/rust/tw_any_coin/tests/chains/aptos/test_cases.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde_json::{json, Value as Json}; use tw_proto::Aptos::Proto; diff --git a/rust/tw_any_coin/tests/chains/binance/binance_address.rs b/rust/tw_any_coin/tests/chains/binance/binance_address.rs index 076bcf9f61f..706b4bae54f 100644 --- a/rust/tw_any_coin/tests/chains/binance/binance_address.rs +++ b/rust/tw_any_coin/tests/chains/binance/binance_address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_any_coin::test_utils::address_utils::{ test_address_bech32_is_valid, test_address_get_data, test_address_invalid, diff --git a/rust/tw_any_coin/tests/chains/binance/binance_compile.rs b/rust/tw_any_coin/tests/chains/binance/binance_compile.rs index 6f3b0766e02..687390a28c5 100644 --- a/rust/tw_any_coin/tests/chains/binance/binance_compile.rs +++ b/rust/tw_any_coin/tests/chains/binance/binance_compile.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::chains::binance::make_token; use tw_any_coin::test_utils::sign_utils::{CompilerHelper, PreImageHelper}; diff --git a/rust/tw_any_coin/tests/chains/binance/binance_sign.rs b/rust/tw_any_coin/tests/chains/binance/binance_sign.rs index ecddd3e6f92..906f6d5a5e4 100644 --- a/rust/tw_any_coin/tests/chains/binance/binance_sign.rs +++ b/rust/tw_any_coin/tests/chains/binance/binance_sign.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::chains::binance::{ make_token, ACCOUNT_12_PRIVATE_KEY, ACCOUNT_15_PRIVATE_KEY, ACCOUNT_16_PRIVATE_KEY, diff --git a/rust/tw_any_coin/tests/chains/binance/binance_wallet_connect.rs b/rust/tw_any_coin/tests/chains/binance/binance_wallet_connect.rs index 60430cd5a99..109118da919 100644 --- a/rust/tw_any_coin/tests/chains/binance/binance_wallet_connect.rs +++ b/rust/tw_any_coin/tests/chains/binance/binance_wallet_connect.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::chains::binance::{make_token, ACCOUNT_19_PRIVATE_KEY}; use std::borrow::Cow; diff --git a/rust/tw_any_coin/tests/chains/binance/mod.rs b/rust/tw_any_coin/tests/chains/binance/mod.rs index a38fee8ce8a..a4f3860818a 100644 --- a/rust/tw_any_coin/tests/chains/binance/mod.rs +++ b/rust/tw_any_coin/tests/chains/binance/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_proto::Binance::Proto; diff --git a/rust/tw_any_coin/tests/chains/bitcoin/bitcoin_address.rs b/rust/tw_any_coin/tests/chains/bitcoin/bitcoin_address.rs index 0397c6c7768..b1eb00ef84c 100644 --- a/rust/tw_any_coin/tests/chains/bitcoin/bitcoin_address.rs +++ b/rust/tw_any_coin/tests/chains/bitcoin/bitcoin_address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_any_coin::test_utils::address_utils::{ test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, diff --git a/rust/tw_any_coin/tests/chains/bitcoin/mod.rs b/rust/tw_any_coin/tests/chains/bitcoin/mod.rs index 00682e69022..deb70ef4017 100644 --- a/rust/tw_any_coin/tests/chains/bitcoin/mod.rs +++ b/rust/tw_any_coin/tests/chains/bitcoin/mod.rs @@ -1,7 +1,5 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. mod bitcoin_address; diff --git a/rust/tw_any_coin/tests/chains/cosmos/cosmos_address.rs b/rust/tw_any_coin/tests/chains/cosmos/cosmos_address.rs index 964414dff44..51bf414bec3 100644 --- a/rust/tw_any_coin/tests/chains/cosmos/cosmos_address.rs +++ b/rust/tw_any_coin/tests/chains/cosmos/cosmos_address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_any_coin::test_utils::address_utils::{ test_address_bech32_is_valid, test_address_create_bech32_with_public_key, diff --git a/rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs b/rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs index b9140fb9731..5daa370bf10 100644 --- a/rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs +++ b/rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; use tw_coin_entry::error::SigningErrorType; diff --git a/rust/tw_any_coin/tests/chains/cosmos/mod.rs b/rust/tw_any_coin/tests/chains/cosmos/mod.rs index 47b109ad2c9..c81241c5434 100644 --- a/rust/tw_any_coin/tests/chains/cosmos/mod.rs +++ b/rust/tw_any_coin/tests/chains/cosmos/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. mod cosmos_address; mod cosmos_sign; diff --git a/rust/tw_any_coin/tests/chains/ethereum/ethereum_address.rs b/rust/tw_any_coin/tests/chains/ethereum/ethereum_address.rs index f0a03ec2cde..7fb5934a6ba 100644 --- a/rust/tw_any_coin/tests/chains/ethereum/ethereum_address.rs +++ b/rust/tw_any_coin/tests/chains/ethereum/ethereum_address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_any_coin::test_utils::address_utils::{ test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, diff --git a/rust/tw_any_coin/tests/chains/ethereum/ethereum_compile.rs b/rust/tw_any_coin/tests/chains/ethereum/ethereum_compile.rs index f31a380d456..6b209eb6451 100644 --- a/rust/tw_any_coin/tests/chains/ethereum/ethereum_compile.rs +++ b/rust/tw_any_coin/tests/chains/ethereum/ethereum_compile.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::borrow::Cow; use tw_any_coin::ffi::tw_any_signer::tw_any_signer_plan; diff --git a/rust/tw_any_coin/tests/chains/ethereum/ethereum_message_sign.rs b/rust/tw_any_coin/tests/chains/ethereum/ethereum_message_sign.rs index 1b348169f49..38e72d2a3ab 100644 --- a/rust/tw_any_coin/tests/chains/ethereum/ethereum_message_sign.rs +++ b/rust/tw_any_coin/tests/chains/ethereum/ethereum_message_sign.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_any_coin::ffi::tw_message_signer::{ tw_message_signer_pre_image_hashes, tw_message_signer_sign, tw_message_signer_verify, diff --git a/rust/tw_any_coin/tests/chains/ethereum/ethereum_sign.rs b/rust/tw_any_coin/tests/chains/ethereum/ethereum_sign.rs index 47a78da82e7..5299cebb811 100644 --- a/rust/tw_any_coin/tests/chains/ethereum/ethereum_sign.rs +++ b/rust/tw_any_coin/tests/chains/ethereum/ethereum_sign.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::borrow::Cow; use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; diff --git a/rust/tw_any_coin/tests/chains/ethereum/mod.rs b/rust/tw_any_coin/tests/chains/ethereum/mod.rs index 153b12547b5..d6e5a36b07b 100644 --- a/rust/tw_any_coin/tests/chains/ethereum/mod.rs +++ b/rust/tw_any_coin/tests/chains/ethereum/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. mod ethereum_address; mod ethereum_compile; diff --git a/rust/tw_any_coin/tests/chains/greenfield/greenfield_address.rs b/rust/tw_any_coin/tests/chains/greenfield/greenfield_address.rs index 6223ac6218d..5f36676b06e 100644 --- a/rust/tw_any_coin/tests/chains/greenfield/greenfield_address.rs +++ b/rust/tw_any_coin/tests/chains/greenfield/greenfield_address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_any_coin::test_utils::address_utils::{ test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, diff --git a/rust/tw_any_coin/tests/chains/greenfield/greenfield_compile.rs b/rust/tw_any_coin/tests/chains/greenfield/greenfield_compile.rs index a48363ef791..cb4dbb7c4a5 100644 --- a/rust/tw_any_coin/tests/chains/greenfield/greenfield_compile.rs +++ b/rust/tw_any_coin/tests/chains/greenfield/greenfield_compile.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::chains::greenfield::{make_amount, PUBLIC_KEY_15560}; use std::borrow::Cow; diff --git a/rust/tw_any_coin/tests/chains/greenfield/greenfield_sign.rs b/rust/tw_any_coin/tests/chains/greenfield/greenfield_sign.rs index f6469f95194..f2ec4752a46 100644 --- a/rust/tw_any_coin/tests/chains/greenfield/greenfield_sign.rs +++ b/rust/tw_any_coin/tests/chains/greenfield/greenfield_sign.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::chains::greenfield::{make_amount, PRIVATE_KEY_15560, PRIVATE_KEY_1686}; use tw_any_coin::test_utils::sign_utils::AnySignerHelper; diff --git a/rust/tw_any_coin/tests/chains/greenfield/mod.rs b/rust/tw_any_coin/tests/chains/greenfield/mod.rs index a14f03efddd..9526a2d1147 100644 --- a/rust/tw_any_coin/tests/chains/greenfield/mod.rs +++ b/rust/tw_any_coin/tests/chains/greenfield/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_proto::Greenfield::Proto; diff --git a/rust/tw_any_coin/tests/chains/internet_computer/internet_computer_address.rs b/rust/tw_any_coin/tests/chains/internet_computer/internet_computer_address.rs index ef44d0e022f..789671d8efb 100644 --- a/rust/tw_any_coin/tests/chains/internet_computer/internet_computer_address.rs +++ b/rust/tw_any_coin/tests/chains/internet_computer/internet_computer_address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_any_coin::test_utils::address_utils::{ test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, diff --git a/rust/tw_any_coin/tests/chains/internet_computer/mod.rs b/rust/tw_any_coin/tests/chains/internet_computer/mod.rs index e305a45c6be..d67ce69c2ee 100644 --- a/rust/tw_any_coin/tests/chains/internet_computer/mod.rs +++ b/rust/tw_any_coin/tests/chains/internet_computer/mod.rs @@ -1,7 +1,5 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. mod internet_computer_address; diff --git a/rust/tw_any_coin/tests/chains/mod.rs b/rust/tw_any_coin/tests/chains/mod.rs index cafbd33253c..38c46ed262e 100644 --- a/rust/tw_any_coin/tests/chains/mod.rs +++ b/rust/tw_any_coin/tests/chains/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. mod aptos; mod binance; diff --git a/rust/tw_any_coin/tests/chains/native_evmos/mod.rs b/rust/tw_any_coin/tests/chains/native_evmos/mod.rs index 2a92924fc00..4f7debf5205 100644 --- a/rust/tw_any_coin/tests/chains/native_evmos/mod.rs +++ b/rust/tw_any_coin/tests/chains/native_evmos/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. mod native_evmos_address; mod native_evmos_sign; diff --git a/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_address.rs b/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_address.rs index d779aabc230..6800702b545 100644 --- a/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_address.rs +++ b/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_any_coin::test_utils::address_utils::{ test_address_bech32_is_valid, test_address_create_bech32_with_public_key, diff --git a/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_sign.rs b/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_sign.rs index 3615cfa3548..c9e90d27a5e 100644 --- a/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_sign.rs +++ b/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_sign.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::borrow::Cow; use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; diff --git a/rust/tw_any_coin/tests/chains/native_injective/mod.rs b/rust/tw_any_coin/tests/chains/native_injective/mod.rs index a421b328188..54f1133ee80 100644 --- a/rust/tw_any_coin/tests/chains/native_injective/mod.rs +++ b/rust/tw_any_coin/tests/chains/native_injective/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. mod native_injective_address; mod native_injective_compile; diff --git a/rust/tw_any_coin/tests/chains/native_injective/native_injective_address.rs b/rust/tw_any_coin/tests/chains/native_injective/native_injective_address.rs index 1bd9c4a8f8e..08f61e94587 100644 --- a/rust/tw_any_coin/tests/chains/native_injective/native_injective_address.rs +++ b/rust/tw_any_coin/tests/chains/native_injective/native_injective_address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_any_coin::test_utils::address_utils::{ test_address_bech32_is_valid, test_address_create_bech32_with_public_key, diff --git a/rust/tw_any_coin/tests/chains/native_injective/native_injective_compile.rs b/rust/tw_any_coin/tests/chains/native_injective/native_injective_compile.rs index fbffdddfb91..797b7e9394e 100644 --- a/rust/tw_any_coin/tests/chains/native_injective/native_injective_compile.rs +++ b/rust/tw_any_coin/tests/chains/native_injective/native_injective_compile.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_any_coin::ffi::tw_transaction_compiler::{ tw_transaction_compiler_compile, tw_transaction_compiler_pre_image_hashes, diff --git a/rust/tw_any_coin/tests/chains/native_injective/native_injective_sign.rs b/rust/tw_any_coin/tests/chains/native_injective/native_injective_sign.rs index f5d7e448a27..6ca790dbee2 100644 --- a/rust/tw_any_coin/tests/chains/native_injective/native_injective_sign.rs +++ b/rust/tw_any_coin/tests/chains/native_injective/native_injective_sign.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::borrow::Cow; use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; diff --git a/rust/tw_any_coin/tests/chains/tbinance/mod.rs b/rust/tw_any_coin/tests/chains/tbinance/mod.rs index 0ccb0a3eebc..07b4922f0c3 100644 --- a/rust/tw_any_coin/tests/chains/tbinance/mod.rs +++ b/rust/tw_any_coin/tests/chains/tbinance/mod.rs @@ -1,7 +1,5 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. mod tbinance_address; diff --git a/rust/tw_any_coin/tests/chains/tbinance/tbinance_address.rs b/rust/tw_any_coin/tests/chains/tbinance/tbinance_address.rs index c24b8d9bee6..cb561ea17e9 100644 --- a/rust/tw_any_coin/tests/chains/tbinance/tbinance_address.rs +++ b/rust/tw_any_coin/tests/chains/tbinance/tbinance_address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_any_coin::test_utils::address_utils::{ test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, diff --git a/rust/tw_any_coin/tests/chains/thorchain/mod.rs b/rust/tw_any_coin/tests/chains/thorchain/mod.rs index 20d258d5105..739ea814591 100644 --- a/rust/tw_any_coin/tests/chains/thorchain/mod.rs +++ b/rust/tw_any_coin/tests/chains/thorchain/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. mod test_cases; mod thorchain_address; diff --git a/rust/tw_any_coin/tests/chains/thorchain/test_cases.rs b/rust/tw_any_coin/tests/chains/thorchain/test_cases.rs index e626e3466c7..5e5c256d337 100644 --- a/rust/tw_any_coin/tests/chains/thorchain/test_cases.rs +++ b/rust/tw_any_coin/tests/chains/thorchain/test_cases.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_cosmos_sdk::test_utils::proto_utils::{make_amount, make_fee, make_message}; use tw_proto::Cosmos::Proto; diff --git a/rust/tw_any_coin/tests/chains/thorchain/thorchain_address.rs b/rust/tw_any_coin/tests/chains/thorchain/thorchain_address.rs index 03ea3d0c188..05d344ed566 100644 --- a/rust/tw_any_coin/tests/chains/thorchain/thorchain_address.rs +++ b/rust/tw_any_coin/tests/chains/thorchain/thorchain_address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_any_coin::test_utils::address_utils::{ test_address_bech32_is_valid, test_address_create_bech32_with_public_key, diff --git a/rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs b/rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs index ca2fda25eab..2439165caa7 100644 --- a/rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs +++ b/rust/tw_any_coin/tests/chains/thorchain/thorchain_compile.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::chains::thorchain::test_cases::send_fd0445af::{ signing_input, JSON_SIGNING_SIGNATURE, JSON_SIGNING_SIGNATURE_JSON, JSON_TX, JSON_TX_PREIMAGE, diff --git a/rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs b/rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs index c45c196b026..e65d2ec1ebc 100644 --- a/rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs +++ b/rust/tw_any_coin/tests/chains/thorchain/thorchain_sign.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::chains::thorchain::test_cases::send_fd0445af::{ signing_input, JSON_SIGNING_SIGNATURE, JSON_SIGNING_SIGNATURE_JSON, JSON_TX, PRIVATE_KEY, diff --git a/rust/tw_any_coin/tests/coin_address_derivation_test.rs b/rust/tw_any_coin/tests/coin_address_derivation_test.rs index 58f522939c9..cd1c64283d2 100644 --- a/rust/tw_any_coin/tests/coin_address_derivation_test.rs +++ b/rust/tw_any_coin/tests/coin_address_derivation_test.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_any_coin::ffi::tw_any_address::{ tw_any_address_create_with_public_key_derivation, tw_any_address_description, diff --git a/rust/tw_any_coin/tests/tw_any_signer_ffi_tests.rs b/rust/tw_any_coin/tests/tw_any_signer_ffi_tests.rs index 74cd4e04d4b..ac9d33676c9 100644 --- a/rust/tw_any_coin/tests/tw_any_signer_ffi_tests.rs +++ b/rust/tw_any_coin/tests/tw_any_signer_ffi_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; use tw_memory::test_utils::tw_data_helper::TWDataHelper; diff --git a/rust/tw_aptos/src/address.rs b/rust/tw_aptos/src/address.rs index 3f5cf1d3217..619ae3d4317 100644 --- a/rust/tw_aptos/src/address.rs +++ b/rust/tw_aptos/src/address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use move_core_types::account_address::{AccountAddress, AccountAddressParseError}; use std::fmt::{Display, Formatter}; diff --git a/rust/tw_aptos/src/aptos_move_packages.rs b/rust/tw_aptos/src/aptos_move_packages.rs index 2e8f7aff378..3d3d318cc0d 100644 --- a/rust/tw_aptos/src/aptos_move_packages.rs +++ b/rust/tw_aptos/src/aptos_move_packages.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::transaction_payload::{EntryFunction, TransactionPayload}; use move_core_types::account_address::AccountAddress; diff --git a/rust/tw_aptos/src/constants.rs b/rust/tw_aptos/src/constants.rs index 6c30d3b68bc..714c1a2f782 100644 --- a/rust/tw_aptos/src/constants.rs +++ b/rust/tw_aptos/src/constants.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub const GAS_UNIT_PRICE: u64 = 100; pub const MAX_GAS_AMOUNT: u64 = 100_000_000; diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs index bed1d3c8d47..cb8dba03f75 100644 --- a/rust/tw_aptos/src/entry.rs +++ b/rust/tw_aptos/src/entry.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::Address; use crate::compiler::Compiler; diff --git a/rust/tw_aptos/src/lib.rs b/rust/tw_aptos/src/lib.rs index b85498f42c2..99f90ae428d 100644 --- a/rust/tw_aptos/src/lib.rs +++ b/rust/tw_aptos/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod address; pub mod aptos_move_packages; diff --git a/rust/tw_aptos/src/liquid_staking.rs b/rust/tw_aptos/src/liquid_staking.rs index 2ac80692050..4b1027abe9e 100644 --- a/rust/tw_aptos/src/liquid_staking.rs +++ b/rust/tw_aptos/src/liquid_staking.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::from_account_error; use crate::transaction_payload::{EntryFunction, TransactionPayload}; diff --git a/rust/tw_aptos/src/nft.rs b/rust/tw_aptos/src/nft.rs index fe6f0375146..caf46b432bb 100644 --- a/rust/tw_aptos/src/nft.rs +++ b/rust/tw_aptos/src/nft.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::from_account_error; use move_core_types::account_address::AccountAddress; diff --git a/rust/tw_aptos/src/signer.rs b/rust/tw_aptos/src/signer.rs index 91431569c73..7249e7bec5e 100644 --- a/rust/tw_aptos/src/signer.rs +++ b/rust/tw_aptos/src/signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::Address; use crate::transaction_builder; diff --git a/rust/tw_aptos/src/transaction.rs b/rust/tw_aptos/src/transaction.rs index b48d7950bbc..c4822285566 100644 --- a/rust/tw_aptos/src/transaction.rs +++ b/rust/tw_aptos/src/transaction.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::constants::APTOS_SALT; use crate::transaction_payload::TransactionPayload; diff --git a/rust/tw_aptos/src/transaction_builder.rs b/rust/tw_aptos/src/transaction_builder.rs index 53f72d32936..723121f9910 100644 --- a/rust/tw_aptos/src/transaction_builder.rs +++ b/rust/tw_aptos/src/transaction_builder.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::from_account_error; use crate::aptos_move_packages::{ diff --git a/rust/tw_aptos/src/transaction_payload.rs b/rust/tw_aptos/src/transaction_payload.rs index 5e33e71d465..a14666ad2b5 100644 --- a/rust/tw_aptos/src/transaction_payload.rs +++ b/rust/tw_aptos/src/transaction_payload.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::serde_helper::vec_bytes; use move_core_types::identifier::Identifier; diff --git a/rust/tw_aptos/tests/signer.rs b/rust/tw_aptos/tests/signer.rs index 09f80e2e26a..5b5268539d4 100644 --- a/rust/tw_aptos/tests/signer.rs +++ b/rust/tw_aptos/tests/signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use move_core_types::account_address::AccountAddress; use move_core_types::language_storage::TypeTag; diff --git a/rust/tw_bech32_address/src/bech32_prefix.rs b/rust/tw_bech32_address/src/bech32_prefix.rs index 8f6f418ed2c..c644ff6046c 100644 --- a/rust/tw_bech32_address/src/bech32_prefix.rs +++ b/rust/tw_bech32_address/src/bech32_prefix.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::error::AddressError; use tw_coin_entry::prefix::AddressPrefix; diff --git a/rust/tw_bech32_address/src/lib.rs b/rust/tw_bech32_address/src/lib.rs index 338e26f6b16..ac5d944054b 100644 --- a/rust/tw_bech32_address/src/lib.rs +++ b/rust/tw_bech32_address/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::bech32_prefix::Bech32Prefix; use serde::{Deserialize, Deserializer, Serialize, Serializer}; diff --git a/rust/tw_coin_entry/src/coin_context.rs b/rust/tw_coin_entry/src/coin_context.rs index f6f4a9dbb83..4cf22327b6d 100644 --- a/rust/tw_coin_entry/src/coin_context.rs +++ b/rust/tw_coin_entry/src/coin_context.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_hash::hasher::Hasher; use tw_keypair::tw::PublicKeyType; diff --git a/rust/tw_coin_entry/src/coin_entry.rs b/rust/tw_coin_entry/src/coin_entry.rs index 83ff059e43d..62133a7e61f 100644 --- a/rust/tw_coin_entry/src/coin_entry.rs +++ b/rust/tw_coin_entry/src/coin_entry.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::coin_context::CoinContext; use crate::derivation::Derivation; diff --git a/rust/tw_coin_entry/src/coin_entry_ext.rs b/rust/tw_coin_entry/src/coin_entry_ext.rs index cc0b39549d3..93e4243aa4a 100644 --- a/rust/tw_coin_entry/src/coin_entry_ext.rs +++ b/rust/tw_coin_entry/src/coin_entry_ext.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::coin_context::CoinContext; use crate::coin_entry::{CoinAddress, CoinEntry, PublicKeyBytes, SignatureBytes}; diff --git a/rust/tw_coin_entry/src/common/compile_input.rs b/rust/tw_coin_entry/src/common/compile_input.rs index fc3dfa5f5fb..587d1860931 100644 --- a/rust/tw_coin_entry/src/common/compile_input.rs +++ b/rust/tw_coin_entry/src/common/compile_input.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::coin_entry::{PublicKeyBytes, SignatureBytes}; use crate::error::{SigningError, SigningErrorType, SigningResult}; diff --git a/rust/tw_coin_entry/src/common/mod.rs b/rust/tw_coin_entry/src/common/mod.rs index 59492cc4690..f333903f3b8 100644 --- a/rust/tw_coin_entry/src/common/mod.rs +++ b/rust/tw_coin_entry/src/common/mod.rs @@ -1,7 +1,5 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod compile_input; diff --git a/rust/tw_coin_entry/src/derivation.rs b/rust/tw_coin_entry/src/derivation.rs index 29378ffa176..91ed87b0e2c 100644 --- a/rust/tw_coin_entry/src/derivation.rs +++ b/rust/tw_coin_entry/src/derivation.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. /// Extend this enum. #[derive(Default)] diff --git a/rust/tw_coin_entry/src/error.rs b/rust/tw_coin_entry/src/error.rs index b6bc7b923cc..bea816c30d0 100644 --- a/rust/tw_coin_entry/src/error.rs +++ b/rust/tw_coin_entry/src/error.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::fmt; use std::fmt::Formatter; diff --git a/rust/tw_coin_entry/src/lib.rs b/rust/tw_coin_entry/src/lib.rs index fd45ec0f96b..11b456c305d 100644 --- a/rust/tw_coin_entry/src/lib.rs +++ b/rust/tw_coin_entry/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod coin_context; pub mod coin_entry; diff --git a/rust/tw_coin_entry/src/modules/json_signer.rs b/rust/tw_coin_entry/src/modules/json_signer.rs index 8757c5ed40d..3fd0988dae6 100644 --- a/rust/tw_coin_entry/src/modules/json_signer.rs +++ b/rust/tw_coin_entry/src/modules/json_signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::coin_context::CoinContext; use crate::error::SigningResult; diff --git a/rust/tw_coin_entry/src/modules/message_signer.rs b/rust/tw_coin_entry/src/modules/message_signer.rs index 141c30352f1..39b7df6cf7a 100644 --- a/rust/tw_coin_entry/src/modules/message_signer.rs +++ b/rust/tw_coin_entry/src/modules/message_signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::coin_context::CoinContext; use tw_proto::{DummyMessage, MessageRead, MessageWrite, NoMessage}; diff --git a/rust/tw_coin_entry/src/modules/mod.rs b/rust/tw_coin_entry/src/modules/mod.rs index f49968d2816..88cfc996f91 100644 --- a/rust/tw_coin_entry/src/modules/mod.rs +++ b/rust/tw_coin_entry/src/modules/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. //! This directory contains optional modules that some chain may or may not implement. diff --git a/rust/tw_coin_entry/src/modules/plan_builder.rs b/rust/tw_coin_entry/src/modules/plan_builder.rs index fd5b9f1b828..0b019039276 100644 --- a/rust/tw_coin_entry/src/modules/plan_builder.rs +++ b/rust/tw_coin_entry/src/modules/plan_builder.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::coin_context::CoinContext; use tw_proto::{DummyMessage, MessageRead, MessageWrite, NoMessage}; diff --git a/rust/tw_coin_entry/src/modules/wallet_connector.rs b/rust/tw_coin_entry/src/modules/wallet_connector.rs index 25db0008587..194ba6826e1 100644 --- a/rust/tw_coin_entry/src/modules/wallet_connector.rs +++ b/rust/tw_coin_entry/src/modules/wallet_connector.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::coin_context::CoinContext; use tw_proto::WalletConnect::Proto as WCProto; diff --git a/rust/tw_coin_entry/src/prefix.rs b/rust/tw_coin_entry/src/prefix.rs index b48bfcccc66..c534dc33134 100644 --- a/rust/tw_coin_entry/src/prefix.rs +++ b/rust/tw_coin_entry/src/prefix.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::error::AddressError; diff --git a/rust/tw_coin_entry/src/test_utils/mod.rs b/rust/tw_coin_entry/src/test_utils/mod.rs index 4cbcc23d463..a3d7c1feff5 100644 --- a/rust/tw_coin_entry/src/test_utils/mod.rs +++ b/rust/tw_coin_entry/src/test_utils/mod.rs @@ -1,7 +1,5 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod test_context; diff --git a/rust/tw_coin_entry/src/test_utils/test_context.rs b/rust/tw_coin_entry/src/test_utils/test_context.rs index 50d31585a22..2fe15403317 100644 --- a/rust/tw_coin_entry/src/test_utils/test_context.rs +++ b/rust/tw_coin_entry/src/test_utils/test_context.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::coin_context::CoinContext; use tw_hash::hasher::Hasher; diff --git a/rust/tw_coin_registry/build.rs b/rust/tw_coin_registry/build.rs index 4c79c1d01f4..f2bb771216f 100644 --- a/rust/tw_coin_registry/build.rs +++ b/rust/tw_coin_registry/build.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use itertools::Itertools; use serde::Deserialize; diff --git a/rust/tw_coin_registry/src/blockchain_type.rs b/rust/tw_coin_registry/src/blockchain_type.rs index b6a27712ea7..87261ec0d95 100644 --- a/rust/tw_coin_registry/src/blockchain_type.rs +++ b/rust/tw_coin_registry/src/blockchain_type.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde::Deserialize; diff --git a/rust/tw_coin_registry/src/coin_context.rs b/rust/tw_coin_registry/src/coin_context.rs index 2185e7615e6..ceb8d969466 100644 --- a/rust/tw_coin_registry/src/coin_context.rs +++ b/rust/tw_coin_registry/src/coin_context.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::registry::CoinItem; use tw_coin_entry::coin_context::CoinContext; diff --git a/rust/tw_coin_registry/src/dispatcher.rs b/rust/tw_coin_registry/src/dispatcher.rs index 29e17dcd870..e6cfebcf7bb 100644 --- a/rust/tw_coin_registry/src/dispatcher.rs +++ b/rust/tw_coin_registry/src/dispatcher.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::blockchain_type::BlockchainType; use crate::coin_context::CoinRegistryContext; diff --git a/rust/tw_coin_registry/src/error.rs b/rust/tw_coin_registry/src/error.rs index 52857b83564..bdd941095ca 100644 --- a/rust/tw_coin_registry/src/error.rs +++ b/rust/tw_coin_registry/src/error.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::error::{SigningError, SigningErrorType}; diff --git a/rust/tw_coin_registry/src/lib.rs b/rust/tw_coin_registry/src/lib.rs index d8e50a8f1c6..102cd55fd8f 100644 --- a/rust/tw_coin_registry/src/lib.rs +++ b/rust/tw_coin_registry/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod blockchain_type; pub mod coin_context; diff --git a/rust/tw_coin_registry/src/registry.rs b/rust/tw_coin_registry/src/registry.rs index 58b6fef8cc7..595ed152f28 100644 --- a/rust/tw_coin_registry/src/registry.rs +++ b/rust/tw_coin_registry/src/registry.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::blockchain_type::BlockchainType; use crate::coin_type::CoinType; diff --git a/rust/tw_cosmos_sdk/build.rs b/rust/tw_cosmos_sdk/build.rs index 06fdb15ce6b..830293ac58a 100644 --- a/rust/tw_cosmos_sdk/build.rs +++ b/rust/tw_cosmos_sdk/build.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use pb_rs::types::FileDescriptor; use pb_rs::ConfigBuilder; diff --git a/rust/tw_cosmos_sdk/src/address.rs b/rust/tw_cosmos_sdk/src/address.rs index 4f141b2a1dd..4f15075115d 100644 --- a/rust/tw_cosmos_sdk/src/address.rs +++ b/rust/tw_cosmos_sdk/src/address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde::Serialize; use std::str::FromStr; diff --git a/rust/tw_cosmos_sdk/src/context.rs b/rust/tw_cosmos_sdk/src/context.rs index f7da7218f25..0fc370bc39f 100644 --- a/rust/tw_cosmos_sdk/src/context.rs +++ b/rust/tw_cosmos_sdk/src/context.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::{Address, CosmosAddress}; use crate::hasher::sha256_hasher::Sha256Hasher; diff --git a/rust/tw_cosmos_sdk/src/hasher/keccak256_hasher.rs b/rust/tw_cosmos_sdk/src/hasher/keccak256_hasher.rs index 21ca81fb087..a37dccb78b1 100644 --- a/rust/tw_cosmos_sdk/src/hasher/keccak256_hasher.rs +++ b/rust/tw_cosmos_sdk/src/hasher/keccak256_hasher.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::hasher::CosmosHasher; use tw_hash::sha3::keccak256; diff --git a/rust/tw_cosmos_sdk/src/hasher/mod.rs b/rust/tw_cosmos_sdk/src/hasher/mod.rs index 7422d204961..960940ef8c5 100644 --- a/rust/tw_cosmos_sdk/src/hasher/mod.rs +++ b/rust/tw_cosmos_sdk/src/hasher/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_memory::Data; diff --git a/rust/tw_cosmos_sdk/src/hasher/sha256_hasher.rs b/rust/tw_cosmos_sdk/src/hasher/sha256_hasher.rs index 3b2310cdbec..33f0e42a824 100644 --- a/rust/tw_cosmos_sdk/src/hasher/sha256_hasher.rs +++ b/rust/tw_cosmos_sdk/src/hasher/sha256_hasher.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::hasher::CosmosHasher; use tw_hash::sha2::sha256; diff --git a/rust/tw_cosmos_sdk/src/lib.rs b/rust/tw_cosmos_sdk/src/lib.rs index 52cba46ef65..3c6f16a7665 100644 --- a/rust/tw_cosmos_sdk/src/lib.rs +++ b/rust/tw_cosmos_sdk/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod address; pub mod context; diff --git a/rust/tw_cosmos_sdk/src/modules/broadcast_msg.rs b/rust/tw_cosmos_sdk/src/modules/broadcast_msg.rs index 3c2b636ea0a..73b0beafd98 100644 --- a/rust/tw_cosmos_sdk/src/modules/broadcast_msg.rs +++ b/rust/tw_cosmos_sdk/src/modules/broadcast_msg.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use quick_protobuf::MessageWrite; use serde::Serialize; diff --git a/rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs b/rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs index 188e4c3d377..5c61af317a5 100644 --- a/rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs +++ b/rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::context::CosmosContext; use crate::hasher::CosmosHasher; diff --git a/rust/tw_cosmos_sdk/src/modules/compiler/mod.rs b/rust/tw_cosmos_sdk/src/modules/compiler/mod.rs index a5d5ef61389..e91eafedb72 100644 --- a/rust/tw_cosmos_sdk/src/modules/compiler/mod.rs +++ b/rust/tw_cosmos_sdk/src/modules/compiler/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod json_preimager; pub mod protobuf_preimager; diff --git a/rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs b/rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs index 591d56a6aba..7aa71bbdb3e 100644 --- a/rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs +++ b/rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::context::CosmosContext; use crate::hasher::CosmosHasher; diff --git a/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs b/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs index f8211227822..6dbdeb2ceaa 100644 --- a/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs +++ b/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::context::CosmosContext; use crate::modules::broadcast_msg::{BroadcastMode, BroadcastMsg}; diff --git a/rust/tw_cosmos_sdk/src/modules/mod.rs b/rust/tw_cosmos_sdk/src/modules/mod.rs index 2a667107a85..e49153b3c92 100644 --- a/rust/tw_cosmos_sdk/src/modules/mod.rs +++ b/rust/tw_cosmos_sdk/src/modules/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod broadcast_msg; pub mod compiler; diff --git a/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs b/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs index 073f7de68cb..0b2c81195ad 100644 --- a/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs +++ b/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::context::CosmosContext; use crate::private_key::SignatureData; diff --git a/rust/tw_cosmos_sdk/src/modules/serializer/mod.rs b/rust/tw_cosmos_sdk/src/modules/serializer/mod.rs index 7361a31450a..7b18b177500 100644 --- a/rust/tw_cosmos_sdk/src/modules/serializer/mod.rs +++ b/rust/tw_cosmos_sdk/src/modules/serializer/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod json_serializer; pub mod protobuf_serializer; diff --git a/rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs b/rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs index 6f77b43c9db..150667bb5db 100644 --- a/rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs +++ b/rust/tw_cosmos_sdk/src/modules/serializer/protobuf_serializer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::context::CosmosContext; use crate::proto::cosmos::base::v1beta1 as base_proto; diff --git a/rust/tw_cosmos_sdk/src/modules/signer/mod.rs b/rust/tw_cosmos_sdk/src/modules/signer/mod.rs index c4071fb11ff..20b32a4f88d 100644 --- a/rust/tw_cosmos_sdk/src/modules/signer/mod.rs +++ b/rust/tw_cosmos_sdk/src/modules/signer/mod.rs @@ -1,7 +1,5 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod tw_signer; diff --git a/rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs b/rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs index 8f63310be5a..d06f16d673c 100644 --- a/rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs +++ b/rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::context::CosmosContext; use crate::modules::compiler::tw_compiler::TWTransactionCompiler; diff --git a/rust/tw_cosmos_sdk/src/modules/tx_builder.rs b/rust/tw_cosmos_sdk/src/modules/tx_builder.rs index a673bc1124d..e08d8c47ba4 100644 --- a/rust/tw_cosmos_sdk/src/modules/tx_builder.rs +++ b/rust/tw_cosmos_sdk/src/modules/tx_builder.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::{Address, CosmosAddress}; use crate::context::CosmosContext; diff --git a/rust/tw_cosmos_sdk/src/private_key/mod.rs b/rust/tw_cosmos_sdk/src/private_key/mod.rs index 30045cc6549..bb9bb32ae46 100644 --- a/rust/tw_cosmos_sdk/src/private_key/mod.rs +++ b/rust/tw_cosmos_sdk/src/private_key/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::error::SigningResult; use tw_keypair::{tw, KeyPairError}; diff --git a/rust/tw_cosmos_sdk/src/private_key/secp256k1.rs b/rust/tw_cosmos_sdk/src/private_key/secp256k1.rs index 921409f4cd4..8318bc676f5 100644 --- a/rust/tw_cosmos_sdk/src/private_key/secp256k1.rs +++ b/rust/tw_cosmos_sdk/src/private_key/secp256k1.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::private_key::CosmosPrivateKey; use tw_coin_entry::error::{SigningError, SigningResult}; diff --git a/rust/tw_cosmos_sdk/src/public_key/mod.rs b/rust/tw_cosmos_sdk/src/public_key/mod.rs index d3a4e20f5e8..f7382521b91 100644 --- a/rust/tw_cosmos_sdk/src/public_key/mod.rs +++ b/rust/tw_cosmos_sdk/src/public_key/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::coin_context::CoinContext; use tw_keypair::{tw, KeyPairResult}; diff --git a/rust/tw_cosmos_sdk/src/public_key/secp256k1.rs b/rust/tw_cosmos_sdk/src/public_key/secp256k1.rs index 230582a8472..1b1d2509194 100644 --- a/rust/tw_cosmos_sdk/src/public_key/secp256k1.rs +++ b/rust/tw_cosmos_sdk/src/public_key/secp256k1.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::proto::cosmos; use crate::public_key::{CosmosPublicKey, JsonPublicKey, ProtobufPublicKey}; diff --git a/rust/tw_cosmos_sdk/src/signature/mod.rs b/rust/tw_cosmos_sdk/src/signature/mod.rs index 7184a3c0fd2..573d46d61cc 100644 --- a/rust/tw_cosmos_sdk/src/signature/mod.rs +++ b/rust/tw_cosmos_sdk/src/signature/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_keypair::KeyPairError; use tw_misc::traits::{FromSlice, ToBytesVec}; diff --git a/rust/tw_cosmos_sdk/src/signature/secp256k1.rs b/rust/tw_cosmos_sdk/src/signature/secp256k1.rs index a5e41ec4fb2..94b2be0d388 100644 --- a/rust/tw_cosmos_sdk/src/signature/secp256k1.rs +++ b/rust/tw_cosmos_sdk/src/signature/secp256k1.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::signature::CosmosSignature; use tw_keypair::ecdsa::secp256k1; diff --git a/rust/tw_cosmos_sdk/src/test_utils/mod.rs b/rust/tw_cosmos_sdk/src/test_utils/mod.rs index 334b4aa821d..666af070a2c 100644 --- a/rust/tw_cosmos_sdk/src/test_utils/mod.rs +++ b/rust/tw_cosmos_sdk/src/test_utils/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod proto_utils; pub mod sign_utils; diff --git a/rust/tw_cosmos_sdk/src/test_utils/proto_utils.rs b/rust/tw_cosmos_sdk/src/test_utils/proto_utils.rs index 5bef94cc9eb..8180be3feab 100644 --- a/rust/tw_cosmos_sdk/src/test_utils/proto_utils.rs +++ b/rust/tw_cosmos_sdk/src/test_utils/proto_utils.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_proto::Cosmos::Proto; use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum; diff --git a/rust/tw_cosmos_sdk/src/test_utils/sign_utils.rs b/rust/tw_cosmos_sdk/src/test_utils/sign_utils.rs index 3e439ad4dd1..93c0dc3b64e 100644 --- a/rust/tw_cosmos_sdk/src/test_utils/sign_utils.rs +++ b/rust/tw_cosmos_sdk/src/test_utils/sign_utils.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::context::CosmosContext; use crate::modules::compiler::tw_compiler::TWTransactionCompiler; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_auth_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_auth_message.rs index 7e892dd7f33..dd0e2df4957 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_auth_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_auth_message.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::CosmosAddress; use crate::proto::cosmos; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs index 85c3a0a4470..b5fa6a99a4c 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_bank_message.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::CosmosAddress; use crate::modules::serializer::protobuf_serializer::build_coin; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_generic_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_generic_message.rs index 6d64926930e..c6762b8ec07 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_generic_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_generic_message.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::transaction::message::{CosmosMessage, JsonMessage}; use serde_json::Value as Json; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_gov_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_gov_message.rs index 2054761a6b5..3f72c02bb35 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_gov_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_gov_message.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::CosmosAddress; use crate::proto::cosmos; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_staking_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_staking_message.rs index 353fd780547..9c8d47d1281 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/cosmos_staking_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/cosmos_staking_message.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::CosmosAddress; use crate::modules::serializer::protobuf_serializer::build_coin; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/ibc_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/ibc_message.rs index 71d51e4b9fe..54eb8dca0b6 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/ibc_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/ibc_message.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::CosmosAddress; use crate::modules::serializer::protobuf_serializer::build_coin; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/mod.rs b/rust/tw_cosmos_sdk/src/transaction/message/mod.rs index bcc1d103bee..3674b107903 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/mod.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::modules::serializer::json_serializer::AnyMsg; use serde::Serialize; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/stride_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/stride_message.rs index fe99e32c155..0565add30ab 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/stride_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/stride_message.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::CosmosAddress; use crate::proto::stride; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/terra_wasm_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/terra_wasm_message.rs index 617f563d930..0613c22aa3f 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/terra_wasm_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/terra_wasm_message.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::CosmosAddress; use crate::modules::serializer::protobuf_serializer::build_coin; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/thorchain_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/thorchain_message.rs index 2d33174bcee..003adb077ec 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/thorchain_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/thorchain_message.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::modules::serializer::protobuf_serializer::build_coin; use crate::proto::types; diff --git a/rust/tw_cosmos_sdk/src/transaction/message/wasm_message.rs b/rust/tw_cosmos_sdk/src/transaction/message/wasm_message.rs index fc24afc2b1b..f647367d469 100644 --- a/rust/tw_cosmos_sdk/src/transaction/message/wasm_message.rs +++ b/rust/tw_cosmos_sdk/src/transaction/message/wasm_message.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::CosmosAddress; use crate::modules::serializer::protobuf_serializer::build_coin; diff --git a/rust/tw_cosmos_sdk/src/transaction/mod.rs b/rust/tw_cosmos_sdk/src/transaction/mod.rs index c54cace61e2..bd36b92b25e 100644 --- a/rust/tw_cosmos_sdk/src/transaction/mod.rs +++ b/rust/tw_cosmos_sdk/src/transaction/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::context::CosmosContext; use crate::private_key::SignatureData; diff --git a/rust/tw_cosmos_sdk/tests/compile.rs b/rust/tw_cosmos_sdk/tests/compile.rs index 78dd632c4a8..04569006937 100644 --- a/rust/tw_cosmos_sdk/tests/compile.rs +++ b/rust/tw_cosmos_sdk/tests/compile.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::borrow::Cow; use tw_coin_entry::test_utils::test_context::TestCoinContext; diff --git a/rust/tw_cosmos_sdk/tests/sign.rs b/rust/tw_cosmos_sdk/tests/sign.rs index 3be2015cc9c..37436b4947f 100644 --- a/rust/tw_cosmos_sdk/tests/sign.rs +++ b/rust/tw_cosmos_sdk/tests/sign.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::borrow::Cow; use tw_coin_entry::test_utils::test_context::TestCoinContext; diff --git a/rust/tw_cosmos_sdk/tests/sign_staking.rs b/rust/tw_cosmos_sdk/tests/sign_staking.rs index deaf265a136..47e1f9c5dfd 100644 --- a/rust/tw_cosmos_sdk/tests/sign_staking.rs +++ b/rust/tw_cosmos_sdk/tests/sign_staking.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::borrow::Cow; use tw_coin_entry::test_utils::test_context::TestCoinContext; diff --git a/rust/tw_cosmos_sdk/tests/sign_stride.rs b/rust/tw_cosmos_sdk/tests/sign_stride.rs index a38706a35c4..a34c4767f38 100644 --- a/rust/tw_cosmos_sdk/tests/sign_stride.rs +++ b/rust/tw_cosmos_sdk/tests/sign_stride.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::borrow::Cow; use tw_coin_entry::test_utils::test_context::TestCoinContext; diff --git a/rust/tw_cosmos_sdk/tests/sign_terra_wasm.rs b/rust/tw_cosmos_sdk/tests/sign_terra_wasm.rs index f3ac72bbf8f..734c953194d 100644 --- a/rust/tw_cosmos_sdk/tests/sign_terra_wasm.rs +++ b/rust/tw_cosmos_sdk/tests/sign_terra_wasm.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde_json::json; use std::borrow::Cow; diff --git a/rust/tw_cosmos_sdk/tests/sign_thorchain.rs b/rust/tw_cosmos_sdk/tests/sign_thorchain.rs index 967ce2e48e9..4783d235747 100644 --- a/rust/tw_cosmos_sdk/tests/sign_thorchain.rs +++ b/rust/tw_cosmos_sdk/tests/sign_thorchain.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::borrow::Cow; use std::str::FromStr; diff --git a/rust/tw_cosmos_sdk/tests/sign_wasm_contract.rs b/rust/tw_cosmos_sdk/tests/sign_wasm_contract.rs index 7e0d375ea48..e2ce673c929 100644 --- a/rust/tw_cosmos_sdk/tests/sign_wasm_contract.rs +++ b/rust/tw_cosmos_sdk/tests/sign_wasm_contract.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde_json::json; use std::borrow::Cow; diff --git a/rust/tw_encoding/src/base32.rs b/rust/tw_encoding/src/base32.rs index d43c10acaf5..665e40229cb 100644 --- a/rust/tw_encoding/src/base32.rs +++ b/rust/tw_encoding/src/base32.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::{EncodingError, EncodingResult}; use data_encoding::{Encoding, Specification}; diff --git a/rust/tw_encoding/src/base58.rs b/rust/tw_encoding/src/base58.rs index 2f0a7cb8694..c383a69b00e 100644 --- a/rust/tw_encoding/src/base58.rs +++ b/rust/tw_encoding/src/base58.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::{EncodingError, EncodingResult}; use bs58::decode::Error; diff --git a/rust/tw_encoding/src/base64.rs b/rust/tw_encoding/src/base64.rs index 5b83eda01a6..66fbbee03bf 100644 --- a/rust/tw_encoding/src/base64.rs +++ b/rust/tw_encoding/src/base64.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::{EncodingError, EncodingResult}; use serde::{Serialize, Serializer}; diff --git a/rust/tw_encoding/src/bcs.rs b/rust/tw_encoding/src/bcs.rs index 8051ce4ac03..5f15345bd66 100644 --- a/rust/tw_encoding/src/bcs.rs +++ b/rust/tw_encoding/src/bcs.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::{EncodingError, EncodingResult}; use serde::Serialize; diff --git a/rust/tw_encoding/src/bech32.rs b/rust/tw_encoding/src/bech32.rs index 397f6e213ee..c0365763baa 100644 --- a/rust/tw_encoding/src/bech32.rs +++ b/rust/tw_encoding/src/bech32.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use bech32::{FromBase32, ToBase32, Variant}; use tw_memory::Data; diff --git a/rust/tw_encoding/src/ffi.rs b/rust/tw_encoding/src/ffi.rs index 1be7b07bc48..bd8e3d957d8 100644 --- a/rust/tw_encoding/src/ffi.rs +++ b/rust/tw_encoding/src/ffi.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![allow(clippy::missing_safety_doc)] diff --git a/rust/tw_encoding/src/hex.rs b/rust/tw_encoding/src/hex.rs index b43ca64d979..aeed9dd751e 100644 --- a/rust/tw_encoding/src/hex.rs +++ b/rust/tw_encoding/src/hex.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub use hex::FromHexError; use serde::{Serialize, Serializer}; diff --git a/rust/tw_encoding/src/lib.rs b/rust/tw_encoding/src/lib.rs index ed1a7002451..b9eb5f096af 100644 --- a/rust/tw_encoding/src/lib.rs +++ b/rust/tw_encoding/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod base32; pub mod base58; diff --git a/rust/tw_encoding/tests/base32_ffi_tests.rs b/rust/tw_encoding/tests/base32_ffi_tests.rs index b80bcf2f00c..dd0906a596c 100644 --- a/rust/tw_encoding/tests/base32_ffi_tests.rs +++ b/rust/tw_encoding/tests/base32_ffi_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::ffi::CString; use tw_encoding::ffi::{decode_base32, encode_base32}; diff --git a/rust/tw_encoding/tests/base58_ffi_tests.rs b/rust/tw_encoding/tests/base58_ffi_tests.rs index ab02bc1276d..1302f23ec38 100644 --- a/rust/tw_encoding/tests/base58_ffi_tests.rs +++ b/rust/tw_encoding/tests/base58_ffi_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::ffi::CString; use tw_encoding::ffi::{decode_base58, encode_base58, Base58Alphabet}; diff --git a/rust/tw_encoding/tests/base64_ffi_tests.rs b/rust/tw_encoding/tests/base64_ffi_tests.rs index 15be407e388..1677a929a32 100644 --- a/rust/tw_encoding/tests/base64_ffi_tests.rs +++ b/rust/tw_encoding/tests/base64_ffi_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::ffi::{CStr, CString}; use tw_encoding::ffi::{decode_base64, encode_base64}; diff --git a/rust/tw_encoding/tests/hex_ffi_tests.rs b/rust/tw_encoding/tests/hex_ffi_tests.rs index 3c05f91678a..d0b259f94a9 100644 --- a/rust/tw_encoding/tests/hex_ffi_tests.rs +++ b/rust/tw_encoding/tests/hex_ffi_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::ffi::{CStr, CString}; use tw_encoding::ffi::{decode_hex, encode_hex}; diff --git a/rust/tw_ethereum/src/entry.rs b/rust/tw_ethereum/src/entry.rs index 807e6ba3d58..15ad5fbee87 100644 --- a/rust/tw_ethereum/src/entry.rs +++ b/rust/tw_ethereum/src/entry.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; diff --git a/rust/tw_ethereum/src/lib.rs b/rust/tw_ethereum/src/lib.rs index 1afc00798db..c080f3da3cf 100644 --- a/rust/tw_ethereum/src/lib.rs +++ b/rust/tw_ethereum/src/lib.rs @@ -1,7 +1,5 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod entry; diff --git a/rust/tw_ethereum/tests/compiler.rs b/rust/tw_ethereum/tests/compiler.rs index 27cce260850..bdf14b3e2a9 100644 --- a/rust/tw_ethereum/tests/compiler.rs +++ b/rust/tw_ethereum/tests/compiler.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::borrow::Cow; use tw_coin_entry::coin_entry_ext::CoinEntryExt; diff --git a/rust/tw_ethereum/tests/signer.rs b/rust/tw_ethereum/tests/signer.rs index 8e0acb0e2ae..06b6af14507 100644 --- a/rust/tw_ethereum/tests/signer.rs +++ b/rust/tw_ethereum/tests/signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::coin_entry_ext::CoinEntryExt; use tw_coin_entry::test_utils::test_context::TestCoinContext; diff --git a/rust/tw_evm/fuzz/fuzz_targets/abi_decode_value.rs b/rust/tw_evm/fuzz/fuzz_targets/abi_decode_value.rs index 4da7cf941e9..bedc03a4ed6 100644 --- a/rust/tw_evm/fuzz/fuzz_targets/abi_decode_value.rs +++ b/rust/tw_evm/fuzz/fuzz_targets/abi_decode_value.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![no_main] diff --git a/rust/tw_evm/fuzz/fuzz_targets/abi_encode_function.rs b/rust/tw_evm/fuzz/fuzz_targets/abi_encode_function.rs index a8652534169..d98471509bd 100644 --- a/rust/tw_evm/fuzz/fuzz_targets/abi_encode_function.rs +++ b/rust/tw_evm/fuzz/fuzz_targets/abi_encode_function.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![no_main] diff --git a/rust/tw_evm/fuzz/fuzz_targets/abi_function_decode_input.rs b/rust/tw_evm/fuzz/fuzz_targets/abi_function_decode_input.rs index cfba6120348..86c46abed44 100644 --- a/rust/tw_evm/fuzz/fuzz_targets/abi_function_decode_input.rs +++ b/rust/tw_evm/fuzz/fuzz_targets/abi_function_decode_input.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![no_main] diff --git a/rust/tw_evm/fuzz/fuzz_targets/rlp_encode.rs b/rust/tw_evm/fuzz/fuzz_targets/rlp_encode.rs index 2a686c6c553..c9265e6e23d 100644 --- a/rust/tw_evm/fuzz/fuzz_targets/rlp_encode.rs +++ b/rust/tw_evm/fuzz/fuzz_targets/rlp_encode.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![no_main] diff --git a/rust/tw_evm/fuzz/fuzz_targets/sign.rs b/rust/tw_evm/fuzz/fuzz_targets/sign.rs index dfa541a2d4c..84d5d0522c3 100644 --- a/rust/tw_evm/fuzz/fuzz_targets/sign.rs +++ b/rust/tw_evm/fuzz/fuzz_targets/sign.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![no_main] diff --git a/rust/tw_evm/src/abi/contract.rs b/rust/tw_evm/src/abi/contract.rs index 117de4c3a87..0226bb48744 100644 --- a/rust/tw_evm/src/abi/contract.rs +++ b/rust/tw_evm/src/abi/contract.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::function::Function; use crate::abi::{AbiError, AbiErrorKind, AbiResult}; diff --git a/rust/tw_evm/src/abi/decode.rs b/rust/tw_evm/src/abi/decode.rs index 76de9399a89..fc1489433d4 100644 --- a/rust/tw_evm/src/abi/decode.rs +++ b/rust/tw_evm/src/abi/decode.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::non_empty_array::{NonEmptyArray, NonEmptyBytes}; use crate::abi::param::Param; diff --git a/rust/tw_evm/src/abi/encode.rs b/rust/tw_evm/src/abi/encode.rs index 17c2c273c38..5ab9acd7068 100644 --- a/rust/tw_evm/src/abi/encode.rs +++ b/rust/tw_evm/src/abi/encode.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::token::Token; use tw_hash::H256; diff --git a/rust/tw_evm/src/abi/function.rs b/rust/tw_evm/src/abi/function.rs index cae72467a02..d4359e378b4 100644 --- a/rust/tw_evm/src/abi/function.rs +++ b/rust/tw_evm/src/abi/function.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::decode::decode_params; use crate::abi::encode::encode_tokens; diff --git a/rust/tw_evm/src/abi/mod.rs b/rust/tw_evm/src/abi/mod.rs index 25b36b9f936..0be990ac556 100644 --- a/rust/tw_evm/src/abi/mod.rs +++ b/rust/tw_evm/src/abi/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::error::{SigningError, SigningErrorType}; diff --git a/rust/tw_evm/src/abi/non_empty_array.rs b/rust/tw_evm/src/abi/non_empty_array.rs index b6a7a535c28..6bf1cd2f4e9 100644 --- a/rust/tw_evm/src/abi/non_empty_array.rs +++ b/rust/tw_evm/src/abi/non_empty_array.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::{AbiError, AbiErrorKind, AbiResult}; use core::fmt; diff --git a/rust/tw_evm/src/abi/param.rs b/rust/tw_evm/src/abi/param.rs index 93ca5dcb3eb..ba58d999f5b 100644 --- a/rust/tw_evm/src/abi/param.rs +++ b/rust/tw_evm/src/abi/param.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::param_type::ParamType; use serde::de::{MapAccess, Visitor}; diff --git a/rust/tw_evm/src/abi/param_token.rs b/rust/tw_evm/src/abi/param_token.rs index 420b8e6f217..aafe7877656 100644 --- a/rust/tw_evm/src/abi/param_token.rs +++ b/rust/tw_evm/src/abi/param_token.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::param::Param; use crate::abi::token::Token; diff --git a/rust/tw_evm/src/abi/param_type/constructor.rs b/rust/tw_evm/src/abi/param_type/constructor.rs index 37eb5d2d39a..25c25b8302b 100644 --- a/rust/tw_evm/src/abi/param_type/constructor.rs +++ b/rust/tw_evm/src/abi/param_type/constructor.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::non_empty_array::NonZeroLen; use crate::abi::param_type::ParamType; diff --git a/rust/tw_evm/src/abi/param_type/mod.rs b/rust/tw_evm/src/abi/param_type/mod.rs index 1ca70655574..ca83a262dc4 100644 --- a/rust/tw_evm/src/abi/param_type/mod.rs +++ b/rust/tw_evm/src/abi/param_type/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::non_empty_array::NonZeroLen; use crate::abi::param::Param; diff --git a/rust/tw_evm/src/abi/param_type/reader.rs b/rust/tw_evm/src/abi/param_type/reader.rs index 1e084fbec6c..826ebedf5c5 100644 --- a/rust/tw_evm/src/abi/param_type/reader.rs +++ b/rust/tw_evm/src/abi/param_type/reader.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::non_empty_array::NonZeroLen; use crate::abi::param_type::constructor::TypeConstructor; diff --git a/rust/tw_evm/src/abi/param_type/writer.rs b/rust/tw_evm/src/abi/param_type/writer.rs index 724a5ed9abe..d0ddcf64af4 100644 --- a/rust/tw_evm/src/abi/param_type/writer.rs +++ b/rust/tw_evm/src/abi/param_type/writer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::param_type::ParamType; diff --git a/rust/tw_evm/src/abi/prebuild/erc1155.rs b/rust/tw_evm/src/abi/prebuild/erc1155.rs index 1a9bf391f98..f3a9bda65da 100644 --- a/rust/tw_evm/src/abi/prebuild/erc1155.rs +++ b/rust/tw_evm/src/abi/prebuild/erc1155.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::contract::Contract; use crate::abi::token::Token; diff --git a/rust/tw_evm/src/abi/prebuild/erc20.rs b/rust/tw_evm/src/abi/prebuild/erc20.rs index 4d3d042d663..55e937e7f83 100644 --- a/rust/tw_evm/src/abi/prebuild/erc20.rs +++ b/rust/tw_evm/src/abi/prebuild/erc20.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::contract::Contract; use crate::abi::token::Token; diff --git a/rust/tw_evm/src/abi/prebuild/erc4337.rs b/rust/tw_evm/src/abi/prebuild/erc4337.rs index bd3a3977204..c6ef8eb76a2 100644 --- a/rust/tw_evm/src/abi/prebuild/erc4337.rs +++ b/rust/tw_evm/src/abi/prebuild/erc4337.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::contract::Contract; use crate::abi::param_type::ParamType; diff --git a/rust/tw_evm/src/abi/prebuild/erc721.rs b/rust/tw_evm/src/abi/prebuild/erc721.rs index e58ac7d0c1b..2f806921b71 100644 --- a/rust/tw_evm/src/abi/prebuild/erc721.rs +++ b/rust/tw_evm/src/abi/prebuild/erc721.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::contract::Contract; use crate::abi::token::Token; diff --git a/rust/tw_evm/src/abi/prebuild/mod.rs b/rust/tw_evm/src/abi/prebuild/mod.rs index 4bbcf59535b..134b2430973 100644 --- a/rust/tw_evm/src/abi/prebuild/mod.rs +++ b/rust/tw_evm/src/abi/prebuild/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod erc1155; pub mod erc20; diff --git a/rust/tw_evm/src/abi/signature.rs b/rust/tw_evm/src/abi/signature.rs index 1cc9da4750f..d7d3e8d3446 100644 --- a/rust/tw_evm/src/abi/signature.rs +++ b/rust/tw_evm/src/abi/signature.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::param_type::ParamType; use tw_hash::sha3::keccak256; diff --git a/rust/tw_evm/src/abi/token.rs b/rust/tw_evm/src/abi/token.rs index 3d52d080d98..c7e45c621be 100644 --- a/rust/tw_evm/src/abi/token.rs +++ b/rust/tw_evm/src/abi/token.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::non_empty_array::{NonEmptyArray, NonEmptyBytes}; use crate::abi::param_token::NamedToken; diff --git a/rust/tw_evm/src/abi/uint.rs b/rust/tw_evm/src/abi/uint.rs index 3b5f6021432..c2b090c274b 100644 --- a/rust/tw_evm/src/abi/uint.rs +++ b/rust/tw_evm/src/abi/uint.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::{AbiError, AbiErrorKind, AbiResult}; use std::fmt; diff --git a/rust/tw_evm/src/address.rs b/rust/tw_evm/src/address.rs index c53887b5662..81fb72e0222 100644 --- a/rust/tw_evm/src/address.rs +++ b/rust/tw_evm/src/address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde::de::Error as SerdeError; use serde::{Deserialize, Deserializer, Serialize, Serializer}; diff --git a/rust/tw_evm/src/evm_context.rs b/rust/tw_evm/src/evm_context.rs index 83113770bb6..ebd14a8d8e5 100644 --- a/rust/tw_evm/src/evm_context.rs +++ b/rust/tw_evm/src/evm_context.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::{Address, EvmAddress}; diff --git a/rust/tw_evm/src/evm_entry.rs b/rust/tw_evm/src/evm_entry.rs index 7ecbba2a885..70a83a374e9 100644 --- a/rust/tw_evm/src/evm_entry.rs +++ b/rust/tw_evm/src/evm_entry.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::evm_context::EvmContext; use crate::modules::abi_encoder::AbiEncoder; diff --git a/rust/tw_evm/src/lib.rs b/rust/tw_evm/src/lib.rs index cacd3c67e82..42425782979 100644 --- a/rust/tw_evm/src/lib.rs +++ b/rust/tw_evm/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod abi; pub mod address; diff --git a/rust/tw_evm/src/message/eip191.rs b/rust/tw_evm/src/message/eip191.rs index 4ff12895222..cb2bd715d8e 100644 --- a/rust/tw_evm/src/message/eip191.rs +++ b/rust/tw_evm/src/message/eip191.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::message::{EthMessage, MessageSigningResult}; use tw_hash::sha3::keccak256; diff --git a/rust/tw_evm/src/message/eip712/eip712_message.rs b/rust/tw_evm/src/message/eip712/eip712_message.rs index be1dff96a3a..ceb3815d328 100644 --- a/rust/tw_evm/src/message/eip712/eip712_message.rs +++ b/rust/tw_evm/src/message/eip712/eip712_message.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::encode::encode_tokens; use crate::abi::non_empty_array::NonEmptyBytes; diff --git a/rust/tw_evm/src/message/eip712/message_types.rs b/rust/tw_evm/src/message/eip712/message_types.rs index 9a4971402fc..4a0d8f0aeae 100644 --- a/rust/tw_evm/src/message/eip712/message_types.rs +++ b/rust/tw_evm/src/message/eip712/message_types.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::message::eip712::property::{Property, PropertyType}; use std::collections::hash_map::Entry; diff --git a/rust/tw_evm/src/message/eip712/mod.rs b/rust/tw_evm/src/message/eip712/mod.rs index 74f7d3c885b..6da281e9169 100644 --- a/rust/tw_evm/src/message/eip712/mod.rs +++ b/rust/tw_evm/src/message/eip712/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod eip712_message; pub mod message_types; diff --git a/rust/tw_evm/src/message/eip712/property.rs b/rust/tw_evm/src/message/eip712/property.rs index 343516ea042..3a85ed1c24c 100644 --- a/rust/tw_evm/src/message/eip712/property.rs +++ b/rust/tw_evm/src/message/eip712/property.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::non_empty_array::NonZeroLen; use crate::abi::param_type::constructor::TypeConstructor; diff --git a/rust/tw_evm/src/message/mod.rs b/rust/tw_evm/src/message/mod.rs index a962df3519b..f92a958e4a9 100644 --- a/rust/tw_evm/src/message/mod.rs +++ b/rust/tw_evm/src/message/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::error::{SigningError, SigningErrorType}; use tw_hash::H256; diff --git a/rust/tw_evm/src/message/signature.rs b/rust/tw_evm/src/message/signature.rs index 74802cd5037..8d3f7c81506 100644 --- a/rust/tw_evm/src/message/signature.rs +++ b/rust/tw_evm/src/message/signature.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::signature::{ eip155_replay_protection, legacy_replay_protection, remove_replay_protection, diff --git a/rust/tw_evm/src/modules/abi_encoder.rs b/rust/tw_evm/src/modules/abi_encoder.rs index 6879a1ab2e4..1e2b746897d 100644 --- a/rust/tw_evm/src/modules/abi_encoder.rs +++ b/rust/tw_evm/src/modules/abi_encoder.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::decode::{decode_params, decode_value}; use crate::abi::function::Function; diff --git a/rust/tw_evm/src/modules/compiler.rs b/rust/tw_evm/src/modules/compiler.rs index 23598eca7aa..2aee8c8ff57 100644 --- a/rust/tw_evm/src/modules/compiler.rs +++ b/rust/tw_evm/src/modules/compiler.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::evm_context::EvmContext; use crate::modules::tx_builder::TxBuilder; diff --git a/rust/tw_evm/src/modules/json_signer.rs b/rust/tw_evm/src/modules/json_signer.rs index 9bacea446c0..d5a60a288bc 100644 --- a/rust/tw_evm/src/modules/json_signer.rs +++ b/rust/tw_evm/src/modules/json_signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::evm_context::EvmContext; use std::marker::PhantomData; diff --git a/rust/tw_evm/src/modules/message_signer.rs b/rust/tw_evm/src/modules/message_signer.rs index b15d95698ea..3b8b6c66e35 100644 --- a/rust/tw_evm/src/modules/message_signer.rs +++ b/rust/tw_evm/src/modules/message_signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::message::eip191::Eip191Message; use crate::message::eip712::eip712_message::Eip712Message; diff --git a/rust/tw_evm/src/modules/mod.rs b/rust/tw_evm/src/modules/mod.rs index ba8e26862b4..6c23a104493 100644 --- a/rust/tw_evm/src/modules/mod.rs +++ b/rust/tw_evm/src/modules/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod abi_encoder; pub mod compiler; diff --git a/rust/tw_evm/src/modules/rlp_encoder.rs b/rust/tw_evm/src/modules/rlp_encoder.rs index e45e2ef0d2d..2530585e044 100644 --- a/rust/tw_evm/src/modules/rlp_encoder.rs +++ b/rust/tw_evm/src/modules/rlp_encoder.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::evm_context::EvmContext; use crate::rlp::buffer::RlpBuffer; diff --git a/rust/tw_evm/src/modules/signer.rs b/rust/tw_evm/src/modules/signer.rs index 18eb01b3374..8ca9b5e2b86 100644 --- a/rust/tw_evm/src/modules/signer.rs +++ b/rust/tw_evm/src/modules/signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::evm_context::EvmContext; use crate::modules::tx_builder::TxBuilder; diff --git a/rust/tw_evm/src/modules/tx_builder.rs b/rust/tw_evm/src/modules/tx_builder.rs index 395fd81489a..a6cfc382528 100644 --- a/rust/tw_evm/src/modules/tx_builder.rs +++ b/rust/tw_evm/src/modules/tx_builder.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::prebuild::erc1155::Erc1155; use crate::abi::prebuild::erc20::Erc20; diff --git a/rust/tw_evm/src/rlp/buffer.rs b/rust/tw_evm/src/rlp/buffer.rs index eea9d9e750e..c29d7d376aa 100644 --- a/rust/tw_evm/src/rlp/buffer.rs +++ b/rust/tw_evm/src/rlp/buffer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_memory::Data; diff --git a/rust/tw_evm/src/rlp/impls.rs b/rust/tw_evm/src/rlp/impls.rs index 1927c01cff8..21e8532da2d 100644 --- a/rust/tw_evm/src/rlp/impls.rs +++ b/rust/tw_evm/src/rlp/impls.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::Address; use crate::rlp::buffer::RlpBuffer; diff --git a/rust/tw_evm/src/rlp/list.rs b/rust/tw_evm/src/rlp/list.rs index 4bf58634d1f..ae7b0924aef 100644 --- a/rust/tw_evm/src/rlp/list.rs +++ b/rust/tw_evm/src/rlp/list.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::rlp::buffer::RlpBuffer; use crate::rlp::RlpEncode; diff --git a/rust/tw_evm/src/rlp/mod.rs b/rust/tw_evm/src/rlp/mod.rs index 88bbe1ff550..3ddea753237 100644 --- a/rust/tw_evm/src/rlp/mod.rs +++ b/rust/tw_evm/src/rlp/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::rlp::buffer::RlpBuffer; diff --git a/rust/tw_evm/src/signature.rs b/rust/tw_evm/src/signature.rs index 0949cb8027e..c3097c197a8 100644 --- a/rust/tw_evm/src/signature.rs +++ b/rust/tw_evm/src/signature.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::ops::BitXor; use tw_number::{NumberResult, U256}; diff --git a/rust/tw_evm/src/transaction/mod.rs b/rust/tw_evm/src/transaction/mod.rs index a80521be11c..04883063628 100644 --- a/rust/tw_evm/src/transaction/mod.rs +++ b/rust/tw_evm/src/transaction/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. //! Transactions can be: //! - Non-typed (legacy, pre-EIP2718) transactions: diff --git a/rust/tw_evm/src/transaction/signature.rs b/rust/tw_evm/src/transaction/signature.rs index 198ca2e91bb..a6a8f4cf2ff 100644 --- a/rust/tw_evm/src/transaction/signature.rs +++ b/rust/tw_evm/src/transaction/signature.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::signature::replay_protection; use tw_hash::H520; diff --git a/rust/tw_evm/src/transaction/transaction_eip1559.rs b/rust/tw_evm/src/transaction/transaction_eip1559.rs index 352a5b53a8e..b86c28b2196 100644 --- a/rust/tw_evm/src/transaction/transaction_eip1559.rs +++ b/rust/tw_evm/src/transaction/transaction_eip1559.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::Address; use crate::rlp::list::RlpList; diff --git a/rust/tw_evm/src/transaction/transaction_non_typed.rs b/rust/tw_evm/src/transaction/transaction_non_typed.rs index 67d386def60..7e2fd1194c9 100644 --- a/rust/tw_evm/src/transaction/transaction_non_typed.rs +++ b/rust/tw_evm/src/transaction/transaction_non_typed.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::Address; use crate::rlp::list::RlpList; diff --git a/rust/tw_evm/src/transaction/user_operation.rs b/rust/tw_evm/src/transaction/user_operation.rs index 6abe6bd048b..0f3f23167ff 100644 --- a/rust/tw_evm/src/transaction/user_operation.rs +++ b/rust/tw_evm/src/transaction/user_operation.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::abi::encode::encode_tokens; use crate::abi::non_empty_array::NonEmptyBytes; diff --git a/rust/tw_evm/tests/abi_encoder.rs b/rust/tw_evm/tests/abi_encoder.rs index b94533bbb27..64feb710531 100644 --- a/rust/tw_evm/tests/abi_encoder.rs +++ b/rust/tw_evm/tests/abi_encoder.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde_json::{json, Value as Json}; use std::borrow::Cow; diff --git a/rust/tw_evm/tests/barz.rs b/rust/tw_evm/tests/barz.rs index 270bd958ae2..cea9732f5c5 100644 --- a/rust/tw_evm/tests/barz.rs +++ b/rust/tw_evm/tests/barz.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::borrow::Cow; use tw_coin_entry::error::SigningErrorType; diff --git a/rust/tw_evm/tests/message_signer.rs b/rust/tw_evm/tests/message_signer.rs index fe5acaca9aa..a00802a4c3d 100644 --- a/rust/tw_evm/tests/message_signer.rs +++ b/rust/tw_evm/tests/message_signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::error::SigningErrorType; use tw_coin_entry::modules::message_signer::MessageSigner; diff --git a/rust/tw_evm/tests/rlp.rs b/rust/tw_evm/tests/rlp.rs index df7bb0c9915..f25a831a0f5 100644 --- a/rust/tw_evm/tests/rlp.rs +++ b/rust/tw_evm/tests/rlp.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::borrow::Cow; use std::str::FromStr; diff --git a/rust/tw_evm/tests/signer.rs b/rust/tw_evm/tests/signer.rs index 9b43ca7cea3..33f50fa3080 100644 --- a/rust/tw_evm/tests/signer.rs +++ b/rust/tw_evm/tests/signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::borrow::Cow; use tw_coin_entry::error::SigningErrorType; diff --git a/rust/tw_hash/fuzz/fuzz_targets/hash_fuzz.rs b/rust/tw_hash/fuzz/fuzz_targets/hash_fuzz.rs index ddd74bce7df..eab6fcac0c8 100644 --- a/rust/tw_hash/fuzz/fuzz_targets/hash_fuzz.rs +++ b/rust/tw_hash/fuzz/fuzz_targets/hash_fuzz.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![no_main] diff --git a/rust/tw_hash/src/blake.rs b/rust/tw_hash/src/blake.rs index b7b6b93fb3b..8cf93d8e0cf 100644 --- a/rust/tw_hash/src/blake.rs +++ b/rust/tw_hash/src/blake.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use blake_hash::{Blake256, Digest}; diff --git a/rust/tw_hash/src/blake2.rs b/rust/tw_hash/src/blake2.rs index 6eaef3a5ad9..52af843d2eb 100644 --- a/rust/tw_hash/src/blake2.rs +++ b/rust/tw_hash/src/blake2.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::Error; use blake2b_ref::Blake2bBuilder; diff --git a/rust/tw_hash/src/crc32.rs b/rust/tw_hash/src/crc32.rs index f413fd13a19..76000454de9 100644 --- a/rust/tw_hash/src/crc32.rs +++ b/rust/tw_hash/src/crc32.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // Table taken from https://web.mit.edu/freebsd/head/sys/libkern/crc32.c (Public Domain code) // This table is used to speed up the crc calculation. diff --git a/rust/tw_hash/src/ffi.rs b/rust/tw_hash/src/ffi.rs index e49f96f336b..08c4faf44b9 100644 --- a/rust/tw_hash/src/ffi.rs +++ b/rust/tw_hash/src/ffi.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![allow(clippy::missing_safety_doc)] diff --git a/rust/tw_hash/src/groestl.rs b/rust/tw_hash/src/groestl.rs index e1ab3cefd3c..30ec191cd62 100644 --- a/rust/tw_hash/src/groestl.rs +++ b/rust/tw_hash/src/groestl.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::hash_wrapper::hasher; use groestl::Groestl512; diff --git a/rust/tw_hash/src/hash_array.rs b/rust/tw_hash/src/hash_array.rs index 95ab0388a12..0c4d893b064 100644 --- a/rust/tw_hash/src/hash_array.rs +++ b/rust/tw_hash/src/hash_array.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::Error; use std::fmt; diff --git a/rust/tw_hash/src/hash_wrapper.rs b/rust/tw_hash/src/hash_wrapper.rs index 20a81d993be..da8bda6154e 100644 --- a/rust/tw_hash/src/hash_wrapper.rs +++ b/rust/tw_hash/src/hash_wrapper.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use digest::Digest; diff --git a/rust/tw_hash/src/hasher.rs b/rust/tw_hash/src/hasher.rs index 086c9915f0c..76722244b10 100644 --- a/rust/tw_hash/src/hasher.rs +++ b/rust/tw_hash/src/hasher.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ripemd::ripemd_160; use crate::sha2::sha256; diff --git a/rust/tw_hash/src/hmac.rs b/rust/tw_hash/src/hmac.rs index 3e3ffdc0ab1..475c5bbdb7f 100644 --- a/rust/tw_hash/src/hmac.rs +++ b/rust/tw_hash/src/hmac.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use hmac::{Hmac, Mac}; use sha2::Sha256; diff --git a/rust/tw_hash/src/lib.rs b/rust/tw_hash/src/lib.rs index 8020e6cde19..3c62256bbf7 100644 --- a/rust/tw_hash/src/lib.rs +++ b/rust/tw_hash/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod blake; pub mod blake2; diff --git a/rust/tw_hash/src/ripemd.rs b/rust/tw_hash/src/ripemd.rs index 2ce18fb2253..04f208281a1 100644 --- a/rust/tw_hash/src/ripemd.rs +++ b/rust/tw_hash/src/ripemd.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::hash_wrapper::hasher; use ripemd::Ripemd160; diff --git a/rust/tw_hash/src/sha1.rs b/rust/tw_hash/src/sha1.rs index 597accf0f75..95262c5ed77 100644 --- a/rust/tw_hash/src/sha1.rs +++ b/rust/tw_hash/src/sha1.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::hash_wrapper::hasher; use sha1::Sha1; diff --git a/rust/tw_hash/src/sha2.rs b/rust/tw_hash/src/sha2.rs index 0ac313162c2..dae77d8c8d8 100644 --- a/rust/tw_hash/src/sha2.rs +++ b/rust/tw_hash/src/sha2.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::hash_wrapper::hasher; use sha2::{Sha224, Sha256, Sha512, Sha512_256}; diff --git a/rust/tw_hash/src/sha3.rs b/rust/tw_hash/src/sha3.rs index c4c5318341a..1ff8fd38719 100644 --- a/rust/tw_hash/src/sha3.rs +++ b/rust/tw_hash/src/sha3.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::hash_wrapper::hasher; use sha3::{Keccak256, Keccak512, Sha3_256, Sha3_512}; diff --git a/rust/tw_hash/tests/hash_ffi_tests.rs b/rust/tw_hash/tests/hash_ffi_tests.rs index a66f6dd13d9..ccd2e8e952e 100644 --- a/rust/tw_hash/tests/hash_ffi_tests.rs +++ b/rust/tw_hash/tests/hash_ffi_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_encoding::hex; use tw_encoding::hex::FromHexError; diff --git a/rust/tw_internet_computer/build.rs b/rust/tw_internet_computer/build.rs index a163f826026..1b8ee954b23 100644 --- a/rust/tw_internet_computer/build.rs +++ b/rust/tw_internet_computer/build.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use pb_rs::types::FileDescriptor; use pb_rs::ConfigBuilder; diff --git a/rust/tw_internet_computer/src/address.rs b/rust/tw_internet_computer/src/address.rs index 17ba0ed3ee4..80821df6f70 100644 --- a/rust/tw_internet_computer/src/address.rs +++ b/rust/tw_internet_computer/src/address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::{ coin_entry::CoinAddress, diff --git a/rust/tw_internet_computer/src/context.rs b/rust/tw_internet_computer/src/context.rs index 45e2a3d5e5d..f429d4bc5db 100644 --- a/rust/tw_internet_computer/src/context.rs +++ b/rust/tw_internet_computer/src/context.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::{ address::{AccountIdentifier, IcpAddress}, diff --git a/rust/tw_internet_computer/src/entry.rs b/rust/tw_internet_computer/src/entry.rs index 99a76913646..bad58a82a04 100644 --- a/rust/tw_internet_computer/src/entry.rs +++ b/rust/tw_internet_computer/src/entry.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::str::FromStr; diff --git a/rust/tw_internet_computer/src/lib.rs b/rust/tw_internet_computer/src/lib.rs index c32ab978e54..acef6a64209 100644 --- a/rust/tw_internet_computer/src/lib.rs +++ b/rust/tw_internet_computer/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod address; pub mod context; diff --git a/rust/tw_internet_computer/src/protocol/envelope.rs b/rust/tw_internet_computer/src/protocol/envelope.rs index cfa00cdd274..c9627d53e1b 100644 --- a/rust/tw_internet_computer/src/protocol/envelope.rs +++ b/rust/tw_internet_computer/src/protocol/envelope.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::collections::BTreeMap; diff --git a/rust/tw_internet_computer/src/protocol/identity.rs b/rust/tw_internet_computer/src/protocol/identity.rs index ed8ce0f7cbb..b97e0308ec2 100644 --- a/rust/tw_internet_computer/src/protocol/identity.rs +++ b/rust/tw_internet_computer/src/protocol/identity.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_hash::H256; use tw_keypair::{ecdsa::secp256k1::PrivateKey, traits::SigningKeyTrait, KeyPairError}; diff --git a/rust/tw_internet_computer/src/protocol/mod.rs b/rust/tw_internet_computer/src/protocol/mod.rs index f21353b57b7..19b13630cc2 100644 --- a/rust/tw_internet_computer/src/protocol/mod.rs +++ b/rust/tw_internet_computer/src/protocol/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod envelope; pub mod identity; diff --git a/rust/tw_internet_computer/src/protocol/principal.rs b/rust/tw_internet_computer/src/protocol/principal.rs index 61680e14f48..905739a86b8 100644 --- a/rust/tw_internet_computer/src/protocol/principal.rs +++ b/rust/tw_internet_computer/src/protocol/principal.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. /// Taken from candid crate and modified to rely upon built-in crc32 and SHA224 functionality. use std::fmt::Write; diff --git a/rust/tw_internet_computer/src/protocol/request_id.rs b/rust/tw_internet_computer/src/protocol/request_id.rs index 5f9d2bcf74b..ecc48a5a17f 100644 --- a/rust/tw_internet_computer/src/protocol/request_id.rs +++ b/rust/tw_internet_computer/src/protocol/request_id.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::collections::BTreeMap; diff --git a/rust/tw_internet_computer/src/protocol/rosetta.rs b/rust/tw_internet_computer/src/protocol/rosetta.rs index 45e2a0be888..a929a4f5f15 100644 --- a/rust/tw_internet_computer/src/protocol/rosetta.rs +++ b/rust/tw_internet_computer/src/protocol/rosetta.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use super::envelope::{Envelope, EnvelopeCallContent, EnvelopeReadStateContent}; use serde::Serialize; diff --git a/rust/tw_internet_computer/src/signer.rs b/rust/tw_internet_computer/src/signer.rs index 113dde0c27f..311fd587f44 100644 --- a/rust/tw_internet_computer/src/signer.rs +++ b/rust/tw_internet_computer/src/signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::marker::PhantomData; diff --git a/rust/tw_internet_computer/src/transactions/mod.rs b/rust/tw_internet_computer/src/transactions/mod.rs index e401c7e08f5..d15fa9be777 100644 --- a/rust/tw_internet_computer/src/transactions/mod.rs +++ b/rust/tw_internet_computer/src/transactions/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod transfer; diff --git a/rust/tw_internet_computer/src/transactions/proto/ledger.proto b/rust/tw_internet_computer/src/transactions/proto/ledger.proto index 726d2ffd74b..59858bbf2ea 100644 --- a/rust/tw_internet_computer/src/transactions/proto/ledger.proto +++ b/rust/tw_internet_computer/src/transactions/proto/ledger.proto @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // -*- c-basic-offset: 2 -*- // Source: https://github.com/dfinity/ic/blob/master/rs/rosetta-api/icp_ledger/proto/ic_ledger/pb/v1/types.proto diff --git a/rust/tw_internet_computer/src/transactions/proto/types.proto b/rust/tw_internet_computer/src/transactions/proto/types.proto index 2c63ceb555e..f869c11ccc3 100644 --- a/rust/tw_internet_computer/src/transactions/proto/types.proto +++ b/rust/tw_internet_computer/src/transactions/proto/types.proto @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // Source: https://github.com/dfinity/ic/blob/master/rs/rosetta-api/icp_ledger/proto/ic_ledger/pb/v1/types.proto // Commit Hash: 703eb96fea44ad2c82740e4360a526a2a127a960 diff --git a/rust/tw_internet_computer/src/transactions/transfer.rs b/rust/tw_internet_computer/src/transactions/transfer.rs index 0b57b56d058..78708226468 100644 --- a/rust/tw_internet_computer/src/transactions/transfer.rs +++ b/rust/tw_internet_computer/src/transactions/transfer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::time::Duration; diff --git a/rust/tw_keypair/fuzz/fuzz_targets/tw_private_sign.rs b/rust/tw_keypair/fuzz/fuzz_targets/tw_private_sign.rs index b7a574f3073..166540310c3 100644 --- a/rust/tw_keypair/fuzz/fuzz_targets/tw_private_sign.rs +++ b/rust/tw_keypair/fuzz/fuzz_targets/tw_private_sign.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![no_main] diff --git a/rust/tw_keypair/fuzz/fuzz_targets/tw_private_to_public.rs b/rust/tw_keypair/fuzz/fuzz_targets/tw_private_to_public.rs index 25de9500029..b92a417a617 100644 --- a/rust/tw_keypair/fuzz/fuzz_targets/tw_private_to_public.rs +++ b/rust/tw_keypair/fuzz/fuzz_targets/tw_private_to_public.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![no_main] diff --git a/rust/tw_keypair/fuzz/fuzz_targets/tw_public_verify.rs b/rust/tw_keypair/fuzz/fuzz_targets/tw_public_verify.rs index 394d2aad416..43a209ed7a4 100644 --- a/rust/tw_keypair/fuzz/fuzz_targets/tw_public_verify.rs +++ b/rust/tw_keypair/fuzz/fuzz_targets/tw_public_verify.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![no_main] diff --git a/rust/tw_keypair/src/ecdsa/canonical.rs b/rust/tw_keypair/src/ecdsa/canonical.rs index fdfc9244479..e335ee5c7de 100644 --- a/rust/tw_keypair/src/ecdsa/canonical.rs +++ b/rust/tw_keypair/src/ecdsa/canonical.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. //! This module is a Proof Of Concept that proves the possibility to implement the following in Rust: //! https://github.com/trustwallet/wallet-core/blob/d9e35ec485b1366dd10509192d02d9dbb6877ab3/src/PrivateKey.cpp#L253-L282 diff --git a/rust/tw_keypair/src/ecdsa/der.rs b/rust/tw_keypair/src/ecdsa/der.rs index 6cd1b8fa91e..77435a546fb 100644 --- a/rust/tw_keypair/src/ecdsa/der.rs +++ b/rust/tw_keypair/src/ecdsa/der.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::{KeyPairError, KeyPairResult}; use der::asn1::UintRef; diff --git a/rust/tw_keypair/src/ecdsa/mod.rs b/rust/tw_keypair/src/ecdsa/mod.rs index 61e49f722c3..dae3dd5bb7e 100644 --- a/rust/tw_keypair/src/ecdsa/mod.rs +++ b/rust/tw_keypair/src/ecdsa/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use ecdsa::elliptic_curve::bigint::U256; use ecdsa::elliptic_curve::consts::U32; diff --git a/rust/tw_keypair/src/ecdsa/nist256p1/keypair.rs b/rust/tw_keypair/src/ecdsa/nist256p1/keypair.rs index df9ff15a8e0..9af364dd93c 100644 --- a/rust/tw_keypair/src/ecdsa/nist256p1/keypair.rs +++ b/rust/tw_keypair/src/ecdsa/nist256p1/keypair.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ecdsa::nist256p1::private::PrivateKey; use crate::ecdsa::nist256p1::public::PublicKey; diff --git a/rust/tw_keypair/src/ecdsa/nist256p1/mod.rs b/rust/tw_keypair/src/ecdsa/nist256p1/mod.rs index 890d77d610a..7e92a7b3dfc 100644 --- a/rust/tw_keypair/src/ecdsa/nist256p1/mod.rs +++ b/rust/tw_keypair/src/ecdsa/nist256p1/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use p256::NistP256; diff --git a/rust/tw_keypair/src/ecdsa/nist256p1/private.rs b/rust/tw_keypair/src/ecdsa/nist256p1/private.rs index 3149967ce68..0d2ede5bf83 100644 --- a/rust/tw_keypair/src/ecdsa/nist256p1/private.rs +++ b/rust/tw_keypair/src/ecdsa/nist256p1/private.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ecdsa::nist256p1::public::PublicKey; use crate::ecdsa::nist256p1::Signature; diff --git a/rust/tw_keypair/src/ecdsa/nist256p1/public.rs b/rust/tw_keypair/src/ecdsa/nist256p1/public.rs index 5255e920621..6ac0ec429c7 100644 --- a/rust/tw_keypair/src/ecdsa/nist256p1/public.rs +++ b/rust/tw_keypair/src/ecdsa/nist256p1/public.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ecdsa::nist256p1::{Signature, VerifySignature}; use crate::traits::VerifyingKeyTrait; diff --git a/rust/tw_keypair/src/ecdsa/secp256k1/keypair.rs b/rust/tw_keypair/src/ecdsa/secp256k1/keypair.rs index 665d341e8e7..d609741cf0a 100644 --- a/rust/tw_keypair/src/ecdsa/secp256k1/keypair.rs +++ b/rust/tw_keypair/src/ecdsa/secp256k1/keypair.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ecdsa::secp256k1::private::PrivateKey; use crate::ecdsa::secp256k1::public::PublicKey; diff --git a/rust/tw_keypair/src/ecdsa/secp256k1/mod.rs b/rust/tw_keypair/src/ecdsa/secp256k1/mod.rs index ebb0400205c..dfba7e20c37 100644 --- a/rust/tw_keypair/src/ecdsa/secp256k1/mod.rs +++ b/rust/tw_keypair/src/ecdsa/secp256k1/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use k256::Secp256k1; diff --git a/rust/tw_keypair/src/ecdsa/secp256k1/private.rs b/rust/tw_keypair/src/ecdsa/secp256k1/private.rs index 088b3bd549f..ec80de8b098 100644 --- a/rust/tw_keypair/src/ecdsa/secp256k1/private.rs +++ b/rust/tw_keypair/src/ecdsa/secp256k1/private.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ecdsa::secp256k1::public::PublicKey; use crate::ecdsa::secp256k1::Signature; diff --git a/rust/tw_keypair/src/ecdsa/secp256k1/public.rs b/rust/tw_keypair/src/ecdsa/secp256k1/public.rs index 92dc6b0cc52..73f61021069 100644 --- a/rust/tw_keypair/src/ecdsa/secp256k1/public.rs +++ b/rust/tw_keypair/src/ecdsa/secp256k1/public.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ecdsa::secp256k1::{Signature, VerifySignature}; use crate::traits::VerifyingKeyTrait; diff --git a/rust/tw_keypair/src/ecdsa/signature.rs b/rust/tw_keypair/src/ecdsa/signature.rs index 7660bc12a0c..22023509ad2 100644 --- a/rust/tw_keypair/src/ecdsa/signature.rs +++ b/rust/tw_keypair/src/ecdsa/signature.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ecdsa::EcdsaCurve; use crate::{KeyPairError, KeyPairResult}; diff --git a/rust/tw_keypair/src/ed25519/keypair.rs b/rust/tw_keypair/src/ed25519/keypair.rs index e9240d2d4d3..3ac2ac1e1fa 100644 --- a/rust/tw_keypair/src/ed25519/keypair.rs +++ b/rust/tw_keypair/src/ed25519/keypair.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ed25519::{private::PrivateKey, public::PublicKey, signature::Signature, Hasher512}; use crate::traits::{KeyPairTrait, SigningKeyTrait, VerifyingKeyTrait}; diff --git a/rust/tw_keypair/src/ed25519/mangle.rs b/rust/tw_keypair/src/ed25519/mangle.rs index f635cc19a40..1ec1da750a6 100644 --- a/rust/tw_keypair/src/ed25519/mangle.rs +++ b/rust/tw_keypair/src/ed25519/mangle.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. /// Clamps the scalar by: /// 1. clearing the 3 lower bits, diff --git a/rust/tw_keypair/src/ed25519/mod.rs b/rust/tw_keypair/src/ed25519/mod.rs index 32c079ad6af..c12af906a36 100644 --- a/rust/tw_keypair/src/ed25519/mod.rs +++ b/rust/tw_keypair/src/ed25519/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use digest::{consts::U64, Digest}; diff --git a/rust/tw_keypair/src/ed25519/modifications/cardano/extended_keypair.rs b/rust/tw_keypair/src/ed25519/modifications/cardano/extended_keypair.rs index 6da604cc660..f746f12d6d1 100644 --- a/rust/tw_keypair/src/ed25519/modifications/cardano/extended_keypair.rs +++ b/rust/tw_keypair/src/ed25519/modifications/cardano/extended_keypair.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ed25519::modifications::cardano::{ extended_private::ExtendedPrivateKey, extended_public::ExtendedPublicKey, diff --git a/rust/tw_keypair/src/ed25519/modifications/cardano/extended_private.rs b/rust/tw_keypair/src/ed25519/modifications/cardano/extended_private.rs index 5c3ac800c52..f2fac1a0687 100644 --- a/rust/tw_keypair/src/ed25519/modifications/cardano/extended_private.rs +++ b/rust/tw_keypair/src/ed25519/modifications/cardano/extended_private.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ed25519::modifications::cardano::extended_public::{ ExtendedPublicKey, ExtendedPublicPart, diff --git a/rust/tw_keypair/src/ed25519/modifications/cardano/extended_public.rs b/rust/tw_keypair/src/ed25519/modifications/cardano/extended_public.rs index 1a8eaa65b08..d3d82dba764 100644 --- a/rust/tw_keypair/src/ed25519/modifications/cardano/extended_public.rs +++ b/rust/tw_keypair/src/ed25519/modifications/cardano/extended_public.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ed25519::public::PublicKey; use crate::ed25519::signature::Signature; diff --git a/rust/tw_keypair/src/ed25519/modifications/cardano/mod.rs b/rust/tw_keypair/src/ed25519/modifications/cardano/mod.rs index d5f0f3d2eae..1a9b99f1ba2 100644 --- a/rust/tw_keypair/src/ed25519/modifications/cardano/mod.rs +++ b/rust/tw_keypair/src/ed25519/modifications/cardano/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use sha2::Sha512; diff --git a/rust/tw_keypair/src/ed25519/modifications/mod.rs b/rust/tw_keypair/src/ed25519/modifications/mod.rs index 7f19f608c5d..555c1e3fd70 100644 --- a/rust/tw_keypair/src/ed25519/modifications/mod.rs +++ b/rust/tw_keypair/src/ed25519/modifications/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod cardano; pub mod waves; diff --git a/rust/tw_keypair/src/ed25519/modifications/waves/keypair.rs b/rust/tw_keypair/src/ed25519/modifications/waves/keypair.rs index 890ac668041..f1c2b270ef4 100644 --- a/rust/tw_keypair/src/ed25519/modifications/waves/keypair.rs +++ b/rust/tw_keypair/src/ed25519/modifications/waves/keypair.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ed25519::modifications::waves::private::PrivateKey; use crate::ed25519::modifications::waves::public::PublicKey; diff --git a/rust/tw_keypair/src/ed25519/modifications/waves/mod.rs b/rust/tw_keypair/src/ed25519/modifications/waves/mod.rs index 45f0320be09..c69cfeddbce 100644 --- a/rust/tw_keypair/src/ed25519/modifications/waves/mod.rs +++ b/rust/tw_keypair/src/ed25519/modifications/waves/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use sha2::Sha512; diff --git a/rust/tw_keypair/src/ed25519/modifications/waves/private.rs b/rust/tw_keypair/src/ed25519/modifications/waves/private.rs index 569f9dde44c..4c6c68f5e46 100644 --- a/rust/tw_keypair/src/ed25519/modifications/waves/private.rs +++ b/rust/tw_keypair/src/ed25519/modifications/waves/private.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ed25519::modifications::waves::public::PublicKey; use crate::ed25519::modifications::waves::signature::Signature; diff --git a/rust/tw_keypair/src/ed25519/modifications/waves/public.rs b/rust/tw_keypair/src/ed25519/modifications/waves/public.rs index 47e3e2ce638..1842f5e5cd3 100644 --- a/rust/tw_keypair/src/ed25519/modifications/waves/public.rs +++ b/rust/tw_keypair/src/ed25519/modifications/waves/public.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ed25519::modifications::waves::signature::Signature; use crate::ed25519::public::PublicKey as StandardPublicKey; diff --git a/rust/tw_keypair/src/ed25519/modifications/waves/signature.rs b/rust/tw_keypair/src/ed25519/modifications/waves/signature.rs index 8c1819a6a73..39613316940 100644 --- a/rust/tw_keypair/src/ed25519/modifications/waves/signature.rs +++ b/rust/tw_keypair/src/ed25519/modifications/waves/signature.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ed25519::public::PublicKey as StandardPublicKey; use crate::ed25519::signature::Signature as StandardSignature; diff --git a/rust/tw_keypair/src/ed25519/private.rs b/rust/tw_keypair/src/ed25519/private.rs index 9384bc66096..5df5c59b5a0 100644 --- a/rust/tw_keypair/src/ed25519/private.rs +++ b/rust/tw_keypair/src/ed25519/private.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ed25519::public::PublicKey; use crate::ed25519::secret::ExpandedSecretKey; diff --git a/rust/tw_keypair/src/ed25519/public.rs b/rust/tw_keypair/src/ed25519/public.rs index 883147a92c8..c0de54f0c12 100644 --- a/rust/tw_keypair/src/ed25519/public.rs +++ b/rust/tw_keypair/src/ed25519/public.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ed25519::mangle::mangle_scalar; use crate::ed25519::secret::ExpandedSecretKey; diff --git a/rust/tw_keypair/src/ed25519/secret.rs b/rust/tw_keypair/src/ed25519/secret.rs index ed39c3e7a34..ca45153f84c 100644 --- a/rust/tw_keypair/src/ed25519/secret.rs +++ b/rust/tw_keypair/src/ed25519/secret.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ed25519::mangle::mangle_scalar; use crate::ed25519::signature::Signature; diff --git a/rust/tw_keypair/src/ed25519/signature.rs b/rust/tw_keypair/src/ed25519/signature.rs index cb45fcce1fc..f0f957938a7 100644 --- a/rust/tw_keypair/src/ed25519/signature.rs +++ b/rust/tw_keypair/src/ed25519/signature.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::{KeyPairError, KeyPairResult}; use curve25519_dalek::edwards::CompressedEdwardsY; diff --git a/rust/tw_keypair/src/ffi/asn.rs b/rust/tw_keypair/src/ffi/asn.rs index 995c8c2b112..7befaa9c10f 100644 --- a/rust/tw_keypair/src/ffi/asn.rs +++ b/rust/tw_keypair/src/ffi/asn.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![allow(clippy::missing_safety_doc)] diff --git a/rust/tw_keypair/src/ffi/mod.rs b/rust/tw_keypair/src/ffi/mod.rs index 15cac0a8cf7..7256fd84acf 100644 --- a/rust/tw_keypair/src/ffi/mod.rs +++ b/rust/tw_keypair/src/ffi/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod asn; pub mod privkey; diff --git a/rust/tw_keypair/src/ffi/privkey.rs b/rust/tw_keypair/src/ffi/privkey.rs index 30f68811458..28850435a90 100644 --- a/rust/tw_keypair/src/ffi/privkey.rs +++ b/rust/tw_keypair/src/ffi/privkey.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![allow(clippy::missing_safety_doc)] diff --git a/rust/tw_keypair/src/ffi/pubkey.rs b/rust/tw_keypair/src/ffi/pubkey.rs index e1c86476b07..7f05f5f0fe9 100644 --- a/rust/tw_keypair/src/ffi/pubkey.rs +++ b/rust/tw_keypair/src/ffi/pubkey.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![allow(clippy::missing_safety_doc)] diff --git a/rust/tw_keypair/src/lib.rs b/rust/tw_keypair/src/lib.rs index 945222877e5..eb3fb583a50 100644 --- a/rust/tw_keypair/src/lib.rs +++ b/rust/tw_keypair/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. //! `tw_keypair` crate defines the keypairs, private and public keys that are used to sign messages, //! verify signatures and more. diff --git a/rust/tw_keypair/src/starkex/keypair.rs b/rust/tw_keypair/src/starkex/keypair.rs index b827bdb80db..cc278e08d92 100644 --- a/rust/tw_keypair/src/starkex/keypair.rs +++ b/rust/tw_keypair/src/starkex/keypair.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::starkex::private::PrivateKey; use crate::starkex::public::PublicKey; diff --git a/rust/tw_keypair/src/starkex/mod.rs b/rust/tw_keypair/src/starkex/mod.rs index 35db9f4e98c..75bf81ae009 100644 --- a/rust/tw_keypair/src/starkex/mod.rs +++ b/rust/tw_keypair/src/starkex/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. mod keypair; mod private; diff --git a/rust/tw_keypair/src/starkex/private.rs b/rust/tw_keypair/src/starkex/private.rs index 973d2c06699..58343428536 100644 --- a/rust/tw_keypair/src/starkex/private.rs +++ b/rust/tw_keypair/src/starkex/private.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::starkex::field_element_from_bytes_be; use crate::starkex::public::PublicKey; diff --git a/rust/tw_keypair/src/starkex/public.rs b/rust/tw_keypair/src/starkex/public.rs index 16dad099952..dce6744e954 100644 --- a/rust/tw_keypair/src/starkex/public.rs +++ b/rust/tw_keypair/src/starkex/public.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::starkex::field_element_from_bytes_be; use crate::starkex::signature::Signature; diff --git a/rust/tw_keypair/src/starkex/signature.rs b/rust/tw_keypair/src/starkex/signature.rs index 9ca0de029b6..a602d728e6c 100644 --- a/rust/tw_keypair/src/starkex/signature.rs +++ b/rust/tw_keypair/src/starkex/signature.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::KeyPairError; use starknet_ff::FieldElement; diff --git a/rust/tw_keypair/src/test_utils/mod.rs b/rust/tw_keypair/src/test_utils/mod.rs index 2e2d49008ed..a9570356b7e 100644 --- a/rust/tw_keypair/src/test_utils/mod.rs +++ b/rust/tw_keypair/src/test_utils/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod tw_private_key_helper; pub mod tw_public_key_helper; diff --git a/rust/tw_keypair/src/test_utils/tw_private_key_helper.rs b/rust/tw_keypair/src/test_utils/tw_private_key_helper.rs index c8cc3795578..6c3c3bfc629 100644 --- a/rust/tw_keypair/src/test_utils/tw_private_key_helper.rs +++ b/rust/tw_keypair/src/test_utils/tw_private_key_helper.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ffi::privkey::{tw_private_key_create_with_data, tw_private_key_delete, TWPrivateKey}; use tw_encoding::hex; diff --git a/rust/tw_keypair/src/test_utils/tw_public_key_helper.rs b/rust/tw_keypair/src/test_utils/tw_public_key_helper.rs index daecadc61aa..fdc7450642a 100644 --- a/rust/tw_keypair/src/test_utils/tw_public_key_helper.rs +++ b/rust/tw_keypair/src/test_utils/tw_public_key_helper.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ffi::pubkey::{tw_public_key_create_with_data, tw_public_key_delete, TWPublicKey}; use crate::tw::PublicKeyType; diff --git a/rust/tw_keypair/src/traits.rs b/rust/tw_keypair/src/traits.rs index 7b32076f7fe..5ac9e30c773 100644 --- a/rust/tw_keypair/src/traits.rs +++ b/rust/tw_keypair/src/traits.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::KeyPairResult; use tw_misc::traits::{FromSlice, ToBytesVec, ToBytesZeroizing}; diff --git a/rust/tw_keypair/src/tw/mod.rs b/rust/tw_keypair/src/tw/mod.rs index 114affc4ce6..29616fd7220 100644 --- a/rust/tw_keypair/src/tw/mod.rs +++ b/rust/tw_keypair/src/tw/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde::Deserialize; diff --git a/rust/tw_keypair/src/tw/private.rs b/rust/tw_keypair/src/tw/private.rs index c0e5fb5ae48..93b54bf3cd1 100644 --- a/rust/tw_keypair/src/tw/private.rs +++ b/rust/tw_keypair/src/tw/private.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ecdsa::{nist256p1, secp256k1}; use crate::traits::SigningKeyTrait; diff --git a/rust/tw_keypair/src/tw/public.rs b/rust/tw_keypair/src/tw/public.rs index 754ed022870..3cbbda214ef 100644 --- a/rust/tw_keypair/src/tw/public.rs +++ b/rust/tw_keypair/src/tw/public.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ecdsa::{nist256p1, secp256k1}; use crate::traits::VerifyingKeyTrait; diff --git a/rust/tw_keypair/tests/asn_parser_ffi_tests.rs b/rust/tw_keypair/tests/asn_parser_ffi_tests.rs index fde7ce39ae5..dbe7586ef6e 100644 --- a/rust/tw_keypair/tests/asn_parser_ffi_tests.rs +++ b/rust/tw_keypair/tests/asn_parser_ffi_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_encoding::hex; use tw_keypair::ffi::asn::ecdsa_signature_from_asn_der; diff --git a/rust/tw_keypair/tests/ed25519_blake2b_tests.rs b/rust/tw_keypair/tests/ed25519_blake2b_tests.rs index d6f1b29a821..53e8e718d84 100644 --- a/rust/tw_keypair/tests/ed25519_blake2b_tests.rs +++ b/rust/tw_keypair/tests/ed25519_blake2b_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde::Deserialize; use tw_encoding::hex; diff --git a/rust/tw_keypair/tests/ed25519_extended_cardano_tests.rs b/rust/tw_keypair/tests/ed25519_extended_cardano_tests.rs index 818b5ef208c..42f2b17bca7 100644 --- a/rust/tw_keypair/tests/ed25519_extended_cardano_tests.rs +++ b/rust/tw_keypair/tests/ed25519_extended_cardano_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde::Deserialize; use tw_encoding::hex; diff --git a/rust/tw_keypair/tests/ed25519_tests.rs b/rust/tw_keypair/tests/ed25519_tests.rs index de8d494b915..4b76afb5613 100644 --- a/rust/tw_keypair/tests/ed25519_tests.rs +++ b/rust/tw_keypair/tests/ed25519_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde::Deserialize; use tw_encoding::hex; diff --git a/rust/tw_keypair/tests/ed25519_waves_tests.rs b/rust/tw_keypair/tests/ed25519_waves_tests.rs index 18a1df852e0..7b39d226383 100644 --- a/rust/tw_keypair/tests/ed25519_waves_tests.rs +++ b/rust/tw_keypair/tests/ed25519_waves_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde::Deserialize; use tw_encoding::hex; diff --git a/rust/tw_keypair/tests/nist256p1_tests.rs b/rust/tw_keypair/tests/nist256p1_tests.rs index 552f40838ca..8e60832de80 100644 --- a/rust/tw_keypair/tests/nist256p1_tests.rs +++ b/rust/tw_keypair/tests/nist256p1_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde::Deserialize; use tw_hash::{H256, H264, H520}; diff --git a/rust/tw_keypair/tests/private_key_ffi_tests.rs b/rust/tw_keypair/tests/private_key_ffi_tests.rs index 7092f7b7407..894f7b812d3 100644 --- a/rust/tw_keypair/tests/private_key_ffi_tests.rs +++ b/rust/tw_keypair/tests/private_key_ffi_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_encoding::hex; use tw_hash::sha2::sha256; diff --git a/rust/tw_keypair/tests/public_key_ffi_tests.rs b/rust/tw_keypair/tests/public_key_ffi_tests.rs index 9922b0fd8a3..9195ed66f5d 100644 --- a/rust/tw_keypair/tests/public_key_ffi_tests.rs +++ b/rust/tw_keypair/tests/public_key_ffi_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_encoding::hex; use tw_hash::sha2::sha256; diff --git a/rust/tw_keypair/tests/secp256k1_tests.rs b/rust/tw_keypair/tests/secp256k1_tests.rs index 9774a47a2c1..f320cdcc32c 100644 --- a/rust/tw_keypair/tests/secp256k1_tests.rs +++ b/rust/tw_keypair/tests/secp256k1_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde::Deserialize; use tw_hash::{H256, H520}; diff --git a/rust/tw_keypair/tests/tw_keypair_starkex_tests.rs b/rust/tw_keypair/tests/tw_keypair_starkex_tests.rs index 40d28c2417d..9fe872ad797 100644 --- a/rust/tw_keypair/tests/tw_keypair_starkex_tests.rs +++ b/rust/tw_keypair/tests/tw_keypair_starkex_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_encoding::hex; use tw_hash::H512; diff --git a/rust/tw_memory/src/ffi/c_byte_array.rs b/rust/tw_memory/src/ffi/c_byte_array.rs index 337cff99677..3f4c2d98724 100644 --- a/rust/tw_memory/src/ffi/c_byte_array.rs +++ b/rust/tw_memory/src/ffi/c_byte_array.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #[repr(C)] pub struct CByteArrayResult { diff --git a/rust/tw_memory/src/ffi/c_byte_array_ref.rs b/rust/tw_memory/src/ffi/c_byte_array_ref.rs index 160b14d3f0d..7f5e3e0b4a0 100644 --- a/rust/tw_memory/src/ffi/c_byte_array_ref.rs +++ b/rust/tw_memory/src/ffi/c_byte_array_ref.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. /// A C-compatible wrapper over a byte array given as the FFI argument. #[repr(C)] diff --git a/rust/tw_memory/src/ffi/c_result.rs b/rust/tw_memory/src/ffi/c_result.rs index 850ed6116d0..fe6ed25bc32 100644 --- a/rust/tw_memory/src/ffi/c_result.rs +++ b/rust/tw_memory/src/ffi/c_result.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::ffi::c_char; diff --git a/rust/tw_memory/src/ffi/mod.rs b/rust/tw_memory/src/ffi/mod.rs index 9afbd7509f1..c69f1550a6a 100644 --- a/rust/tw_memory/src/ffi/mod.rs +++ b/rust/tw_memory/src/ffi/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![allow(clippy::missing_safety_doc)] diff --git a/rust/tw_memory/src/ffi/tw_data.rs b/rust/tw_memory/src/ffi/tw_data.rs index ad4c6ad4400..7a573d8fe4b 100644 --- a/rust/tw_memory/src/ffi/tw_data.rs +++ b/rust/tw_memory/src/ffi/tw_data.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ffi::c_byte_array_ref::CByteArrayRef; use crate::ffi::RawPtrTrait; diff --git a/rust/tw_memory/src/ffi/tw_data_vector.rs b/rust/tw_memory/src/ffi/tw_data_vector.rs index 5c596ede7fa..f9dbe4c3f38 100644 --- a/rust/tw_memory/src/ffi/tw_data_vector.rs +++ b/rust/tw_memory/src/ffi/tw_data_vector.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ffi::tw_data::TWData; use crate::ffi::RawPtrTrait; diff --git a/rust/tw_memory/src/ffi/tw_string.rs b/rust/tw_memory/src/ffi/tw_string.rs index 41d122bd64e..b434715b10d 100644 --- a/rust/tw_memory/src/ffi/tw_string.rs +++ b/rust/tw_memory/src/ffi/tw_string.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ffi::RawPtrTrait; use std::ffi::{c_char, CStr, CString}; diff --git a/rust/tw_memory/src/lib.rs b/rust/tw_memory/src/lib.rs index ff757aa6b04..b4431aacbd2 100644 --- a/rust/tw_memory/src/lib.rs +++ b/rust/tw_memory/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::ffi::{c_char, CString}; diff --git a/rust/tw_memory/src/test_utils/mod.rs b/rust/tw_memory/src/test_utils/mod.rs index d2520378719..af59d951bce 100644 --- a/rust/tw_memory/src/test_utils/mod.rs +++ b/rust/tw_memory/src/test_utils/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod tw_data_helper; pub mod tw_data_vector_helper; diff --git a/rust/tw_memory/src/test_utils/tw_data_helper.rs b/rust/tw_memory/src/test_utils/tw_data_helper.rs index 4a317968e29..ec404609f76 100644 --- a/rust/tw_memory/src/test_utils/tw_data_helper.rs +++ b/rust/tw_memory/src/test_utils/tw_data_helper.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ffi::tw_data::{ tw_data_bytes, tw_data_create_with_bytes, tw_data_delete, tw_data_size, TWData, diff --git a/rust/tw_memory/src/test_utils/tw_data_vector_helper.rs b/rust/tw_memory/src/test_utils/tw_data_vector_helper.rs index 340461e5ec8..6c0c185ed81 100644 --- a/rust/tw_memory/src/test_utils/tw_data_vector_helper.rs +++ b/rust/tw_memory/src/test_utils/tw_data_vector_helper.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ffi::tw_data_vector::{ tw_data_vector_add, tw_data_vector_create, tw_data_vector_delete, TWDataVector, diff --git a/rust/tw_memory/src/test_utils/tw_string_helper.rs b/rust/tw_memory/src/test_utils/tw_string_helper.rs index dd1fb22194a..a797a475dd5 100644 --- a/rust/tw_memory/src/test_utils/tw_string_helper.rs +++ b/rust/tw_memory/src/test_utils/tw_string_helper.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::ffi::tw_string::{ tw_string_create_with_utf8_bytes, tw_string_delete, tw_string_utf8_bytes, TWString, diff --git a/rust/tw_memory/src/test_utils/tw_wrapper.rs b/rust/tw_memory/src/test_utils/tw_wrapper.rs index 8cb5d26ada5..448b16097e9 100644 --- a/rust/tw_memory/src/test_utils/tw_wrapper.rs +++ b/rust/tw_memory/src/test_utils/tw_wrapper.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub trait WithDestructor: Sized { fn destructor() -> unsafe extern "C" fn(*mut Self); diff --git a/rust/tw_memory/tests/c_byte_array_ffi_tests.rs b/rust/tw_memory/tests/c_byte_array_ffi_tests.rs index e3fe8e41ba3..1fd1655c653 100644 --- a/rust/tw_memory/tests/c_byte_array_ffi_tests.rs +++ b/rust/tw_memory/tests/c_byte_array_ffi_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_memory::ffi::c_byte_array::{free_c_byte_array, CByteArray}; diff --git a/rust/tw_memory/tests/c_result_ffi_tests.rs b/rust/tw_memory/tests/c_result_ffi_tests.rs index dca5cee3f9e..c4f6ec6ee3e 100644 --- a/rust/tw_memory/tests/c_result_ffi_tests.rs +++ b/rust/tw_memory/tests/c_result_ffi_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_memory::ffi::c_byte_array::{CByteArray, CByteArrayResult}; use tw_memory::ffi::c_result::{OK_CODE, UNKNOWN_ERROR}; diff --git a/rust/tw_memory/tests/string_ffi_tests.rs b/rust/tw_memory/tests/string_ffi_tests.rs index f15b70838c1..6fc739aba6b 100644 --- a/rust/tw_memory/tests/string_ffi_tests.rs +++ b/rust/tw_memory/tests/string_ffi_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_memory::c_string_standalone; use tw_memory::ffi::free_string; diff --git a/rust/tw_misc/src/lib.rs b/rust/tw_misc/src/lib.rs index b99e9f66996..eeccb7e62b0 100644 --- a/rust/tw_misc/src/lib.rs +++ b/rust/tw_misc/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod macros; #[cfg(feature = "serde")] diff --git a/rust/tw_misc/src/macros.rs b/rust/tw_misc/src/macros.rs index e75c03de50b..fb30a98b8d7 100644 --- a/rust/tw_misc/src/macros.rs +++ b/rust/tw_misc/src/macros.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #[macro_export] macro_rules! try_or_false { diff --git a/rust/tw_misc/src/serde.rs b/rust/tw_misc/src/serde.rs index 869da0e1dc1..8e81ed4dd74 100644 --- a/rust/tw_misc/src/serde.rs +++ b/rust/tw_misc/src/serde.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde::{Deserialize, Serialize}; diff --git a/rust/tw_misc/src/test_utils/json.rs b/rust/tw_misc/src/test_utils/json.rs index 6b4969362e4..06da9d7ed63 100644 --- a/rust/tw_misc/src/test_utils/json.rs +++ b/rust/tw_misc/src/test_utils/json.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde_json::Value as Json; use std::borrow::Cow; diff --git a/rust/tw_misc/src/test_utils/mod.rs b/rust/tw_misc/src/test_utils/mod.rs index 019606a0fca..0860fabfa5e 100644 --- a/rust/tw_misc/src/test_utils/mod.rs +++ b/rust/tw_misc/src/test_utils/mod.rs @@ -1,7 +1,5 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod json; diff --git a/rust/tw_misc/src/traits.rs b/rust/tw_misc/src/traits.rs index cf1ba9a1e80..ab1624d5582 100644 --- a/rust/tw_misc/src/traits.rs +++ b/rust/tw_misc/src/traits.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use zeroize::Zeroizing; diff --git a/rust/tw_number/src/i256.rs b/rust/tw_number/src/i256.rs index aaae24a8257..49a617c4d6f 100644 --- a/rust/tw_number/src/i256.rs +++ b/rust/tw_number/src/i256.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::{NumberError, NumberResult, Sign, U256}; use lazy_static::lazy_static; diff --git a/rust/tw_number/src/lib.rs b/rust/tw_number/src/lib.rs index 13e6bb25ee6..0c8189ba13d 100644 --- a/rust/tw_number/src/lib.rs +++ b/rust/tw_number/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. mod i256; mod sign; diff --git a/rust/tw_number/src/sign.rs b/rust/tw_number/src/sign.rs index 438742ee06c..a6f70a9d4fd 100644 --- a/rust/tw_number/src/sign.rs +++ b/rust/tw_number/src/sign.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::fmt; diff --git a/rust/tw_number/src/u256.rs b/rust/tw_number/src/u256.rs index 049fbb0b95b..a4285f739b4 100644 --- a/rust/tw_number/src/u256.rs +++ b/rust/tw_number/src/u256.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::{NumberError, NumberResult}; use std::borrow::Cow; diff --git a/rust/tw_number/tests/u256.rs b/rust/tw_number/tests/u256.rs index dd8c2dbaafc..c411915cc5f 100644 --- a/rust/tw_number/tests/u256.rs +++ b/rust/tw_number/tests/u256.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::str::FromStr; use tw_encoding::hex; diff --git a/rust/tw_proto/build.rs b/rust/tw_proto/build.rs index b3e60df9a68..83b73253681 100644 --- a/rust/tw_proto/build.rs +++ b/rust/tw_proto/build.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use pb_rs::types::FileDescriptor; use pb_rs::ConfigBuilder; diff --git a/rust/tw_proto/src/common/google/mod.rs b/rust/tw_proto/src/common/google/mod.rs index a11a0995e85..11b986a38f3 100644 --- a/rust/tw_proto/src/common/google/mod.rs +++ b/rust/tw_proto/src/common/google/mod.rs @@ -1,7 +1,5 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod protobuf; diff --git a/rust/tw_proto/src/common/google/protobuf/mod.rs b/rust/tw_proto/src/common/google/protobuf/mod.rs index 3f4d90a7f08..2bd49afe64d 100644 --- a/rust/tw_proto/src/common/google/protobuf/mod.rs +++ b/rust/tw_proto/src/common/google/protobuf/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. mod any; mod timestamp; diff --git a/rust/tw_proto/src/common/mod.rs b/rust/tw_proto/src/common/mod.rs index 972a22f676f..51f75bd9645 100644 --- a/rust/tw_proto/src/common/mod.rs +++ b/rust/tw_proto/src/common/mod.rs @@ -1,7 +1,5 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod google; diff --git a/rust/tw_proto/src/ffi.rs b/rust/tw_proto/src/ffi.rs index fad29e643e9..5eb33e557c7 100644 --- a/rust/tw_proto/src/ffi.rs +++ b/rust/tw_proto/src/ffi.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![allow(clippy::missing_safety_doc)] diff --git a/rust/tw_proto/src/lib.rs b/rust/tw_proto/src/lib.rs index 94f2db92860..23a061c2f47 100644 --- a/rust/tw_proto/src/lib.rs +++ b/rust/tw_proto/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use quick_protobuf::{BytesReader, MessageInfo, Writer}; diff --git a/rust/tw_proto/tests/proto_ffi_tests.rs b/rust/tw_proto/tests/proto_ffi_tests.rs index ce3df7c6a03..8c8bb0e9441 100644 --- a/rust/tw_proto/tests/proto_ffi_tests.rs +++ b/rust/tw_proto/tests/proto_ffi_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_memory::ffi::c_byte_array::CByteArray; diff --git a/rust/tw_proto/tests/proto_tests.rs b/rust/tw_proto/tests/proto_tests.rs index 1bc62808559..24e66fafad2 100644 --- a/rust/tw_proto/tests/proto_tests.rs +++ b/rust/tw_proto/tests/proto_tests.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::borrow::Cow; use tw_proto::{deserialize, deserialize_prefixed, serialize, serialize_prefixed, Ethereum}; diff --git a/rust/tw_ronin/src/address.rs b/rust/tw_ronin/src/address.rs index e14be4225e0..ad4a4c076ea 100644 --- a/rust/tw_ronin/src/address.rs +++ b/rust/tw_ronin/src/address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::fmt; use std::str::FromStr; diff --git a/rust/tw_ronin/src/entry.rs b/rust/tw_ronin/src/entry.rs index 79020884ff9..07e963c6c67 100644 --- a/rust/tw_ronin/src/entry.rs +++ b/rust/tw_ronin/src/entry.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::Address; use crate::ronin_context::RoninContext; diff --git a/rust/tw_ronin/src/lib.rs b/rust/tw_ronin/src/lib.rs index 132d9b9b730..615f3566671 100644 --- a/rust/tw_ronin/src/lib.rs +++ b/rust/tw_ronin/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod address; pub mod entry; diff --git a/rust/tw_ronin/src/ronin_context.rs b/rust/tw_ronin/src/ronin_context.rs index 0799f42e3f5..e30475a9f1f 100644 --- a/rust/tw_ronin/src/ronin_context.rs +++ b/rust/tw_ronin/src/ronin_context.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use crate::address::Address; use tw_evm::evm_context::EvmContext; diff --git a/rust/tw_ronin/tests/address.rs b/rust/tw_ronin/tests/address.rs index 0e62310ddb0..4a97ee560cc 100644 --- a/rust/tw_ronin/tests/address.rs +++ b/rust/tw_ronin/tests/address.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::str::FromStr; use tw_ronin::address::Address; diff --git a/rust/tw_ronin/tests/compiler.rs b/rust/tw_ronin/tests/compiler.rs index 5c99e67b419..f755ed12431 100644 --- a/rust/tw_ronin/tests/compiler.rs +++ b/rust/tw_ronin/tests/compiler.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::borrow::Cow; use tw_coin_entry::coin_entry_ext::CoinEntryExt; diff --git a/rust/tw_ronin/tests/rlp.rs b/rust/tw_ronin/tests/rlp.rs index d4d88b6dbb0..1bfce0fa625 100644 --- a/rust/tw_ronin/tests/rlp.rs +++ b/rust/tw_ronin/tests/rlp.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::borrow::Cow; use tw_coin_entry::error::SigningErrorType; diff --git a/rust/tw_ronin/tests/signer.rs b/rust/tw_ronin/tests/signer.rs index d68396a34b4..16e7e747cbd 100644 --- a/rust/tw_ronin/tests/signer.rs +++ b/rust/tw_ronin/tests/signer.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::borrow::Cow; use tw_coin_entry::coin_entry_ext::CoinEntryExt; diff --git a/rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs b/rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs index 5a172e2c648..946f65d9e86 100644 --- a/rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs +++ b/rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![allow(clippy::missing_safety_doc)] diff --git a/rust/wallet_core_rs/src/ffi/bitcoin/mod.rs b/rust/wallet_core_rs/src/ffi/bitcoin/mod.rs index 5b0dcab0885..84b48e76e1e 100644 --- a/rust/wallet_core_rs/src/ffi/bitcoin/mod.rs +++ b/rust/wallet_core_rs/src/ffi/bitcoin/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #[cfg(feature = "bitcoin-legacy")] pub mod legacy; diff --git a/rust/wallet_core_rs/src/ffi/ethereum/abi.rs b/rust/wallet_core_rs/src/ffi/ethereum/abi.rs index 84f64877781..4c21d659542 100644 --- a/rust/wallet_core_rs/src/ffi/ethereum/abi.rs +++ b/rust/wallet_core_rs/src/ffi/ethereum/abi.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![allow(clippy::missing_safety_doc)] diff --git a/rust/wallet_core_rs/src/ffi/ethereum/mod.rs b/rust/wallet_core_rs/src/ffi/ethereum/mod.rs index e3d6538646b..c3de348c12c 100644 --- a/rust/wallet_core_rs/src/ffi/ethereum/mod.rs +++ b/rust/wallet_core_rs/src/ffi/ethereum/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #[cfg(feature = "ethereum-abi")] pub mod abi; diff --git a/rust/wallet_core_rs/src/ffi/ethereum/rlp.rs b/rust/wallet_core_rs/src/ffi/ethereum/rlp.rs index f13470891c6..a1274efcecd 100644 --- a/rust/wallet_core_rs/src/ffi/ethereum/rlp.rs +++ b/rust/wallet_core_rs/src/ffi/ethereum/rlp.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #![allow(clippy::missing_safety_doc)] diff --git a/rust/wallet_core_rs/src/ffi/mod.rs b/rust/wallet_core_rs/src/ffi/mod.rs index 54176826c15..7df4e1ad4eb 100644 --- a/rust/wallet_core_rs/src/ffi/mod.rs +++ b/rust/wallet_core_rs/src/ffi/mod.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod bitcoin; pub mod ethereum; diff --git a/rust/wallet_core_rs/src/lib.rs b/rust/wallet_core_rs/src/lib.rs index eefcb93f8c8..fa6c23b6bb6 100644 --- a/rust/wallet_core_rs/src/lib.rs +++ b/rust/wallet_core_rs/src/lib.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub extern crate tw_any_coin; pub extern crate tw_aptos; diff --git a/rust/wallet_core_rs/tests/ethereum_abi.rs b/rust/wallet_core_rs/tests/ethereum_abi.rs index 50f1dd09d7e..33802575420 100644 --- a/rust/wallet_core_rs/tests/ethereum_abi.rs +++ b/rust/wallet_core_rs/tests/ethereum_abi.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use serde_json::{json, Value as Json}; use tw_encoding::hex::{DecodeHex, ToHex}; diff --git a/rust/wallet_core_rs/tests/ethereum_rlp.rs b/rust/wallet_core_rs/tests/ethereum_rlp.rs index 2326c482042..48eec4bfa51 100644 --- a/rust/wallet_core_rs/tests/ethereum_rlp.rs +++ b/rust/wallet_core_rs/tests/ethereum_rlp.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use tw_coin_entry::error::SigningErrorType; use tw_coin_registry::coin_type::CoinType; diff --git a/samples/cpp/CMakeLists.txt b/samples/cpp/CMakeLists.txt index 5fe33b0cd93..02bfe6e2ad5 100644 --- a/samples/cpp/CMakeLists.txt +++ b/samples/cpp/CMakeLists.txt @@ -1,8 +1,6 @@ -# Copyright © 2017-2022 Trust Wallet. +# SPDX-License-Identifier: Apache-2.0 # -# This file is part of Trust. The full Trust copyright notice, including -# terms governing use, modification, and redistribution, is contained in the -# file LICENSE at the root of the source code distribution tree. +# Copyright © 2017 Trust Wallet. # Expected input configuration: WALLET_CORE: directory for TrustWalletCore build dir # e.g. cmake . -DWALLET_CORE=../wallet-core diff --git a/samples/cpp/sample.cpp b/samples/cpp/sample.cpp index 11221948e9d..1cc19a3196f 100644 --- a/samples/cpp/sample.cpp +++ b/samples/cpp/sample.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/samples/osx/cocoapods/WalletCoreExample/AppDelegate.swift b/samples/osx/cocoapods/WalletCoreExample/AppDelegate.swift index 5aaa729af77..8c2812c17da 100644 --- a/samples/osx/cocoapods/WalletCoreExample/AppDelegate.swift +++ b/samples/osx/cocoapods/WalletCoreExample/AppDelegate.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import Cocoa diff --git a/samples/osx/cocoapods/WalletCoreExample/ViewController.swift b/samples/osx/cocoapods/WalletCoreExample/ViewController.swift index bb130d940b6..f758c3ed046 100644 --- a/samples/osx/cocoapods/WalletCoreExample/ViewController.swift +++ b/samples/osx/cocoapods/WalletCoreExample/ViewController.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import Cocoa import WalletCore diff --git a/samples/rust/src/build.rs b/samples/rust/src/build.rs index e9d0065dfa9..63852debfc1 100644 --- a/samples/rust/src/build.rs +++ b/samples/rust/src/build.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. use std::fs; use std::path::Path; diff --git a/samples/rust/src/main.rs b/samples/rust/src/main.rs index a2228f5fcb8..06a3938ef6d 100644 --- a/samples/rust/src/main.rs +++ b/samples/rust/src/main.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. pub mod walletcore_iface; pub mod walletcore_extra; diff --git a/samples/rust/src/walletcore_extra.rs b/samples/rust/src/walletcore_extra.rs index 6f3b1c49e98..d6886b92774 100644 --- a/samples/rust/src/walletcore_extra.rs +++ b/samples/rust/src/walletcore_extra.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // Extra type helpers, traits for wallet-core interfaces diff --git a/samples/rust/src/walletcore_iface.rs b/samples/rust/src/walletcore_iface.rs index 5e3a71f8f15..190e55206bd 100644 --- a/samples/rust/src/walletcore_iface.rs +++ b/samples/rust/src/walletcore_iface.rs @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // Rust interfaces to wallet-core // Could be auto-generated diff --git a/src/Aeternity/Address.cpp b/src/Aeternity/Address.cpp index 3ae8774d405..381e4b41ba0 100644 --- a/src/Aeternity/Address.cpp +++ b/src/Aeternity/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include "Identifiers.h" diff --git a/src/Aeternity/Address.h b/src/Aeternity/Address.h index 8a9be7607a8..3d4379a5bde 100644 --- a/src/Aeternity/Address.h +++ b/src/Aeternity/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Aeternity/Entry.cpp b/src/Aeternity/Entry.cpp index ff7a17be354..1a8c25ad659 100644 --- a/src/Aeternity/Entry.cpp +++ b/src/Aeternity/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Aeternity/Entry.h b/src/Aeternity/Entry.h index 913812c67a6..36f63bff565 100644 --- a/src/Aeternity/Entry.h +++ b/src/Aeternity/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Aeternity/Identifiers.h b/src/Aeternity/Identifiers.h index cf9ce851d3e..d0d8a3e5df5 100644 --- a/src/Aeternity/Identifiers.h +++ b/src/Aeternity/Identifiers.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Aeternity/Signer.cpp b/src/Aeternity/Signer.cpp index 20d31a6dd61..ec22012eb88 100644 --- a/src/Aeternity/Signer.cpp +++ b/src/Aeternity/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Base58.h" diff --git a/src/Aeternity/Signer.h b/src/Aeternity/Signer.h index db49586d535..41aacb86b19 100644 --- a/src/Aeternity/Signer.h +++ b/src/Aeternity/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Aeternity/Transaction.cpp b/src/Aeternity/Transaction.cpp index 90dd197b74b..af3f8002928 100644 --- a/src/Aeternity/Transaction.cpp +++ b/src/Aeternity/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" #include "Identifiers.h" diff --git a/src/Aeternity/Transaction.h b/src/Aeternity/Transaction.h index f019395ecd6..4d938f45503 100644 --- a/src/Aeternity/Transaction.h +++ b/src/Aeternity/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Aion/Address.cpp b/src/Aion/Address.cpp index 7490a4a6ad4..4a357ef8a2e 100644 --- a/src/Aion/Address.cpp +++ b/src/Aion/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include "../HexCoding.h" diff --git a/src/Aion/Address.h b/src/Aion/Address.h index 430e5d4762d..459de4faffb 100644 --- a/src/Aion/Address.h +++ b/src/Aion/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Aion/Entry.cpp b/src/Aion/Entry.cpp index 55bbacc128d..9c979ddc9e0 100644 --- a/src/Aion/Entry.cpp +++ b/src/Aion/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Aion/Entry.h b/src/Aion/Entry.h index 896081c7981..981042894ed 100644 --- a/src/Aion/Entry.h +++ b/src/Aion/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Aion/RLP.h b/src/Aion/RLP.h index a1995e11a60..6330bc278de 100644 --- a/src/Aion/RLP.h +++ b/src/Aion/RLP.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Aion/Signer.cpp b/src/Aion/Signer.cpp index 7cc526196c4..b3cacc038e4 100644 --- a/src/Aion/Signer.cpp +++ b/src/Aion/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "../uint256.h" diff --git a/src/Aion/Signer.h b/src/Aion/Signer.h index 341150aee73..3bf90e3c4a3 100644 --- a/src/Aion/Signer.h +++ b/src/Aion/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Aion/Transaction.cpp b/src/Aion/Transaction.cpp index 62f81e6b163..c4681472340 100644 --- a/src/Aion/Transaction.cpp +++ b/src/Aion/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" diff --git a/src/Aion/Transaction.h b/src/Aion/Transaction.h index 2eab15d2c87..d3805859df3 100644 --- a/src/Aion/Transaction.h +++ b/src/Aion/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Algorand/Address.cpp b/src/Algorand/Address.cpp index 417e90ab52a..ba6b6528639 100644 --- a/src/Algorand/Address.cpp +++ b/src/Algorand/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include "../Base32.h" diff --git a/src/Algorand/Address.h b/src/Algorand/Address.h index 50a700f6f43..5215b7bb6b7 100644 --- a/src/Algorand/Address.h +++ b/src/Algorand/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Algorand/AssetTransfer.cpp b/src/Algorand/AssetTransfer.cpp index 5e81bef1168..f8644988cbc 100644 --- a/src/Algorand/AssetTransfer.cpp +++ b/src/Algorand/AssetTransfer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "AssetTransfer.h" #include "BinaryCoding.h" diff --git a/src/Algorand/AssetTransfer.h b/src/Algorand/AssetTransfer.h index d3ade766bc8..3de0f22bb82 100644 --- a/src/Algorand/AssetTransfer.h +++ b/src/Algorand/AssetTransfer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Algorand/BaseTransaction.h b/src/Algorand/BaseTransaction.h index f11b2457f90..4938e43bb2a 100644 --- a/src/Algorand/BaseTransaction.h +++ b/src/Algorand/BaseTransaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Algorand/BinaryCoding.h b/src/Algorand/BinaryCoding.h index 3507124c7ea..2b7ca30fd22 100644 --- a/src/Algorand/BinaryCoding.h +++ b/src/Algorand/BinaryCoding.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Algorand/Entry.cpp b/src/Algorand/Entry.cpp index a9afe1a8975..14a1320fede 100644 --- a/src/Algorand/Entry.cpp +++ b/src/Algorand/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Algorand/Entry.h b/src/Algorand/Entry.h index 1d54006d86d..cc47a32118e 100644 --- a/src/Algorand/Entry.h +++ b/src/Algorand/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Algorand/OptInAssetTransaction.cpp b/src/Algorand/OptInAssetTransaction.cpp index d64367186b4..7246c4e8c54 100644 --- a/src/Algorand/OptInAssetTransaction.cpp +++ b/src/Algorand/OptInAssetTransaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "OptInAssetTransaction.h" #include "BinaryCoding.h" diff --git a/src/Algorand/OptInAssetTransaction.h b/src/Algorand/OptInAssetTransaction.h index 57d368719af..ca98873fa71 100644 --- a/src/Algorand/OptInAssetTransaction.h +++ b/src/Algorand/OptInAssetTransaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Algorand/Signer.cpp b/src/Algorand/Signer.cpp index e849e18df7d..71f44b6fcce 100644 --- a/src/Algorand/Signer.cpp +++ b/src/Algorand/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Address.h" diff --git a/src/Algorand/Signer.h b/src/Algorand/Signer.h index ba41661170f..2c0514328c7 100644 --- a/src/Algorand/Signer.h +++ b/src/Algorand/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Algorand/Transfer.cpp b/src/Algorand/Transfer.cpp index 05ba80e6c0b..645463679c0 100644 --- a/src/Algorand/Transfer.cpp +++ b/src/Algorand/Transfer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transfer.h" #include "BinaryCoding.h" diff --git a/src/Algorand/Transfer.h b/src/Algorand/Transfer.h index e7bf48ade79..8ca7bbcad59 100644 --- a/src/Algorand/Transfer.h +++ b/src/Algorand/Transfer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/AnyAddress.cpp b/src/AnyAddress.cpp index 7d3c3d303c3..ab4267ac388 100644 --- a/src/AnyAddress.cpp +++ b/src/AnyAddress.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "AnyAddress.h" diff --git a/src/AnyAddress.h b/src/AnyAddress.h index 6ae0b432d9b..f3e7434272a 100644 --- a/src/AnyAddress.h +++ b/src/AnyAddress.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Aptos/Entry.h b/src/Aptos/Entry.h index 750c5dba2a9..24ab3d38afe 100644 --- a/src/Aptos/Entry.h +++ b/src/Aptos/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/AsnParser.h b/src/AsnParser.h index 008b6369b58..a6d7d7e8443 100644 --- a/src/AsnParser.h +++ b/src/AsnParser.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Base32.h b/src/Base32.h index 527810c6552..595b36591a8 100644 --- a/src/Base32.h +++ b/src/Base32.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Base58.h b/src/Base58.h index 139c3703aee..5b36feba2e9 100644 --- a/src/Base58.h +++ b/src/Base58.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Base58Address.h b/src/Base58Address.h index baba6324910..1d2092c495e 100644 --- a/src/Base58Address.h +++ b/src/Base58Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Base64.cpp b/src/Base64.cpp index 0a3cea2612d..dfcfa1353aa 100644 --- a/src/Base64.cpp +++ b/src/Base64.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base64.h" #include "rust/bindgen/WalletCoreRSBindgen.h" diff --git a/src/Base64.h b/src/Base64.h index 3f54148939d..a930522886e 100644 --- a/src/Base64.h +++ b/src/Base64.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bech32.cpp b/src/Bech32.cpp index 5cb14b8cbe1..d6bd309a1de 100644 --- a/src/Bech32.cpp +++ b/src/Bech32.cpp @@ -1,9 +1,7 @@ // Copyright © 2017 Pieter Wuille -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bech32.h" #include "Data.h" diff --git a/src/Bech32.h b/src/Bech32.h index fe4d031713c..cb0a4bc7b9d 100644 --- a/src/Bech32.h +++ b/src/Bech32.h @@ -1,9 +1,7 @@ // Copyright © 2017 Pieter Wuille -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bech32Address.cpp b/src/Bech32Address.cpp index efca8a2f5d3..8394773b635 100644 --- a/src/Bech32Address.cpp +++ b/src/Bech32Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bech32Address.h" #include "Bech32.h" diff --git a/src/Bech32Address.h b/src/Bech32Address.h index 42356384fc3..b7a1707b34d 100644 --- a/src/Bech32Address.h +++ b/src/Bech32Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Binance/Address.cpp b/src/Binance/Address.cpp index 00d8ab95dea..0ab1d1a77c0 100644 --- a/src/Binance/Address.cpp +++ b/src/Binance/Address.cpp @@ -1,9 +1,7 @@ // Copyright © 2017 Pieter Wuille -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include "Coin.h" diff --git a/src/Binance/Address.h b/src/Binance/Address.h index ac2a0616e90..227f3088727 100644 --- a/src/Binance/Address.h +++ b/src/Binance/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Binance/Entry.cpp b/src/Binance/Entry.cpp index 3d65666ba7f..c1915455dec 100644 --- a/src/Binance/Entry.cpp +++ b/src/Binance/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Binance/Entry.h b/src/Binance/Entry.h index 579bdba74be..dab9768dff7 100644 --- a/src/Binance/Entry.h +++ b/src/Binance/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/BinaryCoding.cpp b/src/BinaryCoding.cpp index f8d5dcdfb57..210101accbe 100644 --- a/src/BinaryCoding.cpp +++ b/src/BinaryCoding.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "BinaryCoding.h" diff --git a/src/BinaryCoding.h b/src/BinaryCoding.h index 661fddff81b..45050e315d5 100644 --- a/src/BinaryCoding.h +++ b/src/BinaryCoding.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/Address.h b/src/Bitcoin/Address.h index 75d3de2ab94..50aab3e10af 100644 --- a/src/Bitcoin/Address.h +++ b/src/Bitcoin/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/Amount.h b/src/Bitcoin/Amount.h index 1cb6065a7ee..7df4f56adcd 100644 --- a/src/Bitcoin/Amount.h +++ b/src/Bitcoin/Amount.h @@ -1,10 +1,8 @@ // Copyright © 2009-2010 Satoshi Nakamoto // Copyright © 2009-2016 The Bitcoin Core developers -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/CashAddress.cpp b/src/Bitcoin/CashAddress.cpp index c3a07428716..1f98d5fb48c 100644 --- a/src/Bitcoin/CashAddress.cpp +++ b/src/Bitcoin/CashAddress.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "CashAddress.h" #include "../Coin.h" diff --git a/src/Bitcoin/CashAddress.h b/src/Bitcoin/CashAddress.h index 194c874a2b9..58b1a22d5b6 100644 --- a/src/Bitcoin/CashAddress.h +++ b/src/Bitcoin/CashAddress.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/Entry.cpp b/src/Bitcoin/Entry.cpp index 27473c893fc..e599f93ffef 100644 --- a/src/Bitcoin/Entry.cpp +++ b/src/Bitcoin/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Bitcoin/Entry.h b/src/Bitcoin/Entry.h index 7bcfd19857f..9d2c8503097 100644 --- a/src/Bitcoin/Entry.h +++ b/src/Bitcoin/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/FeeCalculator.cpp b/src/Bitcoin/FeeCalculator.cpp index bc8b023131d..3294bfb64d5 100644 --- a/src/Bitcoin/FeeCalculator.cpp +++ b/src/Bitcoin/FeeCalculator.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "FeeCalculator.h" diff --git a/src/Bitcoin/FeeCalculator.h b/src/Bitcoin/FeeCalculator.h index cbfa0d5d00d..32d0f083959 100644 --- a/src/Bitcoin/FeeCalculator.h +++ b/src/Bitcoin/FeeCalculator.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/InputSelector.cpp b/src/Bitcoin/InputSelector.cpp index 3fca632c81f..9b2bc7bb175 100644 --- a/src/Bitcoin/InputSelector.cpp +++ b/src/Bitcoin/InputSelector.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "InputSelector.h" diff --git a/src/Bitcoin/InputSelector.h b/src/Bitcoin/InputSelector.h index 001ed8f2213..e2941e812d1 100644 --- a/src/Bitcoin/InputSelector.h +++ b/src/Bitcoin/InputSelector.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/MessageSigner.cpp b/src/Bitcoin/MessageSigner.cpp index 155b947ca3c..af8a6565322 100644 --- a/src/Bitcoin/MessageSigner.cpp +++ b/src/Bitcoin/MessageSigner.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "MessageSigner.h" #include "Address.h" diff --git a/src/Bitcoin/MessageSigner.h b/src/Bitcoin/MessageSigner.h index 17c4c37fb0f..f81f7f9bdb9 100644 --- a/src/Bitcoin/MessageSigner.h +++ b/src/Bitcoin/MessageSigner.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/OpCodes.h b/src/Bitcoin/OpCodes.h index 592ca986e6f..c4d79a6e929 100644 --- a/src/Bitcoin/OpCodes.h +++ b/src/Bitcoin/OpCodes.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/OutPoint.cpp b/src/Bitcoin/OutPoint.cpp index 557a66c96cb..fd65e3a6a51 100644 --- a/src/Bitcoin/OutPoint.cpp +++ b/src/Bitcoin/OutPoint.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "OutPoint.h" diff --git a/src/Bitcoin/OutPoint.h b/src/Bitcoin/OutPoint.h index 81473b40d2f..bb7f9a951c1 100644 --- a/src/Bitcoin/OutPoint.h +++ b/src/Bitcoin/OutPoint.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/Script.cpp b/src/Bitcoin/Script.cpp index 86bde0a6669..dbeb9681bce 100644 --- a/src/Bitcoin/Script.cpp +++ b/src/Bitcoin/Script.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include "CashAddress.h" diff --git a/src/Bitcoin/Script.h b/src/Bitcoin/Script.h index 87a06790885..a1ec72c3019 100644 --- a/src/Bitcoin/Script.h +++ b/src/Bitcoin/Script.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/SegwitAddress.cpp b/src/Bitcoin/SegwitAddress.cpp index 29454b46b7c..70ec462d391 100644 --- a/src/Bitcoin/SegwitAddress.cpp +++ b/src/Bitcoin/SegwitAddress.cpp @@ -1,9 +1,7 @@ // Copyright © 2017 Pieter Wuille -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "SegwitAddress.h" #include "../Bech32.h" diff --git a/src/Bitcoin/SegwitAddress.h b/src/Bitcoin/SegwitAddress.h index 38f6a287fcb..8d65f7d83fc 100644 --- a/src/Bitcoin/SegwitAddress.h +++ b/src/Bitcoin/SegwitAddress.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/SigHashType.h b/src/Bitcoin/SigHashType.h index 4ff2221fd22..34d4ccc4a94 100644 --- a/src/Bitcoin/SigHashType.h +++ b/src/Bitcoin/SigHashType.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/SignatureBuilder.cpp b/src/Bitcoin/SignatureBuilder.cpp index 55ca7938c20..7dada6c3201 100644 --- a/src/Bitcoin/SignatureBuilder.cpp +++ b/src/Bitcoin/SignatureBuilder.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "SignatureBuilder.h" #include "SigHashType.h" diff --git a/src/Bitcoin/SignatureBuilder.h b/src/Bitcoin/SignatureBuilder.h index 4fd0286b0a2..4f6af0b2124 100644 --- a/src/Bitcoin/SignatureBuilder.h +++ b/src/Bitcoin/SignatureBuilder.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/SignatureVersion.h b/src/Bitcoin/SignatureVersion.h index 6e00aa9c96f..fc6fe2817cf 100644 --- a/src/Bitcoin/SignatureVersion.h +++ b/src/Bitcoin/SignatureVersion.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/Signer.cpp b/src/Bitcoin/Signer.cpp index 0781237f862..0e88b898e1f 100644 --- a/src/Bitcoin/Signer.cpp +++ b/src/Bitcoin/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Hash.h" diff --git a/src/Bitcoin/Signer.h b/src/Bitcoin/Signer.h index c881b821f4c..8956748bbd0 100644 --- a/src/Bitcoin/Signer.h +++ b/src/Bitcoin/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once #include "../proto/Bitcoin.pb.h" diff --git a/src/Bitcoin/SigningInput.cpp b/src/Bitcoin/SigningInput.cpp index 411ded7d20a..bcdf4e50f8f 100644 --- a/src/Bitcoin/SigningInput.cpp +++ b/src/Bitcoin/SigningInput.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "SigningInput.h" diff --git a/src/Bitcoin/SigningInput.h b/src/Bitcoin/SigningInput.h index c408c947ed5..7686df1d1bc 100644 --- a/src/Bitcoin/SigningInput.h +++ b/src/Bitcoin/SigningInput.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/Transaction.cpp b/src/Bitcoin/Transaction.cpp index 3a0cc8a4ac6..ed7a6d73b42 100644 --- a/src/Bitcoin/Transaction.cpp +++ b/src/Bitcoin/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" #include "SegwitAddress.h" diff --git a/src/Bitcoin/Transaction.h b/src/Bitcoin/Transaction.h index 730c06dfd4a..2f04a282526 100644 --- a/src/Bitcoin/Transaction.h +++ b/src/Bitcoin/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/TransactionBuilder.cpp b/src/Bitcoin/TransactionBuilder.cpp index a3774c1ba3f..b4f167d92d6 100644 --- a/src/Bitcoin/TransactionBuilder.cpp +++ b/src/Bitcoin/TransactionBuilder.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TransactionBuilder.h" #include "Script.h" diff --git a/src/Bitcoin/TransactionBuilder.h b/src/Bitcoin/TransactionBuilder.h index aa1c4db4737..06e876c2e22 100644 --- a/src/Bitcoin/TransactionBuilder.h +++ b/src/Bitcoin/TransactionBuilder.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/TransactionInput.cpp b/src/Bitcoin/TransactionInput.cpp index c4ecfce0e53..ee2db04a78d 100644 --- a/src/Bitcoin/TransactionInput.cpp +++ b/src/Bitcoin/TransactionInput.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TransactionInput.h" diff --git a/src/Bitcoin/TransactionInput.h b/src/Bitcoin/TransactionInput.h index 1b930354753..c95c89ab07c 100644 --- a/src/Bitcoin/TransactionInput.h +++ b/src/Bitcoin/TransactionInput.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/TransactionOutput.cpp b/src/Bitcoin/TransactionOutput.cpp index fb10d324a98..008cdb3ff26 100644 --- a/src/Bitcoin/TransactionOutput.cpp +++ b/src/Bitcoin/TransactionOutput.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TransactionOutput.h" diff --git a/src/Bitcoin/TransactionOutput.h b/src/Bitcoin/TransactionOutput.h index 10db28c12e4..4627f5dc899 100644 --- a/src/Bitcoin/TransactionOutput.h +++ b/src/Bitcoin/TransactionOutput.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/TransactionPlan.h b/src/Bitcoin/TransactionPlan.h index f41a93e19b5..ddfc2dc4810 100644 --- a/src/Bitcoin/TransactionPlan.h +++ b/src/Bitcoin/TransactionPlan.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/TransactionSigner.cpp b/src/Bitcoin/TransactionSigner.cpp index 94655ddbadf..282117df361 100644 --- a/src/Bitcoin/TransactionSigner.cpp +++ b/src/Bitcoin/TransactionSigner.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TransactionSigner.h" diff --git a/src/Bitcoin/TransactionSigner.h b/src/Bitcoin/TransactionSigner.h index 6af2e3100ec..8f67a93cc5c 100644 --- a/src/Bitcoin/TransactionSigner.h +++ b/src/Bitcoin/TransactionSigner.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Bitcoin/UTXO.h b/src/Bitcoin/UTXO.h index 239f55c0260..02fdf2dffee 100644 --- a/src/Bitcoin/UTXO.h +++ b/src/Bitcoin/UTXO.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/BitcoinDiamond/Entry.cpp b/src/BitcoinDiamond/Entry.cpp index 6463565d77e..b75a9ea6d1b 100644 --- a/src/BitcoinDiamond/Entry.cpp +++ b/src/BitcoinDiamond/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/BitcoinDiamond/Entry.h b/src/BitcoinDiamond/Entry.h index d3d4768c6ad..ef8002905e5 100644 --- a/src/BitcoinDiamond/Entry.h +++ b/src/BitcoinDiamond/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/BitcoinDiamond/Signer.cpp b/src/BitcoinDiamond/Signer.cpp index 75d43bd2c81..cb7c964e562 100644 --- a/src/BitcoinDiamond/Signer.cpp +++ b/src/BitcoinDiamond/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Bitcoin/TransactionSigner.h" diff --git a/src/BitcoinDiamond/Signer.h b/src/BitcoinDiamond/Signer.h index 3a835ca4b7a..b5639a1d30d 100644 --- a/src/BitcoinDiamond/Signer.h +++ b/src/BitcoinDiamond/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/BitcoinDiamond/Transaction.cpp b/src/BitcoinDiamond/Transaction.cpp index cf662a0ac2f..6d2135b6d27 100644 --- a/src/BitcoinDiamond/Transaction.cpp +++ b/src/BitcoinDiamond/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" #include "../Bitcoin/SigHashType.h" diff --git a/src/BitcoinDiamond/Transaction.h b/src/BitcoinDiamond/Transaction.h index a01950ca641..55f68ca1333 100644 --- a/src/BitcoinDiamond/Transaction.h +++ b/src/BitcoinDiamond/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/BitcoinDiamond/TransactionBuilder.h b/src/BitcoinDiamond/TransactionBuilder.h index 651e7e79262..7134ae515fd 100644 --- a/src/BitcoinDiamond/TransactionBuilder.h +++ b/src/BitcoinDiamond/TransactionBuilder.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Cardano/AddressV2.cpp b/src/Cardano/AddressV2.cpp index 856c4800635..695f9451788 100644 --- a/src/Cardano/AddressV2.cpp +++ b/src/Cardano/AddressV2.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "AddressV2.h" #include "../Base58.h" diff --git a/src/Cardano/AddressV2.h b/src/Cardano/AddressV2.h index 54705dfa739..742c3eeaba6 100644 --- a/src/Cardano/AddressV2.h +++ b/src/Cardano/AddressV2.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Cardano/AddressV3.cpp b/src/Cardano/AddressV3.cpp index f1f349a4f01..84e264256a6 100644 --- a/src/Cardano/AddressV3.cpp +++ b/src/Cardano/AddressV3.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "AddressV3.h" #include "AddressV2.h" diff --git a/src/Cardano/AddressV3.h b/src/Cardano/AddressV3.h index bee1396655b..199d9e03c9f 100644 --- a/src/Cardano/AddressV3.h +++ b/src/Cardano/AddressV3.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Cardano/Entry.cpp b/src/Cardano/Entry.cpp index 03b11551b51..688ac1ed355 100644 --- a/src/Cardano/Entry.cpp +++ b/src/Cardano/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Cardano/Entry.h b/src/Cardano/Entry.h index e81e85aa16e..ed73e24e5c7 100644 --- a/src/Cardano/Entry.h +++ b/src/Cardano/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Cardano/Signer.cpp b/src/Cardano/Signer.cpp index 5abfaf1dcdb..80336de5e94 100644 --- a/src/Cardano/Signer.cpp +++ b/src/Cardano/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "AddressV3.h" diff --git a/src/Cardano/Signer.h b/src/Cardano/Signer.h index 7cfc086aa9e..d6036e8d2d8 100644 --- a/src/Cardano/Signer.h +++ b/src/Cardano/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Cardano/Transaction.cpp b/src/Cardano/Transaction.cpp index dc04539f937..fa8fd157d53 100644 --- a/src/Cardano/Transaction.cpp +++ b/src/Cardano/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" #include "AddressV3.h" diff --git a/src/Cardano/Transaction.h b/src/Cardano/Transaction.h index dd34c84ad23..afdd47b4cb4 100644 --- a/src/Cardano/Transaction.h +++ b/src/Cardano/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Cbor.cpp b/src/Cbor.cpp index 6037f40d0bc..dd5f3ed06c2 100644 --- a/src/Cbor.cpp +++ b/src/Cbor.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Cbor.h" #include "HexCoding.h" diff --git a/src/Cbor.h b/src/Cbor.h index 8c85332dc7f..43f245611bb 100644 --- a/src/Cbor.h +++ b/src/Cbor.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Coin.cpp b/src/Coin.cpp index a9ccd452233..12b7f596563 100644 --- a/src/Coin.cpp +++ b/src/Coin.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" diff --git a/src/Coin.h b/src/Coin.h index 63ac9f07fbe..35ad5bc1d59 100644 --- a/src/Coin.h +++ b/src/Coin.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/CoinEntry.cpp b/src/CoinEntry.cpp index f8ce744377b..c1748f7ab8d 100644 --- a/src/CoinEntry.cpp +++ b/src/CoinEntry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "CoinEntry.h" #include "Coin.h" diff --git a/src/CoinEntry.h b/src/CoinEntry.h index 2581fe17e7b..d9a450a52d6 100644 --- a/src/CoinEntry.h +++ b/src/CoinEntry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Cosmos/Address.h b/src/Cosmos/Address.h index 67b58baf4a8..ed4c7bf61eb 100644 --- a/src/Cosmos/Address.h +++ b/src/Cosmos/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Cosmos/Entry.cpp b/src/Cosmos/Entry.cpp index 2d64e48b4ea..69a4b4c2707 100644 --- a/src/Cosmos/Entry.cpp +++ b/src/Cosmos/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" #include "Address.h" diff --git a/src/Cosmos/Entry.h b/src/Cosmos/Entry.h index 26cbc60fde8..77d8efec01e 100644 --- a/src/Cosmos/Entry.h +++ b/src/Cosmos/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Crc.cpp b/src/Crc.cpp index 7b37ec2a37e..38fc2c84d9c 100644 --- a/src/Crc.cpp +++ b/src/Crc.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Crc.h" diff --git a/src/Crc.h b/src/Crc.h index 4c8adaa4a26..5268f186d13 100644 --- a/src/Crc.h +++ b/src/Crc.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Data.cpp b/src/Data.cpp index 6495add1db2..67b2427b6b8 100644 --- a/src/Data.cpp +++ b/src/Data.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Data.h" diff --git a/src/Data.h b/src/Data.h index 697037ef78f..0d853173f32 100644 --- a/src/Data.h +++ b/src/Data.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Decred/Address.cpp b/src/Decred/Address.cpp index 4f04ef1e0f8..b330145ca69 100644 --- a/src/Decred/Address.cpp +++ b/src/Decred/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" diff --git a/src/Decred/Address.h b/src/Decred/Address.h index e9fa27609cd..4403d7da12a 100644 --- a/src/Decred/Address.h +++ b/src/Decred/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Decred/Entry.cpp b/src/Decred/Entry.cpp index 3f7a6c9f1d0..4e6c17f60a6 100644 --- a/src/Decred/Entry.cpp +++ b/src/Decred/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Decred/Entry.h b/src/Decred/Entry.h index 322e5d091c8..9f207c67c63 100644 --- a/src/Decred/Entry.h +++ b/src/Decred/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Decred/OutPoint.cpp b/src/Decred/OutPoint.cpp index 08830d037aa..88a74321101 100644 --- a/src/Decred/OutPoint.cpp +++ b/src/Decred/OutPoint.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "OutPoint.h" diff --git a/src/Decred/OutPoint.h b/src/Decred/OutPoint.h index a75ca419b7e..8ebd58a643b 100644 --- a/src/Decred/OutPoint.h +++ b/src/Decred/OutPoint.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Decred/Signer.cpp b/src/Decred/Signer.cpp index c208bb3f04d..f968cfe0239 100644 --- a/src/Decred/Signer.cpp +++ b/src/Decred/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" diff --git a/src/Decred/Signer.h b/src/Decred/Signer.h index 665cccb9076..9208c744ee3 100644 --- a/src/Decred/Signer.h +++ b/src/Decred/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Decred/Transaction.cpp b/src/Decred/Transaction.cpp index df48ffe12d1..d04950afbcd 100644 --- a/src/Decred/Transaction.cpp +++ b/src/Decred/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" diff --git a/src/Decred/Transaction.h b/src/Decred/Transaction.h index f77f1bfc580..eb2cb9983e2 100644 --- a/src/Decred/Transaction.h +++ b/src/Decred/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Decred/TransactionBuilder.h b/src/Decred/TransactionBuilder.h index 0fafce4195d..35dd958dc5b 100644 --- a/src/Decred/TransactionBuilder.h +++ b/src/Decred/TransactionBuilder.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Decred/TransactionInput.cpp b/src/Decred/TransactionInput.cpp index d948646900a..8a9c0bca09e 100644 --- a/src/Decred/TransactionInput.cpp +++ b/src/Decred/TransactionInput.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TransactionInput.h" diff --git a/src/Decred/TransactionInput.h b/src/Decred/TransactionInput.h index 9c600f1a8ad..0b916106367 100644 --- a/src/Decred/TransactionInput.h +++ b/src/Decred/TransactionInput.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Decred/TransactionOutput.cpp b/src/Decred/TransactionOutput.cpp index 8cad8d9fa49..33aced75827 100644 --- a/src/Decred/TransactionOutput.cpp +++ b/src/Decred/TransactionOutput.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TransactionOutput.h" diff --git a/src/Decred/TransactionOutput.h b/src/Decred/TransactionOutput.h index 65a51056cb2..4a2978e8c64 100644 --- a/src/Decred/TransactionOutput.h +++ b/src/Decred/TransactionOutput.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Defer.h b/src/Defer.h index 93a0c726828..d3d3665f636 100644 --- a/src/Defer.h +++ b/src/Defer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/DerivationPath.cpp b/src/DerivationPath.cpp index 3705c40d543..dd54e28efbc 100644 --- a/src/DerivationPath.cpp +++ b/src/DerivationPath.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "DerivationPath.h" diff --git a/src/DerivationPath.h b/src/DerivationPath.h index 4a2b26a8190..cf3f1f7ba7b 100644 --- a/src/DerivationPath.h +++ b/src/DerivationPath.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/EOS/Action.cpp b/src/EOS/Action.cpp index 2df8429cfa9..40fa3c7798c 100644 --- a/src/EOS/Action.cpp +++ b/src/EOS/Action.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Action.h" #include "../HexCoding.h" diff --git a/src/EOS/Action.h b/src/EOS/Action.h index 389f8c53d9f..26006cc3efa 100644 --- a/src/EOS/Action.h +++ b/src/EOS/Action.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/EOS/Address.cpp b/src/EOS/Address.cpp index 65707e7038b..72e6d52d2ed 100644 --- a/src/EOS/Address.cpp +++ b/src/EOS/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include "../Base58.h" diff --git a/src/EOS/Address.h b/src/EOS/Address.h index 80b783deccb..9edbc41c816 100644 --- a/src/EOS/Address.h +++ b/src/EOS/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/EOS/Asset.cpp b/src/EOS/Asset.cpp index 570089cc6bd..86a53e38a89 100644 --- a/src/EOS/Asset.cpp +++ b/src/EOS/Asset.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Asset.h" diff --git a/src/EOS/Asset.h b/src/EOS/Asset.h index 72a4bdffc73..5f91f3e6e23 100644 --- a/src/EOS/Asset.h +++ b/src/EOS/Asset.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/EOS/Entry.cpp b/src/EOS/Entry.cpp index 7b3f92a9da3..23611107634 100644 --- a/src/EOS/Entry.cpp +++ b/src/EOS/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" #include "../proto/EOS.pb.h" diff --git a/src/EOS/Entry.h b/src/EOS/Entry.h index f1ea5b07f8a..dedf0d4c76d 100644 --- a/src/EOS/Entry.h +++ b/src/EOS/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/EOS/KeyType.h b/src/EOS/KeyType.h index ac207f048ba..c21d82eb00b 100644 --- a/src/EOS/KeyType.h +++ b/src/EOS/KeyType.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/EOS/Name.cpp b/src/EOS/Name.cpp index b386368a765..9825a0f5b5b 100644 --- a/src/EOS/Name.cpp +++ b/src/EOS/Name.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../BinaryCoding.h" #include "Name.h" diff --git a/src/EOS/Name.h b/src/EOS/Name.h index 3fe613d1089..5280975535f 100644 --- a/src/EOS/Name.h +++ b/src/EOS/Name.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/EOS/PackedTransaction.cpp b/src/EOS/PackedTransaction.cpp index 1e354f6c3f4..4c884f8c815 100644 --- a/src/EOS/PackedTransaction.cpp +++ b/src/EOS/PackedTransaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "PackedTransaction.h" diff --git a/src/EOS/PackedTransaction.h b/src/EOS/PackedTransaction.h index 6c3e041db29..e88a5a97703 100644 --- a/src/EOS/PackedTransaction.h +++ b/src/EOS/PackedTransaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/EOS/Prefixes.h b/src/EOS/Prefixes.h index 0a01179da5e..d3e2335d7f5 100644 --- a/src/EOS/Prefixes.h +++ b/src/EOS/Prefixes.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/EOS/Signer.cpp b/src/EOS/Signer.cpp index 00b79865783..dc8019fbeed 100644 --- a/src/EOS/Signer.cpp +++ b/src/EOS/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Asset.h" diff --git a/src/EOS/Signer.h b/src/EOS/Signer.h index fb211b7ebba..0eef641f9a2 100644 --- a/src/EOS/Signer.h +++ b/src/EOS/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/EOS/Transaction.cpp b/src/EOS/Transaction.cpp index 2078529cc20..f5ae6608f46 100644 --- a/src/EOS/Transaction.cpp +++ b/src/EOS/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../Base58.h" #include "../HexCoding.h" diff --git a/src/EOS/Transaction.h b/src/EOS/Transaction.h index 70bd1154f3e..420f6e7bf73 100644 --- a/src/EOS/Transaction.h +++ b/src/EOS/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Encrypt.cpp b/src/Encrypt.cpp index d799d6006a1..a3a3c8a78a0 100644 --- a/src/Encrypt.cpp +++ b/src/Encrypt.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Encrypt.h" #include "Data.h" diff --git a/src/Encrypt.h b/src/Encrypt.h index 8870028f2f6..2cada9f9cc3 100644 --- a/src/Encrypt.h +++ b/src/Encrypt.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ethereum/ABI/Function.cpp b/src/Ethereum/ABI/Function.cpp index ffe84e940f4..be1007516fe 100644 --- a/src/Ethereum/ABI/Function.cpp +++ b/src/Ethereum/ABI/Function.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Function.h" diff --git a/src/Ethereum/ABI/Function.h b/src/Ethereum/ABI/Function.h index fbed1fcbb4d..2bd474fadc0 100644 --- a/src/Ethereum/ABI/Function.h +++ b/src/Ethereum/ABI/Function.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ethereum/ABI/ProtoParam.h b/src/Ethereum/ABI/ProtoParam.h index 30acb10abc4..c9d6c2f1c80 100644 --- a/src/Ethereum/ABI/ProtoParam.h +++ b/src/Ethereum/ABI/ProtoParam.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ethereum/ABI/ValueDecoder.cpp b/src/Ethereum/ABI/ValueDecoder.cpp index ac46303ce05..425f2ce7918 100644 --- a/src/Ethereum/ABI/ValueDecoder.cpp +++ b/src/Ethereum/ABI/ValueDecoder.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "ValueDecoder.h" #include "proto/EthereumAbi.pb.h" diff --git a/src/Ethereum/ABI/ValueDecoder.h b/src/Ethereum/ABI/ValueDecoder.h index 049bda9231a..02edf2a254c 100644 --- a/src/Ethereum/ABI/ValueDecoder.h +++ b/src/Ethereum/ABI/ValueDecoder.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ethereum/ABI/ValueEncoder.cpp b/src/Ethereum/ABI/ValueEncoder.cpp index fc19239d3e3..c4db5470c49 100644 --- a/src/Ethereum/ABI/ValueEncoder.cpp +++ b/src/Ethereum/ABI/ValueEncoder.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "ValueEncoder.h" diff --git a/src/Ethereum/ABI/ValueEncoder.h b/src/Ethereum/ABI/ValueEncoder.h index 9157003edcd..178ae693f43 100644 --- a/src/Ethereum/ABI/ValueEncoder.h +++ b/src/Ethereum/ABI/ValueEncoder.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ethereum/Address.cpp b/src/Ethereum/Address.cpp index 66255adbdf5..47a804ea0b5 100644 --- a/src/Ethereum/Address.cpp +++ b/src/Ethereum/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include "AddressChecksum.h" diff --git a/src/Ethereum/Address.h b/src/Ethereum/Address.h index df1e77dd224..afac9e1a4de 100644 --- a/src/Ethereum/Address.h +++ b/src/Ethereum/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ethereum/AddressChecksum.cpp b/src/Ethereum/AddressChecksum.cpp index 53a9f229975..e927c272633 100644 --- a/src/Ethereum/AddressChecksum.cpp +++ b/src/Ethereum/AddressChecksum.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "AddressChecksum.h" diff --git a/src/Ethereum/AddressChecksum.h b/src/Ethereum/AddressChecksum.h index 52b6f864784..4d2b78c96c1 100644 --- a/src/Ethereum/AddressChecksum.h +++ b/src/Ethereum/AddressChecksum.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ethereum/Barz.cpp b/src/Ethereum/Barz.cpp index d3fb86ba45a..2cd9a0da071 100644 --- a/src/Ethereum/Barz.cpp +++ b/src/Ethereum/Barz.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "ABI/Function.h" #include "AddressChecksum.h" diff --git a/src/Ethereum/Barz.h b/src/Ethereum/Barz.h index cc6fa74998b..9ea1d0c0ccd 100644 --- a/src/Ethereum/Barz.h +++ b/src/Ethereum/Barz.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ethereum/ContractCall.cpp b/src/Ethereum/ContractCall.cpp index d8d996c2deb..727a21d192b 100644 --- a/src/Ethereum/ContractCall.cpp +++ b/src/Ethereum/ContractCall.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "ContractCall.h" #include "HexCoding.h" diff --git a/src/Ethereum/ContractCall.h b/src/Ethereum/ContractCall.h index 0060abfddba..abbb0a0fce5 100644 --- a/src/Ethereum/ContractCall.h +++ b/src/Ethereum/ContractCall.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ethereum/EIP1014.cpp b/src/Ethereum/EIP1014.cpp index dfc3be3efae..7a72b3e0e51 100644 --- a/src/Ethereum/EIP1014.cpp +++ b/src/Ethereum/EIP1014.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "EIP1014.h" #include "AddressChecksum.h" diff --git a/src/Ethereum/EIP1014.h b/src/Ethereum/EIP1014.h index 3356fe926e8..7471a9052e8 100644 --- a/src/Ethereum/EIP1014.h +++ b/src/Ethereum/EIP1014.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ethereum/EIP1967.cpp b/src/Ethereum/EIP1967.cpp index 58106977ebe..4fe8d527b08 100644 --- a/src/Ethereum/EIP1967.cpp +++ b/src/Ethereum/EIP1967.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "EIP1014.h" #include "ABI/Function.h" diff --git a/src/Ethereum/EIP1967.h b/src/Ethereum/EIP1967.h index b9b65210f97..04e37fffdf8 100644 --- a/src/Ethereum/EIP1967.h +++ b/src/Ethereum/EIP1967.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ethereum/EIP2645.cpp b/src/Ethereum/EIP2645.cpp index ff887abdab2..c9ae7aa9bde 100644 --- a/src/Ethereum/EIP2645.cpp +++ b/src/Ethereum/EIP2645.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/Ethereum/EIP2645.h b/src/Ethereum/EIP2645.h index 3ab0112f77a..e5d51a85f24 100644 --- a/src/Ethereum/EIP2645.h +++ b/src/Ethereum/EIP2645.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ethereum/Entry.cpp b/src/Ethereum/Entry.cpp index 49d43c73ba7..6560b3969f7 100644 --- a/src/Ethereum/Entry.cpp +++ b/src/Ethereum/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Ethereum/Entry.h b/src/Ethereum/Entry.h index 94b93c57792..69d67808596 100644 --- a/src/Ethereum/Entry.h +++ b/src/Ethereum/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ethereum/MessageSigner.cpp b/src/Ethereum/MessageSigner.cpp index a24ee7a81c5..a50863c9d15 100644 --- a/src/Ethereum/MessageSigner.cpp +++ b/src/Ethereum/MessageSigner.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "MessageSigner.h" #include diff --git a/src/Ethereum/MessageSigner.h b/src/Ethereum/MessageSigner.h index 5aeac0e05e4..fc45e49c152 100644 --- a/src/Ethereum/MessageSigner.h +++ b/src/Ethereum/MessageSigner.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ethereum/RLP.cpp b/src/Ethereum/RLP.cpp index 486162a58de..8a543c1e8de 100644 --- a/src/Ethereum/RLP.cpp +++ b/src/Ethereum/RLP.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "RLP.h" diff --git a/src/Ethereum/RLP.h b/src/Ethereum/RLP.h index 57c0ef18113..5163d339514 100644 --- a/src/Ethereum/RLP.h +++ b/src/Ethereum/RLP.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Everscale/Address.cpp b/src/Everscale/Address.cpp index 473bf189aca..dee503e837c 100644 --- a/src/Everscale/Address.cpp +++ b/src/Everscale/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/Everscale/Address.h b/src/Everscale/Address.h index fd3a6c05046..ff114a8ce78 100644 --- a/src/Everscale/Address.h +++ b/src/Everscale/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Everscale/CommonTON/Cell.cpp b/src/Everscale/CommonTON/Cell.cpp index 38ca0c4e92e..b148168fa00 100644 --- a/src/Everscale/CommonTON/Cell.cpp +++ b/src/Everscale/CommonTON/Cell.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Cell.h" diff --git a/src/Everscale/CommonTON/Cell.h b/src/Everscale/CommonTON/Cell.h index 892e8dfb19b..90a024d0eb0 100644 --- a/src/Everscale/CommonTON/Cell.h +++ b/src/Everscale/CommonTON/Cell.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Everscale/CommonTON/CellBuilder.cpp b/src/Everscale/CommonTON/CellBuilder.cpp index e9b047280b9..b03a7891067 100644 --- a/src/Everscale/CommonTON/CellBuilder.cpp +++ b/src/Everscale/CommonTON/CellBuilder.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "CellBuilder.h" #include "Cell.h" diff --git a/src/Everscale/CommonTON/CellBuilder.h b/src/Everscale/CommonTON/CellBuilder.h index 36efae364d9..1a22e9bc65e 100644 --- a/src/Everscale/CommonTON/CellBuilder.h +++ b/src/Everscale/CommonTON/CellBuilder.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Everscale/CommonTON/CellSlice.cpp b/src/Everscale/CommonTON/CellSlice.cpp index ecdf22dbf6f..08089d155d4 100644 --- a/src/Everscale/CommonTON/CellSlice.cpp +++ b/src/Everscale/CommonTON/CellSlice.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "CellSlice.h" diff --git a/src/Everscale/CommonTON/CellSlice.h b/src/Everscale/CommonTON/CellSlice.h index 1409c8a6960..be98c2f185a 100644 --- a/src/Everscale/CommonTON/CellSlice.h +++ b/src/Everscale/CommonTON/CellSlice.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Everscale/CommonTON/Messages.h b/src/Everscale/CommonTON/Messages.h index 32cbbb7eaaa..4103a2f2feb 100644 --- a/src/Everscale/CommonTON/Messages.h +++ b/src/Everscale/CommonTON/Messages.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Everscale/CommonTON/RawAddress.cpp b/src/Everscale/CommonTON/RawAddress.cpp index 40d24e1d210..5f01bf7906c 100644 --- a/src/Everscale/CommonTON/RawAddress.cpp +++ b/src/Everscale/CommonTON/RawAddress.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/Everscale/CommonTON/RawAddress.h b/src/Everscale/CommonTON/RawAddress.h index 868e19c599d..f628aefb018 100644 --- a/src/Everscale/CommonTON/RawAddress.h +++ b/src/Everscale/CommonTON/RawAddress.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Everscale/CommonTON/WorkchainType.h b/src/Everscale/CommonTON/WorkchainType.h index 1fcf9aae19b..2a50e086ede 100644 --- a/src/Everscale/CommonTON/WorkchainType.h +++ b/src/Everscale/CommonTON/WorkchainType.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Everscale/Entry.cpp b/src/Everscale/Entry.cpp index 8f741bb37bb..d0291c7826d 100644 --- a/src/Everscale/Entry.cpp +++ b/src/Everscale/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Everscale/Entry.h b/src/Everscale/Entry.h index 3cd52dbb5c1..00bbd1f1b23 100644 --- a/src/Everscale/Entry.h +++ b/src/Everscale/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Everscale/Messages.cpp b/src/Everscale/Messages.cpp index 5dab778f085..47d2b4d72a2 100644 --- a/src/Everscale/Messages.cpp +++ b/src/Everscale/Messages.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Messages.h" diff --git a/src/Everscale/Messages.h b/src/Everscale/Messages.h index 09a5a624337..bd32b7f1286 100644 --- a/src/Everscale/Messages.h +++ b/src/Everscale/Messages.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Everscale/Signer.cpp b/src/Everscale/Signer.cpp index a648bee7ac9..33e5ee368b0 100644 --- a/src/Everscale/Signer.cpp +++ b/src/Everscale/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Address.h" diff --git a/src/Everscale/Signer.h b/src/Everscale/Signer.h index 6a1fe694609..4e14e19e8e0 100644 --- a/src/Everscale/Signer.h +++ b/src/Everscale/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Everscale/Wallet.cpp b/src/Everscale/Wallet.cpp index 321494f94ed..73fa3c39885 100644 --- a/src/Everscale/Wallet.cpp +++ b/src/Everscale/Wallet.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Wallet.h" #include "Messages.h" diff --git a/src/Everscale/Wallet.h b/src/Everscale/Wallet.h index 31060b55737..a32a8fb5c1d 100644 --- a/src/Everscale/Wallet.h +++ b/src/Everscale/Wallet.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Everscale/WorkchainType.h b/src/Everscale/WorkchainType.h index e5517b9636f..10a6cab39a3 100644 --- a/src/Everscale/WorkchainType.h +++ b/src/Everscale/WorkchainType.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/FIO/Action.cpp b/src/FIO/Action.cpp index 61cc235991f..15b0b21906b 100644 --- a/src/FIO/Action.cpp +++ b/src/FIO/Action.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Action.h" #include "Data.h" diff --git a/src/FIO/Action.h b/src/FIO/Action.h index 59b2b923510..7e9fd4e56ea 100644 --- a/src/FIO/Action.h +++ b/src/FIO/Action.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/FIO/Actor.cpp b/src/FIO/Actor.cpp index f151202cf76..5463da1cda1 100644 --- a/src/FIO/Actor.cpp +++ b/src/FIO/Actor.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Actor.h" diff --git a/src/FIO/Actor.h b/src/FIO/Actor.h index c3407db7888..dd5157890c0 100644 --- a/src/FIO/Actor.h +++ b/src/FIO/Actor.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/FIO/Address.cpp b/src/FIO/Address.cpp index 196d9843bc4..abfc1046846 100644 --- a/src/FIO/Address.cpp +++ b/src/FIO/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include "../Base58.h" diff --git a/src/FIO/Address.h b/src/FIO/Address.h index 3510543c826..049a72d2b9e 100644 --- a/src/FIO/Address.h +++ b/src/FIO/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/FIO/Encryption.cpp b/src/FIO/Encryption.cpp index 45b3cb3c0c6..ee2c2b54c6c 100644 --- a/src/FIO/Encryption.cpp +++ b/src/FIO/Encryption.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Encryption.h" diff --git a/src/FIO/Encryption.h b/src/FIO/Encryption.h index c2ccf72050a..431a685fdd3 100644 --- a/src/FIO/Encryption.h +++ b/src/FIO/Encryption.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/FIO/Entry.cpp b/src/FIO/Entry.cpp index dfb96243b6d..5cc91faefba 100644 --- a/src/FIO/Entry.cpp +++ b/src/FIO/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/FIO/Entry.h b/src/FIO/Entry.h index 2f20456b748..ccfbd5b4c91 100644 --- a/src/FIO/Entry.h +++ b/src/FIO/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/FIO/NewFundsRequest.cpp b/src/FIO/NewFundsRequest.cpp index 4a3c96835b3..3c71dc3d5a8 100644 --- a/src/FIO/NewFundsRequest.cpp +++ b/src/FIO/NewFundsRequest.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "NewFundsRequest.h" #include "../BinaryCoding.h" diff --git a/src/FIO/NewFundsRequest.h b/src/FIO/NewFundsRequest.h index 1ba41b803a1..822f38a0656 100644 --- a/src/FIO/NewFundsRequest.h +++ b/src/FIO/NewFundsRequest.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/FIO/Signer.cpp b/src/FIO/Signer.cpp index defc5acea72..d4e5361c318 100644 --- a/src/FIO/Signer.cpp +++ b/src/FIO/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Address.h" diff --git a/src/FIO/Signer.h b/src/FIO/Signer.h index 763417418c7..72b3d7b7098 100644 --- a/src/FIO/Signer.h +++ b/src/FIO/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/FIO/Transaction.cpp b/src/FIO/Transaction.cpp index b38a9135d2e..d0d7bb31340 100644 --- a/src/FIO/Transaction.cpp +++ b/src/FIO/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" #include "TransactionBuilder.h" diff --git a/src/FIO/Transaction.h b/src/FIO/Transaction.h index 5167155192f..0204b2d0acf 100644 --- a/src/FIO/Transaction.h +++ b/src/FIO/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/FIO/TransactionBuilder.cpp b/src/FIO/TransactionBuilder.cpp index 6b2c5f06242..e9881d1eb71 100644 --- a/src/FIO/TransactionBuilder.cpp +++ b/src/FIO/TransactionBuilder.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TransactionBuilder.h" diff --git a/src/FIO/TransactionBuilder.h b/src/FIO/TransactionBuilder.h index ce2711d1aab..8bd7061db72 100644 --- a/src/FIO/TransactionBuilder.h +++ b/src/FIO/TransactionBuilder.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Filecoin/Address.cpp b/src/Filecoin/Address.cpp index a5d1b476343..07dc7d7577c 100644 --- a/src/Filecoin/Address.cpp +++ b/src/Filecoin/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" diff --git a/src/Filecoin/Address.h b/src/Filecoin/Address.h index ff22fad7c3f..ea5cc9cf190 100644 --- a/src/Filecoin/Address.h +++ b/src/Filecoin/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Filecoin/AddressConverter.cpp b/src/Filecoin/AddressConverter.cpp index a3400c68f28..b805bb6d3f0 100644 --- a/src/Filecoin/AddressConverter.cpp +++ b/src/Filecoin/AddressConverter.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "AddressConverter.h" diff --git a/src/Filecoin/AddressConverter.h b/src/Filecoin/AddressConverter.h index 450e847cb00..2d64406baa4 100644 --- a/src/Filecoin/AddressConverter.h +++ b/src/Filecoin/AddressConverter.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Filecoin/Entry.cpp b/src/Filecoin/Entry.cpp index fb4737b3541..7e28c1fb901 100644 --- a/src/Filecoin/Entry.cpp +++ b/src/Filecoin/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Filecoin/Entry.h b/src/Filecoin/Entry.h index 0be5a4c4d1a..cfd0977881b 100644 --- a/src/Filecoin/Entry.h +++ b/src/Filecoin/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Filecoin/Signer.cpp b/src/Filecoin/Signer.cpp index c2f2ceafa3e..d0d72f4dd2c 100644 --- a/src/Filecoin/Signer.cpp +++ b/src/Filecoin/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/Filecoin/Signer.h b/src/Filecoin/Signer.h index 064b390b29b..db3c68ed0a1 100644 --- a/src/Filecoin/Signer.h +++ b/src/Filecoin/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Filecoin/Transaction.cpp b/src/Filecoin/Transaction.cpp index 20dc505c8b1..3ac1db7f6f1 100644 --- a/src/Filecoin/Transaction.cpp +++ b/src/Filecoin/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" #include diff --git a/src/Filecoin/Transaction.h b/src/Filecoin/Transaction.h index 979181f6bfc..54615507a4c 100644 --- a/src/Filecoin/Transaction.h +++ b/src/Filecoin/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/FullSS58Address.h b/src/FullSS58Address.h index 0815db1986f..a9616186af2 100644 --- a/src/FullSS58Address.h +++ b/src/FullSS58Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Greenfield/Entry.h b/src/Greenfield/Entry.h index c54d843fc99..9ab2416863d 100644 --- a/src/Greenfield/Entry.h +++ b/src/Greenfield/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Groestlcoin/Address.cpp b/src/Groestlcoin/Address.cpp index fa7fe68105a..7871353d279 100644 --- a/src/Groestlcoin/Address.cpp +++ b/src/Groestlcoin/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" diff --git a/src/Groestlcoin/Address.h b/src/Groestlcoin/Address.h index a6c8700184e..2aad25dfe14 100644 --- a/src/Groestlcoin/Address.h +++ b/src/Groestlcoin/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Groestlcoin/Entry.cpp b/src/Groestlcoin/Entry.cpp index d978c2bd861..89254b29e9a 100644 --- a/src/Groestlcoin/Entry.cpp +++ b/src/Groestlcoin/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Groestlcoin/Entry.h b/src/Groestlcoin/Entry.h index 335b9f0c47a..b9d0ca454e3 100644 --- a/src/Groestlcoin/Entry.h +++ b/src/Groestlcoin/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Groestlcoin/Signer.cpp b/src/Groestlcoin/Signer.cpp index 24b8c308528..772e816d8bd 100644 --- a/src/Groestlcoin/Signer.cpp +++ b/src/Groestlcoin/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Bitcoin/TransactionBuilder.h" diff --git a/src/Groestlcoin/Signer.h b/src/Groestlcoin/Signer.h index 927b1b45c7a..1a90b0c7ce3 100644 --- a/src/Groestlcoin/Signer.h +++ b/src/Groestlcoin/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once #include "../proto/Bitcoin.pb.h" diff --git a/src/Groestlcoin/Transaction.h b/src/Groestlcoin/Transaction.h index 25ebf7f6706..e9d34458a5e 100644 --- a/src/Groestlcoin/Transaction.h +++ b/src/Groestlcoin/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/HDWallet.cpp b/src/HDWallet.cpp index b4674f56dcf..23f01790c7f 100644 --- a/src/HDWallet.cpp +++ b/src/HDWallet.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HDWallet.h" diff --git a/src/HDWallet.h b/src/HDWallet.h index 9ed27ffb8af..7cff0fbcb2e 100644 --- a/src/HDWallet.h +++ b/src/HDWallet.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Harmony/Address.cpp b/src/Harmony/Address.cpp index d6520def9a3..9da95d15d66 100644 --- a/src/Harmony/Address.cpp +++ b/src/Harmony/Address.cpp @@ -1,10 +1,8 @@ // Copyright © 2017 Pieter Wuille -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" diff --git a/src/Harmony/Address.h b/src/Harmony/Address.h index 43f03ddda34..2fda40b406d 100644 --- a/src/Harmony/Address.h +++ b/src/Harmony/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Harmony/Entry.cpp b/src/Harmony/Entry.cpp index a12678d17d3..e9ad9fdd624 100644 --- a/src/Harmony/Entry.cpp +++ b/src/Harmony/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" #include "Address.h" diff --git a/src/Harmony/Entry.h b/src/Harmony/Entry.h index 41f0e5c5aae..bc1c3ee7062 100644 --- a/src/Harmony/Entry.h +++ b/src/Harmony/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Harmony/Signer.cpp b/src/Harmony/Signer.cpp index 9b262426187..5870b4769ec 100644 --- a/src/Harmony/Signer.cpp +++ b/src/Harmony/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "../Ethereum/RLP.h" diff --git a/src/Harmony/Signer.h b/src/Harmony/Signer.h index 9a24142279e..f45fdb9b90a 100644 --- a/src/Harmony/Signer.h +++ b/src/Harmony/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Harmony/Staking.cpp b/src/Harmony/Staking.cpp index 794f68a49cf..7632dd9561b 100644 --- a/src/Harmony/Staking.cpp +++ b/src/Harmony/Staking.cpp @@ -1,5 +1,3 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. diff --git a/src/Harmony/Staking.h b/src/Harmony/Staking.h index d7a4d8ee8f2..ef778a01a19 100644 --- a/src/Harmony/Staking.h +++ b/src/Harmony/Staking.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Harmony/Transaction.h b/src/Harmony/Transaction.h index 5e568884fa9..fc4a33e5158 100644 --- a/src/Harmony/Transaction.h +++ b/src/Harmony/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Hash.cpp b/src/Hash.cpp index 389854338ec..80a2a7d2b6a 100644 --- a/src/Hash.cpp +++ b/src/Hash.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Hash.h" diff --git a/src/Hash.h b/src/Hash.h index 20932337e58..bcc46a756bf 100644 --- a/src/Hash.h +++ b/src/Hash.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Hedera/Address.cpp b/src/Hedera/Address.cpp index cab84478d80..cb05e2d212f 100644 --- a/src/Hedera/Address.cpp +++ b/src/Hedera/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include "HexCoding.h" diff --git a/src/Hedera/Address.h b/src/Hedera/Address.h index 426347a919e..076cbeb47f3 100644 --- a/src/Hedera/Address.h +++ b/src/Hedera/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Hedera/DER.cpp b/src/Hedera/DER.cpp index d02d54cfea2..6f72051fd01 100644 --- a/src/Hedera/DER.cpp +++ b/src/Hedera/DER.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "DER.h" #include "PublicKey.h" diff --git a/src/Hedera/DER.h b/src/Hedera/DER.h index d00ac60d759..8918fceae4a 100644 --- a/src/Hedera/DER.h +++ b/src/Hedera/DER.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Hedera/Entry.cpp b/src/Hedera/Entry.cpp index 7a71b45945e..b939e0d170d 100644 --- a/src/Hedera/Entry.cpp +++ b/src/Hedera/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Hedera/Entry.h b/src/Hedera/Entry.h index 1e5c2307252..e9632c3a308 100644 --- a/src/Hedera/Entry.h +++ b/src/Hedera/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Hedera/Signer.cpp b/src/Hedera/Signer.cpp index 36d7b3f33a6..47628377551 100644 --- a/src/Hedera/Signer.cpp +++ b/src/Hedera/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Address.h" diff --git a/src/Hedera/Signer.h b/src/Hedera/Signer.h index 65a924aae5a..c76eaad764e 100644 --- a/src/Hedera/Signer.h +++ b/src/Hedera/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/HexCoding.h b/src/HexCoding.h index 816b4a7fb79..581da5e08cb 100644 --- a/src/HexCoding.h +++ b/src/HexCoding.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/IOST/Account.cpp b/src/IOST/Account.cpp index becf670007b..1aa3c91f5b7 100644 --- a/src/IOST/Account.cpp +++ b/src/IOST/Account.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Account.h" #include "../Base58.h" diff --git a/src/IOST/Account.h b/src/IOST/Account.h index 3e9ab7b01a8..bc93493d2bb 100644 --- a/src/IOST/Account.h +++ b/src/IOST/Account.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/IOST/Entry.cpp b/src/IOST/Entry.cpp index e27ef441c71..c5f8d59dff6 100644 --- a/src/IOST/Entry.cpp +++ b/src/IOST/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" #include "Account.h" diff --git a/src/IOST/Entry.h b/src/IOST/Entry.h index 65ba8aa8221..3b187c6ed68 100644 --- a/src/IOST/Entry.h +++ b/src/IOST/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/IOST/Signer.cpp b/src/IOST/Signer.cpp index e9aa1ccddaa..5b6f39f34d5 100644 --- a/src/IOST/Signer.cpp +++ b/src/IOST/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Account.h" diff --git a/src/IOST/Signer.h b/src/IOST/Signer.h index fe53d41b5f1..229a9227160 100644 --- a/src/IOST/Signer.h +++ b/src/IOST/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Icon/Address.cpp b/src/Icon/Address.cpp index 08ce69a2fa2..15eaf1d7a02 100644 --- a/src/Icon/Address.cpp +++ b/src/Icon/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" diff --git a/src/Icon/Address.h b/src/Icon/Address.h index 6f58d450dcb..19da238ac78 100644 --- a/src/Icon/Address.h +++ b/src/Icon/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Icon/AddressType.h b/src/Icon/AddressType.h index a38eeaeacd0..9823182cde5 100644 --- a/src/Icon/AddressType.h +++ b/src/Icon/AddressType.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Icon/Entry.cpp b/src/Icon/Entry.cpp index 51cb9601bd4..d9e415dbd5b 100644 --- a/src/Icon/Entry.cpp +++ b/src/Icon/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Icon/Entry.h b/src/Icon/Entry.h index 78bd64b2f01..3983aea1b5f 100644 --- a/src/Icon/Entry.h +++ b/src/Icon/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Icon/Signer.cpp b/src/Icon/Signer.cpp index d8a55cd82b8..8a54194df2b 100644 --- a/src/Icon/Signer.cpp +++ b/src/Icon/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" diff --git a/src/Icon/Signer.h b/src/Icon/Signer.h index 7fb34c96647..73c6d518d88 100644 --- a/src/Icon/Signer.h +++ b/src/Icon/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/ImmutableX/Constants.h b/src/ImmutableX/Constants.h index 034d548583e..f1b1e485643 100644 --- a/src/ImmutableX/Constants.h +++ b/src/ImmutableX/Constants.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/ImmutableX/StarkKey.cpp b/src/ImmutableX/StarkKey.cpp index f550b7ab14d..51dec1d669f 100644 --- a/src/ImmutableX/StarkKey.cpp +++ b/src/ImmutableX/StarkKey.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/ImmutableX/StarkKey.h b/src/ImmutableX/StarkKey.h index 5c2643a56da..d5702ede3a1 100644 --- a/src/ImmutableX/StarkKey.h +++ b/src/ImmutableX/StarkKey.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/InternetComputer/Entry.h b/src/InternetComputer/Entry.h index 0d9081d60d1..bf879432460 100644 --- a/src/InternetComputer/Entry.h +++ b/src/InternetComputer/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/IoTeX/Address.cpp b/src/IoTeX/Address.cpp index cb54d8c5731..8e387be97c8 100644 --- a/src/IoTeX/Address.cpp +++ b/src/IoTeX/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" diff --git a/src/IoTeX/Address.h b/src/IoTeX/Address.h index 2cdb7a00e08..355c63e917c 100644 --- a/src/IoTeX/Address.h +++ b/src/IoTeX/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/IoTeX/Entry.cpp b/src/IoTeX/Entry.cpp index 3d7b5628cb3..2f25cebbd00 100644 --- a/src/IoTeX/Entry.cpp +++ b/src/IoTeX/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/IoTeX/Entry.h b/src/IoTeX/Entry.h index bd5c4e17307..3b56c8da833 100644 --- a/src/IoTeX/Entry.h +++ b/src/IoTeX/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/IoTeX/Signer.cpp b/src/IoTeX/Signer.cpp index e1fa6d5d0b2..75c0d2cf7a6 100644 --- a/src/IoTeX/Signer.cpp +++ b/src/IoTeX/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Hash.h" diff --git a/src/IoTeX/Signer.h b/src/IoTeX/Signer.h index 7bcce3e28cb..217c676a1c5 100644 --- a/src/IoTeX/Signer.h +++ b/src/IoTeX/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/IoTeX/Staking.cpp b/src/IoTeX/Staking.cpp index 5d4eed6fd84..4675d899e44 100644 --- a/src/IoTeX/Staking.cpp +++ b/src/IoTeX/Staking.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Staking.h" #include "Data.h" diff --git a/src/IoTeX/Staking.h b/src/IoTeX/Staking.h index 713b6f2fa4d..0e716648925 100644 --- a/src/IoTeX/Staking.h +++ b/src/IoTeX/Staking.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/KeyPair.h b/src/KeyPair.h index e10bf0fa299..d7387c3db2d 100644 --- a/src/KeyPair.h +++ b/src/KeyPair.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Keystore/AESParameters.cpp b/src/Keystore/AESParameters.cpp index a1e097ea501..4d8a47eef09 100644 --- a/src/Keystore/AESParameters.cpp +++ b/src/Keystore/AESParameters.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "AESParameters.h" diff --git a/src/Keystore/AESParameters.h b/src/Keystore/AESParameters.h index feb5057c38a..46b49f9ee6c 100644 --- a/src/Keystore/AESParameters.h +++ b/src/Keystore/AESParameters.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Keystore/Account.cpp b/src/Keystore/Account.cpp index 5278c5acaf2..c97b8eb66d1 100644 --- a/src/Keystore/Account.cpp +++ b/src/Keystore/Account.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Account.h" diff --git a/src/Keystore/Account.h b/src/Keystore/Account.h index c941525473f..22a55c5d48d 100644 --- a/src/Keystore/Account.h +++ b/src/Keystore/Account.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Keystore/EncryptionParameters.cpp b/src/Keystore/EncryptionParameters.cpp index 57a60d56346..db34105c024 100644 --- a/src/Keystore/EncryptionParameters.cpp +++ b/src/Keystore/EncryptionParameters.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "EncryptionParameters.h" diff --git a/src/Keystore/EncryptionParameters.h b/src/Keystore/EncryptionParameters.h index 90701aebc21..711700d7aec 100644 --- a/src/Keystore/EncryptionParameters.h +++ b/src/Keystore/EncryptionParameters.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Keystore/PBKDF2Parameters.cpp b/src/Keystore/PBKDF2Parameters.cpp index a3d5ba80a15..e965e7bba16 100644 --- a/src/Keystore/PBKDF2Parameters.cpp +++ b/src/Keystore/PBKDF2Parameters.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "PBKDF2Parameters.h" diff --git a/src/Keystore/PBKDF2Parameters.h b/src/Keystore/PBKDF2Parameters.h index a6a9fe97b8e..1af9ade0ca9 100644 --- a/src/Keystore/PBKDF2Parameters.h +++ b/src/Keystore/PBKDF2Parameters.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Keystore/ScryptParameters.cpp b/src/Keystore/ScryptParameters.cpp index 0670a6dbed4..6f4bedf9a4d 100644 --- a/src/Keystore/ScryptParameters.cpp +++ b/src/Keystore/ScryptParameters.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "ScryptParameters.h" diff --git a/src/Keystore/ScryptParameters.h b/src/Keystore/ScryptParameters.h index 4dec0a3c798..94cf3f1b1f2 100644 --- a/src/Keystore/ScryptParameters.h +++ b/src/Keystore/ScryptParameters.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Keystore/StoredKey.cpp b/src/Keystore/StoredKey.cpp index a3fca3bb91f..8742e2760af 100644 --- a/src/Keystore/StoredKey.cpp +++ b/src/Keystore/StoredKey.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "StoredKey.h" diff --git a/src/Keystore/StoredKey.h b/src/Keystore/StoredKey.h index d98235ff793..6fae0da8a6d 100644 --- a/src/Keystore/StoredKey.h +++ b/src/Keystore/StoredKey.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Kusama/Address.h b/src/Kusama/Address.h index 8ae956b4164..6d4758002c2 100644 --- a/src/Kusama/Address.h +++ b/src/Kusama/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Kusama/Entry.cpp b/src/Kusama/Entry.cpp index a268f0b1a9c..46df5c4bfff 100644 --- a/src/Kusama/Entry.cpp +++ b/src/Kusama/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Kusama/Entry.h b/src/Kusama/Entry.h index d4d8634e9a6..d127bd39a4d 100644 --- a/src/Kusama/Entry.h +++ b/src/Kusama/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/LiquidStaking/LiquidStaking.cpp b/src/LiquidStaking/LiquidStaking.cpp index b635e5c268c..fbb83adc742 100644 --- a/src/LiquidStaking/LiquidStaking.cpp +++ b/src/LiquidStaking/LiquidStaking.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "LiquidStaking/LiquidStaking.h" #include "Data.h" diff --git a/src/LiquidStaking/LiquidStaking.h b/src/LiquidStaking/LiquidStaking.h index 713421fc421..b4f974b7701 100644 --- a/src/LiquidStaking/LiquidStaking.h +++ b/src/LiquidStaking/LiquidStaking.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/LiquidStaking/TWLiquidStaking.cpp b/src/LiquidStaking/TWLiquidStaking.cpp index da8ad8aa6ae..2b6dceb3451 100644 --- a/src/LiquidStaking/TWLiquidStaking.cpp +++ b/src/LiquidStaking/TWLiquidStaking.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Data.h" #include "proto/LiquidStaking.pb.h" diff --git a/src/Mnemonic.cpp b/src/Mnemonic.cpp index e07181fa610..a6e9a2227cd 100644 --- a/src/Mnemonic.cpp +++ b/src/Mnemonic.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Mnemonic.h" diff --git a/src/Mnemonic.h b/src/Mnemonic.h index 92b7b692fdb..dc6fc4ff2d6 100644 --- a/src/Mnemonic.h +++ b/src/Mnemonic.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Move/Address.h b/src/Move/Address.h index b35504693a9..a734df9cd9e 100644 --- a/src/Move/Address.h +++ b/src/Move/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/MultiversX/Address.cpp b/src/MultiversX/Address.cpp index d882248ab4b..2b3c3a89cec 100644 --- a/src/MultiversX/Address.cpp +++ b/src/MultiversX/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/MultiversX/Address.h b/src/MultiversX/Address.h index d47c56e3f90..2047e9b1df2 100644 --- a/src/MultiversX/Address.h +++ b/src/MultiversX/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/MultiversX/Codec.cpp b/src/MultiversX/Codec.cpp index c613ddfe763..95e19883315 100644 --- a/src/MultiversX/Codec.cpp +++ b/src/MultiversX/Codec.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Codec.h" diff --git a/src/MultiversX/Codec.h b/src/MultiversX/Codec.h index ec9f5511c2c..88d30aa7db3 100644 --- a/src/MultiversX/Codec.h +++ b/src/MultiversX/Codec.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/MultiversX/Entry.cpp b/src/MultiversX/Entry.cpp index b92d236c050..7aec5758beb 100644 --- a/src/MultiversX/Entry.cpp +++ b/src/MultiversX/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" #include "Address.h" diff --git a/src/MultiversX/Entry.h b/src/MultiversX/Entry.h index 6300cb2e906..a51b55efac2 100644 --- a/src/MultiversX/Entry.h +++ b/src/MultiversX/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/MultiversX/Serialization.cpp b/src/MultiversX/Serialization.cpp index ab4c2a98876..dbd7dc25a21 100644 --- a/src/MultiversX/Serialization.cpp +++ b/src/MultiversX/Serialization.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Serialization.h" diff --git a/src/MultiversX/Serialization.h b/src/MultiversX/Serialization.h index 9105f822577..c344bf0593e 100644 --- a/src/MultiversX/Serialization.h +++ b/src/MultiversX/Serialization.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/MultiversX/Signer.cpp b/src/MultiversX/Signer.cpp index 60784a8d46b..a2a80fdedd3 100644 --- a/src/MultiversX/Signer.cpp +++ b/src/MultiversX/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Address.h" diff --git a/src/MultiversX/Signer.h b/src/MultiversX/Signer.h index a7c8ee34545..7ccc88de919 100644 --- a/src/MultiversX/Signer.h +++ b/src/MultiversX/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/MultiversX/Transaction.cpp b/src/MultiversX/Transaction.cpp index c08cac14560..54f38c135cb 100644 --- a/src/MultiversX/Transaction.cpp +++ b/src/MultiversX/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" diff --git a/src/MultiversX/Transaction.h b/src/MultiversX/Transaction.h index cf9c62fa7eb..217ba1ef01f 100644 --- a/src/MultiversX/Transaction.h +++ b/src/MultiversX/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/MultiversX/TransactionFactory.cpp b/src/MultiversX/TransactionFactory.cpp index f1f2cf4eb0a..6894ce17f03 100644 --- a/src/MultiversX/TransactionFactory.cpp +++ b/src/MultiversX/TransactionFactory.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TransactionFactory.h" diff --git a/src/MultiversX/TransactionFactory.h b/src/MultiversX/TransactionFactory.h index a0e201a031a..f3a62e019e6 100644 --- a/src/MultiversX/TransactionFactory.h +++ b/src/MultiversX/TransactionFactory.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/MultiversX/TransactionFactoryConfig.cpp b/src/MultiversX/TransactionFactoryConfig.cpp index 7d1a2b78148..1c8ff82f9bb 100644 --- a/src/MultiversX/TransactionFactoryConfig.cpp +++ b/src/MultiversX/TransactionFactoryConfig.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TransactionFactoryConfig.h" diff --git a/src/MultiversX/TransactionFactoryConfig.h b/src/MultiversX/TransactionFactoryConfig.h index 6f7aa126436..cd6ec75d553 100644 --- a/src/MultiversX/TransactionFactoryConfig.h +++ b/src/MultiversX/TransactionFactoryConfig.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEAR/Account.cpp b/src/NEAR/Account.cpp index 7a68fa709a2..08c0ea8adb5 100644 --- a/src/NEAR/Account.cpp +++ b/src/NEAR/Account.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Account.h" diff --git a/src/NEAR/Account.h b/src/NEAR/Account.h index 1b222843783..6ae30ebcd5a 100644 --- a/src/NEAR/Account.h +++ b/src/NEAR/Account.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEAR/Address.cpp b/src/NEAR/Address.cpp index a8bd93d1caa..4ba2f6aca7a 100644 --- a/src/NEAR/Address.cpp +++ b/src/NEAR/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include "Base58.h" diff --git a/src/NEAR/Address.h b/src/NEAR/Address.h index 38adba9e559..4ee35512d36 100644 --- a/src/NEAR/Address.h +++ b/src/NEAR/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEAR/Entry.cpp b/src/NEAR/Entry.cpp index 118ec62a06a..9be0149177b 100644 --- a/src/NEAR/Entry.cpp +++ b/src/NEAR/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" #include "Address.h" diff --git a/src/NEAR/Entry.h b/src/NEAR/Entry.h index 2efa1b2c254..f18a67be084 100644 --- a/src/NEAR/Entry.h +++ b/src/NEAR/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEAR/Serialization.cpp b/src/NEAR/Serialization.cpp index 7a15d9bc052..f2272383ede 100644 --- a/src/NEAR/Serialization.cpp +++ b/src/NEAR/Serialization.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Serialization.h" diff --git a/src/NEAR/Serialization.h b/src/NEAR/Serialization.h index 4dd17fe840e..e1025553cc3 100644 --- a/src/NEAR/Serialization.h +++ b/src/NEAR/Serialization.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEAR/Signer.cpp b/src/NEAR/Signer.cpp index d4a00c033e3..9e2589ed09e 100644 --- a/src/NEAR/Signer.cpp +++ b/src/NEAR/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Serialization.h" diff --git a/src/NEAR/Signer.h b/src/NEAR/Signer.h index aa4945c1d4d..1343851b9ab 100644 --- a/src/NEAR/Signer.h +++ b/src/NEAR/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEO/Address.cpp b/src/NEO/Address.cpp index e63298521cb..3c4ee0ebd0c 100644 --- a/src/NEO/Address.cpp +++ b/src/NEO/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "OpCode.h" #include "../Base58.h" diff --git a/src/NEO/Address.h b/src/NEO/Address.h index 450aabd3de8..4ab69e36cc1 100644 --- a/src/NEO/Address.h +++ b/src/NEO/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEO/BinaryCoding.h b/src/NEO/BinaryCoding.h index f152e600c3d..03aaa8843b0 100644 --- a/src/NEO/BinaryCoding.h +++ b/src/NEO/BinaryCoding.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEO/CoinReference.h b/src/NEO/CoinReference.h index c1f0272a2bc..6d08a78b257 100644 --- a/src/NEO/CoinReference.h +++ b/src/NEO/CoinReference.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEO/Constants.h b/src/NEO/Constants.h index a0fd45fd016..73866c3ed4f 100644 --- a/src/NEO/Constants.h +++ b/src/NEO/Constants.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEO/Entry.cpp b/src/NEO/Entry.cpp index 68158ba9653..4fc03845630 100644 --- a/src/NEO/Entry.cpp +++ b/src/NEO/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/NEO/Entry.h b/src/NEO/Entry.h index 15d3e721aab..c38662a8d74 100644 --- a/src/NEO/Entry.h +++ b/src/NEO/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEO/ISerializable.h b/src/NEO/ISerializable.h index 2caa5ae1227..79461bd5c92 100644 --- a/src/NEO/ISerializable.h +++ b/src/NEO/ISerializable.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEO/InvocationTransaction.h b/src/NEO/InvocationTransaction.h index e2d13641edc..bc88148f195 100644 --- a/src/NEO/InvocationTransaction.h +++ b/src/NEO/InvocationTransaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEO/MinerTransaction.h b/src/NEO/MinerTransaction.h index 9e6a6cc848c..ce2c9c2778e 100644 --- a/src/NEO/MinerTransaction.h +++ b/src/NEO/MinerTransaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEO/OpCode.h b/src/NEO/OpCode.h index d3633883e06..db1dc7eddb6 100644 --- a/src/NEO/OpCode.h +++ b/src/NEO/OpCode.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEO/ReadData.cpp b/src/NEO/ReadData.cpp index 0f04b9a4f5e..97580a6c1c4 100644 --- a/src/NEO/ReadData.cpp +++ b/src/NEO/ReadData.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Data.h" #include "ReadData.h" diff --git a/src/NEO/ReadData.h b/src/NEO/ReadData.h index cf4b80be581..8c661c29e90 100644 --- a/src/NEO/ReadData.h +++ b/src/NEO/ReadData.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEO/Script.cpp b/src/NEO/Script.cpp index 9259bdc4710..0fc1f31c7e1 100644 --- a/src/NEO/Script.cpp +++ b/src/NEO/Script.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Script.h" #include "BinaryCoding.h" diff --git a/src/NEO/Script.h b/src/NEO/Script.h index 1c2d7d2e8d4..35984613c14 100644 --- a/src/NEO/Script.h +++ b/src/NEO/Script.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once #include "Data.h" diff --git a/src/NEO/Serializable.h b/src/NEO/Serializable.h index ecc997de749..e67bda608ca 100644 --- a/src/NEO/Serializable.h +++ b/src/NEO/Serializable.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEO/Signer.cpp b/src/NEO/Signer.cpp index ad8ff46ec08..df3187c9447 100644 --- a/src/NEO/Signer.cpp +++ b/src/NEO/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "InvocationTransaction.h" diff --git a/src/NEO/Signer.h b/src/NEO/Signer.h index e388539fa5f..d26fb9addb3 100644 --- a/src/NEO/Signer.h +++ b/src/NEO/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEO/Transaction.cpp b/src/NEO/Transaction.cpp index 45fa9bbbd58..933dc97df50 100644 --- a/src/NEO/Transaction.cpp +++ b/src/NEO/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/NEO/Transaction.h b/src/NEO/Transaction.h index 1a17b3f6757..3df16aa3d51 100644 --- a/src/NEO/Transaction.h +++ b/src/NEO/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEO/TransactionAttribute.h b/src/NEO/TransactionAttribute.h index 4199b61d919..5c237a1201c 100644 --- a/src/NEO/TransactionAttribute.h +++ b/src/NEO/TransactionAttribute.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEO/TransactionAttributeUsage.h b/src/NEO/TransactionAttributeUsage.h index fc0e3f90c6d..7c061df70df 100644 --- a/src/NEO/TransactionAttributeUsage.h +++ b/src/NEO/TransactionAttributeUsage.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEO/TransactionOutput.h b/src/NEO/TransactionOutput.h index f4917362731..df473006a19 100644 --- a/src/NEO/TransactionOutput.h +++ b/src/NEO/TransactionOutput.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEO/TransactionType.h b/src/NEO/TransactionType.h index 424e959bc04..4b3840fab2a 100644 --- a/src/NEO/TransactionType.h +++ b/src/NEO/TransactionType.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NEO/Witness.h b/src/NEO/Witness.h index d0a8602454f..fe67e5e260e 100644 --- a/src/NEO/Witness.h +++ b/src/NEO/Witness.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NULS/Address.cpp b/src/NULS/Address.cpp index 1a854067e97..d71d4279341 100644 --- a/src/NULS/Address.cpp +++ b/src/NULS/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include diff --git a/src/NULS/Address.h b/src/NULS/Address.h index 04efcb97b83..73425100bf0 100644 --- a/src/NULS/Address.h +++ b/src/NULS/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once #include "../Base58Address.h" #include "../PrivateKey.h" diff --git a/src/NULS/BinaryCoding.h b/src/NULS/BinaryCoding.h index 4ae1c2f087e..f9c59e4f8db 100644 --- a/src/NULS/BinaryCoding.h +++ b/src/NULS/BinaryCoding.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NULS/Entry.cpp b/src/NULS/Entry.cpp index 1c970b47cfd..12560ffd6be 100644 --- a/src/NULS/Entry.cpp +++ b/src/NULS/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" #include "Address.h" diff --git a/src/NULS/Entry.h b/src/NULS/Entry.h index db7b9741cbb..fe29d702463 100644 --- a/src/NULS/Entry.h +++ b/src/NULS/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NULS/Signer.cpp b/src/NULS/Signer.cpp index 7c6d702659d..2e1ffca2784 100644 --- a/src/NULS/Signer.cpp +++ b/src/NULS/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include diff --git a/src/NULS/Signer.h b/src/NULS/Signer.h index 52657470392..748502aba6e 100644 --- a/src/NULS/Signer.h +++ b/src/NULS/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once #include "../proto/NULS.pb.h" diff --git a/src/Nano/Address.cpp b/src/Nano/Address.cpp index b6b49143212..beb05b919be 100644 --- a/src/Nano/Address.cpp +++ b/src/Nano/Address.cpp @@ -1,9 +1,7 @@ // Copyright © 2019 Mart Roosmaa. -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include diff --git a/src/Nano/Address.h b/src/Nano/Address.h index 28dcd2c16d4..9901577d11d 100644 --- a/src/Nano/Address.h +++ b/src/Nano/Address.h @@ -1,9 +1,7 @@ // Copyright © 2019 Mart Roosmaa. -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nano/Entry.cpp b/src/Nano/Entry.cpp index 922bf06c553..5fae524f9a3 100644 --- a/src/Nano/Entry.cpp +++ b/src/Nano/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" #include "Address.h" diff --git a/src/Nano/Entry.h b/src/Nano/Entry.h index 1ebce88f9d3..441028831f7 100644 --- a/src/Nano/Entry.h +++ b/src/Nano/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nano/Signer.cpp b/src/Nano/Signer.cpp index 95d6cfc4f85..c18eb036eee 100644 --- a/src/Nano/Signer.cpp +++ b/src/Nano/Signer.cpp @@ -1,9 +1,7 @@ // Copyright © 2019 Mart Roosmaa. -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "../BinaryCoding.h" diff --git a/src/Nano/Signer.h b/src/Nano/Signer.h index 2185e6d20e2..fb71e452dfa 100644 --- a/src/Nano/Signer.h +++ b/src/Nano/Signer.h @@ -1,9 +1,7 @@ // Copyright © 2019 Mart Roosmaa. -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once #include "Address.h" diff --git a/src/NativeEvmos/Entry.h b/src/NativeEvmos/Entry.h index bc44a9620e2..17fccc33834 100644 --- a/src/NativeEvmos/Entry.h +++ b/src/NativeEvmos/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NativeInjective/Entry.h b/src/NativeInjective/Entry.h index 1a55e88540b..9061e28c3cd 100644 --- a/src/NativeInjective/Entry.h +++ b/src/NativeInjective/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nebulas/Address.cpp b/src/Nebulas/Address.cpp index 9eaa689720e..760dbe0084c 100644 --- a/src/Nebulas/Address.cpp +++ b/src/Nebulas/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include "../Base58.h" diff --git a/src/Nebulas/Address.h b/src/Nebulas/Address.h index cd63bf0a65e..469a30876ad 100644 --- a/src/Nebulas/Address.h +++ b/src/Nebulas/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nebulas/Entry.cpp b/src/Nebulas/Entry.cpp index 8df53031f88..cf953f588c5 100644 --- a/src/Nebulas/Entry.cpp +++ b/src/Nebulas/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Nebulas/Entry.h b/src/Nebulas/Entry.h index f34efbcc47d..4792552c1f4 100644 --- a/src/Nebulas/Entry.h +++ b/src/Nebulas/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nebulas/Signer.cpp b/src/Nebulas/Signer.cpp index 6304f010964..ada56c94f8b 100644 --- a/src/Nebulas/Signer.cpp +++ b/src/Nebulas/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Base64.h" diff --git a/src/Nebulas/Signer.h b/src/Nebulas/Signer.h index f0d78873a4e..51238e21f3d 100644 --- a/src/Nebulas/Signer.h +++ b/src/Nebulas/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nebulas/Transaction.cpp b/src/Nebulas/Transaction.cpp index 4d84a382b72..915c57f60a2 100644 --- a/src/Nebulas/Transaction.cpp +++ b/src/Nebulas/Transaction.cpp @@ -1,11 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// Copyright © 2017-2023 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" #include "../HexCoding.h" diff --git a/src/Nebulas/Transaction.h b/src/Nebulas/Transaction.h index 5bb100ef503..3edc9e231a6 100644 --- a/src/Nebulas/Transaction.h +++ b/src/Nebulas/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nervos/Address.cpp b/src/Nervos/Address.cpp index 314e679ee8c..5f27a73f94d 100644 --- a/src/Nervos/Address.cpp +++ b/src/Nervos/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include "Constants.h" diff --git a/src/Nervos/Address.h b/src/Nervos/Address.h index 2c0b7a02dfd..6e6568d4bf4 100644 --- a/src/Nervos/Address.h +++ b/src/Nervos/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nervos/Cell.h b/src/Nervos/Cell.h index c57334d4f83..8f7c0c7f79e 100644 --- a/src/Nervos/Cell.h +++ b/src/Nervos/Cell.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nervos/CellDep.cpp b/src/Nervos/CellDep.cpp index d4b30afc0ad..1dabbebfa66 100644 --- a/src/Nervos/CellDep.cpp +++ b/src/Nervos/CellDep.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "CellDep.h" diff --git a/src/Nervos/CellDep.h b/src/Nervos/CellDep.h index 0dc32060386..d28543052e0 100644 --- a/src/Nervos/CellDep.h +++ b/src/Nervos/CellDep.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nervos/CellInput.cpp b/src/Nervos/CellInput.cpp index 8d20ac45365..66e248f2f29 100644 --- a/src/Nervos/CellInput.cpp +++ b/src/Nervos/CellInput.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "CellInput.h" #include "Serialization.h" diff --git a/src/Nervos/CellInput.h b/src/Nervos/CellInput.h index 11fd2a5f8dc..09f5b4da00c 100644 --- a/src/Nervos/CellInput.h +++ b/src/Nervos/CellInput.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nervos/CellOutput.cpp b/src/Nervos/CellOutput.cpp index 09f7e14600e..ea938845446 100644 --- a/src/Nervos/CellOutput.cpp +++ b/src/Nervos/CellOutput.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "CellOutput.h" #include "Serialization.h" diff --git a/src/Nervos/CellOutput.h b/src/Nervos/CellOutput.h index 8ad0afba633..d3e5ac57fd6 100644 --- a/src/Nervos/CellOutput.h +++ b/src/Nervos/CellOutput.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nervos/Constants.h b/src/Nervos/Constants.h index b3af305d479..d492dcc76c4 100644 --- a/src/Nervos/Constants.h +++ b/src/Nervos/Constants.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nervos/Entry.cpp b/src/Nervos/Entry.cpp index 7a6aff9292e..e8a2b4056c4 100644 --- a/src/Nervos/Entry.cpp +++ b/src/Nervos/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Nervos/Entry.h b/src/Nervos/Entry.h index c40e012d8ae..a0876175889 100644 --- a/src/Nervos/Entry.h +++ b/src/Nervos/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nervos/HeaderDep.h b/src/Nervos/HeaderDep.h index 6706eeeeb0b..63e440f9d93 100644 --- a/src/Nervos/HeaderDep.h +++ b/src/Nervos/HeaderDep.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nervos/OutPoint.cpp b/src/Nervos/OutPoint.cpp index 6066d97ee5e..b6c2992e09d 100644 --- a/src/Nervos/OutPoint.cpp +++ b/src/Nervos/OutPoint.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "OutPoint.h" #include "Serialization.h" diff --git a/src/Nervos/OutPoint.h b/src/Nervos/OutPoint.h index 10b67cb9c38..5c7b9e6a241 100644 --- a/src/Nervos/OutPoint.h +++ b/src/Nervos/OutPoint.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nervos/Script.cpp b/src/Nervos/Script.cpp index 94682d37089..abcf703259c 100644 --- a/src/Nervos/Script.cpp +++ b/src/Nervos/Script.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Script.h" #include "Constants.h" diff --git a/src/Nervos/Script.h b/src/Nervos/Script.h index d5a1f132386..2c738a5c3af 100644 --- a/src/Nervos/Script.h +++ b/src/Nervos/Script.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nervos/Serialization.h b/src/Nervos/Serialization.h index eb76d2d1e2e..96760e14321 100644 --- a/src/Nervos/Serialization.h +++ b/src/Nervos/Serialization.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nervos/Signer.cpp b/src/Nervos/Signer.cpp index f223096fa11..d5cb006ad30 100644 --- a/src/Nervos/Signer.cpp +++ b/src/Nervos/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Transaction.h" diff --git a/src/Nervos/Signer.h b/src/Nervos/Signer.h index e2e2c3fb38c..bbbc70d3677 100644 --- a/src/Nervos/Signer.h +++ b/src/Nervos/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nervos/Transaction.cpp b/src/Nervos/Transaction.cpp index 98983bf6d77..ca65efa723e 100644 --- a/src/Nervos/Transaction.cpp +++ b/src/Nervos/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" #include "Constants.h" diff --git a/src/Nervos/Transaction.h b/src/Nervos/Transaction.h index c94b7f0e1bb..9f6f6115fb9 100644 --- a/src/Nervos/Transaction.h +++ b/src/Nervos/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nervos/TransactionPlan.cpp b/src/Nervos/TransactionPlan.cpp index a755b00010a..0feb4663a32 100644 --- a/src/Nervos/TransactionPlan.cpp +++ b/src/Nervos/TransactionPlan.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TransactionPlan.h" #include "Constants.h" diff --git a/src/Nervos/TransactionPlan.h b/src/Nervos/TransactionPlan.h index 39f8bf6d1ea..7812f2ee124 100644 --- a/src/Nervos/TransactionPlan.h +++ b/src/Nervos/TransactionPlan.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nervos/Witness.cpp b/src/Nervos/Witness.cpp index ae954b11cd0..aa66b4aeed1 100644 --- a/src/Nervos/Witness.cpp +++ b/src/Nervos/Witness.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Witness.h" #include "Serialization.h" diff --git a/src/Nervos/Witness.h b/src/Nervos/Witness.h index 15e3319c49b..0e67da4a72a 100644 --- a/src/Nervos/Witness.h +++ b/src/Nervos/Witness.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nimiq/Address.cpp b/src/Nimiq/Address.cpp index c6d59b1e22c..ae58500cfc0 100644 --- a/src/Nimiq/Address.cpp +++ b/src/Nimiq/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" diff --git a/src/Nimiq/Address.h b/src/Nimiq/Address.h index 4d991788928..6b94b61c548 100644 --- a/src/Nimiq/Address.h +++ b/src/Nimiq/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nimiq/Entry.cpp b/src/Nimiq/Entry.cpp index 619504314f1..413d8ba854c 100644 --- a/src/Nimiq/Entry.cpp +++ b/src/Nimiq/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Nimiq/Entry.h b/src/Nimiq/Entry.h index 467e3470dce..50256cd871d 100644 --- a/src/Nimiq/Entry.h +++ b/src/Nimiq/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nimiq/Signer.cpp b/src/Nimiq/Signer.cpp index 81fe18bbec5..ed08cefdff3 100644 --- a/src/Nimiq/Signer.cpp +++ b/src/Nimiq/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include diff --git a/src/Nimiq/Signer.h b/src/Nimiq/Signer.h index b3c0e071c04..1e485e45773 100644 --- a/src/Nimiq/Signer.h +++ b/src/Nimiq/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Nimiq/Transaction.cpp b/src/Nimiq/Transaction.cpp index f975612883e..e5ba42006fe 100644 --- a/src/Nimiq/Transaction.cpp +++ b/src/Nimiq/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" diff --git a/src/Nimiq/Transaction.h b/src/Nimiq/Transaction.h index f9efe6f3be0..4615d440537 100644 --- a/src/Nimiq/Transaction.h +++ b/src/Nimiq/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Numeric.h b/src/Numeric.h index 33cbd55d53e..d7db412fda7 100644 --- a/src/Numeric.h +++ b/src/Numeric.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/NumericLiteral.h b/src/NumericLiteral.h index f689e7f340e..0daa1f13242 100644 --- a/src/NumericLiteral.h +++ b/src/NumericLiteral.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Oasis/Address.cpp b/src/Oasis/Address.cpp index dac0488d3f2..0b394ddf2c1 100644 --- a/src/Oasis/Address.cpp +++ b/src/Oasis/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" diff --git a/src/Oasis/Address.h b/src/Oasis/Address.h index 16c5b383500..1e500aaffda 100644 --- a/src/Oasis/Address.h +++ b/src/Oasis/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Oasis/Entry.cpp b/src/Oasis/Entry.cpp index 2e153eeaa97..48efdde0ae4 100644 --- a/src/Oasis/Entry.cpp +++ b/src/Oasis/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Oasis/Entry.h b/src/Oasis/Entry.h index 1412bea7c2b..497f4df9593 100644 --- a/src/Oasis/Entry.h +++ b/src/Oasis/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Oasis/Signer.cpp b/src/Oasis/Signer.cpp index b7251679184..ea3405382a8 100644 --- a/src/Oasis/Signer.cpp +++ b/src/Oasis/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/Oasis/Signer.h b/src/Oasis/Signer.h index c166eb9bfe0..eae5d2b772d 100644 --- a/src/Oasis/Signer.h +++ b/src/Oasis/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Oasis/Transaction.cpp b/src/Oasis/Transaction.cpp index 5d4b21766fe..262ec08ee82 100644 --- a/src/Oasis/Transaction.cpp +++ b/src/Oasis/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" diff --git a/src/Oasis/Transaction.h b/src/Oasis/Transaction.h index ae26e49ab61..0a423b2c7cd 100644 --- a/src/Oasis/Transaction.h +++ b/src/Oasis/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ontology/Address.cpp b/src/Ontology/Address.cpp index 24d0df56fad..6cf098eb111 100644 --- a/src/Ontology/Address.cpp +++ b/src/Ontology/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include "OpCode.h" diff --git a/src/Ontology/Address.h b/src/Ontology/Address.h index 076d80f8198..463ebb73c91 100644 --- a/src/Ontology/Address.h +++ b/src/Ontology/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ontology/Asset.h b/src/Ontology/Asset.h index 61621b75049..69c060e128c 100644 --- a/src/Ontology/Asset.h +++ b/src/Ontology/Asset.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ontology/Entry.cpp b/src/Ontology/Entry.cpp index 7b5ded7ccd5..6dace88d25a 100644 --- a/src/Ontology/Entry.cpp +++ b/src/Ontology/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Ontology/Entry.h b/src/Ontology/Entry.h index 2b1781cf554..9e43327ab02 100644 --- a/src/Ontology/Entry.h +++ b/src/Ontology/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ontology/Oep4.cpp b/src/Ontology/Oep4.cpp index c3743ad2436..d6388793810 100644 --- a/src/Ontology/Oep4.cpp +++ b/src/Ontology/Oep4.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Oep4.h" #include diff --git a/src/Ontology/Oep4.h b/src/Ontology/Oep4.h index 45bdfc4fe66..31f4a99f283 100644 --- a/src/Ontology/Oep4.h +++ b/src/Ontology/Oep4.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ontology/Oep4TxBuilder.cpp b/src/Ontology/Oep4TxBuilder.cpp index 776c7b66581..9041adc3b78 100644 --- a/src/Ontology/Oep4TxBuilder.cpp +++ b/src/Ontology/Oep4TxBuilder.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Oep4TxBuilder.h" #include "../HexCoding.h" diff --git a/src/Ontology/Oep4TxBuilder.h b/src/Ontology/Oep4TxBuilder.h index 3b1b7f22cb2..ea09784bc6a 100644 --- a/src/Ontology/Oep4TxBuilder.h +++ b/src/Ontology/Oep4TxBuilder.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ontology/Ong.cpp b/src/Ontology/Ong.cpp index a0dedf78349..2ba3f5ca170 100644 --- a/src/Ontology/Ong.cpp +++ b/src/Ontology/Ong.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Ong.h" #include "Data.h" diff --git a/src/Ontology/Ong.h b/src/Ontology/Ong.h index 5e5b8f8ee33..0d69f9cf090 100644 --- a/src/Ontology/Ong.h +++ b/src/Ontology/Ong.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ontology/OngTxBuilder.cpp b/src/Ontology/OngTxBuilder.cpp index 884e44c7eba..2cae37b9cb1 100644 --- a/src/Ontology/OngTxBuilder.cpp +++ b/src/Ontology/OngTxBuilder.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "OngTxBuilder.h" diff --git a/src/Ontology/OngTxBuilder.h b/src/Ontology/OngTxBuilder.h index 943f8656615..8a3365017f9 100644 --- a/src/Ontology/OngTxBuilder.h +++ b/src/Ontology/OngTxBuilder.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ontology/Ont.cpp b/src/Ontology/Ont.cpp index 94df172d58b..9efd9812d06 100644 --- a/src/Ontology/Ont.cpp +++ b/src/Ontology/Ont.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Ont.h" #include "Data.h" diff --git a/src/Ontology/Ont.h b/src/Ontology/Ont.h index bf1649864fc..4ceca2a9aeb 100644 --- a/src/Ontology/Ont.h +++ b/src/Ontology/Ont.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ontology/OntTxBuilder.cpp b/src/Ontology/OntTxBuilder.cpp index 683d8323310..dc26b38e192 100644 --- a/src/Ontology/OntTxBuilder.cpp +++ b/src/Ontology/OntTxBuilder.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "OntTxBuilder.h" diff --git a/src/Ontology/OntTxBuilder.h b/src/Ontology/OntTxBuilder.h index e50f28a324f..dfeaee07fb7 100644 --- a/src/Ontology/OntTxBuilder.h +++ b/src/Ontology/OntTxBuilder.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ontology/OpCode.h b/src/Ontology/OpCode.h index 86ebbaa1613..b4d86aa9303 100644 --- a/src/Ontology/OpCode.h +++ b/src/Ontology/OpCode.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ontology/ParamsBuilder.cpp b/src/Ontology/ParamsBuilder.cpp index 40261a87608..7c529aa0080 100644 --- a/src/Ontology/ParamsBuilder.cpp +++ b/src/Ontology/ParamsBuilder.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "ParamsBuilder.h" #include "Data.h" diff --git a/src/Ontology/ParamsBuilder.h b/src/Ontology/ParamsBuilder.h index ee4a8456eab..a2d8e0d8818 100644 --- a/src/Ontology/ParamsBuilder.h +++ b/src/Ontology/ParamsBuilder.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ontology/SigData.cpp b/src/Ontology/SigData.cpp index b2cfccd051e..e4901bc910f 100644 --- a/src/Ontology/SigData.cpp +++ b/src/Ontology/SigData.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/Ontology/SigData.h b/src/Ontology/SigData.h index c06a2f17df6..67b1290b992 100644 --- a/src/Ontology/SigData.h +++ b/src/Ontology/SigData.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ontology/Signer.cpp b/src/Ontology/Signer.cpp index 0c25073bff6..48cf1bab857 100644 --- a/src/Ontology/Signer.cpp +++ b/src/Ontology/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" diff --git a/src/Ontology/Signer.h b/src/Ontology/Signer.h index 4b65213f87c..cc6745d0ea2 100644 --- a/src/Ontology/Signer.h +++ b/src/Ontology/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ontology/Transaction.cpp b/src/Ontology/Transaction.cpp index 8c0bc0e119d..694c8110ec6 100644 --- a/src/Ontology/Transaction.cpp +++ b/src/Ontology/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" #include "Address.h" diff --git a/src/Ontology/Transaction.h b/src/Ontology/Transaction.h index 48ecd4339de..39baf92e75e 100644 --- a/src/Ontology/Transaction.h +++ b/src/Ontology/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Polkadot/Address.h b/src/Polkadot/Address.h index 9356f01b754..c717bebe016 100644 --- a/src/Polkadot/Address.h +++ b/src/Polkadot/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Polkadot/Entry.cpp b/src/Polkadot/Entry.cpp index 14cf5f806ad..705acc11d9d 100644 --- a/src/Polkadot/Entry.cpp +++ b/src/Polkadot/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Polkadot/Entry.h b/src/Polkadot/Entry.h index be982d40921..de481bca381 100644 --- a/src/Polkadot/Entry.h +++ b/src/Polkadot/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Polkadot/Extrinsic.cpp b/src/Polkadot/Extrinsic.cpp index b1aaf7c6839..ebfbe67d2b0 100644 --- a/src/Polkadot/Extrinsic.cpp +++ b/src/Polkadot/Extrinsic.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Extrinsic.h" #include diff --git a/src/Polkadot/Extrinsic.h b/src/Polkadot/Extrinsic.h index 1f2e76ef7f4..885040a3bc1 100644 --- a/src/Polkadot/Extrinsic.h +++ b/src/Polkadot/Extrinsic.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Polkadot/SS58Address.cpp b/src/Polkadot/SS58Address.cpp index 99ed2cf736f..d73535352da 100644 --- a/src/Polkadot/SS58Address.cpp +++ b/src/Polkadot/SS58Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "SS58Address.h" diff --git a/src/Polkadot/SS58Address.h b/src/Polkadot/SS58Address.h index 6463313f2e3..44ad512874b 100644 --- a/src/Polkadot/SS58Address.h +++ b/src/Polkadot/SS58Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Polkadot/ScaleCodec.h b/src/Polkadot/ScaleCodec.h index 1af3818d6e3..47a0dbdf99d 100644 --- a/src/Polkadot/ScaleCodec.h +++ b/src/Polkadot/ScaleCodec.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Polkadot/Signer.cpp b/src/Polkadot/Signer.cpp index fcba54e5f0e..8107a1bf196 100644 --- a/src/Polkadot/Signer.cpp +++ b/src/Polkadot/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Extrinsic.h" diff --git a/src/Polkadot/Signer.h b/src/Polkadot/Signer.h index fe6d9fd5e9f..fa0204b2b01 100644 --- a/src/Polkadot/Signer.h +++ b/src/Polkadot/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/PrivateKey.cpp b/src/PrivateKey.cpp index 5707103c8d1..501dfc4124c 100644 --- a/src/PrivateKey.cpp +++ b/src/PrivateKey.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "PrivateKey.h" diff --git a/src/PrivateKey.h b/src/PrivateKey.h index e314c8350a0..c6ab3ddd8dc 100644 --- a/src/PrivateKey.h +++ b/src/PrivateKey.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/PublicKey.cpp b/src/PublicKey.cpp index 2609b4216da..d9789e6bc06 100644 --- a/src/PublicKey.cpp +++ b/src/PublicKey.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "PublicKey.h" #include "PrivateKey.h" diff --git a/src/PublicKey.h b/src/PublicKey.h index cde58cab8eb..6a1e3d17855 100644 --- a/src/PublicKey.h +++ b/src/PublicKey.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Result.h b/src/Result.h index 347a3b10f43..456e1b2feac 100644 --- a/src/Result.h +++ b/src/Result.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Ronin/Entry.h b/src/Ronin/Entry.h index b9b5ab2c2d9..eeffca63f40 100644 --- a/src/Ronin/Entry.h +++ b/src/Ronin/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Solana/AccountMeta.h b/src/Solana/AccountMeta.h index a8f935a907a..3f748d88686 100644 --- a/src/Solana/AccountMeta.h +++ b/src/Solana/AccountMeta.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Solana/Address.cpp b/src/Solana/Address.cpp index 25b6699e41c..1cd08d6443d 100644 --- a/src/Solana/Address.cpp +++ b/src/Solana/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include "Program.h" diff --git a/src/Solana/Address.h b/src/Solana/Address.h index 97b90f3dfd0..d86bd6afd4f 100644 --- a/src/Solana/Address.h +++ b/src/Solana/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Solana/AddressLookupTable.h b/src/Solana/AddressLookupTable.h index 2330781f6a9..ae759dc1cda 100644 --- a/src/Solana/AddressLookupTable.h +++ b/src/Solana/AddressLookupTable.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Solana/CompiledInstruction.cpp b/src/Solana/CompiledInstruction.cpp index f2e368edd1a..ee71633e525 100644 --- a/src/Solana/CompiledInstruction.cpp +++ b/src/Solana/CompiledInstruction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Solana/CompiledInstruction.h" diff --git a/src/Solana/CompiledInstruction.h b/src/Solana/CompiledInstruction.h index 913791ec3fc..63100c4e980 100644 --- a/src/Solana/CompiledInstruction.h +++ b/src/Solana/CompiledInstruction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Solana/Constants.h b/src/Solana/Constants.h index f78f89c224e..8b0ab567db2 100644 --- a/src/Solana/Constants.h +++ b/src/Solana/Constants.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Solana/Encoding.h b/src/Solana/Encoding.h index feb2f6fa391..bcef897df3a 100644 --- a/src/Solana/Encoding.h +++ b/src/Solana/Encoding.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Solana/Entry.cpp b/src/Solana/Entry.cpp index 26c2db5c1ea..b814a12d344 100644 --- a/src/Solana/Entry.cpp +++ b/src/Solana/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Solana/Entry.h b/src/Solana/Entry.h index beb6b1457ab..f4c66fac926 100644 --- a/src/Solana/Entry.h +++ b/src/Solana/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Solana/Instruction.h b/src/Solana/Instruction.h index dfc63e02e5b..83cfc232874 100644 --- a/src/Solana/Instruction.h +++ b/src/Solana/Instruction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Solana/LegacyMessage.cpp b/src/Solana/LegacyMessage.cpp index 863e02edfd5..3ce19808524 100644 --- a/src/Solana/LegacyMessage.cpp +++ b/src/Solana/LegacyMessage.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Solana/LegacyMessage.h" #include "Solana/Encoding.h" diff --git a/src/Solana/LegacyMessage.h b/src/Solana/LegacyMessage.h index 1f1b71aa207..dd9e1a887b7 100644 --- a/src/Solana/LegacyMessage.h +++ b/src/Solana/LegacyMessage.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Solana/MessageHeader.h b/src/Solana/MessageHeader.h index f01c2142305..b73e6c0376e 100644 --- a/src/Solana/MessageHeader.h +++ b/src/Solana/MessageHeader.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Solana/Program.cpp b/src/Solana/Program.cpp index 1605aebba97..c835a220827 100644 --- a/src/Solana/Program.cpp +++ b/src/Solana/Program.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Program.h" #include "Address.h" diff --git a/src/Solana/Program.h b/src/Solana/Program.h index c91be4cb80c..814d37d4c9b 100644 --- a/src/Solana/Program.h +++ b/src/Solana/Program.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Solana/Signer.cpp b/src/Solana/Signer.cpp index 5130dbb0cff..b1677f97f02 100644 --- a/src/Solana/Signer.cpp +++ b/src/Solana/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Address.h" diff --git a/src/Solana/Signer.h b/src/Solana/Signer.h index e73e1844856..77cb86183fb 100644 --- a/src/Solana/Signer.h +++ b/src/Solana/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Solana/Transaction.cpp b/src/Solana/Transaction.cpp index 52ee014c09e..6eb79f5dfce 100644 --- a/src/Solana/Transaction.cpp +++ b/src/Solana/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" #include "Solana/Encoding.h" diff --git a/src/Solana/Transaction.h b/src/Solana/Transaction.h index c933f98a7c9..282397bcf46 100644 --- a/src/Solana/Transaction.h +++ b/src/Solana/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Solana/V0Message.h b/src/Solana/V0Message.h index a3f7c18a165..9c76eb74210 100644 --- a/src/Solana/V0Message.h +++ b/src/Solana/V0Message.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Solana/VersionedMessage.cpp b/src/Solana/VersionedMessage.cpp index 420bd64ea18..2267a0a777f 100644 --- a/src/Solana/VersionedMessage.cpp +++ b/src/Solana/VersionedMessage.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Solana/VersionedMessage.h" #include "Solana/Constants.h" diff --git a/src/Solana/VersionedMessage.h b/src/Solana/VersionedMessage.h index 0428945b812..08c21d37168 100644 --- a/src/Solana/VersionedMessage.h +++ b/src/Solana/VersionedMessage.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Solana/VersionedTransaction.cpp b/src/Solana/VersionedTransaction.cpp index c8ff5b1ff7a..c5679b1ddf3 100644 --- a/src/Solana/VersionedTransaction.cpp +++ b/src/Solana/VersionedTransaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Solana/VersionedTransaction.h" #include "Solana/Encoding.h" diff --git a/src/Solana/VersionedTransaction.h b/src/Solana/VersionedTransaction.h index 01460c79ee1..f7fdd263caf 100644 --- a/src/Solana/VersionedTransaction.h +++ b/src/Solana/VersionedTransaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/StarkEx/MessageSigner.cpp b/src/StarkEx/MessageSigner.cpp index b26106f1264..967bc0bf7e0 100644 --- a/src/StarkEx/MessageSigner.cpp +++ b/src/StarkEx/MessageSigner.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/StarkEx/MessageSigner.h b/src/StarkEx/MessageSigner.h index f1e54819f77..1f1eeffe52e 100644 --- a/src/StarkEx/MessageSigner.h +++ b/src/StarkEx/MessageSigner.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Stellar/Address.cpp b/src/Stellar/Address.cpp index 36dd6efccf2..dbf2261388e 100644 --- a/src/Stellar/Address.cpp +++ b/src/Stellar/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include "Crc.h" diff --git a/src/Stellar/Address.h b/src/Stellar/Address.h index 8cffb878b17..fab7bda7408 100644 --- a/src/Stellar/Address.h +++ b/src/Stellar/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Stellar/Entry.cpp b/src/Stellar/Entry.cpp index aed693bdbc7..56c20599a79 100644 --- a/src/Stellar/Entry.cpp +++ b/src/Stellar/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Stellar/Entry.h b/src/Stellar/Entry.h index 4edd56c7959..aef0e1b8221 100644 --- a/src/Stellar/Entry.h +++ b/src/Stellar/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Stellar/Signer.cpp b/src/Stellar/Signer.cpp index 7defda0268d..01ac21aa776 100644 --- a/src/Stellar/Signer.cpp +++ b/src/Stellar/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Base64.h" diff --git a/src/Stellar/Signer.h b/src/Stellar/Signer.h index 39cf60fc172..3968e354f42 100644 --- a/src/Stellar/Signer.h +++ b/src/Stellar/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once #include "Address.h" diff --git a/src/Sui/Address.cpp b/src/Sui/Address.cpp index 58721b4a83c..bc432805e94 100644 --- a/src/Sui/Address.cpp +++ b/src/Sui/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include "HexCoding.h" diff --git a/src/Sui/Address.h b/src/Sui/Address.h index eae3946f26b..74d5fd6ad27 100644 --- a/src/Sui/Address.h +++ b/src/Sui/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Sui/Entry.cpp b/src/Sui/Entry.cpp index f745a5e87e2..cc537615924 100644 --- a/src/Sui/Entry.cpp +++ b/src/Sui/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Sui/Entry.h b/src/Sui/Entry.h index e2bb9959b91..160520b6db7 100644 --- a/src/Sui/Entry.h +++ b/src/Sui/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Sui/Signer.cpp b/src/Sui/Signer.cpp index 875b23b860b..29984779b65 100644 --- a/src/Sui/Signer.cpp +++ b/src/Sui/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Address.h" diff --git a/src/Sui/Signer.h b/src/Sui/Signer.h index 83573cb054a..a23f59454f9 100644 --- a/src/Sui/Signer.h +++ b/src/Sui/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/THORChain/Entry.h b/src/THORChain/Entry.h index 551b26be00b..863117825d0 100644 --- a/src/THORChain/Entry.h +++ b/src/THORChain/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/THORChain/Swap.cpp b/src/THORChain/Swap.cpp index 92ef7a79c95..260337675e3 100644 --- a/src/THORChain/Swap.cpp +++ b/src/THORChain/Swap.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Swap.h" diff --git a/src/THORChain/Swap.h b/src/THORChain/Swap.h index e6aa32a3001..94f35c5b8a3 100644 --- a/src/THORChain/Swap.h +++ b/src/THORChain/Swap.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/THORChain/TWSwap.cpp b/src/THORChain/TWSwap.cpp index 22915577761..0cdab651fd3 100644 --- a/src/THORChain/TWSwap.cpp +++ b/src/THORChain/TWSwap.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Data.h" #include "Swap.h" diff --git a/src/Tezos/Address.cpp b/src/Tezos/Address.cpp index 7259209b2f3..f079b45ce96 100644 --- a/src/Tezos/Address.cpp +++ b/src/Tezos/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include "BinaryCoding.h" diff --git a/src/Tezos/Address.h b/src/Tezos/Address.h index 7a588266ebb..b2f83cffcbd 100644 --- a/src/Tezos/Address.h +++ b/src/Tezos/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Tezos/BinaryCoding.cpp b/src/Tezos/BinaryCoding.cpp index 805dd47d561..7300157f5ca 100644 --- a/src/Tezos/BinaryCoding.cpp +++ b/src/Tezos/BinaryCoding.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../Base58.h" #include "Data.h" diff --git a/src/Tezos/BinaryCoding.h b/src/Tezos/BinaryCoding.h index 07b5db4cf25..24849592375 100644 --- a/src/Tezos/BinaryCoding.h +++ b/src/Tezos/BinaryCoding.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Tezos/Entry.cpp b/src/Tezos/Entry.cpp index a69af51916e..0b197d93e5a 100644 --- a/src/Tezos/Entry.cpp +++ b/src/Tezos/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Tezos/Entry.h b/src/Tezos/Entry.h index 579522f5feb..3354a2ee744 100644 --- a/src/Tezos/Entry.h +++ b/src/Tezos/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Tezos/Forging.cpp b/src/Tezos/Forging.cpp index 19e3c07b68b..9c7b22b41ec 100644 --- a/src/Tezos/Forging.cpp +++ b/src/Tezos/Forging.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Forging.h" #include "Address.h" diff --git a/src/Tezos/Forging.h b/src/Tezos/Forging.h index 7e8f0231aba..878c9e35880 100644 --- a/src/Tezos/Forging.h +++ b/src/Tezos/Forging.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Tezos/MessageSigner.cpp b/src/Tezos/MessageSigner.cpp index 34e9432b132..b3ed0c797c2 100644 --- a/src/Tezos/MessageSigner.cpp +++ b/src/Tezos/MessageSigner.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/Tezos/MessageSigner.h b/src/Tezos/MessageSigner.h index 7ef7d6b3ed5..98773099c99 100644 --- a/src/Tezos/MessageSigner.h +++ b/src/Tezos/MessageSigner.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Tezos/Michelson.cpp b/src/Tezos/Michelson.cpp index 9d32fa3298a..97fcafcd750 100644 --- a/src/Tezos/Michelson.cpp +++ b/src/Tezos/Michelson.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Michelson.h" diff --git a/src/Tezos/Michelson.h b/src/Tezos/Michelson.h index ec1528dfb21..556383f2e12 100644 --- a/src/Tezos/Michelson.h +++ b/src/Tezos/Michelson.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/Tezos/OperationList.cpp b/src/Tezos/OperationList.cpp index 859a2d85b92..d293999080a 100644 --- a/src/Tezos/OperationList.cpp +++ b/src/Tezos/OperationList.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "OperationList.h" #include "Forging.h" diff --git a/src/Tezos/OperationList.h b/src/Tezos/OperationList.h index 21023cd1385..3c673f50d79 100644 --- a/src/Tezos/OperationList.h +++ b/src/Tezos/OperationList.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Tezos/Signer.cpp b/src/Tezos/Signer.cpp index 0cf6b863006..bd61436b4d7 100644 --- a/src/Tezos/Signer.cpp +++ b/src/Tezos/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "OperationList.h" diff --git a/src/Tezos/Signer.h b/src/Tezos/Signer.h index 857875492cc..1ec492b7625 100644 --- a/src/Tezos/Signer.h +++ b/src/Tezos/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/TheOpenNetwork/Address.cpp b/src/TheOpenNetwork/Address.cpp index 28d35e2c40b..d8f0a194455 100644 --- a/src/TheOpenNetwork/Address.cpp +++ b/src/TheOpenNetwork/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" diff --git a/src/TheOpenNetwork/Address.h b/src/TheOpenNetwork/Address.h index de61607cc01..f830de83877 100644 --- a/src/TheOpenNetwork/Address.h +++ b/src/TheOpenNetwork/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/TheOpenNetwork/Entry.cpp b/src/TheOpenNetwork/Entry.cpp index aa4ba83143f..b35c1c0e886 100644 --- a/src/TheOpenNetwork/Entry.cpp +++ b/src/TheOpenNetwork/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/TheOpenNetwork/Entry.h b/src/TheOpenNetwork/Entry.h index 3f3b91b6e38..cdcf623681d 100644 --- a/src/TheOpenNetwork/Entry.h +++ b/src/TheOpenNetwork/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/TheOpenNetwork/Message.cpp b/src/TheOpenNetwork/Message.cpp index 41b890466f9..05321503dc4 100644 --- a/src/TheOpenNetwork/Message.cpp +++ b/src/TheOpenNetwork/Message.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Message.h" diff --git a/src/TheOpenNetwork/Message.h b/src/TheOpenNetwork/Message.h index 928223d3216..cdbba14e416 100644 --- a/src/TheOpenNetwork/Message.h +++ b/src/TheOpenNetwork/Message.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/TheOpenNetwork/Payloads.cpp b/src/TheOpenNetwork/Payloads.cpp index dcd3151b803..460de3fa919 100644 --- a/src/TheOpenNetwork/Payloads.cpp +++ b/src/TheOpenNetwork/Payloads.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Payloads.h" diff --git a/src/TheOpenNetwork/Payloads.h b/src/TheOpenNetwork/Payloads.h index 6f2b0274c14..e65319efac0 100644 --- a/src/TheOpenNetwork/Payloads.h +++ b/src/TheOpenNetwork/Payloads.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Everscale/CommonTON/CellBuilder.h" diff --git a/src/TheOpenNetwork/Signer.cpp b/src/TheOpenNetwork/Signer.cpp index d7f18f1c98f..2e946bf9904 100644 --- a/src/TheOpenNetwork/Signer.cpp +++ b/src/TheOpenNetwork/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" diff --git a/src/TheOpenNetwork/Signer.h b/src/TheOpenNetwork/Signer.h index e18a49df046..656bb2ed7aa 100644 --- a/src/TheOpenNetwork/Signer.h +++ b/src/TheOpenNetwork/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/TheOpenNetwork/WorkchainType.h b/src/TheOpenNetwork/WorkchainType.h index 313e0c968fa..3947f93f21a 100644 --- a/src/TheOpenNetwork/WorkchainType.h +++ b/src/TheOpenNetwork/WorkchainType.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/TheOpenNetwork/wallet/Wallet.cpp b/src/TheOpenNetwork/wallet/Wallet.cpp index 717b9d49b91..409f6aba187 100644 --- a/src/TheOpenNetwork/wallet/Wallet.cpp +++ b/src/TheOpenNetwork/wallet/Wallet.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Wallet.h" diff --git a/src/TheOpenNetwork/wallet/Wallet.h b/src/TheOpenNetwork/wallet/Wallet.h index c18d1088031..356d2be1d4e 100644 --- a/src/TheOpenNetwork/wallet/Wallet.h +++ b/src/TheOpenNetwork/wallet/Wallet.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/TheOpenNetwork/wallet/WalletV4R2.cpp b/src/TheOpenNetwork/wallet/WalletV4R2.cpp index 9e01a4a2950..bb96f5a2823 100644 --- a/src/TheOpenNetwork/wallet/WalletV4R2.cpp +++ b/src/TheOpenNetwork/wallet/WalletV4R2.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "WalletV4R2.h" diff --git a/src/TheOpenNetwork/wallet/WalletV4R2.h b/src/TheOpenNetwork/wallet/WalletV4R2.h index f965db84385..e77384a9eac 100644 --- a/src/TheOpenNetwork/wallet/WalletV4R2.h +++ b/src/TheOpenNetwork/wallet/WalletV4R2.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Theta/Coins.h b/src/Theta/Coins.h index a7fb326b348..33fdd8b4b48 100644 --- a/src/Theta/Coins.h +++ b/src/Theta/Coins.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Theta/Entry.cpp b/src/Theta/Entry.cpp index 803877dc1e3..f8cff52113e 100644 --- a/src/Theta/Entry.cpp +++ b/src/Theta/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" #include "Ethereum/Address.h" diff --git a/src/Theta/Entry.h b/src/Theta/Entry.h index bcb79ac69fe..1d6beb82a5b 100644 --- a/src/Theta/Entry.h +++ b/src/Theta/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Theta/Signer.cpp b/src/Theta/Signer.cpp index 050deff969b..020965d374a 100755 --- a/src/Theta/Signer.cpp +++ b/src/Theta/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" diff --git a/src/Theta/Signer.h b/src/Theta/Signer.h index fb7caf13c2e..d4608c6e3df 100644 --- a/src/Theta/Signer.h +++ b/src/Theta/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Theta/Transaction.cpp b/src/Theta/Transaction.cpp index 939c54aa7b3..c1cb17c11b8 100644 --- a/src/Theta/Transaction.cpp +++ b/src/Theta/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" diff --git a/src/Theta/Transaction.h b/src/Theta/Transaction.h index 5e1a1973d12..7068cc5fa6e 100644 --- a/src/Theta/Transaction.h +++ b/src/Theta/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/TransactionCompiler.cpp b/src/TransactionCompiler.cpp index 5289bffb5b1..b9ee21bb760 100644 --- a/src/TransactionCompiler.cpp +++ b/src/TransactionCompiler.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TransactionCompiler.h" diff --git a/src/TransactionCompiler.h b/src/TransactionCompiler.h index d4963d4c311..0c12b4dd23c 100644 --- a/src/TransactionCompiler.h +++ b/src/TransactionCompiler.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Tron/Address.cpp b/src/Tron/Address.cpp index cbf46146b46..1a66a7f9ccd 100644 --- a/src/Tron/Address.cpp +++ b/src/Tron/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" diff --git a/src/Tron/Address.h b/src/Tron/Address.h index 4c80204a07a..f19a4a3c164 100644 --- a/src/Tron/Address.h +++ b/src/Tron/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Tron/Entry.cpp b/src/Tron/Entry.cpp index d3332240f5c..7cf7f756ca5 100644 --- a/src/Tron/Entry.cpp +++ b/src/Tron/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" #include "Address.h" diff --git a/src/Tron/Entry.h b/src/Tron/Entry.h index a25f876c253..e023427340c 100644 --- a/src/Tron/Entry.h +++ b/src/Tron/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Tron/MessageSigner.cpp b/src/Tron/MessageSigner.cpp index 36bccf47453..8fe0d8619a6 100644 --- a/src/Tron/MessageSigner.cpp +++ b/src/Tron/MessageSigner.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/Tron/MessageSigner.h b/src/Tron/MessageSigner.h index eca2845e653..e6e7bf2a9a9 100644 --- a/src/Tron/MessageSigner.h +++ b/src/Tron/MessageSigner.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Tron/Serialization.cpp b/src/Tron/Serialization.cpp index 55f95775fc0..36e92b62878 100644 --- a/src/Tron/Serialization.cpp +++ b/src/Tron/Serialization.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Serialization.h" #include "../HexCoding.h" diff --git a/src/Tron/Serialization.h b/src/Tron/Serialization.h index b434e9f2c74..cb807524fcd 100644 --- a/src/Tron/Serialization.h +++ b/src/Tron/Serialization.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Tron/Signer.cpp b/src/Tron/Signer.cpp index 735925cbf8e..64ebaa22153 100644 --- a/src/Tron/Signer.cpp +++ b/src/Tron/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" diff --git a/src/Tron/Signer.h b/src/Tron/Signer.h index 3f2fa7f1c65..1d3063378af 100644 --- a/src/Tron/Signer.h +++ b/src/Tron/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/VeChain/Clause.h b/src/VeChain/Clause.h index 14191395097..816647d49d5 100644 --- a/src/VeChain/Clause.h +++ b/src/VeChain/Clause.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/VeChain/Entry.cpp b/src/VeChain/Entry.cpp index c9724dfae75..312007e942a 100644 --- a/src/VeChain/Entry.cpp +++ b/src/VeChain/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" #include diff --git a/src/VeChain/Entry.h b/src/VeChain/Entry.h index 8a06ae5434c..c15d4896b03 100644 --- a/src/VeChain/Entry.h +++ b/src/VeChain/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/VeChain/Signer.cpp b/src/VeChain/Signer.cpp index 23c4f4b3878..78ddc8a81e6 100644 --- a/src/VeChain/Signer.cpp +++ b/src/VeChain/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" diff --git a/src/VeChain/Signer.h b/src/VeChain/Signer.h index 550810961e0..2e3422a3f9f 100644 --- a/src/VeChain/Signer.h +++ b/src/VeChain/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/VeChain/Transaction.cpp b/src/VeChain/Transaction.cpp index 48030e74f77..828d662f79e 100644 --- a/src/VeChain/Transaction.cpp +++ b/src/VeChain/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" diff --git a/src/VeChain/Transaction.h b/src/VeChain/Transaction.h index ae5e0a6fcff..3851ac2f7dc 100644 --- a/src/VeChain/Transaction.h +++ b/src/VeChain/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Verge/Entry.cpp b/src/Verge/Entry.cpp index d0a13896040..fea63ca93f8 100644 --- a/src/Verge/Entry.cpp +++ b/src/Verge/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Verge/Entry.h b/src/Verge/Entry.h index 483e8e51448..f3bb033d560 100644 --- a/src/Verge/Entry.h +++ b/src/Verge/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Verge/Signer.cpp b/src/Verge/Signer.cpp index 6110e68bcfc..72ce2ad9c43 100644 --- a/src/Verge/Signer.cpp +++ b/src/Verge/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Bitcoin/TransactionSigner.h" diff --git a/src/Verge/Signer.h b/src/Verge/Signer.h index c2912972b58..659ab1a894b 100644 --- a/src/Verge/Signer.h +++ b/src/Verge/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Verge/Transaction.cpp b/src/Verge/Transaction.cpp index db3461e012f..e8469f7ba61 100644 --- a/src/Verge/Transaction.cpp +++ b/src/Verge/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" #include "../BinaryCoding.h" diff --git a/src/Verge/Transaction.h b/src/Verge/Transaction.h index 022efdfd0cc..06097df9695 100644 --- a/src/Verge/Transaction.h +++ b/src/Verge/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Verge/TransactionBuilder.h b/src/Verge/TransactionBuilder.h index 51f85b0581b..a1532dea9ed 100644 --- a/src/Verge/TransactionBuilder.h +++ b/src/Verge/TransactionBuilder.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Wasm.h b/src/Wasm.h index 77071075ce0..666c911d1e0 100644 --- a/src/Wasm.h +++ b/src/Wasm.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #ifndef __USE_WASM #define __USE_WASM diff --git a/src/Waves/Address.cpp b/src/Waves/Address.cpp index 3f241010cde..9b9ab7a27fa 100644 --- a/src/Waves/Address.cpp +++ b/src/Waves/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" diff --git a/src/Waves/Address.h b/src/Waves/Address.h index 847c05f4c79..376d05057a8 100644 --- a/src/Waves/Address.h +++ b/src/Waves/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Waves/BinaryCoding.h b/src/Waves/BinaryCoding.h index 9be91647a6e..51e6b126c7d 100644 --- a/src/Waves/BinaryCoding.h +++ b/src/Waves/BinaryCoding.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Waves/Entry.cpp b/src/Waves/Entry.cpp index 2659382e3d1..1b166e3fb41 100644 --- a/src/Waves/Entry.cpp +++ b/src/Waves/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Waves/Entry.h b/src/Waves/Entry.h index bd5d460d8fe..ac78c44e3fa 100644 --- a/src/Waves/Entry.h +++ b/src/Waves/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Waves/Signer.cpp b/src/Waves/Signer.cpp index 665e1caedfc..2d4d6955236 100644 --- a/src/Waves/Signer.cpp +++ b/src/Waves/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" diff --git a/src/Waves/Signer.h b/src/Waves/Signer.h index 0f86a9c55cb..00cd23b0726 100644 --- a/src/Waves/Signer.h +++ b/src/Waves/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Waves/Transaction.cpp b/src/Waves/Transaction.cpp index 34f86821bfd..6e676538fd4 100644 --- a/src/Waves/Transaction.cpp +++ b/src/Waves/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" #include "BinaryCoding.h" diff --git a/src/Waves/Transaction.h b/src/Waves/Transaction.h index cf1f4ba1316..5f775df7faa 100644 --- a/src/Waves/Transaction.h +++ b/src/Waves/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/WebAuthn.cpp b/src/WebAuthn.cpp index 5ced2a3f5aa..83fa598cea5 100644 --- a/src/WebAuthn.cpp +++ b/src/WebAuthn.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Cbor.h" diff --git a/src/WebAuthn.h b/src/WebAuthn.h index 567d446398a..67291d97f91 100644 --- a/src/WebAuthn.h +++ b/src/WebAuthn.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/XRP/Address.cpp b/src/XRP/Address.cpp index 39bf94e975e..6a24147050b 100644 --- a/src/XRP/Address.cpp +++ b/src/XRP/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" #include "../Base58.h" diff --git a/src/XRP/Address.h b/src/XRP/Address.h index 7b464091d43..c03f3a0a746 100644 --- a/src/XRP/Address.h +++ b/src/XRP/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/XRP/BinaryCoding.h b/src/XRP/BinaryCoding.h index db01d934b5a..ef1cb8d1cce 100644 --- a/src/XRP/BinaryCoding.h +++ b/src/XRP/BinaryCoding.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/XRP/Entry.cpp b/src/XRP/Entry.cpp index 8c7d27314f2..ea7274ef24d 100644 --- a/src/XRP/Entry.cpp +++ b/src/XRP/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/XRP/Entry.h b/src/XRP/Entry.h index 27a74ec33ea..394550cdafe 100644 --- a/src/XRP/Entry.h +++ b/src/XRP/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/XRP/Signer.cpp b/src/XRP/Signer.cpp index c60afac81eb..0a8ff4ff0f2 100644 --- a/src/XRP/Signer.cpp +++ b/src/XRP/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "../BinaryCoding.h" diff --git a/src/XRP/Signer.h b/src/XRP/Signer.h index e8953133cb8..4721280bf0e 100644 --- a/src/XRP/Signer.h +++ b/src/XRP/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/XRP/Transaction.cpp b/src/XRP/Transaction.cpp index 52dbc3b0cff..1ebf5d02732 100644 --- a/src/XRP/Transaction.cpp +++ b/src/XRP/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "BinaryCoding.h" #include "Transaction.h" diff --git a/src/XRP/Transaction.h b/src/XRP/Transaction.h index 3058a8c856d..ca453c47919 100644 --- a/src/XRP/Transaction.h +++ b/src/XRP/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/XRP/XAddress.cpp b/src/XRP/XAddress.cpp index 19cdf8ec77d..95bc26507b3 100644 --- a/src/XRP/XAddress.cpp +++ b/src/XRP/XAddress.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "XAddress.h" diff --git a/src/XRP/XAddress.h b/src/XRP/XAddress.h index 675e345a25e..424baed0333 100644 --- a/src/XRP/XAddress.h +++ b/src/XRP/XAddress.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Zcash/Entry.cpp b/src/Zcash/Entry.cpp index 143bb432756..50a45f9f10d 100644 --- a/src/Zcash/Entry.cpp +++ b/src/Zcash/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Zcash/Entry.h b/src/Zcash/Entry.h index 78c2f587970..2b43578fbaf 100644 --- a/src/Zcash/Entry.h +++ b/src/Zcash/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Zcash/Signer.cpp b/src/Zcash/Signer.cpp index 05c09687a46..19099969c65 100644 --- a/src/Zcash/Signer.cpp +++ b/src/Zcash/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Bitcoin/TransactionSigner.h" diff --git a/src/Zcash/Signer.h b/src/Zcash/Signer.h index 77cdbec2739..cc83b5dd897 100644 --- a/src/Zcash/Signer.h +++ b/src/Zcash/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Zcash/TAddress.h b/src/Zcash/TAddress.h index cd8683e9541..0cef48d16d7 100644 --- a/src/Zcash/TAddress.h +++ b/src/Zcash/TAddress.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Zcash/Transaction.cpp b/src/Zcash/Transaction.cpp index d7caf474138..a5432ac2a16 100644 --- a/src/Zcash/Transaction.cpp +++ b/src/Zcash/Transaction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Transaction.h" diff --git a/src/Zcash/Transaction.h b/src/Zcash/Transaction.h index 86b0e3c4a1f..5c0dd6bd154 100644 --- a/src/Zcash/Transaction.h +++ b/src/Zcash/Transaction.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Zcash/TransactionBuilder.h b/src/Zcash/TransactionBuilder.h index 62c9e32081d..d3940801658 100644 --- a/src/Zcash/TransactionBuilder.h +++ b/src/Zcash/TransactionBuilder.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Zen/Address.h b/src/Zen/Address.h index 617872a2401..b387c8c5c15 100644 --- a/src/Zen/Address.h +++ b/src/Zen/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Zen/Entry.cpp b/src/Zen/Entry.cpp index 19ea562e2ba..09087ca4b20 100644 --- a/src/Zen/Entry.cpp +++ b/src/Zen/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Zen/Entry.h b/src/Zen/Entry.h index ce15dd8b8dc..5e89746c89d 100644 --- a/src/Zen/Entry.h +++ b/src/Zen/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Zen/Signer.cpp b/src/Zen/Signer.cpp index 4ea7261b937..405ec592248 100644 --- a/src/Zen/Signer.cpp +++ b/src/Zen/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Bitcoin/TransactionSigner.h" diff --git a/src/Zen/Signer.h b/src/Zen/Signer.h index a2031e1a072..2e4476e4e8e 100644 --- a/src/Zen/Signer.h +++ b/src/Zen/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Zen/TransactionBuilder.h b/src/Zen/TransactionBuilder.h index fddea3bfc48..b3a657f2e6c 100644 --- a/src/Zen/TransactionBuilder.h +++ b/src/Zen/TransactionBuilder.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Zilliqa/Address.cpp b/src/Zilliqa/Address.cpp index 6612ef21523..8c0c2ab6d0d 100644 --- a/src/Zilliqa/Address.cpp +++ b/src/Zilliqa/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" diff --git a/src/Zilliqa/Address.h b/src/Zilliqa/Address.h index 7184e5988a3..a46d6a934f3 100644 --- a/src/Zilliqa/Address.h +++ b/src/Zilliqa/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Zilliqa/AddressChecksum.cpp b/src/Zilliqa/AddressChecksum.cpp index 0f52452a3d0..60f65f8c61f 100644 --- a/src/Zilliqa/AddressChecksum.cpp +++ b/src/Zilliqa/AddressChecksum.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "AddressChecksum.h" diff --git a/src/Zilliqa/AddressChecksum.h b/src/Zilliqa/AddressChecksum.h index bf0ed8df784..511ab562e16 100644 --- a/src/Zilliqa/AddressChecksum.h +++ b/src/Zilliqa/AddressChecksum.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Zilliqa/Entry.cpp b/src/Zilliqa/Entry.cpp index b8216779566..246456d6bb5 100644 --- a/src/Zilliqa/Entry.cpp +++ b/src/Zilliqa/Entry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Entry.h" diff --git a/src/Zilliqa/Entry.h b/src/Zilliqa/Entry.h index ad1a6e00793..fed967de694 100644 --- a/src/Zilliqa/Entry.h +++ b/src/Zilliqa/Entry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/Zilliqa/Signer.cpp b/src/Zilliqa/Signer.cpp index 3310a5d9920..cf2003ec4f7 100644 --- a/src/Zilliqa/Signer.cpp +++ b/src/Zilliqa/Signer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Signer.h" #include "Address.h" diff --git a/src/Zilliqa/Signer.h b/src/Zilliqa/Signer.h index 5b60f30a17f..46eb12168f9 100644 --- a/src/Zilliqa/Signer.h +++ b/src/Zilliqa/Signer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/algorithm/erase.h b/src/algorithm/erase.h index 5847a4e9ac4..ed3a0bf9fd4 100644 --- a/src/algorithm/erase.h +++ b/src/algorithm/erase.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/algorithm/sort_copy.h b/src/algorithm/sort_copy.h index 277aecaf73c..eb1e7b3bf31 100644 --- a/src/algorithm/sort_copy.h +++ b/src/algorithm/sort_copy.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/algorithm/string.hpp b/src/algorithm/string.hpp index 18576071dcc..8d5fc8145a4 100644 --- a/src/algorithm/string.hpp +++ b/src/algorithm/string.hpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/algorithm/to_array.h b/src/algorithm/to_array.h index 9c44c081fad..b6588da79af 100644 --- a/src/algorithm/to_array.h +++ b/src/algorithm/to_array.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/concepts/tw_concepts.h b/src/concepts/tw_concepts.h index d2a5d6860d5..fd31b0e31da 100644 --- a/src/concepts/tw_concepts.h +++ b/src/concepts/tw_concepts.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/interface/TWAES.cpp b/src/interface/TWAES.cpp index af7d228f65b..7b63e735015 100644 --- a/src/interface/TWAES.cpp +++ b/src/interface/TWAES.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWAccount.cpp b/src/interface/TWAccount.cpp index 56d383b10d7..b29be55591d 100644 --- a/src/interface/TWAccount.cpp +++ b/src/interface/TWAccount.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWAnyAddress.cpp b/src/interface/TWAnyAddress.cpp index 40fe3c52d64..5c596625cfe 100644 --- a/src/interface/TWAnyAddress.cpp +++ b/src/interface/TWAnyAddress.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/interface/TWAnySigner.cpp b/src/interface/TWAnySigner.cpp index d018a3c0cf2..97cbfc631e3 100644 --- a/src/interface/TWAnySigner.cpp +++ b/src/interface/TWAnySigner.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWAsnParser.cpp b/src/interface/TWAsnParser.cpp index f1e1b8e04f7..1dbd9b5a3ad 100644 --- a/src/interface/TWAsnParser.cpp +++ b/src/interface/TWAsnParser.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWBarz.cpp b/src/interface/TWBarz.cpp index b3261247494..cbfdea41700 100644 --- a/src/interface/TWBarz.cpp +++ b/src/interface/TWBarz.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/interface/TWBase32.cpp b/src/interface/TWBase32.cpp index 992d7b600e9..1e71325af10 100644 --- a/src/interface/TWBase32.cpp +++ b/src/interface/TWBase32.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWBase58.cpp b/src/interface/TWBase58.cpp index 4545f088019..b8a05c34f1c 100644 --- a/src/interface/TWBase58.cpp +++ b/src/interface/TWBase58.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWBase64.cpp b/src/interface/TWBase64.cpp index ae1fa0af4be..0009dab4b06 100644 --- a/src/interface/TWBase64.cpp +++ b/src/interface/TWBase64.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWBitcoinAddress.cpp b/src/interface/TWBitcoinAddress.cpp index 3e70ee3e6ab..e81dd75a32d 100644 --- a/src/interface/TWBitcoinAddress.cpp +++ b/src/interface/TWBitcoinAddress.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../Base58.h" #include "../Bitcoin/Address.h" diff --git a/src/interface/TWBitcoinFee.cpp b/src/interface/TWBitcoinFee.cpp index 6250d46a580..2e76bc10248 100644 --- a/src/interface/TWBitcoinFee.cpp +++ b/src/interface/TWBitcoinFee.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "Bitcoin/Transaction.h" diff --git a/src/interface/TWBitcoinMessageSigner.cpp b/src/interface/TWBitcoinMessageSigner.cpp index 01243bad9ac..03b1bc5f93c 100644 --- a/src/interface/TWBitcoinMessageSigner.cpp +++ b/src/interface/TWBitcoinMessageSigner.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWBitcoinScript.cpp b/src/interface/TWBitcoinScript.cpp index bd77d7d310b..aa48ac4cdbe 100644 --- a/src/interface/TWBitcoinScript.cpp +++ b/src/interface/TWBitcoinScript.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "../Bitcoin/Script.h" diff --git a/src/interface/TWBitcoinSigHashType.cpp b/src/interface/TWBitcoinSigHashType.cpp index bfcaf9939bb..131b40c050b 100644 --- a/src/interface/TWBitcoinSigHashType.cpp +++ b/src/interface/TWBitcoinSigHashType.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "../Bitcoin/SigHashType.h" diff --git a/src/interface/TWCardano.cpp b/src/interface/TWCardano.cpp index 1449aa6379f..798f8392452 100644 --- a/src/interface/TWCardano.cpp +++ b/src/interface/TWCardano.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWCoinType.cpp b/src/interface/TWCoinType.cpp index c6d31860ab4..908edfc73b9 100644 --- a/src/interface/TWCoinType.cpp +++ b/src/interface/TWCoinType.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/interface/TWData.cpp b/src/interface/TWData.cpp index 4379ced2f43..3cb2d364b88 100644 --- a/src/interface/TWData.cpp +++ b/src/interface/TWData.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/interface/TWDataVector.cpp b/src/interface/TWDataVector.cpp index d17148f8cfd..73985924849 100644 --- a/src/interface/TWDataVector.cpp +++ b/src/interface/TWDataVector.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWDerivationPath.cpp b/src/interface/TWDerivationPath.cpp index b52ca0eff58..0b1c853abdd 100644 --- a/src/interface/TWDerivationPath.cpp +++ b/src/interface/TWDerivationPath.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/interface/TWDerivationPathIndex.cpp b/src/interface/TWDerivationPathIndex.cpp index ba06751d370..6f0a14ea9b2 100644 --- a/src/interface/TWDerivationPathIndex.cpp +++ b/src/interface/TWDerivationPathIndex.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/interface/TWEthereum.cpp b/src/interface/TWEthereum.cpp index ca3299a87be..2c492c48e3f 100644 --- a/src/interface/TWEthereum.cpp +++ b/src/interface/TWEthereum.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Data.h" #include "Ethereum/EIP1014.h" diff --git a/src/interface/TWEthereumAbi.cpp b/src/interface/TWEthereumAbi.cpp index e8eceb53344..829eb594a33 100644 --- a/src/interface/TWEthereumAbi.cpp +++ b/src/interface/TWEthereumAbi.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/interface/TWEthereumAbiFunction.cpp b/src/interface/TWEthereumAbiFunction.cpp index 9a544c533e8..db792629ee0 100644 --- a/src/interface/TWEthereumAbiFunction.cpp +++ b/src/interface/TWEthereumAbiFunction.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWEthereumAbiValue.cpp b/src/interface/TWEthereumAbiValue.cpp index 12ed7cac7da..828521133d4 100644 --- a/src/interface/TWEthereumAbiValue.cpp +++ b/src/interface/TWEthereumAbiValue.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWEthereumMessageSigner.cpp b/src/interface/TWEthereumMessageSigner.cpp index ee0aafd5009..9d40be9e34c 100644 --- a/src/interface/TWEthereumMessageSigner.cpp +++ b/src/interface/TWEthereumMessageSigner.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWEthereumRlp.cpp b/src/interface/TWEthereumRlp.cpp index 4c405d54882..16bb66dae32 100644 --- a/src/interface/TWEthereumRlp.cpp +++ b/src/interface/TWEthereumRlp.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWFIOAccount.cpp b/src/interface/TWFIOAccount.cpp index 575e96d0d80..378113fb24c 100644 --- a/src/interface/TWFIOAccount.cpp +++ b/src/interface/TWFIOAccount.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWFilecoinAddressConverter.cpp b/src/interface/TWFilecoinAddressConverter.cpp index 46e3cd6b4dc..10d6722b27a 100644 --- a/src/interface/TWFilecoinAddressConverter.cpp +++ b/src/interface/TWFilecoinAddressConverter.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/interface/TWGroestlcoinAddress.cpp b/src/interface/TWGroestlcoinAddress.cpp index 300d18055e2..164592aba55 100644 --- a/src/interface/TWGroestlcoinAddress.cpp +++ b/src/interface/TWGroestlcoinAddress.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/interface/TWHDVersion.cpp b/src/interface/TWHDVersion.cpp index abd8b2929ba..5bfeb6d4ccb 100644 --- a/src/interface/TWHDVersion.cpp +++ b/src/interface/TWHDVersion.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWHDWallet.cpp b/src/interface/TWHDWallet.cpp index c874b1ae6f2..bce0e819a6b 100644 --- a/src/interface/TWHDWallet.cpp +++ b/src/interface/TWHDWallet.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWHash.cpp b/src/interface/TWHash.cpp index 26cac076adf..b66adc07fb3 100644 --- a/src/interface/TWHash.cpp +++ b/src/interface/TWHash.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "../Hash.h" diff --git a/src/interface/TWMnemonic.cpp b/src/interface/TWMnemonic.cpp index 630a3823490..e5694b3c9a4 100644 --- a/src/interface/TWMnemonic.cpp +++ b/src/interface/TWMnemonic.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWNEARAccount.cpp b/src/interface/TWNEARAccount.cpp index 588a303ce5c..2c0f55ec0f9 100644 --- a/src/interface/TWNEARAccount.cpp +++ b/src/interface/TWNEARAccount.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWNervosAddress.cpp b/src/interface/TWNervosAddress.cpp index 68ca562fb93..925be23fdac 100644 --- a/src/interface/TWNervosAddress.cpp +++ b/src/interface/TWNervosAddress.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TrustWalletCore/TWData.h" #include "../Base58.h" diff --git a/src/interface/TWPBKDF2.cpp b/src/interface/TWPBKDF2.cpp index 01ea4f00186..3fae3225482 100644 --- a/src/interface/TWPBKDF2.cpp +++ b/src/interface/TWPBKDF2.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/interface/TWPrivateKey.cpp b/src/interface/TWPrivateKey.cpp index 72b28ea7714..37483dca65d 100644 --- a/src/interface/TWPrivateKey.cpp +++ b/src/interface/TWPrivateKey.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../PrivateKey.h" #include "../PublicKey.h" diff --git a/src/interface/TWPublicKey.cpp b/src/interface/TWPublicKey.cpp index 114abec5c1c..6459b947a05 100644 --- a/src/interface/TWPublicKey.cpp +++ b/src/interface/TWPublicKey.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWRippleXAddress.cpp b/src/interface/TWRippleXAddress.cpp index 93fbf42ffd5..4c74dae0fb6 100644 --- a/src/interface/TWRippleXAddress.cpp +++ b/src/interface/TWRippleXAddress.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../XRP/XAddress.h" diff --git a/src/interface/TWSegwitAddress.cpp b/src/interface/TWSegwitAddress.cpp index e9e891d7627..5d74bdb3716 100644 --- a/src/interface/TWSegwitAddress.cpp +++ b/src/interface/TWSegwitAddress.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../Bitcoin/SegwitAddress.h" diff --git a/src/interface/TWSolanaAddress.cpp b/src/interface/TWSolanaAddress.cpp index 000cf0d848f..02b3ad631f1 100644 --- a/src/interface/TWSolanaAddress.cpp +++ b/src/interface/TWSolanaAddress.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Solana/Address.h" #include diff --git a/src/interface/TWStarkExMessageSigner.cpp b/src/interface/TWStarkExMessageSigner.cpp index caca3ac8e55..a17df64afc6 100644 --- a/src/interface/TWStarkExMessageSigner.cpp +++ b/src/interface/TWStarkExMessageSigner.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWStarkWare.cpp b/src/interface/TWStarkWare.cpp index fa1a41bb499..a8f36656b12 100644 --- a/src/interface/TWStarkWare.cpp +++ b/src/interface/TWStarkWare.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/interface/TWStoredKey.cpp b/src/interface/TWStoredKey.cpp index f06c11cbc03..b46ce5de1fa 100644 --- a/src/interface/TWStoredKey.cpp +++ b/src/interface/TWStoredKey.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWString+Hex.cpp b/src/interface/TWString+Hex.cpp index 8a62347b97f..f2802f8c6a8 100644 --- a/src/interface/TWString+Hex.cpp +++ b/src/interface/TWString+Hex.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/interface/TWString.cpp b/src/interface/TWString.cpp index ac118e581a0..3641660b8d8 100644 --- a/src/interface/TWString.cpp +++ b/src/interface/TWString.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWTezosMessageSigner.cpp b/src/interface/TWTezosMessageSigner.cpp index 91b3352ec7f..af09a2a2e77 100644 --- a/src/interface/TWTezosMessageSigner.cpp +++ b/src/interface/TWTezosMessageSigner.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "Tezos/MessageSigner.h" diff --git a/src/interface/TWTransactionCompiler.cpp b/src/interface/TWTransactionCompiler.cpp index dffd88fbb5e..2a55cf129dc 100644 --- a/src/interface/TWTransactionCompiler.cpp +++ b/src/interface/TWTransactionCompiler.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWTronMessageSigner.cpp b/src/interface/TWTronMessageSigner.cpp index 8fab380810e..5fe47eba53b 100644 --- a/src/interface/TWTronMessageSigner.cpp +++ b/src/interface/TWTronMessageSigner.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "Tron/MessageSigner.h" diff --git a/src/interface/TWWalletConnectRequest.cpp b/src/interface/TWWalletConnectRequest.cpp index 621f3c9cbcd..37fc49cbc87 100644 --- a/src/interface/TWWalletConnectRequest.cpp +++ b/src/interface/TWWalletConnectRequest.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/src/interface/TWWebAuthn.cpp b/src/interface/TWWebAuthn.cpp index ad1829e0c14..7291ec0c7b8 100644 --- a/src/interface/TWWebAuthn.cpp +++ b/src/interface/TWWebAuthn.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/src/memory/memzero_wrapper.h b/src/memory/memzero_wrapper.h index 1931b040f84..71af4291e78 100644 --- a/src/memory/memzero_wrapper.h +++ b/src/memory/memzero_wrapper.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/operators/equality_comparable.h b/src/operators/equality_comparable.h index 561b6ddc8d6..4309afbc1ff 100644 --- a/src/operators/equality_comparable.h +++ b/src/operators/equality_comparable.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/proto/Aptos.proto b/src/proto/Aptos.proto index 774bce9f2fd..6323e2e16d1 100644 --- a/src/proto/Aptos.proto +++ b/src/proto/Aptos.proto @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. syntax = "proto3"; diff --git a/src/proto/Barz.proto b/src/proto/Barz.proto index 20853f04ad4..b0be650c0f3 100644 --- a/src/proto/Barz.proto +++ b/src/proto/Barz.proto @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. syntax = "proto3"; diff --git a/src/proto/Everscale.proto b/src/proto/Everscale.proto index 45d7ab5357d..20ef2644ba8 100644 --- a/src/proto/Everscale.proto +++ b/src/proto/Everscale.proto @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. syntax = "proto3"; diff --git a/src/proto/Hedera.proto b/src/proto/Hedera.proto index 64f01b9f68e..4f331c77d3e 100644 --- a/src/proto/Hedera.proto +++ b/src/proto/Hedera.proto @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. syntax = "proto3"; diff --git a/src/proto/InternetComputer.proto b/src/proto/InternetComputer.proto index 00cd513baf0..f76380f21da 100644 --- a/src/proto/InternetComputer.proto +++ b/src/proto/InternetComputer.proto @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. syntax = "proto3"; diff --git a/src/proto/LiquidStaking.proto b/src/proto/LiquidStaking.proto index 94905920d92..2193c5d283e 100644 --- a/src/proto/LiquidStaking.proto +++ b/src/proto/LiquidStaking.proto @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. syntax = "proto3"; diff --git a/src/proto/MultiversX.proto b/src/proto/MultiversX.proto index ec160d41d31..02162ef5796 100644 --- a/src/proto/MultiversX.proto +++ b/src/proto/MultiversX.proto @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. syntax = "proto3"; diff --git a/src/proto/Nervos.proto b/src/proto/Nervos.proto index b58a66558bb..f662d81b1ce 100644 --- a/src/proto/Nervos.proto +++ b/src/proto/Nervos.proto @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. syntax = "proto3"; diff --git a/src/proto/Oasis.proto b/src/proto/Oasis.proto index 5a5cdec18ac..cbf406bfe2d 100644 --- a/src/proto/Oasis.proto +++ b/src/proto/Oasis.proto @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. syntax = "proto3"; diff --git a/src/proto/Sui.proto b/src/proto/Sui.proto index 8c033a8bf85..5b11812db41 100644 --- a/src/proto/Sui.proto +++ b/src/proto/Sui.proto @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. syntax = "proto3"; diff --git a/src/proto/TheOpenNetwork.proto b/src/proto/TheOpenNetwork.proto index 490a0c5f351..1547554301b 100644 --- a/src/proto/TheOpenNetwork.proto +++ b/src/proto/TheOpenNetwork.proto @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. syntax = "proto3"; diff --git a/src/rust/RustCoinEntry.cpp b/src/rust/RustCoinEntry.cpp index 497edc57644..870bbe2c076 100644 --- a/src/rust/RustCoinEntry.cpp +++ b/src/rust/RustCoinEntry.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "RustCoinEntry.h" #include "Wrapper.h" diff --git a/src/rust/RustCoinEntry.h b/src/rust/RustCoinEntry.h index 5c481aaa284..371c483883a 100644 --- a/src/rust/RustCoinEntry.h +++ b/src/rust/RustCoinEntry.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/rust/Wrapper.h b/src/rust/Wrapper.h index 3b4f9436f39..dda425dc319 100644 --- a/src/rust/Wrapper.h +++ b/src/rust/Wrapper.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/src/uint256.h b/src/uint256.h index 06f33ae4708..9d5e4f01171 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/swift/Example/Example/ContentView.swift b/swift/Example/Example/ContentView.swift index 6918ce3fb59..b5da0ca723d 100644 --- a/swift/Example/Example/ContentView.swift +++ b/swift/Example/Example/ContentView.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import SwiftUI import WalletCore diff --git a/swift/Example/Example/ExampleApp.swift b/swift/Example/Example/ExampleApp.swift index 938063a069d..f3f442eefcd 100644 --- a/swift/Example/Example/ExampleApp.swift +++ b/swift/Example/Example/ExampleApp.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import SwiftUI diff --git a/swift/Example/ExampleMac/ContentView.swift b/swift/Example/ExampleMac/ContentView.swift index 41e49a49f7a..80981c59ac9 100644 --- a/swift/Example/ExampleMac/ContentView.swift +++ b/swift/Example/ExampleMac/ContentView.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import SwiftUI import WalletCore diff --git a/swift/Example/ExampleMac/ExampleMacApp.swift b/swift/Example/ExampleMac/ExampleMacApp.swift index 911a53f8948..040a390bd88 100644 --- a/swift/Example/ExampleMac/ExampleMacApp.swift +++ b/swift/Example/ExampleMac/ExampleMacApp.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import SwiftUI diff --git a/swift/Example/ExampleTests/ExampleTests.swift b/swift/Example/ExampleTests/ExampleTests.swift index c719046e6d8..427609b0096 100644 --- a/swift/Example/ExampleTests/ExampleTests.swift +++ b/swift/Example/ExampleTests/ExampleTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Playground.playground/Contents.swift b/swift/Playground.playground/Contents.swift index 832b3e213a6..4c886c35bc6 100644 --- a/swift/Playground.playground/Contents.swift +++ b/swift/Playground.playground/Contents.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import UIKit import TrustWalletCore diff --git a/swift/Sources/Dummy.cpp b/swift/Sources/Dummy.cpp index e433c2b6009..d495a10a6b7 100644 --- a/swift/Sources/Dummy.cpp +++ b/swift/Sources/Dummy.cpp @@ -1,7 +1,5 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // Dummy C++ file to force inclusion of the C++ STL when compiling. diff --git a/swift/Sources/Extensions/Account+Codable.swift b/swift/Sources/Extensions/Account+Codable.swift index 39b48c3d0c5..75449402873 100644 --- a/swift/Sources/Extensions/Account+Codable.swift +++ b/swift/Sources/Extensions/Account+Codable.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import Foundation diff --git a/swift/Sources/Extensions/AddressProtocol.swift b/swift/Sources/Extensions/AddressProtocol.swift index 1660d724252..5a522687283 100644 --- a/swift/Sources/Extensions/AddressProtocol.swift +++ b/swift/Sources/Extensions/AddressProtocol.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import Foundation diff --git a/swift/Sources/Extensions/BitcoinAddress+Extension.swift b/swift/Sources/Extensions/BitcoinAddress+Extension.swift index 48b799a6064..eb11bd99cdd 100644 --- a/swift/Sources/Extensions/BitcoinAddress+Extension.swift +++ b/swift/Sources/Extensions/BitcoinAddress+Extension.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import Foundation diff --git a/swift/Sources/Extensions/CoinType+Address.swift b/swift/Sources/Extensions/CoinType+Address.swift index f0dbc2ea6ae..0cb5e2e0fff 100644 --- a/swift/Sources/Extensions/CoinType+Address.swift +++ b/swift/Sources/Extensions/CoinType+Address.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import Foundation diff --git a/swift/Sources/Extensions/Data+Hex.swift b/swift/Sources/Extensions/Data+Hex.swift index 3888716e089..f57ac017624 100644 --- a/swift/Sources/Extensions/Data+Hex.swift +++ b/swift/Sources/Extensions/Data+Hex.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import Foundation diff --git a/swift/Sources/Extensions/DerivationPath+Extension.swift b/swift/Sources/Extensions/DerivationPath+Extension.swift index 09678360e85..a97d0fb0687 100644 --- a/swift/Sources/Extensions/DerivationPath+Extension.swift +++ b/swift/Sources/Extensions/DerivationPath+Extension.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import Foundation diff --git a/swift/Sources/Extensions/Mnemonic+Extension.swift b/swift/Sources/Extensions/Mnemonic+Extension.swift index 8eecce8b1ad..e9d0ebd825d 100644 --- a/swift/Sources/Extensions/Mnemonic+Extension.swift +++ b/swift/Sources/Extensions/Mnemonic+Extension.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import Foundation diff --git a/swift/Sources/Extensions/PublicKey+Bitcoin.swift b/swift/Sources/Extensions/PublicKey+Bitcoin.swift index 8694b7dbba0..421097f1c75 100644 --- a/swift/Sources/Extensions/PublicKey+Bitcoin.swift +++ b/swift/Sources/Extensions/PublicKey+Bitcoin.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. public extension PublicKey { /// Returns the ripemd160 hash of the sha2 hash of the compressed public key data. diff --git a/swift/Sources/TWCardano.swift b/swift/Sources/TWCardano.swift index 1eb456be7ce..88670f92ab4 100644 --- a/swift/Sources/TWCardano.swift +++ b/swift/Sources/TWCardano.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import Foundation diff --git a/swift/Sources/TWData.swift b/swift/Sources/TWData.swift index f5cef1d23a2..01116fc0460 100644 --- a/swift/Sources/TWData.swift +++ b/swift/Sources/TWData.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import Foundation diff --git a/swift/Sources/TWString.swift b/swift/Sources/TWString.swift index a9225577c9a..30fb0b7ae3c 100644 --- a/swift/Sources/TWString.swift +++ b/swift/Sources/TWString.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import Foundation diff --git a/swift/Tests/AESTests.swift b/swift/Tests/AESTests.swift index 868300617de..2326b2bf3cf 100644 --- a/swift/Tests/AESTests.swift +++ b/swift/Tests/AESTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Addresses/BinanceAddressTests.swift b/swift/Tests/Addresses/BinanceAddressTests.swift index 4de244ee115..a950cf3c30e 100644 --- a/swift/Tests/Addresses/BinanceAddressTests.swift +++ b/swift/Tests/Addresses/BinanceAddressTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Addresses/BitcoinAddressTests.swift b/swift/Tests/Addresses/BitcoinAddressTests.swift index 14341165f07..da079e80027 100644 --- a/swift/Tests/Addresses/BitcoinAddressTests.swift +++ b/swift/Tests/Addresses/BitcoinAddressTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Addresses/JunoAddressTests.swift b/swift/Tests/Addresses/JunoAddressTests.swift index 02935c98c3a..eb6e0739ba7 100644 --- a/swift/Tests/Addresses/JunoAddressTests.swift +++ b/swift/Tests/Addresses/JunoAddressTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Addresses/NEOAddressTests.swift b/swift/Tests/Addresses/NEOAddressTests.swift index 35c4b2ea57b..fffdc401277 100644 --- a/swift/Tests/Addresses/NEOAddressTests.swift +++ b/swift/Tests/Addresses/NEOAddressTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Addresses/OntologyAddressTests.swift b/swift/Tests/Addresses/OntologyAddressTests.swift index 616aa19e309..760465b3ecd 100644 --- a/swift/Tests/Addresses/OntologyAddressTests.swift +++ b/swift/Tests/Addresses/OntologyAddressTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Addresses/TronAddressTests.swift b/swift/Tests/Addresses/TronAddressTests.swift index c8d875a38d0..cc2b3a6773f 100644 --- a/swift/Tests/Addresses/TronAddressTests.swift +++ b/swift/Tests/Addresses/TronAddressTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/AnyAddressTests.swift b/swift/Tests/AnyAddressTests.swift index dc4f16da527..85893b6ba17 100644 --- a/swift/Tests/AnyAddressTests.swift +++ b/swift/Tests/AnyAddressTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/AsnParserTests.swift b/swift/Tests/AsnParserTests.swift index 20e61a692bf..38965bb8b5f 100644 --- a/swift/Tests/AsnParserTests.swift +++ b/swift/Tests/AsnParserTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/BarzTests.swift b/swift/Tests/BarzTests.swift index d86ae0b1aba..c5a40f64363 100644 --- a/swift/Tests/BarzTests.swift +++ b/swift/Tests/BarzTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Base32Tests.swift b/swift/Tests/Base32Tests.swift index 41be00de149..d02a6ebc1d9 100644 --- a/swift/Tests/Base32Tests.swift +++ b/swift/Tests/Base32Tests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Base64Tests.swift b/swift/Tests/Base64Tests.swift index b4fdc17c1cf..dd310795b78 100644 --- a/swift/Tests/Base64Tests.swift +++ b/swift/Tests/Base64Tests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/AcalaEVMTests.swift b/swift/Tests/Blockchains/AcalaEVMTests.swift index bbf3bcab269..dd4577c23a1 100644 --- a/swift/Tests/Blockchains/AcalaEVMTests.swift +++ b/swift/Tests/Blockchains/AcalaEVMTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/AcalaTests.swift b/swift/Tests/Blockchains/AcalaTests.swift index c31d4fb3dbd..4ef108e7f2c 100644 --- a/swift/Tests/Blockchains/AcalaTests.swift +++ b/swift/Tests/Blockchains/AcalaTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/AeternityTests.swift b/swift/Tests/Blockchains/AeternityTests.swift index e92183f72ce..b5a7a619f0d 100644 --- a/swift/Tests/Blockchains/AeternityTests.swift +++ b/swift/Tests/Blockchains/AeternityTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/AgoricTests.swift b/swift/Tests/Blockchains/AgoricTests.swift index 19404b3447b..a35e0ed4948 100644 --- a/swift/Tests/Blockchains/AgoricTests.swift +++ b/swift/Tests/Blockchains/AgoricTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/AionTests.swift b/swift/Tests/Blockchains/AionTests.swift index c65188e041a..89c731de13f 100644 --- a/swift/Tests/Blockchains/AionTests.swift +++ b/swift/Tests/Blockchains/AionTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/AlgorandTests.swift b/swift/Tests/Blockchains/AlgorandTests.swift index db82591866d..7c03fbb1a07 100644 --- a/swift/Tests/Blockchains/AlgorandTests.swift +++ b/swift/Tests/Blockchains/AlgorandTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/AptosTests.swift b/swift/Tests/Blockchains/AptosTests.swift index 66f4b33ae48..335b02aad87 100644 --- a/swift/Tests/Blockchains/AptosTests.swift +++ b/swift/Tests/Blockchains/AptosTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/AvalancheTests.swift b/swift/Tests/Blockchains/AvalancheTests.swift index 6e7b1a81c1d..cc0da61aaae 100644 --- a/swift/Tests/Blockchains/AvalancheTests.swift +++ b/swift/Tests/Blockchains/AvalancheTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/BandChainTests.swift b/swift/Tests/Blockchains/BandChainTests.swift index 03200907d37..60fb141add8 100644 --- a/swift/Tests/Blockchains/BandChainTests.swift +++ b/swift/Tests/Blockchains/BandChainTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/BinanceChainTests.swift b/swift/Tests/Blockchains/BinanceChainTests.swift index 78ad5dd1bc6..8f16331ee46 100644 --- a/swift/Tests/Blockchains/BinanceChainTests.swift +++ b/swift/Tests/Blockchains/BinanceChainTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/BinanceSmartChainTests.swift b/swift/Tests/Blockchains/BinanceSmartChainTests.swift index 972c46c178e..a1d82e098e8 100644 --- a/swift/Tests/Blockchains/BinanceSmartChainTests.swift +++ b/swift/Tests/Blockchains/BinanceSmartChainTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/BitcoinDiamondTests.swift b/swift/Tests/Blockchains/BitcoinDiamondTests.swift index 0a7a0941e54..1afbbdc18f2 100644 --- a/swift/Tests/Blockchains/BitcoinDiamondTests.swift +++ b/swift/Tests/Blockchains/BitcoinDiamondTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/BitcoinTests.swift b/swift/Tests/Blockchains/BitcoinTests.swift index 95dc60c4fcd..40c9e894a7f 100644 --- a/swift/Tests/Blockchains/BitcoinTests.swift +++ b/swift/Tests/Blockchains/BitcoinTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/BitconCashTests.swift b/swift/Tests/Blockchains/BitconCashTests.swift index c5cec4484d5..406dc3026af 100644 --- a/swift/Tests/Blockchains/BitconCashTests.swift +++ b/swift/Tests/Blockchains/BitconCashTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/BluzelleTests.swift b/swift/Tests/Blockchains/BluzelleTests.swift index fde2d9cc4a4..78d32c8843f 100644 --- a/swift/Tests/Blockchains/BluzelleTests.swift +++ b/swift/Tests/Blockchains/BluzelleTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/CardanoTests.swift b/swift/Tests/Blockchains/CardanoTests.swift index fc78c7148ce..4acef413ba3 100644 --- a/swift/Tests/Blockchains/CardanoTests.swift +++ b/swift/Tests/Blockchains/CardanoTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/CeloTests.swift b/swift/Tests/Blockchains/CeloTests.swift index 886490bba7c..367d9531e60 100644 --- a/swift/Tests/Blockchains/CeloTests.swift +++ b/swift/Tests/Blockchains/CeloTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/ConfluxeSpaceTests.swift b/swift/Tests/Blockchains/ConfluxeSpaceTests.swift index ac9128f2a0e..77751907f35 100644 --- a/swift/Tests/Blockchains/ConfluxeSpaceTests.swift +++ b/swift/Tests/Blockchains/ConfluxeSpaceTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/CosmosTests.swift b/swift/Tests/Blockchains/CosmosTests.swift index 7a7c906d5d0..90cfd33423c 100644 --- a/swift/Tests/Blockchains/CosmosTests.swift +++ b/swift/Tests/Blockchains/CosmosTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/CronosTests.swift b/swift/Tests/Blockchains/CronosTests.swift index b28a89849bb..a1e917f30ce 100644 --- a/swift/Tests/Blockchains/CronosTests.swift +++ b/swift/Tests/Blockchains/CronosTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/CryptoorgTests.swift b/swift/Tests/Blockchains/CryptoorgTests.swift index c4c2a1941ea..9f781e24982 100644 --- a/swift/Tests/Blockchains/CryptoorgTests.swift +++ b/swift/Tests/Blockchains/CryptoorgTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/DashTests.swift b/swift/Tests/Blockchains/DashTests.swift index 655594b9a1c..94ebddfccc1 100644 --- a/swift/Tests/Blockchains/DashTests.swift +++ b/swift/Tests/Blockchains/DashTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/DecredTests.swift b/swift/Tests/Blockchains/DecredTests.swift index 2f7b3d4e54b..5dc0a9d4814 100644 --- a/swift/Tests/Blockchains/DecredTests.swift +++ b/swift/Tests/Blockchains/DecredTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/DogeTests.swift b/swift/Tests/Blockchains/DogeTests.swift index fd4708d10b8..991b8c570fc 100644 --- a/swift/Tests/Blockchains/DogeTests.swift +++ b/swift/Tests/Blockchains/DogeTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/ECashTests.swift b/swift/Tests/Blockchains/ECashTests.swift index 7826b92e44f..7f07d59b0ba 100644 --- a/swift/Tests/Blockchains/ECashTests.swift +++ b/swift/Tests/Blockchains/ECashTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/EOSTests.swift b/swift/Tests/Blockchains/EOSTests.swift index 7da87e0c6e6..4f5373c46ad 100644 --- a/swift/Tests/Blockchains/EOSTests.swift +++ b/swift/Tests/Blockchains/EOSTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/EthereumAbiTests.swift b/swift/Tests/Blockchains/EthereumAbiTests.swift index ef7e9f413df..0fbd2e07be2 100644 --- a/swift/Tests/Blockchains/EthereumAbiTests.swift +++ b/swift/Tests/Blockchains/EthereumAbiTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/EthereumRlpTests.swift b/swift/Tests/Blockchains/EthereumRlpTests.swift index 3690657bab9..b62e8d92e59 100644 --- a/swift/Tests/Blockchains/EthereumRlpTests.swift +++ b/swift/Tests/Blockchains/EthereumRlpTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/EthereumTests.swift b/swift/Tests/Blockchains/EthereumTests.swift index 51eb515585f..9d5e73a326a 100644 --- a/swift/Tests/Blockchains/EthereumTests.swift +++ b/swift/Tests/Blockchains/EthereumTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/EverscaleTests.swift b/swift/Tests/Blockchains/EverscaleTests.swift index d3ce3f32293..2fc44ddf7b5 100644 --- a/swift/Tests/Blockchains/EverscaleTests.swift +++ b/swift/Tests/Blockchains/EverscaleTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/EvmosTests.swift b/swift/Tests/Blockchains/EvmosTests.swift index 1809a9c40ca..e1df1d44943 100644 --- a/swift/Tests/Blockchains/EvmosTests.swift +++ b/swift/Tests/Blockchains/EvmosTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/FIOTests.swift b/swift/Tests/Blockchains/FIOTests.swift index 3edbc7cd9d1..4a35e145ea8 100644 --- a/swift/Tests/Blockchains/FIOTests.swift +++ b/swift/Tests/Blockchains/FIOTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/FilecoinTests.swift b/swift/Tests/Blockchains/FilecoinTests.swift index d7c7b880283..2998bdfec56 100644 --- a/swift/Tests/Blockchains/FilecoinTests.swift +++ b/swift/Tests/Blockchains/FilecoinTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/GreenfieldTests.swift b/swift/Tests/Blockchains/GreenfieldTests.swift index a04babe5b52..dce11dcfc84 100644 --- a/swift/Tests/Blockchains/GreenfieldTests.swift +++ b/swift/Tests/Blockchains/GreenfieldTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/GroestlcoinTests.swift b/swift/Tests/Blockchains/GroestlcoinTests.swift index ed0d9b250d5..5490f79e1b3 100644 --- a/swift/Tests/Blockchains/GroestlcoinTests.swift +++ b/swift/Tests/Blockchains/GroestlcoinTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/GroestlcoinTransactionSignerTests.swift b/swift/Tests/Blockchains/GroestlcoinTransactionSignerTests.swift index 58ab98538a9..b1e89358d4c 100644 --- a/swift/Tests/Blockchains/GroestlcoinTransactionSignerTests.swift +++ b/swift/Tests/Blockchains/GroestlcoinTransactionSignerTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/HarmonyTests.swift b/swift/Tests/Blockchains/HarmonyTests.swift index c74eca95520..e0faa2c289d 100644 --- a/swift/Tests/Blockchains/HarmonyTests.swift +++ b/swift/Tests/Blockchains/HarmonyTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/HederaTests.swift b/swift/Tests/Blockchains/HederaTests.swift index c71dd7d1f87..7db774a98b9 100644 --- a/swift/Tests/Blockchains/HederaTests.swift +++ b/swift/Tests/Blockchains/HederaTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/IconTests.swift b/swift/Tests/Blockchains/IconTests.swift index 4745b55be81..3e19c5d48bb 100644 --- a/swift/Tests/Blockchains/IconTests.swift +++ b/swift/Tests/Blockchains/IconTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/InternetComputerTests.swift b/swift/Tests/Blockchains/InternetComputerTests.swift index 0ea025dd0c9..e4942450abf 100644 --- a/swift/Tests/Blockchains/InternetComputerTests.swift +++ b/swift/Tests/Blockchains/InternetComputerTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/IoTeXTests.swift b/swift/Tests/Blockchains/IoTeXTests.swift index 0c91d1f18f1..017df545223 100644 --- a/swift/Tests/Blockchains/IoTeXTests.swift +++ b/swift/Tests/Blockchains/IoTeXTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/KavaTests.swift b/swift/Tests/Blockchains/KavaTests.swift index f9a5f8957a5..1b23b5e3632 100644 --- a/swift/Tests/Blockchains/KavaTests.swift +++ b/swift/Tests/Blockchains/KavaTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/KinTests.swift b/swift/Tests/Blockchains/KinTests.swift index 7dfec26b99b..2f6bf523852 100644 --- a/swift/Tests/Blockchains/KinTests.swift +++ b/swift/Tests/Blockchains/KinTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/KuCoinCommunityChainTests.swift b/swift/Tests/Blockchains/KuCoinCommunityChainTests.swift index df42dfc52fb..6faee34cac3 100644 --- a/swift/Tests/Blockchains/KuCoinCommunityChainTests.swift +++ b/swift/Tests/Blockchains/KuCoinCommunityChainTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/KusamaTests.swift b/swift/Tests/Blockchains/KusamaTests.swift index ae5fd1ea397..b881a2451fb 100644 --- a/swift/Tests/Blockchains/KusamaTests.swift +++ b/swift/Tests/Blockchains/KusamaTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/LitecoinTests.swift b/swift/Tests/Blockchains/LitecoinTests.swift index 01fb5b275b0..f4087fe1a04 100644 --- a/swift/Tests/Blockchains/LitecoinTests.swift +++ b/swift/Tests/Blockchains/LitecoinTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/MonacoinTests.swift b/swift/Tests/Blockchains/MonacoinTests.swift index e34a22a022b..b08d139ab98 100644 --- a/swift/Tests/Blockchains/MonacoinTests.swift +++ b/swift/Tests/Blockchains/MonacoinTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/MultiversXTests.swift b/swift/Tests/Blockchains/MultiversXTests.swift index 1b7d97fed03..7b8cbf09ffc 100644 --- a/swift/Tests/Blockchains/MultiversXTests.swift +++ b/swift/Tests/Blockchains/MultiversXTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/NEARTests.swift b/swift/Tests/Blockchains/NEARTests.swift index d275f2059ec..e145db8c625 100644 --- a/swift/Tests/Blockchains/NEARTests.swift +++ b/swift/Tests/Blockchains/NEARTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/NEOTests.swift b/swift/Tests/Blockchains/NEOTests.swift index 74f87b6102e..21b881831ff 100644 --- a/swift/Tests/Blockchains/NEOTests.swift +++ b/swift/Tests/Blockchains/NEOTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/NULSTests.swift b/swift/Tests/Blockchains/NULSTests.swift index 214d4035c5b..458162f164b 100644 --- a/swift/Tests/Blockchains/NULSTests.swift +++ b/swift/Tests/Blockchains/NULSTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/NativeInjectiveTests.swift b/swift/Tests/Blockchains/NativeInjectiveTests.swift index 63bc493c363..f8686ff84bb 100644 --- a/swift/Tests/Blockchains/NativeInjectiveTests.swift +++ b/swift/Tests/Blockchains/NativeInjectiveTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/NervosTests.swift b/swift/Tests/Blockchains/NervosTests.swift index e250869c9ad..c1716b0775f 100644 --- a/swift/Tests/Blockchains/NervosTests.swift +++ b/swift/Tests/Blockchains/NervosTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/NimiqTests.swift b/swift/Tests/Blockchains/NimiqTests.swift index d056302a5e3..8ee8dac51ed 100644 --- a/swift/Tests/Blockchains/NimiqTests.swift +++ b/swift/Tests/Blockchains/NimiqTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/OasisTests.swift b/swift/Tests/Blockchains/OasisTests.swift index 03ad91930f7..994d0aff619 100644 --- a/swift/Tests/Blockchains/OasisTests.swift +++ b/swift/Tests/Blockchains/OasisTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/OntologyTests.swift b/swift/Tests/Blockchains/OntologyTests.swift index 4daec3a7403..a81c6744980 100644 --- a/swift/Tests/Blockchains/OntologyTests.swift +++ b/swift/Tests/Blockchains/OntologyTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/OsmosisTests.swift b/swift/Tests/Blockchains/OsmosisTests.swift index 690d85eaa0f..45fc92bc3e8 100644 --- a/swift/Tests/Blockchains/OsmosisTests.swift +++ b/swift/Tests/Blockchains/OsmosisTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/PolkadotTests.swift b/swift/Tests/Blockchains/PolkadotTests.swift index 880419171d1..fe1d96c74d8 100644 --- a/swift/Tests/Blockchains/PolkadotTests.swift +++ b/swift/Tests/Blockchains/PolkadotTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/PolygonTests.swift b/swift/Tests/Blockchains/PolygonTests.swift index cba7eb17b76..5c0e06fff09 100644 --- a/swift/Tests/Blockchains/PolygonTests.swift +++ b/swift/Tests/Blockchains/PolygonTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/QtumTests.swift b/swift/Tests/Blockchains/QtumTests.swift index b6cd3394538..1c9fdaf3da1 100644 --- a/swift/Tests/Blockchains/QtumTests.swift +++ b/swift/Tests/Blockchains/QtumTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/RippleTests.swift b/swift/Tests/Blockchains/RippleTests.swift index 0582d8df579..7e5787d4f2f 100644 --- a/swift/Tests/Blockchains/RippleTests.swift +++ b/swift/Tests/Blockchains/RippleTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/RoninTests.swift b/swift/Tests/Blockchains/RoninTests.swift index 2cb54acb389..fc8e102ca12 100644 --- a/swift/Tests/Blockchains/RoninTests.swift +++ b/swift/Tests/Blockchains/RoninTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/ScrollTests.swift b/swift/Tests/Blockchains/ScrollTests.swift index a839cecd13f..c890debc03e 100644 --- a/swift/Tests/Blockchains/ScrollTests.swift +++ b/swift/Tests/Blockchains/ScrollTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/SecretTests.swift b/swift/Tests/Blockchains/SecretTests.swift index 4cb9a0f877f..2acf4b61247 100644 --- a/swift/Tests/Blockchains/SecretTests.swift +++ b/swift/Tests/Blockchains/SecretTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/SmartBitcoinCashTests.swift b/swift/Tests/Blockchains/SmartBitcoinCashTests.swift index 48d5a6aca65..5385549a3b8 100644 --- a/swift/Tests/Blockchains/SmartBitcoinCashTests.swift +++ b/swift/Tests/Blockchains/SmartBitcoinCashTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/SolanaTests.swift b/swift/Tests/Blockchains/SolanaTests.swift index a0632941a0c..82df57bac86 100644 --- a/swift/Tests/Blockchains/SolanaTests.swift +++ b/swift/Tests/Blockchains/SolanaTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/StargazeTests.swift b/swift/Tests/Blockchains/StargazeTests.swift index ee95f7c7591..16bc5f5c3c0 100644 --- a/swift/Tests/Blockchains/StargazeTests.swift +++ b/swift/Tests/Blockchains/StargazeTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/StarkExTests.swift b/swift/Tests/Blockchains/StarkExTests.swift index 88e868ab10c..56dd47f5839 100644 --- a/swift/Tests/Blockchains/StarkExTests.swift +++ b/swift/Tests/Blockchains/StarkExTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/StellarTests.swift b/swift/Tests/Blockchains/StellarTests.swift index 9ff0754b748..159d1575fdb 100644 --- a/swift/Tests/Blockchains/StellarTests.swift +++ b/swift/Tests/Blockchains/StellarTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/SuiTests.swift b/swift/Tests/Blockchains/SuiTests.swift index 04c97d77707..7e8092cd805 100644 --- a/swift/Tests/Blockchains/SuiTests.swift +++ b/swift/Tests/Blockchains/SuiTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/SyscoinTests.swift b/swift/Tests/Blockchains/SyscoinTests.swift index 3e0a6010c8c..9668e7de5de 100644 --- a/swift/Tests/Blockchains/SyscoinTests.swift +++ b/swift/Tests/Blockchains/SyscoinTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/THORChainSwapTests.swift b/swift/Tests/Blockchains/THORChainSwapTests.swift index 8a7dc4a2ff6..b13699a857b 100644 --- a/swift/Tests/Blockchains/THORChainSwapTests.swift +++ b/swift/Tests/Blockchains/THORChainSwapTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/THORChainTests.swift b/swift/Tests/Blockchains/THORChainTests.swift index 45d9cb3ac80..a0ae0db1d45 100644 --- a/swift/Tests/Blockchains/THORChainTests.swift +++ b/swift/Tests/Blockchains/THORChainTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/TerraClassicTests.swift b/swift/Tests/Blockchains/TerraClassicTests.swift index 94d98889f9d..66ab6f11816 100644 --- a/swift/Tests/Blockchains/TerraClassicTests.swift +++ b/swift/Tests/Blockchains/TerraClassicTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/TerraTests.swift b/swift/Tests/Blockchains/TerraTests.swift index 588967419ad..af8b1d0e593 100644 --- a/swift/Tests/Blockchains/TerraTests.swift +++ b/swift/Tests/Blockchains/TerraTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/TezosTests.swift b/swift/Tests/Blockchains/TezosTests.swift index df36e377712..b7374aa2e9d 100644 --- a/swift/Tests/Blockchains/TezosTests.swift +++ b/swift/Tests/Blockchains/TezosTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/TheOpenNetworkTests.swift b/swift/Tests/Blockchains/TheOpenNetworkTests.swift index cda63c430fa..d0265404742 100644 --- a/swift/Tests/Blockchains/TheOpenNetworkTests.swift +++ b/swift/Tests/Blockchains/TheOpenNetworkTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/ThetaFuelTests.swift b/swift/Tests/Blockchains/ThetaFuelTests.swift index 14385120a6b..e0c42392f32 100644 --- a/swift/Tests/Blockchains/ThetaFuelTests.swift +++ b/swift/Tests/Blockchains/ThetaFuelTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/ThetaTests.swift b/swift/Tests/Blockchains/ThetaTests.swift index 17176fdbc71..37e3508cd12 100644 --- a/swift/Tests/Blockchains/ThetaTests.swift +++ b/swift/Tests/Blockchains/ThetaTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/TronTests.swift b/swift/Tests/Blockchains/TronTests.swift index cf098686edb..9c5c7d6ebea 100644 --- a/swift/Tests/Blockchains/TronTests.swift +++ b/swift/Tests/Blockchains/TronTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/WanchainTests.swift b/swift/Tests/Blockchains/WanchainTests.swift index 8718ce3cd9b..f4b2b1b87a0 100644 --- a/swift/Tests/Blockchains/WanchainTests.swift +++ b/swift/Tests/Blockchains/WanchainTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/WavesTests.swift b/swift/Tests/Blockchains/WavesTests.swift index 75c9f4b6154..3f16f7bdfd0 100644 --- a/swift/Tests/Blockchains/WavesTests.swift +++ b/swift/Tests/Blockchains/WavesTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/ZcashTests.swift b/swift/Tests/Blockchains/ZcashTests.swift index 92229c34186..afcd0ef80d0 100644 --- a/swift/Tests/Blockchains/ZcashTests.swift +++ b/swift/Tests/Blockchains/ZcashTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/ZcoinTests.swift b/swift/Tests/Blockchains/ZcoinTests.swift index 7014d4e274d..42b20607823 100644 --- a/swift/Tests/Blockchains/ZcoinTests.swift +++ b/swift/Tests/Blockchains/ZcoinTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/Blockchains/ZenTests.swift b/swift/Tests/Blockchains/ZenTests.swift index c0fa3d9cdd5..ab7adddffd9 100644 --- a/swift/Tests/Blockchains/ZenTests.swift +++ b/swift/Tests/Blockchains/ZenTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/Blockchains/ZilliqaTests.swift b/swift/Tests/Blockchains/ZilliqaTests.swift index a523cd26c97..060db939033 100644 --- a/swift/Tests/Blockchains/ZilliqaTests.swift +++ b/swift/Tests/Blockchains/ZilliqaTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 00e6aecd5f0..5c04bb7bd01 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/CoinTypeTests.swift b/swift/Tests/CoinTypeTests.swift index 5021dcf0f32..25e1601aefc 100644 --- a/swift/Tests/CoinTypeTests.swift +++ b/swift/Tests/CoinTypeTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/DataTests.swift b/swift/Tests/DataTests.swift index eee71d6743a..d003c6e303d 100644 --- a/swift/Tests/DataTests.swift +++ b/swift/Tests/DataTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/DerivationPathTests.swift b/swift/Tests/DerivationPathTests.swift index 3867d434847..7ca9110c591 100644 --- a/swift/Tests/DerivationPathTests.swift +++ b/swift/Tests/DerivationPathTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/HDWalletTests.swift b/swift/Tests/HDWalletTests.swift index d6df8bafa73..789dad6a6c4 100644 --- a/swift/Tests/HDWalletTests.swift +++ b/swift/Tests/HDWalletTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/HashTests.swift b/swift/Tests/HashTests.swift index fc192dceebe..da1c58574d2 100644 --- a/swift/Tests/HashTests.swift +++ b/swift/Tests/HashTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/LiquidStakingTests.swift b/swift/Tests/LiquidStakingTests.swift index 6f3369b4a5d..b5d721c40d6 100644 --- a/swift/Tests/LiquidStakingTests.swift +++ b/swift/Tests/LiquidStakingTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/MnemonicTests.swift b/swift/Tests/MnemonicTests.swift index e76baa79185..3d2f043714d 100644 --- a/swift/Tests/MnemonicTests.swift +++ b/swift/Tests/MnemonicTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/PBKDF2Tests.swift b/swift/Tests/PBKDF2Tests.swift index c5ccbadc1af..cb3645cdf95 100644 --- a/swift/Tests/PBKDF2Tests.swift +++ b/swift/Tests/PBKDF2Tests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/PrivateKeyTests.swift b/swift/Tests/PrivateKeyTests.swift index 82be1a5e7a5..1fd2ce348bb 100644 --- a/swift/Tests/PrivateKeyTests.swift +++ b/swift/Tests/PrivateKeyTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/PublicKeyTests.swift b/swift/Tests/PublicKeyTests.swift index 5819e3d3ed0..026935dbb6d 100644 --- a/swift/Tests/PublicKeyTests.swift +++ b/swift/Tests/PublicKeyTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import WalletCore import XCTest diff --git a/swift/Tests/TransactionCompilerTests.swift b/swift/Tests/TransactionCompilerTests.swift index f28a2665758..3a45d34b0b7 100644 --- a/swift/Tests/TransactionCompilerTests.swift +++ b/swift/Tests/TransactionCompilerTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/UniversalAssetIDTests.swift b/swift/Tests/UniversalAssetIDTests.swift index 73ad5ac8358..e2d2c3ca6d8 100644 --- a/swift/Tests/UniversalAssetIDTests.swift +++ b/swift/Tests/UniversalAssetIDTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/WebAuthnTests.swift b/swift/Tests/WebAuthnTests.swift index 37709233f63..d57140b48e2 100644 --- a/swift/Tests/WebAuthnTests.swift +++ b/swift/Tests/WebAuthnTests.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest import WalletCore diff --git a/swift/Tests/XCTAssert+Extension.swift b/swift/Tests/XCTAssert+Extension.swift index 048c712951c..15aa17f5eb9 100644 --- a/swift/Tests/XCTAssert+Extension.swift +++ b/swift/Tests/XCTAssert+Extension.swift @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import XCTest diff --git a/swift/cpp.xcconfig.in b/swift/cpp.xcconfig.in index 41c80610459..09e3c0f91af 100644 --- a/swift/cpp.xcconfig.in +++ b/swift/cpp.xcconfig.in @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. HEADER_SEARCH_PATHS = $(SRCROOT)/../src @Boost_INCLUDE_DIRS@ SYSTEM_HEADER_SEARCH_PATHS = $(SRCROOT)/../src/build/local/include $(SRCROOT)/../build/local/src/protobuf/protobuf-3.19.2/src diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4826841ebcd..288f2842ba8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,8 +1,6 @@ -# Copyright © 2017-2023 Trust Wallet. +# SPDX-License-Identifier: Apache-2.0 # -# This file is part of Trust. The full Trust copyright notice, including -# terms governing use, modification, and redistribution, is contained in the -# file LICENSE at the root of the source code distribution tree. +# Copyright © 2017 Trust Wallet. enable_testing() diff --git a/tests/chains/Acala/TWAnyAddressTests.cpp b/tests/chains/Acala/TWAnyAddressTests.cpp index f3ddc35b6cd..e62412a1736 100644 --- a/tests/chains/Acala/TWAnyAddressTests.cpp +++ b/tests/chains/Acala/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/tests/chains/Acala/TWAnySignerTests.cpp b/tests/chains/Acala/TWAnySignerTests.cpp index b839211e0ad..c69ada618f0 100644 --- a/tests/chains/Acala/TWAnySignerTests.cpp +++ b/tests/chains/Acala/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "uint256.h" diff --git a/tests/chains/Acala/TWCoinTypeTests.cpp b/tests/chains/Acala/TWCoinTypeTests.cpp index 3e8e742b209..bf861303797 100644 --- a/tests/chains/Acala/TWCoinTypeTests.cpp +++ b/tests/chains/Acala/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/AcalaEVM/TWCoinTypeTests.cpp b/tests/chains/AcalaEVM/TWCoinTypeTests.cpp index 3ea2f79b595..9381d5ac91a 100644 --- a/tests/chains/AcalaEVM/TWCoinTypeTests.cpp +++ b/tests/chains/AcalaEVM/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Aeternity/AddressTests.cpp b/tests/chains/Aeternity/AddressTests.cpp index 986566fd979..2334129c5d9 100644 --- a/tests/chains/Aeternity/AddressTests.cpp +++ b/tests/chains/Aeternity/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/tests/chains/Aeternity/SignerTests.cpp b/tests/chains/Aeternity/SignerTests.cpp index 2564d9b6710..3fe61a03851 100644 --- a/tests/chains/Aeternity/SignerTests.cpp +++ b/tests/chains/Aeternity/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Aeternity/Signer.h" #include "Aeternity/Transaction.h" diff --git a/tests/chains/Aeternity/TWAeternityAddressTests.cpp b/tests/chains/Aeternity/TWAeternityAddressTests.cpp index 5b6def18a98..79cb259b3f6 100644 --- a/tests/chains/Aeternity/TWAeternityAddressTests.cpp +++ b/tests/chains/Aeternity/TWAeternityAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Aeternity/TWAnySignerTests.cpp b/tests/chains/Aeternity/TWAnySignerTests.cpp index ad43b964a00..75be60551f3 100644 --- a/tests/chains/Aeternity/TWAnySignerTests.cpp +++ b/tests/chains/Aeternity/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "uint256.h" diff --git a/tests/chains/Aeternity/TWCoinTypeTests.cpp b/tests/chains/Aeternity/TWCoinTypeTests.cpp index 345ed187cf5..a5ea4fed267 100644 --- a/tests/chains/Aeternity/TWCoinTypeTests.cpp +++ b/tests/chains/Aeternity/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Aeternity/TransactionTests.cpp b/tests/chains/Aeternity/TransactionTests.cpp index d410eca2732..f09aa5a9de1 100644 --- a/tests/chains/Aeternity/TransactionTests.cpp +++ b/tests/chains/Aeternity/TransactionTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PrivateKey.h" diff --git a/tests/chains/Aion/AddressTests.cpp b/tests/chains/Aion/AddressTests.cpp index 6ac25da0182..a2ad39be5ac 100644 --- a/tests/chains/Aion/AddressTests.cpp +++ b/tests/chains/Aion/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Aion/Address.h" #include "Coin.h" diff --git a/tests/chains/Aion/RLPTests.cpp b/tests/chains/Aion/RLPTests.cpp index 7ad104a9bdf..d18b892ef3c 100644 --- a/tests/chains/Aion/RLPTests.cpp +++ b/tests/chains/Aion/RLPTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Aion/RLP.h" #include "HexCoding.h" diff --git a/tests/chains/Aion/SignerTests.cpp b/tests/chains/Aion/SignerTests.cpp index cb27f4a846d..84927f7dcee 100644 --- a/tests/chains/Aion/SignerTests.cpp +++ b/tests/chains/Aion/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Aion/Signer.h" #include "Aion/Transaction.h" diff --git a/tests/chains/Aion/TWAnySignerTests.cpp b/tests/chains/Aion/TWAnySignerTests.cpp index 44f4025beb7..79a3a21d7f2 100644 --- a/tests/chains/Aion/TWAnySignerTests.cpp +++ b/tests/chains/Aion/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "uint256.h" diff --git a/tests/chains/Aion/TWCoinTypeTests.cpp b/tests/chains/Aion/TWCoinTypeTests.cpp index 1ac08c1acc9..39175f51569 100644 --- a/tests/chains/Aion/TWCoinTypeTests.cpp +++ b/tests/chains/Aion/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Aion/TransactionCompilerTests.cpp b/tests/chains/Aion/TransactionCompilerTests.cpp index 43acc78f1f1..e10e739bec2 100644 --- a/tests/chains/Aion/TransactionCompilerTests.cpp +++ b/tests/chains/Aion/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base64.h" #include "Coin.h" diff --git a/tests/chains/Aion/TransactionTests.cpp b/tests/chains/Aion/TransactionTests.cpp index 054c5a85008..0e2e73e3ec1 100644 --- a/tests/chains/Aion/TransactionTests.cpp +++ b/tests/chains/Aion/TransactionTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Aion/Transaction.h" #include "HexCoding.h" diff --git a/tests/chains/Algorand/AddressTests.cpp b/tests/chains/Algorand/AddressTests.cpp index 9dda252fc1c..993b0e3247c 100644 --- a/tests/chains/Algorand/AddressTests.cpp +++ b/tests/chains/Algorand/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Algorand/Address.h" #include "HexCoding.h" diff --git a/tests/chains/Algorand/SignerTests.cpp b/tests/chains/Algorand/SignerTests.cpp index 3cb2ff2d432..6b38bf6ce1d 100644 --- a/tests/chains/Algorand/SignerTests.cpp +++ b/tests/chains/Algorand/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Algorand/Address.h" #include "Algorand/BinaryCoding.h" diff --git a/tests/chains/Algorand/TWAnySignerTests.cpp b/tests/chains/Algorand/TWAnySignerTests.cpp index 1bbf7cfd377..0ac2c03375b 100644 --- a/tests/chains/Algorand/TWAnySignerTests.cpp +++ b/tests/chains/Algorand/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base64.h" #include "HexCoding.h" diff --git a/tests/chains/Algorand/TWCoinTypeTests.cpp b/tests/chains/Algorand/TWCoinTypeTests.cpp index 51334046e72..8aba2311005 100644 --- a/tests/chains/Algorand/TWCoinTypeTests.cpp +++ b/tests/chains/Algorand/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Algorand/TransactionCompilerTests.cpp b/tests/chains/Algorand/TransactionCompilerTests.cpp index 647495b2443..e630a7205a3 100644 --- a/tests/chains/Algorand/TransactionCompilerTests.cpp +++ b/tests/chains/Algorand/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base64.h" #include "Coin.h" diff --git a/tests/chains/Aptos/AddressTests.cpp b/tests/chains/Aptos/AddressTests.cpp index fc68f647a88..b94e4222c86 100644 --- a/tests/chains/Aptos/AddressTests.cpp +++ b/tests/chains/Aptos/AddressTests.cpp @@ -1,4 +1,7 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. +// // Author: Clement Doumergue // // This file is part of Trust. The full Trust copyright notice, including diff --git a/tests/chains/Aptos/CompilerTests.cpp b/tests/chains/Aptos/CompilerTests.cpp index fd00034931a..ebe0ccc49ae 100644 --- a/tests/chains/Aptos/CompilerTests.cpp +++ b/tests/chains/Aptos/CompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "proto/Aptos.pb.h" #include "proto/TransactionCompiler.pb.h" diff --git a/tests/chains/Aptos/TWAnySignerTests.cpp b/tests/chains/Aptos/TWAnySignerTests.cpp index 44885c9f872..dce9c2f3d72 100644 --- a/tests/chains/Aptos/TWAnySignerTests.cpp +++ b/tests/chains/Aptos/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PrivateKey.h" diff --git a/tests/chains/Aptos/TWAptosAddressTests.cpp b/tests/chains/Aptos/TWAptosAddressTests.cpp index ec6612d0960..4dea755b651 100644 --- a/tests/chains/Aptos/TWAptosAddressTests.cpp +++ b/tests/chains/Aptos/TWAptosAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "TestUtilities.h" diff --git a/tests/chains/Aptos/TWCoinTypeTests.cpp b/tests/chains/Aptos/TWCoinTypeTests.cpp index 16934dd916a..6d4f14ce651 100644 --- a/tests/chains/Aptos/TWCoinTypeTests.cpp +++ b/tests/chains/Aptos/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Arbitrum/TWCoinTypeTests.cpp b/tests/chains/Arbitrum/TWCoinTypeTests.cpp index 1b2f67839f4..0b8eb85a5e9 100644 --- a/tests/chains/Arbitrum/TWCoinTypeTests.cpp +++ b/tests/chains/Arbitrum/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/ArbitrumNova/TWCoinTypeTests.cpp b/tests/chains/ArbitrumNova/TWCoinTypeTests.cpp index 6c852405d43..15820936837 100644 --- a/tests/chains/ArbitrumNova/TWCoinTypeTests.cpp +++ b/tests/chains/ArbitrumNova/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Aurora/TWCoinTypeTests.cpp b/tests/chains/Aurora/TWCoinTypeTests.cpp index af42dd3027b..42b7334b872 100644 --- a/tests/chains/Aurora/TWCoinTypeTests.cpp +++ b/tests/chains/Aurora/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Avalanche/TWCoinTypeTests.cpp b/tests/chains/Avalanche/TWCoinTypeTests.cpp index 329a9b69da4..84ab72079c7 100644 --- a/tests/chains/Avalanche/TWCoinTypeTests.cpp +++ b/tests/chains/Avalanche/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Base/TWCoinTypeTests.cpp b/tests/chains/Base/TWCoinTypeTests.cpp index a07285626d2..993248ee996 100644 --- a/tests/chains/Base/TWCoinTypeTests.cpp +++ b/tests/chains/Base/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Binance/AddressTests.cpp b/tests/chains/Binance/AddressTests.cpp index fd7a769c277..56f812afbd4 100644 --- a/tests/chains/Binance/AddressTests.cpp +++ b/tests/chains/Binance/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Binance/Address.h" #include "Coin.h" diff --git a/tests/chains/Binance/TWAnySignerTests.cpp b/tests/chains/Binance/TWAnySignerTests.cpp index d5f4de77c04..630daa83189 100644 --- a/tests/chains/Binance/TWAnySignerTests.cpp +++ b/tests/chains/Binance/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Binance/Address.h" diff --git a/tests/chains/Binance/TWCoinTypeTests.cpp b/tests/chains/Binance/TWCoinTypeTests.cpp index 8930e317212..c7f09c9a16c 100644 --- a/tests/chains/Binance/TWCoinTypeTests.cpp +++ b/tests/chains/Binance/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Binance/TWWalletConnectSigning.cpp b/tests/chains/Binance/TWWalletConnectSigning.cpp index 8a463f99470..7ee7e7956f4 100644 --- a/tests/chains/Binance/TWWalletConnectSigning.cpp +++ b/tests/chains/Binance/TWWalletConnectSigning.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2024 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "proto/Binance.pb.h" diff --git a/tests/chains/Binance/TransactionCompilerTests.cpp b/tests/chains/Binance/TransactionCompilerTests.cpp index efcb375ac30..3c8a1d5a171 100644 --- a/tests/chains/Binance/TransactionCompilerTests.cpp +++ b/tests/chains/Binance/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/BinanceSmartChain/SignerTests.cpp b/tests/chains/BinanceSmartChain/SignerTests.cpp index c970d687488..c9196d6b53b 100644 --- a/tests/chains/BinanceSmartChain/SignerTests.cpp +++ b/tests/chains/BinanceSmartChain/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "Ethereum/Address.h" diff --git a/tests/chains/BinanceSmartChain/TWAnyAddressTests.cpp b/tests/chains/BinanceSmartChain/TWAnyAddressTests.cpp index a0cc87948d2..139e6a8368b 100644 --- a/tests/chains/BinanceSmartChain/TWAnyAddressTests.cpp +++ b/tests/chains/BinanceSmartChain/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/tests/chains/BinanceSmartChain/TWCoinTypeTests.cpp b/tests/chains/BinanceSmartChain/TWCoinTypeTests.cpp index aa44abba026..7dc7a6cb102 100644 --- a/tests/chains/BinanceSmartChain/TWCoinTypeTests.cpp +++ b/tests/chains/BinanceSmartChain/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Bitcoin/BitcoinAddressTests.cpp b/tests/chains/Bitcoin/BitcoinAddressTests.cpp index bf2036dd7de..fa8507663df 100644 --- a/tests/chains/Bitcoin/BitcoinAddressTests.cpp +++ b/tests/chains/Bitcoin/BitcoinAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bitcoin/Address.h" #include "Bitcoin/Script.h" diff --git a/tests/chains/Bitcoin/BitcoinOrdinalNftData.h b/tests/chains/Bitcoin/BitcoinOrdinalNftData.h index 782d45e419d..3923de9d360 100644 --- a/tests/chains/Bitcoin/BitcoinOrdinalNftData.h +++ b/tests/chains/Bitcoin/BitcoinOrdinalNftData.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. std::string nftInscriptionImageData = "\ 89504e470d0a1a0a0000000d4948445200000360000002be0803000000f3\ diff --git a/tests/chains/Bitcoin/BitcoinScriptTests.cpp b/tests/chains/Bitcoin/BitcoinScriptTests.cpp index 1f7c85b327f..f4a6ed9fbbe 100644 --- a/tests/chains/Bitcoin/BitcoinScriptTests.cpp +++ b/tests/chains/Bitcoin/BitcoinScriptTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bitcoin/Script.h" #include "Bitcoin/SignatureBuilder.h" diff --git a/tests/chains/Bitcoin/FeeCalculatorTests.cpp b/tests/chains/Bitcoin/FeeCalculatorTests.cpp index 582a332fcb5..aa2f973c4bd 100644 --- a/tests/chains/Bitcoin/FeeCalculatorTests.cpp +++ b/tests/chains/Bitcoin/FeeCalculatorTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bitcoin/FeeCalculator.h" diff --git a/tests/chains/Bitcoin/InputSelectorTests.cpp b/tests/chains/Bitcoin/InputSelectorTests.cpp index ef1fd80552c..999b9d950a6 100644 --- a/tests/chains/Bitcoin/InputSelectorTests.cpp +++ b/tests/chains/Bitcoin/InputSelectorTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/tests/chains/Bitcoin/MessageSignerTests.cpp b/tests/chains/Bitcoin/MessageSignerTests.cpp index 06ea622d8b2..d55cded3107 100644 --- a/tests/chains/Bitcoin/MessageSignerTests.cpp +++ b/tests/chains/Bitcoin/MessageSignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bitcoin/MessageSigner.h" #include diff --git a/tests/chains/Bitcoin/SegwitAddressTests.cpp b/tests/chains/Bitcoin/SegwitAddressTests.cpp index 3c7408cb3f0..62c7fe7fde5 100644 --- a/tests/chains/Bitcoin/SegwitAddressTests.cpp +++ b/tests/chains/Bitcoin/SegwitAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bech32.h" #include "Bitcoin/SegwitAddress.h" diff --git a/tests/chains/Bitcoin/TWBitcoinAddressTests.cpp b/tests/chains/Bitcoin/TWBitcoinAddressTests.cpp index f51a5464b51..60c6cb7d16e 100644 --- a/tests/chains/Bitcoin/TWBitcoinAddressTests.cpp +++ b/tests/chains/Bitcoin/TWBitcoinAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Bitcoin/TWBitcoinFeeTests.cpp b/tests/chains/Bitcoin/TWBitcoinFeeTests.cpp index 87fa9b584ac..f442d968842 100644 --- a/tests/chains/Bitcoin/TWBitcoinFeeTests.cpp +++ b/tests/chains/Bitcoin/TWBitcoinFeeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/Bitcoin/TWBitcoinScriptTests.cpp b/tests/chains/Bitcoin/TWBitcoinScriptTests.cpp index cc555c1b7a7..1851e7ba9ba 100644 --- a/tests/chains/Bitcoin/TWBitcoinScriptTests.cpp +++ b/tests/chains/Bitcoin/TWBitcoinScriptTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp b/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp index 1dd26b1d942..7a548f0fa90 100644 --- a/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp +++ b/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base58.h" #include "Bitcoin/Address.h" diff --git a/tests/chains/Bitcoin/TWBitcoinTransactionTests.cpp b/tests/chains/Bitcoin/TWBitcoinTransactionTests.cpp index 19d3d3e4338..712595ce63e 100644 --- a/tests/chains/Bitcoin/TWBitcoinTransactionTests.cpp +++ b/tests/chains/Bitcoin/TWBitcoinTransactionTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bitcoin/Transaction.h" #include "HexCoding.h" diff --git a/tests/chains/Bitcoin/TWCoinTypeTests.cpp b/tests/chains/Bitcoin/TWCoinTypeTests.cpp index b11ad81225f..6b5d718b593 100644 --- a/tests/chains/Bitcoin/TWCoinTypeTests.cpp +++ b/tests/chains/Bitcoin/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Bitcoin/TWSegwitAddressTests.cpp b/tests/chains/Bitcoin/TWSegwitAddressTests.cpp index 9723244292c..4adf19e4250 100644 --- a/tests/chains/Bitcoin/TWSegwitAddressTests.cpp +++ b/tests/chains/Bitcoin/TWSegwitAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Bitcoin/TransactionCompilerTests.cpp b/tests/chains/Bitcoin/TransactionCompilerTests.cpp index 212a67ed805..55887c8f6da 100644 --- a/tests/chains/Bitcoin/TransactionCompilerTests.cpp +++ b/tests/chains/Bitcoin/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Bitcoin/TransactionPlanTests.cpp b/tests/chains/Bitcoin/TransactionPlanTests.cpp index 0f5c1a110c1..7953fdd8ef4 100644 --- a/tests/chains/Bitcoin/TransactionPlanTests.cpp +++ b/tests/chains/Bitcoin/TransactionPlanTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TxComparisonHelper.h" #include "Bitcoin/OutPoint.h" diff --git a/tests/chains/Bitcoin/TxComparisonHelper.cpp b/tests/chains/Bitcoin/TxComparisonHelper.cpp index db1f603610e..c003744bd13 100644 --- a/tests/chains/Bitcoin/TxComparisonHelper.cpp +++ b/tests/chains/Bitcoin/TxComparisonHelper.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TxComparisonHelper.h" diff --git a/tests/chains/Bitcoin/TxComparisonHelper.h b/tests/chains/Bitcoin/TxComparisonHelper.h index 41f4a602337..b969b14595f 100644 --- a/tests/chains/Bitcoin/TxComparisonHelper.h +++ b/tests/chains/Bitcoin/TxComparisonHelper.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/tests/chains/BitcoinCash/TWBitcoinCashTests.cpp b/tests/chains/BitcoinCash/TWBitcoinCashTests.cpp index c097120b5b9..734aa7d541c 100644 --- a/tests/chains/BitcoinCash/TWBitcoinCashTests.cpp +++ b/tests/chains/BitcoinCash/TWBitcoinCashTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bitcoin/Address.h" #include "Bitcoin/SigHashType.h" diff --git a/tests/chains/BitcoinCash/TWCoinTypeTests.cpp b/tests/chains/BitcoinCash/TWCoinTypeTests.cpp index 5a7749c9789..e86daa42bb1 100644 --- a/tests/chains/BitcoinCash/TWCoinTypeTests.cpp +++ b/tests/chains/BitcoinCash/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/BitcoinDiamond/AddressTests.cpp b/tests/chains/BitcoinDiamond/AddressTests.cpp index de5f60aeb6f..a99b7ce46ea 100644 --- a/tests/chains/BitcoinDiamond/AddressTests.cpp +++ b/tests/chains/BitcoinDiamond/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/tests/chains/BitcoinDiamond/SignerTests.cpp b/tests/chains/BitcoinDiamond/SignerTests.cpp index b8543e86070..2b0a239d726 100644 --- a/tests/chains/BitcoinDiamond/SignerTests.cpp +++ b/tests/chains/BitcoinDiamond/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bitcoin/TransactionPlan.h" #include "Bitcoin/TransactionSigner.h" diff --git a/tests/chains/BitcoinDiamond/TWAnyAddressTests.cpp b/tests/chains/BitcoinDiamond/TWAnyAddressTests.cpp index 592f555a505..43ae566f74e 100644 --- a/tests/chains/BitcoinDiamond/TWAnyAddressTests.cpp +++ b/tests/chains/BitcoinDiamond/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "HexCoding.h" diff --git a/tests/chains/BitcoinDiamond/TWAnySignerTests.cpp b/tests/chains/BitcoinDiamond/TWAnySignerTests.cpp index e241fb38bf3..f1ab62557f2 100644 --- a/tests/chains/BitcoinDiamond/TWAnySignerTests.cpp +++ b/tests/chains/BitcoinDiamond/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/BitcoinDiamond/TWCoinTypeTests.cpp b/tests/chains/BitcoinDiamond/TWCoinTypeTests.cpp index 023dc5b38fa..7e29ed4578e 100644 --- a/tests/chains/BitcoinDiamond/TWCoinTypeTests.cpp +++ b/tests/chains/BitcoinDiamond/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/BitcoinDiamond/TWSegwitAddressTests.cpp b/tests/chains/BitcoinDiamond/TWSegwitAddressTests.cpp index f13078a1595..bf09d3f8d0f 100644 --- a/tests/chains/BitcoinDiamond/TWSegwitAddressTests.cpp +++ b/tests/chains/BitcoinDiamond/TWSegwitAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // #include "TestUtilities.h" diff --git a/tests/chains/BitcoinDiamond/TransactionCompilerTests.cpp b/tests/chains/BitcoinDiamond/TransactionCompilerTests.cpp index d7192e18b91..744c64deb7a 100644 --- a/tests/chains/BitcoinDiamond/TransactionCompilerTests.cpp +++ b/tests/chains/BitcoinDiamond/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/BitcoinGold/TWAddressTests.cpp b/tests/chains/BitcoinGold/TWAddressTests.cpp index be7a676b460..ec7372aee34 100644 --- a/tests/chains/BitcoinGold/TWAddressTests.cpp +++ b/tests/chains/BitcoinGold/TWAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // #include "TestUtilities.h" diff --git a/tests/chains/BitcoinGold/TWBitcoinGoldTests.cpp b/tests/chains/BitcoinGold/TWBitcoinGoldTests.cpp index daf3df1d564..dddb5e8a13f 100644 --- a/tests/chains/BitcoinGold/TWBitcoinGoldTests.cpp +++ b/tests/chains/BitcoinGold/TWBitcoinGoldTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/tests/chains/BitcoinGold/TWCoinTypeTests.cpp b/tests/chains/BitcoinGold/TWCoinTypeTests.cpp index aeb8f2221a8..c58fa8c23de 100644 --- a/tests/chains/BitcoinGold/TWCoinTypeTests.cpp +++ b/tests/chains/BitcoinGold/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/BitcoinGold/TWSegwitAddressTests.cpp b/tests/chains/BitcoinGold/TWSegwitAddressTests.cpp index 3f7fc51cb2a..e4d6be2f679 100644 --- a/tests/chains/BitcoinGold/TWSegwitAddressTests.cpp +++ b/tests/chains/BitcoinGold/TWSegwitAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // #include "TestUtilities.h" diff --git a/tests/chains/BitcoinGold/TWSignerTests.cpp b/tests/chains/BitcoinGold/TWSignerTests.cpp index 41ef63adedf..02d28a81c4a 100644 --- a/tests/chains/BitcoinGold/TWSignerTests.cpp +++ b/tests/chains/BitcoinGold/TWSignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/tests/chains/Boba/TWCoinTypeTests.cpp b/tests/chains/Boba/TWCoinTypeTests.cpp index 28e41e02f47..a5e5201bf27 100644 --- a/tests/chains/Boba/TWCoinTypeTests.cpp +++ b/tests/chains/Boba/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Callisto/TWCoinTypeTests.cpp b/tests/chains/Callisto/TWCoinTypeTests.cpp index 2c1db281db0..752e2349656 100644 --- a/tests/chains/Callisto/TWCoinTypeTests.cpp +++ b/tests/chains/Callisto/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cardano/AddressTests.cpp b/tests/chains/Cardano/AddressTests.cpp index 5ade41f1cc4..c42ea1b5402 100644 --- a/tests/chains/Cardano/AddressTests.cpp +++ b/tests/chains/Cardano/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Cardano/AddressV3.h" diff --git a/tests/chains/Cardano/SigningTests.cpp b/tests/chains/Cardano/SigningTests.cpp index f283d9091c5..ccc689580eb 100644 --- a/tests/chains/Cardano/SigningTests.cpp +++ b/tests/chains/Cardano/SigningTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Cardano/AddressV3.h" #include "Cardano/Signer.h" diff --git a/tests/chains/Cardano/StakingTests.cpp b/tests/chains/Cardano/StakingTests.cpp index 686f56fede9..45e9e7d13f9 100644 --- a/tests/chains/Cardano/StakingTests.cpp +++ b/tests/chains/Cardano/StakingTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Cardano/AddressV3.h" #include "Cardano/Signer.h" diff --git a/tests/chains/Cardano/TWCardanoAddressTests.cpp b/tests/chains/Cardano/TWCardanoAddressTests.cpp index 50a3d11131a..d18caf17f49 100644 --- a/tests/chains/Cardano/TWCardanoAddressTests.cpp +++ b/tests/chains/Cardano/TWCardanoAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/tests/chains/Cardano/TWCoinTypeTests.cpp b/tests/chains/Cardano/TWCoinTypeTests.cpp index c8ecdf8109f..72ba864892d 100644 --- a/tests/chains/Cardano/TWCoinTypeTests.cpp +++ b/tests/chains/Cardano/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cardano/TransactionCompilerTests.cpp b/tests/chains/Cardano/TransactionCompilerTests.cpp index b2bc61fd7e0..5106f34af35 100644 --- a/tests/chains/Cardano/TransactionCompilerTests.cpp +++ b/tests/chains/Cardano/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Cardano/TransactionTests.cpp b/tests/chains/Cardano/TransactionTests.cpp index 599b3d4119b..8f9a64a038c 100644 --- a/tests/chains/Cardano/TransactionTests.cpp +++ b/tests/chains/Cardano/TransactionTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Cardano/Transaction.h" #include "Cardano/AddressV3.h" diff --git a/tests/chains/Celo/TWCoinTypeTests.cpp b/tests/chains/Celo/TWCoinTypeTests.cpp index 3a87c947e8e..6ac1b830db8 100644 --- a/tests/chains/Celo/TWCoinTypeTests.cpp +++ b/tests/chains/Celo/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/ConfluxeSpace/TWCoinTypeTests.cpp b/tests/chains/ConfluxeSpace/TWCoinTypeTests.cpp index 3724bab24fa..438632f7ad0 100644 --- a/tests/chains/ConfluxeSpace/TWCoinTypeTests.cpp +++ b/tests/chains/ConfluxeSpace/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/AddressTests.cpp b/tests/chains/Cosmos/AddressTests.cpp index e25dc5af853..97034a9b590 100644 --- a/tests/chains/Cosmos/AddressTests.cpp +++ b/tests/chains/Cosmos/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PrivateKey.h" diff --git a/tests/chains/Cosmos/Agoric/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Agoric/TWAnyAddressTests.cpp index 5c536171324..1cb7062bcb7 100644 --- a/tests/chains/Cosmos/Agoric/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Agoric/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/Agoric/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Agoric/TWCoinTypeTests.cpp index bcca563f288..05d975709ae 100644 --- a/tests/chains/Cosmos/Agoric/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Agoric/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/Akash/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Akash/TWAnyAddressTests.cpp index e334cc77ffc..621a43dd782 100644 --- a/tests/chains/Cosmos/Akash/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Akash/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/Akash/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Akash/TWCoinTypeTests.cpp index 41b8759c8c0..e13668774da 100644 --- a/tests/chains/Cosmos/Akash/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Akash/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/Axelar/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Axelar/TWAnyAddressTests.cpp index 0dd526419d0..b1e92806669 100644 --- a/tests/chains/Cosmos/Axelar/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Axelar/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/Axelar/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Axelar/TWCoinTypeTests.cpp index c60a76c9b56..69c7f823c5f 100644 --- a/tests/chains/Cosmos/Axelar/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Axelar/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/BandChain/TWCoinTypeTests.cpp b/tests/chains/Cosmos/BandChain/TWCoinTypeTests.cpp index 91b3138682b..d2a7096dd94 100644 --- a/tests/chains/Cosmos/BandChain/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/BandChain/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/Bluzelle/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Bluzelle/TWCoinTypeTests.cpp index c28de7f82c0..6a569a48677 100644 --- a/tests/chains/Cosmos/Bluzelle/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Bluzelle/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/Comdex/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Comdex/TWAnyAddressTests.cpp index abaeaaa5c2a..6ef956ac13e 100644 --- a/tests/chains/Cosmos/Comdex/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Comdex/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/Comdex/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Comdex/TWCoinTypeTests.cpp index 66360ab1d7a..1f598515c34 100644 --- a/tests/chains/Cosmos/Comdex/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Comdex/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/Coreum/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Coreum/TWAnyAddressTests.cpp index f550429739a..50a619ba0db 100644 --- a/tests/chains/Cosmos/Coreum/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Coreum/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/Coreum/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Coreum/TWCoinTypeTests.cpp index 46e83005f62..a588861349e 100644 --- a/tests/chains/Cosmos/Coreum/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Coreum/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/CosmosTestHelpers.h b/tests/chains/Cosmos/CosmosTestHelpers.h index d15a2ea34b9..50e81c4459f 100644 --- a/tests/chains/Cosmos/CosmosTestHelpers.h +++ b/tests/chains/Cosmos/CosmosTestHelpers.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/tests/chains/Cosmos/Crescent/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Crescent/TWAnyAddressTests.cpp index 08496bba5e8..f53619dd664 100644 --- a/tests/chains/Cosmos/Crescent/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Crescent/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/Crescent/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Crescent/TWCoinTypeTests.cpp index a7000b56edd..7b8586d04ec 100644 --- a/tests/chains/Cosmos/Crescent/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Crescent/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/CryptoOrg/SignerTests.cpp b/tests/chains/Cosmos/CryptoOrg/SignerTests.cpp index f71f90c5cff..c81ffb931af 100644 --- a/tests/chains/Cosmos/CryptoOrg/SignerTests.cpp +++ b/tests/chains/Cosmos/CryptoOrg/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "proto/Cosmos.pb.h" #include "Cosmos/Address.h" diff --git a/tests/chains/Cosmos/CryptoOrg/TWAnyAddressTests.cpp b/tests/chains/Cosmos/CryptoOrg/TWAnyAddressTests.cpp index 5946ec84956..cea33e461fd 100644 --- a/tests/chains/Cosmos/CryptoOrg/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/CryptoOrg/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/CryptoOrg/TWAnySignerTests.cpp b/tests/chains/Cosmos/CryptoOrg/TWAnySignerTests.cpp index f3db43120b7..c39786cac39 100644 --- a/tests/chains/Cosmos/CryptoOrg/TWAnySignerTests.cpp +++ b/tests/chains/Cosmos/CryptoOrg/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "proto/Cosmos.pb.h" diff --git a/tests/chains/Cosmos/CryptoOrg/TWCoinTypeTests.cpp b/tests/chains/Cosmos/CryptoOrg/TWCoinTypeTests.cpp index eb0ac2f4f84..6d301f20534 100644 --- a/tests/chains/Cosmos/CryptoOrg/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/CryptoOrg/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/FetchAI/TWAnyAddressTests.cpp b/tests/chains/Cosmos/FetchAI/TWAnyAddressTests.cpp index bd19c4f3120..1a0f8862c88 100644 --- a/tests/chains/Cosmos/FetchAI/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/FetchAI/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/FetchAI/TWCoinTypeTests.cpp b/tests/chains/Cosmos/FetchAI/TWCoinTypeTests.cpp index 294e824a13d..2653dc3ab4d 100644 --- a/tests/chains/Cosmos/FetchAI/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/FetchAI/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/Juno/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Juno/TWAnyAddressTests.cpp index 6796ff98934..0256957b57f 100644 --- a/tests/chains/Cosmos/Juno/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Juno/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/Juno/TWAnySignerTests.cpp b/tests/chains/Cosmos/Juno/TWAnySignerTests.cpp index dafa5aee10a..3fcb6facd0d 100644 --- a/tests/chains/Cosmos/Juno/TWAnySignerTests.cpp +++ b/tests/chains/Cosmos/Juno/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Cosmos/Address.h" #include "HexCoding.h" diff --git a/tests/chains/Cosmos/Juno/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Juno/TWCoinTypeTests.cpp index f78d7166920..e6683195b92 100644 --- a/tests/chains/Cosmos/Juno/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Juno/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/Kava/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Kava/TWCoinTypeTests.cpp index c9539ea88c1..25b8079bad6 100644 --- a/tests/chains/Cosmos/Kava/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Kava/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/Kujira/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Kujira/TWAnyAddressTests.cpp index a20ce637edd..803e8c6e2cf 100644 --- a/tests/chains/Cosmos/Kujira/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Kujira/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/Kujira/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Kujira/TWCoinTypeTests.cpp index 61ac1042d14..61583919905 100644 --- a/tests/chains/Cosmos/Kujira/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Kujira/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/Mars/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Mars/TWAnyAddressTests.cpp index 68fd646304b..16fd4812c92 100644 --- a/tests/chains/Cosmos/Mars/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Mars/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/Mars/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Mars/TWCoinTypeTests.cpp index e9d25b76a44..4a21d8a6b4d 100644 --- a/tests/chains/Cosmos/Mars/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Mars/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/NativeCanto/TWAnyAddressTests.cpp b/tests/chains/Cosmos/NativeCanto/TWAnyAddressTests.cpp index 6ffbe393b26..cfc4a2f6421 100644 --- a/tests/chains/Cosmos/NativeCanto/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/NativeCanto/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/NativeCanto/TWCoinTypeTests.cpp b/tests/chains/Cosmos/NativeCanto/TWCoinTypeTests.cpp index 6e7822af5be..cc282cd0e1f 100644 --- a/tests/chains/Cosmos/NativeCanto/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/NativeCanto/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/Cosmos/NativeEvmos/TWAnyAddressTests.cpp b/tests/chains/Cosmos/NativeEvmos/TWAnyAddressTests.cpp index 7331a3109e6..3dbb62b8cfd 100644 --- a/tests/chains/Cosmos/NativeEvmos/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/NativeEvmos/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/NativeEvmos/TWCoinTypeTests.cpp b/tests/chains/Cosmos/NativeEvmos/TWCoinTypeTests.cpp index 2713fcbba7e..052ffcf1038 100644 --- a/tests/chains/Cosmos/NativeEvmos/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/NativeEvmos/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/Cosmos/NativeInjective/SignerTests.cpp b/tests/chains/Cosmos/NativeInjective/SignerTests.cpp index a103ff8d09a..4b1ce6b5ec2 100644 --- a/tests/chains/Cosmos/NativeInjective/SignerTests.cpp +++ b/tests/chains/Cosmos/NativeInjective/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Base64.h" diff --git a/tests/chains/Cosmos/NativeInjective/TWAnyAddressTests.cpp b/tests/chains/Cosmos/NativeInjective/TWAnyAddressTests.cpp index f8425d00967..9dd4818c87c 100644 --- a/tests/chains/Cosmos/NativeInjective/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/NativeInjective/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/NativeInjective/TWCoinTypeTests.cpp b/tests/chains/Cosmos/NativeInjective/TWCoinTypeTests.cpp index f92a2f9ca7e..6a120226d4f 100644 --- a/tests/chains/Cosmos/NativeInjective/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/NativeInjective/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/NativeInjective/TransactionCompilerTests.cpp b/tests/chains/Cosmos/NativeInjective/TransactionCompilerTests.cpp index 163b19dfaf1..2bf1a12785a 100644 --- a/tests/chains/Cosmos/NativeInjective/TransactionCompilerTests.cpp +++ b/tests/chains/Cosmos/NativeInjective/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base64.h" #include "HexCoding.h" diff --git a/tests/chains/Cosmos/Neutron/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Neutron/TWAnyAddressTests.cpp index 9fe7681fa9a..662f74bd756 100644 --- a/tests/chains/Cosmos/Neutron/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Neutron/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/Neutron/TWAnySignerTests.cpp b/tests/chains/Cosmos/Neutron/TWAnySignerTests.cpp index 349a932d872..bafa42a9c7d 100644 --- a/tests/chains/Cosmos/Neutron/TWAnySignerTests.cpp +++ b/tests/chains/Cosmos/Neutron/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Cosmos/Address.h" #include "HexCoding.h" diff --git a/tests/chains/Cosmos/Neutron/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Neutron/TWCoinTypeTests.cpp index 8a768bd1647..0c5a3aa3978 100644 --- a/tests/chains/Cosmos/Neutron/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Neutron/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/Noble/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Noble/TWAnyAddressTests.cpp index 83e549af31a..4be942ba178 100644 --- a/tests/chains/Cosmos/Noble/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Noble/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/Noble/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Noble/TWCoinTypeTests.cpp index ea8c64905d1..f63dbdff7ee 100644 --- a/tests/chains/Cosmos/Noble/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Noble/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/Osmosis/SignerTests.cpp b/tests/chains/Cosmos/Osmosis/SignerTests.cpp index b0ae1832083..91935d871e5 100644 --- a/tests/chains/Cosmos/Osmosis/SignerTests.cpp +++ b/tests/chains/Cosmos/Osmosis/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Cosmos/Address.h" #include "HexCoding.h" diff --git a/tests/chains/Cosmos/Osmosis/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Osmosis/TWAnyAddressTests.cpp index 69eee155454..7979390c7a3 100644 --- a/tests/chains/Cosmos/Osmosis/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Osmosis/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/Osmosis/TWAnySignerTests.cpp b/tests/chains/Cosmos/Osmosis/TWAnySignerTests.cpp index a4cb692aacc..41dbaaf789c 100644 --- a/tests/chains/Cosmos/Osmosis/TWAnySignerTests.cpp +++ b/tests/chains/Cosmos/Osmosis/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Cosmos/Address.h" #include "HexCoding.h" diff --git a/tests/chains/Cosmos/Osmosis/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Osmosis/TWCoinTypeTests.cpp index 1f3fa3e3a50..ca043ca6fec 100644 --- a/tests/chains/Cosmos/Osmosis/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Osmosis/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/Persistence/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Persistence/TWAnyAddressTests.cpp index 7a645c96763..3219bf90f55 100644 --- a/tests/chains/Cosmos/Persistence/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Persistence/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/Persistence/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Persistence/TWCoinTypeTests.cpp index 72fc6b7f747..0669a3af94d 100644 --- a/tests/chains/Cosmos/Persistence/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Persistence/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/Quasar/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Quasar/TWAnyAddressTests.cpp index a65e76f7293..99b9eddc9eb 100644 --- a/tests/chains/Cosmos/Quasar/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Quasar/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/Quasar/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Quasar/TWCoinTypeTests.cpp index 1dfb9f81304..724a95988da 100644 --- a/tests/chains/Cosmos/Quasar/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Quasar/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/Secret/SignerTests.cpp b/tests/chains/Cosmos/Secret/SignerTests.cpp index 8ccb283c938..515419fd331 100644 --- a/tests/chains/Cosmos/Secret/SignerTests.cpp +++ b/tests/chains/Cosmos/Secret/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Cosmos/Address.h" #include "TrustWalletCore/TWAnySigner.h" diff --git a/tests/chains/Cosmos/Secret/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Secret/TWAnyAddressTests.cpp index e3f9452e1f3..945f5192010 100644 --- a/tests/chains/Cosmos/Secret/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Secret/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/Secret/TWAnySignerTests.cpp b/tests/chains/Cosmos/Secret/TWAnySignerTests.cpp index 9abb485b2d3..95a0f4712b0 100644 --- a/tests/chains/Cosmos/Secret/TWAnySignerTests.cpp +++ b/tests/chains/Cosmos/Secret/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Cosmos/Address.h" #include "HexCoding.h" diff --git a/tests/chains/Cosmos/Secret/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Secret/TWCoinTypeTests.cpp index d6161fa2fea..aa77bd0ac21 100644 --- a/tests/chains/Cosmos/Secret/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Secret/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/Sei/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Sei/TWAnyAddressTests.cpp index 38a5517203b..45746b25326 100644 --- a/tests/chains/Cosmos/Sei/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Sei/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/Sei/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Sei/TWCoinTypeTests.cpp index e65ff37ce92..88a856d389a 100644 --- a/tests/chains/Cosmos/Sei/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Sei/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/SignerTests.cpp b/tests/chains/Cosmos/SignerTests.cpp index 54b5fbb9344..6e5570004d4 100644 --- a/tests/chains/Cosmos/SignerTests.cpp +++ b/tests/chains/Cosmos/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Cosmos/Sommelier/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Sommelier/TWAnyAddressTests.cpp index 48295f5e549..2775fcb56ab 100644 --- a/tests/chains/Cosmos/Sommelier/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Sommelier/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/Sommelier/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Sommelier/TWCoinTypeTests.cpp index 7c73cdd2a72..f60eb5df59d 100644 --- a/tests/chains/Cosmos/Sommelier/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Sommelier/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/StakingTests.cpp b/tests/chains/Cosmos/StakingTests.cpp index ad800b54184..ae5be9dd728 100644 --- a/tests/chains/Cosmos/StakingTests.cpp +++ b/tests/chains/Cosmos/StakingTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base64.h" #include "Coin.h" diff --git a/tests/chains/Cosmos/Stargaze/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Stargaze/TWAnyAddressTests.cpp index e07e1eb6ecb..0fb293d3483 100644 --- a/tests/chains/Cosmos/Stargaze/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Stargaze/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "TestUtilities.h" diff --git a/tests/chains/Cosmos/Stargaze/TWAnySignerTests.cpp b/tests/chains/Cosmos/Stargaze/TWAnySignerTests.cpp index efde5f8a471..c6e1faec3e1 100644 --- a/tests/chains/Cosmos/Stargaze/TWAnySignerTests.cpp +++ b/tests/chains/Cosmos/Stargaze/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Cosmos/Address.h" #include "HexCoding.h" diff --git a/tests/chains/Cosmos/Stride/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Stride/TWAnyAddressTests.cpp index 26b6e7a129b..9a2035d95c7 100644 --- a/tests/chains/Cosmos/Stride/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Stride/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" #include "TestUtilities.h" diff --git a/tests/chains/Cosmos/Stride/TWAnySignerTests.cpp b/tests/chains/Cosmos/Stride/TWAnySignerTests.cpp index f125a955553..b076cb54de4 100644 --- a/tests/chains/Cosmos/Stride/TWAnySignerTests.cpp +++ b/tests/chains/Cosmos/Stride/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Cosmos/Address.h" #include "HexCoding.h" diff --git a/tests/chains/Cosmos/Stride/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Stride/TWCoinTypeTests.cpp index dafa7950af6..6003f28894a 100644 --- a/tests/chains/Cosmos/Stride/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Stride/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/THORChain/SignerTests.cpp b/tests/chains/Cosmos/THORChain/SignerTests.cpp index fcb07cae3a9..be2e0a334b3 100644 --- a/tests/chains/Cosmos/THORChain/SignerTests.cpp +++ b/tests/chains/Cosmos/THORChain/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "proto/Cosmos.pb.h" #include "Coin.h" diff --git a/tests/chains/Cosmos/THORChain/SwapTests.cpp b/tests/chains/Cosmos/THORChain/SwapTests.cpp index 69901c310b2..44b11f6d686 100644 --- a/tests/chains/Cosmos/THORChain/SwapTests.cpp +++ b/tests/chains/Cosmos/THORChain/SwapTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Binance/Address.h" #include "Bitcoin/Script.h" diff --git a/tests/chains/Cosmos/THORChain/TWAnyAddressTests.cpp b/tests/chains/Cosmos/THORChain/TWAnyAddressTests.cpp index c1bd962e788..f28a401908e 100644 --- a/tests/chains/Cosmos/THORChain/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/THORChain/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "HexCoding.h" diff --git a/tests/chains/Cosmos/THORChain/TWAnySignerTests.cpp b/tests/chains/Cosmos/THORChain/TWAnySignerTests.cpp index a9539054504..a33c5df584e 100644 --- a/tests/chains/Cosmos/THORChain/TWAnySignerTests.cpp +++ b/tests/chains/Cosmos/THORChain/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/tests/chains/Cosmos/THORChain/TWCoinTypeTests.cpp b/tests/chains/Cosmos/THORChain/TWCoinTypeTests.cpp index 3ac726f0c45..65b8c1ee908 100644 --- a/tests/chains/Cosmos/THORChain/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/THORChain/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/THORChain/TWSwapTests.cpp b/tests/chains/Cosmos/THORChain/TWSwapTests.cpp index 9632e015627..df999de49ad 100644 --- a/tests/chains/Cosmos/THORChain/TWSwapTests.cpp +++ b/tests/chains/Cosmos/THORChain/TWSwapTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bitcoin/Script.h" #include "Bitcoin/SegwitAddress.h" diff --git a/tests/chains/Cosmos/TWAnyAddressTests.cpp b/tests/chains/Cosmos/TWAnyAddressTests.cpp index b6387f7bd57..6f520edca1b 100644 --- a/tests/chains/Cosmos/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/TWAnySignerTests.cpp b/tests/chains/Cosmos/TWAnySignerTests.cpp index c2c25f96c17..2ecc82de1aa 100644 --- a/tests/chains/Cosmos/TWAnySignerTests.cpp +++ b/tests/chains/Cosmos/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Cosmos/Address.h" #include "HexCoding.h" diff --git a/tests/chains/Cosmos/TWCoinTypeTests.cpp b/tests/chains/Cosmos/TWCoinTypeTests.cpp index e51876c3d65..01cbdd3c12b 100644 --- a/tests/chains/Cosmos/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/Terra/SignerTests.cpp b/tests/chains/Cosmos/Terra/SignerTests.cpp index aa6004567da..0b8bb3f92a3 100644 --- a/tests/chains/Cosmos/Terra/SignerTests.cpp +++ b/tests/chains/Cosmos/Terra/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Base64.h" diff --git a/tests/chains/Cosmos/Terra/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Terra/TWCoinTypeTests.cpp index c99684d0a4e..b564ac066b9 100644 --- a/tests/chains/Cosmos/Terra/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Terra/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/Cosmos/TerraV2/SignerTests.cpp b/tests/chains/Cosmos/TerraV2/SignerTests.cpp index b6f94de7972..7e382641480 100644 --- a/tests/chains/Cosmos/TerraV2/SignerTests.cpp +++ b/tests/chains/Cosmos/TerraV2/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Base64.h" diff --git a/tests/chains/Cosmos/TerraV2/TWCoinTypeTests.cpp b/tests/chains/Cosmos/TerraV2/TWCoinTypeTests.cpp index 5f8cab7ea50..a38f54b77e8 100644 --- a/tests/chains/Cosmos/TerraV2/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/TerraV2/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/Cosmos/Tia/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Tia/TWAnyAddressTests.cpp index 7a2c17bad5b..29c181f36b2 100644 --- a/tests/chains/Cosmos/Tia/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Tia/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/Tia/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Tia/TWCoinTypeTests.cpp index a3d656fd5f6..17f8d8d59a0 100644 --- a/tests/chains/Cosmos/Tia/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Tia/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cosmos/TransactionCompilerTests.cpp b/tests/chains/Cosmos/TransactionCompilerTests.cpp index 49284252185..e55e966a13f 100644 --- a/tests/chains/Cosmos/TransactionCompilerTests.cpp +++ b/tests/chains/Cosmos/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base64.h" #include "Coin.h" diff --git a/tests/chains/Cosmos/Umee/TWAnyAddressTests.cpp b/tests/chains/Cosmos/Umee/TWAnyAddressTests.cpp index 7dda7239282..2e1ed50c9f3 100644 --- a/tests/chains/Cosmos/Umee/TWAnyAddressTests.cpp +++ b/tests/chains/Cosmos/Umee/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../CosmosTestHelpers.h" diff --git a/tests/chains/Cosmos/Umee/TWCoinTypeTests.cpp b/tests/chains/Cosmos/Umee/TWCoinTypeTests.cpp index 44d47ed62bb..be9788b42c0 100644 --- a/tests/chains/Cosmos/Umee/TWCoinTypeTests.cpp +++ b/tests/chains/Cosmos/Umee/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Cronos/TWAnyAddressTests.cpp b/tests/chains/Cronos/TWAnyAddressTests.cpp index df3c573e96b..2e1c6a0dbdd 100644 --- a/tests/chains/Cronos/TWAnyAddressTests.cpp +++ b/tests/chains/Cronos/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Cronos/TWCoinTypeTests.cpp b/tests/chains/Cronos/TWCoinTypeTests.cpp index 0ebc2dfa068..0b05cd3f785 100644 --- a/tests/chains/Cronos/TWCoinTypeTests.cpp +++ b/tests/chains/Cronos/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/Dash/TWCoinTypeTests.cpp b/tests/chains/Dash/TWCoinTypeTests.cpp index aa99c4d138b..6b6cab670eb 100644 --- a/tests/chains/Dash/TWCoinTypeTests.cpp +++ b/tests/chains/Dash/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Dash/TWDashTests.cpp b/tests/chains/Dash/TWDashTests.cpp index 0fe1335f1f6..2130faba855 100644 --- a/tests/chains/Dash/TWDashTests.cpp +++ b/tests/chains/Dash/TWDashTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Decred/AddressTests.cpp b/tests/chains/Decred/AddressTests.cpp index a8f65f33f0c..51c855ce811 100644 --- a/tests/chains/Decred/AddressTests.cpp +++ b/tests/chains/Decred/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Decred/Address.h" diff --git a/tests/chains/Decred/SignerTests.cpp b/tests/chains/Decred/SignerTests.cpp index f519fa0e8a2..e698910b17c 100644 --- a/tests/chains/Decred/SignerTests.cpp +++ b/tests/chains/Decred/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Decred/Address.h" #include "Decred/Signer.h" diff --git a/tests/chains/Decred/TWAnySignerTests.cpp b/tests/chains/Decred/TWAnySignerTests.cpp index 08ba5bc332e..faafc923616 100644 --- a/tests/chains/Decred/TWAnySignerTests.cpp +++ b/tests/chains/Decred/TWAnySignerTests.cpp @@ -1,9 +1,7 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Decred/TWCoinTypeTests.cpp b/tests/chains/Decred/TWCoinTypeTests.cpp index 592a69ba83d..2a385b663bc 100644 --- a/tests/chains/Decred/TWCoinTypeTests.cpp +++ b/tests/chains/Decred/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Decred/TWDecredTests.cpp b/tests/chains/Decred/TWDecredTests.cpp index 141d8c4731b..647df8ed1e6 100644 --- a/tests/chains/Decred/TWDecredTests.cpp +++ b/tests/chains/Decred/TWDecredTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Decred/TransactionCompilerTests.cpp b/tests/chains/Decred/TransactionCompilerTests.cpp index 55520304c62..23d49b69bf1 100644 --- a/tests/chains/Decred/TransactionCompilerTests.cpp +++ b/tests/chains/Decred/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bitcoin/Script.h" #include "Coin.h" diff --git a/tests/chains/Decred/TransactionTests.cpp b/tests/chains/Decred/TransactionTests.cpp index 098f42c1d11..30414251e4f 100644 --- a/tests/chains/Decred/TransactionTests.cpp +++ b/tests/chains/Decred/TransactionTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bitcoin/Script.h" #include "Decred/OutPoint.h" diff --git a/tests/chains/DigiByte/TWCoinTypeTests.cpp b/tests/chains/DigiByte/TWCoinTypeTests.cpp index aa17445e336..af6949e5c34 100644 --- a/tests/chains/DigiByte/TWCoinTypeTests.cpp +++ b/tests/chains/DigiByte/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/DigiByte/TWDigiByteTests.cpp b/tests/chains/DigiByte/TWDigiByteTests.cpp index 456ca49543e..fe907773a33 100644 --- a/tests/chains/DigiByte/TWDigiByteTests.cpp +++ b/tests/chains/DigiByte/TWDigiByteTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Dogecoin/TWCoinTypeTests.cpp b/tests/chains/Dogecoin/TWCoinTypeTests.cpp index b816cb968a7..c08b7a08bdc 100644 --- a/tests/chains/Dogecoin/TWCoinTypeTests.cpp +++ b/tests/chains/Dogecoin/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Dogecoin/TWDogeTests.cpp b/tests/chains/Dogecoin/TWDogeTests.cpp index 185a1a773a2..9ee431a3414 100644 --- a/tests/chains/Dogecoin/TWDogeTests.cpp +++ b/tests/chains/Dogecoin/TWDogeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/ECO/TWCoinTypeTests.cpp b/tests/chains/ECO/TWCoinTypeTests.cpp index af76592af3a..c982259660f 100644 --- a/tests/chains/ECO/TWCoinTypeTests.cpp +++ b/tests/chains/ECO/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // #include "TestUtilities.h" diff --git a/tests/chains/ECash/TWCoinTypeTests.cpp b/tests/chains/ECash/TWCoinTypeTests.cpp index a767b14c3e5..f6487e721d9 100644 --- a/tests/chains/ECash/TWCoinTypeTests.cpp +++ b/tests/chains/ECash/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/EOS/AddressTests.cpp b/tests/chains/EOS/AddressTests.cpp index 8a14d9059cb..28aed7426a4 100644 --- a/tests/chains/EOS/AddressTests.cpp +++ b/tests/chains/EOS/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "EOS/Address.h" #include "HexCoding.h" diff --git a/tests/chains/EOS/AssetTests.cpp b/tests/chains/EOS/AssetTests.cpp index 05090a56e45..23ae9852a69 100644 --- a/tests/chains/EOS/AssetTests.cpp +++ b/tests/chains/EOS/AssetTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "EOS/Asset.h" #include "HexCoding.h" diff --git a/tests/chains/EOS/NameTests.cpp b/tests/chains/EOS/NameTests.cpp index bc7e74e3068..03f80ce53af 100644 --- a/tests/chains/EOS/NameTests.cpp +++ b/tests/chains/EOS/NameTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "EOS/Name.h" #include "HexCoding.h" diff --git a/tests/chains/EOS/SignatureTests.cpp b/tests/chains/EOS/SignatureTests.cpp index 8ebeb3b815a..0650c6432fb 100644 --- a/tests/chains/EOS/SignatureTests.cpp +++ b/tests/chains/EOS/SignatureTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "EOS/Transaction.h" #include "HexCoding.h" diff --git a/tests/chains/EOS/TWAnySignerTests.cpp b/tests/chains/EOS/TWAnySignerTests.cpp index 1b2face2c2d..575e71566c8 100644 --- a/tests/chains/EOS/TWAnySignerTests.cpp +++ b/tests/chains/EOS/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/EOS/TWCoinTypeTests.cpp b/tests/chains/EOS/TWCoinTypeTests.cpp index d1e857993de..9996fd7467f 100644 --- a/tests/chains/EOS/TWCoinTypeTests.cpp +++ b/tests/chains/EOS/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/EOS/TransactionCompilerTests.cpp b/tests/chains/EOS/TransactionCompilerTests.cpp index 81954d4506a..bb4bb23f72b 100644 --- a/tests/chains/EOS/TransactionCompilerTests.cpp +++ b/tests/chains/EOS/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/EOS/TransactionTests.cpp b/tests/chains/EOS/TransactionTests.cpp index 6ce41687ca0..013db25ed5a 100644 --- a/tests/chains/EOS/TransactionTests.cpp +++ b/tests/chains/EOS/TransactionTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "EOS/Action.h" #include "EOS/Address.h" diff --git a/tests/chains/Ethereum/AddressTests.cpp b/tests/chains/Ethereum/AddressTests.cpp index 1a6da54e421..b2b320cf82e 100644 --- a/tests/chains/Ethereum/AddressTests.cpp +++ b/tests/chains/Ethereum/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Ethereum/Address.h" #include "HexCoding.h" diff --git a/tests/chains/Ethereum/BarzTests.cpp b/tests/chains/Ethereum/BarzTests.cpp index 7fbf6d9875e..6d4bc6dfc45 100644 --- a/tests/chains/Ethereum/BarzTests.cpp +++ b/tests/chains/Ethereum/BarzTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/Ethereum/ContractCallTests.cpp b/tests/chains/Ethereum/ContractCallTests.cpp index 164a9fc332b..8ddf3cddad3 100644 --- a/tests/chains/Ethereum/ContractCallTests.cpp +++ b/tests/chains/Ethereum/ContractCallTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Ethereum/ContractCall.h" #include "HexCoding.h" diff --git a/tests/chains/Ethereum/EIP1014Tests.cpp b/tests/chains/Ethereum/EIP1014Tests.cpp index a13b9764090..6b53150ee4a 100644 --- a/tests/chains/Ethereum/EIP1014Tests.cpp +++ b/tests/chains/Ethereum/EIP1014Tests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/Ethereum/EIP1967Tests.cpp b/tests/chains/Ethereum/EIP1967Tests.cpp index 58a7313dfb1..e0cd759e8f6 100644 --- a/tests/chains/Ethereum/EIP1967Tests.cpp +++ b/tests/chains/Ethereum/EIP1967Tests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/Ethereum/EthereumMessageSignerTests.cpp b/tests/chains/Ethereum/EthereumMessageSignerTests.cpp index d119f4006da..6165fe32e47 100644 --- a/tests/chains/Ethereum/EthereumMessageSignerTests.cpp +++ b/tests/chains/Ethereum/EthereumMessageSignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/Ethereum/TWAnySignerTests.cpp b/tests/chains/Ethereum/TWAnySignerTests.cpp index c26d026c51b..f1ffacc0a1e 100644 --- a/tests/chains/Ethereum/TWAnySignerTests.cpp +++ b/tests/chains/Ethereum/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/Ethereum/TWCoinTypeTests.cpp b/tests/chains/Ethereum/TWCoinTypeTests.cpp index 4ca7b327030..4f205ff7a76 100644 --- a/tests/chains/Ethereum/TWCoinTypeTests.cpp +++ b/tests/chains/Ethereum/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Ethereum/TWEthereumAbiTests.cpp b/tests/chains/Ethereum/TWEthereumAbiTests.cpp index 56fc343c08a..607c2c58193 100644 --- a/tests/chains/Ethereum/TWEthereumAbiTests.cpp +++ b/tests/chains/Ethereum/TWEthereumAbiTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/tests/chains/Ethereum/TWEthereumAbiValueDecoderTests.cpp b/tests/chains/Ethereum/TWEthereumAbiValueDecoderTests.cpp index b5d7ac0aef8..ba4607d10ca 100644 --- a/tests/chains/Ethereum/TWEthereumAbiValueDecoderTests.cpp +++ b/tests/chains/Ethereum/TWEthereumAbiValueDecoderTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/tests/chains/Ethereum/TWEthereumAbiValueEncodeTests.cpp b/tests/chains/Ethereum/TWEthereumAbiValueEncodeTests.cpp index bedfd79585d..1d73afc40e6 100644 --- a/tests/chains/Ethereum/TWEthereumAbiValueEncodeTests.cpp +++ b/tests/chains/Ethereum/TWEthereumAbiValueEncodeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/tests/chains/Ethereum/TWRlpTests.cpp b/tests/chains/Ethereum/TWRlpTests.cpp index 745f3467a59..661529562dc 100644 --- a/tests/chains/Ethereum/TWRlpTests.cpp +++ b/tests/chains/Ethereum/TWRlpTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TrustWalletCore/TWEthereumRlp.h" #include "proto/EthereumRlp.pb.h" diff --git a/tests/chains/Ethereum/TransactionCompilerTests.cpp b/tests/chains/Ethereum/TransactionCompilerTests.cpp index 64c208d5bb5..b8f5ceae311 100644 --- a/tests/chains/Ethereum/TransactionCompilerTests.cpp +++ b/tests/chains/Ethereum/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Ethereum/ValueDecoderTests.cpp b/tests/chains/Ethereum/ValueDecoderTests.cpp index 2f9e547dea5..8a47e82e822 100644 --- a/tests/chains/Ethereum/ValueDecoderTests.cpp +++ b/tests/chains/Ethereum/ValueDecoderTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Ethereum/ABI/ValueDecoder.h" #include diff --git a/tests/chains/Ethereum/ValueEncoderTests.cpp b/tests/chains/Ethereum/ValueEncoderTests.cpp index 83b3dc913c9..58cca246524 100644 --- a/tests/chains/Ethereum/ValueEncoderTests.cpp +++ b/tests/chains/Ethereum/ValueEncoderTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Ethereum/ABI/ValueEncoder.h" #include diff --git a/tests/chains/EthereumClassic/TWCoinTypeTests.cpp b/tests/chains/EthereumClassic/TWCoinTypeTests.cpp index 2181cda4701..e547ced83a0 100644 --- a/tests/chains/EthereumClassic/TWCoinTypeTests.cpp +++ b/tests/chains/EthereumClassic/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Everscale/AddressTests.cpp b/tests/chains/Everscale/AddressTests.cpp index 21f8e41b49d..f984140bd74 100644 --- a/tests/chains/Everscale/AddressTests.cpp +++ b/tests/chains/Everscale/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Everscale/Address.h" #include "Everscale/WorkchainType.h" diff --git a/tests/chains/Everscale/CellBuilderTest.cpp b/tests/chains/Everscale/CellBuilderTest.cpp index a2393bbbdad..73ad08f382b 100644 --- a/tests/chains/Everscale/CellBuilderTest.cpp +++ b/tests/chains/Everscale/CellBuilderTest.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "BinaryCoding.h" #include "HexCoding.h" diff --git a/tests/chains/Everscale/CellTests.cpp b/tests/chains/Everscale/CellTests.cpp index efd7bf22480..8fb9c913cd5 100644 --- a/tests/chains/Everscale/CellTests.cpp +++ b/tests/chains/Everscale/CellTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base64.h" #include "HexCoding.h" diff --git a/tests/chains/Everscale/SignerTests.cpp b/tests/chains/Everscale/SignerTests.cpp index 73e3ba86922..832eb068f80 100644 --- a/tests/chains/Everscale/SignerTests.cpp +++ b/tests/chains/Everscale/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Everscale/Messages.h" #include "Everscale/Signer.h" diff --git a/tests/chains/Everscale/TWAnyAddressTests.cpp b/tests/chains/Everscale/TWAnyAddressTests.cpp index 87ea9c497b3..743f8c56d79 100644 --- a/tests/chains/Everscale/TWAnyAddressTests.cpp +++ b/tests/chains/Everscale/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/tests/chains/Everscale/TWAnySignerTests.cpp b/tests/chains/Everscale/TWAnySignerTests.cpp index f185ceb86b9..eb101efdee8 100644 --- a/tests/chains/Everscale/TWAnySignerTests.cpp +++ b/tests/chains/Everscale/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base64.h" #include "HexCoding.h" diff --git a/tests/chains/Everscale/TWCoinTypeTests.cpp b/tests/chains/Everscale/TWCoinTypeTests.cpp index 27650c73e7a..f4e59de8cd9 100644 --- a/tests/chains/Everscale/TWCoinTypeTests.cpp +++ b/tests/chains/Everscale/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Evmos/SignerTests.cpp b/tests/chains/Evmos/SignerTests.cpp index 981bd727b27..aba0816b53a 100644 --- a/tests/chains/Evmos/SignerTests.cpp +++ b/tests/chains/Evmos/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Base64.h" diff --git a/tests/chains/Evmos/TWAnyAddressTests.cpp b/tests/chains/Evmos/TWAnyAddressTests.cpp index 697b9400dd4..d36bfa9c64f 100644 --- a/tests/chains/Evmos/TWAnyAddressTests.cpp +++ b/tests/chains/Evmos/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Evmos/TWCoinTypeTests.cpp b/tests/chains/Evmos/TWCoinTypeTests.cpp index 73f12b7b68e..781e1e4e22b 100644 --- a/tests/chains/Evmos/TWCoinTypeTests.cpp +++ b/tests/chains/Evmos/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/Evmos/TransactionCompilerTests.cpp b/tests/chains/Evmos/TransactionCompilerTests.cpp index c58e374fb1d..47a700bdf94 100644 --- a/tests/chains/Evmos/TransactionCompilerTests.cpp +++ b/tests/chains/Evmos/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base64.h" #include "HexCoding.h" diff --git a/tests/chains/FIO/AddressTests.cpp b/tests/chains/FIO/AddressTests.cpp index 673d4dc130f..ffc99746168 100644 --- a/tests/chains/FIO/AddressTests.cpp +++ b/tests/chains/FIO/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "FIO/Address.h" #include "HexCoding.h" diff --git a/tests/chains/FIO/EncryptionTests.cpp b/tests/chains/FIO/EncryptionTests.cpp index 4065a54199e..db8341d9eb4 100644 --- a/tests/chains/FIO/EncryptionTests.cpp +++ b/tests/chains/FIO/EncryptionTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "FIO/Encryption.h" #include "FIO/Address.h" diff --git a/tests/chains/FIO/SignerTests.cpp b/tests/chains/FIO/SignerTests.cpp index 16d6299c4e2..db663c9cf0a 100644 --- a/tests/chains/FIO/SignerTests.cpp +++ b/tests/chains/FIO/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "FIO/Actor.h" #include "FIO/Signer.h" diff --git a/tests/chains/FIO/TWCoinTypeTests.cpp b/tests/chains/FIO/TWCoinTypeTests.cpp index 4279a35fc85..e961f70263e 100644 --- a/tests/chains/FIO/TWCoinTypeTests.cpp +++ b/tests/chains/FIO/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/FIO/TWFIOAccountTests.cpp b/tests/chains/FIO/TWFIOAccountTests.cpp index fc9b0e8a900..2afa675188e 100644 --- a/tests/chains/FIO/TWFIOAccountTests.cpp +++ b/tests/chains/FIO/TWFIOAccountTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/FIO/TWFIOTests.cpp b/tests/chains/FIO/TWFIOTests.cpp index f3d0290515a..77b6acdffaf 100644 --- a/tests/chains/FIO/TWFIOTests.cpp +++ b/tests/chains/FIO/TWFIOTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/tests/chains/FIO/TransactionBuilderTests.cpp b/tests/chains/FIO/TransactionBuilderTests.cpp index cc11ba5dfd6..097d587b341 100644 --- a/tests/chains/FIO/TransactionBuilderTests.cpp +++ b/tests/chains/FIO/TransactionBuilderTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "FIO/Action.h" #include "FIO/NewFundsRequest.h" diff --git a/tests/chains/FIO/TransactionCompilerTests.cpp b/tests/chains/FIO/TransactionCompilerTests.cpp index 6fe67156e4c..cfdba2b1fee 100644 --- a/tests/chains/FIO/TransactionCompilerTests.cpp +++ b/tests/chains/FIO/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Fantom/TWCoinTypeTests.cpp b/tests/chains/Fantom/TWCoinTypeTests.cpp index 9d80853a8a8..e574fc3b825 100644 --- a/tests/chains/Fantom/TWCoinTypeTests.cpp +++ b/tests/chains/Fantom/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Filecoin/AddressTests.cpp b/tests/chains/Filecoin/AddressTests.cpp index 06b889d4376..ebe9dc553c1 100644 --- a/tests/chains/Filecoin/AddressTests.cpp +++ b/tests/chains/Filecoin/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Filecoin/Address.h" #include "HexCoding.h" diff --git a/tests/chains/Filecoin/SignerTests.cpp b/tests/chains/Filecoin/SignerTests.cpp index 4f6452d8518..8eccd8f6f76 100644 --- a/tests/chains/Filecoin/SignerTests.cpp +++ b/tests/chains/Filecoin/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Filecoin/Address.h" #include "Filecoin/Signer.h" diff --git a/tests/chains/Filecoin/TWAddressConverterTests.cpp b/tests/chains/Filecoin/TWAddressConverterTests.cpp index 0c8851ab2f2..b50eb9e58aa 100644 --- a/tests/chains/Filecoin/TWAddressConverterTests.cpp +++ b/tests/chains/Filecoin/TWAddressConverterTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/tests/chains/Filecoin/TWAnySignerTests.cpp b/tests/chains/Filecoin/TWAnySignerTests.cpp index 1074abf4fa9..27b8eaacf9c 100644 --- a/tests/chains/Filecoin/TWAnySignerTests.cpp +++ b/tests/chains/Filecoin/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/Filecoin/TWCoinTypeTests.cpp b/tests/chains/Filecoin/TWCoinTypeTests.cpp index 6292d0be1e5..8fd34f04380 100644 --- a/tests/chains/Filecoin/TWCoinTypeTests.cpp +++ b/tests/chains/Filecoin/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Filecoin/TransactionCompilerTests.cpp b/tests/chains/Filecoin/TransactionCompilerTests.cpp index 53e7a4976d2..f04883c127d 100644 --- a/tests/chains/Filecoin/TransactionCompilerTests.cpp +++ b/tests/chains/Filecoin/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Filecoin/TransactionTests.cpp b/tests/chains/Filecoin/TransactionTests.cpp index 8f1026af7ec..82650f13499 100644 --- a/tests/chains/Filecoin/TransactionTests.cpp +++ b/tests/chains/Filecoin/TransactionTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Filecoin/Address.h" #include "Filecoin/Transaction.h" diff --git a/tests/chains/Firo/TWCoinTypeTests.cpp b/tests/chains/Firo/TWCoinTypeTests.cpp index 4e5a5752172..8a29b13ddd0 100644 --- a/tests/chains/Firo/TWCoinTypeTests.cpp +++ b/tests/chains/Firo/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Firo/TWZCoinAddressTests.cpp b/tests/chains/Firo/TWZCoinAddressTests.cpp index 415528c742b..4889637d0fe 100644 --- a/tests/chains/Firo/TWZCoinAddressTests.cpp +++ b/tests/chains/Firo/TWZCoinAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/GoChain/TWCoinTypeTests.cpp b/tests/chains/GoChain/TWCoinTypeTests.cpp index c2e469658c5..052a2700c5a 100644 --- a/tests/chains/GoChain/TWCoinTypeTests.cpp +++ b/tests/chains/GoChain/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Greenfield/TWAnyAddressTests.cpp b/tests/chains/Greenfield/TWAnyAddressTests.cpp index eb6b19293fe..5e4736f9de0 100644 --- a/tests/chains/Greenfield/TWAnyAddressTests.cpp +++ b/tests/chains/Greenfield/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "HexCoding.h" diff --git a/tests/chains/Greenfield/TWAnySignerTests.cpp b/tests/chains/Greenfield/TWAnySignerTests.cpp index c8ba0f795c7..12afa89b367 100644 --- a/tests/chains/Greenfield/TWAnySignerTests.cpp +++ b/tests/chains/Greenfield/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "proto/Greenfield.pb.h" diff --git a/tests/chains/Greenfield/TWCoinTypeTests.cpp b/tests/chains/Greenfield/TWCoinTypeTests.cpp index 657ba1fca39..37292589ba1 100644 --- a/tests/chains/Greenfield/TWCoinTypeTests.cpp +++ b/tests/chains/Greenfield/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Greenfield/TransactionCompilerTests.cpp b/tests/chains/Greenfield/TransactionCompilerTests.cpp index ba5220babe1..9766bbcaa0c 100644 --- a/tests/chains/Greenfield/TransactionCompilerTests.cpp +++ b/tests/chains/Greenfield/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PrivateKey.h" diff --git a/tests/chains/Groestlcoin/AddressTests.cpp b/tests/chains/Groestlcoin/AddressTests.cpp index 40c335a9f5b..8fcce675012 100644 --- a/tests/chains/Groestlcoin/AddressTests.cpp +++ b/tests/chains/Groestlcoin/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Groestlcoin/Address.h" diff --git a/tests/chains/Groestlcoin/TWCoinTypeTests.cpp b/tests/chains/Groestlcoin/TWCoinTypeTests.cpp index d772b967c38..974fdbe9881 100644 --- a/tests/chains/Groestlcoin/TWCoinTypeTests.cpp +++ b/tests/chains/Groestlcoin/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Groestlcoin/TWGroestlcoinSigningTests.cpp b/tests/chains/Groestlcoin/TWGroestlcoinSigningTests.cpp index 02cd09d6e53..db0c4dc4702 100644 --- a/tests/chains/Groestlcoin/TWGroestlcoinSigningTests.cpp +++ b/tests/chains/Groestlcoin/TWGroestlcoinSigningTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bitcoin/Script.h" #include "Groestlcoin/Signer.h" diff --git a/tests/chains/Groestlcoin/TWGroestlcoinTests.cpp b/tests/chains/Groestlcoin/TWGroestlcoinTests.cpp index 9dab12a0c3a..bb186cfd48f 100644 --- a/tests/chains/Groestlcoin/TWGroestlcoinTests.cpp +++ b/tests/chains/Groestlcoin/TWGroestlcoinTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Groestlcoin/TransactionCompilerTests.cpp b/tests/chains/Groestlcoin/TransactionCompilerTests.cpp index 819d5b3f522..dfe513977cf 100644 --- a/tests/chains/Groestlcoin/TransactionCompilerTests.cpp +++ b/tests/chains/Groestlcoin/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Harmony/AddressTests.cpp b/tests/chains/Harmony/AddressTests.cpp index 9bee16bec9d..3d7786b76be 100644 --- a/tests/chains/Harmony/AddressTests.cpp +++ b/tests/chains/Harmony/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Harmony/Address.h" #include "HexCoding.h" diff --git a/tests/chains/Harmony/SignerTests.cpp b/tests/chains/Harmony/SignerTests.cpp index 33e4b97e391..dc917bf26b5 100644 --- a/tests/chains/Harmony/SignerTests.cpp +++ b/tests/chains/Harmony/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/tests/chains/Harmony/StakingTests.cpp b/tests/chains/Harmony/StakingTests.cpp index 9f31e73339f..bbcce90bb7d 100644 --- a/tests/chains/Harmony/StakingTests.cpp +++ b/tests/chains/Harmony/StakingTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base64.h" #include "Coin.h" diff --git a/tests/chains/Harmony/TWAnyAddressTests.cpp b/tests/chains/Harmony/TWAnyAddressTests.cpp index 5aec01a7e94..70cd48bfa18 100644 --- a/tests/chains/Harmony/TWAnyAddressTests.cpp +++ b/tests/chains/Harmony/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Harmony/TWAnySignerTests.cpp b/tests/chains/Harmony/TWAnySignerTests.cpp index 6613c7115ab..24c722b2646 100644 --- a/tests/chains/Harmony/TWAnySignerTests.cpp +++ b/tests/chains/Harmony/TWAnySignerTests.cpp @@ -1,9 +1,7 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "proto/Harmony.pb.h" diff --git a/tests/chains/Harmony/TWCoinTypeTests.cpp b/tests/chains/Harmony/TWCoinTypeTests.cpp index eeeb509a962..66cb9c51292 100644 --- a/tests/chains/Harmony/TWCoinTypeTests.cpp +++ b/tests/chains/Harmony/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Harmony/TWHarmonyStakingTests.cpp b/tests/chains/Harmony/TWHarmonyStakingTests.cpp index be9d1a07fea..5c0283c81f0 100644 --- a/tests/chains/Harmony/TWHarmonyStakingTests.cpp +++ b/tests/chains/Harmony/TWHarmonyStakingTests.cpp @@ -1,9 +1,7 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Data.h" #include "Harmony/Staking.h" diff --git a/tests/chains/Harmony/TransactionCompilerTests.cpp b/tests/chains/Harmony/TransactionCompilerTests.cpp index 9de42582fff..5cc34ad9765 100644 --- a/tests/chains/Harmony/TransactionCompilerTests.cpp +++ b/tests/chains/Harmony/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Hedera/AddressTests.cpp b/tests/chains/Hedera/AddressTests.cpp index 672b0a7b106..4065b5164b2 100644 --- a/tests/chains/Hedera/AddressTests.cpp +++ b/tests/chains/Hedera/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Hedera/Address.h" diff --git a/tests/chains/Hedera/SignerTests.cpp b/tests/chains/Hedera/SignerTests.cpp index 2a61f3023ab..b1b4220226a 100644 --- a/tests/chains/Hedera/SignerTests.cpp +++ b/tests/chains/Hedera/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Hedera/Address.h" #include "Hedera/Protobuf/basic_types.pb.h" diff --git a/tests/chains/Hedera/TWAnySignerTests.cpp b/tests/chains/Hedera/TWAnySignerTests.cpp index 8c62ac85af8..0e2d900604f 100644 --- a/tests/chains/Hedera/TWAnySignerTests.cpp +++ b/tests/chains/Hedera/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "Hedera/Signer.h" diff --git a/tests/chains/Hedera/TWCoinTypeTests.cpp b/tests/chains/Hedera/TWCoinTypeTests.cpp index 09d0ce4c875..f3ee374a49d 100644 --- a/tests/chains/Hedera/TWCoinTypeTests.cpp +++ b/tests/chains/Hedera/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/ICON/AddressTests.cpp b/tests/chains/ICON/AddressTests.cpp index b32d40044ba..2d8229fac8f 100644 --- a/tests/chains/ICON/AddressTests.cpp +++ b/tests/chains/ICON/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Icon/Address.h" diff --git a/tests/chains/ICON/TWAnySignerTests.cpp b/tests/chains/ICON/TWAnySignerTests.cpp index ee3ca846499..c010fe5e159 100644 --- a/tests/chains/ICON/TWAnySignerTests.cpp +++ b/tests/chains/ICON/TWAnySignerTests.cpp @@ -1,9 +1,7 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Data.h" #include "HexCoding.h" diff --git a/tests/chains/ICON/TWCoinTypeTests.cpp b/tests/chains/ICON/TWCoinTypeTests.cpp index 0fb5661bf5b..cd0f6fff5a0 100644 --- a/tests/chains/ICON/TWCoinTypeTests.cpp +++ b/tests/chains/ICON/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/ICON/TransactionCompilerTests.cpp b/tests/chains/ICON/TransactionCompilerTests.cpp index 18efb781d63..a47dc54d2be 100644 --- a/tests/chains/ICON/TransactionCompilerTests.cpp +++ b/tests/chains/ICON/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "proto/Icon.pb.h" diff --git a/tests/chains/IOST/AddressTests.cpp b/tests/chains/IOST/AddressTests.cpp index e088e411477..42c4e41de0d 100644 --- a/tests/chains/IOST/AddressTests.cpp +++ b/tests/chains/IOST/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HDWallet.h" diff --git a/tests/chains/IOST/SignerTests.cpp b/tests/chains/IOST/SignerTests.cpp index 22a3a545940..cde607ed48a 100644 --- a/tests/chains/IOST/SignerTests.cpp +++ b/tests/chains/IOST/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "IOST/Signer.h" diff --git a/tests/chains/IOST/TWCoinTypeTests.cpp b/tests/chains/IOST/TWCoinTypeTests.cpp index bb3d675f549..3b4f8817d7d 100644 --- a/tests/chains/IOST/TWCoinTypeTests.cpp +++ b/tests/chains/IOST/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/IOST/TransactionCompilerTests.cpp b/tests/chains/IOST/TransactionCompilerTests.cpp index 6b33a86a025..9db3d3e0d84 100644 --- a/tests/chains/IOST/TransactionCompilerTests.cpp +++ b/tests/chains/IOST/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base58.h" #include "Coin.h" diff --git a/tests/chains/ImmutableX/StarkKeyTests.cpp b/tests/chains/ImmutableX/StarkKeyTests.cpp index f1ef1fce5b4..6f9efca154e 100644 --- a/tests/chains/ImmutableX/StarkKeyTests.cpp +++ b/tests/chains/ImmutableX/StarkKeyTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Ethereum/EIP2645.h" #include "HexCoding.h" diff --git a/tests/chains/InternetComputer/TWAnyAddressTests.cpp b/tests/chains/InternetComputer/TWAnyAddressTests.cpp index 013ff0f158e..5833e05b68d 100644 --- a/tests/chains/InternetComputer/TWAnyAddressTests.cpp +++ b/tests/chains/InternetComputer/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include diff --git a/tests/chains/InternetComputer/TWAnySignerTests.cpp b/tests/chains/InternetComputer/TWAnySignerTests.cpp index 71e4ba77e62..803c5f1e487 100644 --- a/tests/chains/InternetComputer/TWAnySignerTests.cpp +++ b/tests/chains/InternetComputer/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "proto/Common.pb.h" diff --git a/tests/chains/InternetComputer/TWCoinTypeTests.cpp b/tests/chains/InternetComputer/TWCoinTypeTests.cpp index ea7bbc624d1..ca29c0f517a 100644 --- a/tests/chains/InternetComputer/TWCoinTypeTests.cpp +++ b/tests/chains/InternetComputer/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/IoTeX/AddressTests.cpp b/tests/chains/IoTeX/AddressTests.cpp index ca97adb50a4..fd94363fdff 100644 --- a/tests/chains/IoTeX/AddressTests.cpp +++ b/tests/chains/IoTeX/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/tests/chains/IoTeX/SignerTests.cpp b/tests/chains/IoTeX/SignerTests.cpp index 7b2787511d0..bb743500c66 100644 --- a/tests/chains/IoTeX/SignerTests.cpp +++ b/tests/chains/IoTeX/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/tests/chains/IoTeX/StakingTests.cpp b/tests/chains/IoTeX/StakingTests.cpp index 3a0a2bcc7bd..16515808566 100644 --- a/tests/chains/IoTeX/StakingTests.cpp +++ b/tests/chains/IoTeX/StakingTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Data.h" #include "HexCoding.h" diff --git a/tests/chains/IoTeX/TWAnySignerTests.cpp b/tests/chains/IoTeX/TWAnySignerTests.cpp index 814e352ce66..84336d5fe2e 100644 --- a/tests/chains/IoTeX/TWAnySignerTests.cpp +++ b/tests/chains/IoTeX/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/IoTeX/TWCoinTypeTests.cpp b/tests/chains/IoTeX/TWCoinTypeTests.cpp index 2ddfb56ee69..25107bc3895 100644 --- a/tests/chains/IoTeX/TWCoinTypeTests.cpp +++ b/tests/chains/IoTeX/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/IoTeX/TransactionCompilerTests.cpp b/tests/chains/IoTeX/TransactionCompilerTests.cpp index 2dd65ce85d6..4c25d7e5431 100644 --- a/tests/chains/IoTeX/TransactionCompilerTests.cpp +++ b/tests/chains/IoTeX/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/KavaEvm/TWCoinTypeTests.cpp b/tests/chains/KavaEvm/TWCoinTypeTests.cpp index 3c2af143611..75ea26d7afc 100644 --- a/tests/chains/KavaEvm/TWCoinTypeTests.cpp +++ b/tests/chains/KavaEvm/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Kin/TWCoinTypeTests.cpp b/tests/chains/Kin/TWCoinTypeTests.cpp index 2e52babb04a..0f8c6f5c509 100644 --- a/tests/chains/Kin/TWCoinTypeTests.cpp +++ b/tests/chains/Kin/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Klaytn/TWCoinTypeTests.cpp b/tests/chains/Klaytn/TWCoinTypeTests.cpp index 3ecccb32471..c2ff44c016b 100644 --- a/tests/chains/Klaytn/TWCoinTypeTests.cpp +++ b/tests/chains/Klaytn/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Komodo/AddressTests.cpp b/tests/chains/Komodo/AddressTests.cpp index 5591db9cd64..ab2e54482db 100644 --- a/tests/chains/Komodo/AddressTests.cpp +++ b/tests/chains/Komodo/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HDWallet.h" diff --git a/tests/chains/Komodo/TWAnyAddressTests.cpp b/tests/chains/Komodo/TWAnyAddressTests.cpp index 57c366e9340..fb1374f99fe 100644 --- a/tests/chains/Komodo/TWAnyAddressTests.cpp +++ b/tests/chains/Komodo/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include diff --git a/tests/chains/Komodo/TWCoinTypeTests.cpp b/tests/chains/Komodo/TWCoinTypeTests.cpp index 48295ea72c7..ca1c88a4c15 100644 --- a/tests/chains/Komodo/TWCoinTypeTests.cpp +++ b/tests/chains/Komodo/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Komodo/TransactionCompilerTests.cpp b/tests/chains/Komodo/TransactionCompilerTests.cpp index ebd363ed108..70b12713334 100644 --- a/tests/chains/Komodo/TransactionCompilerTests.cpp +++ b/tests/chains/Komodo/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PrivateKey.h" diff --git a/tests/chains/KuCoinCommunityChain/TWCoinTypeTests.cpp b/tests/chains/KuCoinCommunityChain/TWCoinTypeTests.cpp index 71dbe3758ef..c1f3e42147f 100644 --- a/tests/chains/KuCoinCommunityChain/TWCoinTypeTests.cpp +++ b/tests/chains/KuCoinCommunityChain/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Kusama/AddressTests.cpp b/tests/chains/Kusama/AddressTests.cpp index eed8c10c2f5..a29aa2abf54 100644 --- a/tests/chains/Kusama/AddressTests.cpp +++ b/tests/chains/Kusama/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Kusama/Address.h" diff --git a/tests/chains/Kusama/SignerTests.cpp b/tests/chains/Kusama/SignerTests.cpp index 4c18ddaa015..f271547e1e8 100644 --- a/tests/chains/Kusama/SignerTests.cpp +++ b/tests/chains/Kusama/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Polkadot/Extrinsic.h" diff --git a/tests/chains/Kusama/TWAnySignerTests.cpp b/tests/chains/Kusama/TWAnySignerTests.cpp index 82ecfd4cc82..cb61bf2ee82 100644 --- a/tests/chains/Kusama/TWAnySignerTests.cpp +++ b/tests/chains/Kusama/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "proto/Polkadot.pb.h" diff --git a/tests/chains/Kusama/TWCoinTypeTests.cpp b/tests/chains/Kusama/TWCoinTypeTests.cpp index d43a8d6d2a3..6dcf092e68d 100644 --- a/tests/chains/Kusama/TWCoinTypeTests.cpp +++ b/tests/chains/Kusama/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Linea/TWCoinTypeTests.cpp b/tests/chains/Linea/TWCoinTypeTests.cpp index 534df3a19d5..8570bd33cdd 100644 --- a/tests/chains/Linea/TWCoinTypeTests.cpp +++ b/tests/chains/Linea/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Litecoin/LitecoinAddressTests.cpp b/tests/chains/Litecoin/LitecoinAddressTests.cpp index 89ad34ebcdd..19bee636bb4 100644 --- a/tests/chains/Litecoin/LitecoinAddressTests.cpp +++ b/tests/chains/Litecoin/LitecoinAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bitcoin/Address.h" #include "Bitcoin/SegwitAddress.h" diff --git a/tests/chains/Litecoin/TWCoinTypeTests.cpp b/tests/chains/Litecoin/TWCoinTypeTests.cpp index d16b1f56c90..0bb7e1376f6 100644 --- a/tests/chains/Litecoin/TWCoinTypeTests.cpp +++ b/tests/chains/Litecoin/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Litecoin/TWLitecoinTests.cpp b/tests/chains/Litecoin/TWLitecoinTests.cpp index 423fd665b41..f85584a571a 100644 --- a/tests/chains/Litecoin/TWLitecoinTests.cpp +++ b/tests/chains/Litecoin/TWLitecoinTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/MantaPacific/TWCoinTypeTests.cpp b/tests/chains/MantaPacific/TWCoinTypeTests.cpp index 4f053c7fc01..53dbbf4ffde 100644 --- a/tests/chains/MantaPacific/TWCoinTypeTests.cpp +++ b/tests/chains/MantaPacific/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/Mantle/TWCoinTypeTests.cpp b/tests/chains/Mantle/TWCoinTypeTests.cpp index 85483037897..43518159914 100644 --- a/tests/chains/Mantle/TWCoinTypeTests.cpp +++ b/tests/chains/Mantle/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Meter/TWCoinTypeTests.cpp b/tests/chains/Meter/TWCoinTypeTests.cpp index da05941fe5c..711c4fb8577 100644 --- a/tests/chains/Meter/TWCoinTypeTests.cpp +++ b/tests/chains/Meter/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Metis/TWCoinTypeTests.cpp b/tests/chains/Metis/TWCoinTypeTests.cpp index 94a0cc8a950..b5696255acd 100644 --- a/tests/chains/Metis/TWCoinTypeTests.cpp +++ b/tests/chains/Metis/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Monacoin/TWCoinTypeTests.cpp b/tests/chains/Monacoin/TWCoinTypeTests.cpp index a9196f410ee..30ae52f4ef0 100644 --- a/tests/chains/Monacoin/TWCoinTypeTests.cpp +++ b/tests/chains/Monacoin/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Monacoin/TWMonacoinAddressTests.cpp b/tests/chains/Monacoin/TWMonacoinAddressTests.cpp index 7cfbfe9f629..a4ef2fa7fca 100644 --- a/tests/chains/Monacoin/TWMonacoinAddressTests.cpp +++ b/tests/chains/Monacoin/TWMonacoinAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Monacoin/TWMonacoinTransactionTests.cpp b/tests/chains/Monacoin/TWMonacoinTransactionTests.cpp index 66cc43ad2bd..c2113f0c915 100644 --- a/tests/chains/Monacoin/TWMonacoinTransactionTests.cpp +++ b/tests/chains/Monacoin/TWMonacoinTransactionTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include "HexCoding.h" diff --git a/tests/chains/Moonbeam/TWCoinTypeTests.cpp b/tests/chains/Moonbeam/TWCoinTypeTests.cpp index 75de7b42232..4d30a26c0f5 100644 --- a/tests/chains/Moonbeam/TWCoinTypeTests.cpp +++ b/tests/chains/Moonbeam/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Moonriver/TWCoinTypeTests.cpp b/tests/chains/Moonriver/TWCoinTypeTests.cpp index dcec5f291d7..b278f21814c 100644 --- a/tests/chains/Moonriver/TWCoinTypeTests.cpp +++ b/tests/chains/Moonriver/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/MultiversX/AddressTests.cpp b/tests/chains/MultiversX/AddressTests.cpp index 0fe0d626c6e..2135f4678bd 100644 --- a/tests/chains/MultiversX/AddressTests.cpp +++ b/tests/chains/MultiversX/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/tests/chains/MultiversX/SerializationTests.cpp b/tests/chains/MultiversX/SerializationTests.cpp index 6b55ec56825..37651803c7d 100644 --- a/tests/chains/MultiversX/SerializationTests.cpp +++ b/tests/chains/MultiversX/SerializationTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/tests/chains/MultiversX/SignerTests.cpp b/tests/chains/MultiversX/SignerTests.cpp index 42136ac1c0b..73b1ed7cf29 100644 --- a/tests/chains/MultiversX/SignerTests.cpp +++ b/tests/chains/MultiversX/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/tests/chains/MultiversX/TWAnySignerTests.cpp b/tests/chains/MultiversX/TWAnySignerTests.cpp index cad331fddb1..1b1311b1382 100644 --- a/tests/chains/MultiversX/TWAnySignerTests.cpp +++ b/tests/chains/MultiversX/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/tests/chains/MultiversX/TWCoinTypeTests.cpp b/tests/chains/MultiversX/TWCoinTypeTests.cpp index 8dad4779c47..fd5a2248400 100644 --- a/tests/chains/MultiversX/TWCoinTypeTests.cpp +++ b/tests/chains/MultiversX/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/MultiversX/TestAccounts.h b/tests/chains/MultiversX/TestAccounts.h index 17bed05027c..3026c9895ef 100644 --- a/tests/chains/MultiversX/TestAccounts.h +++ b/tests/chains/MultiversX/TestAccounts.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/tests/chains/MultiversX/TransactionCompilerTests.cpp b/tests/chains/MultiversX/TransactionCompilerTests.cpp index 59dacf8fc0b..438add4c4c6 100644 --- a/tests/chains/MultiversX/TransactionCompilerTests.cpp +++ b/tests/chains/MultiversX/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "TransactionCompiler.h" diff --git a/tests/chains/MultiversX/TransactionFactoryTests.cpp b/tests/chains/MultiversX/TransactionFactoryTests.cpp index f61e2af8744..6b082245973 100644 --- a/tests/chains/MultiversX/TransactionFactoryTests.cpp +++ b/tests/chains/MultiversX/TransactionFactoryTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "boost/format.hpp" #include diff --git a/tests/chains/NEAR/AccountTests.cpp b/tests/chains/NEAR/AccountTests.cpp index 476b43830d5..880794bd506 100644 --- a/tests/chains/NEAR/AccountTests.cpp +++ b/tests/chains/NEAR/AccountTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "NEAR/Account.h" #include "HexCoding.h" diff --git a/tests/chains/NEAR/AddressTests.cpp b/tests/chains/NEAR/AddressTests.cpp index 9bad02940de..7ae6b77a671 100644 --- a/tests/chains/NEAR/AddressTests.cpp +++ b/tests/chains/NEAR/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "NEAR/Address.h" #include "Base58.h" diff --git a/tests/chains/NEAR/SerializationTests.cpp b/tests/chains/NEAR/SerializationTests.cpp index d598656f126..f534edd48f2 100644 --- a/tests/chains/NEAR/SerializationTests.cpp +++ b/tests/chains/NEAR/SerializationTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Base58.h" diff --git a/tests/chains/NEAR/SignerTests.cpp b/tests/chains/NEAR/SignerTests.cpp index ba02c5a17ec..16659e4393c 100644 --- a/tests/chains/NEAR/SignerTests.cpp +++ b/tests/chains/NEAR/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base64.h" #include "Base58.h" diff --git a/tests/chains/NEAR/TWAnySignerTests.cpp b/tests/chains/NEAR/TWAnySignerTests.cpp index cfbda747473..7bd363fc63e 100644 --- a/tests/chains/NEAR/TWAnySignerTests.cpp +++ b/tests/chains/NEAR/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "proto/NEAR.pb.h" diff --git a/tests/chains/NEAR/TWCoinTypeTests.cpp b/tests/chains/NEAR/TWCoinTypeTests.cpp index e423b2ec3b4..b62f545841a 100644 --- a/tests/chains/NEAR/TWCoinTypeTests.cpp +++ b/tests/chains/NEAR/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/NEAR/TWNEARAccountTests.cpp b/tests/chains/NEAR/TWNEARAccountTests.cpp index 39735df5b38..9f09189dfd7 100644 --- a/tests/chains/NEAR/TWNEARAccountTests.cpp +++ b/tests/chains/NEAR/TWNEARAccountTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // diff --git a/tests/chains/NEAR/TransactionCompilerTests.cpp b/tests/chains/NEAR/TransactionCompilerTests.cpp index a37df1c0f98..705ba950804 100644 --- a/tests/chains/NEAR/TransactionCompilerTests.cpp +++ b/tests/chains/NEAR/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base58.h" #include "Coin.h" diff --git a/tests/chains/NEO/AddressTests.cpp b/tests/chains/NEO/AddressTests.cpp index bef89352c17..af24375ebfe 100644 --- a/tests/chains/NEO/AddressTests.cpp +++ b/tests/chains/NEO/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "NEO/Address.h" diff --git a/tests/chains/NEO/BinaryCodingTests.cpp b/tests/chains/NEO/BinaryCodingTests.cpp index 1e2e643e83d..ec01ba5fd98 100644 --- a/tests/chains/NEO/BinaryCodingTests.cpp +++ b/tests/chains/NEO/BinaryCodingTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "NEO/BinaryCoding.h" diff --git a/tests/chains/NEO/CoinReferenceTests.cpp b/tests/chains/NEO/CoinReferenceTests.cpp index b6167bf825c..03f195fc6f4 100644 --- a/tests/chains/NEO/CoinReferenceTests.cpp +++ b/tests/chains/NEO/CoinReferenceTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "uint256.h" #include "HexCoding.h" diff --git a/tests/chains/NEO/ReadDataTests.cpp b/tests/chains/NEO/ReadDataTests.cpp index fda607bdee4..02f16e02617 100644 --- a/tests/chains/NEO/ReadDataTests.cpp +++ b/tests/chains/NEO/ReadDataTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "NEO/ReadData.h" diff --git a/tests/chains/NEO/ScriptTests.cpp b/tests/chains/NEO/ScriptTests.cpp index fa0afbb3237..2b36c1e1d10 100644 --- a/tests/chains/NEO/ScriptTests.cpp +++ b/tests/chains/NEO/ScriptTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "NEO/Script.h" diff --git a/tests/chains/NEO/SignerTests.cpp b/tests/chains/NEO/SignerTests.cpp index 643ea53a5b9..55a214e09ae 100644 --- a/tests/chains/NEO/SignerTests.cpp +++ b/tests/chains/NEO/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PublicKey.h" diff --git a/tests/chains/NEO/TWAnySignerTests.cpp b/tests/chains/NEO/TWAnySignerTests.cpp index 3f6770b6fb2..e600d17ad3f 100644 --- a/tests/chains/NEO/TWAnySignerTests.cpp +++ b/tests/chains/NEO/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "PrivateKey.h" #include "PublicKeyLegacy.h" diff --git a/tests/chains/NEO/TWCoinTypeTests.cpp b/tests/chains/NEO/TWCoinTypeTests.cpp index 6eb46899db5..167405c45ae 100644 --- a/tests/chains/NEO/TWCoinTypeTests.cpp +++ b/tests/chains/NEO/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/NEO/TransactionAttributeTests.cpp b/tests/chains/NEO/TransactionAttributeTests.cpp index 0ed457fc9f8..10e667370e8 100644 --- a/tests/chains/NEO/TransactionAttributeTests.cpp +++ b/tests/chains/NEO/TransactionAttributeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "uint256.h" #include "HexCoding.h" diff --git a/tests/chains/NEO/TransactionCompilerTests.cpp b/tests/chains/NEO/TransactionCompilerTests.cpp index 8ca243df57e..121919c7eda 100644 --- a/tests/chains/NEO/TransactionCompilerTests.cpp +++ b/tests/chains/NEO/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/NEO/TransactionOutputTests.cpp b/tests/chains/NEO/TransactionOutputTests.cpp index c7bcd4ef0e2..cf1e76ed3d5 100644 --- a/tests/chains/NEO/TransactionOutputTests.cpp +++ b/tests/chains/NEO/TransactionOutputTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "uint256.h" #include "HexCoding.h" diff --git a/tests/chains/NEO/TransactionTests.cpp b/tests/chains/NEO/TransactionTests.cpp index 0d3f252b978..615716a9f58 100644 --- a/tests/chains/NEO/TransactionTests.cpp +++ b/tests/chains/NEO/TransactionTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "uint256.h" #include "HexCoding.h" diff --git a/tests/chains/NEO/WitnessTests.cpp b/tests/chains/NEO/WitnessTests.cpp index c597814c1cf..d31c122cc4a 100644 --- a/tests/chains/NEO/WitnessTests.cpp +++ b/tests/chains/NEO/WitnessTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "uint256.h" #include "HexCoding.h" diff --git a/tests/chains/NULS/AddressTests.cpp b/tests/chains/NULS/AddressTests.cpp index f9762480ed2..542d939d183 100644 --- a/tests/chains/NULS/AddressTests.cpp +++ b/tests/chains/NULS/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "NULS/Address.h" #include "HexCoding.h" diff --git a/tests/chains/NULS/SignerTests.cpp b/tests/chains/NULS/SignerTests.cpp index 02b1e589d4e..3db30b5c2f4 100644 --- a/tests/chains/NULS/SignerTests.cpp +++ b/tests/chains/NULS/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "NULS/Signer.h" diff --git a/tests/chains/NULS/TWAnySignerTests.cpp b/tests/chains/NULS/TWAnySignerTests.cpp index 6baa41031b6..d82048ada7a 100644 --- a/tests/chains/NULS/TWAnySignerTests.cpp +++ b/tests/chains/NULS/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "NULS/Address.h" diff --git a/tests/chains/NULS/TWCoinTypeTests.cpp b/tests/chains/NULS/TWCoinTypeTests.cpp index 361db572e09..f2592aa1d2d 100644 --- a/tests/chains/NULS/TWCoinTypeTests.cpp +++ b/tests/chains/NULS/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/NULS/TransactionCompilerTests.cpp b/tests/chains/NULS/TransactionCompilerTests.cpp index 8f78ac18771..cbc2def155d 100644 --- a/tests/chains/NULS/TransactionCompilerTests.cpp +++ b/tests/chains/NULS/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Nano/AddressTests.cpp b/tests/chains/Nano/AddressTests.cpp index 68cf56f8eac..fdf30780912 100644 --- a/tests/chains/Nano/AddressTests.cpp +++ b/tests/chains/Nano/AddressTests.cpp @@ -1,9 +1,7 @@ // Copyright © 2019 Mart Roosmaa. -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Nano/Address.h" diff --git a/tests/chains/Nano/SignerTests.cpp b/tests/chains/Nano/SignerTests.cpp index 051146da28e..c28645d0794 100644 --- a/tests/chains/Nano/SignerTests.cpp +++ b/tests/chains/Nano/SignerTests.cpp @@ -1,9 +1,7 @@ // Copyright © 2019 Mart Roosmaa. -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Nano/Signer.h" diff --git a/tests/chains/Nano/TWAnySignerTests.cpp b/tests/chains/Nano/TWAnySignerTests.cpp index 20b2da029ea..767b9ce0f6c 100644 --- a/tests/chains/Nano/TWAnySignerTests.cpp +++ b/tests/chains/Nano/TWAnySignerTests.cpp @@ -1,9 +1,7 @@ // Copyright © 2019 Mart Roosmaa. -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "proto/Nano.pb.h" diff --git a/tests/chains/Nano/TWCoinTypeTests.cpp b/tests/chains/Nano/TWCoinTypeTests.cpp index ce32e618915..a0b033eeade 100644 --- a/tests/chains/Nano/TWCoinTypeTests.cpp +++ b/tests/chains/Nano/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Nano/TWNanoAddressTests.cpp b/tests/chains/Nano/TWNanoAddressTests.cpp index 99493679867..b01dfc2447a 100644 --- a/tests/chains/Nano/TWNanoAddressTests.cpp +++ b/tests/chains/Nano/TWNanoAddressTests.cpp @@ -1,9 +1,7 @@ // Copyright © 2019 Mart Roosmaa. -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include "HexCoding.h" diff --git a/tests/chains/Nano/TestAccounts.h b/tests/chains/Nano/TestAccounts.h index 1df79e5138e..636c643e9d0 100644 --- a/tests/chains/Nano/TestAccounts.h +++ b/tests/chains/Nano/TestAccounts.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/tests/chains/Nano/TransactionCompilerTests.cpp b/tests/chains/Nano/TransactionCompilerTests.cpp index 508e1a8adf3..88ec1cda1ca 100644 --- a/tests/chains/Nano/TransactionCompilerTests.cpp +++ b/tests/chains/Nano/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "TransactionCompiler.h" diff --git a/tests/chains/Nebl/AddressTests.cpp b/tests/chains/Nebl/AddressTests.cpp index 18ba796ddd9..932f9dfd9b4 100644 --- a/tests/chains/Nebl/AddressTests.cpp +++ b/tests/chains/Nebl/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Nebl/SignerTests.cpp b/tests/chains/Nebl/SignerTests.cpp index 678c7b4012f..eb8cad61749 100644 --- a/tests/chains/Nebl/SignerTests.cpp +++ b/tests/chains/Nebl/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Verge/Signer.h" #include "Verge/Transaction.h" diff --git a/tests/chains/Nebl/TWAnySignerTests.cpp b/tests/chains/Nebl/TWAnySignerTests.cpp index 9e260dac610..745d6ed792c 100644 --- a/tests/chains/Nebl/TWAnySignerTests.cpp +++ b/tests/chains/Nebl/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/tests/chains/Nebl/TWCoinTypeTests.cpp b/tests/chains/Nebl/TWCoinTypeTests.cpp index d83b97e98ac..817ad95c8cc 100644 --- a/tests/chains/Nebl/TWCoinTypeTests.cpp +++ b/tests/chains/Nebl/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Nebl/TWNeblAddressTests.cpp b/tests/chains/Nebl/TWNeblAddressTests.cpp index 146b9403082..085680ffaaa 100644 --- a/tests/chains/Nebl/TWNeblAddressTests.cpp +++ b/tests/chains/Nebl/TWNeblAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "HexCoding.h" diff --git a/tests/chains/Nebl/TransactionBuilderTests.cpp b/tests/chains/Nebl/TransactionBuilderTests.cpp index 5a4fd273950..09bdcb8218d 100644 --- a/tests/chains/Nebl/TransactionBuilderTests.cpp +++ b/tests/chains/Nebl/TransactionBuilderTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Verge/Transaction.h" #include "Verge/TransactionBuilder.h" diff --git a/tests/chains/Nebl/TransactionCompilerTests.cpp b/tests/chains/Nebl/TransactionCompilerTests.cpp index 213232b07ac..30669f9ad36 100644 --- a/tests/chains/Nebl/TransactionCompilerTests.cpp +++ b/tests/chains/Nebl/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Nebulas/AddressTests.cpp b/tests/chains/Nebulas/AddressTests.cpp index 504134a330c..8efe6395086 100644 --- a/tests/chains/Nebulas/AddressTests.cpp +++ b/tests/chains/Nebulas/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Nebulas/Address.h" #include "HexCoding.h" diff --git a/tests/chains/Nebulas/SignerTests.cpp b/tests/chains/Nebulas/SignerTests.cpp index a9b3001b7e3..c904a4a2fe4 100644 --- a/tests/chains/Nebulas/SignerTests.cpp +++ b/tests/chains/Nebulas/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Nebulas/Address.h" diff --git a/tests/chains/Nebulas/TWAnySignerTests.cpp b/tests/chains/Nebulas/TWAnySignerTests.cpp index 2ca8e053d19..21ed9d0c0de 100644 --- a/tests/chains/Nebulas/TWAnySignerTests.cpp +++ b/tests/chains/Nebulas/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/Nebulas/TWCoinTypeTests.cpp b/tests/chains/Nebulas/TWCoinTypeTests.cpp index 9c63f7aee70..2d302a0aa52 100644 --- a/tests/chains/Nebulas/TWCoinTypeTests.cpp +++ b/tests/chains/Nebulas/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Nebulas/TWNebulasAddressTests.cpp b/tests/chains/Nebulas/TWNebulasAddressTests.cpp index 62dffd04c12..e41a4e54e09 100644 --- a/tests/chains/Nebulas/TWNebulasAddressTests.cpp +++ b/tests/chains/Nebulas/TWNebulasAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Nebulas/TransactionTests.cpp b/tests/chains/Nebulas/TransactionTests.cpp index 607b9648b8f..d937cb73734 100644 --- a/tests/chains/Nebulas/TransactionTests.cpp +++ b/tests/chains/Nebulas/TransactionTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Nebulas/Signer.h" #include "HexCoding.h" diff --git a/tests/chains/Neon/TWCoinTypeTests.cpp b/tests/chains/Neon/TWCoinTypeTests.cpp index 9335530a0fc..d0a506e4119 100644 --- a/tests/chains/Neon/TWCoinTypeTests.cpp +++ b/tests/chains/Neon/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Nervos/AddressTests.cpp b/tests/chains/Nervos/AddressTests.cpp index e0797afb3dd..4ff4d1246d5 100644 --- a/tests/chains/Nervos/AddressTests.cpp +++ b/tests/chains/Nervos/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HDWallet.h" #include "HexCoding.h" diff --git a/tests/chains/Nervos/SignerTests.cpp b/tests/chains/Nervos/SignerTests.cpp index ec129c6f640..af862b5f84a 100644 --- a/tests/chains/Nervos/SignerTests.cpp +++ b/tests/chains/Nervos/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Nervos/Address.h" diff --git a/tests/chains/Nervos/TWAnyAddressTests.cpp b/tests/chains/Nervos/TWAnyAddressTests.cpp index f2822dcfc50..be1c5f92e01 100644 --- a/tests/chains/Nervos/TWAnyAddressTests.cpp +++ b/tests/chains/Nervos/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "PrivateKey.h" #include diff --git a/tests/chains/Nervos/TWAnySignerTests.cpp b/tests/chains/Nervos/TWAnySignerTests.cpp index bf6d55115bf..75b42c032f1 100644 --- a/tests/chains/Nervos/TWAnySignerTests.cpp +++ b/tests/chains/Nervos/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Nervos/Address.h" diff --git a/tests/chains/Nervos/TWCoinTypeTests.cpp b/tests/chains/Nervos/TWCoinTypeTests.cpp index 7b0a5870099..d2abfee8137 100644 --- a/tests/chains/Nervos/TWCoinTypeTests.cpp +++ b/tests/chains/Nervos/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Nervos/TWNervosAddressTests.cpp b/tests/chains/Nervos/TWNervosAddressTests.cpp index 2f537d049bf..f98cff90bae 100644 --- a/tests/chains/Nervos/TWNervosAddressTests.cpp +++ b/tests/chains/Nervos/TWNervosAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PrivateKey.h" diff --git a/tests/chains/Nimiq/AddressTests.cpp b/tests/chains/Nimiq/AddressTests.cpp index 937ce7ec4b8..169c18359a9 100644 --- a/tests/chains/Nimiq/AddressTests.cpp +++ b/tests/chains/Nimiq/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Nimiq/Address.h" diff --git a/tests/chains/Nimiq/SignerTests.cpp b/tests/chains/Nimiq/SignerTests.cpp index b99b3006768..c22441f2f39 100644 --- a/tests/chains/Nimiq/SignerTests.cpp +++ b/tests/chains/Nimiq/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PrivateKey.h" diff --git a/tests/chains/Nimiq/TWAnySignerTests.cpp b/tests/chains/Nimiq/TWAnySignerTests.cpp index e47bcc057e5..a618b1ac04f 100644 --- a/tests/chains/Nimiq/TWAnySignerTests.cpp +++ b/tests/chains/Nimiq/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "proto/Nimiq.pb.h" diff --git a/tests/chains/Nimiq/TWCoinTypeTests.cpp b/tests/chains/Nimiq/TWCoinTypeTests.cpp index a5411b5d53b..65439d5fb46 100644 --- a/tests/chains/Nimiq/TWCoinTypeTests.cpp +++ b/tests/chains/Nimiq/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Nimiq/TransactionTests.cpp b/tests/chains/Nimiq/TransactionTests.cpp index 55cf3d276d0..679f955f4ca 100644 --- a/tests/chains/Nimiq/TransactionTests.cpp +++ b/tests/chains/Nimiq/TransactionTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PrivateKey.h" diff --git a/tests/chains/OKXChain/TWCoinTypeTests.cpp b/tests/chains/OKXChain/TWCoinTypeTests.cpp index e047d47b13a..28db8f84c53 100644 --- a/tests/chains/OKXChain/TWCoinTypeTests.cpp +++ b/tests/chains/OKXChain/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Oasis/AddressTests.cpp b/tests/chains/Oasis/AddressTests.cpp index 2f5a5aae988..a85dd59c24f 100644 --- a/tests/chains/Oasis/AddressTests.cpp +++ b/tests/chains/Oasis/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Oasis/Address.h" diff --git a/tests/chains/Oasis/SignerTests.cpp b/tests/chains/Oasis/SignerTests.cpp index 38228588bae..23a26f587bf 100644 --- a/tests/chains/Oasis/SignerTests.cpp +++ b/tests/chains/Oasis/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Oasis/Address.h" diff --git a/tests/chains/Oasis/TWAnySignerTests.cpp b/tests/chains/Oasis/TWAnySignerTests.cpp index 036cad243e6..63f24e03fe7 100644 --- a/tests/chains/Oasis/TWAnySignerTests.cpp +++ b/tests/chains/Oasis/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "HexCoding.h" diff --git a/tests/chains/Oasis/TWCoinTypeTests.cpp b/tests/chains/Oasis/TWCoinTypeTests.cpp index a2a1738b374..15085e876c0 100644 --- a/tests/chains/Oasis/TWCoinTypeTests.cpp +++ b/tests/chains/Oasis/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Oasis/TransactionCompilerTests.cpp b/tests/chains/Oasis/TransactionCompilerTests.cpp index 19b7f4a0e9b..18edd6b58e9 100644 --- a/tests/chains/Oasis/TransactionCompilerTests.cpp +++ b/tests/chains/Oasis/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Ontology/AccountTests.cpp b/tests/chains/Ontology/AccountTests.cpp index b95586c27ed..1917eea454f 100644 --- a/tests/chains/Ontology/AccountTests.cpp +++ b/tests/chains/Ontology/AccountTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Hash.h" #include "HexCoding.h" diff --git a/tests/chains/Ontology/AddressTests.cpp b/tests/chains/Ontology/AddressTests.cpp index 95cc3147123..559c5ebe5b0 100644 --- a/tests/chains/Ontology/AddressTests.cpp +++ b/tests/chains/Ontology/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PublicKey.h" diff --git a/tests/chains/Ontology/Oep4Tests.cpp b/tests/chains/Ontology/Oep4Tests.cpp index a0cb87f8d90..6c43b61be89 100644 --- a/tests/chains/Ontology/Oep4Tests.cpp +++ b/tests/chains/Ontology/Oep4Tests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" diff --git a/tests/chains/Ontology/OngTests.cpp b/tests/chains/Ontology/OngTests.cpp index fcea1ec34e6..1aa603bebf9 100644 --- a/tests/chains/Ontology/OngTests.cpp +++ b/tests/chains/Ontology/OngTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" diff --git a/tests/chains/Ontology/OntTests.cpp b/tests/chains/Ontology/OntTests.cpp index a645eb49708..4d499ddbdcc 100644 --- a/tests/chains/Ontology/OntTests.cpp +++ b/tests/chains/Ontology/OntTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Ontology/Ont.h" diff --git a/tests/chains/Ontology/ParamsBuilderTests.cpp b/tests/chains/Ontology/ParamsBuilderTests.cpp index d90f3c7a698..e9262dc59a7 100644 --- a/tests/chains/Ontology/ParamsBuilderTests.cpp +++ b/tests/chains/Ontology/ParamsBuilderTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PublicKey.h" diff --git a/tests/chains/Ontology/TWAnySignerTests.cpp b/tests/chains/Ontology/TWAnySignerTests.cpp index 64558fc5be3..3d9754490aa 100644 --- a/tests/chains/Ontology/TWAnySignerTests.cpp +++ b/tests/chains/Ontology/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "TestUtilities.h" diff --git a/tests/chains/Ontology/TWCoinTypeTests.cpp b/tests/chains/Ontology/TWCoinTypeTests.cpp index 79171b64b7a..026dd2a2fc3 100644 --- a/tests/chains/Ontology/TWCoinTypeTests.cpp +++ b/tests/chains/Ontology/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Ontology/TransactionCompilerTests.cpp b/tests/chains/Ontology/TransactionCompilerTests.cpp index 29b6cbdea17..8315ae6180d 100644 --- a/tests/chains/Ontology/TransactionCompilerTests.cpp +++ b/tests/chains/Ontology/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Ontology/TransactionTests.cpp b/tests/chains/Ontology/TransactionTests.cpp index 1aaab639fd1..154245ff49b 100644 --- a/tests/chains/Ontology/TransactionTests.cpp +++ b/tests/chains/Ontology/TransactionTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PrivateKey.h" diff --git a/tests/chains/OpBNBtestnet/TWCoinTypeTests.cpp b/tests/chains/OpBNBtestnet/TWCoinTypeTests.cpp index 12b126c45ea..aca675e68fb 100644 --- a/tests/chains/OpBNBtestnet/TWCoinTypeTests.cpp +++ b/tests/chains/OpBNBtestnet/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Optimism/TWCoinTypeTests.cpp b/tests/chains/Optimism/TWCoinTypeTests.cpp index 50eadccbad3..9e51cffa575 100644 --- a/tests/chains/Optimism/TWCoinTypeTests.cpp +++ b/tests/chains/Optimism/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/POANetwork/TWCoinTypeTests.cpp b/tests/chains/POANetwork/TWCoinTypeTests.cpp index 19537398c1e..b43bedb7d0f 100644 --- a/tests/chains/POANetwork/TWCoinTypeTests.cpp +++ b/tests/chains/POANetwork/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Pivx/AddressTests.cpp b/tests/chains/Pivx/AddressTests.cpp index f6075bc5f1a..4032e352241 100644 --- a/tests/chains/Pivx/AddressTests.cpp +++ b/tests/chains/Pivx/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/tests/chains/Pivx/TWAnyAddressTests.cpp b/tests/chains/Pivx/TWAnyAddressTests.cpp index d4e0840d9cd..9185d79531a 100644 --- a/tests/chains/Pivx/TWAnyAddressTests.cpp +++ b/tests/chains/Pivx/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "HexCoding.h" diff --git a/tests/chains/Pivx/TWCoinTypeTests.cpp b/tests/chains/Pivx/TWCoinTypeTests.cpp index c73ad2b3390..dc73c23eba3 100644 --- a/tests/chains/Pivx/TWCoinTypeTests.cpp +++ b/tests/chains/Pivx/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Pivx/TransactionCompilerTests.cpp b/tests/chains/Pivx/TransactionCompilerTests.cpp index efaf999e356..b654b874273 100644 --- a/tests/chains/Pivx/TransactionCompilerTests.cpp +++ b/tests/chains/Pivx/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Polkadot/AddressTests.cpp b/tests/chains/Polkadot/AddressTests.cpp index 98fa39772f2..47fb3a5d8ca 100644 --- a/tests/chains/Polkadot/AddressTests.cpp +++ b/tests/chains/Polkadot/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Polkadot/Address.h" diff --git a/tests/chains/Polkadot/ExtrinsicTests.cpp b/tests/chains/Polkadot/ExtrinsicTests.cpp index 41f090f52c9..dd921a64343 100644 --- a/tests/chains/Polkadot/ExtrinsicTests.cpp +++ b/tests/chains/Polkadot/ExtrinsicTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Data.h" #include "HexCoding.h" diff --git a/tests/chains/Polkadot/SS58AddressTests.cpp b/tests/chains/Polkadot/SS58AddressTests.cpp index b7a7aba3fb2..6482c70f1f0 100644 --- a/tests/chains/Polkadot/SS58AddressTests.cpp +++ b/tests/chains/Polkadot/SS58AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Polkadot/SS58Address.h" #include "HexCoding.h" diff --git a/tests/chains/Polkadot/ScaleCodecTests.cpp b/tests/chains/Polkadot/ScaleCodecTests.cpp index d61ba217b63..7202e84037d 100644 --- a/tests/chains/Polkadot/ScaleCodecTests.cpp +++ b/tests/chains/Polkadot/ScaleCodecTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" diff --git a/tests/chains/Polkadot/SignerTests.cpp b/tests/chains/Polkadot/SignerTests.cpp index eaf3a2913a3..ca1631603be 100644 --- a/tests/chains/Polkadot/SignerTests.cpp +++ b/tests/chains/Polkadot/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Polkadot/Address.h" diff --git a/tests/chains/Polkadot/TWCoinTypeTests.cpp b/tests/chains/Polkadot/TWCoinTypeTests.cpp index 6bda66d2f96..f59d9208226 100644 --- a/tests/chains/Polkadot/TWCoinTypeTests.cpp +++ b/tests/chains/Polkadot/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Polkadot/TransactionCompilerTests.cpp b/tests/chains/Polkadot/TransactionCompilerTests.cpp index 71ddb332c7b..2476fe54ab8 100644 --- a/tests/chains/Polkadot/TransactionCompilerTests.cpp +++ b/tests/chains/Polkadot/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Polygon/TWCoinTypeTests.cpp b/tests/chains/Polygon/TWCoinTypeTests.cpp index 992e52df91d..bf535061355 100644 --- a/tests/chains/Polygon/TWCoinTypeTests.cpp +++ b/tests/chains/Polygon/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/PolygonZkEvm/TWCoinTypeTests.cpp b/tests/chains/PolygonZkEvm/TWCoinTypeTests.cpp index ea8b21db7a8..3329a57d97d 100644 --- a/tests/chains/PolygonZkEvm/TWCoinTypeTests.cpp +++ b/tests/chains/PolygonZkEvm/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/Qtum/TWCoinTypeTests.cpp b/tests/chains/Qtum/TWCoinTypeTests.cpp index 8b7cd06e89d..f32c75e50b4 100644 --- a/tests/chains/Qtum/TWCoinTypeTests.cpp +++ b/tests/chains/Qtum/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Qtum/TWQtumAddressTests.cpp b/tests/chains/Qtum/TWQtumAddressTests.cpp index 9bbccf926f2..cf95e3bc12a 100644 --- a/tests/chains/Qtum/TWQtumAddressTests.cpp +++ b/tests/chains/Qtum/TWQtumAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Ravencoin/TWCoinTypeTests.cpp b/tests/chains/Ravencoin/TWCoinTypeTests.cpp index 1a78402e7c9..2e7e0df55fb 100644 --- a/tests/chains/Ravencoin/TWCoinTypeTests.cpp +++ b/tests/chains/Ravencoin/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Ravencoin/TWRavencoinTransactionTests.cpp b/tests/chains/Ravencoin/TWRavencoinTransactionTests.cpp index 44b354d0b1e..161acf0d6c9 100644 --- a/tests/chains/Ravencoin/TWRavencoinTransactionTests.cpp +++ b/tests/chains/Ravencoin/TWRavencoinTransactionTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Ronin/TWAnyAddressTests.cpp b/tests/chains/Ronin/TWAnyAddressTests.cpp index 7bb11ed3560..b1d162c2974 100644 --- a/tests/chains/Ronin/TWAnyAddressTests.cpp +++ b/tests/chains/Ronin/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Ronin/TWAnySignerTests.cpp b/tests/chains/Ronin/TWAnySignerTests.cpp index f9a6ebad9fa..7e73d988335 100644 --- a/tests/chains/Ronin/TWAnySignerTests.cpp +++ b/tests/chains/Ronin/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "proto/Ethereum.pb.h" diff --git a/tests/chains/Ronin/TWCoinTypeTests.cpp b/tests/chains/Ronin/TWCoinTypeTests.cpp index ab90321df83..3efdb99911f 100644 --- a/tests/chains/Ronin/TWCoinTypeTests.cpp +++ b/tests/chains/Ronin/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Rootstock/TWCoinTypeTests.cpp b/tests/chains/Rootstock/TWCoinTypeTests.cpp index adf559b3644..04795f50dae 100644 --- a/tests/chains/Rootstock/TWCoinTypeTests.cpp +++ b/tests/chains/Rootstock/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Scroll/TWAnySignerTests.cpp b/tests/chains/Scroll/TWAnySignerTests.cpp index 8d00ce21d66..b719ff58bf9 100644 --- a/tests/chains/Scroll/TWAnySignerTests.cpp +++ b/tests/chains/Scroll/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "proto/Ethereum.pb.h" diff --git a/tests/chains/Scroll/TWCoinTypeTests.cpp b/tests/chains/Scroll/TWCoinTypeTests.cpp index fe07ff53096..afe6e3c63b0 100644 --- a/tests/chains/Scroll/TWCoinTypeTests.cpp +++ b/tests/chains/Scroll/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/SmartBitcoinCash/TWCoinTypeTests.cpp b/tests/chains/SmartBitcoinCash/TWCoinTypeTests.cpp index 4ec3646ce34..30c048093bc 100644 --- a/tests/chains/SmartBitcoinCash/TWCoinTypeTests.cpp +++ b/tests/chains/SmartBitcoinCash/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Solana/AddressTests.cpp b/tests/chains/Solana/AddressTests.cpp index 1a3196f5d69..4ae3da89d43 100644 --- a/tests/chains/Solana/AddressTests.cpp +++ b/tests/chains/Solana/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base58.h" #include "HexCoding.h" diff --git a/tests/chains/Solana/ProgramTests.cpp b/tests/chains/Solana/ProgramTests.cpp index 578c7151954..c3b8f7ab317 100644 --- a/tests/chains/Solana/ProgramTests.cpp +++ b/tests/chains/Solana/ProgramTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base58.h" #include "PrivateKey.h" diff --git a/tests/chains/Solana/SignerTests.cpp b/tests/chains/Solana/SignerTests.cpp index 97a3a304cc2..c8ffaf4fe51 100644 --- a/tests/chains/Solana/SignerTests.cpp +++ b/tests/chains/Solana/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PublicKey.h" diff --git a/tests/chains/Solana/TWAnySignerTests.cpp b/tests/chains/Solana/TWAnySignerTests.cpp index a3a867c29ee..8e67cd22bab 100644 --- a/tests/chains/Solana/TWAnySignerTests.cpp +++ b/tests/chains/Solana/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base58.h" #include "HexCoding.h" diff --git a/tests/chains/Solana/TWCoinTypeTests.cpp b/tests/chains/Solana/TWCoinTypeTests.cpp index a2e4de41f0f..4fe7feb42af 100644 --- a/tests/chains/Solana/TWCoinTypeTests.cpp +++ b/tests/chains/Solana/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Solana/TWSolanaAddressTests.cpp b/tests/chains/Solana/TWSolanaAddressTests.cpp index f264ff83ca5..877fbcc5dff 100644 --- a/tests/chains/Solana/TWSolanaAddressTests.cpp +++ b/tests/chains/Solana/TWSolanaAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Solana/TransactionCompilerTests.cpp b/tests/chains/Solana/TransactionCompilerTests.cpp index 853508d719a..08a199038ae 100644 --- a/tests/chains/Solana/TransactionCompilerTests.cpp +++ b/tests/chains/Solana/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base58.h" #include "Coin.h" diff --git a/tests/chains/Solana/TransactionTests.cpp b/tests/chains/Solana/TransactionTests.cpp index 6a7a53f1e1e..7fcefceb521 100644 --- a/tests/chains/Solana/TransactionTests.cpp +++ b/tests/chains/Solana/TransactionTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Solana/Address.h" #include "Solana/Program.h" diff --git a/tests/chains/StarkEx/MessageSignerTests.cpp b/tests/chains/StarkEx/MessageSignerTests.cpp index 42d43604298..d8140ecead3 100644 --- a/tests/chains/StarkEx/MessageSignerTests.cpp +++ b/tests/chains/StarkEx/MessageSignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/tests/chains/Stellar/AddressTests.cpp b/tests/chains/Stellar/AddressTests.cpp index 3b9b7f5cfd9..b4c84772273 100644 --- a/tests/chains/Stellar/AddressTests.cpp +++ b/tests/chains/Stellar/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bitcoin/Address.h" #include "HexCoding.h" diff --git a/tests/chains/Stellar/TWAnySignerTests.cpp b/tests/chains/Stellar/TWAnySignerTests.cpp index 81c4f1f3ca5..fe8728b1de4 100644 --- a/tests/chains/Stellar/TWAnySignerTests.cpp +++ b/tests/chains/Stellar/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Stellar/TWCoinTypeTests.cpp b/tests/chains/Stellar/TWCoinTypeTests.cpp index 113a6366e72..95e6cbd55f1 100644 --- a/tests/chains/Stellar/TWCoinTypeTests.cpp +++ b/tests/chains/Stellar/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Stellar/TWStellarAddressTests.cpp b/tests/chains/Stellar/TWStellarAddressTests.cpp index dad0ef2dcfd..aeb02ed3a06 100644 --- a/tests/chains/Stellar/TWStellarAddressTests.cpp +++ b/tests/chains/Stellar/TWStellarAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Stellar/TransactionCompilerTests.cpp b/tests/chains/Stellar/TransactionCompilerTests.cpp index 94ea746502c..578877ba49c 100644 --- a/tests/chains/Stellar/TransactionCompilerTests.cpp +++ b/tests/chains/Stellar/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Stellar/TransactionTests.cpp b/tests/chains/Stellar/TransactionTests.cpp index 1de448313d9..1803cb1605f 100644 --- a/tests/chains/Stellar/TransactionTests.cpp +++ b/tests/chains/Stellar/TransactionTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Stratis/TWCoinTypeTests.cpp b/tests/chains/Stratis/TWCoinTypeTests.cpp index c2b85b47792..978e12df6b1 100644 --- a/tests/chains/Stratis/TWCoinTypeTests.cpp +++ b/tests/chains/Stratis/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Stratis/TWStratisTests.cpp b/tests/chains/Stratis/TWStratisTests.cpp index 4b15ca2b480..6d8dbf1f95b 100644 --- a/tests/chains/Stratis/TWStratisTests.cpp +++ b/tests/chains/Stratis/TWStratisTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Sui/AddressTests.cpp b/tests/chains/Sui/AddressTests.cpp index 0ced66e12ed..e42f6a5d55d 100644 --- a/tests/chains/Sui/AddressTests.cpp +++ b/tests/chains/Sui/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Sui/Address.h" diff --git a/tests/chains/Sui/CompilerTests.cpp b/tests/chains/Sui/CompilerTests.cpp index 0b94cd53781..cbe887f78d5 100644 --- a/tests/chains/Sui/CompilerTests.cpp +++ b/tests/chains/Sui/CompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Sui/Signer.h" #include "HexCoding.h" diff --git a/tests/chains/Sui/SignerTests.cpp b/tests/chains/Sui/SignerTests.cpp index c261dc501ca..eb61ce26ef0 100644 --- a/tests/chains/Sui/SignerTests.cpp +++ b/tests/chains/Sui/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Sui/Signer.h" #include "Sui/Address.h" diff --git a/tests/chains/Sui/TWCoinTypeTests.cpp b/tests/chains/Sui/TWCoinTypeTests.cpp index ef021d1c58d..459a08e9ff5 100644 --- a/tests/chains/Sui/TWCoinTypeTests.cpp +++ b/tests/chains/Sui/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Syscoin/TWCoinTypeTests.cpp b/tests/chains/Syscoin/TWCoinTypeTests.cpp index 484ced710d5..f33efe2beb6 100644 --- a/tests/chains/Syscoin/TWCoinTypeTests.cpp +++ b/tests/chains/Syscoin/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Syscoin/TWSyscoinTests.cpp b/tests/chains/Syscoin/TWSyscoinTests.cpp index 8c8419d7b6d..77cc8390314 100644 --- a/tests/chains/Syscoin/TWSyscoinTests.cpp +++ b/tests/chains/Syscoin/TWSyscoinTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/TBinance/TWAnyAddressTests.cpp b/tests/chains/TBinance/TWAnyAddressTests.cpp index 8d88ba5e971..196cc13bc86 100644 --- a/tests/chains/TBinance/TWAnyAddressTests.cpp +++ b/tests/chains/TBinance/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "HexCoding.h" diff --git a/tests/chains/TBinance/TWCoinTypeTests.cpp b/tests/chains/TBinance/TWCoinTypeTests.cpp index 564992756e3..6c55888e754 100644 --- a/tests/chains/TBinance/TWCoinTypeTests.cpp +++ b/tests/chains/TBinance/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Tezos/AddressTests.cpp b/tests/chains/Tezos/AddressTests.cpp index 62d0a8d4801..0fca1e431cd 100644 --- a/tests/chains/Tezos/AddressTests.cpp +++ b/tests/chains/Tezos/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HDWallet.h" #include "HexCoding.h" diff --git a/tests/chains/Tezos/ForgingTests.cpp b/tests/chains/Tezos/ForgingTests.cpp index fb50b907dca..95ffe14dd78 100644 --- a/tests/chains/Tezos/ForgingTests.cpp +++ b/tests/chains/Tezos/ForgingTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HDWallet.h" #include "HexCoding.h" diff --git a/tests/chains/Tezos/MessageSignerTests.cpp b/tests/chains/Tezos/MessageSignerTests.cpp index 4fc6faa70c8..3f38133d55a 100644 --- a/tests/chains/Tezos/MessageSignerTests.cpp +++ b/tests/chains/Tezos/MessageSignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/tests/chains/Tezos/OperationListTests.cpp b/tests/chains/Tezos/OperationListTests.cpp index ea42fe028be..4218e01c6e1 100644 --- a/tests/chains/Tezos/OperationListTests.cpp +++ b/tests/chains/Tezos/OperationListTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Tezos/Address.h" #include "Tezos/BinaryCoding.h" diff --git a/tests/chains/Tezos/PublicKeyTests.cpp b/tests/chains/Tezos/PublicKeyTests.cpp index 3131c388e92..c91383fcb86 100644 --- a/tests/chains/Tezos/PublicKeyTests.cpp +++ b/tests/chains/Tezos/PublicKeyTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Tezos/BinaryCoding.h" #include "Tezos/Forging.h" diff --git a/tests/chains/Tezos/SignerTests.cpp b/tests/chains/Tezos/SignerTests.cpp index 451bcdfa131..2f63e20c9e8 100644 --- a/tests/chains/Tezos/SignerTests.cpp +++ b/tests/chains/Tezos/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base58.h" #include "HexCoding.h" diff --git a/tests/chains/Tezos/TWAnySignerTests.cpp b/tests/chains/Tezos/TWAnySignerTests.cpp index 1b7a3fe3e7b..30943e749c8 100644 --- a/tests/chains/Tezos/TWAnySignerTests.cpp +++ b/tests/chains/Tezos/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Tezos/BinaryCoding.h" diff --git a/tests/chains/Tezos/TWCoinTypeTests.cpp b/tests/chains/Tezos/TWCoinTypeTests.cpp index d55339643ca..50e2e1baea1 100644 --- a/tests/chains/Tezos/TWCoinTypeTests.cpp +++ b/tests/chains/Tezos/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Tezos/TransactionCompilerTests.cpp b/tests/chains/Tezos/TransactionCompilerTests.cpp index b9e5836ecff..56f3ec7a008 100644 --- a/tests/chains/Tezos/TransactionCompilerTests.cpp +++ b/tests/chains/Tezos/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/TheOpenNetwork/AddressTests.cpp b/tests/chains/TheOpenNetwork/AddressTests.cpp index c96c77ef289..297fe113531 100644 --- a/tests/chains/TheOpenNetwork/AddressTests.cpp +++ b/tests/chains/TheOpenNetwork/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PublicKey.h" diff --git a/tests/chains/TheOpenNetwork/SignerTests.cpp b/tests/chains/TheOpenNetwork/SignerTests.cpp index 655297436a2..796ccb94acb 100644 --- a/tests/chains/TheOpenNetwork/SignerTests.cpp +++ b/tests/chains/TheOpenNetwork/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" diff --git a/tests/chains/TheOpenNetwork/TWAnyAddressTests.cpp b/tests/chains/TheOpenNetwork/TWAnyAddressTests.cpp index c0f76c560c5..003faec99a6 100644 --- a/tests/chains/TheOpenNetwork/TWAnyAddressTests.cpp +++ b/tests/chains/TheOpenNetwork/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Data.h" diff --git a/tests/chains/TheOpenNetwork/TWAnySignerTests.cpp b/tests/chains/TheOpenNetwork/TWAnySignerTests.cpp index ee7695a4e9c..2a2bbe740be 100644 --- a/tests/chains/TheOpenNetwork/TWAnySignerTests.cpp +++ b/tests/chains/TheOpenNetwork/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" diff --git a/tests/chains/TheOpenNetwork/TWCoinTypeTests.cpp b/tests/chains/TheOpenNetwork/TWCoinTypeTests.cpp index 9d1c497d6fd..533746d4828 100644 --- a/tests/chains/TheOpenNetwork/TWCoinTypeTests.cpp +++ b/tests/chains/TheOpenNetwork/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Theta/SignerTests.cpp b/tests/chains/Theta/SignerTests.cpp index 6fd92992a84..295ee5fc98d 100644 --- a/tests/chains/Theta/SignerTests.cpp +++ b/tests/chains/Theta/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Theta/Signer.h" diff --git a/tests/chains/Theta/TWAnySignerTests.cpp b/tests/chains/Theta/TWAnySignerTests.cpp index b47863b3359..07d544c0a50 100644 --- a/tests/chains/Theta/TWAnySignerTests.cpp +++ b/tests/chains/Theta/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "proto/Theta.pb.h" diff --git a/tests/chains/Theta/TWCoinTypeTests.cpp b/tests/chains/Theta/TWCoinTypeTests.cpp index 65ec43ce3a0..595dfe4172e 100644 --- a/tests/chains/Theta/TWCoinTypeTests.cpp +++ b/tests/chains/Theta/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Theta/TransactionCompilerTests.cpp b/tests/chains/Theta/TransactionCompilerTests.cpp index 677c63b9c9d..fd35e22f406 100644 --- a/tests/chains/Theta/TransactionCompilerTests.cpp +++ b/tests/chains/Theta/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Theta/TransactionTests.cpp b/tests/chains/Theta/TransactionTests.cpp index a5dc2cd456c..7cebeb254b2 100644 --- a/tests/chains/Theta/TransactionTests.cpp +++ b/tests/chains/Theta/TransactionTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Theta/Transaction.h" diff --git a/tests/chains/ThetaFuel/TWAnySignerTests.cpp b/tests/chains/ThetaFuel/TWAnySignerTests.cpp index 610c24c4eea..6650ab26722 100644 --- a/tests/chains/ThetaFuel/TWAnySignerTests.cpp +++ b/tests/chains/ThetaFuel/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/ThetaFuel/TWCoinTypeTests.cpp b/tests/chains/ThetaFuel/TWCoinTypeTests.cpp index c18b9a34639..c00d89bc9e6 100644 --- a/tests/chains/ThetaFuel/TWCoinTypeTests.cpp +++ b/tests/chains/ThetaFuel/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/ThunderToken/TWCoinTypeTests.cpp b/tests/chains/ThunderToken/TWCoinTypeTests.cpp index fec4f509b8a..7534dd9a20f 100644 --- a/tests/chains/ThunderToken/TWCoinTypeTests.cpp +++ b/tests/chains/ThunderToken/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Tron/AddressTests.cpp b/tests/chains/Tron/AddressTests.cpp index c250ff1d07a..7489812ddca 100644 --- a/tests/chains/Tron/AddressTests.cpp +++ b/tests/chains/Tron/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PrivateKey.h" diff --git a/tests/chains/Tron/SerializationTests.cpp b/tests/chains/Tron/SerializationTests.cpp index b5d1c2287cc..ef321093f73 100644 --- a/tests/chains/Tron/SerializationTests.cpp +++ b/tests/chains/Tron/SerializationTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "proto/Tron.pb.h" #include "Tron/Signer.h" diff --git a/tests/chains/Tron/SignerTests.cpp b/tests/chains/Tron/SignerTests.cpp index ebd7235bcad..f27150bb585 100644 --- a/tests/chains/Tron/SignerTests.cpp +++ b/tests/chains/Tron/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bitcoin/Address.h" #include "Tron/Address.h" diff --git a/tests/chains/Tron/TWAnySignerTests.cpp b/tests/chains/Tron/TWAnySignerTests.cpp index 4600bbf6a53..bff0316d20a 100644 --- a/tests/chains/Tron/TWAnySignerTests.cpp +++ b/tests/chains/Tron/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "proto/Tron.pb.h" diff --git a/tests/chains/Tron/TWCoinTypeTests.cpp b/tests/chains/Tron/TWCoinTypeTests.cpp index a09982445bf..9aae259d088 100644 --- a/tests/chains/Tron/TWCoinTypeTests.cpp +++ b/tests/chains/Tron/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Tron/TransactionCompilerTests.cpp b/tests/chains/Tron/TransactionCompilerTests.cpp index 117bce53cf2..0962f4bda07 100644 --- a/tests/chains/Tron/TransactionCompilerTests.cpp +++ b/tests/chains/Tron/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Tron/TronMessageSignerTests.cpp b/tests/chains/Tron/TronMessageSignerTests.cpp index 059ce210822..4b0940a5db6 100644 --- a/tests/chains/Tron/TronMessageSignerTests.cpp +++ b/tests/chains/Tron/TronMessageSignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/VeChain/SignerTests.cpp b/tests/chains/VeChain/SignerTests.cpp index 6dfd156a7b6..1317cc17ee4 100644 --- a/tests/chains/VeChain/SignerTests.cpp +++ b/tests/chains/VeChain/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "VeChain/Signer.h" diff --git a/tests/chains/VeChain/TWAnySignerTests.cpp b/tests/chains/VeChain/TWAnySignerTests.cpp index 7eb04b0cda1..c2aab9d4d8c 100644 --- a/tests/chains/VeChain/TWAnySignerTests.cpp +++ b/tests/chains/VeChain/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "proto/VeChain.pb.h" diff --git a/tests/chains/VeChain/TWCoinTypeTests.cpp b/tests/chains/VeChain/TWCoinTypeTests.cpp index 123ff9b6242..325499d53a8 100644 --- a/tests/chains/VeChain/TWCoinTypeTests.cpp +++ b/tests/chains/VeChain/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/VeChain/TransactionCompilerTests.cpp b/tests/chains/VeChain/TransactionCompilerTests.cpp index e9745a8ee81..6a9f9ab9c90 100644 --- a/tests/chains/VeChain/TransactionCompilerTests.cpp +++ b/tests/chains/VeChain/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Verge/AddressTests.cpp b/tests/chains/Verge/AddressTests.cpp index 17b54ab2ef9..5185d3bd005 100644 --- a/tests/chains/Verge/AddressTests.cpp +++ b/tests/chains/Verge/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Verge/SignerTests.cpp b/tests/chains/Verge/SignerTests.cpp index cce6831c583..14ff7bb0720 100644 --- a/tests/chains/Verge/SignerTests.cpp +++ b/tests/chains/Verge/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Verge/Signer.h" #include "Verge/Transaction.h" diff --git a/tests/chains/Verge/TWAnyAddressTests.cpp b/tests/chains/Verge/TWAnyAddressTests.cpp index 1ede3bd3b36..0476122548a 100644 --- a/tests/chains/Verge/TWAnyAddressTests.cpp +++ b/tests/chains/Verge/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "HexCoding.h" diff --git a/tests/chains/Verge/TWAnySignerTests.cpp b/tests/chains/Verge/TWAnySignerTests.cpp index 961d262fe26..7d65a5a792d 100644 --- a/tests/chains/Verge/TWAnySignerTests.cpp +++ b/tests/chains/Verge/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/tests/chains/Verge/TWCoinTypeTests.cpp b/tests/chains/Verge/TWCoinTypeTests.cpp index 725863b58a9..47d2546607f 100644 --- a/tests/chains/Verge/TWCoinTypeTests.cpp +++ b/tests/chains/Verge/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Verge/TransactionBuilderTests.cpp b/tests/chains/Verge/TransactionBuilderTests.cpp index 283dfd94d07..dd0b84e1c94 100644 --- a/tests/chains/Verge/TransactionBuilderTests.cpp +++ b/tests/chains/Verge/TransactionBuilderTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Verge/Transaction.h" #include "Verge/TransactionBuilder.h" diff --git a/tests/chains/Verge/TransactionCompilerTests.cpp b/tests/chains/Verge/TransactionCompilerTests.cpp index c3d95f45b85..796676fa20f 100644 --- a/tests/chains/Verge/TransactionCompilerTests.cpp +++ b/tests/chains/Verge/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Viacoin/TWCoinTypeTests.cpp b/tests/chains/Viacoin/TWCoinTypeTests.cpp index 83efc7566eb..1c416808887 100644 --- a/tests/chains/Viacoin/TWCoinTypeTests.cpp +++ b/tests/chains/Viacoin/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Viacoin/TWViacoinAddressTests.cpp b/tests/chains/Viacoin/TWViacoinAddressTests.cpp index d31ce2acbd1..996afe1fe8b 100644 --- a/tests/chains/Viacoin/TWViacoinAddressTests.cpp +++ b/tests/chains/Viacoin/TWViacoinAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Viction/TWCoinTypeTests.cpp b/tests/chains/Viction/TWCoinTypeTests.cpp index f138c29f6c9..8bb5b07ecb5 100644 --- a/tests/chains/Viction/TWCoinTypeTests.cpp +++ b/tests/chains/Viction/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/WAX/TWAnySignerTests.cpp b/tests/chains/WAX/TWAnySignerTests.cpp index 7e6d2f4300b..f81d7663dec 100644 --- a/tests/chains/WAX/TWAnySignerTests.cpp +++ b/tests/chains/WAX/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/WAX/TWCoinTypeTests.cpp b/tests/chains/WAX/TWCoinTypeTests.cpp index a28219ba786..bb223717567 100644 --- a/tests/chains/WAX/TWCoinTypeTests.cpp +++ b/tests/chains/WAX/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/Wanchain/TWCoinTypeTests.cpp b/tests/chains/Wanchain/TWCoinTypeTests.cpp index 22baff337e0..9d2f2704238 100644 --- a/tests/chains/Wanchain/TWCoinTypeTests.cpp +++ b/tests/chains/Wanchain/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Waves/AddressTests.cpp b/tests/chains/Waves/AddressTests.cpp index 509cbc7f75c..22de9778b55 100644 --- a/tests/chains/Waves/AddressTests.cpp +++ b/tests/chains/Waves/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PrivateKey.h" diff --git a/tests/chains/Waves/LeaseTests.cpp b/tests/chains/Waves/LeaseTests.cpp index 8428544af3e..588f7f99db7 100644 --- a/tests/chains/Waves/LeaseTests.cpp +++ b/tests/chains/Waves/LeaseTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PrivateKey.h" diff --git a/tests/chains/Waves/SignerTests.cpp b/tests/chains/Waves/SignerTests.cpp index 26fdb7292de..bbc63fad0c5 100644 --- a/tests/chains/Waves/SignerTests.cpp +++ b/tests/chains/Waves/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PublicKey.h" diff --git a/tests/chains/Waves/TWAnySignerTests.cpp b/tests/chains/Waves/TWAnySignerTests.cpp index df7b3afae3b..23e858b682c 100644 --- a/tests/chains/Waves/TWAnySignerTests.cpp +++ b/tests/chains/Waves/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base58.h" #include "HexCoding.h" diff --git a/tests/chains/Waves/TWCoinTypeTests.cpp b/tests/chains/Waves/TWCoinTypeTests.cpp index 83b463552f5..4e3fdc744e1 100644 --- a/tests/chains/Waves/TWCoinTypeTests.cpp +++ b/tests/chains/Waves/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Waves/TransactionTests.cpp b/tests/chains/Waves/TransactionTests.cpp index f97e76784ad..a71ca9c9069 100644 --- a/tests/chains/Waves/TransactionTests.cpp +++ b/tests/chains/Waves/TransactionTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PrivateKey.h" diff --git a/tests/chains/XRP/AddressTests.cpp b/tests/chains/XRP/AddressTests.cpp index cc83acadf5e..cac0f014219 100644 --- a/tests/chains/XRP/AddressTests.cpp +++ b/tests/chains/XRP/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "XRP/Address.h" #include "XRP/XAddress.h" diff --git a/tests/chains/XRP/BinaryCodingTests.cpp b/tests/chains/XRP/BinaryCodingTests.cpp index ed39af8c588..a8081beb0fc 100644 --- a/tests/chains/XRP/BinaryCodingTests.cpp +++ b/tests/chains/XRP/BinaryCodingTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "XRP/BinaryCoding.h" diff --git a/tests/chains/XRP/TWAnySignerTests.cpp b/tests/chains/XRP/TWAnySignerTests.cpp index a6753435329..3dd17ea7d1b 100644 --- a/tests/chains/XRP/TWAnySignerTests.cpp +++ b/tests/chains/XRP/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "proto/Common.pb.h" diff --git a/tests/chains/XRP/TWCoinTypeTests.cpp b/tests/chains/XRP/TWCoinTypeTests.cpp index 2c69d2c7449..d9fcc697b53 100644 --- a/tests/chains/XRP/TWCoinTypeTests.cpp +++ b/tests/chains/XRP/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/XRP/TWRippleAddressTests.cpp b/tests/chains/XRP/TWRippleAddressTests.cpp index 13a4f51c51d..db9d8ec9382 100644 --- a/tests/chains/XRP/TWRippleAddressTests.cpp +++ b/tests/chains/XRP/TWRippleAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/XRP/TransactionCompilerTests.cpp b/tests/chains/XRP/TransactionCompilerTests.cpp index 8408624f083..f5040e2cf41 100644 --- a/tests/chains/XRP/TransactionCompilerTests.cpp +++ b/tests/chains/XRP/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/XRP/TransactionTests.cpp b/tests/chains/XRP/TransactionTests.cpp index 56da08e9139..eccde7ccaf4 100644 --- a/tests/chains/XRP/TransactionTests.cpp +++ b/tests/chains/XRP/TransactionTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "XRP/Address.h" #include "XRP/Transaction.h" diff --git a/tests/chains/Zcash/AddressTests.cpp b/tests/chains/Zcash/AddressTests.cpp index 8c221c3daab..32ea339ed5f 100644 --- a/tests/chains/Zcash/AddressTests.cpp +++ b/tests/chains/Zcash/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Zcash/TAddress.h" #include "HexCoding.h" diff --git a/tests/chains/Zcash/TWCoinTypeTests.cpp b/tests/chains/Zcash/TWCoinTypeTests.cpp index e9727d9eb62..b85b7e8afd9 100644 --- a/tests/chains/Zcash/TWCoinTypeTests.cpp +++ b/tests/chains/Zcash/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Zcash/TWZcashAddressTests.cpp b/tests/chains/Zcash/TWZcashAddressTests.cpp index 006498644da..4b1cfd3dee6 100644 --- a/tests/chains/Zcash/TWZcashAddressTests.cpp +++ b/tests/chains/Zcash/TWZcashAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Zcash/TWZcashTransactionTests.cpp b/tests/chains/Zcash/TWZcashTransactionTests.cpp index e12df1f1e88..cc20c735e5b 100644 --- a/tests/chains/Zcash/TWZcashTransactionTests.cpp +++ b/tests/chains/Zcash/TWZcashTransactionTests.cpp @@ -1,9 +1,7 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Zcash/TransactionCompilerTests.cpp b/tests/chains/Zcash/TransactionCompilerTests.cpp index dc67ff52266..6e92a26d50d 100644 --- a/tests/chains/Zcash/TransactionCompilerTests.cpp +++ b/tests/chains/Zcash/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/Zelcash/TWCoinTypeTests.cpp b/tests/chains/Zelcash/TWCoinTypeTests.cpp index 040deb768c4..ce646194bfb 100644 --- a/tests/chains/Zelcash/TWCoinTypeTests.cpp +++ b/tests/chains/Zelcash/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Zelcash/TWZelcashAddressTests.cpp b/tests/chains/Zelcash/TWZelcashAddressTests.cpp index cba68ab86b4..f247625c93d 100644 --- a/tests/chains/Zelcash/TWZelcashAddressTests.cpp +++ b/tests/chains/Zelcash/TWZelcashAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Zelcash/TWZelcashTransactionTests.cpp b/tests/chains/Zelcash/TWZelcashTransactionTests.cpp index 6206875a0ac..9a61ddcdd10 100644 --- a/tests/chains/Zelcash/TWZelcashTransactionTests.cpp +++ b/tests/chains/Zelcash/TWZelcashTransactionTests.cpp @@ -1,9 +1,7 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/Zen/AddressTests.cpp b/tests/chains/Zen/AddressTests.cpp index eac89795832..bef8a87317a 100644 --- a/tests/chains/Zen/AddressTests.cpp +++ b/tests/chains/Zen/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "HDWallet.h" diff --git a/tests/chains/Zen/SignerTests.cpp b/tests/chains/Zen/SignerTests.cpp index 5ffabe1b339..8b054461429 100644 --- a/tests/chains/Zen/SignerTests.cpp +++ b/tests/chains/Zen/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Zen/Signer.h" #include "Zen/Address.h" diff --git a/tests/chains/Zen/TWAnyAddressTests.cpp b/tests/chains/Zen/TWAnyAddressTests.cpp index 5063cc4a4f4..d6e2a86b245 100644 --- a/tests/chains/Zen/TWAnyAddressTests.cpp +++ b/tests/chains/Zen/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "HexCoding.h" diff --git a/tests/chains/Zen/TWAnySignerTests.cpp b/tests/chains/Zen/TWAnySignerTests.cpp index aa8845c4fe8..55a83993273 100644 --- a/tests/chains/Zen/TWAnySignerTests.cpp +++ b/tests/chains/Zen/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/tests/chains/Zen/TWCoinTypeTests.cpp b/tests/chains/Zen/TWCoinTypeTests.cpp index 5fc0aebbad9..3cd9d0e75a8 100644 --- a/tests/chains/Zen/TWCoinTypeTests.cpp +++ b/tests/chains/Zen/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Zen/TransactionBuilderTests.cpp b/tests/chains/Zen/TransactionBuilderTests.cpp index 3c6ea1df885..8ba691be1b6 100644 --- a/tests/chains/Zen/TransactionBuilderTests.cpp +++ b/tests/chains/Zen/TransactionBuilderTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bitcoin/Transaction.h" #include "Bitcoin/TransactionPlan.h" diff --git a/tests/chains/Zen/TransactionCompilerTests.cpp b/tests/chains/Zen/TransactionCompilerTests.cpp index 6e9c81a35d3..006b9539811 100644 --- a/tests/chains/Zen/TransactionCompilerTests.cpp +++ b/tests/chains/Zen/TransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/chains/ZenEON/TWCoinTypeTests.cpp b/tests/chains/ZenEON/TWCoinTypeTests.cpp index a326e6c2772..a0571a15621 100644 --- a/tests/chains/ZenEON/TWCoinTypeTests.cpp +++ b/tests/chains/ZenEON/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Zilliqa/AddressTests.cpp b/tests/chains/Zilliqa/AddressTests.cpp index d682239df96..db26af859b7 100644 --- a/tests/chains/Zilliqa/AddressTests.cpp +++ b/tests/chains/Zilliqa/AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PrivateKey.h" diff --git a/tests/chains/Zilliqa/SignatureTests.cpp b/tests/chains/Zilliqa/SignatureTests.cpp index 6f11ae3fc38..bea022cd485 100644 --- a/tests/chains/Zilliqa/SignatureTests.cpp +++ b/tests/chains/Zilliqa/SignatureTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include "HexCoding.h" diff --git a/tests/chains/Zilliqa/SignerTests.cpp b/tests/chains/Zilliqa/SignerTests.cpp index 9606d060211..f8e0440307d 100644 --- a/tests/chains/Zilliqa/SignerTests.cpp +++ b/tests/chains/Zilliqa/SignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "PrivateKey.h" diff --git a/tests/chains/Zilliqa/TWAnySignerTests.cpp b/tests/chains/Zilliqa/TWAnySignerTests.cpp index c96d0f1ecc1..1b420161496 100644 --- a/tests/chains/Zilliqa/TWAnySignerTests.cpp +++ b/tests/chains/Zilliqa/TWAnySignerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "proto/Zilliqa.pb.h" diff --git a/tests/chains/Zilliqa/TWCoinTypeTests.cpp b/tests/chains/Zilliqa/TWCoinTypeTests.cpp index 59288082520..f1c5545b0d2 100644 --- a/tests/chains/Zilliqa/TWCoinTypeTests.cpp +++ b/tests/chains/Zilliqa/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/chains/Zilliqa/TWZilliqaAddressTests.cpp b/tests/chains/Zilliqa/TWZilliqaAddressTests.cpp index b9c6ce74002..63b7b515816 100644 --- a/tests/chains/Zilliqa/TWZilliqaAddressTests.cpp +++ b/tests/chains/Zilliqa/TWZilliqaAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/chains/ZkSyncV2/TWCoinTypeTests.cpp b/tests/chains/ZkSyncV2/TWCoinTypeTests.cpp index 0b484f47afb..b72bb030ab0 100644 --- a/tests/chains/ZkSyncV2/TWCoinTypeTests.cpp +++ b/tests/chains/ZkSyncV2/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/chains/xDai/TWCoinTypeTests.cpp b/tests/chains/xDai/TWCoinTypeTests.cpp index 5e1e39a38c8..68ebf0e6ee6 100644 --- a/tests/chains/xDai/TWCoinTypeTests.cpp +++ b/tests/chains/xDai/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // // This is a GENERATED FILE, changes made here MAY BE LOST. // Generated one-time (codegen/bin/cointests) diff --git a/tests/common/AnyAddressTests.cpp b/tests/common/AnyAddressTests.cpp index 23dbe0d962c..fc765c6f548 100644 --- a/tests/common/AnyAddressTests.cpp +++ b/tests/common/AnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "AnyAddress.h" #include "HexCoding.h" diff --git a/tests/common/Base64Tests.cpp b/tests/common/Base64Tests.cpp index 503c79bdf42..b5d01b4ccba 100644 --- a/tests/common/Base64Tests.cpp +++ b/tests/common/Base64Tests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base64.h" #include "Data.h" diff --git a/tests/common/BaseEncoding.cpp b/tests/common/BaseEncoding.cpp index d876682a9ab..69d655c94f9 100644 --- a/tests/common/BaseEncoding.cpp +++ b/tests/common/BaseEncoding.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base32.h" #include "HexCoding.h" diff --git a/tests/common/Bech32AddressTests.cpp b/tests/common/Bech32AddressTests.cpp index 6f8b8995896..11d8ec18b81 100644 --- a/tests/common/Bech32AddressTests.cpp +++ b/tests/common/Bech32AddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bech32Address.h" #include "HexCoding.h" diff --git a/tests/common/Bech32Tests.cpp b/tests/common/Bech32Tests.cpp index 249d0997a71..c081fd9cf6c 100644 --- a/tests/common/Bech32Tests.cpp +++ b/tests/common/Bech32Tests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Bech32.h" #include "HexCoding.h" diff --git a/tests/common/BinaryCodingTests.cpp b/tests/common/BinaryCodingTests.cpp index 225a3c6fd84..5ce20884b9a 100644 --- a/tests/common/BinaryCodingTests.cpp +++ b/tests/common/BinaryCodingTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "BinaryCoding.h" #include "HexCoding.h" diff --git a/tests/common/CborTests.cpp b/tests/common/CborTests.cpp index 1b0bfb41f56..a941565dd39 100644 --- a/tests/common/CborTests.cpp +++ b/tests/common/CborTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Cbor.h" diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index 059290d6c46..77695e734eb 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/common/CoinAddressValidationTests.cpp b/tests/common/CoinAddressValidationTests.cpp index 7920ceb8c20..06fea830573 100644 --- a/tests/common/CoinAddressValidationTests.cpp +++ b/tests/common/CoinAddressValidationTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "HexCoding.h" diff --git a/tests/common/DataTests.cpp b/tests/common/DataTests.cpp index 1dafab2c2fb..9d0a573ecc7 100644 --- a/tests/common/DataTests.cpp +++ b/tests/common/DataTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Data.h" #include "HexCoding.h" diff --git a/tests/common/EncryptTests.cpp b/tests/common/EncryptTests.cpp index f55237294ce..52c030913a2 100644 --- a/tests/common/EncryptTests.cpp +++ b/tests/common/EncryptTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Encrypt.h" #include "Data.h" diff --git a/tests/common/HDWallet/HDWalletInternalTests.cpp b/tests/common/HDWallet/HDWalletInternalTests.cpp index 08d0b655bd3..19e8c45868f 100644 --- a/tests/common/HDWallet/HDWalletInternalTests.cpp +++ b/tests/common/HDWallet/HDWalletInternalTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HDWallet.h" #include "Data.h" diff --git a/tests/common/HDWallet/HDWalletTests.cpp b/tests/common/HDWallet/HDWalletTests.cpp index 9f8dcec791e..32d68f6b006 100644 --- a/tests/common/HDWallet/HDWalletTests.cpp +++ b/tests/common/HDWallet/HDWalletTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Base58.h" #include "Bitcoin/Address.h" diff --git a/tests/common/HashTests.cpp b/tests/common/HashTests.cpp index 224ba408a26..f1c1229e377 100644 --- a/tests/common/HashTests.cpp +++ b/tests/common/HashTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Hash.h" #include "HexCoding.h" diff --git a/tests/common/HexCodingTests.cpp b/tests/common/HexCodingTests.cpp index 1656c2e8a74..db49b1b7376 100644 --- a/tests/common/HexCodingTests.cpp +++ b/tests/common/HexCodingTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Data.h" diff --git a/tests/common/Keystore/DerivationPathTests.cpp b/tests/common/Keystore/DerivationPathTests.cpp index da293ca8fb4..178c9072f54 100644 --- a/tests/common/Keystore/DerivationPathTests.cpp +++ b/tests/common/Keystore/DerivationPathTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coin.h" #include "DerivationPath.h" diff --git a/tests/common/Keystore/StoredKeyTests.cpp b/tests/common/Keystore/StoredKeyTests.cpp index 9a1fc98e8db..ba189cfe5a1 100644 --- a/tests/common/Keystore/StoredKeyTests.cpp +++ b/tests/common/Keystore/StoredKeyTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Keystore/StoredKey.h" diff --git a/tests/common/LiquidStaking/LiquidStakingTests.cpp b/tests/common/LiquidStaking/LiquidStakingTests.cpp index e454bcb4f6b..46f00178f57 100644 --- a/tests/common/LiquidStaking/LiquidStakingTests.cpp +++ b/tests/common/LiquidStaking/LiquidStakingTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "LiquidStaking/LiquidStaking.h" #include "HexCoding.h" diff --git a/tests/common/MnemonicTests.cpp b/tests/common/MnemonicTests.cpp index b47124533b9..bb0b86a55ac 100644 --- a/tests/common/MnemonicTests.cpp +++ b/tests/common/MnemonicTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Mnemonic.h" diff --git a/tests/common/NumericLiteralTests.cpp b/tests/common/NumericLiteralTests.cpp index f4f73b79e01..ef1dcee68dd 100644 --- a/tests/common/NumericLiteralTests.cpp +++ b/tests/common/NumericLiteralTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "NumericLiteral.h" diff --git a/tests/common/PrivateKeyTests.cpp b/tests/common/PrivateKeyTests.cpp index 38b57cd62d1..fa9ec36aea3 100644 --- a/tests/common/PrivateKeyTests.cpp +++ b/tests/common/PrivateKeyTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Hash.h" #include "HexCoding.h" diff --git a/tests/common/PublicKeyLegacy.h b/tests/common/PublicKeyLegacy.h index 41ed229526f..33007667c76 100644 --- a/tests/common/PublicKeyLegacy.h +++ b/tests/common/PublicKeyLegacy.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/tests/common/PublicKeyTests.cpp b/tests/common/PublicKeyTests.cpp index 7267545091d..d73c1c1ac8a 100644 --- a/tests/common/PublicKeyTests.cpp +++ b/tests/common/PublicKeyTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "PublicKey.h" diff --git a/tests/common/TestUtilities.cpp b/tests/common/TestUtilities.cpp index d6c48ea39f6..924b8df3cd8 100644 --- a/tests/common/TestUtilities.cpp +++ b/tests/common/TestUtilities.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/common/TestUtilities.h b/tests/common/TestUtilities.h index 0ede12aa9e4..35949c7166d 100644 --- a/tests/common/TestUtilities.h +++ b/tests/common/TestUtilities.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/tests/common/Uint256Tests.cpp b/tests/common/Uint256Tests.cpp index 57fc6d07fa4..9d084e62b02 100644 --- a/tests/common/Uint256Tests.cpp +++ b/tests/common/Uint256Tests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "uint256.h" #include "HexCoding.h" diff --git a/tests/common/WalletConsoleTests.cpp b/tests/common/WalletConsoleTests.cpp index 5b51118d118..7ac70b0c654 100644 --- a/tests/common/WalletConsoleTests.cpp +++ b/tests/common/WalletConsoleTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "../walletconsole/lib/CommandExecutor.h" #include "../walletconsole/lib/WalletConsole.h" diff --git a/tests/common/WebAuthnTests.cpp b/tests/common/WebAuthnTests.cpp index cd491b39a41..9faaaf39d02 100644 --- a/tests/common/WebAuthnTests.cpp +++ b/tests/common/WebAuthnTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include "HexCoding.h" diff --git a/tests/common/algorithm/erase_tests.cpp b/tests/common/algorithm/erase_tests.cpp index 1cb2937caed..97b6f06897d 100644 --- a/tests/common/algorithm/erase_tests.cpp +++ b/tests/common/algorithm/erase_tests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "algorithm/erase.h" diff --git a/tests/common/algorithm/sort_copy_tests.cpp b/tests/common/algorithm/sort_copy_tests.cpp index ccb59b45490..3bbe1a1c567 100644 --- a/tests/common/algorithm/sort_copy_tests.cpp +++ b/tests/common/algorithm/sort_copy_tests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "algorithm/sort_copy.h" diff --git a/tests/common/algorithm/string.cpp b/tests/common/algorithm/string.cpp index dca8a1fbb45..18f51c95e9c 100644 --- a/tests/common/algorithm/string.cpp +++ b/tests/common/algorithm/string.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "algorithm/string.hpp" diff --git a/tests/common/algorithm/to_array_tests.cpp b/tests/common/algorithm/to_array_tests.cpp index d358dc391ff..033e5faa8e0 100644 --- a/tests/common/algorithm/to_array_tests.cpp +++ b/tests/common/algorithm/to_array_tests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "algorithm/to_array.h" diff --git a/tests/common/memory/memzero_tests.cpp b/tests/common/memory/memzero_tests.cpp index b16c76ef5e6..5edee36dbf7 100644 --- a/tests/common/memory/memzero_tests.cpp +++ b/tests/common/memory/memzero_tests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "memory/memzero_wrapper.h" diff --git a/tests/common/operators/equality_comparable_tests.cpp b/tests/common/operators/equality_comparable_tests.cpp index eaa6ed9bf20..ce54cd16e57 100644 --- a/tests/common/operators/equality_comparable_tests.cpp +++ b/tests/common/operators/equality_comparable_tests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "operators/equality_comparable.h" diff --git a/tests/common/rust/bindgen/WalletCoreRsTests.cpp b/tests/common/rust/bindgen/WalletCoreRsTests.cpp index bd5cd298a41..f2f1eb7f1f5 100644 --- a/tests/common/rust/bindgen/WalletCoreRsTests.cpp +++ b/tests/common/rust/bindgen/WalletCoreRsTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "HexCoding.h" #include "Polkadot/Signer.h" diff --git a/tests/interface/TWAESTests.cpp b/tests/interface/TWAESTests.cpp index b0fd9dd9dca..52675f2aaab 100644 --- a/tests/interface/TWAESTests.cpp +++ b/tests/interface/TWAESTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/interface/TWAccountTests.cpp b/tests/interface/TWAccountTests.cpp index f1ec11cb252..afc84a58498 100644 --- a/tests/interface/TWAccountTests.cpp +++ b/tests/interface/TWAccountTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include diff --git a/tests/interface/TWAnyAddressTests.cpp b/tests/interface/TWAnyAddressTests.cpp index 401b779512c..1068efe40fb 100644 --- a/tests/interface/TWAnyAddressTests.cpp +++ b/tests/interface/TWAnyAddressTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/interface/TWAsnParserTests.cpp b/tests/interface/TWAsnParserTests.cpp index 629e11634dd..ce0ccf0266e 100644 --- a/tests/interface/TWAsnParserTests.cpp +++ b/tests/interface/TWAsnParserTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/interface/TWBase32Tests.cpp b/tests/interface/TWBase32Tests.cpp index b6fd92f29e2..da8fd615984 100644 --- a/tests/interface/TWBase32Tests.cpp +++ b/tests/interface/TWBase32Tests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/interface/TWBase58Tests.cpp b/tests/interface/TWBase58Tests.cpp index 88516a511bb..b0c4e17b3e6 100644 --- a/tests/interface/TWBase58Tests.cpp +++ b/tests/interface/TWBase58Tests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/interface/TWBase64Tests.cpp b/tests/interface/TWBase64Tests.cpp index e553bb83990..bf313b3be60 100644 --- a/tests/interface/TWBase64Tests.cpp +++ b/tests/interface/TWBase64Tests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/interface/TWCoinTypeTests.cpp b/tests/interface/TWCoinTypeTests.cpp index 83cecb15c9a..b7547ae34a9 100644 --- a/tests/interface/TWCoinTypeTests.cpp +++ b/tests/interface/TWCoinTypeTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/interface/TWDataTests.cpp b/tests/interface/TWDataTests.cpp index 7094db38a88..9c1a69c3b07 100644 --- a/tests/interface/TWDataTests.cpp +++ b/tests/interface/TWDataTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "TestUtilities.h" diff --git a/tests/interface/TWDataVectorTests.cpp b/tests/interface/TWDataVectorTests.cpp index 8f467925627..670d50edeba 100644 --- a/tests/interface/TWDataVectorTests.cpp +++ b/tests/interface/TWDataVectorTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "HexCoding.h" diff --git a/tests/interface/TWDerivationPathTests.cpp b/tests/interface/TWDerivationPathTests.cpp index 43991b71ddf..24284ec8ef4 100644 --- a/tests/interface/TWDerivationPathTests.cpp +++ b/tests/interface/TWDerivationPathTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" #include "TrustWalletCore/TWDerivation.h" diff --git a/tests/interface/TWHDWalletTests.cpp b/tests/interface/TWHDWalletTests.cpp index 2fd888d767a..91d183be300 100644 --- a/tests/interface/TWHDWalletTests.cpp +++ b/tests/interface/TWHDWalletTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/interface/TWHRPTests.cpp b/tests/interface/TWHRPTests.cpp index 8c6a38ba969..14ddb39b657 100644 --- a/tests/interface/TWHRPTests.cpp +++ b/tests/interface/TWHRPTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/interface/TWHashTests.cpp b/tests/interface/TWHashTests.cpp index cce13a574f1..800be2f28e9 100644 --- a/tests/interface/TWHashTests.cpp +++ b/tests/interface/TWHashTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Hash.h" #include "HexCoding.h" diff --git a/tests/interface/TWMnemonicTests.cpp b/tests/interface/TWMnemonicTests.cpp index 5233a447cc2..2fd55e74b85 100644 --- a/tests/interface/TWMnemonicTests.cpp +++ b/tests/interface/TWMnemonicTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/interface/TWPBKDF2Tests.cpp b/tests/interface/TWPBKDF2Tests.cpp index 71dff4798a0..dbd22ca6fa4 100644 --- a/tests/interface/TWPBKDF2Tests.cpp +++ b/tests/interface/TWPBKDF2Tests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/interface/TWPrivateKeyTests.cpp b/tests/interface/TWPrivateKeyTests.cpp index 8255ac2cc29..3c4882247ec 100644 --- a/tests/interface/TWPrivateKeyTests.cpp +++ b/tests/interface/TWPrivateKeyTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/interface/TWPublicKeyTests.cpp b/tests/interface/TWPublicKeyTests.cpp index b5432faf79c..16b707e0a95 100644 --- a/tests/interface/TWPublicKeyTests.cpp +++ b/tests/interface/TWPublicKeyTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/interface/TWStoredKeyTests.cpp b/tests/interface/TWStoredKeyTests.cpp index 1dc55cd4782..af17983161a 100644 --- a/tests/interface/TWStoredKeyTests.cpp +++ b/tests/interface/TWStoredKeyTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/interface/TWStringTests.cpp b/tests/interface/TWStringTests.cpp index fc2f06adfc8..15177373914 100644 --- a/tests/interface/TWStringTests.cpp +++ b/tests/interface/TWStringTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "TestUtilities.h" diff --git a/tests/interface/TWTransactionCompilerTests.cpp b/tests/interface/TWTransactionCompilerTests.cpp index 9746a4e9b8f..74302d6c1aa 100644 --- a/tests/interface/TWTransactionCompilerTests.cpp +++ b/tests/interface/TWTransactionCompilerTests.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "proto/Binance.pb.h" #include "proto/Bitcoin.pb.h" diff --git a/tests/main.cpp b/tests/main.cpp index 77776b964c3..93248dc0b6c 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include diff --git a/trezor-crypto/CMakeLists.txt b/trezor-crypto/CMakeLists.txt index 2e38617da2f..238dbc31217 100644 --- a/trezor-crypto/CMakeLists.txt +++ b/trezor-crypto/CMakeLists.txt @@ -1,8 +1,6 @@ -# Copyright © 2017-2022 Trust Wallet. +# SPDX-License-Identifier: Apache-2.0 # -# This file is part of Trust. The full Trust copyright notice, including -# terms governing use, modification, and redistribution, is contained in the -# file LICENSE at the root of the source code distribution tree. +# Copyright © 2017 Trust Wallet. set(TW_WARNING_FLAGS -W diff --git a/trezor-crypto/crypto/tests/CMakeLists.txt b/trezor-crypto/crypto/tests/CMakeLists.txt index f2bf56bee5a..0765cae1f44 100644 --- a/trezor-crypto/crypto/tests/CMakeLists.txt +++ b/trezor-crypto/crypto/tests/CMakeLists.txt @@ -1,8 +1,6 @@ -# Copyright © 2017-2022 Trust Wallet. +# SPDX-License-Identifier: Apache-2.0 # -# This file is part of Trust. The full Trust copyright notice, including -# terms governing use, modification, and redistribution, is contained in the -# file LICENSE at the root of the source code distribution tree. +# Copyright © 2017 Trust Wallet. enable_testing() diff --git a/trezor-crypto/include/TrezorCrypto/TrezorCrypto.h b/trezor-crypto/include/TrezorCrypto/TrezorCrypto.h index 8aa414c9a50..10910c17b2c 100644 --- a/trezor-crypto/include/TrezorCrypto/TrezorCrypto.h +++ b/trezor-crypto/include/TrezorCrypto/TrezorCrypto.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/walletconsole/CMakeLists.txt b/walletconsole/CMakeLists.txt index f72134a29b9..f5c8c08ff31 100644 --- a/walletconsole/CMakeLists.txt +++ b/walletconsole/CMakeLists.txt @@ -1,8 +1,6 @@ -# Copyright © 2017-2022 Trust Wallet. +# SPDX-License-Identifier: Apache-2.0 # -# This file is part of Trust. The full Trust copyright notice, including -# terms governing use, modification, and redistribution, is contained in the -# file LICENSE at the root of the source code distribution tree. +# Copyright © 2017 Trust Wallet. # walletconsole executable file(GLOB walletconsole_sources *.cpp) diff --git a/walletconsole/lib/Address.cpp b/walletconsole/lib/Address.cpp index 6aab3c60ca4..166efc215cc 100644 --- a/walletconsole/lib/Address.cpp +++ b/walletconsole/lib/Address.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Address.h" diff --git a/walletconsole/lib/Address.h b/walletconsole/lib/Address.h index 1d3306c190c..d9db3474502 100644 --- a/walletconsole/lib/Address.h +++ b/walletconsole/lib/Address.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/walletconsole/lib/Buffer.cpp b/walletconsole/lib/Buffer.cpp index 27f40a7e596..5f75eba20fd 100644 --- a/walletconsole/lib/Buffer.cpp +++ b/walletconsole/lib/Buffer.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Buffer.h" #include "WalletConsole.h" diff --git a/walletconsole/lib/Buffer.h b/walletconsole/lib/Buffer.h index 05428f2c012..c0043f6d7a9 100644 --- a/walletconsole/lib/Buffer.h +++ b/walletconsole/lib/Buffer.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/walletconsole/lib/CMakeLists.txt b/walletconsole/lib/CMakeLists.txt index c3ee4a7f6d2..dc3453b0112 100644 --- a/walletconsole/lib/CMakeLists.txt +++ b/walletconsole/lib/CMakeLists.txt @@ -1,8 +1,6 @@ -# Copyright © 2017-2022 Trust Wallet. +# SPDX-License-Identifier: Apache-2.0 # -# This file is part of Trust. The full Trust copyright notice, including -# terms governing use, modification, and redistribution, is contained in the -# file LICENSE at the root of the source code distribution tree. +# Copyright © 2017 Trust Wallet. # walletconsolelib library file(GLOB_RECURSE walletconsolelib_sources *.cpp) diff --git a/walletconsole/lib/Coins.cpp b/walletconsole/lib/Coins.cpp index 368c1b9853e..b73eb6a1eb9 100644 --- a/walletconsole/lib/Coins.cpp +++ b/walletconsole/lib/Coins.cpp @@ -1,9 +1,7 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Coins.h" diff --git a/walletconsole/lib/Coins.h b/walletconsole/lib/Coins.h index 4aacc299eca..6b9a5321fd8 100644 --- a/walletconsole/lib/Coins.h +++ b/walletconsole/lib/Coins.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/walletconsole/lib/CommandExecutor.cpp b/walletconsole/lib/CommandExecutor.cpp index 973c2cf4dd7..4692abf9b98 100644 --- a/walletconsole/lib/CommandExecutor.cpp +++ b/walletconsole/lib/CommandExecutor.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "CommandExecutor.h" #include "WalletConsole.h" diff --git a/walletconsole/lib/CommandExecutor.h b/walletconsole/lib/CommandExecutor.h index a722e47c1fc..2098cb502be 100644 --- a/walletconsole/lib/CommandExecutor.h +++ b/walletconsole/lib/CommandExecutor.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/walletconsole/lib/Keys.cpp b/walletconsole/lib/Keys.cpp index 974e3f46e38..b30e90b98f8 100644 --- a/walletconsole/lib/Keys.cpp +++ b/walletconsole/lib/Keys.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2021 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Keys.h" diff --git a/walletconsole/lib/Keys.h b/walletconsole/lib/Keys.h index 674a50e86c9..d33ace97f44 100644 --- a/walletconsole/lib/Keys.h +++ b/walletconsole/lib/Keys.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/walletconsole/lib/Util.cpp b/walletconsole/lib/Util.cpp index a0312d05261..6cc444822ec 100644 --- a/walletconsole/lib/Util.cpp +++ b/walletconsole/lib/Util.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "Util.h" diff --git a/walletconsole/lib/Util.h b/walletconsole/lib/Util.h index ab9f0f13c9b..d025feb064e 100644 --- a/walletconsole/lib/Util.h +++ b/walletconsole/lib/Util.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/walletconsole/lib/WalletConsole.cpp b/walletconsole/lib/WalletConsole.cpp index 6602428a982..47c4f73a6f9 100644 --- a/walletconsole/lib/WalletConsole.cpp +++ b/walletconsole/lib/WalletConsole.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "WalletConsole.h" #include "CommandExecutor.h" diff --git a/walletconsole/lib/WalletConsole.h b/walletconsole/lib/WalletConsole.h index fc740ae363a..e1482ffffd4 100644 --- a/walletconsole/lib/WalletConsole.h +++ b/walletconsole/lib/WalletConsole.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/walletconsole/main.cpp b/walletconsole/main.cpp index 27819c1e57f..e04d5c09b6a 100644 --- a/walletconsole/main.cpp +++ b/walletconsole/main.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2020 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include "WalletConsole.h" #include diff --git a/wasm/CMakeLists.txt b/wasm/CMakeLists.txt index 592e636d94c..1b4017a7ea4 100644 --- a/wasm/CMakeLists.txt +++ b/wasm/CMakeLists.txt @@ -1,8 +1,6 @@ -# Copyright © 2017-2022 Trust Wallet. +# SPDX-License-Identifier: Apache-2.0 # -# This file is part of Trust. The full Trust copyright notice, including -# terms governing use, modification, and redistribution, is contained in the -# file LICENSE at the root of the source code distribution tree. +# Copyright © 2017 Trust Wallet. file(GLOB wasm_sources src/*.cpp src/generated/*.cpp) file(GLOB wasm_headers src/*.h src/generated/*.h) diff --git a/wasm/index.ts b/wasm/index.ts index 2e298c52d53..a7f54ee4b95 100644 --- a/wasm/index.ts +++ b/wasm/index.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import * as Loader from "./lib/wallet-core"; import { TW } from "./generated/core_proto"; diff --git a/wasm/src/AnySigner.cpp b/wasm/src/AnySigner.cpp index cbddc1a8bf1..9ebd732149c 100644 --- a/wasm/src/AnySigner.cpp +++ b/wasm/src/AnySigner.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // #include diff --git a/wasm/src/BitcoinSigHashTypeExt.cpp b/wasm/src/BitcoinSigHashTypeExt.cpp index c8c1af417cd..5ec44c8385f 100644 --- a/wasm/src/BitcoinSigHashTypeExt.cpp +++ b/wasm/src/BitcoinSigHashTypeExt.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/wasm/src/BitcoinSigHashTypeExt.h b/wasm/src/BitcoinSigHashTypeExt.h index 3d4f4c1c547..315c62b6c91 100644 --- a/wasm/src/BitcoinSigHashTypeExt.h +++ b/wasm/src/BitcoinSigHashTypeExt.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/wasm/src/CoinTypeExt.cpp b/wasm/src/CoinTypeExt.cpp index 7dfcf0af553..40bd47b5292 100644 --- a/wasm/src/CoinTypeExt.cpp +++ b/wasm/src/CoinTypeExt.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include #include "WasmString.h" diff --git a/wasm/src/CoinTypeExt.h b/wasm/src/CoinTypeExt.h index 538928204c8..3f7d4d35842 100644 --- a/wasm/src/CoinTypeExt.h +++ b/wasm/src/CoinTypeExt.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/wasm/src/HDVersionExt.cpp b/wasm/src/HDVersionExt.cpp index 2434ef77f3a..da7b736a6db 100644 --- a/wasm/src/HDVersionExt.cpp +++ b/wasm/src/HDVersionExt.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #include diff --git a/wasm/src/HDVersionExt.h b/wasm/src/HDVersionExt.h index 18d460f68bd..2bc5ca14035 100644 --- a/wasm/src/HDVersionExt.h +++ b/wasm/src/HDVersionExt.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. #pragma once diff --git a/wasm/src/HexCoding.cpp b/wasm/src/HexCoding.cpp index 1a1c58f57c9..b91dd05fc7c 100644 --- a/wasm/src/HexCoding.cpp +++ b/wasm/src/HexCoding.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // #include diff --git a/wasm/src/Random.cpp b/wasm/src/Random.cpp index 81b5674c4af..84e49a515f4 100644 --- a/wasm/src/Random.cpp +++ b/wasm/src/Random.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // #include diff --git a/wasm/src/WasmData.cpp b/wasm/src/WasmData.cpp index 7426d443675..7916c9e39a0 100644 --- a/wasm/src/WasmData.cpp +++ b/wasm/src/WasmData.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // #include "WasmData.h" diff --git a/wasm/src/WasmData.h b/wasm/src/WasmData.h index 133f9b69f73..8cd2a4853ad 100644 --- a/wasm/src/WasmData.h +++ b/wasm/src/WasmData.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // #pragma once diff --git a/wasm/src/WasmString.cpp b/wasm/src/WasmString.cpp index 2ea9f9cec79..f03f58d7b6e 100644 --- a/wasm/src/WasmString.cpp +++ b/wasm/src/WasmString.cpp @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // #include "WasmString.h" diff --git a/wasm/src/WasmString.h b/wasm/src/WasmString.h index ae58e22daa8..9d7c8999e18 100644 --- a/wasm/src/WasmString.h +++ b/wasm/src/WasmString.h @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. // #pragma once diff --git a/wasm/src/enum-ext.d.ts b/wasm/src/enum-ext.d.ts index 83c302e540e..06164ae114c 100644 --- a/wasm/src/enum-ext.d.ts +++ b/wasm/src/enum-ext.d.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. export class BitcoinSigHashTypeExt { static isSingle(type: BitcoinSigHashType): boolean; diff --git a/wasm/src/keystore/default-impl.ts b/wasm/src/keystore/default-impl.ts index 528b63e5554..6da4ae6c57a 100644 --- a/wasm/src/keystore/default-impl.ts +++ b/wasm/src/keystore/default-impl.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import {WalletCore, CoinType, PrivateKey, StoredKey, StoredKeyEncryption} from "../wallet-core"; import * as Types from "./types"; diff --git a/wasm/src/keystore/extension-storage.ts b/wasm/src/keystore/extension-storage.ts index 3056a4b8681..b17e3398649 100644 --- a/wasm/src/keystore/extension-storage.ts +++ b/wasm/src/keystore/extension-storage.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import { Storage } from "webextension-polyfill"; import * as Types from "./types"; diff --git a/wasm/src/keystore/fs-storage.ts b/wasm/src/keystore/fs-storage.ts index 3689be747e3..fd5eaf64631 100644 --- a/wasm/src/keystore/fs-storage.ts +++ b/wasm/src/keystore/fs-storage.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import * as Types from "./types"; import * as fs from "fs/promises"; diff --git a/wasm/src/keystore/index.ts b/wasm/src/keystore/index.ts index 499adb36cdb..663f3affeb0 100644 --- a/wasm/src/keystore/index.ts +++ b/wasm/src/keystore/index.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. export { Default } from "./default-impl"; export * from "./types"; diff --git a/wasm/src/keystore/types.ts b/wasm/src/keystore/types.ts index aea0ee3b4ae..bccb05ef58a 100644 --- a/wasm/src/keystore/types.ts +++ b/wasm/src/keystore/types.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import { CoinType, PrivateKey, StoredKeyEncryption } from "../wallet-core"; diff --git a/wasm/tests/AES.test.ts b/wasm/tests/AES.test.ts index 9a44fb1b337..a6a6b65a56a 100644 --- a/wasm/tests/AES.test.ts +++ b/wasm/tests/AES.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/AnyAddress.test.ts b/wasm/tests/AnyAddress.test.ts index 8d0de564223..5701ea89c98 100644 --- a/wasm/tests/AnyAddress.test.ts +++ b/wasm/tests/AnyAddress.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/Base32.test.ts b/wasm/tests/Base32.test.ts index 36915d1d41b..281a535c48d 100644 --- a/wasm/tests/Base32.test.ts +++ b/wasm/tests/Base32.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import { assert } from "chai"; import { Buffer } from "buffer"; diff --git a/wasm/tests/Base64.test.ts b/wasm/tests/Base64.test.ts index b2f6297627b..b89c99820ec 100644 --- a/wasm/tests/Base64.test.ts +++ b/wasm/tests/Base64.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import { assert } from "chai"; import { Buffer } from "buffer"; diff --git a/wasm/tests/BitcoinSigHashType.test.ts b/wasm/tests/BitcoinSigHashType.test.ts index cfb37e5e2e9..4ff0fdc1a26 100644 --- a/wasm/tests/BitcoinSigHashType.test.ts +++ b/wasm/tests/BitcoinSigHashType.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/Blockchain/Aptos.test.ts b/wasm/tests/Blockchain/Aptos.test.ts index 78486eab2bd..98ccaf9ce27 100644 --- a/wasm/tests/Blockchain/Aptos.test.ts +++ b/wasm/tests/Blockchain/Aptos.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/Blockchain/Bitcoin.test.ts b/wasm/tests/Blockchain/Bitcoin.test.ts index 270f774d86f..98b6cc9213f 100644 --- a/wasm/tests/Blockchain/Bitcoin.test.ts +++ b/wasm/tests/Blockchain/Bitcoin.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/Blockchain/Ethereum.test.ts b/wasm/tests/Blockchain/Ethereum.test.ts index ec652bfabba..357e8d306f3 100644 --- a/wasm/tests/Blockchain/Ethereum.test.ts +++ b/wasm/tests/Blockchain/Ethereum.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/Blockchain/Greenfield.test.ts b/wasm/tests/Blockchain/Greenfield.test.ts index 2af8e9561ee..222d3482d96 100644 --- a/wasm/tests/Blockchain/Greenfield.test.ts +++ b/wasm/tests/Blockchain/Greenfield.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/Blockchain/Hedera.test.ts b/wasm/tests/Blockchain/Hedera.test.ts index 6c26bfadd6c..e9dbbe0c881 100644 --- a/wasm/tests/Blockchain/Hedera.test.ts +++ b/wasm/tests/Blockchain/Hedera.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/Blockchain/InternetComputer.test.ts b/wasm/tests/Blockchain/InternetComputer.test.ts index 1adf5a8fa52..210a06961e0 100644 --- a/wasm/tests/Blockchain/InternetComputer.test.ts +++ b/wasm/tests/Blockchain/InternetComputer.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/Blockchain/Sui.test.ts b/wasm/tests/Blockchain/Sui.test.ts index d729afece5f..fe4a3e15bcf 100644 --- a/wasm/tests/Blockchain/Sui.test.ts +++ b/wasm/tests/Blockchain/Sui.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/Blockchain/TheOpenNetwork.test.ts b/wasm/tests/Blockchain/TheOpenNetwork.test.ts index 46e5ffbdbf9..6cd2eea2261 100644 --- a/wasm/tests/Blockchain/TheOpenNetwork.test.ts +++ b/wasm/tests/Blockchain/TheOpenNetwork.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/CoinType.test.ts b/wasm/tests/CoinType.test.ts index 285891251c6..75a0bed0408 100644 --- a/wasm/tests/CoinType.test.ts +++ b/wasm/tests/CoinType.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/HDVersion.test.ts b/wasm/tests/HDVersion.test.ts index 3ef5aac2729..864f5e2cca5 100644 --- a/wasm/tests/HDVersion.test.ts +++ b/wasm/tests/HDVersion.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2023 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/HDWallet.test.ts b/wasm/tests/HDWallet.test.ts index 567517cc74d..a45fc92c3dd 100644 --- a/wasm/tests/HDWallet.test.ts +++ b/wasm/tests/HDWallet.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/HRP.test.ts b/wasm/tests/HRP.test.ts index 99d596e5f98..e6e81505003 100644 --- a/wasm/tests/HRP.test.ts +++ b/wasm/tests/HRP.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/Hash.test.ts b/wasm/tests/Hash.test.ts index 47c26e87e81..1747995eb48 100644 --- a/wasm/tests/Hash.test.ts +++ b/wasm/tests/Hash.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/HexCoding.test.ts b/wasm/tests/HexCoding.test.ts index 0bacf5d67ce..9879072a047 100644 --- a/wasm/tests/HexCoding.test.ts +++ b/wasm/tests/HexCoding.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/KeyStore+extension.test.ts b/wasm/tests/KeyStore+extension.test.ts index cf876dcc448..9cc1dcf5c22 100644 --- a/wasm/tests/KeyStore+extension.test.ts +++ b/wasm/tests/KeyStore+extension.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/KeyStore+fs.test.ts b/wasm/tests/KeyStore+fs.test.ts index cdadb92940a..78359293dd6 100644 --- a/wasm/tests/KeyStore+fs.test.ts +++ b/wasm/tests/KeyStore+fs.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/Mnemonic.test.ts b/wasm/tests/Mnemonic.test.ts index 690c614aa95..f392a659e27 100644 --- a/wasm/tests/Mnemonic.test.ts +++ b/wasm/tests/Mnemonic.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/PBKDF2.test.ts b/wasm/tests/PBKDF2.test.ts index 138ff504963..8658a79d69e 100644 --- a/wasm/tests/PBKDF2.test.ts +++ b/wasm/tests/PBKDF2.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/StoredKey.test.ts b/wasm/tests/StoredKey.test.ts index a24f8d8479f..7f3b04638c2 100644 --- a/wasm/tests/StoredKey.test.ts +++ b/wasm/tests/StoredKey.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/initWasm.test.ts b/wasm/tests/initWasm.test.ts index 0fb9dd84fef..e20b9cb00b6 100644 --- a/wasm/tests/initWasm.test.ts +++ b/wasm/tests/initWasm.test.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import "mocha"; import { assert } from "chai"; diff --git a/wasm/tests/mock.ts b/wasm/tests/mock.ts index 99451f70d76..7064f115489 100644 --- a/wasm/tests/mock.ts +++ b/wasm/tests/mock.ts @@ -1,8 +1,6 @@ -// Copyright © 2017-2022 Trust Wallet. +// SPDX-License-Identifier: Apache-2.0 // -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. +// Copyright © 2017 Trust Wallet. import { Events, Storage } from "webextension-polyfill"; From 105a900b7613b8971c79053eafe718da5df53036 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Tue, 16 Jan 2024 19:38:18 +0700 Subject: [PATCH 040/128] [Bitcoin/V2] Pass Signing/Planning 2.0 requests to Rust through FFI (#3665) * [Bitcoin/V2]: Add BitcoinV2.proto signing, planning request/result to the legacy Bitcoin.proto * [Bitcoin/V2]: Forward `signing_v2` and `plan_v2` requests to Rust * Add a Plan/Sign V2 test * TODO fix the test * [Bitcoin/V2]: Fix the test * [Linux]: Fix CI * [CI]: Fix Rust sample, SonarCloud pipelines * [Bitcoin/V2]: Try using a GitHub Action for SonarCloud Scan * [CI]: Update sonar-scanner-cli to 5.0.1.3006 * [CI]: Enable all CI pipelines --- .github/workflows/linux-ci-sonarcloud.yml | 12 ++ .github/workflows/linux-ci.yml | 2 +- .github/workflows/linux-sampleapp-ci.yml | 2 +- .../src/modules/legacy/build_and_sign.rs | 5 +- samples/rust/src/build.rs | 2 + src/Bitcoin/Signer.cpp | 31 +++++ src/proto/Bitcoin.proto | 18 +++ .../chains/Bitcoin/TWBitcoinSigningTests.cpp | 130 ++++++++++++++++++ tools/sonarcloud-analysis | 4 +- 9 files changed, 201 insertions(+), 5 deletions(-) diff --git a/.github/workflows/linux-ci-sonarcloud.yml b/.github/workflows/linux-ci-sonarcloud.yml index d8154a0c9b4..775486d741a 100644 --- a/.github/workflows/linux-ci-sonarcloud.yml +++ b/.github/workflows/linux-ci-sonarcloud.yml @@ -16,16 +16,25 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + - name: Install system dependencies run: | tools/install-sys-dependencies-linux tools/install-rust-dependencies + - name: Cache internal dependencies id: internal_cache uses: actions/cache@v3 with: path: build/local key: ${{ runner.os }}-internal-${{ hashFiles('tools/install-dependencies') }} + - name: Install internal dependencies run: | tools/install-dependencies @@ -33,12 +42,14 @@ jobs: CC: /usr/bin/clang CXX: /usr/bin/clang++ if: steps.internal_cache.outputs.cache-hit != 'true' + - name: Code generation run: | tools/generate-files native env: CC: /usr/bin/clang CXX: /usr/bin/clang++ + - name: CMake (coverage/clang-tidy/clang-asan) run: | cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Debug -DTW_CODE_COVERAGE=ON -DTW_ENABLE_CLANG_TIDY=ON -DTW_CLANG_ASAN=ON -GNinja @@ -46,6 +57,7 @@ jobs: env: CC: /usr/bin/clang CXX: /usr/bin/clang++ + - name: SonarCloud Scan run: | ./tools/sonarcloud-analysis diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml index eb9d47d4432..27241508303 100644 --- a/.github/workflows/linux-ci.yml +++ b/.github/workflows/linux-ci.yml @@ -21,7 +21,7 @@ jobs: run: | sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r-ubuntu-test-jammy.list sudo apt-get update - sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.5 libc6-dev=2.35-0ubuntu3.5 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 + sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.6 libc6-dev=2.35-0ubuntu3.6 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 - uses: actions/checkout@v3 - name: Install system dependencies run: | diff --git a/.github/workflows/linux-sampleapp-ci.yml b/.github/workflows/linux-sampleapp-ci.yml index ebed950f55a..0e944049d54 100644 --- a/.github/workflows/linux-sampleapp-ci.yml +++ b/.github/workflows/linux-sampleapp-ci.yml @@ -21,7 +21,7 @@ jobs: run: | sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r-ubuntu-test-jammy.list sudo apt-get update - sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.5 libc6-dev=2.35-0ubuntu3.5 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 + sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.6 libc6-dev=2.35-0ubuntu3.6 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 - uses: actions/checkout@v3 - name: Install system dependencies run: | diff --git a/rust/tw_bitcoin/src/modules/legacy/build_and_sign.rs b/rust/tw_bitcoin/src/modules/legacy/build_and_sign.rs index 097f6b15772..17dbaa03638 100644 --- a/rust/tw_bitcoin/src/modules/legacy/build_and_sign.rs +++ b/rust/tw_bitcoin/src/modules/legacy/build_and_sign.rs @@ -102,6 +102,7 @@ pub fn taproot_build_and_sign_transaction( let transaction = signed .transaction + .as_ref() .expect("transaction not returned from signer"); // Convert the returned transaction data into the (legacy) `Transaction` @@ -141,10 +142,12 @@ pub fn taproot_build_and_sign_transaction( // Put the `Transaction` into the `SigningOutput`, return. let legacy_output = LegacyProto::SigningOutput { transaction: Some(legacy_transaction), - encoded: signed.encoded, + encoded: signed.encoded.clone(), transaction_id: txid_hex.into(), error: CommonProto::SigningError::OK, error_message: Default::default(), + // Set the Bitcoin 2.0 result as well. + signing_result_v2: Some(signed), }; Ok(legacy_output) diff --git a/samples/rust/src/build.rs b/samples/rust/src/build.rs index 63852debfc1..eca91cdf01f 100644 --- a/samples/rust/src/build.rs +++ b/samples/rust/src/build.rs @@ -23,7 +23,9 @@ fn main() { .out_dir(out_dir) .input(proto_src.to_string() + "/Common.proto") .input(proto_src.to_string() + "/Bitcoin.proto") + .input(proto_src.to_string() + "/BitcoinV2.proto") .input(proto_src.to_string() + "/Ethereum.proto") + .input(proto_src.to_string() + "/Utxo.proto") .include(proto_src) .run() .expect("Codegen failed."); diff --git a/src/Bitcoin/Signer.cpp b/src/Bitcoin/Signer.cpp index 0e88b898e1f..ea372085d90 100644 --- a/src/Bitcoin/Signer.cpp +++ b/src/Bitcoin/Signer.cpp @@ -15,6 +15,23 @@ namespace TW::Bitcoin { Proto::TransactionPlan Signer::plan(const Proto::SigningInput& input) noexcept { + if (input.has_planning_v2()) { + Proto::TransactionPlan plan; + + // Forward the `Bitcoin.Proto.SigningInput.planning_v2` request to Rust. + auto planningV2Data = data(input.planning_v2().SerializeAsString()); + Rust::TWDataWrapper planningV2DataPtr(planningV2Data); + Rust::TWDataWrapper planningOutputV2DataPtr = Rust::tw_any_signer_plan(planningV2DataPtr.get(), input.coin_type()); + + auto planningOutputV2Data = planningOutputV2DataPtr.toDataOrDefault(); + BitcoinV2::Proto::TransactionPlan planningOutputV2; + planningOutputV2.ParseFromArray(planningOutputV2Data.data(), static_cast(planningOutputV2Data.size())); + + // Set `Bitcoin.Proto.TransactionPlan.planning_result_v2`. Remain other fields default. + *plan.mutable_planning_result_v2() = planningOutputV2; + return plan; + } + auto plan = TransactionSigner::plan(input); return plan.proto(); } @@ -26,7 +43,21 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input, std::optiona Rust::CByteArrayWrapper res = Rust::tw_bitcoin_legacy_taproot_build_and_sign_transaction(serializedInput.data(), serializedInput.size()); output.ParseFromArray(res.data.data(), static_cast(res.data.size())); return output; + } else if (input.has_signing_v2()) { + // Forward the `Bitcoin.Proto.SigningInput.signing_v2` request to Rust. + auto signingV2Data = data(input.signing_v2().SerializeAsString()); + Rust::TWDataWrapper signingV2DataPtr(signingV2Data); + Rust::TWDataWrapper signingOutputV2DataPtr = Rust::tw_any_signer_sign(signingV2DataPtr.get(), input.coin_type()); + + auto signingOutputV2Data = signingOutputV2DataPtr.toDataOrDefault(); + BitcoinV2::Proto::SigningOutput signingOutputV2; + signingOutputV2.ParseFromArray(signingOutputV2Data.data(), static_cast(signingOutputV2Data.size())); + + // Set `Bitcoin.Proto.SigningOutput.signing_result_v2`. Remain other fields default. + *output.mutable_signing_result_v2() = signingOutputV2; + return output; } + auto result = TransactionSigner::sign(input, false, optionalExternalSigs); if (!result) { output.set_error(result.error()); diff --git a/src/proto/Bitcoin.proto b/src/proto/Bitcoin.proto index ca7aa01f004..4345c7093cc 100644 --- a/src/proto/Bitcoin.proto +++ b/src/proto/Bitcoin.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package TW.Bitcoin.Proto; option java_package = "wallet.core.jni.proto"; +import "BitcoinV2.proto"; import "Common.proto"; // A transaction, with its inputs and outputs @@ -153,7 +154,16 @@ message SigningInput { // transaction creation time that will be used for verge(xvg) uint32 time = 17; + // Deprecated. Consider using `Bitcoin.Proto.SigningInput.signing_v2` instead. bool is_it_brc_operation = 18; + + // If set, uses Bitcoin 2.0 Planning protocol. + // As a result, `Bitcoin.Proto.TransactionPlan.planning_result_v2` is set. + BitcoinV2.Proto.ComposePlan planning_v2 = 20; + + // If set, uses Bitcoin 2.0 Signing protocol. + // As a result, `Bitcoin.Proto.SigningOutput.signing_result_v2` is set. + BitcoinV2.Proto.SigningInput signing_v2 = 21; } // Describes a preliminary transaction plan. @@ -187,6 +197,10 @@ message TransactionPlan { // zen preblockheight int64 preblockheight = 10; + + // Result of a transaction planning using the Bitcoin 2.0 protocol. + // Set if `Bitcoin.Proto.SigningInput.planning_v2` used. + BitcoinV2.Proto.TransactionPlan planning_result_v2 = 12; }; // Result containing the signed and encoded transaction. @@ -206,6 +220,10 @@ message SigningOutput { // error description string error_message = 5; + + // Result of a transaction signing using the Bitcoin 2.0 protocol. + // Set if `Bitcoin.Proto.SigningInput.signing_v2` used. + BitcoinV2.Proto.SigningOutput signing_result_v2 = 7; } /// Pre-image hash to be used for signing diff --git a/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp b/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp index 7a548f0fa90..3c22eeae902 100644 --- a/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp +++ b/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp @@ -32,6 +32,8 @@ namespace TW::Bitcoin { +constexpr uint64_t ONE_BTC = 100'000'000; + // clang-format off SigningInput buildInputP2PKH(bool omitKey = false) { auto hash0 = parse_hex("fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f"); @@ -574,6 +576,134 @@ TEST(BitcoinSigning, SignNftInscriptionReveal) { ASSERT_EQ(result.substr(292, result.size() - 292), expectedHex.substr(292, result.size() - 292)); } +TEST(BitcoinSigning, PlanAndSignBrc20) { + auto privateKey = parse_hex("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129"); + auto publicKey = parse_hex("030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb"); + + // Construct a `BitcoinV2.Proto.ComposePlan` message. + + auto dustSatoshis = 546; + auto ticker = "oadf"; + auto tokensAmount = 20; + auto feePerVb = 25; + + auto txId1 = parse_hex("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911"); + std::reverse(begin(txId1), end(txId1)); + + BitcoinV2::Proto::Input tx1; + tx1.set_txid(txId1.data(), (int)txId1.size()); + tx1.set_vout(0); + tx1.set_value(ONE_BTC); + tx1.set_sighash_type(Utxo::Proto::SighashType::All); + tx1.mutable_builder()->set_p2wpkh(publicKey.data(), (int)publicKey.size()); + + auto txId2 = parse_hex("858e450a1da44397bde05ca2f8a78510d74c623cc2f69736a8b3fbfadc161f6e"); + std::reverse(begin(txId2), end(txId2)); + + BitcoinV2::Proto::Input tx2; + tx2.set_txid(txId2.data(), (int)txId2.size()); + tx2.set_vout(0); + tx2.set_value(2 * ONE_BTC); + tx2.set_sighash_type(Utxo::Proto::SighashType::All); + tx2.mutable_builder()->set_p2wpkh(publicKey.data(), (int)publicKey.size()); + + BitcoinV2::Proto::Output taggedOutput; + taggedOutput.set_value(dustSatoshis); + taggedOutput.mutable_builder()->mutable_p2wpkh()->set_pubkey(publicKey.data(), (int)publicKey.size()); + + BitcoinV2::Proto::Output changeOutput; + // Will be set by the library. + changeOutput.set_value(0); + changeOutput.mutable_builder()->mutable_p2wpkh()->set_pubkey(publicKey.data(), (int)publicKey.size()); + + BitcoinV2::Proto::Input_InputBrc20Inscription brc20Inscription; + brc20Inscription.set_one_prevout(false); + brc20Inscription.set_inscribe_to(publicKey.data(), (int)publicKey.size()); + brc20Inscription.set_ticker(ticker); + brc20Inscription.set_transfer_amount(tokensAmount); + + BitcoinV2::Proto::ComposePlan composePlan; + auto& composeBrc20 = *composePlan.mutable_brc20(); + composeBrc20.set_private_key(privateKey.data(), (int)privateKey.size()); + *composeBrc20.add_inputs() = tx1; + *composeBrc20.add_inputs() = tx2; + composeBrc20.set_input_selector(Utxo::Proto::InputSelector::SelectAscending); + *composeBrc20.mutable_tagged_output() = taggedOutput; + *composeBrc20.mutable_inscription() = brc20Inscription; + composeBrc20.set_fee_per_vb(feePerVb); + *composeBrc20.mutable_change_output() = changeOutput; + composeBrc20.set_disable_change_output(false); + + // Construct a `Bitcoin.Proto.SigningInput` message with `planning_v2` field only. + Proto::SigningInput input; + *input.mutable_planning_v2() = composePlan; + + // Plan the transaction using standard `TWAnySignerPlan`. + Proto::TransactionPlan plan; + ANY_PLAN(input, plan, TWCoinTypeBitcoin); + + // Check the result Planning V2. + EXPECT_TRUE(plan.has_planning_result_v2()); + const auto& planV2 = plan.planning_result_v2(); + EXPECT_EQ(planV2.error(), BitcoinV2::Proto::Error::OK); + EXPECT_TRUE(planV2.has_brc20()); + const auto& brc20Plan = planV2.brc20(); + + // Check the result Commit `SigningInput`. + auto commitOutputAmount = 3846; + EXPECT_TRUE(brc20Plan.has_commit()); + const auto& brc20Commit = brc20Plan.commit(); + EXPECT_EQ(brc20Commit.version(), 2); + EXPECT_EQ(brc20Commit.inputs_size(), 1); + EXPECT_EQ(brc20Commit.outputs_size(), 2); + // Change output generation is disabled, included in `commit.outputs`. + EXPECT_FALSE(brc20Commit.has_change_output()); + EXPECT_EQ(brc20Commit.outputs(0).value(), commitOutputAmount); + EXPECT_EQ(brc20Commit.outputs(0).builder().brc20_inscribe().ticker(), ticker); + EXPECT_EQ(brc20Commit.outputs(0).builder().brc20_inscribe().transfer_amount(), tokensAmount); + // Change: tx1 value - out1 value + EXPECT_EQ(brc20Commit.outputs(1).value(), ONE_BTC - commitOutputAmount - 3175); + + // Check the result Reveal `SigningInput`. + EXPECT_TRUE(brc20Plan.has_reveal()); + const auto& brc20Reveal = brc20Plan.reveal(); + EXPECT_EQ(brc20Reveal.version(), 2); + EXPECT_EQ(brc20Reveal.inputs_size(), 1); + EXPECT_EQ(brc20Reveal.outputs_size(), 1); + // Change output generation is disabled, included in `commit.outputs`. + EXPECT_FALSE(brc20Reveal.has_change_output()); + EXPECT_EQ(brc20Reveal.inputs(0).value(), commitOutputAmount); + EXPECT_EQ(brc20Reveal.inputs(0).builder().brc20_inscribe().ticker(), ticker); + EXPECT_EQ(brc20Reveal.inputs(0).builder().brc20_inscribe().transfer_amount(), tokensAmount); + EXPECT_EQ(brc20Reveal.outputs(0).value(), dustSatoshis); + + // Construct a `Bitcoin.Proto.SigningInput` message with `signing_v2` (Commit) field only. + { + Proto::SigningInput commitInput; + *commitInput.mutable_signing_v2() = brc20Commit; + + Proto::SigningOutput output; + ANY_SIGN(commitInput, TWCoinTypeBitcoin); + EXPECT_EQ(output.error(), Common::Proto::SigningError::OK); + EXPECT_TRUE(output.has_signing_result_v2()); + EXPECT_EQ(hex(output.signing_result_v2().encoded()), "0200000000010111b9f62923af73e297abb69f749e7a1aa2735fbdfd32ac5f6aa89e5c96841c180000000000ffffffff02060f000000000000225120e8b706a97732e705e22ae7710703e7f589ed13c636324461afa443016134cc0593c5f50500000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d02483045022100912004efb9b4e8368ba00d6bfbdfde22a43b037f64ae09d79aac030c77edbc2802206c5702646eadea2274c4aafee99c12b5054cb60da18c21f67f5f3003a318112d0121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000"); + } + + // Construct a `Bitcoin.Proto.SigningInput` message with `signing_v2` (Reveal) field only. + { + Proto::SigningInput revealInput; + *revealInput.mutable_signing_v2() = brc20Reveal; + // `schnorr` is used to sign the Reveal transaction. + revealInput.mutable_signing_v2()->set_dangerous_use_fixed_schnorr_rng(true); + + Proto::SigningOutput output; + ANY_SIGN(revealInput, TWCoinTypeBitcoin); + EXPECT_EQ(output.error(), Common::Proto::SigningError::OK); + EXPECT_TRUE(output.has_signing_result_v2()); + EXPECT_EQ(hex(output.signing_result_v2().encoded()), "0200000000010173711b50d9adb30fdc51231cd56a95b3b627453add775c56188449f2dccaef250000000000ffffffff012202000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d03405cd7fb811a8ebcc55ac791321243a6d1a4089abc548d93288dfe5870f6af7f96b0cb0c7c41c0126791179e8c190d8fecf9bdc4cc740ec3e7d6a43b1b0a345f155b0063036f7264010118746578742f706c61696e3b636861727365743d7574662d3800377b2270223a226272632d3230222c226f70223a227472616e73666572222c227469636b223a226f616466222c22616d74223a223230227d6821c00f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000"); + } +} + TEST(BitcoinSigning, SignP2PKH) { auto input = buildInputP2PKH(); diff --git a/tools/sonarcloud-analysis b/tools/sonarcloud-analysis index 9ff3a8440e4..a8daa2f79ad 100755 --- a/tools/sonarcloud-analysis +++ b/tools/sonarcloud-analysis @@ -1,7 +1,7 @@ #!/usr/bin/env bash -TARGET=sonar-scanner-cli-4.7.0.2747-linux.zip -TARGET_DIR=sonar-scanner-4.7.0.2747-linux +TARGET=sonar-scanner-cli-5.0.1.3006-linux.zip +TARGET_DIR=sonar-scanner-5.0.1.3006-linux curl https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/${TARGET} --output ${TARGET} unzip ${TARGET} cp tools/sonar-scanner.properties ${TARGET_DIR}/conf From b65adc4c86e49eb905f659ade025185a62e87ca9 Mon Sep 17 00:00:00 2001 From: h8s <360470+hewigovens@users.noreply.github.com> Date: Tue, 16 Jan 2024 22:04:26 +0900 Subject: [PATCH 041/128] Update LICENSE (#3664) Co-authored-by: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> --- LICENSE | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/LICENSE b/LICENSE index 261eeb9e9f8..591e7191a60 100644 --- a/LICENSE +++ b/LICENSE @@ -175,18 +175,7 @@ END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] + Copyright 2017 Trust Wallet Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 43bf58c0c99d78789b5a11714ebc686b4268fa06 Mon Sep 17 00:00:00 2001 From: Fabio Lama <42901763+lamafab@users.noreply.github.com> Date: Mon, 22 Jan 2024 18:03:43 +0100 Subject: [PATCH 042/128] [PoC/Bitcoin/Utxo] Correctly Consider Fees for UTXO Selection (#3667) * change utxo selection logic for tw_utxo::Compiler * small cleanup * update selected utxos, init input_selection test module * expand tests for change output generation * handle change output correctly in fee estimation, expand tests * calculate the effective fee rate, not the theoretical * add extra sanity checks * add input_selection_select_ascending test * test for empty inputs and outputs, extend error variant * additional comments and sanity checks * simplify transaction builder for BRC20, update tests * update reveal transaction signing in BRC20 builder * remove weight and fee_estimate checks in tw_utxo, plan for later * extra check for error * add input_selection_insufficient_inputs test * scrap the BitcoinPlanBuilder * remove utils module * cargo fmt * calculate the effective fee after setting the change output, if enabled * add UseAll selection tests, check for 'effective' fee * remove fee_estimate.rs, covered in input_selection.rs * use consts for alice and bob info * do extra checks in compile_impl * update change amount correctly in the native Transaction type before calculating sighash * build index of private keys AFTER selecting UTXOs * cargo fmt * [BRC20]: Take Inscription amount as a string * [BRC20]: Take Inscription amount as a string in C++ * [BRC20]: Add a test for BitcoinV2 bridge through the legacy `SigningInput` --------- Co-authored-by: Satoshi Otomakan --- rust/tw_bitcoin/src/entry.rs | 60 +- rust/tw_bitcoin/src/lib.rs | 1 - rust/tw_bitcoin/src/modules/mod.rs | 2 - rust/tw_bitcoin/src/modules/plan_builder.rs | 230 ------ rust/tw_bitcoin/src/modules/signer.rs | 96 ++- .../src/modules/transactions/brc20.rs | 6 +- .../src/modules/transactions/input_builder.rs | 9 +- .../transactions/input_claim_builder.rs | 10 +- .../modules/transactions/output_builder.rs | 9 +- rust/tw_bitcoin/src/modules/utils.rs | 188 ----- rust/tw_bitcoin/tests/brc20.rs | 4 +- rust/tw_bitcoin/tests/free_estimate.rs | 246 ------ rust/tw_bitcoin/tests/input_selection.rs | 747 ++++++++++++++++++ rust/tw_bitcoin/tests/legacy_build_sign.rs | 3 +- rust/tw_bitcoin/tests/legacy_scripts.rs | 7 +- rust/tw_bitcoin/tests/p2pkh.rs | 15 - rust/tw_bitcoin/tests/p2tr_script_path.rs | 3 +- rust/tw_bitcoin/tests/plan_builder.rs | 192 ----- rust/tw_utxo/src/compiler.rs | 281 ++++--- rust/tw_utxo/tests/input_selection.rs | 10 +- rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs | 10 +- src/Bitcoin/Script.cpp | 4 +- src/Bitcoin/Script.h | 2 +- src/interface/TWBitcoinScript.cpp | 2 +- src/proto/BitcoinV2.proto | 5 +- src/proto/Utxo.proto | 22 +- .../chains/Bitcoin/TWBitcoinSigningTests.cpp | 184 ++--- 27 files changed, 1159 insertions(+), 1189 deletions(-) delete mode 100644 rust/tw_bitcoin/src/modules/plan_builder.rs delete mode 100644 rust/tw_bitcoin/src/modules/utils.rs delete mode 100644 rust/tw_bitcoin/tests/free_estimate.rs create mode 100644 rust/tw_bitcoin/tests/input_selection.rs delete mode 100644 rust/tw_bitcoin/tests/plan_builder.rs diff --git a/rust/tw_bitcoin/src/entry.rs b/rust/tw_bitcoin/src/entry.rs index 0ba49752e50..18bab2ce741 100644 --- a/rust/tw_bitcoin/src/entry.rs +++ b/rust/tw_bitcoin/src/entry.rs @@ -1,4 +1,3 @@ -use crate::modules::plan_builder::BitcoinPlanBuilder; use crate::modules::signer::Signer; use crate::{Error, Result}; use bitcoin::address::NetworkChecked; @@ -11,6 +10,7 @@ use tw_coin_entry::derivation::Derivation; use tw_coin_entry::error::{AddressError, AddressResult}; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; +use tw_coin_entry::modules::plan_builder::NoPlanBuilder; use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_coin_entry::prefix::NoPrefix; use tw_coin_entry::signing_output_error; @@ -44,7 +44,7 @@ impl CoinEntry for BitcoinEntry { // Optional modules: type JsonSigner = NoJsonSigner; - type PlanBuilder = BitcoinPlanBuilder; + type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; type WalletConnector = NoWalletConnector; @@ -128,7 +128,7 @@ impl CoinEntry for BitcoinEntry { #[inline] fn plan_builder(&self) -> Option { - Some(BitcoinPlanBuilder) + None } } @@ -147,14 +147,15 @@ impl BitcoinEntry { .map(crate::modules::transactions::InputBuilder::utxo_from_proto) .collect::>>()?; - // Convert output builders into Utxo outputs. + // Convert output builders into Utxo outputs (does not contain the change output). let mut utxo_outputs = proto .outputs .iter() .map(crate::modules::transactions::OutputBuilder::utxo_from_proto) .collect::>>()?; - // If automatic change output is enabled, a change script must be provided. + // If automatic change output creation is enabled (by default), a change + // script must be provided. let change_script_pubkey = if proto.disable_change_output { Cow::default() } else { @@ -186,17 +187,23 @@ impl BitcoinEntry { disable_change_output: proto.disable_change_output, }; - // Generate the sighashes to be signed. + // Generate the sighashes to be signed. This also selects the inputs + // according to the input selecter and appends a change output, if + // enabled. let utxo_presigning = tw_utxo::compiler::Compiler::preimage_hashes(utxo_signing); handle_utxo_error(&utxo_presigning.error)?; - // If a change output was created by the Utxo compiler, we return it here too. - if utxo_presigning.outputs.len() == utxo_outputs.len() + 1 { + // Check whether the change output is present. + if !proto.disable_change_output { + // Change output has been added. + debug_assert_eq!(utxo_presigning.outputs.len(), utxo_outputs.len() + 1); + let change_output = utxo_presigning .outputs .last() .expect("expected change output"); + // Push it to the list of outputs. utxo_outputs.push(Proto::mod_PreSigningOutput::TxOut { value: change_output.value, script_pubkey: change_output.script_pubkey.to_vec().into(), @@ -205,6 +212,10 @@ impl BitcoinEntry { }) } + // Sanity check. + debug_assert!(utxo_presigning.inputs.len() <= proto.inputs.len()); + debug_assert_eq!(utxo_presigning.outputs.len(), utxo_outputs.len()); + Ok(Proto::PreSigningOutput { error: Proto::Error::OK, error_message: Default::default(), @@ -241,13 +252,14 @@ impl BitcoinEntry { crate::modules::transactions::InputClaimBuilder::utxo_claim_from_proto( input, signature, )?; + utxo_input_claims.push(utxo_claim); } - // Process all the outputs. + // Prepare all the outputs. let mut utxo_outputs = vec![]; - for output in proto.outputs { - let utxo = crate::modules::transactions::OutputBuilder::utxo_from_proto(&output)?; + for output in &proto.outputs { + let utxo = crate::modules::transactions::OutputBuilder::utxo_from_proto(output)?; utxo_outputs.push(utxo); } @@ -272,9 +284,13 @@ impl BitcoinEntry { let utxo_serialized = tw_utxo::compiler::Compiler::compile(utxo_preserializtion); handle_utxo_error(&utxo_serialized.error)?; - // Prepare `Proto::TransactionInput` protobufs for signing output. + let mut total_input_amount = 0; + + // Prepare `Proto::TransactionInput` for end result. let mut proto_inputs = vec![]; for input in utxo_input_claims { + total_input_amount += input.value; + proto_inputs.push(Proto::TransactionInput { txid: Cow::Owned(input.txid.to_vec()), vout: input.vout, @@ -288,7 +304,7 @@ impl BitcoinEntry { }); } - // Prepare `Proto::TransactionOutput` protobufs for output. + // Prepare `Proto::TransactionOutput` for end result. let mut proto_outputs = vec![]; for output in utxo_outputs { proto_outputs.push(Proto::TransactionOutput { @@ -299,7 +315,7 @@ impl BitcoinEntry { }); } - // Prepare `Proto::Transaction` protobuf for output. + // Prepare `Proto::Transaction` for end result. let transaction = Proto::Transaction { version: proto.version, lock_time: proto.lock_time, @@ -307,6 +323,21 @@ impl BitcoinEntry { outputs: proto_outputs, }; + let total_output_amount = transaction + .outputs + .iter() + .map(|output| output.value) + .sum::(); + + // Sanity check. + debug_assert_eq!(transaction.inputs.len(), proto.inputs.len()); + debug_assert_eq!(transaction.outputs.len(), proto.outputs.len()); + // Every output is accounted for, including the fee. + debug_assert_eq!( + total_input_amount, + total_output_amount + utxo_serialized.fee + ); + // Return the full protobuf output. Ok(Proto::SigningOutput { error: Proto::Error::OK, @@ -353,6 +384,7 @@ fn handle_utxo_error(utxo_err: &UtxoProto::Error) -> Result<()> { UtxoProto::Error::Error_missing_sighash_method => Proto::Error::Error_utxo_missing_sighash_method, UtxoProto::Error::Error_failed_encoding => Proto::Error::Error_utxo_failed_encoding, UtxoProto::Error::Error_insufficient_inputs => Proto::Error::Error_utxo_insufficient_inputs, + UtxoProto::Error::Error_no_outputs_specified => Proto::Error::Error_utxo_no_outputs_specified, UtxoProto::Error::Error_missing_change_script_pubkey => Proto::Error::Error_utxo_missing_change_script_pubkey, }; diff --git a/rust/tw_bitcoin/src/lib.rs b/rust/tw_bitcoin/src/lib.rs index a3bb3d0ac69..be3e835b6a6 100644 --- a/rust/tw_bitcoin/src/lib.rs +++ b/rust/tw_bitcoin/src/lib.rs @@ -16,7 +16,6 @@ pub type Result = std::result::Result; #[derive(Debug)] pub struct Error(Proto::Error); -// TODO: We can improve this. impl Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self.0) diff --git a/rust/tw_bitcoin/src/modules/mod.rs b/rust/tw_bitcoin/src/modules/mod.rs index ff6024a4895..e6842d7bfc7 100644 --- a/rust/tw_bitcoin/src/modules/mod.rs +++ b/rust/tw_bitcoin/src/modules/mod.rs @@ -1,5 +1,3 @@ pub mod legacy; -pub mod plan_builder; pub mod signer; pub mod transactions; -mod utils; diff --git a/rust/tw_bitcoin/src/modules/plan_builder.rs b/rust/tw_bitcoin/src/modules/plan_builder.rs deleted file mode 100644 index fd8eb880cad..00000000000 --- a/rust/tw_bitcoin/src/modules/plan_builder.rs +++ /dev/null @@ -1,230 +0,0 @@ -use crate::modules::utils::hard_clone_proto_output; -use crate::{aliases::*, pre_processor, BitcoinEntry}; -use crate::{Error, Result}; -use tw_coin_entry::coin_entry::CoinEntry; -use tw_coin_entry::modules::plan_builder::PlanBuilder; -use tw_coin_entry::signing_output_error; -use tw_proto::BitcoinV2::Proto; -use tw_proto::BitcoinV2::Proto::mod_Input::InputBrc20Inscription; -use tw_proto::Utxo::Proto as UtxoProto; - -pub struct BitcoinPlanBuilder; - -impl PlanBuilder for BitcoinPlanBuilder { - type SigningInput<'a> = Proto::ComposePlan<'a>; - type Plan = Proto::TransactionPlan<'static>; - - #[inline] - fn plan( - &self, - _coin: &dyn tw_coin_entry::coin_context::CoinContext, - proto: Self::SigningInput<'_>, - ) -> Self::Plan { - self.plan_impl(_coin, proto) - .unwrap_or_else(|err| signing_output_error!(Proto::TransactionPlan, err)) - } -} - -impl BitcoinPlanBuilder { - fn plan_impl( - &self, - _coin: &dyn tw_coin_entry::coin_context::CoinContext, - proto: Proto::ComposePlan<'_>, - ) -> Result> { - let plan = match proto.compose { - Proto::mod_ComposePlan::OneOfcompose::brc20(plan) => { - let built_plan = self.plan_brc20(_coin, plan)?; - - Proto::TransactionPlan { - error: Proto::Error::OK, - error_message: Default::default(), - plan: Proto::mod_TransactionPlan::OneOfplan::brc20(built_plan), - } - }, - _ => panic!(), - }; - - Ok(plan) - } - fn plan_brc20( - &self, - _coin: &dyn tw_coin_entry::coin_context::CoinContext, - proto: Proto::mod_ComposePlan::ComposeBrc20Plan<'_>, - ) -> Result> { - // Hard-clones - let inscription = proto - .inscription - .ok_or_else(|| Error::from(Proto::Error::Error_missing_inscription))?; - - let brc20_info = InputBrc20Inscription { - one_prevout: inscription.one_prevout, - inscribe_to: inscription.inscribe_to.to_vec().into(), - ticker: inscription.ticker.to_string().into(), - transfer_amount: inscription.transfer_amount, - }; - - let tagged_output = super::utils::hard_clone_proto_output( - proto - .tagged_output - .ok_or_else(|| Error::from(Proto::Error::Error_missing_tagged_output))?, - )?; - - // First, we create the reveal transaction in order to calculate its input requirement (fee + dust limit). - - // We can use a zeroed Txid here. - let txid = vec![0; 32]; - let dummy_brc20_input = Proto::Input { - txid: txid.into(), - // The value is not relevant here, but we raise it above the output - // or we get an error. - value: u64::MAX, - sighash_type: UtxoProto::SighashType::UseDefault, - to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { - variant: ProtoInputBuilder::brc20_inscribe(brc20_info.clone()), - }), - ..Default::default() - }; - - let dummy_reveal = Proto::SigningInput { - inputs: vec![dummy_brc20_input], - outputs: vec![tagged_output.clone()], - input_selector: UtxoProto::InputSelector::UseAll, - // Disable change output creation. - fee_per_vb: proto.fee_per_vb, - disable_change_output: true, - ..Default::default() - }; - - // We can now determine the fee of the reveal transaction. - let dummy_presigned = BitcoinEntry.preimage_hashes(_coin, dummy_reveal); - if dummy_presigned.error != Proto::Error::OK { - return Err(Error::from(dummy_presigned.error)); - } - - assert_eq!(dummy_presigned.error, Proto::Error::OK); - let reveal_fee_estimate = dummy_presigned.fee_estimate; - - // Create the BRC20 output for the COMMIT transaction; we set the - // amount to the estimated fee (REVEAL) plus the dust limit (`tagged_output.value`). - let brc20_output = Proto::Output { - value: reveal_fee_estimate + tagged_output.value, - to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { - variant: ProtoOutputBuilder::brc20_inscribe( - Proto::mod_Output::OutputBrc20Inscription { - inscribe_to: brc20_info.inscribe_to.to_vec().into(), - ticker: brc20_info.ticker.to_string().into(), - transfer_amount: brc20_info.transfer_amount, - }, - ), - }), - }; - - let brc20_output_value = brc20_output.value; - - // Clone the change output, if provided. - let change_output = if let Some(change) = proto.change_output { - Some(super::utils::hard_clone_proto_output(change)?) - } else { - None - }; - - // Create the full COMMIT transaction with the appropriately selected inputs. - let commit_signing = Proto::SigningInput { - private_key: proto.private_key.to_vec().into(), - inputs: proto - .inputs - .iter() - .cloned() - .map(super::utils::hard_clone_proto_input) - .collect::>()?, - outputs: vec![brc20_output], - input_selector: proto.input_selector, - change_output: change_output.clone(), - fee_per_vb: proto.fee_per_vb, - disable_change_output: proto.disable_change_output, - ..Default::default() - }; - - let mut commit_signing = pre_processor(commit_signing); - - // We now determine the Txid of the COMMIT transaction, which we will have - // to use in the REVEAL transaction. - let presigned = BitcoinEntry.preimage_hashes(_coin, commit_signing.clone()); - if presigned.error != Proto::Error::OK { - return Err(Error::from(presigned.error)); - } - - assert_eq!(presigned.error, Proto::Error::OK); - let commit_txid: Vec = presigned.txid.to_vec().iter().copied().rev().collect(); - - // Create a list of the selected input Txids, as indicated by the - // `InputSelector`. - let selected_txids: Vec<_> = presigned - .utxo_inputs - .iter() - .map(|utxo| utxo.txid.clone()) - .collect(); - - // Create the list of selected inputs and update the COMMIT transaction. - let selected_inputs: Vec<_> = proto - .inputs - .into_iter() - .filter(|input| selected_txids.contains(&input.txid)) - .map(super::utils::hard_clone_proto_input) - .collect::>()?; - - commit_signing.inputs = selected_inputs; - - // Update the change amount to calculated amount. - if !proto.disable_change_output && presigned.utxo_outputs.len() == 2 { - let change_amount = presigned - .utxo_outputs - .last() - .expect("No Utxo outputs generated") - .value; - - let mut change_output = change_output.expect("change output expected"); - change_output.value = change_amount; - - commit_signing - .outputs - .push(hard_clone_proto_output(change_output)?); - } - - commit_signing.input_selector = UtxoProto::InputSelector::UseAll; - commit_signing.disable_change_output = true; - commit_signing.fee_per_vb = 0; - commit_signing.change_output = Default::default(); - - // Now we construct the *actual* REVEAL transaction. - - let brc20_input = Proto::Input { - value: brc20_output_value, - txid: commit_txid.into(), // Reference COMMIT transaction. - sighash_type: UtxoProto::SighashType::UseDefault, - to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { - variant: ProtoInputBuilder::brc20_inscribe(brc20_info.clone()), - }), - ..Default::default() - }; - - // Build the REVEAL transaction. - let reveal_signing = Proto::SigningInput { - private_key: proto.private_key.to_vec().into(), - inputs: vec![brc20_input], - outputs: vec![tagged_output], - input_selector: UtxoProto::InputSelector::UseAll, - change_output: Default::default(), - fee_per_vb: 0, - disable_change_output: true, - ..Default::default() - }; - - let reveal_signing = pre_processor(reveal_signing); - - Ok(Proto::mod_TransactionPlan::Brc20Plan { - commit: Some(commit_signing), - reveal: Some(reveal_signing), - }) - } -} diff --git a/rust/tw_bitcoin/src/modules/signer.rs b/rust/tw_bitcoin/src/modules/signer.rs index cdf17b68eef..76089cad6f7 100644 --- a/rust/tw_bitcoin/src/modules/signer.rs +++ b/rust/tw_bitcoin/src/modules/signer.rs @@ -20,7 +20,58 @@ impl Signer { // `preimage_hashes_impl` and `compile_impl`. But we're leaving this // here in case this methods gets extended and the pre-processing does // not get accidentally forgotten. - let proto = crate::entry::pre_processor(proto); + let mut proto = crate::entry::pre_processor(proto); + + // Generate the sighashes. + let pre_signed = BitcoinEntry.preimage_hashes_impl(_coin, proto.clone())?; + if pre_signed.error != Proto::Error::OK { + return Err(Error::from(pre_signed.error)); + } + + // Sanity check. + debug_assert!(proto.inputs.len() >= pre_signed.utxo_inputs.len()); + debug_assert_eq!(pre_signed.utxo_inputs.len(), pre_signed.sighashes.len()); + + if proto.disable_change_output { + debug_assert_eq!(proto.outputs.len(), pre_signed.utxo_outputs.len()); + } else { + // If a change output was generated... + debug_assert_eq!(proto.outputs.len() + 1, pre_signed.utxo_outputs.len()); // plus change output. + + // Update the given change output with the specified amount and push + // it to the proto structure. + let change_output_amount = pre_signed + .utxo_outputs + .last() + .expect("No change output provided") + .value; + + let mut change_output = proto.change_output.clone().expect("change output expected"); + change_output.value = change_output_amount; + proto.outputs.push(change_output); + } + + // The `pre_signed` result contains a list of selected inputs in order + // to cover the output amount and fees, assuming the input selector was + // used. We therefore need to update the proto structure. + + // Clear the inputs. + let available = std::mem::take(&mut proto.inputs); + + for selected in &pre_signed.utxo_inputs { + // Find the input in the passed on UTXO list. + let index = available + .iter() + .position(|input| input.txid == selected.txid && input.vout == selected.vout) + .expect("Selected input not found in proto structure"); + + // Update the input with the selected input. + proto.inputs.push(available[index].clone()); + } + + // Sanity check. + debug_assert_eq!(proto.outputs.len(), pre_signed.utxo_outputs.len()); + debug_assert_eq!(proto.inputs.len(), pre_signed.utxo_inputs.len()); // Collect individual private keys per input, if there are any. let mut individual_keys = HashMap::new(); @@ -30,14 +81,6 @@ impl Signer { } } - // Generate the sighashes. - let pre_signed = BitcoinEntry.preimage_hashes_impl(_coin, proto.clone())?; - - // Check for error. - if pre_signed.error != Proto::Error::OK { - return Err(Error::from(pre_signed.error)); - } - // Sign the sighashes. let signatures = crate::modules::signer::Signer::signatures_from_proto( &pre_signed, @@ -46,8 +89,41 @@ impl Signer { proto.dangerous_use_fixed_schnorr_rng, )?; + // Sanity check. + debug_assert_eq!(signatures.len(), proto.inputs.len()); + debug_assert_eq!(signatures.len(), pre_signed.sighashes.len()); + + // Prepare values for sanity check. + let total_input_amount = proto.inputs.iter().map(|input| input.value).sum::(); + let total_output_amount = proto.outputs.iter().map(|output| output.value).sum::(); + // Construct the final transaction. - BitcoinEntry.compile_impl(_coin, proto, signatures, vec![]) + let mut compiled = BitcoinEntry.compile_impl(_coin, proto, signatures, vec![])?; + + // Note: the fee that we used for estimation might be SLIGHLY off + // from the final fee. This is due to the fact that we must set a + // change output (which must consider the fee) before we can calculate + // the final fee. This leads to a chicken-and-egg problem. However, + // the fee difference, should there be one, is generally as small as + // one weight unit. Hence, we overwrite the final fee with the + // estimated fee. + compiled.weight = pre_signed.weight_estimate; + + // Sanity check. + let compiled_total_output_amount = compiled + .transaction + .as_ref() + .expect("No transaction was constructed") + .outputs + .iter() + .map(|output| output.value) + .sum::(); + + // Every output is accounted for, including the fee. + debug_assert_eq!(total_output_amount, compiled_total_output_amount); + debug_assert_eq!(total_input_amount, total_output_amount + compiled.fee); + + Ok(compiled) } pub fn signatures_from_proto( input: &Proto::PreSigningOutput<'_>, diff --git a/rust/tw_bitcoin/src/modules/transactions/brc20.rs b/rust/tw_bitcoin/src/modules/transactions/brc20.rs index 51afda68777..e67b81f24bb 100644 --- a/rust/tw_bitcoin/src/modules/transactions/brc20.rs +++ b/rust/tw_bitcoin/src/modules/transactions/brc20.rs @@ -38,12 +38,12 @@ impl BRC20TransferPayload { impl BRC20TransferPayload { const OPERATION: &str = "transfer"; - fn new(ticker: Brc20Ticker, value: u64) -> Self { + fn new(ticker: Brc20Ticker, amount: String) -> Self { BRC20TransferPayload { protocol: Self::PROTOCOL_ID.to_string(), operation: Self::OPERATION.to_string(), ticker, - amount: value.to_string(), + amount, } } } @@ -54,7 +54,7 @@ impl BRC20TransferInscription { pub fn new( recipient: PublicKey, ticker: Brc20Ticker, - value: u64, + value: String, ) -> Result { let data: BRC20TransferPayload = BRC20TransferPayload::new(ticker, value); diff --git a/rust/tw_bitcoin/src/modules/transactions/input_builder.rs b/rust/tw_bitcoin/src/modules/transactions/input_builder.rs index 4d1660cb61f..b9402b11680 100644 --- a/rust/tw_bitcoin/src/modules/transactions/input_builder.rs +++ b/rust/tw_bitcoin/src/modules/transactions/input_builder.rs @@ -213,9 +213,12 @@ impl InputBuilder { let pubkey = bitcoin::PublicKey::from_slice(brc20.inscribe_to.as_ref())?; let ticker = Brc20Ticker::new(brc20.ticker.to_string())?; - let transfer = - BRC20TransferInscription::new(pubkey, ticker, brc20.transfer_amount) - .expect("invalid BRC20 transfer construction"); + let transfer = BRC20TransferInscription::new( + pubkey, + ticker, + brc20.transfer_amount.to_string(), + ) + .expect("invalid BRC20 transfer construction"); // We construct a control block to estimate the fee, // otherwise we do not need it here. diff --git a/rust/tw_bitcoin/src/modules/transactions/input_claim_builder.rs b/rust/tw_bitcoin/src/modules/transactions/input_claim_builder.rs index b93269031fc..d2b3fc4400f 100644 --- a/rust/tw_bitcoin/src/modules/transactions/input_claim_builder.rs +++ b/rust/tw_bitcoin/src/modules/transactions/input_claim_builder.rs @@ -116,9 +116,12 @@ impl InputClaimBuilder { let ticker = Brc20Ticker::new(brc20.ticker.to_string())?; // Construct the BRC20 transfer inscription. - let transfer = - BRC20TransferInscription::new(pubkey, ticker, brc20.transfer_amount) - .expect("invalid BRC20 transfer construction"); + let transfer = BRC20TransferInscription::new( + pubkey, + ticker, + brc20.transfer_amount.to_string(), + ) + .expect("invalid BRC20 transfer construction"); // Create a control block for that inscription. let control_block = transfer @@ -158,6 +161,7 @@ impl InputClaimBuilder { let claim = UtxoProto::TxInClaim { txid: input.txid.to_vec().into(), vout: input.vout, + value: input.value, sequence: input.sequence, script_sig: script_sig.to_vec().into(), witness_items: witness diff --git a/rust/tw_bitcoin/src/modules/transactions/output_builder.rs b/rust/tw_bitcoin/src/modules/transactions/output_builder.rs index 72fef4c0c39..83e3ea13bef 100644 --- a/rust/tw_bitcoin/src/modules/transactions/output_builder.rs +++ b/rust/tw_bitcoin/src/modules/transactions/output_builder.rs @@ -140,9 +140,12 @@ impl OutputBuilder { let xonly = XOnlyPublicKey::from(pubkey.inner); let ticker = Brc20Ticker::new(brc20.ticker.to_string())?; - let transfer = - BRC20TransferInscription::new(pubkey, ticker, brc20.transfer_amount) - .expect("invalid BRC20 transfer construction"); + let transfer = BRC20TransferInscription::new( + pubkey, + ticker, + brc20.transfer_amount.to_string(), + ) + .expect("invalid BRC20 transfer construction"); // Construct the control block. let control_block = transfer diff --git a/rust/tw_bitcoin/src/modules/utils.rs b/rust/tw_bitcoin/src/modules/utils.rs deleted file mode 100644 index f64f9bfb29e..00000000000 --- a/rust/tw_bitcoin/src/modules/utils.rs +++ /dev/null @@ -1,188 +0,0 @@ -use crate::aliases::*; -use crate::{Error, Result}; -use tw_proto::BitcoinV2::Proto; - -// Convenience function: our protobuf library wraps certain types (such as -// `bytes`) in `Cow`, but given that calling `clone()` on a `Cow::Borrowed(T)` -// does not actually clone the underlying data (but the smart pointer instead), -// we must hard-clone individual fields manually. This is unfortunately required -// due to how protobuf library works and our use of the 'static constraints. -pub fn hard_clone_proto_input(proto: Proto::Input<'_>) -> Result> { - fn new_builder(variant: ProtoInputBuilder<'static>) -> ProtoInputRecipient<'static> { - ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { variant }) - } - - let to_recipient = match proto.to_recipient { - ProtoInputRecipient::builder(builder) => match builder.variant { - ProtoInputBuilder::p2sh(script) => { - new_builder(ProtoInputBuilder::p2sh(script.to_vec().into())) - }, - ProtoInputBuilder::p2pkh(script) => { - new_builder(ProtoInputBuilder::p2pkh(script.to_vec().into())) - }, - ProtoInputBuilder::p2wsh(script) => { - new_builder(ProtoInputBuilder::p2wsh(script.to_vec().into())) - }, - ProtoInputBuilder::p2wpkh(script) => { - new_builder(ProtoInputBuilder::p2wpkh(script.to_vec().into())) - }, - ProtoInputBuilder::p2tr_key_path(key_path) => new_builder( - ProtoInputBuilder::p2tr_key_path(Proto::mod_Input::InputTaprootKeyPath { - one_prevout: key_path.one_prevout, - public_key: key_path.public_key.to_vec().into(), - }), - ), - ProtoInputBuilder::p2tr_script_path(script) => new_builder( - ProtoInputBuilder::p2tr_script_path(Proto::mod_Input::InputTaprootScriptPath { - one_prevout: script.one_prevout, - payload: script.payload.to_vec().into(), - control_block: script.control_block.to_vec().into(), - }), - ), - ProtoInputBuilder::brc20_inscribe(brc20) => new_builder( - ProtoInputBuilder::brc20_inscribe(Proto::mod_Input::InputBrc20Inscription { - one_prevout: brc20.one_prevout, - inscribe_to: brc20.inscribe_to.to_vec().into(), - ticker: brc20.ticker.to_string().into(), - transfer_amount: brc20.transfer_amount, - }), - ), - ProtoInputBuilder::ordinal_inscribe(ord) => new_builder( - ProtoInputBuilder::ordinal_inscribe(Proto::mod_Input::InputOrdinalInscription { - one_prevout: ord.one_prevout, - inscribe_to: ord.inscribe_to.to_vec().into(), - mime_type: ord.mime_type.to_string().into(), - payload: ord.payload.to_vec().into(), - }), - ), - ProtoInputBuilder::None => { - return Err(Error::from(Proto::Error::Error_missing_input_builder)) - }, - }, - ProtoInputRecipient::custom_script(custom) => { - ProtoInputRecipient::custom_script(Proto::mod_Input::InputScriptWitness { - script_pubkey: custom.script_pubkey.to_vec().into(), - script_sig: custom.script_sig.to_vec().into(), - witness_items: custom - .witness_items - .iter() - .map(|item| item.to_vec().into()) - .collect(), - signing_method: custom.signing_method, - }) - }, - ProtoInputRecipient::None => { - return Err(Error::from(Proto::Error::Error_missing_recipient)) - }, - }; - - Ok(Proto::Input { - private_key: proto.private_key.to_vec().into(), - txid: proto.txid.to_vec().into(), - to_recipient, - ..proto - }) -} - -// Convenience function: our protobuf library wraps certain types (such as -// `bytes`) in `Cow`, but given that calling `clone()` on a `Cow::Borrowed(T)` -// does not actually clone the underlying data (but the smart pointer instead), -// we must hard-clone individual fields manually. This is unfortunately required -// due to how protobuf library works and our use of the 'static constraints. -pub fn hard_clone_proto_output(proto: Proto::Output<'_>) -> Result> { - fn new_builder(variant: ProtoOutputBuilder<'static>) -> ProtoOutputRecipient<'static> { - ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { variant }) - } - - fn new_script_or_hash( - proto: Proto::mod_Output::OutputRedeemScriptOrHash<'_>, - ) -> Result> { - let variant = match proto.variant { - ProtoOutputRedeemScriptOrHashBuilder::redeem_script(script) => { - ProtoOutputRedeemScriptOrHashBuilder::redeem_script(script.to_vec().into()) - }, - ProtoOutputRedeemScriptOrHashBuilder::hash(hash) => { - ProtoOutputRedeemScriptOrHashBuilder::hash(hash.to_vec().into()) - }, - ProtoOutputRedeemScriptOrHashBuilder::None => { - return Err(Error::from(Proto::Error::Error_missing_recipient)) - }, - }; - - Ok(Proto::mod_Output::OutputRedeemScriptOrHash { variant }) - } - - fn new_pubkey_or_hash( - proto: Proto::ToPublicKeyOrHash<'_>, - ) -> Result> { - let to_address = match proto.to_address { - ProtoPubkeyOrHash::pubkey(pubkey) => ProtoPubkeyOrHash::pubkey(pubkey.to_vec().into()), - ProtoPubkeyOrHash::hash(hash) => ProtoPubkeyOrHash::hash(hash.to_vec().into()), - ProtoPubkeyOrHash::None => { - return Err(Error::from(Proto::Error::Error_missing_recipient)) - }, - }; - - Ok(Proto::ToPublicKeyOrHash { to_address }) - } - - let to_recipient = match proto.to_recipient { - ProtoOutputRecipient::builder(builder) => match builder.variant { - ProtoOutputBuilder::p2sh(script_or_hash) => new_builder(ProtoOutputBuilder::p2sh( - new_script_or_hash(script_or_hash)?, - )), - ProtoOutputBuilder::p2pkh(pubkey_or_hash) => new_builder(ProtoOutputBuilder::p2pkh( - new_pubkey_or_hash(pubkey_or_hash)?, - )), - ProtoOutputBuilder::p2wsh(script_or_hash) => new_builder(ProtoOutputBuilder::p2wsh( - new_script_or_hash(script_or_hash)?, - )), - ProtoOutputBuilder::p2wpkh(pubkey_or_hash) => new_builder(ProtoOutputBuilder::p2wpkh( - new_pubkey_or_hash(pubkey_or_hash)?, - )), - ProtoOutputBuilder::p2tr_key_path(pubkey) => { - new_builder(ProtoOutputBuilder::p2tr_key_path(pubkey.to_vec().into())) - }, - ProtoOutputBuilder::p2tr_script_path(script_path) => new_builder( - ProtoOutputBuilder::p2tr_script_path(Proto::mod_Output::OutputTaprootScriptPath { - internal_key: script_path.internal_key.to_vec().into(), - merkle_root: script_path.merkle_root.to_vec().into(), - }), - ), - ProtoOutputBuilder::p2tr_dangerous_assume_tweaked(tweaked) => new_builder( - ProtoOutputBuilder::p2tr_dangerous_assume_tweaked(tweaked.to_vec().into()), - ), - ProtoOutputBuilder::brc20_inscribe(brc20) => new_builder( - ProtoOutputBuilder::brc20_inscribe(Proto::mod_Output::OutputBrc20Inscription { - inscribe_to: brc20.inscribe_to.to_vec().into(), - ticker: brc20.ticker.to_string().into(), - transfer_amount: brc20.transfer_amount, - }), - ), - ProtoOutputBuilder::ordinal_inscribe(ord) => new_builder( - ProtoOutputBuilder::ordinal_inscribe(Proto::mod_Output::OutputOrdinalInscription { - inscribe_to: ord.inscribe_to.to_vec().into(), - mime_type: ord.mime_type.to_string().into(), - payload: ord.payload.to_vec().into(), - }), - ), - ProtoOutputBuilder::None => { - return Err(Error::from(Proto::Error::Error_missing_output_builder)) - }, - }, - ProtoOutputRecipient::custom_script_pubkey(custom) => { - ProtoOutputRecipient::custom_script_pubkey(custom.to_vec().into()) - }, - ProtoOutputRecipient::from_address(address) => { - ProtoOutputRecipient::from_address(address.to_string().into()) - }, - ProtoOutputRecipient::None => { - return Err(Error::from(Proto::Error::Error_missing_output_builder)) - }, - }; - - Ok(Proto::Output { - value: proto.value, - to_recipient, - }) -} diff --git a/rust/tw_bitcoin/tests/brc20.rs b/rust/tw_bitcoin/tests/brc20.rs index 67c5a2678b8..46458321c7b 100644 --- a/rust/tw_bitcoin/tests/brc20.rs +++ b/rust/tw_bitcoin/tests/brc20.rs @@ -38,7 +38,7 @@ fn coin_entry_sign_brc20_commit_reveal_transfer() { Proto::mod_Output::OutputBrc20Inscription { inscribe_to: alice_pubkey.as_slice().into(), ticker: "oadf".into(), - transfer_amount: 20, + transfer_amount: "20".into(), }, ), }), @@ -93,7 +93,7 @@ fn coin_entry_sign_brc20_commit_reveal_transfer() { one_prevout: false, inscribe_to: alice_pubkey.as_slice().into(), ticker: "oadf".into(), - transfer_amount: 20, + transfer_amount: "20".into(), }), }), ..Default::default() diff --git a/rust/tw_bitcoin/tests/free_estimate.rs b/rust/tw_bitcoin/tests/free_estimate.rs deleted file mode 100644 index bc64fbfa429..00000000000 --- a/rust/tw_bitcoin/tests/free_estimate.rs +++ /dev/null @@ -1,246 +0,0 @@ -mod common; - -use common::{hex, ONE_BTC}; -use tw_bitcoin::aliases::*; -use tw_bitcoin::entry::BitcoinEntry; -use tw_coin_entry::coin_entry::CoinEntry; -use tw_coin_entry::test_utils::test_context::TestCoinContext; -use tw_proto::BitcoinV2::Proto; -use tw_proto::Utxo::Proto as UtxoProto; - -const SAT_VB: u64 = 20; - -#[test] -fn p2pkh_fee_estimate() { - let coin = TestCoinContext::default(); - - let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); - let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); - - let bob_pubkey = hex("025a0af1510f0f24d40dd00d7c0e51605ca504bbc177c3e19b065f373a1efdd22f"); - let txid: Vec = hex("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911") - .into_iter() - .rev() - .collect(); - - let mut signing = Proto::SigningInput { - private_key: alice_private_key.as_slice().into(), - inputs: vec![], - outputs: vec![], - input_selector: UtxoProto::InputSelector::UseAll, - fee_per_vb: SAT_VB, - disable_change_output: true, - ..Default::default() - }; - - signing.inputs.push(Proto::Input { - txid: txid.as_slice().into(), - vout: 0, - value: 2 * ONE_BTC, - sighash_type: UtxoProto::SighashType::All, - to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { - variant: ProtoInputBuilder::p2pkh(alice_pubkey.as_slice().into()), - }), - ..Default::default() - }); - - signing.outputs.push(Proto::Output { - value: ONE_BTC, - to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { - variant: ProtoOutputBuilder::p2pkh(Proto::ToPublicKeyOrHash { - to_address: ProtoPubkeyOrHash::pubkey(bob_pubkey.as_slice().into()), - }), - }), - }); - - let prehashes = BitcoinEntry.preimage_hashes(&coin, signing.clone()); - assert_eq!(prehashes.error, Proto::Error::OK); - assert_eq!(prehashes.weight_estimate, 768); - assert_eq!(prehashes.fee_estimate, (768 + 3) / 4 * SAT_VB); - - let signed = BitcoinEntry.sign(&coin, signing); - assert_eq!(signed.error, Proto::Error::OK); - assert_eq!(signed.weight, 768); - assert_eq!(signed.fee, (768 + 3) / 4 * SAT_VB); -} - -#[test] -fn p2wpkh_fee_estimate() { - let coin = TestCoinContext::default(); - - let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); - let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); - - let bob_pubkey = hex("025a0af1510f0f24d40dd00d7c0e51605ca504bbc177c3e19b065f373a1efdd22f"); - let txid: Vec = hex("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911") - .into_iter() - .rev() - .collect(); - - let mut signing = Proto::SigningInput { - private_key: alice_private_key.as_slice().into(), - inputs: vec![], - outputs: vec![], - input_selector: UtxoProto::InputSelector::UseAll, - fee_per_vb: SAT_VB, - disable_change_output: true, - ..Default::default() - }; - - signing.inputs.push(Proto::Input { - txid: txid.as_slice().into(), - vout: 0, - value: 2 * ONE_BTC, - sequence: u32::MAX, - sighash_type: UtxoProto::SighashType::All, - to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { - variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), - }), - ..Default::default() - }); - - signing.outputs.push(Proto::Output { - value: ONE_BTC, - to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { - variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { - to_address: ProtoPubkeyOrHash::pubkey(bob_pubkey.as_slice().into()), - }), - }), - }); - - let prehashes = BitcoinEntry.preimage_hashes(&coin, signing.clone()); - assert_eq!(prehashes.error, Proto::Error::OK); - // TODO: The estimated weight/fee is slightly off from the finalized - // weight/fee. This is probably good enough, but we can probably improve - // this. - assert_eq!(prehashes.weight_estimate, 436); - assert_eq!(prehashes.fee_estimate, (436 + 3) / 4 * SAT_VB); - - let signed = BitcoinEntry.sign(&coin, signing); - assert_eq!(signed.error, Proto::Error::OK); - assert_eq!(signed.weight, 438); - assert_eq!(signed.fee, (438 + 3) / 4 * SAT_VB); -} - -#[test] -fn p2tr_key_path_fee_estimate() { - let coin = TestCoinContext::default(); - - let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); - let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); - - let bob_pubkey = hex("025a0af1510f0f24d40dd00d7c0e51605ca504bbc177c3e19b065f373a1efdd22f"); - let txid: Vec = hex("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911") - .into_iter() - .rev() - .collect(); - - let tx1 = Proto::Input { - txid: txid.as_slice().into(), - vout: 0, - value: 2 * ONE_BTC, - sighash_type: UtxoProto::SighashType::All, - to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { - variant: ProtoInputBuilder::p2tr_key_path(Proto::mod_Input::InputTaprootKeyPath { - one_prevout: false, - public_key: alice_pubkey.as_slice().into(), - }), - }), - ..Default::default() - }; - - let out1 = Proto::Output { - value: ONE_BTC, - to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { - variant: ProtoOutputBuilder::p2tr_key_path(bob_pubkey.as_slice().into()), - }), - }; - - let signing = Proto::SigningInput { - private_key: alice_private_key.as_slice().into(), - inputs: vec![tx1], - outputs: vec![out1], - input_selector: UtxoProto::InputSelector::UseAll, - fee_per_vb: SAT_VB, - disable_change_output: true, - ..Default::default() - }; - - let prehashes = BitcoinEntry.preimage_hashes(&coin, signing.clone()); - assert_eq!(prehashes.error, Proto::Error::OK); - // TODO: The estimated weight/fee is slightly off from the finalized - // weight/fee. This is probably good enough, but we can probably improve - // this. - assert_eq!(prehashes.weight_estimate, 450); - assert_eq!(prehashes.fee_estimate, (450 + 3) / 4 * SAT_VB); - - let signed = BitcoinEntry.sign(&coin, signing); - assert_eq!(signed.error, Proto::Error::OK); - assert_eq!(signed.weight, 445); - assert_eq!(signed.fee, (445 + 3) / 4 * SAT_VB); -} - -#[test] -fn brc20_inscribe_fee_estimate() { - let coin = TestCoinContext::default(); - - let alice_private_key = hex("57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"); - let alice_pubkey = hex("028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"); - - let txid: Vec = hex("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911") - .into_iter() - .rev() - .collect(); - - let tx1 = Proto::Input { - txid: txid.as_slice().into(), - vout: 0, - value: 2 * ONE_BTC, - sighash_type: UtxoProto::SighashType::All, - to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { - variant: ProtoInputBuilder::brc20_inscribe(Proto::mod_Input::InputBrc20Inscription { - one_prevout: false, - inscribe_to: alice_pubkey.as_slice().into(), - ticker: "oadf".into(), - transfer_amount: 20, - }), - }), - ..Default::default() - }; - - let out1 = Proto::Output { - value: ONE_BTC, - to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { - variant: ProtoOutputBuilder::brc20_inscribe( - Proto::mod_Output::OutputBrc20Inscription { - inscribe_to: alice_pubkey.as_slice().into(), - ticker: "oadf".into(), - transfer_amount: 20, - }, - ), - }), - }; - - let signing = Proto::SigningInput { - private_key: alice_private_key.as_slice().into(), - inputs: vec![tx1], - outputs: vec![out1], - input_selector: UtxoProto::InputSelector::UseAll, - fee_per_vb: SAT_VB, - disable_change_output: true, - ..Default::default() - }; - - let prehashes = BitcoinEntry.preimage_hashes(&coin, signing.clone()); - assert_eq!(prehashes.error, Proto::Error::OK); - // TODO: The estimated weight/fee is slightly off from the finalized - // weight/fee. This is probably good enough, but we can probably improve - // this. - assert_eq!(prehashes.weight_estimate, 575); - assert_eq!(prehashes.fee_estimate, (575 + 3) / 4 * SAT_VB); - - let signed = BitcoinEntry.sign(&coin, signing); - assert_eq!(signed.error, Proto::Error::OK); - assert_eq!(signed.weight, 571); - assert_eq!(signed.fee, (571 + 3) / 4 * SAT_VB); -} diff --git a/rust/tw_bitcoin/tests/input_selection.rs b/rust/tw_bitcoin/tests/input_selection.rs new file mode 100644 index 00000000000..38afea369e5 --- /dev/null +++ b/rust/tw_bitcoin/tests/input_selection.rs @@ -0,0 +1,747 @@ +mod common; +use common::{hex, ONE_BTC}; + +use tw_bitcoin::aliases::*; +use tw_bitcoin::entry::BitcoinEntry; +use tw_coin_entry::coin_entry::CoinEntry; +use tw_coin_entry::test_utils::test_context::TestCoinContext; +use tw_proto::BitcoinV2::Proto; +use tw_proto::Utxo::Proto as UtxoProto; + +const SAT_VBYTE: u64 = 50; + +const ALICE_PRIVATE_KEY: &str = "57a64865bce5d4855e99b1cce13327c46171434f2d72eeaf9da53ee075e7f90a"; +const ALICE_PUBKEY: &str = "028d7dce6d72fb8f7af9566616c6436349c67ad379f2404dd66fe7085fe0fba28f"; +const BOB_PUBKEY: &str = "025a0af1510f0f24d40dd00d7c0e51605ca504bbc177c3e19b065f373a1efdd22f"; + +#[test] +fn input_selection_no_change_output() { + let coin = TestCoinContext::default(); + + let alice_private_key = hex(ALICE_PRIVATE_KEY); + let alice_pubkey = hex(ALICE_PUBKEY); + let bob_pubkey = hex(BOB_PUBKEY); + + let txid: Vec = vec![1; 32]; + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: 50_000_000, // 0.5 BTC + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(bob_pubkey.as_slice().into()), + }), + }), + }; + + // TODO: Mandate that fee_per_byte is non-zero? + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + inputs: vec![tx1], + outputs: vec![out1], + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + + // By default, we mandate that a change output is set. + assert_eq!(signed.error, Proto::Error::Error_invalid_change_output); + assert_eq!(signed.error_message, "Error_invalid_change_output"); + assert_eq!(signed.transaction, None); + assert!(signed.encoded.is_empty()); + assert!(signed.txid.is_empty()); + assert_eq!(signed.weight, 0); + assert_eq!(signed.fee, 0); +} + +#[test] +fn input_selection_no_utxo_inputs() { + let coin = TestCoinContext::default(); + + let alice_private_key = hex(ALICE_PRIVATE_KEY); + let alice_pubkey = hex(ALICE_PUBKEY); + let bob_pubkey = hex(BOB_PUBKEY); + + let out1 = Proto::Output { + value: 50_000_000, // 0.5 BTC + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(bob_pubkey.as_slice().into()), + }), + }), + }; + + let change_output = Proto::Output { + // Will be set for us. + value: 0, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(alice_pubkey.as_slice().into()), + }), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + // No inputs. + inputs: vec![], + outputs: vec![out1], + // We set the change output accordingly. + change_output: Some(change_output), + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + + // Error + assert_eq!(signed.error, Proto::Error::Error_utxo_insufficient_inputs); + assert_eq!(signed.error_message, "Error_utxo_insufficient_inputs"); + assert_eq!(signed.transaction, None); + assert!(signed.encoded.is_empty()); + assert!(signed.txid.is_empty()); + assert_eq!(signed.weight, 0); + assert_eq!(signed.fee, 0); +} + +#[test] +fn input_selection_no_utxo_outputs() { + let coin = TestCoinContext::default(); + + let alice_private_key = hex(ALICE_PRIVATE_KEY); + let alice_pubkey = hex(ALICE_PUBKEY); + + let txid: Vec = vec![1; 32]; + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + // No inputs. + inputs: vec![tx1], + outputs: vec![], + disable_change_output: true, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + + // Error + assert_eq!(signed.error, Proto::Error::Error_utxo_no_outputs_specified); + assert_eq!(signed.error_message, "Error_utxo_no_outputs_specified"); + assert_eq!(signed.transaction, None); + assert!(signed.encoded.is_empty()); + assert!(signed.txid.is_empty()); + assert_eq!(signed.weight, 0); + assert_eq!(signed.fee, 0); +} + +#[test] +fn input_selection_insufficient_inputs() { + let coin = TestCoinContext::default(); + + let alice_private_key = hex(ALICE_PRIVATE_KEY); + let alice_pubkey = hex(ALICE_PUBKEY); + let bob_pubkey = hex(BOB_PUBKEY); + + let txid: Vec = vec![1; 32]; + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: ONE_BTC * 2, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(bob_pubkey.as_slice().into()), + }), + }), + }; + + let change_output = Proto::Output { + // Will be set for us. + value: 0, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(alice_pubkey.as_slice().into()), + }), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + // No inputs. + inputs: vec![tx1], + outputs: vec![out1], + // We set the change output accordingly. + change_output: Some(change_output), + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + + // Input does not cover output. + assert_eq!(signed.error, Proto::Error::Error_utxo_insufficient_inputs); + assert_eq!(signed.error_message, "Error_utxo_insufficient_inputs"); + assert_eq!(signed.transaction, None); + assert!(signed.encoded.is_empty()); + assert!(signed.txid.is_empty()); + assert_eq!(signed.weight, 0); + assert_eq!(signed.fee, 0); +} + +#[test] +fn input_selection_no_utxo_outputs_with_change_output() { + let coin = TestCoinContext::default(); + + let alice_private_key = hex(ALICE_PRIVATE_KEY); + let alice_pubkey = hex(ALICE_PUBKEY); + + let txid: Vec = vec![1; 32]; + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let change_output = Proto::Output { + // Will be set for us. + value: 0, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(alice_pubkey.as_slice().into()), + }), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + // No inputs. + inputs: vec![tx1], + outputs: vec![], + // We set the change output accordingly. + change_output: Some(change_output), + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + + // Even though there is a change output, we mandate "normal" outputs. + assert_eq!(signed.error, Proto::Error::Error_utxo_no_outputs_specified); + assert_eq!(signed.error_message, "Error_utxo_no_outputs_specified"); + assert_eq!(signed.transaction, None); + assert!(signed.encoded.is_empty()); + assert!(signed.txid.is_empty()); + assert_eq!(signed.weight, 0); + assert_eq!(signed.fee, 0); +} + +#[test] +fn input_selection_select_in_order() { + let coin = TestCoinContext::default(); + + let alice_private_key = hex(ALICE_PRIVATE_KEY); + let alice_pubkey = hex(ALICE_PUBKEY); + let bob_pubkey = hex(BOB_PUBKEY); + + let txid: Vec = vec![1; 32]; + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC * 3, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let txid: Vec = vec![2; 32]; + let tx2 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC * 2, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let txid: Vec = vec![3; 32]; + let tx3 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: ONE_BTC / 2, // 0.5 BTC + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(bob_pubkey.as_slice().into()), + }), + }), + }; + + let change_output = Proto::Output { + // Will be set for us. + value: 0, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(alice_pubkey.as_slice().into()), + }), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + input_selector: UtxoProto::InputSelector::SelectInOrder, + inputs: vec![tx1.clone(), tx2, tx3], + outputs: vec![out1.clone()], + // We set the change output accordingly. + change_output: Some(change_output), + fee_per_vb: SAT_VBYTE, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + + assert_eq!(signed.error, Proto::Error::OK); + assert!(signed.error_message.is_empty()); + assert_eq!(signed.weight, 560); + assert_eq!(signed.fee, (signed.weight + 3) / 4 * SAT_VBYTE); + assert_eq!(signed.fee, 7_000); + + let tx = signed.transaction.unwrap(); + assert_eq!(tx.version, 2); + + // Inputs, only one input was selected (ONE_BTC * 3). + assert_eq!(tx.inputs.len(), 1); + assert_eq!(tx.inputs[0].txid, tx1.txid); + assert_eq!(tx.inputs[0].txid, vec![1; 32]); + assert_eq!(tx.inputs[0].vout, 0); + assert_eq!(tx.inputs[0].sequence, u32::MAX); + assert!(tx.inputs[0].script_sig.is_empty()); + assert!(!tx.inputs[0].witness_items.is_empty()); + + // Outputs. + assert_eq!(tx.outputs.len(), 2); + + // Output for recipient. + assert!(!tx.outputs[0].script_pubkey.is_empty()); + assert_eq!(tx.outputs[0].value, out1.value); + assert_eq!(tx.outputs[0].value, 50_000_000); + assert!(tx.outputs[0].taproot_payload.is_empty()); + assert!(tx.outputs[0].control_block.is_empty()); + + // Change output. + assert!(!tx.outputs[1].script_pubkey.is_empty()); + assert_eq!(tx.outputs[1].value, tx1.value - out1.value - signed.fee); + assert_eq!(tx.outputs[1].value, ONE_BTC * 3 - 50_000_000 - 7_000); + assert!(tx.outputs[1].taproot_payload.is_empty()); + assert!(tx.outputs[1].control_block.is_empty()); +} + +#[test] +fn input_selection_select_ascending() { + let coin = TestCoinContext::default(); + + let alice_private_key = hex(ALICE_PRIVATE_KEY); + let alice_pubkey = hex(ALICE_PUBKEY); + let bob_pubkey = hex(BOB_PUBKEY); + + let txid: Vec = vec![1; 32]; + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC * 3, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let txid: Vec = vec![2; 32]; + let tx2 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC * 2, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let txid: Vec = vec![3; 32]; + let tx3 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: ONE_BTC + ONE_BTC / 2, // 1.5 BTC + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(bob_pubkey.as_slice().into()), + }), + }), + }; + + let change_output = Proto::Output { + // Will be set for us. + value: 0, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(alice_pubkey.as_slice().into()), + }), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + //input_selector: UtxoProto::InputSelector::SelectAscending, // default + inputs: vec![tx1, tx2.clone(), tx3.clone()], + outputs: vec![out1.clone()], + // We set the change output accordingly. + change_output: Some(change_output), + fee_per_vb: SAT_VBYTE, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + + assert_eq!(signed.error, Proto::Error::OK); + assert!(signed.error_message.is_empty()); + assert_eq!(signed.weight, 832); + assert_eq!(signed.fee, (signed.weight + 3) / 4 * SAT_VBYTE); + assert_eq!(signed.fee, 10_400); + + let tx = signed.transaction.unwrap(); + assert_eq!(tx.version, 2); + + // Inputs (ascending; third < second < ...). + assert_eq!(tx.inputs.len(), 2); + + assert_eq!(tx.inputs[0].txid, tx3.txid); + assert_eq!(tx.inputs[0].txid, vec![3; 32]); + assert_eq!(tx.inputs[0].vout, 0); + assert_eq!(tx.inputs[0].sequence, u32::MAX); + assert!(tx.inputs[0].script_sig.is_empty()); + assert!(!tx.inputs[0].witness_items.is_empty()); + + assert_eq!(tx.inputs[1].txid, tx2.txid); + assert_eq!(tx.inputs[1].txid, vec![2; 32]); + assert_eq!(tx.inputs[1].vout, 0); + assert_eq!(tx.inputs[1].sequence, u32::MAX); + assert!(tx.inputs[1].script_sig.is_empty()); + assert!(!tx.inputs[1].witness_items.is_empty()); + + // Outputs. + assert_eq!(tx.outputs.len(), 2); + + // Output for recipient. + assert!(!tx.outputs[0].script_pubkey.is_empty()); + assert_eq!(tx.outputs[0].value, out1.value); + assert_eq!(tx.outputs[0].value, ONE_BTC + ONE_BTC / 2); // 1.5 BTC + assert!(tx.outputs[0].taproot_payload.is_empty()); + assert!(tx.outputs[0].control_block.is_empty()); + + // Change output. + assert!(!tx.outputs[1].script_pubkey.is_empty()); + assert_eq!( + tx.outputs[1].value, + tx3.value + tx2.value - out1.value - signed.fee + ); + assert_eq!( + tx.outputs[1].value, + (ONE_BTC + ONE_BTC * 2) - (ONE_BTC + ONE_BTC / 2) - 10_400 + ); + assert!(tx.outputs[1].taproot_payload.is_empty()); + assert!(tx.outputs[1].control_block.is_empty()); +} + +#[test] +fn input_selection_use_all() { + let coin = TestCoinContext::default(); + + let alice_private_key = hex(ALICE_PRIVATE_KEY); + let alice_pubkey = hex(ALICE_PUBKEY); + let bob_pubkey = hex(BOB_PUBKEY); + + let txid: Vec = vec![1; 32]; + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC * 3, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let txid: Vec = vec![2; 32]; + let tx2 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC * 2, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let txid: Vec = vec![3; 32]; + let tx3 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: ONE_BTC, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(bob_pubkey.as_slice().into()), + }), + }), + }; + + let change_output = Proto::Output { + // Will be set for us. + value: 0, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(alice_pubkey.as_slice().into()), + }), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + input_selector: UtxoProto::InputSelector::UseAll, + inputs: vec![tx1.clone(), tx2.clone(), tx3.clone()], + outputs: vec![out1.clone()], + // We set the change output accordingly. + change_output: Some(change_output), + fee_per_vb: SAT_VBYTE, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + + assert_eq!(signed.error, Proto::Error::OK); + assert!(signed.error_message.is_empty()); + assert_eq!(signed.weight, 1_104); + assert_eq!(signed.fee, (signed.weight + 3) / 4 * SAT_VBYTE); + assert_eq!(signed.fee, 13_800); + + let tx = signed.transaction.unwrap(); + assert_eq!(tx.version, 2); + + // All inputs used + assert_eq!(tx.inputs.len(), 3); + + assert_eq!(tx.inputs[0].txid, tx1.txid); + assert_eq!(tx.inputs[0].txid, vec![1; 32]); + assert_eq!(tx.inputs[0].vout, 0); + assert_eq!(tx.inputs[0].sequence, u32::MAX); + assert!(tx.inputs[0].script_sig.is_empty()); + assert!(!tx.inputs[0].witness_items.is_empty()); + + assert_eq!(tx.inputs[1].txid, tx2.txid); + assert_eq!(tx.inputs[1].txid, vec![2; 32]); + assert_eq!(tx.inputs[1].vout, 0); + assert_eq!(tx.inputs[1].sequence, u32::MAX); + assert!(tx.inputs[1].script_sig.is_empty()); + assert!(!tx.inputs[1].witness_items.is_empty()); + + assert_eq!(tx.inputs[2].txid, tx3.txid); + assert_eq!(tx.inputs[2].txid, vec![3; 32]); + assert_eq!(tx.inputs[2].vout, 0); + assert_eq!(tx.inputs[2].sequence, u32::MAX); + assert!(tx.inputs[2].script_sig.is_empty()); + assert!(!tx.inputs[2].witness_items.is_empty()); + + // Outputs. + assert_eq!(tx.outputs.len(), 2); + + // Output for recipient. + assert!(!tx.outputs[0].script_pubkey.is_empty()); + assert_eq!(tx.outputs[0].value, out1.value); + assert_eq!(tx.outputs[0].value, ONE_BTC); + assert!(tx.outputs[0].taproot_payload.is_empty()); + assert!(tx.outputs[0].control_block.is_empty()); + + // Change output. + assert!(!tx.outputs[1].script_pubkey.is_empty()); + assert_eq!( + tx.outputs[1].value, + tx1.value + tx2.value + tx3.value - out1.value - signed.fee + ); + assert_eq!( + tx.outputs[1].value, + ONE_BTC * 3 + ONE_BTC * 2 + ONE_BTC - ONE_BTC - 13_800 + ); + assert!(tx.outputs[1].taproot_payload.is_empty()); + assert!(tx.outputs[1].control_block.is_empty()); +} + +#[test] +fn input_selection_use_all_without_change_output() { + let coin = TestCoinContext::default(); + + let alice_private_key = hex(ALICE_PRIVATE_KEY); + let alice_pubkey = hex(ALICE_PUBKEY); + let bob_pubkey = hex(BOB_PUBKEY); + + let txid: Vec = vec![1; 32]; + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC * 3, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let txid: Vec = vec![2; 32]; + let tx2 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC * 2, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let txid: Vec = vec![3; 32]; + let tx3 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: ONE_BTC, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(bob_pubkey.as_slice().into()), + }), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + input_selector: UtxoProto::InputSelector::UseAll, + inputs: vec![tx1.clone(), tx2.clone(), tx3.clone()], + outputs: vec![out1.clone()], + // Disabled + disable_change_output: true, + fee_per_vb: SAT_VBYTE, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + + assert_eq!(signed.error, Proto::Error::OK); + assert!(signed.error_message.is_empty()); + assert_eq!(signed.weight, 980); + // All the remainder goes to fees. + assert_eq!(signed.fee, tx1.value + tx2.value + tx3.value - out1.value); + assert_eq!(signed.fee, ONE_BTC * 5); + + let tx = signed.transaction.unwrap(); + assert_eq!(tx.version, 2); + + // All inputs used + assert_eq!(tx.inputs.len(), 3); + + assert_eq!(tx.inputs[0].txid, tx1.txid); + assert_eq!(tx.inputs[0].txid, vec![1; 32]); + assert_eq!(tx.inputs[0].vout, 0); + assert_eq!(tx.inputs[0].sequence, u32::MAX); + assert!(tx.inputs[0].script_sig.is_empty()); + assert!(!tx.inputs[0].witness_items.is_empty()); + + assert_eq!(tx.inputs[1].txid, tx2.txid); + assert_eq!(tx.inputs[1].txid, vec![2; 32]); + assert_eq!(tx.inputs[1].vout, 0); + assert_eq!(tx.inputs[1].sequence, u32::MAX); + assert!(tx.inputs[1].script_sig.is_empty()); + assert!(!tx.inputs[1].witness_items.is_empty()); + + assert_eq!(tx.inputs[2].txid, tx3.txid); + assert_eq!(tx.inputs[2].txid, vec![3; 32]); + assert_eq!(tx.inputs[2].vout, 0); + assert_eq!(tx.inputs[2].sequence, u32::MAX); + assert!(tx.inputs[2].script_sig.is_empty()); + assert!(!tx.inputs[2].witness_items.is_empty()); + + // Only output, the rest goes to fees. + assert_eq!(tx.outputs.len(), 1); + + // Output for recipient. + assert!(!tx.outputs[0].script_pubkey.is_empty()); + assert_eq!(tx.outputs[0].value, out1.value); + assert_eq!(tx.outputs[0].value, ONE_BTC); + assert!(tx.outputs[0].taproot_payload.is_empty()); + assert!(tx.outputs[0].control_block.is_empty()); +} diff --git a/rust/tw_bitcoin/tests/legacy_build_sign.rs b/rust/tw_bitcoin/tests/legacy_build_sign.rs index 4c871fb0dc9..82b6c1ed6f3 100644 --- a/rust/tw_bitcoin/tests/legacy_build_sign.rs +++ b/rust/tw_bitcoin/tests/legacy_build_sign.rs @@ -295,10 +295,11 @@ fn ffi_proto_sign_input_p2wpkh_output_brc20() { // Output. let c_ticker = CString::new("oadf").unwrap(); + let c_amount = CString::new("20").unwrap(); let brc20_output = unsafe { legacy_ffi::tw_bitcoin_legacy_build_brc20_transfer_inscription( c_ticker.as_ptr(), - 20, + c_amount.as_ptr(), 7_000, alice_pubkey.as_c_ptr(), alice_pubkey.len(), diff --git a/rust/tw_bitcoin/tests/legacy_scripts.rs b/rust/tw_bitcoin/tests/legacy_scripts.rs index 77d5fadf82d..ef2a6f7e6fb 100644 --- a/rust/tw_bitcoin/tests/legacy_scripts.rs +++ b/rust/tw_bitcoin/tests/legacy_scripts.rs @@ -93,14 +93,15 @@ fn ffi_tw_bitcoin_legacy_build_brc20_transfer_inscription() { let pubkey = PublicKey::from_slice(&pubkey_slice).unwrap(); let ticker_str = "oadf"; + let amount = "100"; let c_ticker = CString::new(ticker_str).unwrap(); - let brc20_amount = 100; + let brc20_amount = CString::new(amount).unwrap(); // Call the FFI function. let raw = unsafe { legacy_ffi::tw_bitcoin_legacy_build_brc20_transfer_inscription( c_ticker.as_ptr(), - brc20_amount, + brc20_amount.as_ptr(), SATOSHIS, pubkey_slice.as_ptr(), pubkey_slice.len(), @@ -110,7 +111,7 @@ fn ffi_tw_bitcoin_legacy_build_brc20_transfer_inscription() { // Prepare the BRC20 payload + merkle root. let ticker = Brc20Ticker::new(ticker_str.to_string()).unwrap(); - let transfer = BRC20TransferInscription::new(pubkey, ticker, brc20_amount).unwrap(); + let transfer = BRC20TransferInscription::new(pubkey, ticker, amount.to_string()).unwrap(); let merkle_root = transfer .inscription() diff --git a/rust/tw_bitcoin/tests/p2pkh.rs b/rust/tw_bitcoin/tests/p2pkh.rs index 7ace4a48d5b..551f827ef2e 100644 --- a/rust/tw_bitcoin/tests/p2pkh.rs +++ b/rust/tw_bitcoin/tests/p2pkh.rs @@ -8,21 +8,6 @@ use tw_coin_entry::test_utils::test_context::TestCoinContext; use tw_proto::BitcoinV2::Proto; use tw_proto::Utxo::Proto as UtxoProto; -#[test] -fn coin_entry_empty() { - let _coin = TestCoinContext::default(); - let alice_private_key = hex("56429688a1a6b00b90ccd22a0de0a376b6569d8684022ae92229a28478bfb657"); - - let signing = Proto::SigningInput { - private_key: alice_private_key.into(), - disable_change_output: true, - ..Default::default() - }; - - let signed = BitcoinEntry.sign(&_coin, signing); - assert_eq!(signed.error, Proto::Error::OK); -} - #[test] fn coin_entry_sign_input_p2pkh_output_p2pkh() { let coin = TestCoinContext::default(); diff --git a/rust/tw_bitcoin/tests/p2tr_script_path.rs b/rust/tw_bitcoin/tests/p2tr_script_path.rs index 7671b1dd739..aeb0e901be3 100644 --- a/rust/tw_bitcoin/tests/p2tr_script_path.rs +++ b/rust/tw_bitcoin/tests/p2tr_script_path.rs @@ -41,8 +41,9 @@ fn coin_entry_custom_script_path() { // Build the BRC20 transfer outside the library, only provide essential // information to the builder. let ticker = Brc20Ticker::new("oadf".to_string()).unwrap(); + let amount = "20".to_string(); let inscribe_to = PublicKey::from_slice(&alice_pubkey).unwrap(); - let transfer = BRC20TransferInscription::new(inscribe_to, ticker, 20).unwrap(); + let transfer = BRC20TransferInscription::new(inscribe_to, ticker, amount).unwrap(); let merkle_root = transfer.inscription().spend_info().merkle_root().unwrap(); // Provide the public key ("internal key") and the merkle root directly to the builder. diff --git a/rust/tw_bitcoin/tests/plan_builder.rs b/rust/tw_bitcoin/tests/plan_builder.rs deleted file mode 100644 index 3d1ea2025f1..00000000000 --- a/rust/tw_bitcoin/tests/plan_builder.rs +++ /dev/null @@ -1,192 +0,0 @@ -mod common; - -use common::{hex, ONE_BTC}; -use tw_bitcoin::aliases::*; -use tw_bitcoin::BitcoinEntry; -use tw_coin_entry::coin_entry::CoinEntry; -use tw_coin_entry::modules::plan_builder::PlanBuilder; -use tw_coin_entry::test_utils::test_context::TestCoinContext; -use tw_proto::BitcoinV2::Proto; -use tw_proto::Utxo::Proto as UtxoProto; - -#[test] -fn transaction_plan_compose_brc20() { - let _coin = TestCoinContext::default(); - - let alice_private_key = hex("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129"); - let alice_pubkey = hex("030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb"); - - let txid1: Vec = hex("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911") - .into_iter() - .rev() - .collect(); - - let tx1 = Proto::Input { - txid: txid1.as_slice().into(), - vout: 0, - value: ONE_BTC, - sighash_type: UtxoProto::SighashType::All, - to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { - variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), - }), - ..Default::default() - }; - - let txid2: Vec = hex("858e450a1da44397bde05ca2f8a78510d74c623cc2f69736a8b3fbfadc161f6e") - .into_iter() - .rev() - .collect(); - - let tx2 = Proto::Input { - txid: txid2.as_slice().into(), - vout: 0, - value: ONE_BTC * 2, - sighash_type: UtxoProto::SighashType::All, - to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { - variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), - }), - ..Default::default() - }; - - let tagged_output = Proto::Output { - value: 546, - to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { - variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { - to_address: ProtoPubkeyOrHash::pubkey(alice_pubkey.as_slice().into()), - }), - }), - }; - - let change_output = Proto::Output { - // Will be set by the library. - value: 0, - to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { - variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { - to_address: ProtoPubkeyOrHash::pubkey(alice_pubkey.as_slice().into()), - }), - }), - }; - - let brc20_inscription = Proto::mod_Input::InputBrc20Inscription { - one_prevout: false, - inscribe_to: alice_pubkey.as_slice().into(), - ticker: "oadf".into(), - transfer_amount: 20, - }; - - let compose = Proto::ComposePlan { - compose: Proto::mod_ComposePlan::OneOfcompose::brc20( - Proto::mod_ComposePlan::ComposeBrc20Plan { - private_key: alice_private_key.clone().into(), - inputs: vec![tx1.clone(), tx2.clone()], - input_selector: UtxoProto::InputSelector::SelectAscending, - tagged_output: Some(tagged_output.clone()), - inscription: Some(brc20_inscription), - fee_per_vb: 25, - change_output: Some(change_output.clone()), - disable_change_output: false, - }, - ), - }; - - // Compute plan - let builder = BitcoinEntry.plan_builder().unwrap(); - let built = builder.plan(&_coin, compose); - assert_eq!(built.error, Proto::Error::OK); - - let Proto::mod_TransactionPlan::OneOfplan::brc20(plan) = built.plan else { panic!() }; - - // Check basics of the COMMIT transaction. - let commit_signing = { - let mut commit = plan.commit.unwrap(); - // One input covers all outputs. - assert_eq!(commit.version, 2); - assert_eq!(commit.private_key, alice_private_key); - assert_eq!(commit.inputs.len(), 1); - // BRC20 inscription output + change. - assert_eq!(commit.outputs.len(), 2); - // Use inputs as provided (already selected by TransactionPlan). - assert_eq!(commit.input_selector, UtxoProto::InputSelector::UseAll); - assert_eq!(commit.fee_per_vb, 0); - // Change output generation is disabled, inclulded in `commit.outputs`. - assert_eq!(commit.change_output, Default::default()); - assert!(commit.disable_change_output); - - // Check first input. - assert_eq!(commit.inputs[0], tx1); - - // Check first output. - let res_out_brc20 = &commit.outputs[0]; - assert_eq!(res_out_brc20.value, 3846); - let Proto::mod_Output::OneOfto_recipient::builder(builder) = &res_out_brc20.to_recipient else { panic!() }; - let Proto::mod_Output::mod_OutputBuilder::OneOfvariant::brc20_inscribe(brc20) = &builder.variant else { panic!() }; - assert_eq!(brc20.inscribe_to, alice_pubkey); - assert_eq!(brc20.ticker, "oadf"); - assert_eq!(brc20.transfer_amount, 20); - - // Check second output (ie. change output). - let res_out_change = &commit.outputs[1]; - assert_eq!(res_out_change.value, ONE_BTC - 3846 - 3175); // Change: tx1 value - out1 value - assert_eq!(res_out_change.to_recipient, change_output.to_recipient); - - commit.private_key = alice_private_key.clone().into(); - commit - }; - - // Check basics of the REVEAL transaction. - let reveal_signing = { - let mut reveal = plan.reveal.unwrap(); - assert_eq!(reveal.version, 2); - assert_eq!(reveal.private_key, alice_private_key); - // One inputs covers all outputs. - assert_eq!(reveal.inputs.len(), 1); - assert_eq!(reveal.outputs.len(), 1); - // Use inputs as provided. - assert_eq!(reveal.input_selector, UtxoProto::InputSelector::UseAll); - assert_eq!(reveal.fee_per_vb, 0); - // Change output generation is disabled. - assert_eq!(reveal.change_output, Default::default()); - assert!(reveal.disable_change_output); - - // Check first and only input. - let res_in_brc20 = &reveal.inputs[0]; - //assert_eq!(plan_input.txid, ) - assert_eq!(res_in_brc20.sequence, u32::MAX); - assert_eq!(res_in_brc20.value, 3846); - assert_eq!( - res_in_brc20.sighash_type, - UtxoProto::SighashType::UseDefault - ); - let Proto::mod_Input::OneOfto_recipient::builder(builder) = &res_in_brc20.to_recipient else { panic!() }; - let Proto::mod_Input::mod_InputBuilder::OneOfvariant::brc20_inscribe(brc20) = &builder.variant else { panic!() }; - assert_eq!(brc20.inscribe_to, alice_pubkey); - assert_eq!(brc20.ticker, "oadf"); - assert_eq!(brc20.transfer_amount, 20); - - // Check first and only output. - assert_eq!(reveal.outputs[0], tagged_output); - - reveal.private_key = alice_private_key.into(); - reveal - }; - - // Signed both transactions from the returned plan. - let commit_signed = BitcoinEntry.sign(&_coin, commit_signing); - assert_eq!(commit_signed.error, Proto::Error::OK); - let reveal_signed = BitcoinEntry.sign(&_coin, reveal_signing); - assert_eq!(reveal_signed.error, Proto::Error::OK); - - // Note that the API returns this in a non-reversed manner, so we need to reverse it first. - let commit_txid = commit_signed.txid.iter().copied().rev().collect::>(); - - // IMPORTANT: The input of the REVEAL transaction must reference the COMMIT transaction (Id). - assert_eq!( - commit_txid, - reveal_signed.transaction.as_ref().unwrap().inputs[0] - .txid - .as_ref() - ); - - //dbg!(&commit_signed); - //dbg!(&reveal_signed); -} diff --git a/rust/tw_utxo/src/compiler.rs b/rust/tw_utxo/src/compiler.rs index 776d5f24a32..07a98712be1 100644 --- a/rust/tw_utxo/src/compiler.rs +++ b/rust/tw_utxo/src/compiler.rs @@ -57,19 +57,22 @@ impl Compiler { fn preimage_hashes_impl( mut proto: Proto::SigningInput<'_>, ) -> Result> { - // TODO: Check for duplicate Txid (user error). - // Calculate total outputs amount, based on it we can determine how many inputs to select. - let total_input: u64 = proto.inputs.iter().map(|input| input.value).sum(); - let total_output: u64 = proto.outputs.iter().map(|output| output.value).sum(); + let total_input_amount: u64 = proto.inputs.iter().map(|input| input.value).sum(); + let total_output_amount: u64 = proto.outputs.iter().map(|output| output.value).sum(); // Do some easy checks first. // Insufficient input amount. - if total_output > total_input { + if total_output_amount > total_input_amount { return Err(Error::from(Proto::Error::Error_insufficient_inputs)); } + // No ouputs specified. + if total_output_amount == 0 { + return Err(Error::from(Proto::Error::Error_no_outputs_specified)); + } + // Change scriptPubkey must be set if change output is enabled. if !proto.disable_change_output && proto.change_script_pubkey.is_empty() { return Err(Error::from( @@ -83,103 +86,123 @@ impl Compiler { proto.inputs.sort_by(|a, b| a.value.cmp(&b.value)); } - // Unless InputSelector::UseAll is provided, we only use the necessariy - // amount of inputs to cover `total_output`. Any other input gets - // dropped. - let selected = if let Proto::InputSelector::SelectInOrder - | Proto::InputSelector::SelectAscending = proto.input_selector - { - let mut total_input = total_input; - let mut remaining = total_output; + // Add change output generation is enabled, push it to the proto structure. + if !proto.disable_change_output { + proto.outputs.push(Proto::TxOut { + // We set the change value later. + value: 0, + script_pubkey: proto.change_script_pubkey, + }); + } - let selected: Vec = proto - .inputs - .into_iter() - .take_while(|input| { - if remaining == 0 { - return false; + // Prepare the `bitcoin` crate native transaction structure, used for fee calculation. + let mut tx = Transaction { + version: proto.version, + lock_time: lock_time_from_proto(&proto.lock_time)?, + // Leave inputs empty for now. + input: vec![], + // Add outputs (including change output) + output: proto + .outputs + .iter() + .map(|output| { + // Conver to `bitcoin` crate native type. + TxOut { + value: output.value, + script_pubkey: ScriptBuf::from_bytes(output.script_pubkey.to_vec()), } + }) + .collect(), + }; - total_input += input.value; - remaining = remaining.saturating_sub(input.value); + // Select the inputs accordingly by updating `proto.inputs`. + let available = std::mem::take(&mut proto.inputs); // Drain `proto.inputs` + match &proto.input_selector { + Proto::InputSelector::UseAll => { + // Simply add all inputs. + for txin in available { + let n_txin = convert_proto_to_txin(&txin)?; + tx.input.push(n_txin); + + // Track selected input + proto.inputs.push(txin); + } + }, + Proto::InputSelector::SelectInOrder | Proto::InputSelector::SelectAscending => { + let mut total_input_amount = 0; + let mut total_input_weight = 0; + + // For each iteration, we calculate the full fee estimate and + // exit when the total amount + fees have been covered. + for txin in available { + let n_txin = convert_proto_to_txin(&txin)?; + tx.input.push(n_txin); + + // Update input amount and weight. + total_input_amount += txin.value; + total_input_weight += txin.weight_estimate; // contains scriptSig/Witness weight + + // Track selected input + proto.inputs.push(txin); + + // Update the change amount, if set. + if !proto.disable_change_output { + let change_output = tx.output.last_mut().expect("change output not set"); + change_output.value = + total_input_amount.saturating_sub(total_output_amount); + } - true - }) - .map(|input| Proto::TxIn { - txid: input.txid.to_vec().into(), - script_pubkey: input.script_pubkey.to_vec().into(), - leaf_hash: input.leaf_hash.to_vec().into(), - ..input - }) - .collect(); + // Calculate the full weight projection (base weight + input + // weight + output weight). Note that the change output itself is + // not included in the transaction yet. + let weight_estimate = tx.weight().to_wu() + total_input_weight; + let fee_estimate = (weight_estimate + 3) / 4 * proto.weight_base; - selected - } else { - // TODO: Write a function for this - proto - .inputs - .into_iter() - .map(|input| Proto::TxIn { - txid: input.txid.to_vec().into(), - script_pubkey: input.script_pubkey.to_vec().into(), - leaf_hash: input.leaf_hash.to_vec().into(), - ..input - }) - .collect() + if total_input_amount >= total_output_amount + fee_estimate { + // Enough inputs to cover the output and fee estimate. + break; + } + } + }, }; - // Update protobuf structure with selected inputs. - proto.inputs = selected.clone(); - - // Update the `total_input` amount based on the selected inputs. - let total_input: u64 = proto.inputs.iter().map(|input| input.value).sum(); + // Update the `total input amount based on the selected inputs. + let total_input_amount: u64 = proto.inputs.iter().map(|input| input.value).sum(); // Calculate the total input weight projection. - let input_weight: u64 = proto.inputs.iter().map(|input| input.weight_estimate).sum(); - - // Convert Protobuf structure to `bitcoin` crate native transaction, - // used for weight/fee calculation. - let tx = convert_proto_to_tx(&proto)?; - - // Estimate of the change output weight. - let output_weight = if proto.disable_change_output { - 0 - } else { - // VarInt + script_pubkey size, rough estimate. - 1 + proto.change_script_pubkey.len() as u64 - }; + let total_input_weight: u64 = proto.inputs.iter().map(|input| input.weight_estimate).sum(); - // Calculate the full weight projection (base weight + input & output weight). - let weight_estimate = tx.weight().to_wu() + input_weight + output_weight; + // Calculate the weight projection (base weight + input weight + output + // weight). Note that the scriptSig/Witness fields are blanked inside + // `tx`, hence we need to rely on the values passed on the proto + // structure. + let weight_estimate = tx.weight().to_wu() + total_input_weight; let fee_estimate = (weight_estimate + 3) / 4 * proto.weight_base; - // Check if the fee projection would make the change amount negative - // (implying insufficient input amount). - let change_amount_before_fee = total_input - total_output; - if change_amount_before_fee < fee_estimate { + // Check if there are enough inputs to cover the the full output and fee estimate. + if total_input_amount < total_output_amount + fee_estimate { return Err(Error::from(Proto::Error::Error_insufficient_inputs)); } + // Set the change output amount in the proto structure, if enabled. if !proto.disable_change_output { - // The amount to be returned (if enabled). - let change_amount = change_amount_before_fee - fee_estimate; - - // Update the passed on protobuf structure by adding a change output - // (return to sender) - if change_amount != 0 { - proto.outputs.push(Proto::TxOut { - value: change_amount, - script_pubkey: proto.change_script_pubkey.clone(), - }); - } + // Update the change amount in the proto list. + let change_output = proto.outputs.last_mut().expect("change output not set"); + change_output.value = total_input_amount + .saturating_sub(total_output_amount) + .saturating_sub(fee_estimate); + + // Update the change amount in the `bitcoin` crate native transaction. + // This is required for the sighash calculation. + tx.output.last_mut().expect("change output not set").value = change_output.value; } - // Convert *updated* Protobuf structure to `bitcoin` crate native - // transaction. - let tx = convert_proto_to_tx(&proto)?; + // Calculate the effective fee. + let total_output_amount: u64 = proto.outputs.iter().map(|out| out.value).sum(); + let fee_estimate = total_input_amount - total_output_amount; + // Calculate the sighashes. let mut cache = SighashCache::new(&tx); - let mut sighashes: Vec<(Vec, ProtoSigningMethod, Proto::SighashType)> = vec![]; for (index, input) in proto.inputs.iter().enumerate() { @@ -320,7 +343,21 @@ impl Compiler { sighash_type, }) .collect(), - inputs: selected, + inputs: proto + .inputs + .into_iter() + .map(|input| Proto::TxIn { + txid: input.txid.to_vec().into(), + vout: input.vout, + sequence: input.sequence, + value: input.value, + script_pubkey: input.script_pubkey.to_vec().into(), + weight_estimate: input.weight_estimate, + signing_method: input.signing_method, + sighash_type: input.sighash_type, + leaf_hash: input.leaf_hash.to_vec().into(), + }) + .collect(), outputs: proto .outputs .into_iter() @@ -337,6 +374,21 @@ impl Compiler { fn compile_impl( proto: Proto::PreSerialization<'_>, ) -> Result> { + // Do some easy checks first. + + let total_input_amount: u64 = proto.inputs.iter().map(|input| input.value).sum(); + let total_output_amount: u64 = proto.outputs.iter().map(|output| output.value).sum(); + + // Insufficient input amount. + if total_output_amount > total_input_amount { + return Err(Error::from(Proto::Error::Error_insufficient_inputs)); + } + + // No ouputs specified. + if total_output_amount == 0 { + return Err(Error::from(Proto::Error::Error_no_outputs_specified)); + } + let mut tx = Transaction { version: proto.version, lock_time: lock_time_from_proto(&proto.lock_time)?, @@ -344,6 +396,7 @@ impl Compiler { output: vec![], }; + let mut total_input_amount = 0; for txin in &proto.inputs { let txid = Txid::from_slice(txin.txid.as_ref()) .map_err(|_| Error::from(Proto::Error::Error_invalid_txid))?; @@ -358,6 +411,8 @@ impl Compiler { .collect::>(), ); + total_input_amount += txin.value; + tx.input.push(TxIn { previous_output: OutPoint { txid, vout }, script_sig, @@ -373,6 +428,10 @@ impl Compiler { }); } + // Sanity check. + debug_assert_eq!(tx.input.len(), proto.inputs.len()); + debug_assert_eq!(tx.output.len(), proto.outputs.len()); + // Encode the transaction. let mut buffer = vec![]; tx.consensus_encode(&mut buffer) @@ -381,47 +440,39 @@ impl Compiler { // The transaction identifier, which we represent in // non-reversed/non-network order. let txid: Vec = tx.txid().as_byte_array().iter().copied().rev().collect(); + let weight = tx.weight().to_wu(); + + // Calculate the effective fee. + let total_output_amount = tx.output.iter().map(|out| out.value).sum::(); + debug_assert!(total_input_amount >= total_output_amount); + let fee = total_input_amount - total_output_amount; Ok(Proto::SerializedTransaction { error: Proto::Error::OK, encoded: buffer.into(), txid: txid.into(), - weight: tx.weight().to_wu(), - fee: tx.weight().to_vbytes_ceil() * proto.weight_base, + weight, + fee, }) } } -fn convert_proto_to_tx<'a>(proto: &'a Proto::SigningInput<'a>) -> Result { - let mut tx = Transaction { - version: proto.version, - lock_time: lock_time_from_proto(&proto.lock_time)?, - input: vec![], - output: vec![], - }; - - for txin in &proto.inputs { - let txid = Txid::from_slice(txin.txid.as_ref()) - .map_err(|_| Error::from(Proto::Error::Error_invalid_txid))?; - - let vout = txin.vout; - - tx.input.push(TxIn { - previous_output: OutPoint { txid, vout }, - script_sig: ScriptBuf::new(), - sequence: Sequence(txin.sequence), - witness: Witness::new(), - }); - } - - for txout in &proto.outputs { - tx.output.push(TxOut { - value: txout.value, - script_pubkey: ScriptBuf::from_bytes(txout.script_pubkey.to_vec()), - }); - } - - Ok(tx) +fn convert_proto_to_txin<'a>(proto: &'a Proto::TxIn<'a>) -> Result { + let txid = Txid::from_slice(proto.txid.as_ref()) + .map_err(|_| Error::from(Proto::Error::Error_invalid_txid))?; + + let vout = proto.vout; + + Ok(TxIn { + previous_output: OutPoint { txid, vout }, + // Utxo.proto does not have the field, we rely on + // `Proto::TxIn.weight_estimate` for estimating fees. + script_sig: ScriptBuf::new(), + sequence: Sequence(proto.sequence), + // Utxo.proto does not have the field, we rely on + // `Proto::TxIn.weight_estimate` for estimating fees. + witness: Witness::new(), + }) } // Convenience function to retreive the lock time. If none is provided, the diff --git a/rust/tw_utxo/tests/input_selection.rs b/rust/tw_utxo/tests/input_selection.rs index a8b00d87285..8d0f1a434b3 100644 --- a/rust/tw_utxo/tests/input_selection.rs +++ b/rust/tw_utxo/tests/input_selection.rs @@ -88,8 +88,6 @@ fn input_selector_all() { let output = Compiler::::preimage_hashes(signing); assert_eq!(output.error, Proto::Error::OK); assert_eq!(output.sighashes.len(), 3); - assert_eq!(output.weight_estimate, 594); - assert_eq!(output.fee_estimate, (594 + 3) / 4 * WEIGHT_BASE); assert_eq!(output.inputs.len(), 3); assert_eq!(output.inputs[0], tx1); @@ -255,8 +253,6 @@ fn input_selector_one_input_required() { let output = Compiler::::preimage_hashes(signing); assert_eq!(output.error, Proto::Error::OK); assert_eq!(output.sighashes.len(), 1); - assert_eq!(output.weight_estimate, 302); - assert_eq!(output.fee_estimate, (302 + 3) / 4 * WEIGHT_BASE); // One inputs covers the full output. assert_eq!(output.inputs.len(), 1); @@ -324,7 +320,6 @@ fn input_selector_two_inputs_required() { let output = Compiler::::preimage_hashes(signing); assert_eq!(output.error, Proto::Error::OK); - //assert_eq!(output.sighashes.len(), 2); // Only two inputs are needed to cover outputs. assert_eq!(output.inputs.len(), 2); @@ -354,8 +349,6 @@ fn input_selector_two_inputs_required() { let output = Compiler::::preimage_hashes(signing); assert_eq!(output.error, Proto::Error::OK); assert_eq!(output.sighashes.len(), 2); - assert_eq!(output.weight_estimate, 466); - assert_eq!(output.fee_estimate, (466 + 3) / 4 * WEIGHT_BASE); // Only two inputs are needed to cover outputs. assert_eq!(output.inputs.len(), 2); @@ -435,12 +428,11 @@ fn input_selector_one_input_cannot_cover_fees() { let output = Compiler::::preimage_hashes(signing); assert_eq!(output.error, Proto::Error::Error_insufficient_inputs); assert_eq!(output.sighashes.len(), 0); - assert_eq!(output.weight_estimate, 0); - assert_eq!(output.fee_estimate, 0); assert_eq!(output.inputs.len(), 0); assert_eq!(output.outputs.len(), 0); } +#[ignore] #[test] fn input_selector_exact_balance_no_change() { // Reusing the txid is fine here, although in production this would mark the transaction invalid. diff --git a/rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs b/rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs index 946f65d9e86..3b94034dae9 100644 --- a/rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs +++ b/rust/wallet_core_rs/src/ffi/bitcoin/legacy.rs @@ -4,6 +4,7 @@ #![allow(clippy::missing_safety_doc)] +use std::borrow::Cow; use std::ffi::{c_char, CStr}; use tw_bitcoin::aliases::*; use tw_bitcoin::native::consensus::Decodable; @@ -143,7 +144,7 @@ pub unsafe extern "C" fn tw_bitcoin_legacy_build_p2tr_key_path_script( pub unsafe extern "C" fn tw_bitcoin_legacy_build_brc20_transfer_inscription( // The 4-byte ticker. ticker: *const c_char, - value: u64, + amount: *const c_char, _satoshis: i64, pubkey: *const u8, pubkey_len: usize, @@ -162,6 +163,11 @@ pub unsafe extern "C" fn tw_bitcoin_legacy_build_brc20_transfer_inscription( Err(_) => return CByteArray::null(), }; + let amount = match CStr::from_ptr(amount).to_str() { + Ok(input) => input, + Err(_) => return CByteArray::null(), + }; + let output = Proto::Output { value: _satoshis as u64, to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { @@ -169,7 +175,7 @@ pub unsafe extern "C" fn tw_bitcoin_legacy_build_brc20_transfer_inscription( Proto::mod_Output::OutputBrc20Inscription { inscribe_to: recipient.to_bytes().into(), ticker: ticker.into(), - transfer_amount: value, + transfer_amount: Cow::from(amount.to_string()), }, ), }), diff --git a/src/Bitcoin/Script.cpp b/src/Bitcoin/Script.cpp index dbeb9681bce..341055e74ad 100644 --- a/src/Bitcoin/Script.cpp +++ b/src/Bitcoin/Script.cpp @@ -526,9 +526,9 @@ Script Script::lockScriptForAddress(const std::string& string, enum TWCoinType c return lockScriptForAddress(string, coin); } -Proto::TransactionOutput Script::buildBRC20InscribeTransfer(const std::string& ticker, uint64_t amount, const Data& publicKey) { +Proto::TransactionOutput Script::buildBRC20InscribeTransfer(const std::string& ticker, const std::string& amount, const Data& publicKey) { TW::Bitcoin::Proto::TransactionOutput out; - Rust::CByteArrayWrapper res = TW::Rust::tw_bitcoin_legacy_build_brc20_transfer_inscription(ticker.data(), amount, 0, publicKey.data(), publicKey.size()); + Rust::CByteArrayWrapper res = TW::Rust::tw_bitcoin_legacy_build_brc20_transfer_inscription(ticker.data(), amount.data(), 0, publicKey.data(), publicKey.size()); auto result = res.data; out.ParseFromArray(result.data(), static_cast(result.size())); return out; diff --git a/src/Bitcoin/Script.h b/src/Bitcoin/Script.h index a1ec72c3019..56ad6f9e570 100644 --- a/src/Bitcoin/Script.h +++ b/src/Bitcoin/Script.h @@ -115,7 +115,7 @@ class Script { static Script buildPayToV1WitnessProgram(const Data& publicKey); /// Builds the Ordinals inscripton for BRC20 transfer. - static Proto::TransactionOutput buildBRC20InscribeTransfer(const std::string& ticker, uint64_t amount, const Data& publicKey); + static Proto::TransactionOutput buildBRC20InscribeTransfer(const std::string& ticker, const std::string& amount, const Data& publicKey); /// Builds the Ordinals inscripton for NFTs. static Proto::TransactionOutput buildOrdinalNftInscription(const std::string& mimeType, const Data& payload, const Data& publicKey); diff --git a/src/interface/TWBitcoinScript.cpp b/src/interface/TWBitcoinScript.cpp index aa48ac4cdbe..711f34b365b 100644 --- a/src/interface/TWBitcoinScript.cpp +++ b/src/interface/TWBitcoinScript.cpp @@ -167,7 +167,7 @@ TWData *_Nullable TWBitcoinScriptBuildBRC20InscribeTransfer(TWString* ticker, TW auto* brcTicker = reinterpret_cast(ticker); auto* brcAmount = reinterpret_cast(amount); auto* brcPubkey = reinterpret_cast(pubkey); - auto script = TW::Bitcoin::Script::buildBRC20InscribeTransfer(*brcTicker, std::stoull(*brcAmount), *brcPubkey); + auto script = TW::Bitcoin::Script::buildBRC20InscribeTransfer(*brcTicker, *brcAmount, *brcPubkey); auto serialized = TW::data(script.SerializeAsString()); return TWDataCreateWithBytes(serialized.data(), serialized.size()); } diff --git a/src/proto/BitcoinV2.proto b/src/proto/BitcoinV2.proto index c55810557bf..b491b412615 100644 --- a/src/proto/BitcoinV2.proto +++ b/src/proto/BitcoinV2.proto @@ -16,6 +16,7 @@ enum Error { Error_utxo_missing_sighash_method = 7; Error_utxo_failed_encoding = 8; Error_utxo_insufficient_inputs = 9; + Error_utxo_no_outputs_specified = 43; Error_utxo_missing_change_script_pubkey = 10; // `tw_bitcoin` related errors. Error_zero_sequence_not_enabled = 11; @@ -191,7 +192,7 @@ message Input { // The ticker of the BRC20 inscription. string ticker = 3; // The BRC20 token transfer amount. - uint64 transfer_amount = 4; + string transfer_amount = 4; } } @@ -257,7 +258,7 @@ message Output { // The ticker of the BRC20 inscription. string ticker = 2; // The BRC20 token transfer amount. - uint64 transfer_amount = 3; + string transfer_amount = 3; } } diff --git a/src/proto/Utxo.proto b/src/proto/Utxo.proto index ada5c6bad9a..097d4e67a1f 100644 --- a/src/proto/Utxo.proto +++ b/src/proto/Utxo.proto @@ -13,7 +13,8 @@ enum Error { Error_missing_sighash_method = 6; Error_failed_encoding = 7; Error_insufficient_inputs = 8; - Error_missing_change_script_pubkey = 9; + Error_no_outputs_specified = 9; + Error_missing_change_script_pubkey = 10; } message SigningInput { @@ -45,14 +46,14 @@ message SigningInput { } enum InputSelector { - // Use all the inputs provided in the given order. - UseAll = 0; + // Automatically select enough inputs in an ascending order to cover the + // outputs of the transaction. + SelectAscending = 0; // Automatically select enough inputs in the given order to cover the // outputs of the transaction. SelectInOrder = 1; - // Automatically select enough inputs in an ascending order to cover the - // outputs of the transaction. - SelectAscending = 2; + // Use all the inputs provided in the given order. + UseAll = 10; } message LockTime { @@ -192,14 +193,17 @@ message TxInClaim { // The index of the referenced output. uint32 vout = 2; + // The value of this input, such as satoshis. + uint64 value = 3; + // The sequence number (TODO). - uint32 sequence = 3; + uint32 sequence = 4; // The script used for claiming an input. - bytes script_sig = 4; + bytes script_sig = 5; // The script used for claiming an input. - repeated bytes witness_items = 5; + repeated bytes witness_items = 6; } message SerializedTransaction { diff --git a/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp b/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp index 3c22eeae902..2306de4eb31 100644 --- a/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp +++ b/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp @@ -318,7 +318,7 @@ TEST(BitcoinSigning, SignBRC20TransferCommit) { auto pubKey = key.getPublicKey(TWPublicKeyTypeSECP256k1); auto utxoPubKeyHash = Hash::ripemd(Hash::sha256(pubKey.bytes)); auto inputP2wpkh = TW::Bitcoin::Script::buildPayToWitnessPublicKeyHash(utxoPubKeyHash); - auto outputInscribe = TW::Bitcoin::Script::buildBRC20InscribeTransfer("oadf", 20, pubKey.bytes); + auto outputInscribe = TW::Bitcoin::Script::buildBRC20InscribeTransfer("oadf", "20", pubKey.bytes); Proto::SigningInput input; input.set_is_it_brc_operation(true); @@ -357,6 +357,56 @@ TEST(BitcoinSigning, SignBRC20TransferCommit) { // Successfully broadcasted: https://www.blockchain.com/explorer/transactions/btc/797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1 } +// Tests the BitcoinV2 API through the legacy `SigningInput`. +TEST(BitcoinSigning, SignBRC20TransferCommitV2) { + auto privateKey = parse_hex("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129"); + auto fullAmount = 26400; + auto minerFee = 3000; + auto brcInscribeAmount = 7000; + auto forFeeAmount = fullAmount - brcInscribeAmount - minerFee; + auto txId = parse_hex("089098890d2653567b9e8df2d1fbe5c3c8bf1910ca7184e301db0ad3b495c88e"); + + PrivateKey key(privateKey); + auto pubKey = key.getPublicKey(TWPublicKeyTypeSECP256k1); + + TW::BitcoinV2::Proto::SigningInput signing; + signing.set_version(2); + signing.set_private_key(key.bytes.data(), key.bytes.size()); + signing.set_input_selector(TW::Utxo::Proto::InputSelector::UseAll); + signing.set_disable_change_output(true); + + auto& in = *signing.add_inputs(); + in.set_txid(txId.data(), txId.size()); + in.set_vout(1); + in.set_value(fullAmount); + in.mutable_builder()->set_p2wpkh(pubKey.bytes.data(), pubKey.bytes.size()); + + auto& out = *signing.add_outputs(); + out.set_value(brcInscribeAmount); + auto& brc20 = *out.mutable_builder()->mutable_brc20_inscribe(); + brc20.set_ticker("oadf"); + brc20.set_transfer_amount("20"); + brc20.set_inscribe_to(pubKey.bytes.data(), pubKey.bytes.size()); + + auto& changeOut = *signing.add_outputs(); + changeOut.set_value(forFeeAmount); + changeOut.mutable_builder()->mutable_p2wpkh()->set_pubkey(pubKey.bytes.data(), pubKey.bytes.size()); + + Proto::SigningInput legacy; + *legacy.mutable_signing_v2() = signing; + + Proto::SigningOutput output; + ANY_SIGN(legacy, TWCoinTypeBitcoin); + + EXPECT_EQ(output.error(), Common::Proto::OK); + ASSERT_TRUE(output.has_signing_result_v2()); + EXPECT_EQ(output.signing_result_v2().error(), BitcoinV2::Proto::Error::OK); + EXPECT_EQ(hex(output.signing_result_v2().encoded()), "02000000000101089098890d2653567b9e8df2d1fbe5c3c8bf1910ca7184e301db0ad3b495c88e0100000000ffffffff02581b000000000000225120e8b706a97732e705e22ae7710703e7f589ed13c636324461afa443016134cc051040000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d02483045022100a44aa28446a9a886b378a4a65e32ad9a3108870bd725dc6105160bed4f317097022069e9de36422e4ce2e42b39884aa5f626f8f94194d1013007d5a1ea9220a06dce0121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000"); + EXPECT_EQ(hex(output.signing_result_v2().txid()), "797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1"); + + // Successfully broadcasted: https://www.blockchain.com/explorer/transactions/btc/797d17d47ae66e598341f9dfdea020b04d4017dcf9cc33f0e51f7a6082171fb1 +} + TEST(BitcoinSigning, SignBRC20TransferReveal) { auto privateKey = parse_hex("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129"); auto dustSatoshi = 546; @@ -367,7 +417,7 @@ TEST(BitcoinSigning, SignBRC20TransferReveal) { auto pubKey = key.getPublicKey(TWPublicKeyTypeSECP256k1); auto utxoPubKeyHash = Hash::ripemd(Hash::sha256(pubKey.bytes)); auto inputP2wpkh = TW::Bitcoin::Script::buildPayToWitnessPublicKeyHash(utxoPubKeyHash); - auto outputInscribe = TW::Bitcoin::Script::buildBRC20InscribeTransfer("oadf", 20, pubKey.bytes); + auto outputInscribe = TW::Bitcoin::Script::buildBRC20InscribeTransfer("oadf", "20", pubKey.bytes); Proto::SigningInput input; input.set_is_it_brc_operation(true); @@ -424,7 +474,7 @@ TEST(BitcoinSigning, SignBRC20TransferInscription) { auto utxoPubKeyHashBob = Hash::ripemd(Hash::sha256(parse_hex("02f453bb46e7afc8796a9629e89e07b5cb0867e9ca340b571e7bcc63fc20c43f2e"))); auto inputP2wpkh = TW::Bitcoin::Script::buildPayToWitnessPublicKeyHash(utxoPubKeyHash); auto outputP2wpkh = TW::Bitcoin::Script::buildPayToWitnessPublicKeyHash(utxoPubKeyHashBob); - auto outputInscribe = TW::Bitcoin::Script::buildBRC20InscribeTransfer("oadf", 20, pubKey.bytes); + auto outputInscribe = TW::Bitcoin::Script::buildBRC20InscribeTransfer("oadf", "20", pubKey.bytes); Proto::SigningInput input; input.set_is_it_brc_operation(true); @@ -576,134 +626,6 @@ TEST(BitcoinSigning, SignNftInscriptionReveal) { ASSERT_EQ(result.substr(292, result.size() - 292), expectedHex.substr(292, result.size() - 292)); } -TEST(BitcoinSigning, PlanAndSignBrc20) { - auto privateKey = parse_hex("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129"); - auto publicKey = parse_hex("030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb"); - - // Construct a `BitcoinV2.Proto.ComposePlan` message. - - auto dustSatoshis = 546; - auto ticker = "oadf"; - auto tokensAmount = 20; - auto feePerVb = 25; - - auto txId1 = parse_hex("181c84965c9ea86a5fac32fdbd5f73a21a7a9e749fb6ab97e273af2329f6b911"); - std::reverse(begin(txId1), end(txId1)); - - BitcoinV2::Proto::Input tx1; - tx1.set_txid(txId1.data(), (int)txId1.size()); - tx1.set_vout(0); - tx1.set_value(ONE_BTC); - tx1.set_sighash_type(Utxo::Proto::SighashType::All); - tx1.mutable_builder()->set_p2wpkh(publicKey.data(), (int)publicKey.size()); - - auto txId2 = parse_hex("858e450a1da44397bde05ca2f8a78510d74c623cc2f69736a8b3fbfadc161f6e"); - std::reverse(begin(txId2), end(txId2)); - - BitcoinV2::Proto::Input tx2; - tx2.set_txid(txId2.data(), (int)txId2.size()); - tx2.set_vout(0); - tx2.set_value(2 * ONE_BTC); - tx2.set_sighash_type(Utxo::Proto::SighashType::All); - tx2.mutable_builder()->set_p2wpkh(publicKey.data(), (int)publicKey.size()); - - BitcoinV2::Proto::Output taggedOutput; - taggedOutput.set_value(dustSatoshis); - taggedOutput.mutable_builder()->mutable_p2wpkh()->set_pubkey(publicKey.data(), (int)publicKey.size()); - - BitcoinV2::Proto::Output changeOutput; - // Will be set by the library. - changeOutput.set_value(0); - changeOutput.mutable_builder()->mutable_p2wpkh()->set_pubkey(publicKey.data(), (int)publicKey.size()); - - BitcoinV2::Proto::Input_InputBrc20Inscription brc20Inscription; - brc20Inscription.set_one_prevout(false); - brc20Inscription.set_inscribe_to(publicKey.data(), (int)publicKey.size()); - brc20Inscription.set_ticker(ticker); - brc20Inscription.set_transfer_amount(tokensAmount); - - BitcoinV2::Proto::ComposePlan composePlan; - auto& composeBrc20 = *composePlan.mutable_brc20(); - composeBrc20.set_private_key(privateKey.data(), (int)privateKey.size()); - *composeBrc20.add_inputs() = tx1; - *composeBrc20.add_inputs() = tx2; - composeBrc20.set_input_selector(Utxo::Proto::InputSelector::SelectAscending); - *composeBrc20.mutable_tagged_output() = taggedOutput; - *composeBrc20.mutable_inscription() = brc20Inscription; - composeBrc20.set_fee_per_vb(feePerVb); - *composeBrc20.mutable_change_output() = changeOutput; - composeBrc20.set_disable_change_output(false); - - // Construct a `Bitcoin.Proto.SigningInput` message with `planning_v2` field only. - Proto::SigningInput input; - *input.mutable_planning_v2() = composePlan; - - // Plan the transaction using standard `TWAnySignerPlan`. - Proto::TransactionPlan plan; - ANY_PLAN(input, plan, TWCoinTypeBitcoin); - - // Check the result Planning V2. - EXPECT_TRUE(plan.has_planning_result_v2()); - const auto& planV2 = plan.planning_result_v2(); - EXPECT_EQ(planV2.error(), BitcoinV2::Proto::Error::OK); - EXPECT_TRUE(planV2.has_brc20()); - const auto& brc20Plan = planV2.brc20(); - - // Check the result Commit `SigningInput`. - auto commitOutputAmount = 3846; - EXPECT_TRUE(brc20Plan.has_commit()); - const auto& brc20Commit = brc20Plan.commit(); - EXPECT_EQ(brc20Commit.version(), 2); - EXPECT_EQ(brc20Commit.inputs_size(), 1); - EXPECT_EQ(brc20Commit.outputs_size(), 2); - // Change output generation is disabled, included in `commit.outputs`. - EXPECT_FALSE(brc20Commit.has_change_output()); - EXPECT_EQ(brc20Commit.outputs(0).value(), commitOutputAmount); - EXPECT_EQ(brc20Commit.outputs(0).builder().brc20_inscribe().ticker(), ticker); - EXPECT_EQ(brc20Commit.outputs(0).builder().brc20_inscribe().transfer_amount(), tokensAmount); - // Change: tx1 value - out1 value - EXPECT_EQ(brc20Commit.outputs(1).value(), ONE_BTC - commitOutputAmount - 3175); - - // Check the result Reveal `SigningInput`. - EXPECT_TRUE(brc20Plan.has_reveal()); - const auto& brc20Reveal = brc20Plan.reveal(); - EXPECT_EQ(brc20Reveal.version(), 2); - EXPECT_EQ(brc20Reveal.inputs_size(), 1); - EXPECT_EQ(brc20Reveal.outputs_size(), 1); - // Change output generation is disabled, included in `commit.outputs`. - EXPECT_FALSE(brc20Reveal.has_change_output()); - EXPECT_EQ(brc20Reveal.inputs(0).value(), commitOutputAmount); - EXPECT_EQ(brc20Reveal.inputs(0).builder().brc20_inscribe().ticker(), ticker); - EXPECT_EQ(brc20Reveal.inputs(0).builder().brc20_inscribe().transfer_amount(), tokensAmount); - EXPECT_EQ(brc20Reveal.outputs(0).value(), dustSatoshis); - - // Construct a `Bitcoin.Proto.SigningInput` message with `signing_v2` (Commit) field only. - { - Proto::SigningInput commitInput; - *commitInput.mutable_signing_v2() = brc20Commit; - - Proto::SigningOutput output; - ANY_SIGN(commitInput, TWCoinTypeBitcoin); - EXPECT_EQ(output.error(), Common::Proto::SigningError::OK); - EXPECT_TRUE(output.has_signing_result_v2()); - EXPECT_EQ(hex(output.signing_result_v2().encoded()), "0200000000010111b9f62923af73e297abb69f749e7a1aa2735fbdfd32ac5f6aa89e5c96841c180000000000ffffffff02060f000000000000225120e8b706a97732e705e22ae7710703e7f589ed13c636324461afa443016134cc0593c5f50500000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d02483045022100912004efb9b4e8368ba00d6bfbdfde22a43b037f64ae09d79aac030c77edbc2802206c5702646eadea2274c4aafee99c12b5054cb60da18c21f67f5f3003a318112d0121030f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000"); - } - - // Construct a `Bitcoin.Proto.SigningInput` message with `signing_v2` (Reveal) field only. - { - Proto::SigningInput revealInput; - *revealInput.mutable_signing_v2() = brc20Reveal; - // `schnorr` is used to sign the Reveal transaction. - revealInput.mutable_signing_v2()->set_dangerous_use_fixed_schnorr_rng(true); - - Proto::SigningOutput output; - ANY_SIGN(revealInput, TWCoinTypeBitcoin); - EXPECT_EQ(output.error(), Common::Proto::SigningError::OK); - EXPECT_TRUE(output.has_signing_result_v2()); - EXPECT_EQ(hex(output.signing_result_v2().encoded()), "0200000000010173711b50d9adb30fdc51231cd56a95b3b627453add775c56188449f2dccaef250000000000ffffffff012202000000000000160014e311b8d6ddff856ce8e9a4e03bc6d4fe5050a83d03405cd7fb811a8ebcc55ac791321243a6d1a4089abc548d93288dfe5870f6af7f96b0cb0c7c41c0126791179e8c190d8fecf9bdc4cc740ec3e7d6a43b1b0a345f155b0063036f7264010118746578742f706c61696e3b636861727365743d7574662d3800377b2270223a226272632d3230222c226f70223a227472616e73666572222c227469636b223a226f616466222c22616d74223a223230227d6821c00f209b6ada5edb42c77fd2bc64ad650ae38314c8f451f3e36d80bc8e26f132cb00000000"); - } -} - TEST(BitcoinSigning, SignP2PKH) { auto input = buildInputP2PKH(); From d02cb7d02243a32d44a1ac1378e73df3b68a11e4 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Thu, 25 Jan 2024 16:04:50 +0700 Subject: [PATCH 043/128] feat(ZetaChain): Add support for NativeZetaChain (#3669) * feat(ZetaChain): Add `new-cosmos-chain` command * feat(ZetaChain): Add `NativeZetaChain` mainnet * feat(ZetaChain): Add support for custom Cosmos chain * feat(ZetaChain): Add `SignerInfo` to the Cosmos.proto protocol * feat(ZetaChain): Fix zetachain params in registry.json * feat(ZetaChain): Add mobile tests * [CI] Trigger CI * feat(ZetaChain): Update JSON Public Key Type * feat(ZetaChain): Fix `accountPath` in `registry.json` * feat(ZetaChain): Fix rustfmt * feat(ZetaChain): Slightly refactor NativeEvmos and NativeInjective public key types --- .../blockchains/CoinAddressDerivationTests.kt | 1 + .../TestNativeZetaChainAddress.kt | 28 ++++ .../TestNativeZetaChainSigner.kt | 71 ++++++++++ codegen-v2/src/codegen/cpp/mod.rs | 8 ++ .../src/codegen/cpp/new_cosmos_chain.rs | 20 +++ .../cpp/tw_coin_type_tests_generator.rs | 13 +- .../codegen/rust/coin_integration_tests.rs | 33 ++++- codegen-v2/src/codegen/rust/mod.rs | 1 + .../src/codegen/rust/new_cosmos_chain.rs | 16 +++ .../integration_tests/cosmos_address_tests.rs | 69 +++++++++ .../integration_tests/mod_address.rs | 5 + codegen-v2/src/codegen/template_generator.rs | 1 + codegen-v2/src/main.rs | 14 ++ codegen-v2/src/registry.rs | 2 + docs/registry.md | 1 + include/TrustWalletCore/TWCoinType.h | 1 + .../core/test/CoinAddressDerivationTests.kt | 1 + registry.json | 30 ++++ rust/Cargo.lock | 2 + rust/chains/tw_binance/src/address.rs | 10 +- rust/chains/tw_binance/src/compiler.rs | 4 +- rust/chains/tw_binance/src/context.rs | 9 +- rust/chains/tw_binance/src/signer.rs | 3 +- rust/chains/tw_greenfield/src/address.rs | 12 +- rust/chains/tw_greenfield/src/compiler.rs | 3 +- rust/chains/tw_greenfield/src/context.rs | 9 +- .../tw_greenfield/src/modules/tx_builder.rs | 4 +- rust/chains/tw_greenfield/src/public_key.rs | 23 ++- rust/chains/tw_native_evmos/Cargo.toml | 1 + rust/chains/tw_native_evmos/src/context.rs | 7 +- .../src/ethermint_public_key.rs | 66 ++++----- rust/chains/tw_native_injective/Cargo.toml | 1 + .../chains/tw_native_injective/src/context.rs | 7 +- rust/chains/tw_native_injective/src/entry.rs | 6 +- .../src/injective_public_key.rs | 60 ++++---- rust/chains/tw_thorchain/src/entry.rs | 6 +- .../tests/chains/cosmos/cosmos_sign.rs | 65 +++++++++ rust/tw_any_coin/tests/chains/mod.rs | 1 + .../native_evmos/native_evmos_address.rs | 15 ++ .../tw_any_coin/tests/chains/zetachain/mod.rs | 6 + .../chains/zetachain/zetachain_address.rs | 87 ++++++++++++ .../tests/chains/zetachain/zetachain_sign.rs | 75 ++++++++++ .../tests/coin_address_derivation_test.rs | 1 + rust/tw_cosmos_sdk/src/address.rs | 19 +-- rust/tw_cosmos_sdk/src/context.rs | 11 +- .../src/hasher/keccak256_hasher.rs | 19 --- rust/tw_cosmos_sdk/src/hasher/mod.rs | 14 -- .../tw_cosmos_sdk/src/hasher/sha256_hasher.rs | 19 --- rust/tw_cosmos_sdk/src/lib.rs | 1 - .../src/modules/compiler/json_preimager.rs | 9 +- .../modules/compiler/protobuf_preimager.rs | 12 +- .../src/modules/compiler/tw_compiler.rs | 16 ++- .../src/modules/signer/tw_signer.rs | 6 +- rust/tw_cosmos_sdk/src/modules/tx_builder.rs | 132 ++++++++++-------- rust/tw_cosmos_sdk/src/public_key/mod.rs | 22 ++- .../tw_cosmos_sdk/src/public_key/secp256k1.rs | 59 +++++--- rust/tw_proto/src/lib.rs | 10 +- src/proto/Cosmos.proto | 40 +++++- .../Blockchains/NativeZetaChainTests.swift | 58 ++++++++ swift/Tests/CoinAddressDerivationTests.swift | 3 + .../NativeZetaChain/TWCoinTypeTests.cpp | 29 ++++ tests/common/CoinAddressDerivationTests.cpp | 3 + 62 files changed, 991 insertions(+), 289 deletions(-) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativezetachain/TestNativeZetaChainAddress.kt create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativezetachain/TestNativeZetaChainSigner.kt create mode 100644 codegen-v2/src/codegen/cpp/new_cosmos_chain.rs create mode 100644 codegen-v2/src/codegen/rust/new_cosmos_chain.rs create mode 100644 codegen-v2/src/codegen/rust/templates/integration_tests/cosmos_address_tests.rs create mode 100644 codegen-v2/src/codegen/rust/templates/integration_tests/mod_address.rs create mode 100644 rust/tw_any_coin/tests/chains/zetachain/mod.rs create mode 100644 rust/tw_any_coin/tests/chains/zetachain/zetachain_address.rs create mode 100644 rust/tw_any_coin/tests/chains/zetachain/zetachain_sign.rs delete mode 100644 rust/tw_cosmos_sdk/src/hasher/keccak256_hasher.rs delete mode 100644 rust/tw_cosmos_sdk/src/hasher/mod.rs delete mode 100644 rust/tw_cosmos_sdk/src/hasher/sha256_hasher.rs create mode 100644 swift/Tests/Blockchains/NativeZetaChainTests.swift create mode 100644 tests/chains/NativeZetaChain/TWCoinTypeTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index 1436bb783cf..ed19bae335c 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -151,5 +151,6 @@ class CoinAddressDerivationTests { SEI -> assertEquals("sei142j9u5eaduzd7faumygud6ruhdwme98qagm0sj", address) INTERNETCOMPUTER -> assertEquals("b9a13d974ee9db036d5abc5b66ace23e513cb5676f3996626c7717c339a3ee87", address) TIA -> assertEquals("celestia142j9u5eaduzd7faumygud6ruhdwme98qpwmfv7", address) + NATIVEZETACHAIN -> assertEquals("zeta13u6g7vqgw074mgmf2ze2cadzvkz9snlwywj304", address) } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativezetachain/TestNativeZetaChainAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativezetachain/TestNativeZetaChainAddress.kt new file mode 100644 index 00000000000..93a2fbd2a3a --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativezetachain/TestNativeZetaChainAddress.kt @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +package com.trustwallet.core.app.blockchains.nativezetachain + +import com.trustwallet.core.app.utils.toHex +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.* + +class TestNativeZetaChainAddress { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testAddress() { + val key = PrivateKey("8d2a3bd62d300a148c89dc8635f87b7a24a951bd1c4e78675fe40e1a640d46ed".toHexByteArray()) + val pubKey = key.getPublicKeySecp256k1(false) + val address = AnyAddress(pubKey, CoinType.NATIVEZETACHAIN) + val expected = AnyAddress("zeta14py36sx57ud82t9yrks9z6hdsrpn5x6kmxs0ne", CoinType.NATIVEZETACHAIN) + + assertEquals(address.description(), expected.description()) + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativezetachain/TestNativeZetaChainSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativezetachain/TestNativeZetaChainSigner.kt new file mode 100644 index 00000000000..2dd00e4f2f8 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nativezetachain/TestNativeZetaChainSigner.kt @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +package com.trustwallet.core.app.blockchains.nativezetachain + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.java.AnySigner +import wallet.core.jni.AnyAddress +import wallet.core.jni.CoinType +import wallet.core.jni.PrivateKey +import wallet.core.jni.proto.Cosmos + +class TestNativeZetaChainSigner { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun NativeZetaChainTransactionSigning() { + val key = PrivateKey("8d2a3bd62d300a148c89dc8635f87b7a24a951bd1c4e78675fe40e1a640d46ed".toHexByteArray()) + val publicKey = key.getPublicKeySecp256k1(false) + val from = AnyAddress(publicKey, CoinType.NATIVEZETACHAIN).description() + + val transferAmount = Cosmos.Amount.newBuilder().apply { + // 0.3 ZETA + amount = "300000000000000000" + denom = "azeta" + }.build() + + val message = Cosmos.Message.newBuilder().apply { + sendCoinsMessage = Cosmos.Message.Send.newBuilder().apply { + fromAddress = from + toAddress = "zeta1cscf4ldnkkz7f0wpveur6dpd0d6p2zxnsuu70y" + addAllAmounts(listOf(transferAmount)) + }.build() + }.build() + + val transferFee = Cosmos.Fee.newBuilder().apply { + gas = 200000 + }.build() + + val signingInput = Cosmos.SigningInput.newBuilder().apply { + signingMode = Cosmos.SigningMode.Protobuf + accountNumber = 2726346 + chainId = "athens_7001-1" + sequence = 2 + fee = transferFee + privateKey = ByteString.copyFrom(key.data()) + txHasher = Cosmos.TxHasher.Keccak256 + signerInfo = Cosmos.SignerInfo.newBuilder().apply { + // Zetachain requires a compressed public key to sign a transaction, + // however an uncompressed public key is used to generate address. + publicKeyType = Cosmos.SignerPublicKeyType.Secp256k1 + jsonType = "ethermint/PubKeyEthSecp256k1" + protobufType = "/ethermint.crypto.v1.ethsecp256k1.PubKey" + }.build() + addAllMessages(listOf(message)) + }.build() + + val output = AnySigner.sign(signingInput, CoinType.NATIVEZETACHAIN, Cosmos.SigningOutput.parser()) + + // Successfully broadcasted (testnet): + // https://explorer.zetachain.com/cosmos/tx/A2FC8816657856ED274C4418C3CAEAEE645561275F6C63AB5F8B1DCFB37341A0 + assertEquals(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CpoBCpcBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEncKK3pldGExNHB5MzZzeDU3dWQ4MnQ5eXJrczl6Nmhkc3JwbjV4NmtteHMwbmUSK3pldGExY3NjZjRsZG5ra3o3ZjB3cHZldXI2ZHBkMGQ2cDJ6eG5zdXU3MHkaGwoFYXpldGESEjMwMDAwMDAwMDAwMDAwMDAwMBJhClkKTwooL2V0aGVybWludC5jcnlwdG8udjEuZXRoc2VjcDI1NmsxLlB1YktleRIjCiECho5+FjRBfbKt/Z/jggW/oP6gGJin/TBWXRP3BWo3wGUSBAoCCAEYAhIEEMCaDBpAgGvqca0w2N9wnHnnxS9HiVud4aQ9lNCumzgNIW6wOR4kvPScacGS1G3kwCw7wyI2NJL8M1eVYjafFIt2FpKl3w==\"}") + } +} diff --git a/codegen-v2/src/codegen/cpp/mod.rs b/codegen-v2/src/codegen/cpp/mod.rs index 3ddf0131626..1ef7188d86f 100644 --- a/codegen-v2/src/codegen/cpp/mod.rs +++ b/codegen-v2/src/codegen/cpp/mod.rs @@ -9,6 +9,7 @@ use std::path::PathBuf; pub mod blockchain_dispatcher_generator; pub mod entry_generator; pub mod new_blockchain; +pub mod new_cosmos_chain; pub mod new_evmchain; pub mod tw_any_address_tests_generator; pub mod tw_any_signer_tests_generator; @@ -41,3 +42,10 @@ pub fn coin_integration_tests_directory(coin: &CoinItem) -> PathBuf { .join("chains") .join(coin.coin_type()) } + +pub fn cosmos_coin_integration_tests_directory(coin: &CoinItem) -> PathBuf { + integration_tests_directory() + .join("chains") + .join("Cosmos") + .join(coin.coin_type()) +} diff --git a/codegen-v2/src/codegen/cpp/new_cosmos_chain.rs b/codegen-v2/src/codegen/cpp/new_cosmos_chain.rs new file mode 100644 index 00000000000..08c4406f994 --- /dev/null +++ b/codegen-v2/src/codegen/cpp/new_cosmos_chain.rs @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::codegen::cpp::tw_coin_address_derivation_tests_generator::CoinAddressDerivationTestsGenerator; +use crate::codegen::cpp::tw_coin_type_generator::TWCoinTypeGenerator; +use crate::codegen::cpp::tw_coin_type_tests_generator::TWCoinTypeTestsGenerator; +use crate::registry::CoinItem; +use crate::Result; + +pub fn new_cosmos_chain(coin: &CoinItem) -> Result<()> { + // Add the new coin type to the `TWCoinType` enum. + TWCoinTypeGenerator::generate_coin_type_variant(coin)?; + + // Add integration tests. + TWCoinTypeTestsGenerator::generate(coin)?; + CoinAddressDerivationTestsGenerator::generate_new_coin_type_case(coin)?; + + Ok(()) +} diff --git a/codegen-v2/src/codegen/cpp/tw_coin_type_tests_generator.rs b/codegen-v2/src/codegen/cpp/tw_coin_type_tests_generator.rs index 67edddac4ff..a3f9f50c5da 100644 --- a/codegen-v2/src/codegen/cpp/tw_coin_type_tests_generator.rs +++ b/codegen-v2/src/codegen/cpp/tw_coin_type_tests_generator.rs @@ -2,7 +2,9 @@ // // Copyright © 2017 Trust Wallet. -use crate::codegen::cpp::coin_integration_tests_directory; +use crate::codegen::cpp::{ + coin_integration_tests_directory, cosmos_coin_integration_tests_directory, +}; use crate::codegen::template_generator::TemplateGenerator; use crate::registry::CoinItem; use crate::Result; @@ -20,6 +22,15 @@ pub struct TWCoinTypeTestsGenerator; impl TWCoinTypeTestsGenerator { pub fn generate(coin: &CoinItem) -> Result<()> { let coin_tests_dir = coin_integration_tests_directory(coin); + Self::generate_at(coin, coin_tests_dir) + } + + pub fn generate_cosmos(coin: &CoinItem) -> Result<()> { + let cosmos_coin_tests_dir = cosmos_coin_integration_tests_directory(coin); + Self::generate_at(coin, cosmos_coin_tests_dir) + } + + fn generate_at(coin: &CoinItem, coin_tests_dir: PathBuf) -> Result<()> { let tw_coin_type_tests_path = coin_tests_dir.join("TWCoinTypeTests.cpp"); fs::create_dir(coin_tests_dir)?; diff --git a/codegen-v2/src/codegen/rust/coin_integration_tests.rs b/codegen-v2/src/codegen/rust/coin_integration_tests.rs index 2743d546066..6c4ce2b4e0f 100644 --- a/codegen-v2/src/codegen/rust/coin_integration_tests.rs +++ b/codegen-v2/src/codegen/rust/coin_integration_tests.rs @@ -12,8 +12,11 @@ use std::fs; use std::path::PathBuf; const ADDRESS_TESTS_TEMPLATE: &str = include_str!("templates/integration_tests/address_tests.rs"); +const COSMOS_ADDRESS_TESTS_TEMPLATE: &str = + include_str!("templates/integration_tests/cosmos_address_tests.rs"); const COMPILE_TESTS_TEMPLATE: &str = include_str!("templates/integration_tests/compile_tests.rs"); const MOD_TESTS_TEMPLATE: &str = include_str!("templates/integration_tests/mod.rs"); +const MOD_ADDRESS_TESTS_TEMPLATE: &str = include_str!("templates/integration_tests/mod_address.rs"); const SIGN_TESTS_TEMPLATE: &str = include_str!("templates/integration_tests/sign_tests.rs"); pub fn chains_integration_tests_directory() -> PathBuf { @@ -49,10 +52,28 @@ impl CoinIntegrationTests { fs::create_dir_all(&blockchain_tests_path)?; self.list_blockchain_in_chains_mod()?; - self.create_address_tests()?; + self.create_address_tests(ADDRESS_TESTS_TEMPLATE)?; self.create_compile_tests()?; self.create_sign_tests()?; - self.create_chain_tests_mod_rs()?; + self.create_chain_tests_mod_rs(MOD_TESTS_TEMPLATE)?; + + Ok(blockchain_tests_path) + } + + /// For a Cosmos chain, it's enough to create address tests only, but with additional Bech32 prefix tests. + pub fn create_cosmos(self) -> Result { + let blockchain_tests_path = self.coin_tests_directory(); + if blockchain_tests_path.exists() { + println!("[SKIP] integration tests already exists: {blockchain_tests_path:?}"); + return Ok(blockchain_tests_path); + } + + fs::create_dir_all(&blockchain_tests_path)?; + + self.list_blockchain_in_chains_mod()?; + self.create_address_tests(COSMOS_ADDRESS_TESTS_TEMPLATE)?; + // `mod.rs` should contain `{COIN_TYPE}_address` module only. + self.create_chain_tests_mod_rs(MOD_ADDRESS_TESTS_TEMPLATE)?; Ok(blockchain_tests_path) } @@ -61,14 +82,14 @@ impl CoinIntegrationTests { coin_integration_tests_directory(&self.coin.id) } - fn create_address_tests(&self) -> Result<()> { + fn create_address_tests(&self, template: &'static str) -> Result<()> { let coin_id = self.coin.id.as_str(); let address_tests_path = self .coin_tests_directory() .join(format!("{coin_id}_address.rs")); println!("[ADD] {address_tests_path:?}"); - TemplateGenerator::new(ADDRESS_TESTS_TEMPLATE) + TemplateGenerator::new(template) .write_to(address_tests_path) .with_default_patterns(&self.coin) .write() @@ -100,11 +121,11 @@ impl CoinIntegrationTests { .write() } - fn create_chain_tests_mod_rs(&self) -> Result<()> { + fn create_chain_tests_mod_rs(&self, template: &'static str) -> Result<()> { let blockchain_tests_mod_path = self.coin_tests_directory().join("mod.rs"); println!("[ADD] {blockchain_tests_mod_path:?}"); - TemplateGenerator::new(MOD_TESTS_TEMPLATE) + TemplateGenerator::new(template) .write_to(blockchain_tests_mod_path) .with_default_patterns(&self.coin) .write() diff --git a/codegen-v2/src/codegen/rust/mod.rs b/codegen-v2/src/codegen/rust/mod.rs index 5364b415fee..8905e725a32 100644 --- a/codegen-v2/src/codegen/rust/mod.rs +++ b/codegen-v2/src/codegen/rust/mod.rs @@ -12,6 +12,7 @@ pub mod coin_crate; pub mod coin_integration_tests; pub mod coin_registry_manifest_generator; pub mod new_blockchain; +pub mod new_cosmos_chain; pub mod new_evmchain; pub mod toml_editor; diff --git a/codegen-v2/src/codegen/rust/new_cosmos_chain.rs b/codegen-v2/src/codegen/rust/new_cosmos_chain.rs new file mode 100644 index 00000000000..96151ff6446 --- /dev/null +++ b/codegen-v2/src/codegen/rust/new_cosmos_chain.rs @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::codegen::rust::coin_address_derivation_test_generator::CoinAddressDerivationTestGenerator; +use crate::codegen::rust::coin_integration_tests::CoinIntegrationTests; +use crate::registry::CoinItem; +use crate::Result; + +pub fn new_cosmos_chain(coin: &CoinItem) -> Result<()> { + // Create integration tests. + CoinIntegrationTests::new(coin.clone()).create_cosmos()?; + CoinAddressDerivationTestGenerator::generate_new_coin_type_case(coin)?; + + Ok(()) +} diff --git a/codegen-v2/src/codegen/rust/templates/integration_tests/cosmos_address_tests.rs b/codegen-v2/src/codegen/rust/templates/integration_tests/cosmos_address_tests.rs new file mode 100644 index 00000000000..6bf6079ac9d --- /dev/null +++ b/codegen-v2/src/codegen/rust/templates/integration_tests/cosmos_address_tests.rs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_any_coin::test_utils::address_utils::{ + test_address_bech32_is_valid, test_address_create_bech32_with_public_key, + test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, + AddressBech32IsValid, AddressCreateBech32WithPublicKey, +}; +use tw_coin_registry::coin_type::CoinType; +use tw_keypair::tw::PublicKeyType; + +#[test] +fn test_{COIN_ID}_address_normalization() { + test_address_normalization(CoinType::{COIN_TYPE}, "DENORMALIZED", "EXPECTED"); +} + +#[test] +fn test_{COIN_ID}_address_is_valid() { + test_address_valid(CoinType::{COIN_TYPE}, "VALID {COIN_TYPE} ADDRESS"); +} + +#[test] +fn test_{COIN_ID}_address_invalid() { + test_address_invalid(CoinType::{COIN_TYPE}, "INVALID ADDRESS"); + // Cosmos has a different `hrp`. + test_address_invalid(CoinType::Cosmos, "VALID {COIN_TYPE} ADDRESS"); +} + +#[test] +fn test_{COIN_ID}_address_get_data() { + test_address_get_data(CoinType::{COIN_TYPE}, "ADDRESS", "HEX(DATA)"); +} + +#[test] +fn test_{COIN_ID}_is_valid_bech32() { + // {COIN_TYPE} address must be valid if its Base32 prefix passed. + test_address_bech32_is_valid(AddressBech32IsValid { + coin: CoinType::{COIN_TYPE}, + address: "{COIN_TYPE} ADDRESS", + hrp: "{HRP}", + }); + // {COIN_TYPE} address must be valid for the standard Cosmos hub if its Base32 prefix passed. + test_address_bech32_is_valid(AddressBech32IsValid { + coin: CoinType::Cosmos, + address: "{COIN_TYPE} ADDRESS", + hrp: "{HRP}", + }); + // Cosmos address must be valid with "cosmos" hrp. + test_address_bech32_is_valid(AddressBech32IsValid { + coin: CoinType::{COIN_TYPE}, + address: "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02", + hrp: "cosmos", + }); +} + +#[test] +fn test_any_address_create_bech32_with_public_key() { + test_address_create_bech32_with_public_key(AddressCreateBech32WithPublicKey { + // TODO consider using `CoinType::{COIN_TYPE}` if the chain's `addressHasher` is different from Cosmos's. + coin: CoinType::Cosmos, + private_key: "PRIVATE_KEY", + // TODO consider using another `PublicKeyType` if the chain's `publicKeyType` is different from Cosmos's. + public_key_type: PublicKeyType::Secp256k1, + hrp: "{HRP}", + expected: "{COIN_TYPE} ADDRESS", + }); +} + diff --git a/codegen-v2/src/codegen/rust/templates/integration_tests/mod_address.rs b/codegen-v2/src/codegen/rust/templates/integration_tests/mod_address.rs new file mode 100644 index 00000000000..6f501e180bc --- /dev/null +++ b/codegen-v2/src/codegen/rust/templates/integration_tests/mod_address.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +mod {COIN_ID}_address; diff --git a/codegen-v2/src/codegen/template_generator.rs b/codegen-v2/src/codegen/template_generator.rs index 684116c514d..724066d47ac 100644 --- a/codegen-v2/src/codegen/template_generator.rs +++ b/codegen-v2/src/codegen/template_generator.rs @@ -43,6 +43,7 @@ impl TemplateGenerator { .add_pattern("{DECIMALS}", coin.decimals) .add_pattern("{P2PKH_PREFIX}", coin.p2pkh_prefix) .add_pattern("{P2SH_PREFIX}", coin.p2sh_prefix) + .add_pattern("{HRP}", coin.hrp.as_str()) .add_pattern("{STATIC_PREFIX}", coin.static_prefix) .add_pattern("{EXPLORER_URL}", &coin.explorer.url) .add_pattern("{EXPLORER_TX_PATH}", &coin.explorer.tx_path) diff --git a/codegen-v2/src/main.rs b/codegen-v2/src/main.rs index 9bd35a8b9ee..53fd140cc65 100644 --- a/codegen-v2/src/main.rs +++ b/codegen-v2/src/main.rs @@ -21,6 +21,7 @@ fn main() -> Result<()> { "new-blockchain-rust" => new_blockchain_rust(&args[2..]), "new-blockchain" => new_blockchain(&args[2..]), "new-evmchain" => new_evmchain(&args[2..]), + "new-cosmos-chain" => new_cosmos_chain(&args[2..]), "swift" => generate_swift_bindings(), _ => Err(Error::InvalidCommand), } @@ -64,6 +65,19 @@ fn new_evmchain(args: &[String]) -> Result<()> { Ok(()) } +fn new_cosmos_chain(args: &[String]) -> Result<()> { + let coin_str = args.iter().next().ok_or_else(|| Error::InvalidCommand)?; + let coin_id = CoinId::new(coin_str.clone())?; + let coin_item = read_coin_from_registry(&coin_id)?; + + println!("New '{coin_str}' Cosmos chain template requested"); + + rust::new_cosmos_chain::new_cosmos_chain(&coin_item)?; + cpp::new_cosmos_chain::new_cosmos_chain(&coin_item)?; + + Ok(()) +} + fn generate_swift_bindings() -> Result<()> { // NOTE: The paths will be configurable, eventually. const OUT_DIR: &str = "bindings/"; diff --git a/codegen-v2/src/registry.rs b/codegen-v2/src/registry.rs index 17eda663c7c..853f0d2249e 100644 --- a/codegen-v2/src/registry.rs +++ b/codegen-v2/src/registry.rs @@ -47,6 +47,8 @@ pub struct CoinItem { #[serde(rename = "staticPrefix")] #[serde(default)] pub static_prefix: u8, + #[serde(default)] + pub hrp: String, pub explorer: CoinExplorer, } diff --git a/docs/registry.md b/docs/registry.md index 490965bc8b1..558ee6febf3 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -123,6 +123,7 @@ This list is generated from [./registry.json](../registry.json) | 10002020 | Ronin | RON | | | | 10002222 | KavaEvm | KAVA | | | | 10004689 | IoTeX EVM | IOTX | | | +| 10007000 | NativeZetaChain | ZETA | | | | 10007700 | NativeCanto | CANTO | | | | 10008217 | Klaytn | KLAY | | | | 10009000 | Avalanche C-Chain | AVAX | | | diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index f18f8ff9c67..64e5b5f9225 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -178,6 +178,7 @@ enum TWCoinType { TWCoinTypeInternetComputer = 223, TWCoinTypeTia = 21000118, TWCoinTypeMantaPacific = 169, + TWCoinTypeNativeZetaChain = 10007000, // end_of_tw_coin_type_marker_do_not_modify }; diff --git a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt index db177dc1b16..6a2a7f54b05 100644 --- a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt +++ b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt @@ -144,5 +144,6 @@ class CoinAddressDerivationTests { Sei -> "sei142j9u5eaduzd7faumygud6ruhdwme98qagm0sj" InternetComputer -> "b9a13d974ee9db036d5abc5b66ace23e513cb5676f3996626c7717c339a3ee87" Tia -> "celestia142j9u5eaduzd7faumygud6ruhdwme98qpwmfv7" + NativeZetaChain -> "zeta13u6g7vqgw074mgmf2ze2cadzvkz9snlwywj304" } } diff --git a/registry.json b/registry.json index 1e33dfc115b..2df0f2c23c6 100644 --- a/registry.json +++ b/registry.json @@ -4421,6 +4421,36 @@ "documentation": "https://docs.canto.io/" } }, + { + "id": "zetachain", + "name": "NativeZetaChain", + "displayName": "NativeZetaChain", + "coinId": 10007000, + "symbol": "ZETA", + "decimals": 18, + "blockchain": "Cosmos", + "chainId": "zetachain_7000-1", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "hrp": "zeta", + "addressHasher": "keccak256", + "explorer": { + "url": "https://explorer.zetachain.com", + "txPath": "/cosmos/tx/", + "accountPath": "/address/", + "sampleTx": "2DBB071DDD47985F4470A21E5943CE95D371AE4BDE2267E201D3553FB2BDCFDE", + "sampleAccount": "zeta14py36sx57ud82t9yrks9z6hdsrpn5x6kmxs0ne" + }, + "info": { + "url": "https://www.zetachain.com/", + "documentation": "https://www.zetachain.com/docs/" + } + }, { "id": "ton", "name": "TON", diff --git a/rust/Cargo.lock b/rust/Cargo.lock index efbc436e0d4..2f08868d5b9 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1940,6 +1940,7 @@ version = "0.1.0" dependencies = [ "tw_coin_entry", "tw_cosmos_sdk", + "tw_hash", "tw_keypair", "tw_memory", "tw_proto", @@ -1951,6 +1952,7 @@ version = "0.1.0" dependencies = [ "tw_coin_entry", "tw_cosmos_sdk", + "tw_hash", "tw_keypair", "tw_memory", "tw_proto", diff --git a/rust/chains/tw_binance/src/address.rs b/rust/chains/tw_binance/src/address.rs index ec4cffad8f3..3c718a97bd8 100644 --- a/rust/chains/tw_binance/src/address.rs +++ b/rust/chains/tw_binance/src/address.rs @@ -30,15 +30,7 @@ impl CoinAddress for BinanceAddress { } } -impl CosmosAddress for BinanceAddress { - fn from_str_with_coin(coin: &dyn CoinContext, addr: &str) -> AddressResult - where - Self: Sized, - { - let prefix = None; - Self::from_str_with_coin_and_prefix(coin, addr.to_string(), prefix) - } -} +impl CosmosAddress for BinanceAddress {} impl BinanceAddress { pub const VALIDATOR_HRP: &'static str = "bva"; diff --git a/rust/chains/tw_binance/src/compiler.rs b/rust/chains/tw_binance/src/compiler.rs index 8c9012d2910..754cdd854ed 100644 --- a/rust/chains/tw_binance/src/compiler.rs +++ b/rust/chains/tw_binance/src/compiler.rs @@ -71,7 +71,9 @@ impl BinanceCompiler { public_key, } = SingleSignaturePubkey::from_sign_pubkey_list(signatures, public_keys)?; let signature = BinanceSignature::try_from(signature.as_slice())?; - let public_key = Secp256PublicKey::from_bytes(coin, public_key.as_slice())?; + let public_key_params = None; + let public_key = + Secp256PublicKey::from_bytes(coin, public_key.as_slice(), public_key_params)?; let signature_bytes = signature.to_vec(); let signature_json = JsonSerializer::::serialize_signature( diff --git a/rust/chains/tw_binance/src/context.rs b/rust/chains/tw_binance/src/context.rs index 4cd308db000..58952365b63 100644 --- a/rust/chains/tw_binance/src/context.rs +++ b/rust/chains/tw_binance/src/context.rs @@ -4,18 +4,21 @@ use crate::address::BinanceAddress; use tw_cosmos_sdk::context::CosmosContext; -use tw_cosmos_sdk::hasher::sha256_hasher::Sha256Hasher; use tw_cosmos_sdk::private_key::secp256k1::Secp256PrivateKey; use tw_cosmos_sdk::public_key::secp256k1::Secp256PublicKey; use tw_cosmos_sdk::signature::secp256k1::Secp256k1Signature; +use tw_hash::hasher::Hasher; pub struct BinanceContext; impl CosmosContext for BinanceContext { type Address = BinanceAddress; - /// Binance Beacon chain uses `sha256` hash. - type TxHasher = Sha256Hasher; type PrivateKey = Secp256PrivateKey; type PublicKey = Secp256PublicKey; type Signature = Secp256k1Signature; + + /// Binance Beacon chain uses `sha256` hash. + fn default_tx_hasher() -> Hasher { + Hasher::Sha256 + } } diff --git a/rust/chains/tw_binance/src/signer.rs b/rust/chains/tw_binance/src/signer.rs index 7f6a7809e33..b17553e067e 100644 --- a/rust/chains/tw_binance/src/signer.rs +++ b/rust/chains/tw_binance/src/signer.rs @@ -39,7 +39,8 @@ impl BinanceSigner { let key_pair = secp256k1::KeyPair::try_from(input.private_key.as_ref())?; let signature = BinanceSignature::from(key_pair.sign(tx_hash)?); - let public_key = Secp256PublicKey::from_secp256k1_public_key(coin, key_pair.public())?; + let public_key = + Secp256PublicKey::from_secp256k1_public_key(coin.public_key_type(), key_pair.public())?; let signature_bytes = signature.to_vec(); let signature_json = JsonSerializer::::serialize_signature( diff --git a/rust/chains/tw_greenfield/src/address.rs b/rust/chains/tw_greenfield/src/address.rs index 9318c48cecc..d0d4d794448 100644 --- a/rust/chains/tw_greenfield/src/address.rs +++ b/rust/chains/tw_greenfield/src/address.rs @@ -5,9 +5,8 @@ use serde::Serialize; use std::fmt; use std::str::FromStr; -use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::CoinAddress; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::AddressError; use tw_cosmos_sdk::address::CosmosAddress; use tw_evm::address::Address as EthereumAddress; use tw_keypair::ecdsa::secp256k1; @@ -23,14 +22,7 @@ impl GreenfieldAddress { } } -impl CosmosAddress for GreenfieldAddress { - fn from_str_with_coin(_coin: &dyn CoinContext, addr: &str) -> AddressResult - where - Self: Sized, - { - GreenfieldAddress::from_str(addr) - } -} +impl CosmosAddress for GreenfieldAddress {} impl CoinAddress for GreenfieldAddress { #[inline] diff --git a/rust/chains/tw_greenfield/src/compiler.rs b/rust/chains/tw_greenfield/src/compiler.rs index 46485a5bfe1..a37ac39a7f2 100644 --- a/rust/chains/tw_greenfield/src/compiler.rs +++ b/rust/chains/tw_greenfield/src/compiler.rs @@ -71,7 +71,8 @@ impl GreenfieldCompiler { public_key, } = SingleSignaturePubkey::from_sign_pubkey_list(signatures, public_keys)?; - let public_key = GreenfieldPublicKey::from_bytes(coin, &public_key)?; + let public_key_params = None; + let public_key = GreenfieldPublicKey::from_bytes(coin, &public_key, public_key_params)?; let signature = GreenfieldSignature::try_from(raw_signature.as_slice())?; let signature_bytes = signature.to_vec(); diff --git a/rust/chains/tw_greenfield/src/context.rs b/rust/chains/tw_greenfield/src/context.rs index f4f9db90b25..888147cde48 100644 --- a/rust/chains/tw_greenfield/src/context.rs +++ b/rust/chains/tw_greenfield/src/context.rs @@ -5,17 +5,20 @@ use crate::address::GreenfieldAddress; use crate::public_key::GreenfieldPublicKey; use tw_cosmos_sdk::context::CosmosContext; -use tw_cosmos_sdk::hasher::keccak256_hasher::Keccak256Hasher; use tw_cosmos_sdk::private_key::secp256k1::Secp256PrivateKey; use tw_cosmos_sdk::signature::secp256k1::Secp256k1Signature; +use tw_hash::hasher::Hasher; pub struct GreenfieldContext; impl CosmosContext for GreenfieldContext { type Address = GreenfieldAddress; - /// Greenfield uses EIP712 message signing algorithm built upon `keccak256` hash. - type TxHasher = Keccak256Hasher; type PrivateKey = Secp256PrivateKey; type PublicKey = GreenfieldPublicKey; type Signature = Secp256k1Signature; + + /// Greenfield uses EIP712 message signing algorithm built upon `keccak256` hash. + fn default_tx_hasher() -> Hasher { + Hasher::Keccak256 + } } diff --git a/rust/chains/tw_greenfield/src/modules/tx_builder.rs b/rust/chains/tw_greenfield/src/modules/tx_builder.rs index 2fef62ccd1e..2e0951c8cce 100644 --- a/rust/chains/tw_greenfield/src/modules/tx_builder.rs +++ b/rust/chains/tw_greenfield/src/modules/tx_builder.rs @@ -56,7 +56,9 @@ impl TxBuilder { coin: &dyn CoinContext, input: &Proto::SigningInput, ) -> SigningResult { - let public_key = GreenfieldPublicKey::from_bytes(coin, &input.public_key)?; + let public_key_params = None; + let public_key = + GreenfieldPublicKey::from_bytes(coin, &input.public_key, public_key_params)?; let sign_mode = match input.signing_mode { Proto::SigningMode::Eip712 => GreenfieldSignMode::Eip712, }; diff --git a/rust/chains/tw_greenfield/src/public_key.rs b/rust/chains/tw_greenfield/src/public_key.rs index a10f6d32954..41f4a3cced6 100644 --- a/rust/chains/tw_greenfield/src/public_key.rs +++ b/rust/chains/tw_greenfield/src/public_key.rs @@ -3,10 +3,11 @@ // Copyright © 2017 Trust Wallet. use tw_coin_entry::coin_context::CoinContext; -use tw_cosmos_sdk::public_key::{CosmosPublicKey, JsonPublicKey, ProtobufPublicKey}; +use tw_cosmos_sdk::public_key::{ + CosmosPublicKey, JsonPublicKey, ProtobufPublicKey, PublicKeyParams, +}; use tw_keypair::ecdsa::secp256k1; -use tw_keypair::tw::{PrivateKey, PublicKeyType}; -use tw_keypair::{KeyPairError, KeyPairResult}; +use tw_keypair::{tw, KeyPairError, KeyPairResult}; use tw_memory::Data; use tw_proto::{google, to_any}; @@ -28,16 +29,26 @@ impl ProtobufPublicKey for GreenfieldPublicKey { } impl CosmosPublicKey for GreenfieldPublicKey { - fn from_private_key(_coin: &dyn CoinContext, private_key: &PrivateKey) -> KeyPairResult { + fn from_private_key( + _coin: &dyn CoinContext, + private_key: &tw::PrivateKey, + // Ignore custom public key parameters. + _params: Option, + ) -> KeyPairResult { let public_key = private_key - .get_public_key_by_type(PublicKeyType::Secp256k1)? + .get_public_key_by_type(tw::PublicKeyType::Secp256k1)? .to_secp256k1() .ok_or(KeyPairError::InvalidPublicKey)? .clone(); Ok(GreenfieldPublicKey(public_key)) } - fn from_bytes(_coin: &dyn CoinContext, public_key_bytes: &[u8]) -> KeyPairResult { + fn from_bytes( + _coin: &dyn CoinContext, + public_key_bytes: &[u8], + // Ignore custom public key parameters. + _params: Option, + ) -> KeyPairResult { secp256k1::PublicKey::try_from(public_key_bytes).map(GreenfieldPublicKey) } diff --git a/rust/chains/tw_native_evmos/Cargo.toml b/rust/chains/tw_native_evmos/Cargo.toml index cc4762a7971..87137a252cc 100644 --- a/rust/chains/tw_native_evmos/Cargo.toml +++ b/rust/chains/tw_native_evmos/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] tw_coin_entry = { path = "../../tw_coin_entry" } tw_cosmos_sdk = { path = "../../tw_cosmos_sdk" } +tw_hash = { path = "../../tw_hash" } tw_keypair = { path = "../../tw_keypair" } tw_memory = { path = "../../tw_memory" } tw_proto = { path = "../../tw_proto" } diff --git a/rust/chains/tw_native_evmos/src/context.rs b/rust/chains/tw_native_evmos/src/context.rs index 2f2352dde83..abebb7cd8f9 100644 --- a/rust/chains/tw_native_evmos/src/context.rs +++ b/rust/chains/tw_native_evmos/src/context.rs @@ -5,16 +5,19 @@ use crate::ethermint_public_key::EthermintEthSecp256PublicKey; use tw_cosmos_sdk::address::Address; use tw_cosmos_sdk::context::CosmosContext; -use tw_cosmos_sdk::hasher::keccak256_hasher::Keccak256Hasher; use tw_cosmos_sdk::private_key::secp256k1::Secp256PrivateKey; use tw_cosmos_sdk::signature::secp256k1::Secp256k1Signature; +use tw_hash::hasher::Hasher; pub struct NativeEvmosContext; impl CosmosContext for NativeEvmosContext { type Address = Address; - type TxHasher = Keccak256Hasher; type PrivateKey = Secp256PrivateKey; type PublicKey = EthermintEthSecp256PublicKey; type Signature = Secp256k1Signature; + + fn default_tx_hasher() -> Hasher { + Hasher::Keccak256 + } } diff --git a/rust/chains/tw_native_evmos/src/ethermint_public_key.rs b/rust/chains/tw_native_evmos/src/ethermint_public_key.rs index 0059dc31871..c2bf2d8f0ad 100644 --- a/rust/chains/tw_native_evmos/src/ethermint_public_key.rs +++ b/rust/chains/tw_native_evmos/src/ethermint_public_key.rs @@ -4,62 +4,56 @@ use tw_coin_entry::coin_context::CoinContext; use tw_cosmos_sdk::proto::ethermint; -use tw_cosmos_sdk::public_key::{CosmosPublicKey, JsonPublicKey, ProtobufPublicKey}; -use tw_keypair::ecdsa::secp256k1; -use tw_keypair::KeyPairResult; -use tw_keypair::{tw, KeyPairError}; +use tw_cosmos_sdk::public_key::secp256k1::Secp256PublicKey; +use tw_cosmos_sdk::public_key::{ + CosmosPublicKey, JsonPublicKey, ProtobufPublicKey, PublicKeyParams, +}; +use tw_keypair::{tw, KeyPairResult}; use tw_memory::Data; -use tw_proto::{google, to_any}; +use tw_proto::{google, type_url}; -pub struct EthermintEthSecp256PublicKey { - public_key: Data, -} +const ETHERMINT_SECP256K1_PUBLIC_KEY_TYPE: &str = "ethermint/PubKeyEthSecp256k1"; + +pub struct EthermintEthSecp256PublicKey(Secp256PublicKey); impl EthermintEthSecp256PublicKey { - pub fn new(public_key: &secp256k1::PublicKey) -> KeyPairResult { - Ok(EthermintEthSecp256PublicKey { - // NativeEvmos chain requires the public key to be compressed. - // This trick is needed because `registry.json` contains extended public key type. - public_key: public_key.compressed().to_vec(), - }) + fn default_public_key_params() -> PublicKeyParams { + PublicKeyParams { + // `NativeEvmos` requires the public key to be compressed, + // however the uncompressed public key is used to generate an address. + public_key_type: tw::PublicKeyType::Secp256k1, + json_type: ETHERMINT_SECP256K1_PUBLIC_KEY_TYPE.to_string(), + protobuf_type_url: type_url::(), + } } } impl CosmosPublicKey for EthermintEthSecp256PublicKey { - fn from_private_key(coin: &dyn CoinContext, private_key: &tw::PrivateKey) -> KeyPairResult - where - Self: Sized, - { - let tw_public_key = private_key.get_public_key_by_type(coin.public_key_type())?; - let secp256k1_key = tw_public_key - .to_secp256k1() - .ok_or(KeyPairError::InvalidPublicKey)?; - EthermintEthSecp256PublicKey::new(secp256k1_key) - } - - fn from_bytes(_coin: &dyn CoinContext, public_key_bytes: &[u8]) -> KeyPairResult { - let public_key = secp256k1::PublicKey::try_from(public_key_bytes)?; - EthermintEthSecp256PublicKey::new(&public_key) + fn from_bytes( + coin: &dyn CoinContext, + public_key_bytes: &[u8], + maybe_params: Option, + ) -> KeyPairResult { + // Use default Ethermint public key parameters if otherwise is not specified, + // however the uncompressed public key is used to generate an address. + let params = maybe_params.unwrap_or_else(Self::default_public_key_params); + Secp256PublicKey::from_bytes(coin, public_key_bytes, Some(params)) + .map(EthermintEthSecp256PublicKey) } fn to_bytes(&self) -> Data { - self.public_key.clone() + self.0.to_bytes() } } impl JsonPublicKey for EthermintEthSecp256PublicKey { fn public_key_type(&self) -> String { - const ETHERMINT_SECP256K1_PUBLIC_KEY_TYPE: &str = "ethermint/PubKeyEthSecp256k1"; - - ETHERMINT_SECP256K1_PUBLIC_KEY_TYPE.to_string() + self.0.public_key_type() } } impl ProtobufPublicKey for EthermintEthSecp256PublicKey { fn to_proto(&self) -> google::protobuf::Any { - let proto = ethermint::crypto::v1::ethsecp256k1::PubKey { - key: self.public_key.clone(), - }; - to_any(&proto) + self.0.to_proto() } } diff --git a/rust/chains/tw_native_injective/Cargo.toml b/rust/chains/tw_native_injective/Cargo.toml index d793539a241..b8d8a0bcb50 100644 --- a/rust/chains/tw_native_injective/Cargo.toml +++ b/rust/chains/tw_native_injective/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] tw_coin_entry = { path = "../../tw_coin_entry" } tw_cosmos_sdk = { path = "../../tw_cosmos_sdk" } +tw_hash = { path = "../../tw_hash" } tw_keypair = { path = "../../tw_keypair" } tw_memory = { path = "../../tw_memory" } tw_proto = { path = "../../tw_proto" } diff --git a/rust/chains/tw_native_injective/src/context.rs b/rust/chains/tw_native_injective/src/context.rs index a9ac9bf6bfc..c50dff8c44a 100644 --- a/rust/chains/tw_native_injective/src/context.rs +++ b/rust/chains/tw_native_injective/src/context.rs @@ -5,16 +5,19 @@ use crate::injective_public_key::InjectiveEthSecp256PublicKey; use tw_cosmos_sdk::address::Address; use tw_cosmos_sdk::context::CosmosContext; -use tw_cosmos_sdk::hasher::keccak256_hasher::Keccak256Hasher; use tw_cosmos_sdk::private_key::secp256k1::Secp256PrivateKey; use tw_cosmos_sdk::signature::secp256k1::Secp256k1Signature; +use tw_hash::hasher::Hasher; pub struct NativeInjectiveContext; impl CosmosContext for NativeInjectiveContext { type Address = Address; - type TxHasher = Keccak256Hasher; type PrivateKey = Secp256PrivateKey; type PublicKey = InjectiveEthSecp256PublicKey; type Signature = Secp256k1Signature; + + fn default_tx_hasher() -> Hasher { + Hasher::Keccak256 + } } diff --git a/rust/chains/tw_native_injective/src/entry.rs b/rust/chains/tw_native_injective/src/entry.rs index caf1cbdf3bf..a93d89c294e 100644 --- a/rust/chains/tw_native_injective/src/entry.rs +++ b/rust/chains/tw_native_injective/src/entry.rs @@ -12,7 +12,7 @@ use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; use tw_coin_entry::modules::wallet_connector::NoWalletConnector; -use tw_cosmos_sdk::address::{Address, Bech32Prefix, CosmosAddress}; +use tw_cosmos_sdk::address::{Address, Bech32Prefix}; use tw_cosmos_sdk::modules::compiler::tw_compiler::TWTransactionCompiler; use tw_cosmos_sdk::modules::signer::tw_signer::TWSigner; use tw_keypair::tw; @@ -37,9 +37,9 @@ impl CoinEntry for NativeInjectiveEntry { &self, coin: &dyn CoinContext, address: &str, - _prefix: Option, + prefix: Option, ) -> AddressResult { - Address::from_str_with_coin(coin, address) + Address::from_str_with_coin_and_prefix(coin, address.to_string(), prefix) } #[inline] diff --git a/rust/chains/tw_native_injective/src/injective_public_key.rs b/rust/chains/tw_native_injective/src/injective_public_key.rs index 47b01c575fd..c405867f6e8 100644 --- a/rust/chains/tw_native_injective/src/injective_public_key.rs +++ b/rust/chains/tw_native_injective/src/injective_public_key.rs @@ -4,52 +4,56 @@ use tw_coin_entry::coin_context::CoinContext; use tw_cosmos_sdk::proto::injective; -use tw_cosmos_sdk::public_key::secp256k1::prepare_secp256k1_public_key; -use tw_cosmos_sdk::public_key::{CosmosPublicKey, JsonPublicKey, ProtobufPublicKey}; -use tw_keypair::tw::PrivateKey; +use tw_cosmos_sdk::public_key::secp256k1::Secp256PublicKey; +use tw_cosmos_sdk::public_key::{ + CosmosPublicKey, JsonPublicKey, ProtobufPublicKey, PublicKeyParams, +}; use tw_keypair::KeyPairResult; use tw_memory::Data; -use tw_proto::{google, to_any}; +use tw_proto::{google, type_url}; -pub struct InjectiveEthSecp256PublicKey { - public_key: Data, -} +/// https://github.com/cosmostation/cosmostation-chrome-extension/blob/e2fd27d71a17993f8eef07ce30f7a04a32e52788/src/constants/cosmos.ts#L4 +const INJECTIVE_SECP256K1_PUBLIC_KEY_TYPE: &str = "injective/PubKeyEthSecp256k1"; -impl CosmosPublicKey for InjectiveEthSecp256PublicKey { - fn from_private_key(coin: &dyn CoinContext, private_key: &PrivateKey) -> KeyPairResult - where - Self: Sized, - { - let public_key = private_key.get_public_key_by_type(coin.public_key_type())?; - Ok(InjectiveEthSecp256PublicKey { - public_key: public_key.to_bytes(), - }) +pub struct InjectiveEthSecp256PublicKey(Secp256PublicKey); + +impl InjectiveEthSecp256PublicKey { + fn default_public_key_params(coin: &dyn CoinContext) -> PublicKeyParams { + PublicKeyParams { + // `NativeInjective` uses the same public key type as specified in `registry.json`. + public_key_type: coin.public_key_type(), + json_type: INJECTIVE_SECP256K1_PUBLIC_KEY_TYPE.to_string(), + protobuf_type_url: type_url::(), + } } +} - fn from_bytes(coin: &dyn CoinContext, public_key_bytes: &[u8]) -> KeyPairResult { - let public_key = prepare_secp256k1_public_key(coin, public_key_bytes)?; - Ok(InjectiveEthSecp256PublicKey { public_key }) +impl CosmosPublicKey for InjectiveEthSecp256PublicKey { + fn from_bytes( + coin: &dyn CoinContext, + public_key_bytes: &[u8], + maybe_params: Option, + ) -> KeyPairResult { + // Use default Ethermint public key parameters if otherwise is not specified, + // however the uncompressed public key is used to generate an address. + let params = maybe_params.unwrap_or_else(|| Self::default_public_key_params(coin)); + Secp256PublicKey::from_bytes(coin, public_key_bytes, Some(params)) + .map(InjectiveEthSecp256PublicKey) } fn to_bytes(&self) -> Data { - self.public_key.clone() + self.0.to_bytes() } } impl JsonPublicKey for InjectiveEthSecp256PublicKey { fn public_key_type(&self) -> String { - /// https://github.com/cosmostation/cosmostation-chrome-extension/blob/e2fd27d71a17993f8eef07ce30f7a04a32e52788/src/constants/cosmos.ts#L4 - const INJECTIVE_SECP256K1_PUBLIC_KEY_TYPE: &str = "injective/PubKeyEthSecp256k1"; - - INJECTIVE_SECP256K1_PUBLIC_KEY_TYPE.to_string() + self.0.public_key_type() } } impl ProtobufPublicKey for InjectiveEthSecp256PublicKey { fn to_proto(&self) -> google::protobuf::Any { - let proto = injective::crypto::v1beta1::ethsecp256k1::PubKey { - key: self.public_key.clone(), - }; - to_any(&proto) + self.0.to_proto() } } diff --git a/rust/chains/tw_thorchain/src/entry.rs b/rust/chains/tw_thorchain/src/entry.rs index 7ec28508188..838c10bd526 100644 --- a/rust/chains/tw_thorchain/src/entry.rs +++ b/rust/chains/tw_thorchain/src/entry.rs @@ -13,7 +13,7 @@ use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; use tw_coin_entry::modules::wallet_connector::NoWalletConnector; -use tw_cosmos_sdk::address::{Address, Bech32Prefix, CosmosAddress}; +use tw_cosmos_sdk::address::{Address, Bech32Prefix}; use tw_keypair::tw; use tw_proto::Cosmos::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; @@ -36,9 +36,9 @@ impl CoinEntry for ThorchainEntry { &self, coin: &dyn CoinContext, address: &str, - _prefix: Option, + prefix: Option, ) -> AddressResult { - Address::from_str_with_coin(coin, address) + Address::from_str_with_coin_and_prefix(coin, address.to_string(), prefix) } #[inline] diff --git a/rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs b/rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs index 5daa370bf10..384b8b14cfb 100644 --- a/rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs +++ b/rust/tw_any_coin/tests/chains/cosmos/cosmos_sign.rs @@ -61,3 +61,68 @@ fn test_any_signer_sign_cosmos() { let expected = r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CpIBCo8BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm8KLWNvc21vczFta3k2OWNuOGVrdHd5MDg0NXZlYzl1cHNkcGhrdHh0MDNna3dseBItY29zbW9zMThzMGhkbnNsbGdjY2x3ZXU5YXltdzRuZ2t0cjJrMHJreWdkemRwGg8KBXVhdG9tEgY0MDAwMDASZQpOCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAuzvXOQ3owLGf5VGjeSzHzbpEfRn1+alK0HB4T4dVjZJEgQKAggBEhMKDQoFdWF0b20SBDEwMDAQwJoMGkCvvVE6d29P30cO9/lnXyGunWMPxNY12NuqDcCnFkNM0H4CUQdl1Gc9+ogIJbro5nyzZzlv9rl2/GsZox/JXoCX"}"#; assert_eq!(output.serialized, expected); } + +/// Tests if it is possible to sign a transaction for a custom chain using `CoinType::Cosmos`. +/// Successfully broadcasted (testnet): +/// https://explorer.zetachain.com/cosmos/tx/A2FC8816657856ED274C4418C3CAEAEE645561275F6C63AB5F8B1DCFB37341A0 +#[test] +fn test_any_signer_sign_custom_chain() { + use tw_proto::Cosmos::Proto; + use tw_proto::Cosmos::Proto::mod_Message::OneOfmessage_oneof as MessageEnum; + + let send_msg = Proto::mod_Message::Send { + from_address: "zeta14py36sx57ud82t9yrks9z6hdsrpn5x6kmxs0ne".into(), + to_address: "zeta1cscf4ldnkkz7f0wpveur6dpd0d6p2zxnsuu70y".into(), + amounts: vec![Proto::Amount { + denom: "azeta".into(), + // 0.3 ZETA + amount: "300000000000000000".into(), + }], + ..Proto::mod_Message::Send::default() + }; + + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::Protobuf, + account_number: 2726346, + chain_id: "athens_7001-1".into(), + sequence: 2, + fee: Some(Proto::Fee { + gas: 200000, + amounts: vec![], + }), + private_key: "8d2a3bd62d300a148c89dc8635f87b7a24a951bd1c4e78675fe40e1a640d46ed" + .decode_hex() + .unwrap() + .into(), + messages: vec![Proto::Message { + message_oneof: MessageEnum::send_coins_message(send_msg), + }], + // Use a different Transaction hashing algorithm. + tx_hasher: Proto::TxHasher::Keccak256, + signer_info: Some(Proto::SignerInfo { + // Zetachain requires a compressed public key to sign a transaction, + // however an uncompressed public key is used to generate address. + public_key_type: Proto::SignerPublicKeyType::Secp256k1, + json_type: "ethermint/PubKeyEthSecp256k1".into(), + protobuf_type: "/ethermint.crypto.v1.ethsecp256k1.PubKey".into(), + }), + ..Proto::SigningInput::default() + }; + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output = TWDataHelper::wrap(unsafe { + tw_any_signer_sign(input_data.ptr(), CoinType::Cosmos as u32) + }) + .to_vec() + .expect("!tw_any_signer_sign returned nullptr"); + + let output: Proto::SigningOutput = deserialize(&output).unwrap(); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + assert_eq!( + output.serialized, + r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CpoBCpcBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEncKK3pldGExNHB5MzZzeDU3dWQ4MnQ5eXJrczl6Nmhkc3JwbjV4NmtteHMwbmUSK3pldGExY3NjZjRsZG5ra3o3ZjB3cHZldXI2ZHBkMGQ2cDJ6eG5zdXU3MHkaGwoFYXpldGESEjMwMDAwMDAwMDAwMDAwMDAwMBJhClkKTwooL2V0aGVybWludC5jcnlwdG8udjEuZXRoc2VjcDI1NmsxLlB1YktleRIjCiECho5+FjRBfbKt/Z/jggW/oP6gGJin/TBWXRP3BWo3wGUSBAoCCAEYAhIEEMCaDBpAgGvqca0w2N9wnHnnxS9HiVud4aQ9lNCumzgNIW6wOR4kvPScacGS1G3kwCw7wyI2NJL8M1eVYjafFIt2FpKl3w=="}"# + ); +} diff --git a/rust/tw_any_coin/tests/chains/mod.rs b/rust/tw_any_coin/tests/chains/mod.rs index 38c46ed262e..7a9f7285560 100644 --- a/rust/tw_any_coin/tests/chains/mod.rs +++ b/rust/tw_any_coin/tests/chains/mod.rs @@ -13,3 +13,4 @@ mod native_evmos; mod native_injective; mod tbinance; mod thorchain; +mod zetachain; diff --git a/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_address.rs b/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_address.rs index 6800702b545..00b620ec8a3 100644 --- a/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_address.rs +++ b/rust/tw_any_coin/tests/chains/native_evmos/native_evmos_address.rs @@ -37,6 +37,11 @@ fn test_native_evmos_address_invalid() { CoinType::NativeEvmos, "evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw", ); + // Cosmos has a different `hrp`. + test_address_invalid( + CoinType::Cosmos, + "evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw34", + ); } #[test] @@ -55,6 +60,16 @@ fn test_any_address_is_valid_bech32() { address: "evmos14py36sx57ud82t9yrks9z6hdsrpn5x6k0r05np", hrp: "evmos", }); + test_address_bech32_is_valid(AddressBech32IsValid { + coin: CoinType::NativeEvmos, + address: "evmos14py36sx57ud82t9yrks9z6hdsrpn5x6k0r05np", + hrp: "evmos", + }); + test_address_bech32_is_valid(AddressBech32IsValid { + coin: CoinType::NativeEvmos, + address: "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02", + hrp: "cosmos", + }); } #[test] diff --git a/rust/tw_any_coin/tests/chains/zetachain/mod.rs b/rust/tw_any_coin/tests/chains/zetachain/mod.rs new file mode 100644 index 00000000000..b742c655b68 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/zetachain/mod.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +mod zetachain_address; +mod zetachain_sign; diff --git a/rust/tw_any_coin/tests/chains/zetachain/zetachain_address.rs b/rust/tw_any_coin/tests/chains/zetachain/zetachain_address.rs new file mode 100644 index 00000000000..374fc39cda6 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/zetachain/zetachain_address.rs @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_any_coin::test_utils::address_utils::{ + test_address_bech32_is_valid, test_address_create_bech32_with_public_key, + test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, + AddressBech32IsValid, AddressCreateBech32WithPublicKey, +}; +use tw_coin_registry::coin_type::CoinType; +use tw_keypair::tw::PublicKeyType; + +#[test] +fn test_zetachain_address_normalization() { + test_address_normalization( + CoinType::NativeZetaChain, + "zeta14py36sx57ud82t9yrks9z6hdsrpn5x6kmxs0ne", + "zeta14py36sx57ud82t9yrks9z6hdsrpn5x6kmxs0ne", + ); +} + +#[test] +fn test_zetachain_address_is_valid() { + test_address_valid( + CoinType::NativeZetaChain, + "zeta1em87eul072u3yz4vpgm9mdfszhu0clxekuw08f", + ); + test_address_valid( + CoinType::NativeZetaChain, + "zeta17xpfvakm2amg962yls6f84z3kell8c5lxad43d", + ); +} + +#[test] +fn test_zetachain_address_invalid() { + test_address_invalid( + CoinType::NativeZetaChain, + "zeta17xpfvakm2amg962yls6f84z3kell8c5lxad4", + ); + // Cosmos has a different `hrp`. + test_address_invalid( + CoinType::Cosmos, + "zeta17xpfvakm2amg962yls6f84z3kell8c5lxad43d", + ); +} + +#[test] +fn test_zetachain_address_get_data() { + test_address_get_data( + CoinType::NativeZetaChain, + "zeta14py36sx57ud82t9yrks9z6hdsrpn5x6kmxs0ne", + "A8491D40D4F71A752CA41DA0516AED80C33A1B56", + ); +} + +#[test] +fn test_zetachain_is_valid_bech32() { + // NativeZetaChain address must be valid if its Base32 prefix passed. + test_address_bech32_is_valid(AddressBech32IsValid { + coin: CoinType::NativeZetaChain, + address: "zeta14py36sx57ud82t9yrks9z6hdsrpn5x6kmxs0ne", + hrp: "zeta", + }); + // NativeZetaChain address must be valid for the standard Cosmos hub if its Base32 prefix passed. + test_address_bech32_is_valid(AddressBech32IsValid { + coin: CoinType::Cosmos, + address: "zeta14py36sx57ud82t9yrks9z6hdsrpn5x6kmxs0ne", + hrp: "zeta", + }); + // Cosmos address must be valid with "cosmos" hrp. + test_address_bech32_is_valid(AddressBech32IsValid { + coin: CoinType::NativeZetaChain, + address: "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02", + hrp: "cosmos", + }); +} + +#[test] +fn test_any_address_create_bech32_with_public_key() { + test_address_create_bech32_with_public_key(AddressCreateBech32WithPublicKey { + coin: CoinType::NativeZetaChain, + private_key: "8d2a3bd62d300a148c89dc8635f87b7a24a951bd1c4e78675fe40e1a640d46ed", + public_key_type: PublicKeyType::Secp256k1Extended, + hrp: "zeta", + expected: "zeta14py36sx57ud82t9yrks9z6hdsrpn5x6kmxs0ne", + }); +} diff --git a/rust/tw_any_coin/tests/chains/zetachain/zetachain_sign.rs b/rust/tw_any_coin/tests/chains/zetachain/zetachain_sign.rs new file mode 100644 index 00000000000..d2fa28fc914 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/zetachain/zetachain_sign.rs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_any_coin::ffi::tw_any_signer::tw_any_signer_sign; +use tw_coin_entry::error::SigningErrorType; +use tw_coin_registry::coin_type::CoinType; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_proto::Cosmos::Proto::{self, mod_Message::OneOfmessage_oneof as MessageEnum}; +use tw_proto::{deserialize, serialize}; + +const PRIVATE_KEY: &str = "8d2a3bd62d300a148c89dc8635f87b7a24a951bd1c4e78675fe40e1a640d46ed"; + +/// Successfully broadcasted (testnet): +/// https://explorer.zetachain.com/cosmos/tx/A2FC8816657856ED274C4418C3CAEAEE645561275F6C63AB5F8B1DCFB37341A0 +#[test] +fn test_zetachain_sign_msg_send_testnet() { + let send_msg = Proto::mod_Message::Send { + from_address: "zeta14py36sx57ud82t9yrks9z6hdsrpn5x6kmxs0ne".into(), + to_address: "zeta1cscf4ldnkkz7f0wpveur6dpd0d6p2zxnsuu70y".into(), + amounts: vec![Proto::Amount { + denom: "azeta".into(), + // 0.3 ZETA + amount: "300000000000000000".into(), + }], + ..Proto::mod_Message::Send::default() + }; + + let input = Proto::SigningInput { + signing_mode: Proto::SigningMode::Protobuf, + account_number: 2726346, + chain_id: "athens_7001-1".into(), + sequence: 2, + fee: Some(Proto::Fee { + gas: 200000, + amounts: vec![], + }), + private_key: PRIVATE_KEY.decode_hex().unwrap().into(), + messages: vec![Proto::Message { + message_oneof: MessageEnum::send_coins_message(send_msg), + }], + tx_hasher: Proto::TxHasher::Keccak256, + signer_info: Some(Proto::SignerInfo { + // Zetachain requires a compressed public key to sign a transaction, + // however an uncompressed public key is used to generate address. + public_key_type: Proto::SignerPublicKeyType::Secp256k1, + json_type: "ethermint/PubKeyEthSecp256k1".into(), + protobuf_type: "/ethermint.crypto.v1.ethsecp256k1.PubKey".into(), + }), + ..Proto::SigningInput::default() + }; + + let input_data = TWDataHelper::create(serialize(&input).unwrap()); + + let output = TWDataHelper::wrap(unsafe { + tw_any_signer_sign(input_data.ptr(), CoinType::NativeZetaChain as u32) + }) + .to_vec() + .expect("!tw_any_signer_sign returned nullptr"); + + let output: Proto::SigningOutput = deserialize(&output).unwrap(); + assert_eq!(output.error, SigningErrorType::OK); + assert!(output.error_message.is_empty()); + + assert_eq!( + output.serialized, + r#"{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CpoBCpcBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEncKK3pldGExNHB5MzZzeDU3dWQ4MnQ5eXJrczl6Nmhkc3JwbjV4NmtteHMwbmUSK3pldGExY3NjZjRsZG5ra3o3ZjB3cHZldXI2ZHBkMGQ2cDJ6eG5zdXU3MHkaGwoFYXpldGESEjMwMDAwMDAwMDAwMDAwMDAwMBJhClkKTwooL2V0aGVybWludC5jcnlwdG8udjEuZXRoc2VjcDI1NmsxLlB1YktleRIjCiECho5+FjRBfbKt/Z/jggW/oP6gGJin/TBWXRP3BWo3wGUSBAoCCAEYAhIEEMCaDBpAgGvqca0w2N9wnHnnxS9HiVud4aQ9lNCumzgNIW6wOR4kvPScacGS1G3kwCw7wyI2NJL8M1eVYjafFIt2FpKl3w=="}"# + ); + assert_eq!(output.signature.to_hex(), "806bea71ad30d8df709c79e7c52f47895b9de1a43d94d0ae9b380d216eb0391e24bcf49c69c192d46de4c02c3bc322363492fc33579562369f148b761692a5df"); + assert_eq!( + output.signature_json, + r#"[{"pub_key":{"type":"ethermint/PubKeyEthSecp256k1","value":"AoaOfhY0QX2yrf2f44IFv6D+oBiYp/0wVl0T9wVqN8Bl"},"signature":"gGvqca0w2N9wnHnnxS9HiVud4aQ9lNCumzgNIW6wOR4kvPScacGS1G3kwCw7wyI2NJL8M1eVYjafFIt2FpKl3w=="}]"# + ); +} diff --git a/rust/tw_any_coin/tests/coin_address_derivation_test.rs b/rust/tw_any_coin/tests/coin_address_derivation_test.rs index cd1c64283d2..09bd78cd283 100644 --- a/rust/tw_any_coin/tests/coin_address_derivation_test.rs +++ b/rust/tw_any_coin/tests/coin_address_derivation_test.rs @@ -145,6 +145,7 @@ fn test_coin_address_derivation() { CoinType::InternetComputer => "290cc7c359f44c8516fc169c5ed4f0f3ae2e24bf5de0d4c51f5e7545b5474faa", CoinType::Binance => "bnb1ten42eesehw0ktddcp0fws7d3ycsqez3aqvnpg", CoinType::TBinance => "tbnb1ten42eesehw0ktddcp0fws7d3ycsqez3n49hpe", + CoinType::NativeZetaChain => "zeta14s0vgnj0pjnazu4hsqlksdk7slah9vcfcwctsr", // end_of_coin_address_derivation_tests_marker_do_not_modify _ => panic!("{:?} must be covered", coin), }; diff --git a/rust/tw_cosmos_sdk/src/address.rs b/rust/tw_cosmos_sdk/src/address.rs index 4f15075115d..10cbca9a10c 100644 --- a/rust/tw_cosmos_sdk/src/address.rs +++ b/rust/tw_cosmos_sdk/src/address.rs @@ -4,24 +4,11 @@ use serde::Serialize; use std::str::FromStr; -use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_coin_entry::error::AddressError; pub type Address = tw_bech32_address::Bech32Address; pub type Bech32Prefix = tw_bech32_address::bech32_prefix::Bech32Prefix; -pub trait CosmosAddress: FromStr + Serialize + ToString { - fn from_str_with_coin(coin: &dyn CoinContext, addr: &str) -> AddressResult - where - Self: Sized; -} +pub trait CosmosAddress: FromStr + Serialize + ToString {} -impl CosmosAddress for Address { - fn from_str_with_coin(coin: &dyn CoinContext, addr: &str) -> AddressResult - where - Self: Sized, - { - let prefix = None; - Address::from_str_with_coin_and_prefix(coin, addr.to_string(), prefix) - } -} +impl CosmosAddress for Address {} diff --git a/rust/tw_cosmos_sdk/src/context.rs b/rust/tw_cosmos_sdk/src/context.rs index 0fc370bc39f..077c8c1ede4 100644 --- a/rust/tw_cosmos_sdk/src/context.rs +++ b/rust/tw_cosmos_sdk/src/context.rs @@ -3,21 +3,21 @@ // Copyright © 2017 Trust Wallet. use crate::address::{Address, CosmosAddress}; -use crate::hasher::sha256_hasher::Sha256Hasher; -use crate::hasher::CosmosHasher; use crate::private_key::secp256k1::Secp256PrivateKey; use crate::private_key::CosmosPrivateKey; use crate::public_key::secp256k1::Secp256PublicKey; use crate::public_key::CosmosPublicKey; use crate::signature::secp256k1::Secp256k1Signature; use crate::signature::CosmosSignature; +use tw_hash::hasher::Hasher; pub trait CosmosContext { type Address: CosmosAddress; - type TxHasher: CosmosHasher; type PrivateKey: CosmosPrivateKey; type PublicKey: CosmosPublicKey; type Signature: CosmosSignature; + + fn default_tx_hasher() -> Hasher; } #[derive(Default)] @@ -25,8 +25,11 @@ pub struct StandardCosmosContext; impl CosmosContext for StandardCosmosContext { type Address = Address; - type TxHasher = Sha256Hasher; type PrivateKey = Secp256PrivateKey; type PublicKey = Secp256PublicKey; type Signature = Secp256k1Signature; + + fn default_tx_hasher() -> Hasher { + Hasher::Sha256 + } } diff --git a/rust/tw_cosmos_sdk/src/hasher/keccak256_hasher.rs b/rust/tw_cosmos_sdk/src/hasher/keccak256_hasher.rs deleted file mode 100644 index a37dccb78b1..00000000000 --- a/rust/tw_cosmos_sdk/src/hasher/keccak256_hasher.rs +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -use crate::hasher::CosmosHasher; -use tw_hash::sha3::keccak256; -use tw_memory::Data; - -pub struct Keccak256Hasher; - -impl CosmosHasher for Keccak256Hasher { - fn hash_sign_doc(sign_doc: &[u8]) -> Data { - keccak256(sign_doc) - } - - fn hash_json_tx(json: &str) -> Data { - keccak256(json.as_bytes()) - } -} diff --git a/rust/tw_cosmos_sdk/src/hasher/mod.rs b/rust/tw_cosmos_sdk/src/hasher/mod.rs deleted file mode 100644 index 960940ef8c5..00000000000 --- a/rust/tw_cosmos_sdk/src/hasher/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -use tw_memory::Data; - -pub mod keccak256_hasher; -pub mod sha256_hasher; - -pub trait CosmosHasher { - fn hash_sign_doc(sign_doc: &[u8]) -> Data; - - fn hash_json_tx(json: &str) -> Data; -} diff --git a/rust/tw_cosmos_sdk/src/hasher/sha256_hasher.rs b/rust/tw_cosmos_sdk/src/hasher/sha256_hasher.rs deleted file mode 100644 index 33f0e42a824..00000000000 --- a/rust/tw_cosmos_sdk/src/hasher/sha256_hasher.rs +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -use crate::hasher::CosmosHasher; -use tw_hash::sha2::sha256; -use tw_memory::Data; - -pub struct Sha256Hasher; - -impl CosmosHasher for Sha256Hasher { - fn hash_sign_doc(sign_doc: &[u8]) -> Data { - sha256(sign_doc) - } - - fn hash_json_tx(json: &str) -> Data { - sha256(json.as_bytes()) - } -} diff --git a/rust/tw_cosmos_sdk/src/lib.rs b/rust/tw_cosmos_sdk/src/lib.rs index 3c6f16a7665..a0a49da4057 100644 --- a/rust/tw_cosmos_sdk/src/lib.rs +++ b/rust/tw_cosmos_sdk/src/lib.rs @@ -4,7 +4,6 @@ pub mod address; pub mod context; -pub mod hasher; pub mod modules; pub mod private_key; pub mod public_key; diff --git a/rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs b/rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs index 5c61af317a5..a8310883057 100644 --- a/rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs +++ b/rust/tw_cosmos_sdk/src/modules/compiler/json_preimager.rs @@ -3,12 +3,12 @@ // Copyright © 2017 Trust Wallet. use crate::context::CosmosContext; -use crate::hasher::CosmosHasher; use crate::modules::serializer::json_serializer::JsonSerializer; use crate::public_key::JsonPublicKey; use crate::transaction::UnsignedTransaction; use std::marker::PhantomData; use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_hash::hasher::Hasher; use tw_memory::Data; pub struct JsonTxPreimage { @@ -24,11 +24,14 @@ impl JsonPreimager where Context::PublicKey: JsonPublicKey, { - pub fn preimage_hash(unsigned: &UnsignedTransaction) -> SigningResult { + pub fn preimage_hash( + unsigned: &UnsignedTransaction, + hasher: Hasher, + ) -> SigningResult { let tx_to_sign = JsonSerializer::build_unsigned_tx(unsigned)?; let encoded_tx = serde_json::to_string(&tx_to_sign) .map_err(|_| SigningError(SigningErrorType::Error_internal))?; - let tx_hash = Context::TxHasher::hash_json_tx(&encoded_tx); + let tx_hash = hasher.hash(encoded_tx.as_bytes()); Ok(JsonTxPreimage { encoded_tx, diff --git a/rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs b/rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs index 7aa71bbdb3e..dc82a15920a 100644 --- a/rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs +++ b/rust/tw_cosmos_sdk/src/modules/compiler/protobuf_preimager.rs @@ -3,11 +3,11 @@ // Copyright © 2017 Trust Wallet. use crate::context::CosmosContext; -use crate::hasher::CosmosHasher; use crate::modules::serializer::protobuf_serializer::{ProtobufSerializer, SignDirectArgs}; use crate::transaction::UnsignedTransaction; use std::marker::PhantomData; use tw_coin_entry::error::SigningResult; +use tw_hash::hasher::Hasher; use tw_memory::Data; use tw_proto::serialize; @@ -23,10 +23,11 @@ pub struct ProtobufPreimager { impl ProtobufPreimager { pub fn preimage_hash( unsigned: &UnsignedTransaction, + hasher: Hasher, ) -> SigningResult { let tx_to_sign = ProtobufSerializer::build_sign_doc(unsigned)?; let encoded_tx = serialize(&tx_to_sign)?; - let tx_hash = Context::TxHasher::hash_sign_doc(&encoded_tx); + let tx_hash = hasher.hash(&encoded_tx); Ok(ProtobufTxPreimage { encoded_tx, @@ -34,10 +35,13 @@ impl ProtobufPreimager { }) } - pub fn preimage_hash_direct(args: &SignDirectArgs) -> SigningResult { + pub fn preimage_hash_direct( + args: &SignDirectArgs, + hasher: Hasher, + ) -> SigningResult { let tx_to_sign = ProtobufSerializer::::build_direct_sign_doc(args); let encoded_tx = serialize(&tx_to_sign)?; - let tx_hash = Context::TxHasher::hash_sign_doc(&encoded_tx); + let tx_hash = hasher.hash(&encoded_tx); Ok(ProtobufTxPreimage { encoded_tx, diff --git a/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs b/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs index 6dbdeb2ceaa..4059f09fbcc 100644 --- a/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs +++ b/rust/tw_cosmos_sdk/src/modules/compiler/tw_compiler.rs @@ -58,16 +58,17 @@ impl TWTransactionCompiler { coin: &dyn CoinContext, input: Proto::SigningInput<'_>, ) -> SigningResult> { + let tx_hasher = TxBuilder::::tx_hasher_from_proto(&input); let preimage = match TxBuilder::::try_sign_direct_args(&input) { // If there was a `SignDirect` message in the signing input, generate the tx preimage directly. Ok(Some(sign_direct_args)) => { - ProtobufPreimager::::preimage_hash_direct(&sign_direct_args)? + ProtobufPreimager::::preimage_hash_direct(&sign_direct_args, tx_hasher)? }, // Otherwise, generate the tx preimage by using `TxBuilder`. _ => { // Please note the [`Proto::SigningInput::public_key`] should be set already. let unsigned_tx = TxBuilder::::unsigned_tx_from_proto(coin, &input)?; - ProtobufPreimager::::preimage_hash(&unsigned_tx)? + ProtobufPreimager::::preimage_hash(&unsigned_tx, tx_hasher)? }, }; @@ -84,7 +85,8 @@ impl TWTransactionCompiler { ) -> SigningResult> { // Please note the [`Proto::SigningInput::public_key`] should be set already. let unsigned_tx = TxBuilder::::unsigned_tx_from_proto(coin, &input)?; - let preimage = JsonPreimager::preimage_hash(&unsigned_tx)?; + let tx_hasher = TxBuilder::::tx_hasher_from_proto(&input); + let preimage = JsonPreimager::preimage_hash(&unsigned_tx, tx_hasher)?; Ok(CompilerProto::PreSigningOutput { data: Cow::from(preimage.encoded_tx.as_bytes().to_vec()), @@ -104,7 +106,9 @@ impl TWTransactionCompiler { public_key, } = SingleSignaturePubkey::from_sign_pubkey_list(signatures, public_keys)?; let signature = Context::Signature::try_from(&signature)?; - let public_key = Context::PublicKey::from_bytes(coin, &public_key)?; + + let params = TxBuilder::::public_key_params_from_proto(&input); + let public_key = Context::PublicKey::from_bytes(coin, &public_key, params)?; let signed_tx_raw = match TxBuilder::::try_sign_direct_args(&input) { // If there was a `SignDirect` message in the signing input, generate the `TxRaw` directly. @@ -150,7 +154,9 @@ impl TWTransactionCompiler { public_key, } = SingleSignaturePubkey::from_sign_pubkey_list(signatures, public_keys)?; let signature = Context::Signature::try_from(&signature)?; - let public_key = Context::PublicKey::from_bytes(coin, &public_key)?; + + let params = TxBuilder::::public_key_params_from_proto(&input); + let public_key = Context::PublicKey::from_bytes(coin, &public_key, params)?; // Set the public key. It will be used to construct a signer info. input.public_key = Cow::from(public_key.to_bytes()); diff --git a/rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs b/rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs index d06f16d673c..4a6d4b276c0 100644 --- a/rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs +++ b/rust/tw_cosmos_sdk/src/modules/signer/tw_signer.rs @@ -4,6 +4,7 @@ use crate::context::CosmosContext; use crate::modules::compiler::tw_compiler::TWTransactionCompiler; +use crate::modules::tx_builder::TxBuilder; use crate::private_key::CosmosPrivateKey; use crate::public_key::CosmosPublicKey; use std::borrow::Cow; @@ -32,7 +33,10 @@ impl TWSigner { mut input: Proto::SigningInput<'_>, ) -> SigningResult> { let private_key = Context::PrivateKey::try_from(&input.private_key)?; - let public_key = Context::PublicKey::from_private_key(coin, private_key.as_ref())?; + + let params = TxBuilder::::public_key_params_from_proto(&input); + let public_key = Context::PublicKey::from_private_key(coin, private_key.as_ref(), params)?; + // Set the public key. It will be used to construct a signer info. input.public_key = Cow::from(public_key.to_bytes()); diff --git a/rust/tw_cosmos_sdk/src/modules/tx_builder.rs b/rust/tw_cosmos_sdk/src/modules/tx_builder.rs index e08d8c47ba4..c39d13f8c70 100644 --- a/rust/tw_cosmos_sdk/src/modules/tx_builder.rs +++ b/rust/tw_cosmos_sdk/src/modules/tx_builder.rs @@ -2,10 +2,10 @@ // // Copyright © 2017 Trust Wallet. -use crate::address::{Address, CosmosAddress}; +use crate::address::Address; use crate::context::CosmosContext; use crate::modules::serializer::protobuf_serializer::SignDirectArgs; -use crate::public_key::CosmosPublicKey; +use crate::public_key::{CosmosPublicKey, PublicKeyParams}; use crate::transaction::message::cosmos_generic_message::JsonRawMessage; use crate::transaction::message::{CosmosMessage, CosmosMessageBox}; use crate::transaction::{Coin, Fee, SignMode, SignerInfo, TxBody, UnsignedTransaction}; @@ -13,6 +13,8 @@ use std::marker::PhantomData; use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_hash::hasher::Hasher; +use tw_keypair::tw; use tw_misc::traits::{OptionalEmpty, ToBytesVec}; use tw_number::U256; use tw_proto::Cosmos::Proto; @@ -53,7 +55,9 @@ where coin: &dyn CoinContext, input: &Proto::SigningInput, ) -> SigningResult> { - let public_key = Context::PublicKey::from_bytes(coin, &input.public_key)?; + let params = Self::public_key_params_from_proto(input); + let public_key = Context::PublicKey::from_bytes(coin, &input.public_key, params)?; + Ok(SignerInfo { public_key, sequence: input.sequence, @@ -62,6 +66,27 @@ where }) } + pub fn public_key_params_from_proto(input: &Proto::SigningInput) -> Option { + input.signer_info.clone().map(|params| PublicKeyParams { + public_key_type: match params.public_key_type { + Proto::SignerPublicKeyType::Secp256k1 => tw::PublicKeyType::Secp256k1, + Proto::SignerPublicKeyType::Secp256k1Extended => { + tw::PublicKeyType::Secp256k1Extended + }, + }, + json_type: params.json_type.to_string(), + protobuf_type_url: params.protobuf_type.to_string(), + }) + } + + pub fn tx_hasher_from_proto(input: &Proto::SigningInput) -> Hasher { + match input.tx_hasher { + Proto::TxHasher::UseDefault => Context::default_tx_hasher(), + Proto::TxHasher::Sha256 => Hasher::Sha256, + Proto::TxHasher::Keccak256 => Hasher::Keccak256, + } + } + fn fee_from_proto(input: &Proto::Fee) -> SigningResult> { let amounts = input .amounts @@ -197,7 +222,7 @@ where } pub fn send_msg_from_proto( - coin: &dyn CoinContext, + _coin: &dyn CoinContext, send: &Proto::mod_Message::Send<'_>, ) -> SigningResult { use crate::transaction::message::cosmos_bank_message::SendMessage; @@ -209,15 +234,15 @@ where .collect::>()?; let msg = SendMessage { custom_type_prefix: send.type_prefix.to_string().empty_or_some(), - from_address: Address::from_str_with_coin(coin, &send.from_address)?, - to_address: Address::from_str_with_coin(coin, &send.to_address)?, + from_address: Address::from_str(&send.from_address)?, + to_address: Address::from_str(&send.to_address)?, amount: amounts, }; Ok(msg.into_boxed()) } pub fn transfer_tokens_msg_from_proto( - coin: &dyn CoinContext, + _coin: &dyn CoinContext, transfer: &Proto::mod_Message::Transfer<'_>, ) -> SigningResult { use crate::transaction::message::ibc_message::{Height, TransferTokensMessage}; @@ -236,8 +261,7 @@ where source_port: transfer.source_port.to_string(), source_channel: transfer.source_channel.to_string(), token, - sender: Address::from_str_with_coin(coin, &transfer.sender)?, - // Don't use `Address::from_str_with_coin` as the recipient address can belong to another Cosmos chain. + sender: Address::from_str(&transfer.sender)?, receiver: Address::from_str(&transfer.receiver)?, timeout_height: Height { revision_number: height.revision_number, @@ -249,7 +273,7 @@ where } pub fn delegate_msg_from_proto( - coin: &dyn CoinContext, + _coin: &dyn CoinContext, delegate: &Proto::mod_Message::Delegate<'_>, ) -> SigningResult { use crate::transaction::message::cosmos_staking_message::DelegateMessage; @@ -262,14 +286,14 @@ where let msg = DelegateMessage { custom_type_prefix: delegate.type_prefix.to_string().empty_or_some(), amount, - delegator_address: Address::from_str_with_coin(coin, &delegate.delegator_address)?, - validator_address: Address::from_str_with_coin(coin, &delegate.validator_address)?, + delegator_address: Address::from_str(&delegate.delegator_address)?, + validator_address: Address::from_str(&delegate.validator_address)?, }; Ok(msg.into_boxed()) } pub fn undelegate_msg_from_proto( - coin: &dyn CoinContext, + _coin: &dyn CoinContext, undelegate: &Proto::mod_Message::Undelegate<'_>, ) -> SigningResult { use crate::transaction::message::cosmos_staking_message::UndelegateMessage; @@ -283,42 +307,42 @@ where let msg = UndelegateMessage { custom_type_prefix: undelegate.type_prefix.to_string().empty_or_some(), amount, - delegator_address: Address::from_str_with_coin(coin, &undelegate.delegator_address)?, - validator_address: Address::from_str_with_coin(coin, &undelegate.validator_address)?, + delegator_address: Address::from_str(&undelegate.delegator_address)?, + validator_address: Address::from_str(&undelegate.validator_address)?, }; Ok(msg.into_boxed()) } pub fn withdraw_reward_msg_from_proto( - coin: &dyn CoinContext, + _coin: &dyn CoinContext, withdraw: &Proto::mod_Message::WithdrawDelegationReward<'_>, ) -> SigningResult { use crate::transaction::message::cosmos_staking_message::WithdrawDelegationRewardMessage; let msg = WithdrawDelegationRewardMessage { custom_type_prefix: withdraw.type_prefix.to_string().empty_or_some(), - delegator_address: Address::from_str_with_coin(coin, &withdraw.delegator_address)?, - validator_address: Address::from_str_with_coin(coin, &withdraw.validator_address)?, + delegator_address: Address::from_str(&withdraw.delegator_address)?, + validator_address: Address::from_str(&withdraw.validator_address)?, }; Ok(msg.into_boxed()) } pub fn set_withdraw_address_msg_from_proto( - coin: &dyn CoinContext, + _coin: &dyn CoinContext, set: &Proto::mod_Message::SetWithdrawAddress<'_>, ) -> SigningResult { use crate::transaction::message::cosmos_staking_message::SetWithdrawAddressMessage; let msg = SetWithdrawAddressMessage { custom_type_prefix: set.type_prefix.to_string().empty_or_some(), - delegator_address: Address::from_str_with_coin(coin, &set.delegator_address)?, - withdraw_address: Address::from_str_with_coin(coin, &set.withdraw_address)?, + delegator_address: Address::from_str(&set.delegator_address)?, + withdraw_address: Address::from_str(&set.withdraw_address)?, }; Ok(msg.into_boxed()) } pub fn redelegate_msg_from_proto( - coin: &dyn CoinContext, + _coin: &dyn CoinContext, redelegate: &Proto::mod_Message::BeginRedelegate<'_>, ) -> SigningResult { use crate::transaction::message::cosmos_staking_message::BeginRedelegateMessage; @@ -328,15 +352,13 @@ where .as_ref() .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; let amount = Self::coin_from_proto(amount)?; - let validator_src_address = - Address::from_str_with_coin(coin, &redelegate.validator_src_address)?; - let validator_dst_address = - Address::from_str_with_coin(coin, &redelegate.validator_dst_address)?; + let validator_src_address = Address::from_str(&redelegate.validator_src_address)?; + let validator_dst_address = Address::from_str(&redelegate.validator_dst_address)?; let msg = BeginRedelegateMessage { custom_type_prefix: redelegate.type_prefix.to_string().empty_or_some(), amount, - delegator_address: Address::from_str_with_coin(coin, &redelegate.delegator_address)?, + delegator_address: Address::from_str(&redelegate.delegator_address)?, validator_src_address, validator_dst_address, }; @@ -358,7 +380,7 @@ where } pub fn wasm_terra_execute_contract_transfer_msg_from_proto( - coin: &dyn CoinContext, + _coin: &dyn CoinContext, transfer: &Proto::mod_Message::WasmTerraExecuteContractTransfer<'_>, ) -> SigningResult { use crate::transaction::message::terra_wasm_message::TerraExecuteContractMessage; @@ -370,8 +392,8 @@ where }; let msg = TerraExecuteContractMessage { - sender: Address::from_str_with_coin(coin, &transfer.sender_address)?, - contract: Address::from_str_with_coin(coin, &transfer.contract_address)?, + sender: Address::from_str(&transfer.sender_address)?, + contract: Address::from_str(&transfer.contract_address)?, execute_msg: ExecuteMsg::json(execute_payload)?, // Used in case you are sending native tokens along with this message. coins: Vec::default(), @@ -380,7 +402,7 @@ where } pub fn wasm_terra_execute_contract_send_msg_from_proto( - coin: &dyn CoinContext, + _coin: &dyn CoinContext, send: &Proto::mod_Message::WasmTerraExecuteContractSend<'_>, ) -> SigningResult { use crate::transaction::message::terra_wasm_message::TerraExecuteContractMessage; @@ -393,8 +415,8 @@ where }; let msg = TerraExecuteContractMessage { - sender: Address::from_str_with_coin(coin, &send.sender_address)?, - contract: Address::from_str_with_coin(coin, &send.contract_address)?, + sender: Address::from_str(&send.sender_address)?, + contract: Address::from_str(&send.contract_address)?, execute_msg: ExecuteMsg::json(execute_payload)?, // Used in case you are sending native tokens along with this message. coins: Vec::default(), @@ -403,7 +425,7 @@ where } pub fn wasm_terra_execute_contract_generic_msg_from_proto( - coin: &dyn CoinContext, + _coin: &dyn CoinContext, generic: &Proto::mod_Message::WasmTerraExecuteContractGeneric<'_>, ) -> SigningResult { use crate::transaction::message::terra_wasm_message::TerraExecuteContractMessage; @@ -416,8 +438,8 @@ where .collect::>()?; let msg = TerraExecuteContractMessage { - sender: Address::from_str_with_coin(coin, &generic.sender_address)?, - contract: Address::from_str_with_coin(coin, &generic.contract_address)?, + sender: Address::from_str(&generic.sender_address)?, + contract: Address::from_str(&generic.contract_address)?, execute_msg: ExecuteMsg::String(generic.execute_msg.to_string()), coins, }; @@ -425,7 +447,7 @@ where } pub fn wasm_execute_contract_transfer_msg_from_proto( - coin: &dyn CoinContext, + _coin: &dyn CoinContext, transfer: &Proto::mod_Message::WasmExecuteContractTransfer<'_>, ) -> SigningResult { use crate::transaction::message::wasm_message::{ @@ -438,8 +460,8 @@ where }; let msg = WasmExecuteContractMessage { - sender: Address::from_str_with_coin(coin, &transfer.sender_address)?, - contract: Address::from_str_with_coin(coin, &transfer.contract_address)?, + sender: Address::from_str(&transfer.sender_address)?, + contract: Address::from_str(&transfer.contract_address)?, msg: ExecuteMsg::json(transfer_payload)?, // Used in case you are sending native tokens along with this message. coins: Vec::default(), @@ -448,7 +470,7 @@ where } pub fn wasm_execute_contract_send_msg_from_proto( - coin: &dyn CoinContext, + _coin: &dyn CoinContext, send: &Proto::mod_Message::WasmExecuteContractSend<'_>, ) -> SigningResult { use crate::transaction::message::wasm_message::{ @@ -462,8 +484,8 @@ where }; let msg = WasmExecuteContractMessage { - sender: Address::from_str_with_coin(coin, &send.sender_address)?, - contract: Address::from_str_with_coin(coin, &send.contract_address)?, + sender: Address::from_str(&send.sender_address)?, + contract: Address::from_str(&send.contract_address)?, msg: ExecuteMsg::json(execute_payload)?, // Used in case you are sending native tokens along with this message. coins: Vec::default(), @@ -472,7 +494,7 @@ where } pub fn wasm_execute_contract_generic_msg_from_proto( - coin: &dyn CoinContext, + _coin: &dyn CoinContext, generic: &Proto::mod_Message::WasmExecuteContractGeneric<'_>, ) -> SigningResult { use crate::transaction::message::wasm_message::{ExecuteMsg, WasmExecuteContractMessage}; @@ -484,8 +506,8 @@ where .collect::>()?; let msg = WasmExecuteContractMessage { - sender: Address::from_str_with_coin(coin, &generic.sender_address)?, - contract: Address::from_str_with_coin(coin, &generic.contract_address)?, + sender: Address::from_str(&generic.sender_address)?, + contract: Address::from_str(&generic.contract_address)?, msg: ExecuteMsg::String(generic.execute_msg.to_string()), coins, }; @@ -513,7 +535,7 @@ where } pub fn auth_grant_msg_from_proto( - coin: &dyn CoinContext, + _coin: &dyn CoinContext, auth: &Proto::mod_Message::AuthGrant<'_>, ) -> SigningResult { use crate::transaction::message::cosmos_auth_message::AuthGrantMessage; @@ -533,8 +555,8 @@ where }; let msg = AuthGrantMessage { - granter: Address::from_str_with_coin(coin, &auth.granter)?, - grantee: Address::from_str_with_coin(coin, &auth.grantee)?, + granter: Address::from_str(&auth.granter)?, + grantee: Address::from_str(&auth.grantee)?, grant_msg, expiration_secs: auth.expiration, }; @@ -542,21 +564,21 @@ where } pub fn auth_revoke_msg_from_proto( - coin: &dyn CoinContext, + _coin: &dyn CoinContext, auth: &Proto::mod_Message::AuthRevoke<'_>, ) -> SigningResult { use crate::transaction::message::cosmos_auth_message::AuthRevokeMessage; let msg = AuthRevokeMessage { - granter: Address::from_str_with_coin(coin, &auth.granter)?, - grantee: Address::from_str_with_coin(coin, &auth.grantee)?, + granter: Address::from_str(&auth.granter)?, + grantee: Address::from_str(&auth.grantee)?, msg_type_url: auth.msg_type_url.to_string(), }; Ok(msg.into_boxed()) } pub fn vote_msg_from_proto( - coin: &dyn CoinContext, + _coin: &dyn CoinContext, vote: &Proto::mod_Message::MsgVote<'_>, ) -> SigningResult { use crate::transaction::message::cosmos_gov_message::{VoteMessage, VoteOption}; @@ -572,20 +594,20 @@ where let msg = VoteMessage { proposal_id: vote.proposal_id, - voter: Address::from_str_with_coin(coin, &vote.voter)?, + voter: Address::from_str(&vote.voter)?, option, }; Ok(msg.into_boxed()) } pub fn stride_stake_msg_from_proto( - coin: &dyn CoinContext, + _coin: &dyn CoinContext, stake: &Proto::mod_Message::MsgStrideLiquidStakingStake<'_>, ) -> SigningResult { use crate::transaction::message::stride_message::StrideLiquidStakeMessage; let msg = StrideLiquidStakeMessage { - creator: Address::from_str_with_coin(coin, &stake.creator)?, + creator: Address::from_str(&stake.creator)?, amount: U256::from_str(&stake.amount)?, host_denom: stake.host_denom.to_string(), }; diff --git a/rust/tw_cosmos_sdk/src/public_key/mod.rs b/rust/tw_cosmos_sdk/src/public_key/mod.rs index f7382521b91..746de80b584 100644 --- a/rust/tw_cosmos_sdk/src/public_key/mod.rs +++ b/rust/tw_cosmos_sdk/src/public_key/mod.rs @@ -9,13 +9,31 @@ use tw_proto::google; pub mod secp256k1; +pub struct PublicKeyParams { + pub public_key_type: tw::PublicKeyType, + pub json_type: String, + pub protobuf_type_url: String, +} + pub trait CosmosPublicKey: JsonPublicKey + ProtobufPublicKey + Sized { fn from_private_key( coin: &dyn CoinContext, private_key: &tw::PrivateKey, - ) -> KeyPairResult; + params: Option, + ) -> KeyPairResult { + let public_key_type = match params { + Some(ref params) => params.public_key_type, + None => coin.public_key_type(), + }; + let public_key = private_key.get_public_key_by_type(public_key_type)?; + Self::from_bytes(coin, &public_key.to_bytes(), params) + } - fn from_bytes(coin: &dyn CoinContext, public_key_bytes: &[u8]) -> KeyPairResult; + fn from_bytes( + coin: &dyn CoinContext, + public_key_bytes: &[u8], + params: Option, + ) -> KeyPairResult; fn to_bytes(&self) -> Data; } diff --git a/rust/tw_cosmos_sdk/src/public_key/secp256k1.rs b/rust/tw_cosmos_sdk/src/public_key/secp256k1.rs index 1b1d2509194..6b147ed4238 100644 --- a/rust/tw_cosmos_sdk/src/public_key/secp256k1.rs +++ b/rust/tw_cosmos_sdk/src/public_key/secp256k1.rs @@ -3,46 +3,59 @@ // Copyright © 2017 Trust Wallet. use crate::proto::cosmos; -use crate::public_key::{CosmosPublicKey, JsonPublicKey, ProtobufPublicKey}; +use crate::public_key::{CosmosPublicKey, JsonPublicKey, ProtobufPublicKey, PublicKeyParams}; use tw_coin_entry::coin_context::CoinContext; use tw_keypair::ecdsa::secp256k1; -use tw_keypair::tw::{self, PublicKeyType}; +use tw_keypair::tw; use tw_keypair::{KeyPairError, KeyPairResult}; use tw_memory::Data; use tw_misc::traits::ToBytesVec; -use tw_proto::{google, to_any}; +use tw_proto::{google, to_any_with_type_url, type_url}; + +const DEFAULT_JSON_PUBLIC_KEY_TYPE: &str = "tendermint/PubKeySecp256k1"; #[derive(Clone)] pub struct Secp256PublicKey { public_key: Data, + json_type: String, + protobuf_type_url: String, } impl Secp256PublicKey { pub fn from_secp256k1_public_key( - coin: &dyn CoinContext, + public_key_type: tw::PublicKeyType, public_key: &secp256k1::PublicKey, ) -> KeyPairResult { - let public_key = prepare_secp256k1_public_key(coin, public_key.compressed().as_slice())?; - Ok(Secp256PublicKey { public_key }) + let public_key = + prepare_secp256k1_public_key(public_key_type, public_key.compressed().as_slice())?; + Ok(Secp256PublicKey { + public_key, + json_type: DEFAULT_JSON_PUBLIC_KEY_TYPE.to_string(), + protobuf_type_url: type_url::(), + }) } } impl CosmosPublicKey for Secp256PublicKey { - fn from_private_key(coin: &dyn CoinContext, private_key: &tw::PrivateKey) -> KeyPairResult - where - Self: Sized, - { - let public_key = private_key.get_public_key_by_type(coin.public_key_type())?; + fn from_bytes( + coin: &dyn CoinContext, + public_key_bytes: &[u8], + maybe_params: Option, + ) -> KeyPairResult { + let params = maybe_params.unwrap_or_else(|| PublicKeyParams { + public_key_type: coin.public_key_type(), + json_type: DEFAULT_JSON_PUBLIC_KEY_TYPE.to_string(), + protobuf_type_url: type_url::(), + }); + + let public_key = prepare_secp256k1_public_key(params.public_key_type, public_key_bytes)?; Ok(Secp256PublicKey { - public_key: public_key.to_bytes(), + public_key, + json_type: params.json_type, + protobuf_type_url: params.protobuf_type_url, }) } - fn from_bytes(coin: &dyn CoinContext, public_key_bytes: &[u8]) -> KeyPairResult { - let public_key = prepare_secp256k1_public_key(coin, public_key_bytes)?; - Ok(Secp256PublicKey { public_key }) - } - fn to_bytes(&self) -> Data { self.public_key.clone() } @@ -53,24 +66,24 @@ impl ProtobufPublicKey for Secp256PublicKey { let proto = cosmos::crypto::secp256k1::PubKey { key: self.public_key.clone(), }; - to_any(&proto) + to_any_with_type_url(&proto, self.protobuf_type_url.clone()) } } impl JsonPublicKey for Secp256PublicKey { fn public_key_type(&self) -> String { - "tendermint/PubKeySecp256k1".to_string() + self.json_type.clone() } } pub fn prepare_secp256k1_public_key( - coin: &dyn CoinContext, + public_key_type: tw::PublicKeyType, public_key_bytes: &[u8], ) -> KeyPairResult { let public_key = secp256k1::PublicKey::try_from(public_key_bytes)?; - match coin.public_key_type() { - PublicKeyType::Secp256k1 => Ok(public_key.compressed().to_vec()), - PublicKeyType::Secp256k1Extended => Ok(public_key.uncompressed().to_vec()), + match public_key_type { + tw::PublicKeyType::Secp256k1 => Ok(public_key.compressed().to_vec()), + tw::PublicKeyType::Secp256k1Extended => Ok(public_key.uncompressed().to_vec()), _ => Err(KeyPairError::InvalidPublicKey), } } diff --git a/rust/tw_proto/src/lib.rs b/rust/tw_proto/src/lib.rs index 23a061c2f47..fd222ddbb4e 100644 --- a/rust/tw_proto/src/lib.rs +++ b/rust/tw_proto/src/lib.rs @@ -46,7 +46,15 @@ where T: MessageInfo + MessageWrite, { let value = serialize(message).expect("Protobuf serialization should never fail"); - let type_url = format!("/{}", T::PATH); + let type_url = type_url::(); + google::protobuf::Any { type_url, value } +} + +pub fn to_any_with_type_url(message: &T, type_url: String) -> google::protobuf::Any +where + T: MessageInfo + MessageWrite, +{ + let value = serialize(message).expect("Protobuf serialization should never fail"); google::protobuf::Any { type_url, value } } diff --git a/src/proto/Cosmos.proto b/src/proto/Cosmos.proto index 323ac93bb08..6ac55b56570 100644 --- a/src/proto/Cosmos.proto +++ b/src/proto/Cosmos.proto @@ -103,8 +103,8 @@ message Message { string type_prefix = 5; } - // cosmos-sdk/MsgSetWithdrawAddress - message SetWithdrawAddress { + // cosmos-sdk/MsgSetWithdrawAddress + message SetWithdrawAddress { string delegator_address = 1; string withdraw_address = 2; string type_prefix = 3; @@ -295,8 +295,8 @@ message Message { // cosmos-sdk/MsgRevoke message AuthRevoke { - string granter = 1; - string grantee = 2; + string granter = 1; + string grantee = 2; string msg_type_url = 3; } @@ -317,8 +317,8 @@ message Message { // cosmos-sdk/MsgVote defines a message to cast a vote. message MsgVote { uint64 proposal_id = 1; - string voter = 2; - VoteOption option = 3; + string voter = 2; + VoteOption option = 3; } message MsgStrideLiquidStakingStake { @@ -367,6 +367,29 @@ enum SigningMode { Protobuf = 1; // Protobuf-serialized (binary), Stargate } +enum TxHasher { + // For Cosmos chain, `Sha256` is used by default. + UseDefault = 0; + Sha256 = 1; + Keccak256 = 2; +} + +enum SignerPublicKeyType { + // Default public key type. + Secp256k1 = 0; + // Mostly used in Cosmos chains with EVM support. + Secp256k1Extended = 1; +} + +// Custom Signer info required to sign a transaction and generate a broadcast JSON message. +message SignerInfo { + // Public key type used to sign a transaction. + // It can be different from the value from `registry.json`. + SignerPublicKeyType public_key_type = 1; + string json_type = 2; + string protobuf_type = 3; +} + // Input data necessary to create a signed transaction. message SigningInput { // Specify if protobuf (a.k.a. Stargate) or earlier JSON serialization is used @@ -397,6 +420,11 @@ message SigningInput { BroadcastMode mode = 9; bytes public_key = 10; + + TxHasher tx_hasher = 11; + + // Optional. If set, use a different Signer info when signing the transaction. + SignerInfo signer_info = 12; } // Result containing the signed and encoded transaction. diff --git a/swift/Tests/Blockchains/NativeZetaChainTests.swift b/swift/Tests/Blockchains/NativeZetaChainTests.swift new file mode 100644 index 00000000000..5c5a8650098 --- /dev/null +++ b/swift/Tests/Blockchains/NativeZetaChainTests.swift @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +import WalletCore +import XCTest + +class NativeZetaChainTests: XCTestCase { + func testAddress() { + let key = PrivateKey(data: Data(hexString: "8d2a3bd62d300a148c89dc8635f87b7a24a951bd1c4e78675fe40e1a640d46ed")!)! + let pubkey = key.getPublicKeySecp256k1(compressed: false) + let address = AnyAddress(publicKey: pubkey, coin: .nativeZetaChain) + let addressFromString = AnyAddress(string: "zeta14py36sx57ud82t9yrks9z6hdsrpn5x6kmxs0ne", coin: .nativeZetaChain)! + + XCTAssertEqual(address.description, addressFromString.description) + } + + func testSign() { + let privateKey = PrivateKey(data: Data(hexString: "8d2a3bd62d300a148c89dc8635f87b7a24a951bd1c4e78675fe40e1a640d46ed")!)! + let publicKey = privateKey.getPublicKeySecp256k1(compressed: false) + let fromAddress = AnyAddress(publicKey: publicKey, coin: .nativeZetaChain) + + let message = CosmosMessage.with { + $0.sendCoinsMessage = CosmosMessage.Send.with { + $0.fromAddress = fromAddress.description + $0.toAddress = "zeta1cscf4ldnkkz7f0wpveur6dpd0d6p2zxnsuu70y" + $0.amounts = [CosmosAmount.with { + $0.amount = "300000000000000000" + $0.denom = "azeta" + }] + } + } + + let fee = CosmosFee.with { + $0.gas = 200000 + } + + let input = CosmosSigningInput.with { + $0.signingMode = .protobuf; + $0.accountNumber = 2726346 + $0.chainID = "athens_7001-1" + $0.sequence = 2 + $0.messages = [message] + $0.fee = fee + $0.privateKey = privateKey.data + $0.txHasher = CosmosTxHasher.keccak256 + $0.signerInfo = CosmosSignerInfo.with { + $0.publicKeyType = CosmosSignerPublicKeyType.secp256K1 + $0.jsonType = "ethermint/PubKeyEthSecp256k1" + $0.protobufType = "/ethermint.crypto.v1.ethsecp256k1.PubKey" + } + } + + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .nativeZetaChain) + + XCTAssertJSONEqual(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CpoBCpcBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEncKK3pldGExNHB5MzZzeDU3dWQ4MnQ5eXJrczl6Nmhkc3JwbjV4NmtteHMwbmUSK3pldGExY3NjZjRsZG5ra3o3ZjB3cHZldXI2ZHBkMGQ2cDJ6eG5zdXU3MHkaGwoFYXpldGESEjMwMDAwMDAwMDAwMDAwMDAwMBJhClkKTwooL2V0aGVybWludC5jcnlwdG8udjEuZXRoc2VjcDI1NmsxLlB1YktleRIjCiECho5+FjRBfbKt/Z/jggW/oP6gGJin/TBWXRP3BWo3wGUSBAoCCAEYAhIEEMCaDBpAgGvqca0w2N9wnHnnxS9HiVud4aQ9lNCumzgNIW6wOR4kvPScacGS1G3kwCw7wyI2NJL8M1eVYjafFIt2FpKl3w==\"}") + } +} diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 5c04bb7bd01..2e4ce928ecd 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -388,6 +388,9 @@ class CoinAddressDerivationTests: XCTestCase { case .tia: let expectedResult = "celestia142j9u5eaduzd7faumygud6ruhdwme98qpwmfv7" assertCoinDerivation(coin, expectedResult, derivedAddress, address) + case .nativeZetaChain: + let expectedResult = "zeta13u6g7vqgw074mgmf2ze2cadzvkz9snlwywj304" + assertCoinDerivation(coin, expectedResult, derivedAddress, address) @unknown default: fatalError() } diff --git a/tests/chains/NativeZetaChain/TWCoinTypeTests.cpp b/tests/chains/NativeZetaChain/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..89e2cdafc2d --- /dev/null +++ b/tests/chains/NativeZetaChain/TWCoinTypeTests.cpp @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#include "TestUtilities.h" +#include +#include + +TEST(TWNativeZetaChainCoinType, TWCoinType) { + const auto coin = TWCoinTypeNativeZetaChain; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("2DBB071DDD47985F4470A21E5943CE95D371AE4BDE2267E201D3553FB2BDCFDE")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("zeta14py36sx57ud82t9yrks9z6hdsrpn5x6kmxs0ne")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "zetachain"); + assertStringsEqual(name, "NativeZetaChain"); + assertStringsEqual(symbol, "ZETA"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainCosmos); + ASSERT_EQ(TWCoinTypeP2pkhPrefix(coin), 0); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0); + assertStringsEqual(txUrl, "https://explorer.zetachain.com/cosmos/tx/2DBB071DDD47985F4470A21E5943CE95D371AE4BDE2267E201D3553FB2BDCFDE"); + assertStringsEqual(accUrl, "https://explorer.zetachain.com/address/zeta14py36sx57ud82t9yrks9z6hdsrpn5x6kmxs0ne"); +} diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index 77695e734eb..6aa4c4604b8 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -384,6 +384,9 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeTia: EXPECT_EQ(address, "celestia1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0g3wnkv"); break; + case TWCoinTypeNativeZetaChain: + EXPECT_EQ(address, "zeta1nk9x9ajk4rgkzhqjjn7hr6w0k0jg2kj027x9uy"); + break; // end_of_coin_address_derivation_tests_marker_do_not_modify // no default branch here, intentionally, to better notice any missing coins } From ca1a2d2b5ac6a9ccdcefbe8f04d5187318212fd7 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Thu, 25 Jan 2024 18:26:59 +0700 Subject: [PATCH 044/128] feat(ZetaChain) Add support for ZetaEVM mainnet (#3670) * feat(ZetaChain): Add `new-cosmos-chain` command * feat(ZetaChain): Add `NativeZetaChain` mainnet * feat(ZetaChain): Add support for custom Cosmos chain * feat(ZetaChain): Add `SignerInfo` to the Cosmos.proto protocol * feat(ZetaChain): Fix zetachain params in registry.json * feat(ZetaChain): Add mobile tests * [CI] Trigger CI * feat(ZetaChain): Update JSON Public Key Type * feat(ZetaChain): Fix `accountPath` in `registry.json` * feat(ZetaChain): Fix rustfmt * feat(ZetaChain): Add ZetaEVM chain * feat(ZetaChain): Add mobile tests * feat(ZetaChain): Fix Swift test * feat(ZetaChain): Slightly refactor NativeEvmos and NativeInjective public key types * feat(ZetaChain): Fix C++ test --- .../blockchains/CoinAddressDerivationTests.kt | 1 + docs/registry.md | 1 + include/TrustWalletCore/TWCoinType.h | 1 + .../core/test/CoinAddressDerivationTests.kt | 1 + registry.json | 28 +++++++++++++++ .../tests/coin_address_derivation_test.rs | 1 + swift/Tests/CoinAddressDerivationTests.swift | 3 +- tests/chains/ZetaEVM/TWCoinTypeTests.cpp | 35 +++++++++++++++++++ tests/common/CoinAddressDerivationTests.cpp | 1 + 9 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 tests/chains/ZetaEVM/TWCoinTypeTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index ed19bae335c..05d20a89744 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -47,6 +47,7 @@ class CoinAddressDerivationTests { FANTOM, CELO, CRONOSCHAIN, SMARTBITCOINCASH, KUCOINCOMMUNITYCHAIN, BOBA, METIS, AURORA, EVMOS, MOONRIVER, MOONBEAM, KAVAEVM, KLAYTN, METER, OKXCHAIN, POLYGONZKEVM, SCROLL, CONFLUXESPACE, ACALAEVM, OPBNB, NEON, BASE, LINEA, GREENFIELD, MANTLE, ZENEON, MANTAPACIFIC, + ZETAEVM, -> assertEquals("0x8f348F300873Fd5DA36950B2aC75a26584584feE", address) RONIN -> assertEquals("ronin:8f348F300873Fd5DA36950B2aC75a26584584feE", address) diff --git a/docs/registry.md b/docs/registry.md index 558ee6febf3..4fd54a26c0c 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -141,6 +141,7 @@ This list is generated from [./registry.json](../registry.json) | 19000118 | Sei | SEI | | | | 20000118 | Stargaze | STARS | | | | 20000714 | BNB Smart Chain | BNB | | | +| 20007000 | Zeta EVM | ZETA | | | | 20009001 | Native Evmos | EVMOS | | | | 21000118 | Celestia | TIA | | | | 30000118 | Juno | JUNO | | | diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 64e5b5f9225..289ff480a82 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -179,6 +179,7 @@ enum TWCoinType { TWCoinTypeTia = 21000118, TWCoinTypeMantaPacific = 169, TWCoinTypeNativeZetaChain = 10007000, + TWCoinTypeZetaEVM = 20007000, // end_of_tw_coin_type_marker_do_not_modify }; diff --git a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt index 6a2a7f54b05..444e9ea9767 100644 --- a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt +++ b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt @@ -40,6 +40,7 @@ class CoinAddressDerivationTests { Fantom, Celo, CronosChain, SmartBitcoinCash, KuCoinCommunityChain, Boba, Metis, Aurora, Evmos, Moonriver, Moonbeam, KavaEvm, Klaytn, Meter, OKXChain, PolygonzkEVM, Scroll, ConfluxeSpace, AcalaEVM, OpBNB, Neon, Base, Linea, Greenfield, Mantle, ZenEON, MantaPacific, + ZetaEVM, -> "0x8f348F300873Fd5DA36950B2aC75a26584584feE" Ronin -> "ronin:8f348F300873Fd5DA36950B2aC75a26584584feE" diff --git a/registry.json b/registry.json index 2df0f2c23c6..e034c34bede 100644 --- a/registry.json +++ b/registry.json @@ -4451,6 +4451,34 @@ "documentation": "https://www.zetachain.com/docs/" } }, + { + "id": "zetaevm", + "name": "Zeta EVM", + "coinId": 20007000, + "symbol": "ZETA", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "7000", + "addressHasher": "keccak256", + "explorer": { + "url": "https://explorer.zetachain.com", + "txPath": "/evm/tx/", + "accountPath": "/address/", + "sampleTx": "0x04cb1201857de29af97b755e51c888454fb96c1f3bb3c1329bb94d5353d5c19e", + "sampleAccount": "0x85539A58F9c88DdDccBaBBfc660968323Fd1e167" + }, + "info": { + "url": "https://www.zetachain.com/", + "documentation": "https://www.zetachain.com/docs/" + } + }, { "id": "ton", "name": "TON", diff --git a/rust/tw_any_coin/tests/coin_address_derivation_test.rs b/rust/tw_any_coin/tests/coin_address_derivation_test.rs index 09bd78cd283..12ce7f772fc 100644 --- a/rust/tw_any_coin/tests/coin_address_derivation_test.rs +++ b/rust/tw_any_coin/tests/coin_address_derivation_test.rs @@ -85,6 +85,7 @@ fn test_coin_address_derivation() { | CoinType::Mantle | CoinType::ZenEON | CoinType::MantaPacific + | CoinType::ZetaEVM // end_of_evm_address_derivation_tests_marker_do_not_modify => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", CoinType::Bitcoin diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 2e4ce928ecd..4d18de7ae96 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -113,7 +113,8 @@ class CoinAddressDerivationTests: XCTestCase { .greenfield, .mantle, .zenEON, - .mantaPacific: + .mantaPacific, + .zetaEVM: let expectedResult = "0x8f348F300873Fd5DA36950B2aC75a26584584feE" assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .ronin: diff --git a/tests/chains/ZetaEVM/TWCoinTypeTests.cpp b/tests/chains/ZetaEVM/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..b8e9eaf9ef9 --- /dev/null +++ b/tests/chains/ZetaEVM/TWCoinTypeTests.cpp @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWZetaEVMCoinType, TWCoinType) { + const auto coin = TWCoinTypeZetaEVM; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto chainId = WRAPS(TWCoinTypeChainId(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x04cb1201857de29af97b755e51c888454fb96c1f3bb3c1329bb94d5353d5c19e")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x85539A58F9c88DdDccBaBBfc660968323Fd1e167")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "zetaevm"); + assertStringsEqual(name, "Zeta EVM"); + assertStringsEqual(symbol, "ZETA"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainEthereum); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); + assertStringsEqual(chainId, "7000"); + assertStringsEqual(txUrl, "https://explorer.zetachain.com/evm/tx/0x04cb1201857de29af97b755e51c888454fb96c1f3bb3c1329bb94d5353d5c19e"); + assertStringsEqual(accUrl, "https://explorer.zetachain.com/address/0x85539A58F9c88DdDccBaBBfc660968323Fd1e167"); +} diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index 6aa4c4604b8..f052b41322b 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -83,6 +83,7 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeMantle: case TWCoinTypeZenEON: case TWCoinTypeMantaPacific: + case TWCoinTypeZetaEVM: // end_of_evm_address_derivation_tests_marker_do_not_modify EXPECT_EQ(address, "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); break; From 24f24c118506881a1e3bfdc3655013db26fe38ac Mon Sep 17 00:00:00 2001 From: Fabio Lama <42901763+lamafab@users.noreply.github.com> Date: Thu, 25 Jan 2024 17:05:48 +0100 Subject: [PATCH 045/128] [BitcoinV2] Implement InputSelector::SelectDescending (#3672) --- rust/tw_bitcoin/tests/input_selection.rs | 127 +++++++++++++++++++++++ rust/tw_utxo/src/compiler.rs | 9 +- src/proto/Utxo.proto | 3 + 3 files changed, 136 insertions(+), 3 deletions(-) diff --git a/rust/tw_bitcoin/tests/input_selection.rs b/rust/tw_bitcoin/tests/input_selection.rs index 38afea369e5..3105370c378 100644 --- a/rust/tw_bitcoin/tests/input_selection.rs +++ b/rust/tw_bitcoin/tests/input_selection.rs @@ -502,6 +502,133 @@ fn input_selection_select_ascending() { assert!(tx.outputs[1].control_block.is_empty()); } +#[test] +fn input_selection_select_descending() { + let coin = TestCoinContext::default(); + + let alice_private_key = hex(ALICE_PRIVATE_KEY); + let alice_pubkey = hex(ALICE_PUBKEY); + let bob_pubkey = hex(BOB_PUBKEY); + + let txid: Vec = vec![1; 32]; + let tx1 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let txid: Vec = vec![2; 32]; + let tx2 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC * 3, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let txid: Vec = vec![3; 32]; + let tx3 = Proto::Input { + txid: txid.as_slice().into(), + vout: 0, + value: ONE_BTC * 2, + sighash_type: UtxoProto::SighashType::All, + to_recipient: ProtoInputRecipient::builder(Proto::mod_Input::InputBuilder { + variant: ProtoInputBuilder::p2wpkh(alice_pubkey.as_slice().into()), + }), + ..Default::default() + }; + + let out1 = Proto::Output { + value: ONE_BTC * 4, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(bob_pubkey.as_slice().into()), + }), + }), + }; + + let change_output = Proto::Output { + // Will be set for us. + value: 0, + to_recipient: ProtoOutputRecipient::builder(Proto::mod_Output::OutputBuilder { + variant: ProtoOutputBuilder::p2wpkh(Proto::ToPublicKeyOrHash { + to_address: ProtoPubkeyOrHash::pubkey(alice_pubkey.as_slice().into()), + }), + }), + }; + + let signing = Proto::SigningInput { + private_key: alice_private_key.as_slice().into(), + // Select descending. + input_selector: UtxoProto::InputSelector::SelectDescending, + inputs: vec![tx1, tx2.clone(), tx3.clone()], + outputs: vec![out1.clone()], + // We set the change output accordingly. + change_output: Some(change_output), + fee_per_vb: SAT_VBYTE, + ..Default::default() + }; + + let signed = BitcoinEntry.sign(&coin, signing); + + assert_eq!(signed.error, Proto::Error::OK); + assert!(signed.error_message.is_empty()); + assert_eq!(signed.weight, 832); + assert_eq!(signed.fee, (signed.weight + 3) / 4 * SAT_VBYTE); + assert_eq!(signed.fee, 10_400); + + let tx = signed.transaction.unwrap(); + assert_eq!(tx.version, 2); + + // Inputs (descending; second > third). + assert_eq!(tx.inputs.len(), 2); + + assert_eq!(tx.inputs[0].txid, tx2.txid); + assert_eq!(tx.inputs[0].txid, vec![2; 32]); + assert_eq!(tx.inputs[0].vout, 0); + assert_eq!(tx.inputs[0].sequence, u32::MAX); + assert!(tx.inputs[0].script_sig.is_empty()); + assert!(!tx.inputs[0].witness_items.is_empty()); + + assert_eq!(tx.inputs[1].txid, tx3.txid); + assert_eq!(tx.inputs[1].txid, vec![3; 32]); + assert_eq!(tx.inputs[1].vout, 0); + assert_eq!(tx.inputs[1].sequence, u32::MAX); + assert!(tx.inputs[1].script_sig.is_empty()); + assert!(!tx.inputs[1].witness_items.is_empty()); + + // Outputs. + assert_eq!(tx.outputs.len(), 2); + + // Output for recipient. + assert!(!tx.outputs[0].script_pubkey.is_empty()); + assert_eq!(tx.outputs[0].value, out1.value); + assert_eq!(tx.outputs[0].value, ONE_BTC * 4); + assert!(tx.outputs[0].taproot_payload.is_empty()); + assert!(tx.outputs[0].control_block.is_empty()); + + // Change output. + assert!(!tx.outputs[1].script_pubkey.is_empty()); + assert_eq!( + tx.outputs[1].value, + tx2.value + tx3.value - out1.value - signed.fee + ); + assert_eq!( + tx.outputs[1].value, + (ONE_BTC * 3) + (ONE_BTC * 2) - (ONE_BTC * 4) - 10_400, + ); + assert!(tx.outputs[1].taproot_payload.is_empty()); + assert!(tx.outputs[1].control_block.is_empty()); +} + #[test] fn input_selection_use_all() { let coin = TestCoinContext::default(); diff --git a/rust/tw_utxo/src/compiler.rs b/rust/tw_utxo/src/compiler.rs index 07a98712be1..822cd8a3542 100644 --- a/rust/tw_utxo/src/compiler.rs +++ b/rust/tw_utxo/src/compiler.rs @@ -80,10 +80,11 @@ impl Compiler { )); } - // If the input selector is InputSelector::SelectAscending, we sort the - // input first. + // If enabled, sort the order of the UTXOs. if let Proto::InputSelector::SelectAscending = proto.input_selector { proto.inputs.sort_by(|a, b| a.value.cmp(&b.value)); + } else if let Proto::InputSelector::SelectDescending = proto.input_selector { + proto.inputs.sort_by(|a, b| b.value.cmp(&a.value)); } // Add change output generation is enabled, push it to the proto structure. @@ -128,7 +129,9 @@ impl Compiler { proto.inputs.push(txin); } }, - Proto::InputSelector::SelectInOrder | Proto::InputSelector::SelectAscending => { + Proto::InputSelector::SelectInOrder + | Proto::InputSelector::SelectAscending + | Proto::InputSelector::SelectDescending => { let mut total_input_amount = 0; let mut total_input_weight = 0; diff --git a/src/proto/Utxo.proto b/src/proto/Utxo.proto index 097d4e67a1f..b5b2bbe40e8 100644 --- a/src/proto/Utxo.proto +++ b/src/proto/Utxo.proto @@ -52,6 +52,9 @@ enum InputSelector { // Automatically select enough inputs in the given order to cover the // outputs of the transaction. SelectInOrder = 1; + // Automatically select enough inputs in an descending order to cover the + // outputs of the transaction. + SelectDescending = 2; // Use all the inputs provided in the given order. UseAll = 10; } From d42429becefeec92ec44b9411ccb1c1a5ecf24b5 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Thu, 25 Jan 2024 23:10:53 +0700 Subject: [PATCH 046/128] feat(dYdX): Add support for dYdX Cosmos chain (#3671) * feat(dYdX): Add support for dYdX Cosmos chain * feat(dYdX): Fix Kotlin tests --- .../blockchains/CoinAddressDerivationTests.kt | 1 + .../app/blockchains/dydx/TestDydxAddress.kt | 27 ++++++ docs/registry.md | 1 + include/TrustWalletCore/TWCoinType.h | 1 + .../core/test/CoinAddressDerivationTests.kt | 1 + registry.json | 32 +++++++ .../tests/chains/cosmos/cosmos_address.rs | 6 +- .../tests/chains/dydx/dydx_address.rs | 83 +++++++++++++++++++ rust/tw_any_coin/tests/chains/dydx/mod.rs | 5 ++ rust/tw_any_coin/tests/chains/mod.rs | 1 + .../tests/coin_address_derivation_test.rs | 1 + swift/Tests/Blockchains/DydxTests.swift | 17 ++++ swift/Tests/CoinAddressDerivationTests.swift | 3 + tests/chains/Dydx/TWCoinTypeTests.cpp | 29 +++++++ tests/common/CoinAddressDerivationTests.cpp | 3 + 15 files changed, 208 insertions(+), 3 deletions(-) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/dydx/TestDydxAddress.kt create mode 100644 rust/tw_any_coin/tests/chains/dydx/dydx_address.rs create mode 100644 rust/tw_any_coin/tests/chains/dydx/mod.rs create mode 100644 swift/Tests/Blockchains/DydxTests.swift create mode 100644 tests/chains/Dydx/TWCoinTypeTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index 05d20a89744..0d0e8f18103 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -153,5 +153,6 @@ class CoinAddressDerivationTests { INTERNETCOMPUTER -> assertEquals("b9a13d974ee9db036d5abc5b66ace23e513cb5676f3996626c7717c339a3ee87", address) TIA -> assertEquals("celestia142j9u5eaduzd7faumygud6ruhdwme98qpwmfv7", address) NATIVEZETACHAIN -> assertEquals("zeta13u6g7vqgw074mgmf2ze2cadzvkz9snlwywj304", address) + DYDX -> assertEquals("dydx142j9u5eaduzd7faumygud6ruhdwme98qeayaky", address) } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/dydx/TestDydxAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/dydx/TestDydxAddress.kt new file mode 100644 index 00000000000..408f8fb4ae3 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/dydx/TestDydxAddress.kt @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +package com.trustwallet.core.app.blockchains.dydx + +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.* + +class TestDydxAddress { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testAddress() { + val key = PrivateKey("a498a9ee41af9bab5ef2a8be63d5c970135c3c109e70efc8c56c534e6636b433".toHexByteArray()) + val pubKey = key.getPublicKeySecp256k1(true) + val address = AnyAddress(pubKey, CoinType.DYDX) + val expected = AnyAddress("dydx1mry47pkga5tdswtluy0m8teslpalkdq0hc72uz", CoinType.DYDX) + + assertEquals(address.description(), expected.description()) + } +} diff --git a/docs/registry.md b/docs/registry.md index 4fd54a26c0c..a2023da41f1 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -144,6 +144,7 @@ This list is generated from [./registry.json](../registry.json) | 20007000 | Zeta EVM | ZETA | | | | 20009001 | Native Evmos | EVMOS | | | | 21000118 | Celestia | TIA | | | +| 22000118 | dYdX | DYDX | | | | 30000118 | Juno | JUNO | | | | 30000714 | TBNB | BNB | | | | 40000118 | Stride | STRD | | | diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 289ff480a82..e65dc4878e7 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -180,6 +180,7 @@ enum TWCoinType { TWCoinTypeMantaPacific = 169, TWCoinTypeNativeZetaChain = 10007000, TWCoinTypeZetaEVM = 20007000, + TWCoinTypeDydx = 22000118, // end_of_tw_coin_type_marker_do_not_modify }; diff --git a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt index 444e9ea9767..a586146c54d 100644 --- a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt +++ b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt @@ -146,5 +146,6 @@ class CoinAddressDerivationTests { InternetComputer -> "b9a13d974ee9db036d5abc5b66ace23e513cb5676f3996626c7717c339a3ee87" Tia -> "celestia142j9u5eaduzd7faumygud6ruhdwme98qpwmfv7" NativeZetaChain -> "zeta13u6g7vqgw074mgmf2ze2cadzvkz9snlwywj304" + Dydx -> "dydx142j9u5eaduzd7faumygud6ruhdwme98qeayaky" } } diff --git a/registry.json b/registry.json index e034c34bede..ca3f9cf2660 100644 --- a/registry.json +++ b/registry.json @@ -4361,6 +4361,38 @@ "documentation": "https://docs.agoric.com" } }, + { + "id": "dydx", + "name": "Dydx", + "displayName": "dYdX", + "coinId": 22000118, + "symbol": "DYDX", + "decimals": 18, + "blockchain": "Cosmos", + "derivation": [ + { + "path": "m/44'/118'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1", + "hrp": "dydx", + "chainId": "dydx-mainnet-1", + "addressHasher": "sha256ripemd", + "explorer": { + "url": "https://www.mintscan.io/dydx", + "txPath": "/tx/", + "accountPath": "/address/", + "sampleTx": "F236222E4F7C92FA84711FD6451ED22DD56CBDFA319BFDAFB99A21E4E9B9EC2F", + "sampleAccount": "dydx1adl7usw7z2dnysyn7wvrghu0u0q6gr7jqs4gtt" + }, + "info": { + "url": "https://dydx.exchange", + "source": "https://github.com/dydxprotocol", + "rpc": "https://dydx-dao-api.polkachu.com", + "documentation": "https://docs.dydx.exchange" + } + }, { "id": "nativeinjective", "name": "NativeInjective", diff --git a/rust/tw_any_coin/tests/chains/cosmos/cosmos_address.rs b/rust/tw_any_coin/tests/chains/cosmos/cosmos_address.rs index 51bf414bec3..a9acab72f63 100644 --- a/rust/tw_any_coin/tests/chains/cosmos/cosmos_address.rs +++ b/rust/tw_any_coin/tests/chains/cosmos/cosmos_address.rs @@ -73,9 +73,9 @@ fn test_any_address_is_valid_bech32() { fn test_any_address_create_bech32_with_public_key() { test_address_create_bech32_with_public_key(AddressCreateBech32WithPublicKey { coin: CoinType::Cosmos, - private_key: "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", + private_key: "a498a9ee41af9bab5ef2a8be63d5c970135c3c109e70efc8c56c534e6636b433", public_key_type: PublicKeyType::Secp256k1, - hrp: "juno", - expected: "juno1ten42eesehw0ktddcp0fws7d3ycsqez3fksy86", + hrp: "dydx", + expected: "dydx1mry47pkga5tdswtluy0m8teslpalkdq0hc72uz", }); } diff --git a/rust/tw_any_coin/tests/chains/dydx/dydx_address.rs b/rust/tw_any_coin/tests/chains/dydx/dydx_address.rs new file mode 100644 index 00000000000..8ba214f3e41 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/dydx/dydx_address.rs @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_any_coin::test_utils::address_utils::{ + test_address_bech32_is_valid, test_address_create_bech32_with_public_key, + test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, + AddressBech32IsValid, AddressCreateBech32WithPublicKey, +}; +use tw_coin_registry::coin_type::CoinType; +use tw_keypair::tw::PublicKeyType; + +#[test] +fn test_dydx_address_normalization() { + test_address_normalization( + CoinType::Dydx, + "dydx1adl7usw7z2dnysyn7wvrghu0u0q6gr7jqs4gtt", + "dydx1adl7usw7z2dnysyn7wvrghu0u0q6gr7jqs4gtt", + ); +} + +#[test] +fn test_dydx_address_is_valid() { + test_address_valid( + CoinType::Dydx, + "dydxvaloper1gf9yvztyvnc3aqrkjd8hfnaf8pk56sl7mzfage", + ); +} + +#[test] +fn test_dydx_address_invalid() { + test_address_invalid( + CoinType::Dydx, + "dydxvaloper1gf9yvztyvnc3aqrkjd8hfnaf8pk56sl7mz", + ); + // Cosmos has a different `hrp`. + test_address_invalid( + CoinType::Cosmos, + "dydx1adl7usw7z2dnysyn7wvrghu0u0q6gr7jqs4gtt", + ); +} + +#[test] +fn test_dydx_address_get_data() { + test_address_get_data( + CoinType::Dydx, + "dydxvaloper1gf9yvztyvnc3aqrkjd8hfnaf8pk56sl7mzfage", + "424a46096464f11e8076934f74cfa9386d4d43fe", + ); +} + +#[test] +fn test_dydx_is_valid_bech32() { + // Dydx address must be valid if its Base32 prefix passed. + test_address_bech32_is_valid(AddressBech32IsValid { + coin: CoinType::Dydx, + address: "dydx1adl7usw7z2dnysyn7wvrghu0u0q6gr7jqs4gtt", + hrp: "dydx", + }); + // Dydx address must be valid for the standard Cosmos hub if its Base32 prefix passed. + test_address_bech32_is_valid(AddressBech32IsValid { + coin: CoinType::Cosmos, + address: "dydx1adl7usw7z2dnysyn7wvrghu0u0q6gr7jqs4gtt", + hrp: "dydx", + }); + // Cosmos address must be valid with "cosmos" hrp. + test_address_bech32_is_valid(AddressBech32IsValid { + coin: CoinType::Dydx, + address: "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02", + hrp: "cosmos", + }); +} + +#[test] +fn test_any_address_create_bech32_with_public_key() { + test_address_create_bech32_with_public_key(AddressCreateBech32WithPublicKey { + coin: CoinType::Cosmos, + private_key: "a498a9ee41af9bab5ef2a8be63d5c970135c3c109e70efc8c56c534e6636b433", + public_key_type: PublicKeyType::Secp256k1, + hrp: "dydx", + expected: "dydx1mry47pkga5tdswtluy0m8teslpalkdq0hc72uz", + }); +} diff --git a/rust/tw_any_coin/tests/chains/dydx/mod.rs b/rust/tw_any_coin/tests/chains/dydx/mod.rs new file mode 100644 index 00000000000..d559c269a8b --- /dev/null +++ b/rust/tw_any_coin/tests/chains/dydx/mod.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +mod dydx_address; diff --git a/rust/tw_any_coin/tests/chains/mod.rs b/rust/tw_any_coin/tests/chains/mod.rs index 7a9f7285560..cefb0e37ae4 100644 --- a/rust/tw_any_coin/tests/chains/mod.rs +++ b/rust/tw_any_coin/tests/chains/mod.rs @@ -6,6 +6,7 @@ mod aptos; mod binance; mod bitcoin; mod cosmos; +mod dydx; mod ethereum; mod greenfield; mod internet_computer; diff --git a/rust/tw_any_coin/tests/coin_address_derivation_test.rs b/rust/tw_any_coin/tests/coin_address_derivation_test.rs index 12ce7f772fc..9cf10ad244a 100644 --- a/rust/tw_any_coin/tests/coin_address_derivation_test.rs +++ b/rust/tw_any_coin/tests/coin_address_derivation_test.rs @@ -147,6 +147,7 @@ fn test_coin_address_derivation() { CoinType::Binance => "bnb1ten42eesehw0ktddcp0fws7d3ycsqez3aqvnpg", CoinType::TBinance => "tbnb1ten42eesehw0ktddcp0fws7d3ycsqez3n49hpe", CoinType::NativeZetaChain => "zeta14s0vgnj0pjnazu4hsqlksdk7slah9vcfcwctsr", + CoinType::Dydx => "dydx1ten42eesehw0ktddcp0fws7d3ycsqez3kaamq3", // end_of_coin_address_derivation_tests_marker_do_not_modify _ => panic!("{:?} must be covered", coin), }; diff --git a/swift/Tests/Blockchains/DydxTests.swift b/swift/Tests/Blockchains/DydxTests.swift new file mode 100644 index 00000000000..a464c834c8a --- /dev/null +++ b/swift/Tests/Blockchains/DydxTests.swift @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +import WalletCore +import XCTest + +class DydxTests: XCTestCase { + func testAddress() { + let key = PrivateKey(data: Data(hexString: "a498a9ee41af9bab5ef2a8be63d5c970135c3c109e70efc8c56c534e6636b433")!)! + let pubkey = key.getPublicKeySecp256k1(compressed: true) + let address = AnyAddress(publicKey: pubkey, coin: .dydx) + let addressFromString = AnyAddress(string: "dydx1mry47pkga5tdswtluy0m8teslpalkdq0hc72uz", coin: .dydx)! + + XCTAssertEqual(address.description, addressFromString.description) + } +} diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 4d18de7ae96..339b2ccde19 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -392,6 +392,9 @@ class CoinAddressDerivationTests: XCTestCase { case .nativeZetaChain: let expectedResult = "zeta13u6g7vqgw074mgmf2ze2cadzvkz9snlwywj304" assertCoinDerivation(coin, expectedResult, derivedAddress, address) + case .dydx: + let expectedResult = "dydx142j9u5eaduzd7faumygud6ruhdwme98qeayaky" + assertCoinDerivation(coin, expectedResult, derivedAddress, address) @unknown default: fatalError() } diff --git a/tests/chains/Dydx/TWCoinTypeTests.cpp b/tests/chains/Dydx/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..1dd49ee8eb4 --- /dev/null +++ b/tests/chains/Dydx/TWCoinTypeTests.cpp @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#include "TestUtilities.h" +#include +#include + +TEST(TWDydxCoinType, TWCoinType) { + const auto coin = TWCoinTypeDydx; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("F236222E4F7C92FA84711FD6451ED22DD56CBDFA319BFDAFB99A21E4E9B9EC2F")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("dydx1adl7usw7z2dnysyn7wvrghu0u0q6gr7jqs4gtt")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "dydx"); + assertStringsEqual(name, "dYdX"); + assertStringsEqual(symbol, "DYDX"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainCosmos); + ASSERT_EQ(TWCoinTypeP2pkhPrefix(coin), 0); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0); + assertStringsEqual(txUrl, "https://www.mintscan.io/dydx/tx/F236222E4F7C92FA84711FD6451ED22DD56CBDFA319BFDAFB99A21E4E9B9EC2F"); + assertStringsEqual(accUrl, "https://www.mintscan.io/dydx/address/dydx1adl7usw7z2dnysyn7wvrghu0u0q6gr7jqs4gtt"); +} diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index f052b41322b..93411f2e8cf 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -388,6 +388,9 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeNativeZetaChain: EXPECT_EQ(address, "zeta1nk9x9ajk4rgkzhqjjn7hr6w0k0jg2kj027x9uy"); break; + case TWCoinTypeDydx: + EXPECT_EQ(address, "dydx1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0sz38vk"); + break; // end_of_coin_address_derivation_tests_marker_do_not_modify // no default branch here, intentionally, to better notice any missing coins } From 6f15eb4d8dd006c03c2592e9118ed8451f1b4112 Mon Sep 17 00:00:00 2001 From: wh Date: Thu, 1 Feb 2024 20:30:25 +0800 Subject: [PATCH 047/128] [EVM]Make public key as optional (#3673) * [EVM]Make public key as optional As evm public key is optional, we need to keep compatible with before implemented in C++. Otherwise we have to change our existing prod codes to adapt this change. * [Rust]Fix format check --- rust/tw_coin_entry/src/common/compile_input.rs | 16 ++++++++++++++++ rust/tw_evm/src/modules/compiler.rs | 4 ++-- .../chains/Ethereum/TransactionCompilerTests.cpp | 6 ++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/rust/tw_coin_entry/src/common/compile_input.rs b/rust/tw_coin_entry/src/common/compile_input.rs index 587d1860931..e079d5a5c7b 100644 --- a/rust/tw_coin_entry/src/common/compile_input.rs +++ b/rust/tw_coin_entry/src/common/compile_input.rs @@ -33,4 +33,20 @@ impl SingleSignaturePubkey { public_key, }) } + + pub fn from_sign_list(signatures: Vec) -> SigningResult { + if signatures.len() > 1 { + return Err(SigningError(SigningErrorType::Error_no_support_n2n)); + } + + let signature = signatures + .into_iter() + .next() + .ok_or(SigningError(SigningErrorType::Error_signatures_count))?; + + Ok(SingleSignaturePubkey { + signature, + public_key: PublicKeyBytes::default(), + }) + } } diff --git a/rust/tw_evm/src/modules/compiler.rs b/rust/tw_evm/src/modules/compiler.rs index 2aee8c8ff57..cd961f6c66c 100644 --- a/rust/tw_evm/src/modules/compiler.rs +++ b/rust/tw_evm/src/modules/compiler.rs @@ -57,12 +57,12 @@ impl Compiler { fn compile_impl( input: Proto::SigningInput<'_>, signatures: Vec, - public_keys: Vec, + _: Vec, ) -> SigningResult> { let SingleSignaturePubkey { signature, public_key: _, - } = SingleSignaturePubkey::from_sign_pubkey_list(signatures, public_keys)?; + } = SingleSignaturePubkey::from_sign_list(signatures)?; let signature = secp256k1::Signature::from_bytes(&signature)?; let chain_id = U256::from_big_endian_slice(&input.chain_id)?; diff --git a/tests/chains/Ethereum/TransactionCompilerTests.cpp b/tests/chains/Ethereum/TransactionCompilerTests.cpp index b8f5ceae311..3660ed2e610 100644 --- a/tests/chains/Ethereum/TransactionCompilerTests.cpp +++ b/tests/chains/Ethereum/TransactionCompilerTests.cpp @@ -71,6 +71,12 @@ TEST(EthereumCompiler, CompileWithSignatures) { auto outputData = TransactionCompiler::compileWithSignatures(coin, txInputData, {signature}, {publicKeyData}); + // We dont care about public key in ethereum. It is not part of the transaction. + auto outputDataWithoutPubKey = + TransactionCompiler::compileWithSignatures(coin, txInputData, {signature}, {}); + + EXPECT_EQ(outputData, outputDataWithoutPubKey); + const auto ExpectedTx = "f86c0b8504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a0" "360a84fb41ad07f07c845fedc34cde728421803ebbaae392fc39c116b29fc07ba053bd9d1376e15a191d844db4" From e13420c9becb7e4b104e965d078f3519e719a0c6 Mon Sep 17 00:00:00 2001 From: h8s <360470+hewigovens@users.noreply.github.com> Date: Fri, 2 Feb 2024 15:18:18 +0900 Subject: [PATCH 048/128] Fix broken links (#3674) --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 78085a5c69e..c425811aa56 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,13 @@ It is a core part of the popular [Trust Wallet](https://trustwallet.com), and so Most of the code is C++ with a set of strict C interfaces, and idiomatic interfaces for supported languages: Swift for iOS and Java (Kotlin) for Android. -![iOS CI](https://github.com/trustwallet/wallet-core/workflows/iOS%20CI/badge.svg) -![Android CI](https://github.com/trustwallet/wallet-core/workflows/Android%20CI/badge.svg) -![Linux CI](https://github.com/trustwallet/wallet-core/workflows/Linux%20CI/badge.svg) -![Wasm CI](https://github.com/trustwallet/wallet-core/workflows/Wasm%20CI/badge.svg) -![Kotlin CI](https://github.com/trustwallet/wallet-core/workflows/Kotlin%20CI/badge.svg) -![Docker CI](https://github.com/trustwallet/wallet-core/workflows/Docker%20CI/badge.svg) +[![iOS CI](https://github.com/trustwallet/wallet-core/actions/workflows/ios-ci.yml/badge.svg)](https://github.com/trustwallet/wallet-core/actions/workflows/ios-ci.yml) +[![Android CI](https://github.com/trustwallet/wallet-core/actions/workflows/android-ci.yml/badge.svg)](https://github.com/trustwallet/wallet-core/actions/workflows/android-ci.yml) +[![Linux CI](https://github.com/trustwallet/wallet-core/actions/workflows/linux-ci.yml/badge.svg)](https://github.com/trustwallet/wallet-core/actions/workflows/linux-ci.yml) +[![Rust CI](https://github.com/trustwallet/wallet-core/actions/workflows/linux-ci-rust.yml/badge.svg)](https://github.com/trustwallet/wallet-core/actions/workflows/linux-ci-rust.yml) +[![Wasm CI](https://github.com/trustwallet/wallet-core/actions/workflows/wasm-ci.yml/badge.svg)](https://github.com/trustwallet/wallet-core/actions/workflows/wasm-ci.yml) +[![Kotlin CI](https://github.com/trustwallet/wallet-core/actions/workflows/kotlin-ci.yml/badge.svg)](https://github.com/trustwallet/wallet-core/actions/workflows/kotlin-ci.yml) +[![Docker CI](https://github.com/trustwallet/wallet-core/actions/workflows/docker.yml/badge.svg)](https://github.com/trustwallet/wallet-core/actions/workflows/docker.yml) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=TrustWallet_wallet-core&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=TrustWallet_wallet-core) [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/trustwallet/wallet-core) @@ -101,7 +102,7 @@ Please check out the [Kotlin Multiplatform sample](https://github.com/trustwalle Projects using Trust Wallet Core. Add yours too! -[Trust Wallet](https://trustwallet.com) +[Trust Wallet](https://trustwallet.com) [Coinpaprika](https://coinpaprika.com/) | [crypto.com](https://crypto.com) From 1d7795376afe740bbb5dc6f36a54b0b7895741ee Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 7 Feb 2024 11:33:18 +0700 Subject: [PATCH 049/128] [Solana]: Update blockhash and re-sign transaction (#3678) * [Solana]: Add Solana skeleton * [Solana]: Implement `SolanaAddress` * [Solana]: Add transaction serialization/deserialization * [Solana]: Versioned transaction signing * [Solana]: Add `tw_solana_transaction_update_blockhash_and_sign` in Rust * [Solana]: Add TWSolanaTransactionUpdateBlockhashAndSign * [Solana]: Add Kotlin test * [CI] Trigger CI * [Solana]: Do not sign a transaction if there is no private keys provided * Add `SigningOutput::message_encoded` that needs to be sent to a fee estimation service * [Solana]: Remove extra comments --- .../solana/TestSolanaTransaction.kt | 42 ++ include/TrustWalletCore/TWSolanaTransaction.h | 33 ++ rust/Cargo.lock | 26 ++ rust/Cargo.toml | 1 + rust/chains/tw_solana/Cargo.toml | 17 + rust/chains/tw_solana/src/address.rs | 82 ++++ rust/chains/tw_solana/src/compiler.rs | 50 +++ rust/chains/tw_solana/src/entry.rs | 91 +++++ rust/chains/tw_solana/src/lib.rs | 15 + rust/chains/tw_solana/src/modules/mod.rs | 6 + .../chains/tw_solana/src/modules/tx_signer.rs | 49 +++ rust/chains/tw_solana/src/modules/utils.rs | 78 ++++ rust/chains/tw_solana/src/signer.rs | 27 ++ .../tw_solana/src/transaction/legacy.rs | 55 +++ rust/chains/tw_solana/src/transaction/mod.rs | 177 ++++++++ .../tw_solana/src/transaction/short_vec.rs | 386 ++++++++++++++++++ rust/chains/tw_solana/src/transaction/v0.rs | 58 +++ .../tw_solana/src/transaction/versioned.rs | 222 ++++++++++ .../tests/update_blockhash_and_sign.rs | 27 ++ rust/tw_any_coin/tests/chains/mod.rs | 1 + rust/tw_any_coin/tests/chains/solana/mod.rs | 7 + .../tests/chains/solana/solana_address.rs | 48 +++ .../tests/chains/solana/solana_compile.rs | 9 + .../tests/chains/solana/solana_sign.rs | 9 + .../tests/coin_address_derivation_test.rs | 1 + rust/tw_coin_entry/src/error.rs | 5 + rust/tw_coin_registry/Cargo.toml | 1 + rust/tw_coin_registry/src/blockchain_type.rs | 1 + rust/tw_coin_registry/src/dispatcher.rs | 3 + rust/tw_hash/src/hash_array.rs | 55 +++ rust/tw_hash/src/lib.rs | 2 +- .../src/test_utils/tw_data_vector_helper.rs | 6 + rust/wallet_core_rs/Cargo.toml | 4 +- rust/wallet_core_rs/src/ffi/mod.rs | 1 + rust/wallet_core_rs/src/ffi/solana/mod.rs | 6 + .../src/ffi/solana/transaction.rs | 51 +++ .../tests/solana_transaction.rs | 115 ++++++ src/DataVector.h | 25 ++ src/interface/TWSolanaTransaction.cpp | 28 ++ src/interface/TWTransactionCompiler.cpp | 16 +- src/proto/Solana.proto | 4 + tests/chains/Solana/TWSolanaTransaction.cpp | 36 ++ 42 files changed, 1859 insertions(+), 17 deletions(-) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/solana/TestSolanaTransaction.kt create mode 100644 include/TrustWalletCore/TWSolanaTransaction.h create mode 100644 rust/chains/tw_solana/Cargo.toml create mode 100644 rust/chains/tw_solana/src/address.rs create mode 100644 rust/chains/tw_solana/src/compiler.rs create mode 100644 rust/chains/tw_solana/src/entry.rs create mode 100644 rust/chains/tw_solana/src/lib.rs create mode 100644 rust/chains/tw_solana/src/modules/mod.rs create mode 100644 rust/chains/tw_solana/src/modules/tx_signer.rs create mode 100644 rust/chains/tw_solana/src/modules/utils.rs create mode 100644 rust/chains/tw_solana/src/signer.rs create mode 100644 rust/chains/tw_solana/src/transaction/legacy.rs create mode 100644 rust/chains/tw_solana/src/transaction/mod.rs create mode 100644 rust/chains/tw_solana/src/transaction/short_vec.rs create mode 100644 rust/chains/tw_solana/src/transaction/v0.rs create mode 100644 rust/chains/tw_solana/src/transaction/versioned.rs create mode 100644 rust/chains/tw_solana/tests/update_blockhash_and_sign.rs create mode 100644 rust/tw_any_coin/tests/chains/solana/mod.rs create mode 100644 rust/tw_any_coin/tests/chains/solana/solana_address.rs create mode 100644 rust/tw_any_coin/tests/chains/solana/solana_compile.rs create mode 100644 rust/tw_any_coin/tests/chains/solana/solana_sign.rs create mode 100644 rust/wallet_core_rs/src/ffi/solana/mod.rs create mode 100644 rust/wallet_core_rs/src/ffi/solana/transaction.rs create mode 100644 rust/wallet_core_rs/tests/solana_transaction.rs create mode 100644 src/DataVector.h create mode 100644 src/interface/TWSolanaTransaction.cpp create mode 100644 tests/chains/Solana/TWSolanaTransaction.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/solana/TestSolanaTransaction.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/solana/TestSolanaTransaction.kt new file mode 100644 index 00000000000..5f26065966f --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/solana/TestSolanaTransaction.kt @@ -0,0 +1,42 @@ +package com.trustwallet.core.app.blockchains.solana + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.toHex +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.Base58 +import wallet.core.java.AnySigner +import wallet.core.jni.CoinType.SOLANA +import wallet.core.jni.SolanaTransaction +import wallet.core.jni.DataVector +import wallet.core.jni.proto.Common.SigningError +import wallet.core.jni.proto.Solana +import wallet.core.jni.proto.Solana.SigningOutput + +class TestSolanaTransaction { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testUpdateBlockhashAndSign() { + val encodedTx = "AnQTYwZpkm3fs4SdLxnV6gQj3hSLsyacpxDdLMALYWObm722f79IfYFTbZeFK9xHtMumiDOWAM2hHQP4r/GtbARpncaXgOVFv7OgbRLMbuCEJHO1qwcdCbtH72VzyzU8yw9sqqHIAaCUE8xaQTgT6Z5IyZfeyMe2QGJIfOjz65UPAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8Aqe6sdLXiXSDILEtzckCjkjchiSf6zVGpMYiAE5BE2IqHAQUEAgQDAQoMoA8AAAAAAAAG" + val newBlockhash = "CyPYVsYWrsJNfVpi8aazu7WsrswNFuDd385z6GNoBGUg" + + val myPrivateKey = "7f0932159226ddec9e1a4b0b8fe7cdc135049f9e549a867d722aa720dd64f32e".toHexByteArray() + val feePayerPrivateKey = "4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7".toHexByteArray() + + val privateKeys = DataVector() + privateKeys.add(myPrivateKey) + privateKeys.add(feePayerPrivateKey) + + val outputData = SolanaTransaction.updateBlockhashAndSign(encodedTx, newBlockhash, privateKeys) + val output = SigningOutput.parseFrom(outputData) + + assertEquals(output.error, SigningError.OK) + val expectedString = "Ajzc/Tke0CG8Cew5qFa6xZI/7Ya3DN0M8Ige6tKPsGzhg8Bw9DqL18KUrEZZ1F4YqZBo4Rv+FsDT8A7Nss7p4A6BNVZzzGprCJqYQeNg0EVIbmPc6mDitNniHXGeKgPZ6QZbM4FElw9O7IOFTpOBPvQFeqy0vZf/aayncL8EK/UEAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqbHiki6ThNH3auuyZPQpJntnN0mA//56nMpK/6HIuu8xAQUEAgQDAQoMoA8AAAAAAAAG" + assertEquals(output.encoded, expectedString) + } +} \ No newline at end of file diff --git a/include/TrustWalletCore/TWSolanaTransaction.h b/include/TrustWalletCore/TWSolanaTransaction.h new file mode 100644 index 00000000000..681313ff248 --- /dev/null +++ b/include/TrustWalletCore/TWSolanaTransaction.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#pragma once + +#include "TWBase.h" +#include "TWData.h" +#include "TWDataVector.h" +#include "TWString.h" + +TW_EXTERN_C_BEGIN + +TW_EXPORT_STRUCT +struct TWSolanaTransaction; + +/// Decode Solana transaction, update the recent blockhash and re-sign the transaction. +/// +/// # Warning +/// +/// This is a temporary solution. It will be removed when `Solana.proto` supports +/// direct transaction signing. +/// +/// \param encodedTx base64 encoded Solana transaction. +/// \param recentBlockhash base58 encoded recent blockhash. +/// \param privateKeys list of private keys that should be used to re-sign the transaction. +/// \return serialized `Solana::Proto::SigningOutput`. +TW_EXPORT_STATIC_METHOD +TWData *_Nonnull TWSolanaTransactionUpdateBlockhashAndSign(TWString *_Nonnull encodedTx, + TWString *_Nonnull recentBlockhash, + const struct TWDataVector *_Nonnull privateKeys); + +TW_EXTERN_C_END diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 2f08868d5b9..033fa1c04c2 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -162,6 +162,15 @@ dependencies = [ "serde", ] +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bitcoin" version = "0.30.1" @@ -1759,6 +1768,7 @@ dependencies = [ "tw_native_evmos", "tw_native_injective", "tw_ronin", + "tw_solana", "tw_thorchain", ] @@ -1995,6 +2005,21 @@ dependencies = [ "tw_proto", ] +[[package]] +name = "tw_solana" +version = "0.1.0" +dependencies = [ + "bincode", + "serde", + "serde_json", + "tw_coin_entry", + "tw_encoding", + "tw_hash", + "tw_keypair", + "tw_memory", + "tw_proto", +] + [[package]] name = "tw_thorchain" version = "0.1.0" @@ -2092,6 +2117,7 @@ dependencies = [ "tw_misc", "tw_number", "tw_proto", + "tw_solana", ] [[package]] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 5896e9dcb5e..a98ff23da18 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -5,6 +5,7 @@ members = [ "chains/tw_greenfield", "chains/tw_native_evmos", "chains/tw_native_injective", + "chains/tw_solana", "chains/tw_thorchain", "tw_any_coin", "tw_aptos", diff --git a/rust/chains/tw_solana/Cargo.toml b/rust/chains/tw_solana/Cargo.toml new file mode 100644 index 00000000000..1d3e0611345 --- /dev/null +++ b/rust/chains/tw_solana/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "tw_solana" +version = "0.1.0" +edition = "2021" + +[dependencies] +bincode = "1.3.3" +serde = { version = "1.0", features = ["derive"] } +tw_coin_entry = { path = "../../tw_coin_entry" } +tw_encoding = { path = "../../tw_encoding" } +tw_hash = { path = "../../tw_hash" } +tw_keypair = { path = "../../tw_keypair" } +tw_memory = { path = "../../tw_memory" } +tw_proto = { path = "../../tw_proto" } + +[dev-dependencies] +serde_json = "1.0" diff --git a/rust/chains/tw_solana/src/address.rs b/rust/chains/tw_solana/src/address.rs new file mode 100644 index 00000000000..57c7808892b --- /dev/null +++ b/rust/chains/tw_solana/src/address.rs @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::SOLANA_ALPHABET; +use serde::de::Error as DeError; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt; +use std::str::FromStr; +use tw_coin_entry::coin_entry::CoinAddress; +use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_encoding::base58; +use tw_hash::H256; +use tw_keypair::tw; +use tw_memory::Data; + +#[derive(Clone, Copy)] +pub struct SolanaAddress { + bytes: H256, +} + +impl SolanaAddress { + pub fn with_public_key(public_key: &tw::PublicKey) -> AddressResult { + let bytes = public_key + .to_ed25519() + .ok_or(AddressError::PublicKeyTypeMismatch)? + .to_bytes(); + Ok(SolanaAddress { bytes }) + } + + pub fn with_public_key_bytes(bytes: H256) -> SolanaAddress { + SolanaAddress { bytes } + } + + pub fn bytes(&self) -> H256 { + self.bytes + } +} + +impl CoinAddress for SolanaAddress { + #[inline] + fn data(&self) -> Data { + self.bytes.to_vec() + } +} + +impl FromStr for SolanaAddress { + type Err = AddressError; + + fn from_str(s: &str) -> Result { + let bytes = + base58::decode(s, &SOLANA_ALPHABET).map_err(|_| AddressError::FromBase58Error)?; + let bytes = H256::try_from(bytes.as_slice()).map_err(|_| AddressError::InvalidInput)?; + Ok(SolanaAddress { bytes }) + } +} + +impl fmt::Display for SolanaAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let encoded = base58::encode(self.bytes.as_slice(), &SOLANA_ALPHABET); + write!(f, "{}", encoded) + } +} + +impl Serialize for SolanaAddress { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.to_string().serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for SolanaAddress { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let addr_str = String::deserialize(deserializer)?; + SolanaAddress::from_str(&addr_str).map_err(|e| DeError::custom(format!("{e:?}"))) + } +} diff --git a/rust/chains/tw_solana/src/compiler.rs b/rust/chains/tw_solana/src/compiler.rs new file mode 100644 index 00000000000..5f2aabe50db --- /dev/null +++ b/rust/chains/tw_solana/src/compiler.rs @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::error::SigningResult; +use tw_coin_entry::signing_output_error; +use tw_proto::Solana::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct SolanaCompiler; + +impl SolanaCompiler { + #[inline] + pub fn preimage_hashes( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> CompilerProto::PreSigningOutput<'static> { + Self::preimage_hashes_impl(coin, input) + .unwrap_or_else(|e| signing_output_error!(CompilerProto::PreSigningOutput, e)) + } + + fn preimage_hashes_impl( + _coin: &dyn CoinContext, + _input: Proto::SigningInput<'_>, + ) -> SigningResult> { + todo!() + } + + #[inline] + pub fn compile( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Proto::SigningOutput<'static> { + Self::compile_impl(coin, input, signatures, public_keys) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn compile_impl( + _coin: &dyn CoinContext, + _input: Proto::SigningInput<'_>, + _signatures: Vec, + _public_keys: Vec, + ) -> SigningResult> { + todo!() + } +} diff --git a/rust/chains/tw_solana/src/entry.rs b/rust/chains/tw_solana/src/entry.rs new file mode 100644 index 00000000000..9d9f0f7b49a --- /dev/null +++ b/rust/chains/tw_solana/src/entry.rs @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::address::SolanaAddress; +use crate::compiler::SolanaCompiler; +use crate::signer::SolanaSigner; +use std::str::FromStr; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; +use tw_coin_entry::derivation::Derivation; +use tw_coin_entry::error::AddressResult; +use tw_coin_entry::modules::json_signer::NoJsonSigner; +use tw_coin_entry::modules::message_signer::NoMessageSigner; +use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::wallet_connector::NoWalletConnector; +use tw_coin_entry::prefix::NoPrefix; +use tw_keypair::tw::PublicKey; +use tw_proto::Solana::Proto; +use tw_proto::TxCompiler::Proto as CompilerProto; + +pub struct SolanaEntry; + +impl CoinEntry for SolanaEntry { + type AddressPrefix = NoPrefix; + type Address = SolanaAddress; + type SigningInput<'a> = Proto::SigningInput<'a>; + type SigningOutput = Proto::SigningOutput<'static>; + type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + + // Optional modules: + type JsonSigner = NoJsonSigner; + type PlanBuilder = NoPlanBuilder; + type MessageSigner = NoMessageSigner; + type WalletConnector = NoWalletConnector; + + #[inline] + fn parse_address( + &self, + _coin: &dyn CoinContext, + address: &str, + _prefix: Option, + ) -> AddressResult { + SolanaAddress::from_str(address) + } + + #[inline] + fn parse_address_unchecked( + &self, + _coin: &dyn CoinContext, + address: &str, + ) -> AddressResult { + SolanaAddress::from_str(address) + } + + #[inline] + fn derive_address( + &self, + _coin: &dyn CoinContext, + public_key: PublicKey, + _derivation: Derivation, + _prefix: Option, + ) -> AddressResult { + SolanaAddress::with_public_key(&public_key) + } + + #[inline] + fn sign(&self, coin: &dyn CoinContext, input: Self::SigningInput<'_>) -> Self::SigningOutput { + SolanaSigner::sign(coin, input) + } + + #[inline] + fn preimage_hashes( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + ) -> Self::PreSigningOutput { + SolanaCompiler::preimage_hashes(coin, input) + } + + #[inline] + fn compile( + &self, + coin: &dyn CoinContext, + input: Self::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, + ) -> Self::SigningOutput { + SolanaCompiler::compile(coin, input, signatures, public_keys) + } +} diff --git a/rust/chains/tw_solana/src/lib.rs b/rust/chains/tw_solana/src/lib.rs new file mode 100644 index 00000000000..8283647ad9b --- /dev/null +++ b/rust/chains/tw_solana/src/lib.rs @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_encoding::base58::Alphabet; + +pub mod address; +pub mod compiler; +pub mod entry; +pub mod modules; +pub mod signer; +pub mod transaction; + +// cbindgen:ignore +pub const SOLANA_ALPHABET: Alphabet = *Alphabet::BITCOIN; diff --git a/rust/chains/tw_solana/src/modules/mod.rs b/rust/chains/tw_solana/src/modules/mod.rs new file mode 100644 index 00000000000..576e5ccc106 --- /dev/null +++ b/rust/chains/tw_solana/src/modules/mod.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +pub mod tx_signer; +pub mod utils; diff --git a/rust/chains/tw_solana/src/modules/tx_signer.rs b/rust/chains/tw_solana/src/modules/tx_signer.rs new file mode 100644 index 00000000000..763f037149f --- /dev/null +++ b/rust/chains/tw_solana/src/modules/tx_signer.rs @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::transaction::{versioned, Pubkey, Signature}; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_keypair::ed25519; +use tw_keypair::traits::SigningKeyTrait; +use tw_memory::Data; + +pub struct TxSigner; + +impl TxSigner { + pub fn sign_versioned( + mut tx: versioned::VersionedTransaction, + keys: &[ed25519::sha512::PrivateKey], + ) -> SigningResult { + if keys.len() != tx.message.num_required_signatures() { + return Err(SigningError(SigningErrorType::Error_signatures_count)); + } + + tx.zeroize_signatures(); + let message_data = bincode::serialize(&tx.message) + .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?; + + for private_key in keys { + let signing_pubkey = Pubkey(private_key.public().to_bytes()); + // Find an index of the corresponding account. + let account_index = tx + .message + .get_account_index(signing_pubkey) + .ok_or(SigningError(SigningErrorType::Error_missing_private_key))?; + let signature_to_reassign = tx + .signatures + .get_mut(account_index) + .ok_or(SigningError(SigningErrorType::Error_signatures_count))?; + + let ed25519_signature = private_key.sign(message_data.clone())?; + *signature_to_reassign = Signature(ed25519_signature.to_bytes()); + } + + Ok(tx) + } + + pub fn preimage_versioned(tx: &versioned::VersionedTransaction) -> SigningResult { + bincode::serialize(&tx.message) + .map_err(|_| SigningError(SigningErrorType::Error_invalid_params)) + } +} diff --git a/rust/chains/tw_solana/src/modules/utils.rs b/rust/chains/tw_solana/src/modules/utils.rs new file mode 100644 index 00000000000..99f9f69806c --- /dev/null +++ b/rust/chains/tw_solana/src/modules/utils.rs @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::modules::tx_signer::TxSigner; +use crate::transaction::versioned::VersionedTransaction; +use crate::SOLANA_ALPHABET; +use std::borrow::Cow; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::signing_output_error; +use tw_encoding::{base58, base64}; +use tw_hash::H256; +use tw_keypair::{ed25519, KeyPairResult}; +use tw_memory::Data; +use tw_proto::Solana::Proto; + +pub struct SolanaTransaction; + +impl SolanaTransaction { + pub fn update_blockhash_and_sign( + encoded_tx: &str, + recent_blockhash: &str, + private_keys: &[Data], + ) -> Proto::SigningOutput<'static> { + Self::update_blockhash_and_sign_impl(encoded_tx, recent_blockhash, private_keys) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn update_blockhash_and_sign_impl( + encoded_tx: &str, + recent_blockhash: &str, + private_keys: &[Data], + ) -> SigningResult> { + let is_url = false; + let tx_bytes = base64::decode(encoded_tx, is_url)?; + + let mut tx_to_sign: VersionedTransaction = bincode::deserialize(&tx_bytes) + .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; + + let new_blockchain_hash = base58::decode(recent_blockhash, &SOLANA_ALPHABET)?; + let new_blockchain_hash = H256::try_from(new_blockchain_hash.as_slice()) + .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?; + + // Update the transaction's blockhash and re-sign it. + tx_to_sign.message.set_recent_blockhash(new_blockchain_hash); + + let unsigned_encoded = TxSigner::preimage_versioned(&tx_to_sign)?; + + // Do not sign the transaction if there is no private keys, but set zeroed signatures. + // It's needed to estimate the transaction fee with an updated blockhash without using real private keys. + let signed_tx = if private_keys.is_empty() { + tx_to_sign.zeroize_signatures(); + tx_to_sign + } else { + let private_keys = private_keys + .iter() + .map(|pk| ed25519::sha512::PrivateKey::try_from(pk.as_slice())) + .collect::>>()?; + + TxSigner::sign_versioned(tx_to_sign, &private_keys)? + }; + + let unsigned_encoded = base64::encode(&unsigned_encoded, is_url); + let signed_encoded = bincode::serialize(&signed_tx) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let signed_encoded = base64::encode(&signed_encoded, is_url); + let message_encoded = bincode::serialize(&signed_tx.message) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let message_encoded = base64::encode(&message_encoded, is_url); + + Ok(Proto::SigningOutput { + encoded: Cow::from(signed_encoded), + unsigned_tx: Cow::from(unsigned_encoded), + message_encoded: Cow::from(message_encoded), + ..Proto::SigningOutput::default() + }) + } +} diff --git a/rust/chains/tw_solana/src/signer.rs b/rust/chains/tw_solana/src/signer.rs new file mode 100644 index 00000000000..27633d26f26 --- /dev/null +++ b/rust/chains/tw_solana/src/signer.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::SigningResult; +use tw_coin_entry::signing_output_error; +use tw_proto::Solana::Proto; + +pub struct SolanaSigner; + +impl SolanaSigner { + pub fn sign( + coin: &dyn CoinContext, + input: Proto::SigningInput<'_>, + ) -> Proto::SigningOutput<'static> { + Self::sign_impl(coin, input) + .unwrap_or_else(|e| signing_output_error!(Proto::SigningOutput, e)) + } + + fn sign_impl( + _coin: &dyn CoinContext, + _input: Proto::SigningInput<'_>, + ) -> SigningResult> { + todo!() + } +} diff --git a/rust/chains/tw_solana/src/transaction/legacy.rs b/rust/chains/tw_solana/src/transaction/legacy.rs new file mode 100644 index 00000000000..94be08e1bc7 --- /dev/null +++ b/rust/chains/tw_solana/src/transaction/legacy.rs @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::transaction::{short_vec, CompiledInstruction, MessageHeader, Pubkey, Signature}; +use serde::{Deserialize, Serialize}; +use tw_hash::{as_byte_sequence, H256}; + +#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Message { + /// The message header, identifying signed and read-only `account_keys`. + // NOTE: Serialization-related changes must be paired with the direct read at sigverify. + pub header: MessageHeader, + + /// All the account keys used by this transaction. + #[serde(with = "short_vec")] + pub account_keys: Vec, + + /// The id of a recent ledger entry. + #[serde(with = "as_byte_sequence")] + pub recent_blockhash: H256, + + /// Programs that will be executed in sequence and committed in one atomic transaction if all + /// succeed. + #[serde(with = "short_vec")] + pub instructions: Vec, +} + +#[derive(Debug, PartialEq, Default, Eq, Clone, Serialize, Deserialize)] +pub struct Transaction { + /// A set of signatures of a serialized [`Message`], signed by the first + /// keys of the `Message`'s [`account_keys`], where the number of signatures + /// is equal to [`num_required_signatures`] of the `Message`'s + /// [`MessageHeader`]. + /// + /// [`account_keys`]: Message::account_keys + /// [`MessageHeader`]: crate::message::MessageHeader + /// [`num_required_signatures`]: crate::message::MessageHeader::num_required_signatures + // NOTE: Serialization-related changes must be paired with the direct read at sigverify. + #[serde(with = "short_vec")] + pub signatures: Vec, + + /// The message to sign. + pub message: Message, +} + +impl Transaction { + /// Zeroize signatures before re-signing... + pub fn zeroize_signatures(&mut self) { + self.signatures + .iter_mut() + .for_each(|signature| *signature = Signature::default()); + } +} diff --git a/rust/chains/tw_solana/src/transaction/mod.rs b/rust/chains/tw_solana/src/transaction/mod.rs new file mode 100644 index 00000000000..5da19f1c730 --- /dev/null +++ b/rust/chains/tw_solana/src/transaction/mod.rs @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use serde::{Deserialize, Serialize}; +use tw_hash::{as_byte_sequence, H256, H512}; + +pub mod legacy; +pub mod short_vec; +pub mod v0; +pub mod versioned; + +#[derive(Clone, Copy, Default, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct Pubkey(#[serde(with = "as_byte_sequence")] pub(crate) H256); + +#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, Copy)] +#[serde(rename_all = "camelCase")] +pub struct MessageHeader { + /// The number of signatures required for this message to be considered + /// valid. The signers of those signatures must match the first + /// `num_required_signatures` of [`Message::account_keys`]. + // NOTE: Serialization-related changes must be paired with the direct read at sigverify. + pub num_required_signatures: u8, + + /// The last `num_readonly_signed_accounts` of the signed keys are read-only + /// accounts. + pub num_readonly_signed_accounts: u8, + + /// The last `num_readonly_unsigned_accounts` of the unsigned keys are + /// read-only accounts. + pub num_readonly_unsigned_accounts: u8, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +#[serde(rename_all = "camelCase")] +pub struct CompiledInstruction { + /// Index into the transaction keys array indicating the program account that executes this instruction. + pub program_id_index: u8, + /// Ordered indices into the transaction keys array indicating which accounts to pass to the program. + #[serde(with = "short_vec")] + pub accounts: Vec, + /// The program input data. + #[serde(with = "short_vec")] + pub data: Vec, +} + +#[derive(Serialize, Deserialize, Debug, Default, Clone, Copy, Eq, PartialEq, Hash)] +pub struct Signature(#[serde(with = "as_byte_sequence")] pub(crate) H512); + +#[cfg(test)] +mod tests { + use super::*; + use crate::address::SolanaAddress; + use crate::transaction::v0::MessageAddressTableLookup; + use crate::transaction::versioned::{VersionedMessage, VersionedTransaction}; + use crate::SOLANA_ALPHABET; + use std::str::FromStr; + use tw_encoding::hex::ToHex; + use tw_encoding::{base58, base64}; + use tw_memory::Data; + + fn address_pubkey(addr: &'static str) -> Pubkey { + Pubkey(SolanaAddress::from_str(addr).unwrap().bytes()) + } + + fn base58_decode(s: &'static str) -> Data { + base58::decode(s, &SOLANA_ALPHABET).unwrap() + } + + fn base58_decode_h256(s: &'static str) -> H256 { + let bytes = base58::decode(s, &SOLANA_ALPHABET).unwrap(); + H256::try_from(bytes.as_slice()).unwrap() + } + + #[test] + fn test_rango_transaction_ser_de() { + let serialized = base64::decode("AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAHEIoR5xuWyrvjIW4xU7CWlPOfyFAiy8B295hGo6tNjBmRCgUkQaFYTleMcAX2p74eBXQZd1dwDyQZAPJfSv2KGc5kcFLJj5qd2BVMaSNGVPfVBm74GbLwUq5/U1Ccdqc2gokZQxRDpMq7aeToP3nRaWIP4RXMxN+LJetccXMPq/QumgOqt7kkqk07cyPCKgYoQ4fQtOqqZn5sEqjWHYj3CDS5ha48uggePWu090s1ff4yoCjAvULeZ+cqYFn+Adk5Teyfw71W3u/F6VTnLQEPW96gJr5Kcm3bGi08n224JyF++PTko52VL0CIM2xtl0WkvNslD6Wawxr7yd9HYllN4Lz8lFwXilWGgyJdOq1qqBuZbE49glHeCO/sJHNnIHC0BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwZGb+UhFzL/7K26csOb57yM5bvF9xJrLEObOkAAAAAEedVb8jHAbu50xW7OaBUH/bGy3qP0jlECsc2iVrwTjwbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCpjJclj04kifG7PRApFI4NgwtaE5na/xCEBI572Nvp+Fm0P/on9df2SnTAmx8pWHneSwmrNt/J3VFLMhqns4zl6OL4d+g9rsaIj0Orta57MRu3jDSWCJf85ae4LBbiD/GXvOojZjsHekJrpRUuPggLJr943hDVD5UareeEucjCvaoHCgAFAsBcFQAKAAkDBBcBAAAAAAANBgAGACMJDAEBCQIABgwCAAAAAMqaOwAAAAAMAQYBEQs1DA8ABgEFAiMhCwsOCx0MDxoBGQcYBAgDJBscDB4PBwUQEhEfFR8UFwcFISITHw8MDCAfFgstwSCbM0HWnIEAAwAAABEBZAABCh0BAyZHAQMAypo7AAAAAJaWFAYAAAAAMgAADAMGAAABCQPZoILFk7gfE2y5bt3AC+g/4OwNzdiHKBhIbdeYvYFEjQPKyMkExMUkx0R25UNa/g5KsG0vfUwdUJ8e8HecK/Jkd3qm9XefBOB0BaD1+J+dBJz09vfyGuRYZH09HfdE/kL8v6Ql+H03+tO+9lMmmVg8O1c6gAN6eX0Cbn4=", false).unwrap(); + let actual: VersionedTransaction = bincode::deserialize(&serialized).unwrap(); + + let expected = VersionedTransaction { + signatures: vec![Signature(H512::default())], + message: VersionedMessage::V0(v0::Message { + header: MessageHeader { + num_required_signatures: 1, + num_readonly_signed_accounts: 0, + num_readonly_unsigned_accounts: 7, + }, + account_keys: vec![ + address_pubkey("AHy6YZA8BsHgQfVkk7MbwpAN94iyN7Nf1zN4nPqUN32Q"), + address_pubkey("g7dD1FHSemkUQrX1Eak37wzvDjscgBW2pFCENwjLdMX"), + address_pubkey("7m57LBTxtzhWn6WdFxKtnoJLBQXyNERLYebebXLVaKy3"), + address_pubkey("AEBCPtV8FFkWFAKxrz7mbYvobpkZuWaRWQCyJVRaheUD"), + address_pubkey("BND2ehwWVeHVA5EtMm2b7Vu51AT8f2PNWusS9KQX5moy"), + address_pubkey("DVCeozFGbe6ew3eWTnZByjHeYqTq1cvbrB7JJhkLxaRJ"), + address_pubkey("GvgWmk8iPACw1AEMt47WzkuTkKoSGbn4Xk3aLM8vdbJD"), + address_pubkey("HkphEpUqnFBxBuCPEq5j1HA9L8EwmsmRT6UcFKziptM1"), + address_pubkey("Hzxx6b5a7dmmJeDXLQzr4dTrc2HGK9ar5YRakZgr3ZZ7"), + address_pubkey("11111111111111111111111111111111"), + address_pubkey("ComputeBudget111111111111111111111111111111"), + address_pubkey("JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"), + address_pubkey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), + address_pubkey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"), + address_pubkey("D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"), + address_pubkey("GGztQqQ6pCPaJQnNpXBgELr5cs3WwDakRbh1iEMzjgSJ"), + ], + recent_blockhash: base58_decode_h256( + "DiSimxK2z1cRa6yD4goqte3rDMmghJAD8WDUZEab2CzD", + ), + instructions: vec![ + CompiledInstruction { + program_id_index: 10, + accounts: vec![], + data: base58_decode("K1FDJ7"), + }, + CompiledInstruction { + program_id_index: 10, + accounts: vec![], + data: base58_decode("3E9ErJ5MrzbZ"), + }, + CompiledInstruction { + program_id_index: 13, + accounts: vec![0, 6, 0, 35, 9, 12], + data: base58_decode("2"), + }, + CompiledInstruction { + program_id_index: 9, + accounts: vec![0, 6], + data: base58_decode("3Bxs3zzLZLuLQEYX"), + }, + CompiledInstruction { + program_id_index: 12, + accounts: vec![6], + data: base58_decode("J"), + }, + CompiledInstruction { + program_id_index: 11, + accounts: vec![ + 12, 15, 0, 6, 1, 5, 2, 35, 33, 11, 11, 14, 11, 29, 12, 15, 26, 1, 25, + 7, 24, 4, 8, 3, 36, 27, 28, 12, 30, 15, 7, 5, 16, 18, 17, 31, 21, 31, + 20, 23, 7, 5, 33, 34, 19, 31, 15, 12, 12, 32, 31, 22, 11, + ], + data: base58_decode( + "5n9zLuyvSGkuf4iDD6PfDvzvzehUkDghmApUkZSXSx57jF9RGSH5Y23tzFJDG3", + ), + }, + CompiledInstruction { + program_id_index: 12, + accounts: vec![6, 0, 0], + data: base58_decode("A"), + }, + ], + address_table_lookups: vec![ + MessageAddressTableLookup { + account_key: address_pubkey("FeXRmSWmwChZbB2EC7Qjw9XKk28yBrPj3k3nzT1DKfak"), + writable_indexes: vec![202, 200, 201], + readonly_indexes: vec![196, 197, 36, 199], + }, + MessageAddressTableLookup { + account_key: address_pubkey("5cFsmTCEfmvpBUBHqsWZnf9n5vTWLYH2LT8X7HdShwxP"), + writable_indexes: vec![160, 245, 248, 159, 157], + readonly_indexes: vec![156, 244, 246, 247], + }, + MessageAddressTableLookup { + account_key: address_pubkey("HJ5StCvsDU4JsvK39VcsHjaoTRTtQU749MQ9qUsJaG1m"), + writable_indexes: vec![122, 121, 125], + readonly_indexes: vec![110, 126], + }, + ], + }), + }; + + assert_eq!(actual, expected); + + let serialized_again = bincode::serialize(&actual).unwrap(); + assert_eq!(serialized_again.to_hex(), serialized.to_hex()); + } +} diff --git a/rust/chains/tw_solana/src/transaction/short_vec.rs b/rust/chains/tw_solana/src/transaction/short_vec.rs new file mode 100644 index 00000000000..9b89f34277d --- /dev/null +++ b/rust/chains/tw_solana/src/transaction/short_vec.rs @@ -0,0 +1,386 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +//! Compact serde-encoding of vectors with small length. +//! Source code: https://github.com/solana-labs/solana/blob/a16f982169eb197fad0eb8c58c307fb069f69d8f/sdk/program/src/short_vec.rs + +#![allow(clippy::arithmetic_side_effects)] +use serde::{ + de::{self, Deserializer, SeqAccess, Visitor}, + ser::{self, SerializeTuple, Serializer}, + Deserialize, Serialize, +}; +use std::{convert::TryFrom, fmt, marker::PhantomData}; + +/// Same as u16, but serialized with 1 to 3 bytes. If the value is above +/// 0x7f, the top bit is set and the remaining value is stored in the next +/// bytes. Each byte follows the same pattern until the 3rd byte. The 3rd +/// byte, if needed, uses all 8 bits to store the last byte of the original +/// value. +pub struct ShortU16(pub u16); + +impl Serialize for ShortU16 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + // Pass a non-zero value to serialize_tuple() so that serde_json will + // generate an open bracket. + let mut seq = serializer.serialize_tuple(1)?; + + let mut rem_val = self.0; + loop { + let mut elem = (rem_val & 0x7f) as u8; + rem_val >>= 7; + if rem_val == 0 { + seq.serialize_element(&elem)?; + break; + } else { + elem |= 0x80; + seq.serialize_element(&elem)?; + } + } + seq.end() + } +} + +enum VisitStatus { + Done(u16), + More(u16), +} + +#[derive(Debug)] +enum VisitError { + TooLong(usize), + TooShort(usize), + Overflow(u32), + Alias, + ByteThreeContinues, +} + +impl VisitError { + fn into_de_error<'de, A>(self) -> A::Error + where + A: SeqAccess<'de>, + { + match self { + VisitError::TooLong(len) => de::Error::invalid_length(len, &"three or fewer bytes"), + VisitError::TooShort(len) => de::Error::invalid_length(len, &"more bytes"), + VisitError::Overflow(val) => de::Error::invalid_value( + de::Unexpected::Unsigned(val as u64), + &"a value in the range [0, 65535]", + ), + VisitError::Alias => de::Error::invalid_value( + de::Unexpected::Other("alias encoding"), + &"strict form encoding", + ), + VisitError::ByteThreeContinues => de::Error::invalid_value( + de::Unexpected::Other("continue signal on byte-three"), + &"a terminal signal on or before byte-three", + ), + } + } +} + +type VisitResult = Result; + +const MAX_ENCODING_LENGTH: usize = 3; +fn visit_byte(elem: u8, val: u16, nth_byte: usize) -> VisitResult { + if elem == 0 && nth_byte != 0 { + return Err(VisitError::Alias); + } + + let val = u32::from(val); + let elem = u32::from(elem); + let elem_val = elem & 0x7f; + let elem_done = (elem & 0x80) == 0; + + if nth_byte >= MAX_ENCODING_LENGTH { + return Err(VisitError::TooLong(nth_byte.saturating_add(1))); + } else if nth_byte == MAX_ENCODING_LENGTH.saturating_sub(1) && !elem_done { + return Err(VisitError::ByteThreeContinues); + } + + let shift = u32::try_from(nth_byte) + .unwrap_or(std::u32::MAX) + .saturating_mul(7); + let elem_val = elem_val.checked_shl(shift).unwrap_or(std::u32::MAX); + + let new_val = val | elem_val; + let val = u16::try_from(new_val).map_err(|_| VisitError::Overflow(new_val))?; + + if elem_done { + Ok(VisitStatus::Done(val)) + } else { + Ok(VisitStatus::More(val)) + } +} + +struct ShortU16Visitor; + +impl<'de> Visitor<'de> for ShortU16Visitor { + type Value = ShortU16; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a ShortU16") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + // Decodes an unsigned 16 bit integer one-to-one encoded as follows: + // 1 byte : 0xxxxxxx => 00000000 0xxxxxxx : 0 - 127 + // 2 bytes : 1xxxxxxx 0yyyyyyy => 00yyyyyy yxxxxxxx : 128 - 16,383 + // 3 bytes : 1xxxxxxx 1yyyyyyy 000000zz => zzyyyyyy yxxxxxxx : 16,384 - 65,535 + let mut val: u16 = 0; + for nth_byte in 0..MAX_ENCODING_LENGTH { + let elem: u8 = seq.next_element()?.ok_or_else(|| { + VisitError::TooShort(nth_byte.saturating_add(1)).into_de_error::() + })?; + match visit_byte(elem, val, nth_byte).map_err(|e| e.into_de_error::())? { + VisitStatus::Done(new_val) => return Ok(ShortU16(new_val)), + VisitStatus::More(new_val) => val = new_val, + } + } + + Err(VisitError::ByteThreeContinues.into_de_error::()) + } +} + +impl<'de> Deserialize<'de> for ShortU16 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_tuple(3, ShortU16Visitor) + } +} + +/// If you don't want to use the ShortVec newtype, you can do ShortVec +/// serialization on an ordinary vector with the following field annotation: +/// +/// #[serde(with = "short_vec")] +/// +pub fn serialize( + elements: &[T], + serializer: S, +) -> Result { + // Pass a non-zero value to serialize_tuple() so that serde_json will + // generate an open bracket. + let mut seq = serializer.serialize_tuple(1)?; + + let len = elements.len(); + if len > std::u16::MAX as usize { + return Err(ser::Error::custom("length larger than u16")); + } + let short_len = ShortU16(len as u16); + seq.serialize_element(&short_len)?; + + for element in elements { + seq.serialize_element(element)?; + } + seq.end() +} + +struct ShortVecVisitor { + _t: PhantomData, +} + +impl<'de, T> Visitor<'de> for ShortVecVisitor +where + T: Deserialize<'de>, +{ + type Value = Vec; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a Vec with a multi-byte length") + } + + fn visit_seq(self, mut seq: A) -> Result, A::Error> + where + A: SeqAccess<'de>, + { + let short_len: ShortU16 = seq + .next_element()? + .ok_or_else(|| de::Error::invalid_length(0, &self))?; + let len = short_len.0 as usize; + + let mut result = Vec::with_capacity(len); + for i in 0..len { + let elem = seq + .next_element()? + .ok_or_else(|| de::Error::invalid_length(i, &self))?; + result.push(elem); + } + Ok(result) + } +} + +/// If you don't want to use the ShortVec newtype, you can do ShortVec +/// deserialization on an ordinary vector with the following field annotation: +/// +/// #[serde(with = "short_vec")] +/// +pub fn deserialize<'de, D, T>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, + T: Deserialize<'de>, +{ + let visitor = ShortVecVisitor { _t: PhantomData }; + deserializer.deserialize_tuple(std::usize::MAX, visitor) +} + +pub struct ShortVec(pub Vec); + +impl Serialize for ShortVec { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serialize(&self.0, serializer) + } +} + +impl<'de, T: Deserialize<'de>> Deserialize<'de> for ShortVec { + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + deserialize(deserializer).map(ShortVec) + } +} + +/// Return the decoded value and how many bytes it consumed. +#[allow(clippy::result_unit_err)] +pub fn decode_shortu16_len(bytes: &[u8]) -> Result<(usize, usize), ()> { + let mut val = 0; + for (nth_byte, byte) in bytes.iter().take(MAX_ENCODING_LENGTH).enumerate() { + match visit_byte(*byte, val, nth_byte).map_err(|_| ())? { + VisitStatus::More(new_val) => val = new_val, + VisitStatus::Done(new_val) => { + return Ok((usize::from(new_val), nth_byte.saturating_add(1))); + }, + } + } + Err(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use bincode::{deserialize, serialize}; + + /// Return the serialized length. + fn encode_len(len: u16) -> Vec { + bincode::serialize(&ShortU16(len)).unwrap() + } + + fn assert_len_encoding(len: u16, bytes: &[u8]) { + assert_eq!(encode_len(len), bytes, "unexpected usize encoding"); + assert_eq!( + decode_shortu16_len(bytes).unwrap(), + (usize::from(len), bytes.len()), + "unexpected usize decoding" + ); + } + + #[test] + fn test_short_vec_encode_len() { + assert_len_encoding(0x0, &[0x0]); + assert_len_encoding(0x7f, &[0x7f]); + assert_len_encoding(0x80, &[0x80, 0x01]); + assert_len_encoding(0xff, &[0xff, 0x01]); + assert_len_encoding(0x100, &[0x80, 0x02]); + assert_len_encoding(0x7fff, &[0xff, 0xff, 0x01]); + assert_len_encoding(0xffff, &[0xff, 0xff, 0x03]); + } + + fn assert_good_deserialized_value(value: u16, bytes: &[u8]) { + assert_eq!(value, deserialize::(bytes).unwrap().0); + } + + fn assert_bad_deserialized_value(bytes: &[u8]) { + assert!(deserialize::(bytes).is_err()); + } + + #[test] + fn test_deserialize() { + assert_good_deserialized_value(0x0000, &[0x00]); + assert_good_deserialized_value(0x007f, &[0x7f]); + assert_good_deserialized_value(0x0080, &[0x80, 0x01]); + assert_good_deserialized_value(0x00ff, &[0xff, 0x01]); + assert_good_deserialized_value(0x0100, &[0x80, 0x02]); + assert_good_deserialized_value(0x07ff, &[0xff, 0x0f]); + assert_good_deserialized_value(0x3fff, &[0xff, 0x7f]); + assert_good_deserialized_value(0x4000, &[0x80, 0x80, 0x01]); + assert_good_deserialized_value(0xffff, &[0xff, 0xff, 0x03]); + + // aliases + // 0x0000 + assert_bad_deserialized_value(&[0x80, 0x00]); + assert_bad_deserialized_value(&[0x80, 0x80, 0x00]); + // 0x007f + assert_bad_deserialized_value(&[0xff, 0x00]); + assert_bad_deserialized_value(&[0xff, 0x80, 0x00]); + // 0x0080 + assert_bad_deserialized_value(&[0x80, 0x81, 0x00]); + // 0x00ff + assert_bad_deserialized_value(&[0xff, 0x81, 0x00]); + // 0x0100 + assert_bad_deserialized_value(&[0x80, 0x82, 0x00]); + // 0x07ff + assert_bad_deserialized_value(&[0xff, 0x8f, 0x00]); + // 0x3fff + assert_bad_deserialized_value(&[0xff, 0xff, 0x00]); + + // too short + assert_bad_deserialized_value(&[]); + assert_bad_deserialized_value(&[0x80]); + + // too long + assert_bad_deserialized_value(&[0x80, 0x80, 0x80, 0x00]); + + // too large + // 0x0001_0000 + assert_bad_deserialized_value(&[0x80, 0x80, 0x04]); + // 0x0001_8000 + assert_bad_deserialized_value(&[0x80, 0x80, 0x06]); + } + + #[test] + fn test_short_vec_u8() { + let vec = ShortVec(vec![4u8; 32]); + let bytes = serialize(&vec).unwrap(); + assert_eq!(bytes.len(), vec.0.len() + 1); + + let vec1: ShortVec = deserialize(&bytes).unwrap(); + assert_eq!(vec.0, vec1.0); + } + + #[test] + fn test_short_vec_u8_too_long() { + let vec = ShortVec(vec![4u8; std::u16::MAX as usize]); + assert!(matches!(serialize(&vec), Ok(_))); + + let vec = ShortVec(vec![4u8; std::u16::MAX as usize + 1]); + assert!(matches!(serialize(&vec), Err(_))); + } + + #[test] + fn test_short_vec_json() { + let vec = ShortVec(vec![0, 1, 2]); + let s = serde_json::to_string(&vec).unwrap(); + assert_eq!(s, "[[3],0,1,2]"); + } + + #[test] + fn test_short_vec_aliased_length() { + let bytes = [ + 0x81, 0x80, 0x00, // 3-byte alias of 1 + 0x00, + ]; + assert!(deserialize::>(&bytes).is_err()); + } +} diff --git a/rust/chains/tw_solana/src/transaction/v0.rs b/rust/chains/tw_solana/src/transaction/v0.rs new file mode 100644 index 00000000000..18f70a40ef4 --- /dev/null +++ b/rust/chains/tw_solana/src/transaction/v0.rs @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::transaction::{short_vec, CompiledInstruction, MessageHeader, Pubkey}; +use serde::{Deserialize, Serialize}; +use tw_hash::{as_byte_sequence, H256}; + +#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone)] +#[serde(rename_all = "camelCase")] +pub struct MessageAddressTableLookup { + /// Address lookup table account key + pub account_key: Pubkey, + /// List of indexes used to load writable account addresses + #[serde(with = "short_vec")] + pub writable_indexes: Vec, + /// List of indexes used to load readonly account addresses + #[serde(with = "short_vec")] + pub readonly_indexes: Vec, +} + +#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Message { + /// The message header, identifying signed and read-only `account_keys`. + /// Header values only describe static `account_keys`, they do not describe + /// any additional account keys loaded via address table lookups. + pub header: MessageHeader, + + /// List of accounts loaded by this transaction. + #[serde(with = "short_vec")] + pub account_keys: Vec, + + /// The blockhash of a recent block. + #[serde(with = "as_byte_sequence")] + pub recent_blockhash: H256, + + /// Instructions that invoke a designated program, are executed in sequence, + /// and committed in one atomic transaction if all succeed. + /// + /// # Notes + /// + /// Program indexes must index into the list of message `account_keys` because + /// program id's cannot be dynamically loaded from a lookup table. + /// + /// Account indexes must index into the list of addresses + /// constructed from the concatenation of three key lists: + /// 1) message `account_keys` + /// 2) ordered list of keys loaded from `writable` lookup table indexes + /// 3) ordered list of keys loaded from `readable` lookup table indexes + #[serde(with = "short_vec")] + pub instructions: Vec, + + /// List of address table lookups used to load additional accounts + /// for this transaction. + #[serde(with = "short_vec")] + pub address_table_lookups: Vec, +} diff --git a/rust/chains/tw_solana/src/transaction/versioned.rs b/rust/chains/tw_solana/src/transaction/versioned.rs new file mode 100644 index 00000000000..ffe94c9d76d --- /dev/null +++ b/rust/chains/tw_solana/src/transaction/versioned.rs @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +//! Source code: https://github.com/solana-labs/solana/blob/a16f982169eb197fad0eb8c58c307fb069f69d8f/sdk/program/src/message/versions/mod.rs + +use crate::transaction::{ + legacy, short_vec, v0, CompiledInstruction, MessageHeader, Pubkey, Signature, +}; +use serde::de::{SeqAccess, Unexpected, Visitor}; +use serde::ser::SerializeTuple; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt; +use tw_hash::{as_byte_sequence, H256}; + +/// Bit mask that indicates whether a serialized message is versioned. +pub const MESSAGE_VERSION_PREFIX: u8 = 0x80; + +// NOTE: Serialization-related changes must be paired with the direct read at sigverify. +/// An atomic transaction +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct VersionedTransaction { + /// List of signatures + #[serde(with = "short_vec")] + pub signatures: Vec, + /// Message to sign. + pub message: VersionedMessage, +} + +impl VersionedTransaction { + /// Fill the signatures up with zeroed signatures + /// (same number of signatures as [`VersionedTransaction::num_required_signatures`]). + pub fn zeroize_signatures(&mut self) { + self.signatures = vec![Signature::default(); self.message.num_required_signatures()]; + } +} + +/// Either a legacy message or a v0 message. +/// +/// # Serialization +/// +/// If the first bit is set, the remaining 7 bits will be used to determine +/// which message version is serialized starting from version `0`. If the first +/// is bit is not set, all bytes are used to encode the legacy `Message` +/// format. +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum VersionedMessage { + Legacy(legacy::Message), + V0(v0::Message), +} + +impl VersionedMessage { + pub fn num_required_signatures(&self) -> usize { + match self { + VersionedMessage::Legacy(legacy) => legacy.header.num_required_signatures as usize, + VersionedMessage::V0(v0) => v0.header.num_required_signatures as usize, + } + } + + pub fn get_account_index(&self, account_pubkey: Pubkey) -> Option { + let account_keys = match self { + VersionedMessage::Legacy(legacy) => &legacy.account_keys, + VersionedMessage::V0(v0) => &v0.account_keys, + }; + account_keys.iter().position(|pk| *pk == account_pubkey) + } + + pub fn set_recent_blockhash(&mut self, recent_blockhash: H256) { + match self { + VersionedMessage::Legacy(legacy) => legacy.recent_blockhash = recent_blockhash, + VersionedMessage::V0(v0) => v0.recent_blockhash = recent_blockhash, + } + } +} + +impl Serialize for VersionedMessage { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Self::Legacy(message) => { + let mut seq = serializer.serialize_tuple(1)?; + seq.serialize_element(message)?; + seq.end() + }, + Self::V0(message) => { + let mut seq = serializer.serialize_tuple(2)?; + seq.serialize_element(&MESSAGE_VERSION_PREFIX)?; + seq.serialize_element(message)?; + seq.end() + }, + } + } +} + +enum MessagePrefix { + Legacy(u8), + Versioned(u8), +} + +impl<'de> Deserialize<'de> for MessagePrefix { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct PrefixVisitor; + + impl<'de> Visitor<'de> for PrefixVisitor { + type Value = MessagePrefix; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("message prefix byte") + } + + // Serde's integer visitors bubble up to u64 so check the prefix + // with this function instead of visit_u8. This approach is + // necessary because serde_json directly calls visit_u64 for + // unsigned integers. + fn visit_u64(self, value: u64) -> Result { + if value > u8::MAX as u64 { + Err(de::Error::invalid_type(Unexpected::Unsigned(value), &self))?; + } + + let byte = value as u8; + if byte & MESSAGE_VERSION_PREFIX != 0 { + Ok(MessagePrefix::Versioned(byte & !MESSAGE_VERSION_PREFIX)) + } else { + Ok(MessagePrefix::Legacy(byte)) + } + } + } + + deserializer.deserialize_u8(PrefixVisitor) + } +} + +impl<'de> Deserialize<'de> for VersionedMessage { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct MessageVisitor; + + impl<'de> Visitor<'de> for MessageVisitor { + type Value = VersionedMessage; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("message bytes") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let prefix: MessagePrefix = seq + .next_element()? + .ok_or_else(|| de::Error::invalid_length(0, &self))?; + + match prefix { + MessagePrefix::Legacy(num_required_signatures) => { + // The remaining fields of the legacy Message struct after the first byte. + #[derive(Serialize, Deserialize)] + struct RemainingLegacyMessage { + pub num_readonly_signed_accounts: u8, + pub num_readonly_unsigned_accounts: u8, + #[serde(with = "short_vec")] + pub account_keys: Vec, + #[serde(with = "as_byte_sequence")] + pub recent_blockhash: H256, + #[serde(with = "short_vec")] + pub instructions: Vec, + } + + let message: RemainingLegacyMessage = + seq.next_element()?.ok_or_else(|| { + // will never happen since tuple length is always 2 + de::Error::invalid_length(1, &self) + })?; + + Ok(VersionedMessage::Legacy(legacy::Message { + header: MessageHeader { + num_required_signatures, + num_readonly_signed_accounts: message.num_readonly_signed_accounts, + num_readonly_unsigned_accounts: message + .num_readonly_unsigned_accounts, + }, + account_keys: message.account_keys, + recent_blockhash: message.recent_blockhash, + instructions: message.instructions, + })) + }, + MessagePrefix::Versioned(version) => { + match version { + 0 => { + Ok(VersionedMessage::V0(seq.next_element()?.ok_or_else( + || { + // will never happen since tuple length is always 2 + de::Error::invalid_length(1, &self) + }, + )?)) + }, + 127 => { + // 0xff is used as the first byte of the off-chain messages + // which corresponds to version 127 of the versioned messages. + // This explicit check is added to prevent the usage of version 127 + // in the runtime as a valid transaction. + Err(de::Error::custom("off-chain messages are not accepted")) + }, + _ => Err(de::Error::invalid_value( + de::Unexpected::Unsigned(version as u64), + &"a valid transaction message version", + )), + } + }, + } + } + } + + deserializer.deserialize_tuple(2, MessageVisitor) + } +} diff --git a/rust/chains/tw_solana/tests/update_blockhash_and_sign.rs b/rust/chains/tw_solana/tests/update_blockhash_and_sign.rs new file mode 100644 index 00000000000..a4a4cd1c08d --- /dev/null +++ b/rust/chains/tw_solana/tests/update_blockhash_and_sign.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_coin_entry::error::SigningErrorType; +use tw_encoding::base58; +use tw_solana::modules::utils::SolanaTransaction; +use tw_solana::SOLANA_ALPHABET; + +#[test] +fn test_update_recent_blockhash_and_sign() { + // base64 encoded + let encoded_tx = "AQPWaOi7dMdmQpXi8HyQQKwiqIftrg1igGQxGtZeT50ksn4wAnyH4DtDrkkuE0fqgx80LTp4LwNN9a440SrmoA8BAAEDZsL1CMnFVcrMn7JtiOiN1U4hC7WovOVof2DX51xM0H/GizyJTHgrBanCf8bGbrFNTn0x3pCGq30hKbywSTr6AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgIAAQwCAAAAKgAAAAAAAAA="; + // base58 encoded + let new_blockhash = "CyPYVsYWrsJNfVpi8aazu7WsrswNFuDd385z6GNoBGUg"; + let private_key = base58::decode( + "A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr", + &SOLANA_ALPHABET, + ) + .unwrap(); + + let output = + SolanaTransaction::update_blockhash_and_sign(encoded_tx, new_blockhash, &[private_key]); + assert_eq!(output.error, SigningErrorType::OK); + let expected = "AdQl49kO1FxfkAnAuK9KSQEGLzxHNYLqBrYGFN711q7aT/qyrzYMn/7/IdFBy6yMhjOA1CkwZsgmqmbu+XKvVAUBAAEDZsL1CMnFVcrMn7JtiOiN1U4hC7WovOVof2DX51xM0H/GizyJTHgrBanCf8bGbrFNTn0x3pCGq30hKbywSTr6AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAseKSLpOE0fdq67Jk9Ckme2c3SYD//nqcykr/oci67zEBAgIAAQwCAAAAKgAAAAAAAAA="; + assert_eq!(output.encoded, expected); +} diff --git a/rust/tw_any_coin/tests/chains/mod.rs b/rust/tw_any_coin/tests/chains/mod.rs index cefb0e37ae4..cd580c22dde 100644 --- a/rust/tw_any_coin/tests/chains/mod.rs +++ b/rust/tw_any_coin/tests/chains/mod.rs @@ -12,6 +12,7 @@ mod greenfield; mod internet_computer; mod native_evmos; mod native_injective; +mod solana; mod tbinance; mod thorchain; mod zetachain; diff --git a/rust/tw_any_coin/tests/chains/solana/mod.rs b/rust/tw_any_coin/tests/chains/solana/mod.rs new file mode 100644 index 00000000000..2b199e6a807 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/solana/mod.rs @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +mod solana_address; +mod solana_compile; +mod solana_sign; diff --git a/rust/tw_any_coin/tests/chains/solana/solana_address.rs b/rust/tw_any_coin/tests/chains/solana/solana_address.rs new file mode 100644 index 00000000000..9ba8aa31d4d --- /dev/null +++ b/rust/tw_any_coin/tests/chains/solana/solana_address.rs @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_any_coin::test_utils::address_utils::{ + test_address_get_data, test_address_invalid, test_address_normalization, test_address_valid, +}; +use tw_coin_registry::coin_type::CoinType; + +#[test] +fn test_solana_address_normalization() { + test_address_normalization( + CoinType::Solana, + "2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdST", + "2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdST", + ); +} + +#[test] +fn test_solana_address_is_valid() { + test_address_valid( + CoinType::Solana, + "2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdST", + ); +} + +#[test] +fn test_solana_address_invalid() { + // Contains invalid base-58 character + test_address_invalid( + CoinType::Solana, + "2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdSl", + ); + // Is invalid length + test_address_invalid( + CoinType::Solana, + "2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpd", + ); +} + +#[test] +fn test_solana_address_get_data() { + test_address_get_data( + CoinType::Solana, + "2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdST", + "18f9d8d877393bbbe8d697a8a2e52879cc7e84f467656d1cce6bab5a8d2637ec", + ); +} diff --git a/rust/tw_any_coin/tests/chains/solana/solana_compile.rs b/rust/tw_any_coin/tests/chains/solana/solana_compile.rs new file mode 100644 index 00000000000..84b9a412183 --- /dev/null +++ b/rust/tw_any_coin/tests/chains/solana/solana_compile.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#[test] +#[ignore] +fn test_solana_compile() { + todo!() +} diff --git a/rust/tw_any_coin/tests/chains/solana/solana_sign.rs b/rust/tw_any_coin/tests/chains/solana/solana_sign.rs new file mode 100644 index 00000000000..194cad133fe --- /dev/null +++ b/rust/tw_any_coin/tests/chains/solana/solana_sign.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#[test] +#[ignore] +fn test_solana_sign() { + todo!() +} diff --git a/rust/tw_any_coin/tests/coin_address_derivation_test.rs b/rust/tw_any_coin/tests/coin_address_derivation_test.rs index 9cf10ad244a..b7abadbb806 100644 --- a/rust/tw_any_coin/tests/coin_address_derivation_test.rs +++ b/rust/tw_any_coin/tests/coin_address_derivation_test.rs @@ -148,6 +148,7 @@ fn test_coin_address_derivation() { CoinType::TBinance => "tbnb1ten42eesehw0ktddcp0fws7d3ycsqez3n49hpe", CoinType::NativeZetaChain => "zeta14s0vgnj0pjnazu4hsqlksdk7slah9vcfcwctsr", CoinType::Dydx => "dydx1ten42eesehw0ktddcp0fws7d3ycsqez3kaamq3", + CoinType::Solana => "5sn9QYhDaq61jLXJ8Li5BKqGL4DDMJQvU1rdN8XgVuwC", // end_of_coin_address_derivation_tests_marker_do_not_modify _ => panic!("{:?} must be covered", coin), }; diff --git a/rust/tw_coin_entry/src/error.rs b/rust/tw_coin_entry/src/error.rs index bea816c30d0..e8ceb589066 100644 --- a/rust/tw_coin_entry/src/error.rs +++ b/rust/tw_coin_entry/src/error.rs @@ -28,13 +28,18 @@ pub type AddressResult = Result; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum AddressError { UnknownCoinType, + Unsupported, MissingPrefix, FromHexError, + FromBase58Error, + FromBech32Error, PublicKeyTypeMismatch, UnexpectedAddressPrefix, UnexpectedHasher, InvalidHrp, + InvalidRegistry, InvalidInput, + InvalidChecksum, } pub type SigningResult = Result; diff --git a/rust/tw_coin_registry/Cargo.toml b/rust/tw_coin_registry/Cargo.toml index 35dd638a29d..2359e4d6329 100644 --- a/rust/tw_coin_registry/Cargo.toml +++ b/rust/tw_coin_registry/Cargo.toml @@ -25,6 +25,7 @@ tw_misc = { path = "../tw_misc" } tw_native_evmos = { path = "../chains/tw_native_evmos" } tw_native_injective = { path = "../chains/tw_native_injective" } tw_ronin = { path = "../tw_ronin" } +tw_solana = { path = "../chains/tw_solana" } tw_thorchain = { path = "../chains/tw_thorchain" } [build-dependencies] diff --git a/rust/tw_coin_registry/src/blockchain_type.rs b/rust/tw_coin_registry/src/blockchain_type.rs index 87261ec0d95..19b7198ed90 100644 --- a/rust/tw_coin_registry/src/blockchain_type.rs +++ b/rust/tw_coin_registry/src/blockchain_type.rs @@ -19,6 +19,7 @@ pub enum BlockchainType { NativeEvmos, NativeInjective, Ronin, + Solana, Thorchain, // end_of_blockchain_type - USED TO GENERATE CODE #[serde(other)] diff --git a/rust/tw_coin_registry/src/dispatcher.rs b/rust/tw_coin_registry/src/dispatcher.rs index e6cfebcf7bb..c978e17f5c0 100644 --- a/rust/tw_coin_registry/src/dispatcher.rs +++ b/rust/tw_coin_registry/src/dispatcher.rs @@ -19,6 +19,7 @@ use tw_internet_computer::entry::InternetComputerEntry; use tw_native_evmos::entry::NativeEvmosEntry; use tw_native_injective::entry::NativeInjectiveEntry; use tw_ronin::entry::RoninEntry; +use tw_solana::entry::SolanaEntry; use tw_thorchain::entry::ThorchainEntry; pub type CoinEntryExtStaticRef = &'static dyn CoinEntryExt; @@ -35,6 +36,7 @@ const INTERNET_COMPUTER: InternetComputerEntry = InternetComputerEntry; const NATIVE_EVMOS: NativeEvmosEntry = NativeEvmosEntry; const NATIVE_INJECTIVE: NativeInjectiveEntry = NativeInjectiveEntry; const RONIN: RoninEntry = RoninEntry; +const SOLANA: SolanaEntry = SolanaEntry; const THORCHAIN: ThorchainEntry = ThorchainEntry; // end_of_blockchain_entries - USED TO GENERATE CODE @@ -51,6 +53,7 @@ pub fn blockchain_dispatcher(blockchain: BlockchainType) -> RegistryResult Ok(&NATIVE_EVMOS), BlockchainType::NativeInjective => Ok(&NATIVE_INJECTIVE), BlockchainType::Ronin => Ok(&RONIN), + BlockchainType::Solana => Ok(&SOLANA), BlockchainType::Thorchain => Ok(&THORCHAIN), // end_of_blockchain_dispatcher - USED TO GENERATE CODE BlockchainType::Unsupported => Err(RegistryError::Unsupported), diff --git a/rust/tw_hash/src/hash_array.rs b/rust/tw_hash/src/hash_array.rs index 0c4d893b064..f76893b5349 100644 --- a/rust/tw_hash/src/hash_array.rs +++ b/rust/tw_hash/src/hash_array.rs @@ -198,6 +198,61 @@ mod impl_serde { } } +#[cfg(feature = "serde")] +pub mod as_byte_sequence { + use super::Hash; + use serde::de::{Error, SeqAccess, Visitor}; + use serde::ser::SerializeTuple; + use serde::{Deserializer, Serializer}; + use std::fmt; + use std::marker::PhantomData; + + struct ByteArrayVisitor { + _n: PhantomData<[u8; N]>, + } + + impl<'de, const N: usize> Visitor<'de> for ByteArrayVisitor { + type Value = Hash; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("struct Hash") + } + + fn visit_seq(self, mut seq: A) -> Result, A::Error> + where + A: SeqAccess<'de>, + { + let mut result = Hash::::default(); + for i in 0..N { + result[i] = seq + .next_element()? + .ok_or_else(|| Error::invalid_length(i, &self))?; + } + Ok(result) + } + } + + pub fn deserialize<'de, const N: usize, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let visitor = ByteArrayVisitor:: { _n: PhantomData }; + deserializer.deserialize_tuple(N, visitor) + } + + pub fn serialize(hash: &Hash, serializer: S) -> Result + where + S: Serializer, + { + let mut tup = serializer.serialize_tuple(N)?; + for el in hash.0 { + tup.serialize_element(&el)?; + } + + tup.end() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/rust/tw_hash/src/lib.rs b/rust/tw_hash/src/lib.rs index 3c62256bbf7..48838e77569 100644 --- a/rust/tw_hash/src/lib.rs +++ b/rust/tw_hash/src/lib.rs @@ -17,7 +17,7 @@ pub mod sha3; mod hash_array; mod hash_wrapper; -pub use hash_array::{concat, Hash, H160, H256, H264, H32, H512, H520}; +pub use hash_array::{as_byte_sequence, concat, Hash, H160, H256, H264, H32, H512, H520}; use tw_encoding::hex::FromHexError; diff --git a/rust/tw_memory/src/test_utils/tw_data_vector_helper.rs b/rust/tw_memory/src/test_utils/tw_data_vector_helper.rs index 6c0c185ed81..63f6287c137 100644 --- a/rust/tw_memory/src/test_utils/tw_data_vector_helper.rs +++ b/rust/tw_memory/src/test_utils/tw_data_vector_helper.rs @@ -32,6 +32,12 @@ impl TWDataVectorHelper { } } +impl Default for TWDataVectorHelper { + fn default() -> Self { + TWDataVectorHelper::create([]) + } +} + impl Drop for TWDataVectorHelper { fn drop(&mut self) { if self.ptr.is_null() { diff --git a/rust/wallet_core_rs/Cargo.toml b/rust/wallet_core_rs/Cargo.toml index a72042ac6cb..9f9fe5c9ea4 100644 --- a/rust/wallet_core_rs/Cargo.toml +++ b/rust/wallet_core_rs/Cargo.toml @@ -8,10 +8,11 @@ name = "wallet_core_rs" crate-type = ["staticlib", "rlib"] # Creates static lib [features] -default = ["bitcoin-legacy", "ethereum-abi", "ethereum-rlp"] +default = ["bitcoin-legacy", "ethereum-abi", "ethereum-rlp", "solana-transaction"] bitcoin-legacy = [] ethereum-abi = [] ethereum-rlp = [] +solana-transaction = [] [dependencies] tw_any_coin = { path = "../tw_any_coin" } @@ -26,6 +27,7 @@ tw_keypair = { path = "../tw_keypair" } tw_memory = { path = "../tw_memory" } tw_misc = { path = "../tw_misc" } tw_proto = { path = "../tw_proto" } +tw_solana = { path = "../chains/tw_solana" } [dev-dependencies] serde_json = "1.0" diff --git a/rust/wallet_core_rs/src/ffi/mod.rs b/rust/wallet_core_rs/src/ffi/mod.rs index 7df4e1ad4eb..4b64452c2e7 100644 --- a/rust/wallet_core_rs/src/ffi/mod.rs +++ b/rust/wallet_core_rs/src/ffi/mod.rs @@ -4,3 +4,4 @@ pub mod bitcoin; pub mod ethereum; +pub mod solana; diff --git a/rust/wallet_core_rs/src/ffi/solana/mod.rs b/rust/wallet_core_rs/src/ffi/solana/mod.rs new file mode 100644 index 00000000000..073788657fa --- /dev/null +++ b/rust/wallet_core_rs/src/ffi/solana/mod.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#[cfg(feature = "solana-transaction")] +pub mod transaction; diff --git a/rust/wallet_core_rs/src/ffi/solana/transaction.rs b/rust/wallet_core_rs/src/ffi/solana/transaction.rs new file mode 100644 index 00000000000..a4ae7400a05 --- /dev/null +++ b/rust/wallet_core_rs/src/ffi/solana/transaction.rs @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#![allow(clippy::missing_safety_doc)] + +use tw_memory::ffi::tw_data::TWData; +use tw_memory::ffi::tw_data_vector::TWDataVector; +use tw_memory::ffi::tw_string::TWString; +use tw_memory::ffi::RawPtrTrait; +use tw_misc::try_or_else; +use tw_solana::modules::utils::SolanaTransaction; + +/// Decode Solana transaction, update the recent blockhash and re-sign the transaction. +/// +/// # Warning +/// +/// This is a temporary solution. It will be removed when `Solana.proto` supports +/// direct transaction signing. +/// +/// \param encoded_tx base64 encoded Solana transaction. +/// \param recent_blockhash base58 encoded recent blockhash. +/// \param private_keys list of private keys that should be used to re-sign the transaction. +/// \return serialized `Solana::Proto::SigningOutput`. +#[no_mangle] +pub unsafe extern "C" fn tw_solana_transaction_update_blockhash_and_sign( + encoded_tx: *const TWString, + recent_blockhash: *const TWString, + private_keys: *const TWDataVector, +) -> *mut TWData { + let encoded_tx = try_or_else!(TWString::from_ptr_as_ref(encoded_tx), std::ptr::null_mut); + let encoded_tx = try_or_else!(encoded_tx.as_str(), std::ptr::null_mut); + + let recent_blockhash = try_or_else!( + TWString::from_ptr_as_ref(recent_blockhash), + std::ptr::null_mut + ); + let recent_blockhash = try_or_else!(recent_blockhash.as_str(), std::ptr::null_mut); + + let private_keys = try_or_else!( + TWDataVector::from_ptr_as_ref(private_keys), + std::ptr::null_mut + ) + .to_data_vec(); + + let output = + SolanaTransaction::update_blockhash_and_sign(encoded_tx, recent_blockhash, &private_keys); + let output_proto = try_or_else!(tw_proto::serialize(&output), std::ptr::null_mut); + + TWData::from(output_proto).into_ptr() +} diff --git a/rust/wallet_core_rs/tests/solana_transaction.rs b/rust/wallet_core_rs/tests/solana_transaction.rs new file mode 100644 index 00000000000..66b10315eb3 --- /dev/null +++ b/rust/wallet_core_rs/tests/solana_transaction.rs @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_encoding::base58; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_memory::test_utils::tw_data_vector_helper::TWDataVectorHelper; +use tw_memory::test_utils::tw_string_helper::TWStringHelper; +use tw_proto::Common::Proto::SigningError; +use tw_proto::Solana::Proto; +use tw_solana::SOLANA_ALPHABET; +use wallet_core_rs::ffi::solana::transaction::tw_solana_transaction_update_blockhash_and_sign; + +#[test] +fn test_solana_transaction_update_blockhash_and_sign_token_transfer_with_external_fee_payer() { + // base64 encoded + // https://explorer.solana.com/tx/3KbvREZUat76wgWMtnJfWbJL74Vzh4U2eabVJa3Z3bb2fPtW8AREP5pbmRwUrxZCESbTomWpL41PeKDcPGbojsej?cluster=devnet + let encoded_tx = "AnQTYwZpkm3fs4SdLxnV6gQj3hSLsyacpxDdLMALYWObm722f79IfYFTbZeFK9xHtMumiDOWAM2hHQP4r/GtbARpncaXgOVFv7OgbRLMbuCEJHO1qwcdCbtH72VzyzU8yw9sqqHIAaCUE8xaQTgT6Z5IyZfeyMe2QGJIfOjz65UPAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8Aqe6sdLXiXSDILEtzckCjkjchiSf6zVGpMYiAE5BE2IqHAQUEAgQDAQoMoA8AAAAAAAAG"; + let encoded_tx = TWStringHelper::create(encoded_tx); + + // base58 encoded + let new_blockhash = "CyPYVsYWrsJNfVpi8aazu7WsrswNFuDd385z6GNoBGUg"; + let new_blockhash = TWStringHelper::create(new_blockhash); + + let my_private_key = base58::decode( + "9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5", + &SOLANA_ALPHABET, + ) + .unwrap(); + let fee_payer_private_key = base58::decode( + "66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c", + &SOLANA_ALPHABET, + ) + .unwrap(); + let private_keys = TWDataVectorHelper::create([fee_payer_private_key, my_private_key]); + + let output_data = unsafe { + TWDataHelper::wrap(tw_solana_transaction_update_blockhash_and_sign( + encoded_tx.ptr(), + new_blockhash.ptr(), + private_keys.ptr(), + )) + .to_vec() + .expect("Expected a non-null output data") + }; + let output: Proto::SigningOutput = tw_proto::deserialize(&output_data).unwrap(); + assert_eq!(output.error, SigningError::OK); + + let expected = "Ajzc/Tke0CG8Cew5qFa6xZI/7Ya3DN0M8Ige6tKPsGzhg8Bw9DqL18KUrEZZ1F4YqZBo4Rv+FsDT8A7Nss7p4A6BNVZzzGprCJqYQeNg0EVIbmPc6mDitNniHXGeKgPZ6QZbM4FElw9O7IOFTpOBPvQFeqy0vZf/aayncL8EK/UEAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqbHiki6ThNH3auuyZPQpJntnN0mA//56nMpK/6HIuu8xAQUEAgQDAQoMoA8AAAAAAAAG"; + assert_eq!(output.encoded, expected); +} + +#[test] +fn test_solana_transaction_update_blockhash_and_sign_no_matching_pubkey() { + // base64 encoded + let encoded_tx = "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAHEIoR5xuWyrvjIW4xU7CWlPOfyFAiy8B295hGo6tNjBmRCgUkQaFYTleMcAX2p74eBXQZd1dwDyQZAPJfSv2KGc5kcFLJj5qd2BVMaSNGVPfVBm74GbLwUq5/U1Ccdqc2gokZQxRDpMq7aeToP3nRaWIP4RXMxN+LJetccXMPq/QumgOqt7kkqk07cyPCKgYoQ4fQtOqqZn5sEqjWHYj3CDS5ha48uggePWu090s1ff4yoCjAvULeZ+cqYFn+Adk5Teyfw71W3u/F6VTnLQEPW96gJr5Kcm3bGi08n224JyF++PTko52VL0CIM2xtl0WkvNslD6Wawxr7yd9HYllN4Lz8lFwXilWGgyJdOq1qqBuZbE49glHeCO/sJHNnIHC0BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwZGb+UhFzL/7K26csOb57yM5bvF9xJrLEObOkAAAAAEedVb8jHAbu50xW7OaBUH/bGy3qP0jlECsc2iVrwTjwbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCpjJclj04kifG7PRApFI4NgwtaE5na/xCEBI572Nvp+Fm0P/on9df2SnTAmx8pWHneSwmrNt/J3VFLMhqns4zl6OL4d+g9rsaIj0Orta57MRu3jDSWCJf85ae4LBbiD/GXvOojZjsHekJrpRUuPggLJr943hDVD5UareeEucjCvaoHCgAFAsBcFQAKAAkDBBcBAAAAAAANBgAGACMJDAEBCQIABgwCAAAAAMqaOwAAAAAMAQYBEQs1DA8ABgEFAiMhCwsOCx0MDxoBGQcYBAgDJBscDB4PBwUQEhEfFR8UFwcFISITHw8MDCAfFgstwSCbM0HWnIEAAwAAABEBZAABCh0BAyZHAQMAypo7AAAAAJaWFAYAAAAAMgAADAMGAAABCQPZoILFk7gfE2y5bt3AC+g/4OwNzdiHKBhIbdeYvYFEjQPKyMkExMUkx0R25UNa/g5KsG0vfUwdUJ8e8HecK/Jkd3qm9XefBOB0BaD1+J+dBJz09vfyGuRYZH09HfdE/kL8v6Ql+H03+tO+9lMmmVg8O1c6gAN6eX0Cbn4="; + let encoded_tx = TWStringHelper::create(encoded_tx); + + // base58 encoded + let new_blockhash = "CyPYVsYWrsJNfVpi8aazu7WsrswNFuDd385z6GNoBGUg"; + let new_blockhash = TWStringHelper::create(new_blockhash); + + // there is no matching pubkey in the transaction account keys. + let private_key = base58::decode( + "A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr", + &SOLANA_ALPHABET, + ) + .unwrap(); + let private_keys = TWDataVectorHelper::create([private_key]); + + let output_data = unsafe { + TWDataHelper::wrap(tw_solana_transaction_update_blockhash_and_sign( + encoded_tx.ptr(), + new_blockhash.ptr(), + private_keys.ptr(), + )) + .to_vec() + .expect("Expected a non-null output data") + }; + let output: Proto::SigningOutput = tw_proto::deserialize(&output_data).unwrap(); + assert_eq!(output.error, SigningError::Error_missing_private_key); +} + +#[test] +fn test_solana_transaction_update_blockhash_and_sign_empty_private_keys() { + // base64 encoded + // https://explorer.solana.com/tx/3KbvREZUat76wgWMtnJfWbJL74Vzh4U2eabVJa3Z3bb2fPtW8AREP5pbmRwUrxZCESbTomWpL41PeKDcPGbojsej?cluster=devnet + let encoded_tx = "AnQTYwZpkm3fs4SdLxnV6gQj3hSLsyacpxDdLMALYWObm722f79IfYFTbZeFK9xHtMumiDOWAM2hHQP4r/GtbARpncaXgOVFv7OgbRLMbuCEJHO1qwcdCbtH72VzyzU8yw9sqqHIAaCUE8xaQTgT6Z5IyZfeyMe2QGJIfOjz65UPAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8Aqe6sdLXiXSDILEtzckCjkjchiSf6zVGpMYiAE5BE2IqHAQUEAgQDAQoMoA8AAAAAAAAG"; + let encoded_tx = TWStringHelper::create(encoded_tx); + + // base58 encoded + let new_blockhash = "CyPYVsYWrsJNfVpi8aazu7WsrswNFuDd385z6GNoBGUg"; + let new_blockhash = TWStringHelper::create(new_blockhash); + + // empty private keys + let private_keys = TWDataVectorHelper::default(); + + let output_data = unsafe { + TWDataHelper::wrap(tw_solana_transaction_update_blockhash_and_sign( + encoded_tx.ptr(), + new_blockhash.ptr(), + private_keys.ptr(), + )) + .to_vec() + .expect("Expected a non-null output data") + }; + let output: Proto::SigningOutput = tw_proto::deserialize(&output_data).unwrap(); + assert_eq!(output.error, SigningError::OK); + + let expected = "AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqbHiki6ThNH3auuyZPQpJntnN0mA//56nMpK/6HIuu8xAQUEAgQDAQoMoA8AAAAAAAAG"; + assert_eq!(output.encoded, expected); + + let expected_message = "AgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqbHiki6ThNH3auuyZPQpJntnN0mA//56nMpK/6HIuu8xAQUEAgQDAQoMoA8AAAAAAAAG"; + assert_eq!(output.message_encoded, expected_message); +} diff --git a/src/DataVector.h b/src/DataVector.h new file mode 100644 index 00000000000..3e3a071807a --- /dev/null +++ b/src/DataVector.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#pragma once + +#include "TrustWalletCore/TWDataVector.h" +#include "Data.h" + +namespace TW { + +static std::vector createFromTWDataVector(const struct TWDataVector* _Nonnull dataVector) { + std::vector ret; + const auto n = TWDataVectorSize(dataVector); + for (auto i = 0uL; i < n; ++i) { + const auto* const elem = TWDataVectorGet(dataVector, i); + if (const auto* const data = reinterpret_cast(elem); data) { + ret.emplace_back(*data); + TWDataDelete(elem); + } + } + return ret; +} + +} // namespace TW diff --git a/src/interface/TWSolanaTransaction.cpp b/src/interface/TWSolanaTransaction.cpp new file mode 100644 index 00000000000..a585c9f7508 --- /dev/null +++ b/src/interface/TWSolanaTransaction.cpp @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#include "TrustWalletCore/TWSolanaTransaction.h" +#include "DataVector.h" +#include "rust/Wrapper.h" + +using namespace TW; + +TWData *_Nonnull TWSolanaTransactionUpdateBlockhashAndSign(TWString *_Nonnull encodedTx, + TWString *_Nonnull recentBlockhash, + const struct TWDataVector *_Nonnull privateKeys) { + + auto& encodedTxRef = *reinterpret_cast(encodedTx); + auto& recentBlockhashRef = *reinterpret_cast(recentBlockhash); + + Rust::TWStringWrapper encodedTxStr = encodedTxRef; + Rust::TWStringWrapper recentBlockhashStr = recentBlockhashRef; + Rust::TWDataVectorWrapper privateKeysVec = createFromTWDataVector(privateKeys); + + Rust::TWDataWrapper output = Rust::tw_solana_transaction_update_blockhash_and_sign(encodedTxStr.get(), + recentBlockhashStr.get(), + privateKeysVec.get()); + + auto outputData = output.toDataOrDefault(); + return TWDataCreateWithBytes(outputData.data(), outputData.size()); +} diff --git a/src/interface/TWTransactionCompiler.cpp b/src/interface/TWTransactionCompiler.cpp index 2a55cf129dc..5db643063ee 100644 --- a/src/interface/TWTransactionCompiler.cpp +++ b/src/interface/TWTransactionCompiler.cpp @@ -5,26 +5,12 @@ #include #include "TransactionCompiler.h" -#include "Data.h" -#include "uint256.h" +#include "DataVector.h" #include using namespace TW; -static std::vector createFromTWDataVector(const struct TWDataVector* _Nonnull dataVector) { - std::vector ret; - const auto n = TWDataVectorSize(dataVector); - for (auto i = 0uL; i < n; ++i) { - const auto* const elem = TWDataVectorGet(dataVector, i); - if (const auto* const data = reinterpret_cast(elem); data) { - ret.emplace_back(*data); - TWDataDelete(elem); - } - } - return ret; -} - TWData *_Nonnull TWTransactionCompilerPreImageHashes(enum TWCoinType coinType, TWData *_Nonnull txInputData) { Data result; try { diff --git a/src/proto/Solana.proto b/src/proto/Solana.proto index 7ee3e0cf244..45d4e858f7f 100644 --- a/src/proto/Solana.proto +++ b/src/proto/Solana.proto @@ -198,6 +198,10 @@ message SigningOutput { // The unsigned transaction string unsigned_tx = 4; + + // The encoded message. Can be used to estimate a transaction fee required to execute the message. + // Please note that this is set only on `SolanaTransaction.updateBlockhashAndSign`, but no on `AnySigner.sign`. + string message_encoded = 5; } /// Transaction pre-signing output diff --git a/tests/chains/Solana/TWSolanaTransaction.cpp b/tests/chains/Solana/TWSolanaTransaction.cpp new file mode 100644 index 00000000000..54f579931e5 --- /dev/null +++ b/tests/chains/Solana/TWSolanaTransaction.cpp @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#include "PrivateKey.h" +#include "TrustWalletCore/TWSolanaTransaction.h" +#include "proto/Solana.pb.h" +#include "TestUtilities.h" + +#include + +using namespace TW; +namespace TW::Solana::tests { + +TEST(TWSolanaTransaction, UpdateBlockhashAndSign) { + // base64 encoded + // https://explorer.solana.com/tx/3KbvREZUat76wgWMtnJfWbJL74Vzh4U2eabVJa3Z3bb2fPtW8AREP5pbmRwUrxZCESbTomWpL41PeKDcPGbojsej?cluster=devnet + auto encodedTx = STRING("AnQTYwZpkm3fs4SdLxnV6gQj3hSLsyacpxDdLMALYWObm722f79IfYFTbZeFK9xHtMumiDOWAM2hHQP4r/GtbARpncaXgOVFv7OgbRLMbuCEJHO1qwcdCbtH72VzyzU8yw9sqqHIAaCUE8xaQTgT6Z5IyZfeyMe2QGJIfOjz65UPAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8Aqe6sdLXiXSDILEtzckCjkjchiSf6zVGpMYiAE5BE2IqHAQUEAgQDAQoMoA8AAAAAAAAG"); + // base58 encoded + auto newBlockhash = STRING("CyPYVsYWrsJNfVpi8aazu7WsrswNFuDd385z6GNoBGUg"); + + auto myPrivateKey = DATA("7f0932159226ddec9e1a4b0b8fe7cdc135049f9e549a867d722aa720dd64f32e"); + auto feePayerPrivateKey = DATA("4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7"); + auto privateKeysVec = WRAP(TWDataVector, TWDataVectorCreateWithData(myPrivateKey.get())); + TWDataVectorAdd(privateKeysVec.get(), feePayerPrivateKey.get()); + + auto outputData = WRAPD(TWSolanaTransactionUpdateBlockhashAndSign(encodedTx.get(), newBlockhash.get(), privateKeysVec.get())); + + Proto::SigningOutput output; + output.ParseFromArray(TWDataBytes(outputData.get()), static_cast(TWDataSize(outputData.get()))); + + EXPECT_EQ(output.error(), Common::Proto::SigningError::OK); + EXPECT_EQ(output.encoded(), "Ajzc/Tke0CG8Cew5qFa6xZI/7Ya3DN0M8Ige6tKPsGzhg8Bw9DqL18KUrEZZ1F4YqZBo4Rv+FsDT8A7Nss7p4A6BNVZzzGprCJqYQeNg0EVIbmPc6mDitNniHXGeKgPZ6QZbM4FElw9O7IOFTpOBPvQFeqy0vZf/aayncL8EK/UEAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqbHiki6ThNH3auuyZPQpJntnN0mA//56nMpK/6HIuu8xAQUEAgQDAQoMoA8AAAAAAAAG"); +} + +} // TW::Solana::tests From 2c423d27900077394e7165db50555dc56d0ba4be Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Thu, 8 Feb 2024 08:21:06 +0100 Subject: [PATCH 050/128] feat(ios): fix ios ci (#3681) * feat(ios): fix ios ci * feat(kotlin): fix kotlin ci * feat(kotlin): fix kotlin ci --- samples/kmp/gradle.properties | 5 ++++- samples/kmp/shared/build.gradle.kts | 2 +- tools/ios-test | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/samples/kmp/gradle.properties b/samples/kmp/gradle.properties index dc2c082de2b..8f3835a8705 100644 --- a/samples/kmp/gradle.properties +++ b/samples/kmp/gradle.properties @@ -10,4 +10,7 @@ android.nonTransitiveRClass=true #MPP kotlin.mpp.enableCInteropCommonization=true -kotlin.mpp.androidSourceSetLayoutVersion=2 \ No newline at end of file +kotlin.mpp.androidSourceSetLayoutVersion=2 + +#Native +kotlin.native.cacheKind.iosArm64=none diff --git a/samples/kmp/shared/build.gradle.kts b/samples/kmp/shared/build.gradle.kts index 8de33b05a29..ca1ad047e3d 100644 --- a/samples/kmp/shared/build.gradle.kts +++ b/samples/kmp/shared/build.gradle.kts @@ -35,7 +35,7 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation("com.trustwallet:wallet-core-kotlin:4.0.16") + implementation("com.trustwallet:wallet-core-kotlin:4.0.22") } } val commonTest by getting { diff --git a/tools/ios-test b/tools/ios-test index 38014c0723d..639c605a8d6 100755 --- a/tools/ios-test +++ b/tools/ios-test @@ -11,7 +11,7 @@ xcodegen && pod install xcodebuild -workspace TrustWalletCore.xcworkspace \ -scheme WalletCore \ -sdk iphonesimulator \ - -destination "platform=iOS Simulator,name=iPhone 14,OS=16.4" \ + -destination "platform=iOS Simulator,name=iPhone 15,OS=17.2" \ test | xcbeautify popd From c08a40707ab12812fd6ed3e30698e5c81763bf7c Mon Sep 17 00:00:00 2001 From: David Kim <63450340+PowerStream3604@users.noreply.github.com> Date: Thu, 8 Feb 2024 18:29:21 +0900 Subject: [PATCH 051/128] [Barz] Add prefixedMsgHash and diamondCut Encoder (#3680) * Add declaration to Barz header * Add Bytes32 type to ETH ABI Proto * Add DiamondCut input to Barz Proto * Add functions for Barz * Add Barz interface * Add Barz test * Update comments * Update header comments * Remove comments * Update hardcode text to constants * Update initData encoding --------- Co-authored-by: Sztergbaum Roman --- include/TrustWalletCore/TWBarz.h | 16 ++++ src/Ethereum/ABI/ProtoParam.h | 20 +++++ src/Ethereum/Barz.cpp | 133 ++++++++++++++++++++++++++++ src/Ethereum/Barz.h | 2 + src/interface/TWBarz.cpp | 21 +++++ src/proto/Barz.proto | 22 +++++ tests/chains/Ethereum/BarzTests.cpp | 102 +++++++++++++++++++++ 7 files changed, 316 insertions(+) diff --git a/include/TrustWalletCore/TWBarz.h b/include/TrustWalletCore/TWBarz.h index bc2d0615ba0..1454e219b5d 100644 --- a/include/TrustWalletCore/TWBarz.h +++ b/include/TrustWalletCore/TWBarz.h @@ -39,4 +39,20 @@ TWData *_Nonnull TWBarzGetInitCode(TWString* _Nonnull factory, struct TWPublicKe /// \return Bytes of the formatted signature TW_EXPORT_STATIC_METHOD TWData *_Nonnull TWBarzGetFormattedSignature(TWData* _Nonnull signature, TWData* _Nonnull challenge, TWData* _Nonnull authenticatorData, TWString* _Nonnull clientDataJSON); + +/// Returns the final hash to be signed by Barz for signing messages & typed data +/// +/// \param msgHash Original msgHash +/// \param barzAddress The address of Barz wallet signing the message +/// \param chainId The chainId of the network the verification will happen +/// \return The final hash to be signed +TW_EXPORT_STATIC_METHOD +TWData *_Nonnull TWBarzGetPrefixedMsgHash(TWData* _Nonnull msgHash, TWString* _Nonnull barzAddress, uint32_t chainId); + +/// Returns the encoded diamondCut function call for Barz contract upgrades +/// +/// \param input The serialized data of DiamondCutInput +/// \return The encoded bytes of diamondCut function call +TW_EXPORT_STATIC_METHOD +TWData *_Nonnull TWBarzGetDiamondCutCode(TWData *_Nonnull input); TW_EXTERN_C_END diff --git a/src/Ethereum/ABI/ProtoParam.h b/src/Ethereum/ABI/ProtoParam.h index c9d6c2f1c80..8391ee94a26 100644 --- a/src/Ethereum/ABI/ProtoParam.h +++ b/src/Ethereum/ABI/ProtoParam.h @@ -74,6 +74,26 @@ class ProtoByteArray final: public BaseProtoParam { Data m_data; }; +class ProtoBytes32 final: public BaseProtoParam { +public: + explicit ProtoBytes32(const Data& data): m_data(data) { + if (data.size() != 32) { + throw std::invalid_argument("Data must be exactly 32 bytes long"); + } + } + + ~ProtoBytes32() override = default; + + AbiProto::Token toToken() const override { + AbiProto::Token proto; + proto.set_byte_array_fix(m_data.data(), m_data.size()); + return proto; + } + +private: + Data m_data; +}; + class ProtoString final: public BaseProtoParam { public: explicit ProtoString(std::string str): m_string(std::move(str)) { diff --git a/src/Ethereum/Barz.cpp b/src/Ethereum/Barz.cpp index 2cd9a0da071..6002af3692b 100644 --- a/src/Ethereum/Barz.cpp +++ b/src/Ethereum/Barz.cpp @@ -2,6 +2,8 @@ // // Copyright © 2017 Trust Wallet. +#include +#include "ABI/ValueEncoder.h" #include "ABI/Function.h" #include "AddressChecksum.h" #include "EIP1014.h" @@ -85,4 +87,135 @@ Data getFormattedSignature(const Data& signature, const Data challenge, const Da return {}; } +Data getPrefixedMsgHash(const Data msgHash, const std::string& barzAddress, const uint32_t chainId) { + // keccak256("EIP712Domain(uint256 chainId,address verifyingContract)"); + const Data& domainSeparatorTypeHashData = parse_hex("0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218"); + // keccak256("BarzMessage(bytes message)") + const Data& barzMsgHashData = parse_hex("0xb1bcb804a4a3a1af3ee7920d949bdfd417ea1b736c3552c8d6563a229a619100"); + const auto signedDataPrefix = "0x1901"; + + auto encodedDomainSeparatorData = Ethereum::ABI::Function::encodeParams(Ethereum::ABI::BaseParams { + std::make_shared(domainSeparatorTypeHashData), + std::make_shared(chainId), + std::make_shared(barzAddress) + }); + if (!encodedDomainSeparatorData.has_value()) { + return {}; + } + + Data domainSeparator = encodedDomainSeparatorData.value(); + const Data domainSeparatorHash = Hash::keccak256(domainSeparator); + + auto encodedRawMessageData = Ethereum::ABI::Function::encodeParams(Ethereum::ABI::BaseParams { + std::make_shared(barzMsgHashData), + std::make_shared(Hash::keccak256(msgHash)), + }); + + Data rawMessageData = encodedRawMessageData.value(); + + auto encodedMsg = Ethereum::ABI::Function::encodeParams(Ethereum::ABI::BaseParams { + std::make_shared(domainSeparatorHash), + std::make_shared(Hash::keccak256(rawMessageData)) + }); + auto encodedMsgData = signedDataPrefix + hex(encodedMsg.value()); + + Data finalEncodedMsgData = parse_hex(encodedMsgData); + + const Data encodedMsgHash = Hash::keccak256(finalEncodedMsgData); + + Data envelope; + append(envelope, encodedMsgHash); + + return envelope; +} + +// Function to encode the diamondCut function call using protobuf message as input +Data getDiamondCutCode(const Proto::DiamondCutInput& input) { + const auto diamondCutSelector = "1f931c1c"; + const auto dataLocationChunk = "60"; + const char defaultPadding = '0'; + Data encoded; + + // function diamondCut( + // FacetCut[] calldata diamondCut, + // address init, + // bytes calldata _calldata // Note that Barz does not use the _calldata for initialization. + // ) + Data encodedSignature = parse_hex(diamondCutSelector); // diamondCut() function selector + encoded.insert(encoded.end(), encodedSignature.begin(), encodedSignature.end()); + + // First argument Data Location `diamondCut` + Data dataLocation = parse_hex(dataLocationChunk); + pad_left(dataLocation, 32); + append(encoded, dataLocation); + + // Encode second Parameter `init` + Data initAddress = parse_hex(input.init_address()); + pad_left(initAddress, 32); + append(encoded, initAddress); + + // Third Argument Data location `_calldata` + auto callDataDataLocation = int(hex(encoded).size()) / 2; + + Ethereum::ABI::ValueEncoder::encodeUInt256(input.facet_cuts_size(), encoded); + + // Prepend the function selector for the diamondCut function + int instructChunk = 0; + int totalInstructChunk = 0; + int prevDataPosition = 0; + const auto encodingChunk = 32; + const auto bytesChunkLine = 5; + int chunkLocation; + Data dataPosition; + // Encode each FacetCut from the input + for (const auto& facetCut : input.facet_cuts()) { + if (instructChunk == 0) { + prevDataPosition = input.facet_cuts_size() * encodingChunk; + Ethereum::ABI::ValueEncoder::encodeUInt256(prevDataPosition, encoded); + chunkLocation = int(hex(encoded).size()) / 2; + } else { + prevDataPosition = prevDataPosition + (instructChunk * encodingChunk); + Ethereum::ABI::ValueEncoder::encodeUInt256(prevDataPosition, dataPosition); + instructChunk = 0; + + encoded.insert(encoded.begin() + chunkLocation, dataPosition.begin(), dataPosition.end()); + ++instructChunk; + } + Ethereum::ABI::ValueEncoder::encodeAddress(parse_hex(facetCut.facet_address()), encoded); // facet address + ++instructChunk; + Ethereum::ABI::ValueEncoder::encodeUInt256(facetCut.action(), encoded); // FacetAction enum + ++instructChunk; + append(encoded, dataLocation); // adding 0x60 DataStorage position + ++instructChunk; + Ethereum::ABI::ValueEncoder::encodeUInt256(facetCut.function_selectors_size(), encoded); // Number of FacetSelector + ++instructChunk; + // Encode and append function selectors + for (const auto& selector : facetCut.function_selectors()) { + Ethereum::ABI::ValueEncoder::encodeBytes(parse_hex(hex(selector)), encoded); + ++instructChunk; + } + totalInstructChunk += instructChunk; + } + + Data calldataLength; + Ethereum::ABI::ValueEncoder::encodeUInt256((totalInstructChunk * encodingChunk) + (bytesChunkLine * encodingChunk), calldataLength); + + encoded.insert(encoded.begin() + callDataDataLocation, calldataLength.begin(), calldataLength.end()); + + auto initDataSize = int(hex(parse_hex(input.init_data())).size()); + if (initDataSize == 0 || initDataSize % 2 != 0) + return {}; + + auto initDataLength = initDataSize / 2; // 1 byte is encoded into 2 char + Ethereum::ABI::ValueEncoder::encodeUInt256(initDataLength, encoded); + + append(encoded, parse_hex(input.init_data())); + + const int paddingLength = (encodingChunk * 2) - (initDataSize % (encodingChunk * 2)); + const std::string padding(paddingLength, defaultPadding); + append(encoded, parse_hex(padding)); + + return encoded; +} + } // namespace TW::Barz diff --git a/src/Ethereum/Barz.h b/src/Ethereum/Barz.h index 9ea1d0c0ccd..f29afc4eeed 100644 --- a/src/Ethereum/Barz.h +++ b/src/Ethereum/Barz.h @@ -14,5 +14,7 @@ namespace TW::Barz { std::string getCounterfactualAddress(const Proto::ContractAddressInput input); Data getInitCode(const std::string& factoryAddress, const PublicKey& publicKey, const std::string& verificationFacet, const uint32_t salt); Data getFormattedSignature(const Data& signature, const Data challenge, const Data& authenticatorData, const std::string& clientDataJSON); +Data getPrefixedMsgHash(const Data msgHash, const std::string& barzAddress, const uint32_t chainId); +Data getDiamondCutCode(const Proto::DiamondCutInput& input); // action should be one of 0, 1, 2. 0 = Add, 1 = Remove, 2 = Replace } diff --git a/src/interface/TWBarz.cpp b/src/interface/TWBarz.cpp index cbfdea41700..60da9690279 100644 --- a/src/interface/TWBarz.cpp +++ b/src/interface/TWBarz.cpp @@ -36,4 +36,25 @@ TWData *_Nonnull TWBarzGetFormattedSignature(TWData* _Nonnull signature, TWData* const auto initCode = TW::Barz::getFormattedSignature(signatureData, challengeData, authenticatorDataConverted, clientDataJSONStr); return TWDataCreateWithData(&initCode); +} + +TWData *_Nonnull TWBarzGetPrefixedMsgHash(TWData* _Nonnull msgHash, TWString* _Nonnull barzAddress, uint32_t chainId) { + const auto& msgHashData = *reinterpret_cast(msgHash); + const auto& barzAddressData = *reinterpret_cast(barzAddress); + + const auto prefixedMsgHash = TW::Barz::getPrefixedMsgHash(msgHashData, barzAddressData, chainId); + return TWDataCreateWithData(&prefixedMsgHash); +} + +TWData *_Nonnull TWBarzGetDiamondCutCode(TWData *_Nonnull input) { + TW::Barz::Proto::DiamondCutInput inputProto; + + const auto bytes = TWDataBytes(input); + const auto size = static_cast(TWDataSize(input)); + if (!inputProto.ParseFromArray(bytes, size)) { + return ""; + } + + const auto diamondCutCode = TW::Barz::getDiamondCutCode(inputProto); + return TWDataCreateWithData(&diamondCutCode); } \ No newline at end of file diff --git a/src/proto/Barz.proto b/src/proto/Barz.proto index b0be650c0f3..6c8342054e7 100644 --- a/src/proto/Barz.proto +++ b/src/proto/Barz.proto @@ -28,3 +28,25 @@ message ContractAddressInput { // Salt is used to derive multiple account from the same public key uint32 salt = 9; } + +// FacetCutAction represents the action to be performed for a FacetCut +enum FacetCutAction { + ADD = 0; + REPLACE = 1; + REMOVE = 2; +} + +// FacetCut represents a single operation to be performed on a facet +message FacetCut { + string facet_address = 1; // The address of the facet + FacetCutAction action = 2; // The action to perform + repeated bytes function_selectors = 3; // List of function selectors, each is bytes4 +} + +// DiamondCutInput represents the input parameters for a diamondCut operation +message DiamondCutInput { + repeated FacetCut facet_cuts = 1; // List of facet cuts to apply + string init_address = 2; // Address to call with `init` data after applying cuts + bytes init_data = 3; // Data to pass to `init` function call +} + diff --git a/tests/chains/Ethereum/BarzTests.cpp b/tests/chains/Ethereum/BarzTests.cpp index 6d4bc6dfc45..479726d7393 100644 --- a/tests/chains/Ethereum/BarzTests.cpp +++ b/tests/chains/Ethereum/BarzTests.cpp @@ -313,4 +313,106 @@ TEST(Barz, SignR1BatchedTransferAccountDeployed) { TWEthereumAbiFunctionDelete(transferFunc); } +TEST(Barz, GetPrefixedMsgHash) { + { + const Data& msgHash = parse_hex("0xa6ebe22d8c1ec7edbd7f5776e49a161f67ab97161d7b8c648d80abf365765cf2"); + const std::string& barzAddress = "0x913233BfC283ffe89a5E70ADC39c0926d240bbD9"; + const auto chainId = 3604; + + const auto& prefixedMsgHash = Barz::getPrefixedMsgHash(msgHash, barzAddress, chainId); + ASSERT_EQ(hexEncoded(prefixedMsgHash), "0x0488fb3e4fdaa890bf55532fc9840fb9edef9c38244f431c9430a78a86d89157"); + } +} + +TEST(Barz, GetPrefixedMsgHashWithZeroChainId) { + { + const Data& msgHash = parse_hex("0xcf267a78c5adaf96f341a696eb576824284c572f3e61be619694d539db1925f9"); + const std::string& barzAddress = "0xB91aaa96B138A1B1D94c9df4628187132c5F2bf1"; + const auto chainId = 0; + + const auto& prefixedMsgHash = Barz::getPrefixedMsgHash(msgHash, barzAddress, chainId); + ASSERT_EQ(hexEncoded(prefixedMsgHash), "0xc74e78634261222af51530703048f98a1b7b995a606a624f0a008e7aaba7a21b"); + } +} + +TEST(Barz, GetDiamondCutCode) { + { + TW::Barz::Proto::DiamondCutInput input; + + input.set_init_address("0x0000000000000000000000000000000000000000"); + input.set_init_data("0x00"); + + auto* facetCutAdd = input.add_facet_cuts(); + facetCutAdd->set_facet_address("0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6"); + facetCutAdd->set_action(TW::Barz::Proto::FacetCutAction::ADD); + + auto functionSelectorAdd = parse_hex("0xfdd8a83c"); + facetCutAdd->add_function_selectors(functionSelectorAdd.data(), functionSelectorAdd.size()); + + const auto& diamondCutCode = Barz::getDiamondCutCode(input); + ASSERT_EQ(hexEncoded(diamondCutCode), "0x1f931c1c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000002279b7a0a67db372996a5fab50d91eaa73d2ebe6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001fdd8a83c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000"); + } } + +TEST(Barz, GetDiamondCutCodeWithMultipleCut) { + { + TW::Barz::Proto::DiamondCutInput input; + + input.set_init_address("0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6"); + input.set_init_data("0x12341234"); + + auto* facetCutMigrationFacet = input.add_facet_cuts(); + facetCutMigrationFacet->set_facet_address("0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6"); + facetCutMigrationFacet->set_action(TW::Barz::Proto::FacetCutAction::ADD); + + auto migrationSignature = parse_hex("0xfdd8a83c"); + + facetCutMigrationFacet->add_function_selectors(migrationSignature.data(), migrationSignature.size()); + facetCutMigrationFacet->add_function_selectors(migrationSignature.data(), migrationSignature.size()); + facetCutMigrationFacet->add_function_selectors(migrationSignature.data(), migrationSignature.size()); + + auto* facetCutTestFacet = input.add_facet_cuts(); + facetCutTestFacet->set_facet_address("0x6e3c94d74af6227aEeF75b54a679e969189a6aEC"); + facetCutTestFacet->set_action(TW::Barz::Proto::FacetCutAction::ADD); + + auto testSignature = parse_hex("0x12345678"); + facetCutTestFacet->add_function_selectors(testSignature.data(), testSignature.size()); + + + const auto& diamondCutCode = Barz::getDiamondCutCode(input); + ASSERT_EQ(hexEncoded(diamondCutCode), "0x1f931c1c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000002279b7a0a67db372996a5fab50d91eaa73d2ebe600000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000002279b7a0a67db372996a5fab50d91eaa73d2ebe6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000003fdd8a83c00000000000000000000000000000000000000000000000000000000fdd8a83c00000000000000000000000000000000000000000000000000000000fdd8a83c000000000000000000000000000000000000000000000000000000000000000000000000000000006e3c94d74af6227aeef75b54a679e969189a6aec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001123456780000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041234123400000000000000000000000000000000000000000000000000000000"); + } +} + +TEST(Barz, GetDiamondCutCodeWithZeroSelector) { + { + TW::Barz::Proto::DiamondCutInput input; + + input.set_init_address("0x0000000000000000000000000000000000000000"); + input.set_init_data("0x00"); + auto* facetCutAdd = input.add_facet_cuts(); + facetCutAdd->set_facet_address("0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6"); + facetCutAdd->set_action(TW::Barz::Proto::FacetCutAction::ADD); + + const auto& diamondCutCode = Barz::getDiamondCutCode(input); + ASSERT_EQ(hexEncoded(diamondCutCode), "0x1f931c1c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000002279b7a0a67db372996a5fab50d91eaa73d2ebe600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000"); + } +} + +TEST(Barz, GetDiamondCutCodeWithLongInitData) { + { + TW::Barz::Proto::DiamondCutInput input; + + input.set_init_address("0x0000000000000000000000000000000000000000"); + input.set_init_data("0xb61d27f6000000000000000000000000c2ce171d25837cd43e496719f5355a847edc679b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024a526d83b00000000000000000000000090f79bf6eb2c4f870365e785982e1f101e93b90600000000000000000000000000000000000000000000000000000000"); + auto* facetCutAdd = input.add_facet_cuts(); + facetCutAdd->set_facet_address("0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6"); + facetCutAdd->set_action(TW::Barz::Proto::FacetCutAction::ADD); + + const auto& diamondCutCode = Barz::getDiamondCutCode(input); + ASSERT_EQ(hexEncoded(diamondCutCode), "0x1f931c1c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000002279b7a0a67db372996a5fab50d91eaa73d2ebe600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c4b61d27f6000000000000000000000000c2ce171d25837cd43e496719f5355a847edc679b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024a526d83b00000000000000000000000090f79bf6eb2c4f870365e785982e1f101e93b9060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + } +} + +} + From 7523c9805ce094639dfbda6d643e439f5ef1705e Mon Sep 17 00:00:00 2001 From: Sztergbaum Roman Date: Fri, 9 Feb 2024 12:19:54 +0100 Subject: [PATCH 052/128] [WIP]: support timeout_height from proto (#3684) --- .../src/modules/serializer/json_serializer.rs | 22 +++++++ rust/tw_cosmos_sdk/src/modules/tx_builder.rs | 4 +- rust/tw_cosmos_sdk/tests/sign.rs | 64 +++++++++++++++++++ src/proto/Cosmos.proto | 3 + 4 files changed, 90 insertions(+), 3 deletions(-) diff --git a/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs b/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs index 0b2c81195ad..1bf3dff3649 100644 --- a/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs +++ b/rust/tw_cosmos_sdk/src/modules/serializer/json_serializer.rs @@ -18,6 +18,9 @@ pub struct SignedTxJson { pub memo: String, pub msg: Vec>, pub signatures: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + pub timeout_height: Option, } #[derive(Serialize)] @@ -28,6 +31,9 @@ pub struct UnsignedTxJson { pub memo: String, pub msgs: Vec>, pub sequence: String, + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + pub timeout_height: Option, } #[derive(Serialize)] @@ -69,11 +75,19 @@ where let signature = Self::serialize_signature(&signed.signer.public_key, signed.signature.clone()); + let convert = |value: u64| { + if value == 0 { + None + } else { + Some(value.to_string()) + } + }; Ok(SignedTxJson { fee: Self::build_fee(&signed.fee), memo: signed.tx_body.memo.clone(), msg, signatures: vec![signature], + timeout_height: convert(signed.tx_body.timeout_height), }) } @@ -87,6 +101,13 @@ where .map(|msg| msg.to_json()) .collect::>()?; + let convert = |value: u64| { + if value == 0 { + None + } else { + Some(value.to_string()) + } + }; Ok(UnsignedTxJson { account_number: unsigned.account_number.to_string(), chain_id: unsigned.chain_id.clone(), @@ -94,6 +115,7 @@ where memo: unsigned.tx_body.memo.clone(), msgs, sequence: unsigned.signer.sequence.to_string(), + timeout_height: convert(unsigned.tx_body.timeout_height), }) } diff --git a/rust/tw_cosmos_sdk/src/modules/tx_builder.rs b/rust/tw_cosmos_sdk/src/modules/tx_builder.rs index c39d13f8c70..8bd97cafbb1 100644 --- a/rust/tw_cosmos_sdk/src/modules/tx_builder.rs +++ b/rust/tw_cosmos_sdk/src/modules/tx_builder.rs @@ -20,8 +20,6 @@ use tw_number::U256; use tw_proto::Cosmos::Proto; use tw_proto::{google, serialize}; -const DEFAULT_TIMEOUT_HEIGHT: u64 = 0; - pub struct TxBuilder { _phantom: PhantomData, } @@ -126,7 +124,7 @@ where Ok(TxBody { messages, memo: input.memo.to_string(), - timeout_height: DEFAULT_TIMEOUT_HEIGHT, + timeout_height: input.timeout_height, }) } diff --git a/rust/tw_cosmos_sdk/tests/sign.rs b/rust/tw_cosmos_sdk/tests/sign.rs index 37436b4947f..8b703393ff3 100644 --- a/rust/tw_cosmos_sdk/tests/sign.rs +++ b/rust/tw_cosmos_sdk/tests/sign.rs @@ -135,6 +135,70 @@ fn test_sign_raw_json() { }); } +#[test] +fn test_sign_raw_json_with_timeout() { + let coin = TestCoinContext::default() + .with_public_key_type(PublicKeyType::Secp256k1) + .with_hrp("cosmos"); + + let raw_json_msg = Proto::mod_Message::RawJSON { + type_pb: "osmosis/poolmanager/split-amount-in".into(), + value: r#"{ + "routes": [ + { + "pools": [ + { + "pool_id": "463", + "token_out_denom": "ibc/1DC495FCEFDA068A3820F903EDBD78B942FBD204D7E93D3BA2B432E9669D1A59" + }, + { + "pool_id": "916", + "token_out_denom": "ibc/573FCD90FACEE750F55A8864EF7D38265F07E5A9273FA0E8DAFD39951332B580" + } + ], + "token_in_amount": "70000" + }, + { + "pools": [ + { + "pool_id": "907", + "token_out_denom": "ibc/573FCD90FACEE750F55A8864EF7D38265F07E5A9273FA0E8DAFD39951332B580" + } + ], + "token_in_amount": "30000" + } + ], + "sender": "osmo1qr7dhmvcqm4fnleaqel3gel4u20nk5rp9rwsae", + "token_in_denom": "uosmo", + "token_out_min_amount": "885297" + }"#.into(), + }; + let input = Proto::SigningInput { + account_number: 24139, + chain_id: "osmosis-1".into(), + sequence: 191, + fee: Some(make_fee(617438, make_amount("uosmo", "1853"))), + private_key: account_1037_private_key(), + messages: vec![make_message(MessageEnum::raw_json_message(raw_json_msg))], + timeout_height: 13692007, + ..Proto::SigningInput::default() + }; + + // `RawJSON` doesn't support Protobuf serialization and signing. + test_sign_protobuf_error::(TestErrorInput { + coin: &coin, + input: input.clone(), + error: SigningError::Error_not_supported, + }); + test_sign_json::(TestInput { + coin: &coin, + input, + tx: r#"{"mode":"block","tx":{"fee":{"amount":[{"amount":"1853","denom":"uosmo"}],"gas":"617438"},"memo":"","msg":[{"type":"osmosis/poolmanager/split-amount-in","value":{"routes":[{"pools":[{"pool_id":"463","token_out_denom":"ibc/1DC495FCEFDA068A3820F903EDBD78B942FBD204D7E93D3BA2B432E9669D1A59"},{"pool_id":"916","token_out_denom":"ibc/573FCD90FACEE750F55A8864EF7D38265F07E5A9273FA0E8DAFD39951332B580"}],"token_in_amount":"70000"},{"pools":[{"pool_id":"907","token_out_denom":"ibc/573FCD90FACEE750F55A8864EF7D38265F07E5A9273FA0E8DAFD39951332B580"}],"token_in_amount":"30000"}],"sender":"osmo1qr7dhmvcqm4fnleaqel3gel4u20nk5rp9rwsae","token_in_denom":"uosmo","token_out_min_amount":"885297"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"7gMxXwqzZDe5+h1i16q7A7CgGUtLl2+Q8/YaUZCeYvp8kISbBwD2SlNTpJtz1RLskzF2uNcDebo61HbcVn9dAw=="}],"timeout_height":"13692007"}}"#, + signature: "ee03315f0ab36437b9fa1d62d7aabb03b0a0194b4b976f90f3f61a51909e62fa7c90849b0700f64a5353a49b73d512ec933176b8d70379ba3ad476dc567f5d03", + signature_json: r#"[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"7gMxXwqzZDe5+h1i16q7A7CgGUtLl2+Q8/YaUZCeYvp8kISbBwD2SlNTpJtz1RLskzF2uNcDebo61HbcVn9dAw=="}]"#, + }); +} + #[test] fn test_sign_ibc_transfer() { let coin = TestCoinContext::default() diff --git a/src/proto/Cosmos.proto b/src/proto/Cosmos.proto index 6ac55b56570..04384442e93 100644 --- a/src/proto/Cosmos.proto +++ b/src/proto/Cosmos.proto @@ -425,6 +425,9 @@ message SigningInput { // Optional. If set, use a different Signer info when signing the transaction. SignerInfo signer_info = 12; + + // Optional timeout_height + uint64 timeout_height = 13; } // Result containing the signed and encoded transaction. From 80f66bba7922e6218d86a440be7131d9f18ad9aa Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Sat, 17 Feb 2024 10:53:01 +0100 Subject: [PATCH 053/128] [Bitcoin]: Add linker options (#3687) --- samples/kmp/shared/build.gradle.kts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/samples/kmp/shared/build.gradle.kts b/samples/kmp/shared/build.gradle.kts index ca1ad047e3d..461c09d9882 100644 --- a/samples/kmp/shared/build.gradle.kts +++ b/samples/kmp/shared/build.gradle.kts @@ -29,13 +29,23 @@ kotlin { podfile = project.file("../iosApp/Podfile") framework { baseName = "shared" + + listOf( + iosX64(), + iosArm64(), + iosSimulatorArm64() + ).forEach { + it.binaries.all { + linkerOpts += "-ld64" + } + } } } sourceSets { val commonMain by getting { dependencies { - implementation("com.trustwallet:wallet-core-kotlin:4.0.22") + implementation("com.trustwallet:wallet-core-kotlin:4.0.24") } } val commonTest by getting { From f8ccbc96ce5b6f25160a8c4e4570067d76432def Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 28 Feb 2024 15:52:36 +0100 Subject: [PATCH 054/128] [RVN]: Update block explorer (#3708) * [RVN]: Update block explorer * [Misc]: Fix Protobuf comments regarding U256 encoding --- registry.json | 2 +- src/proto/Aeternity.proto | 4 +-- src/proto/Aion.proto | 8 ++--- src/proto/Cardano.proto | 2 +- src/proto/EOS.proto | 4 +-- src/proto/Ethereum.proto | 30 +++++++++---------- src/proto/Filecoin.proto | 6 ++-- src/proto/Harmony.proto | 24 +++++++-------- src/proto/Icon.proto | 2 +- src/proto/NULS.proto | 4 +-- src/proto/Nebulas.proto | 18 +++++------ src/proto/Polkadot.proto | 14 ++++----- src/proto/Theta.proto | 6 ++-- src/proto/VeChain.proto | 2 +- src/proto/Zilliqa.proto | 4 +-- tests/chains/Ravencoin/TWCoinTypeTests.cpp | 4 +-- .../Ravencoin/TWRavencoinTransactionTests.cpp | 2 +- 17 files changed, 68 insertions(+), 68 deletions(-) diff --git a/registry.json b/registry.json index ca3f9cf2660..941c1312d0a 100644 --- a/registry.json +++ b/registry.json @@ -1593,7 +1593,7 @@ "publicKeyHasher": "sha256ripemd", "base58Hasher": "sha256d", "explorer": { - "url": "https://ravencoin.network", + "url": "https://blockbook.ravencoin.org", "txPath": "/tx/", "accountPath": "/address/" }, diff --git a/src/proto/Aeternity.proto b/src/proto/Aeternity.proto index fc501db4859..98c5fe5b28d 100644 --- a/src/proto/Aeternity.proto +++ b/src/proto/Aeternity.proto @@ -11,10 +11,10 @@ message SigningInput { // Address of the recipient with "ak_" prefix string to_address = 2; - // Amount (uint256, serialized little endian) + // Amount (uint256, serialized big endian) bytes amount = 3; - // Fee amount (uint256, serialized little endian) + // Fee amount (uint256, serialized big endian) bytes fee = 4; // Message, optional diff --git a/src/proto/Aion.proto b/src/proto/Aion.proto index 3df8e2d5679..f4ac15ce4e8 100644 --- a/src/proto/Aion.proto +++ b/src/proto/Aion.proto @@ -7,19 +7,19 @@ import "Common.proto"; // Input data necessary to create a signed transaction. message SigningInput { - // Nonce (uint256, serialized little endian) + // Nonce (uint256, serialized big endian) bytes nonce = 1; - // Gas price (uint256, serialized little endian) + // Gas price (uint256, serialized big endian) bytes gas_price = 2; - // Gas limit (uint256, serialized little endian) + // Gas limit (uint256, serialized big endian) bytes gas_limit = 3; // Recipient's address. string to_address = 4; - // Amount to send in wei (uint256, serialized little endian) + // Amount to send in wei (uint256, serialized big endian) bytes amount = 5; // Optional payload diff --git a/src/proto/Cardano.proto b/src/proto/Cardano.proto index d32bae5509b..e4581e9ab75 100644 --- a/src/proto/Cardano.proto +++ b/src/proto/Cardano.proto @@ -22,7 +22,7 @@ message TokenAmount { // The name of the asset (within the policy) string asset_name = 2; - // The amount (uint256, serialized little endian) + // The amount (uint256, serialized big endian) bytes amount = 3; // The name of the asset (hex encoded). Ignored if `asset_name` is set diff --git a/src/proto/EOS.proto b/src/proto/EOS.proto index f890a8c83d1..04bec8f5906 100644 --- a/src/proto/EOS.proto +++ b/src/proto/EOS.proto @@ -25,10 +25,10 @@ message Asset { // Input data necessary to create a signed transaction. message SigningInput { - // Chain id (uint256, serialized little endian) + // Chain id (uint256, serialized big endian) bytes chain_id = 1; - // Reference Block Id (uint256, serialized little endian) + // Reference Block Id (uint256, serialized big endian) bytes reference_block_id = 2; // Timestamp on the reference block diff --git a/src/proto/Ethereum.proto b/src/proto/Ethereum.proto index 52255fc6b0b..d3de8617ba8 100644 --- a/src/proto/Ethereum.proto +++ b/src/proto/Ethereum.proto @@ -9,7 +9,7 @@ import "Common.proto"; message Transaction { // Native coin transfer transaction message Transfer { - // Amount to send in wei (uint256, serialized little endian) + // Amount to send in wei (uint256, serialized big endian) bytes amount = 1; // Optional payload data @@ -21,7 +21,7 @@ message Transaction { // destination address (string) string to = 1; - // Amount to send (uint256, serialized little endian) + // Amount to send (uint256, serialized big endian) bytes amount = 2; } @@ -30,7 +30,7 @@ message Transaction { // Target of the approval string spender = 1; - // Amount to send (uint256, serialized little endian) + // Amount to send (uint256, serialized big endian) bytes amount = 2; } @@ -42,7 +42,7 @@ message Transaction { // Destination address string to = 2; - // ID of the token (uint256, serialized little endian) + // ID of the token (uint256, serialized big endian) bytes token_id = 3; } @@ -54,10 +54,10 @@ message Transaction { // Destination address string to = 2; - // ID of the token (uint256, serialized little endian) + // ID of the token (uint256, serialized big endian) bytes token_id = 3; - // The amount of tokens being transferred (uint256, serialized little endian) + // The amount of tokens being transferred (uint256, serialized big endian) bytes value = 4; bytes data = 5; @@ -65,7 +65,7 @@ message Transaction { // Generic smart contract transaction message ContractGeneric { - // Amount to send in wei (uint256, serialized little endian) + // Amount to send in wei (uint256, serialized big endian) bytes amount = 1; // Contract call payload data @@ -78,7 +78,7 @@ message Transaction { // Recipient addresses. string address = 1; - // Amounts to send in wei (uint256, serialized little endian) + // Amounts to send in wei (uint256, serialized big endian) bytes amount = 2; // Contract call payloads data @@ -136,28 +136,28 @@ message UserOperation { // Input data necessary to create a signed transaction. // Legacy and EIP2718/EIP1559 transactions supported, see TransactionMode. message SigningInput { - // Chain identifier (uint256, serialized little endian) + // Chain identifier (uint256, serialized big endian) bytes chain_id = 1; - // Nonce (uint256, serialized little endian) + // Nonce (uint256, serialized big endian) bytes nonce = 2; // Transaction version selector: Legacy or enveloped, has impact on fee structure. // Default is Legacy (value 0) TransactionMode tx_mode = 3; - // Gas price (uint256, serialized little endian) + // Gas price (uint256, serialized big endian) // Relevant for legacy transactions only (disregarded for enveloped/EIP1559) bytes gas_price = 4; - // Gas limit (uint256, serialized little endian) + // Gas limit (uint256, serialized big endian) bytes gas_limit = 5; - // Maximum optional inclusion fee (aka tip) (uint256, serialized little endian) + // Maximum optional inclusion fee (aka tip) (uint256, serialized big endian) // Relevant for enveloped/EIP1559 transactions only, tx_mode=Enveloped, (disregarded for legacy) bytes max_inclusion_fee_per_gas = 6; - // Maximum fee (uint256, serialized little endian) + // Maximum fee (uint256, serialized big endian) // Relevant for enveloped/EIP1559 transactions only, tx_mode=Enveloped, (disregarded for legacy) bytes max_fee_per_gas = 7; @@ -180,7 +180,7 @@ message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; - // The V, R, S components of the resulting signature, (each uint256, serialized little endian) + // The V, R, S components of the resulting signature, (each uint256, serialized big endian) bytes v = 2; bytes r = 3; bytes s = 4; diff --git a/src/proto/Filecoin.proto b/src/proto/Filecoin.proto index f43b2d1d6e5..09a26ce03a4 100644 --- a/src/proto/Filecoin.proto +++ b/src/proto/Filecoin.proto @@ -25,16 +25,16 @@ message SigningInput { // Transaction nonce. uint64 nonce = 3; - // Transfer value (uint256, serialized little endian) + // Transfer value (uint256, serialized big endian) bytes value = 4; // Gas limit. int64 gas_limit = 5; - // Gas fee cap (uint256, serialized little endian) + // Gas fee cap (uint256, serialized big endian) bytes gas_fee_cap = 6; - // Gas premium (uint256, serialized little endian) + // Gas premium (uint256, serialized big endian) bytes gas_premium = 7; // Message params. diff --git a/src/proto/Harmony.proto b/src/proto/Harmony.proto index 8d341e7ebe1..85852dbc348 100644 --- a/src/proto/Harmony.proto +++ b/src/proto/Harmony.proto @@ -7,7 +7,7 @@ import "Common.proto"; // Input data necessary to create a signed transaction. message SigningInput { - // Chain identifier (uint256, serialized little endian) + // Chain identifier (uint256, serialized big endian) bytes chain_id = 1; // The secret private key used for signing (32 bytes). @@ -39,28 +39,28 @@ message SigningOutput { // A Transfer message message TransactionMessage { - // Nonce (uint256, serialized little endian) + // Nonce (uint256, serialized big endian) bytes nonce = 1; - // Gas price (uint256, serialized little endian) + // Gas price (uint256, serialized big endian) bytes gas_price = 2; - // Gas limit (uint256, serialized little endian) + // Gas limit (uint256, serialized big endian) bytes gas_limit = 3; // Recipient's address. string to_address = 4; - // Amount to send in wei (uint256, serialized little endian) + // Amount to send in wei (uint256, serialized big endian) bytes amount = 5; // Optional payload bytes payload = 6; - // From shard ID (uint256, serialized little endian) + // From shard ID (uint256, serialized big endian) bytes from_shard_id = 7; - // To Shard ID (uint256, serialized little endian) + // To Shard ID (uint256, serialized big endian) bytes to_shard_id = 8; } @@ -75,13 +75,13 @@ message StakingMessage { DirectiveCollectRewards collect_rewards = 5; } - // Nonce (uint256, serialized little endian) + // Nonce (uint256, serialized big endian) bytes nonce = 6; - // Gas price (uint256, serialized little endian) + // Gas price (uint256, serialized big endian) bytes gas_price = 7; - // Gas limit (uint256, serialized little endian) + // Gas limit (uint256, serialized big endian) bytes gas_limit = 8; } @@ -156,7 +156,7 @@ message DirectiveDelegate { // Validator address string validator_address = 2; - // Delegate amount (uint256, serialized little endian) + // Delegate amount (uint256, serialized big endian) bytes amount = 3; } @@ -168,7 +168,7 @@ message DirectiveUndelegate { // Validator address string validator_address = 2; - // Undelegate amount (uint256, serialized little endian) + // Undelegate amount (uint256, serialized big endian) bytes amount = 3; } diff --git a/src/proto/Icon.proto b/src/proto/Icon.proto index 9c19fbe6ec4..9b378f4dc82 100644 --- a/src/proto/Icon.proto +++ b/src/proto/Icon.proto @@ -13,7 +13,7 @@ message SigningInput { // Recipient address. string to_address = 2; - // Transfer amount (uint256, serialized little endian) + // Transfer amount (uint256, serialized big endian) bytes value = 3; // The amount of step to send with the transaction. diff --git a/src/proto/NULS.proto b/src/proto/NULS.proto index 2ee273d7824..7bfe9d62238 100644 --- a/src/proto/NULS.proto +++ b/src/proto/NULS.proto @@ -37,7 +37,7 @@ message TransactionCoinTo { // ID of the asset uint32 assets_id = 3; - // transaction amount (uint256, serialized little endian) + // transaction amount (uint256, serialized big endian) bytes id_amount = 4; // lock time @@ -97,7 +97,7 @@ message SigningInput { // Destination address string to = 3; - // Transfer amount (uint256, serialized little endian) + // Transfer amount (uint256, serialized big endian) bytes amount = 4; // Chain ID diff --git a/src/proto/Nebulas.proto b/src/proto/Nebulas.proto index ea915410d59..215bd503495 100644 --- a/src/proto/Nebulas.proto +++ b/src/proto/Nebulas.proto @@ -8,25 +8,25 @@ message SigningInput { // sender's address. string from_address = 1; - // Chain identifier (uint256, serialized little endian) + // Chain identifier (uint256, serialized big endian) bytes chain_id = 2; - // Nonce (uint256, serialized little endian) + // Nonce (uint256, serialized big endian) bytes nonce = 3; - // Gas price (uint256, serialized little endian) + // Gas price (uint256, serialized big endian) bytes gas_price = 4; - // Gas limit (uint256, serialized little endian) + // Gas limit (uint256, serialized big endian) bytes gas_limit = 5; // Recipient's address. string to_address = 6; - // Amount to send in wei, 1 NAS = 10^18 Wei (uint256, serialized little endian) + // Amount to send in wei, 1 NAS = 10^18 Wei (uint256, serialized big endian) bytes amount = 7; - // Timestamp to create transaction (uint256, serialized little endian) + // Timestamp to create transaction (uint256, serialized big endian) bytes timestamp = 8; // Optional payload @@ -65,7 +65,7 @@ message RawTransaction { // destination address bytes to = 3; - // amount (uint256, serialized little endian) + // amount (uint256, serialized big endian) bytes value = 4; // Nonce (should be larger than in the last transaction of the account) @@ -80,10 +80,10 @@ message RawTransaction { // chain ID (4 bytes) uint32 chain_id = 8; - // gas price (uint256, serialized little endian) + // gas price (uint256, serialized big endian) bytes gas_price = 9; - // gas limit (uint256, serialized little endian) + // gas limit (uint256, serialized big endian) bytes gas_limit = 10; // algorithm diff --git a/src/proto/Polkadot.proto b/src/proto/Polkadot.proto index b853bdf441c..c25ddbd019e 100644 --- a/src/proto/Polkadot.proto +++ b/src/proto/Polkadot.proto @@ -45,7 +45,7 @@ message Balance { // destination address string to_address = 1; - // amount (uint256, serialized little endian) + // amount (uint256, serialized big endian) bytes value = 2; // max 32 chars @@ -105,7 +105,7 @@ message Staking { // controller ID (optional) string controller = 1; - // amount (uint256, serialized little endian) + // amount (uint256, serialized big endian) bytes value = 2; // destination for rewards @@ -120,7 +120,7 @@ message Staking { // controller ID (optional) string controller = 1; - // amount (uint256, serialized little endian) + // amount (uint256, serialized big endian) bytes value = 2; // destination for rewards @@ -135,7 +135,7 @@ message Staking { // Bond extra amount message BondExtra { - // amount (uint256, serialized little endian) + // amount (uint256, serialized big endian) bytes value = 1; // call indices @@ -144,7 +144,7 @@ message Staking { // Unbond message Unbond { - // amount (uint256, serialized little endian) + // amount (uint256, serialized big endian) bytes value = 1; // call indices @@ -153,7 +153,7 @@ message Staking { // Rebond message Rebond { - // amount (uint256, serialized little endian) + // amount (uint256, serialized big endian) bytes value = 1; // call indices @@ -179,7 +179,7 @@ message Staking { // Chill and unbound message ChillAndUnbond { - // amount (uint256, serialized little endian) + // amount (uint256, serialized big endian) bytes value = 1; // call indices diff --git a/src/proto/Theta.proto b/src/proto/Theta.proto index 01e8ebdfff9..7191c6cc5ae 100644 --- a/src/proto/Theta.proto +++ b/src/proto/Theta.proto @@ -13,16 +13,16 @@ message SigningInput { /// Recipient address string to_address = 2; - /// Theta token amount to send in wei (uint256, serialized little endian) + /// Theta token amount to send in wei (uint256, serialized big endian) bytes theta_amount = 3; - /// TFuel token amount to send in wei (uint256, serialized little endian) + /// TFuel token amount to send in wei (uint256, serialized big endian) bytes tfuel_amount = 4; /// Sequence number of the transaction for the sender address uint64 sequence = 5; - /// Fee amount in TFuel wei for the transaction (uint256, serialized little endian) + /// Fee amount in TFuel wei for the transaction (uint256, serialized big endian) bytes fee = 6; /// The secret private key used for signing (32 bytes). diff --git a/src/proto/VeChain.proto b/src/proto/VeChain.proto index 62f641ad577..26d4086c1db 100644 --- a/src/proto/VeChain.proto +++ b/src/proto/VeChain.proto @@ -10,7 +10,7 @@ message Clause { /// Recipient address. string to = 1; - /// Transaction amount (uint256, serialized little endian) + /// Transaction amount (uint256, serialized big endian) bytes value = 2; /// Payload data. diff --git a/src/proto/Zilliqa.proto b/src/proto/Zilliqa.proto index 03e5e2167fa..4e4e661fc98 100644 --- a/src/proto/Zilliqa.proto +++ b/src/proto/Zilliqa.proto @@ -7,13 +7,13 @@ option java_package = "wallet.core.jni.proto"; message Transaction { // Transfer transaction message Transfer { - // Amount to send (uint256, serialized little endian) + // Amount to send (uint256, serialized big endian) bytes amount = 1; } // Generic contract call message Raw { - // Amount to send (uint256, serialized little endian) + // Amount to send (uint256, serialized big endian) bytes amount = 1; // Smart contract code diff --git a/tests/chains/Ravencoin/TWCoinTypeTests.cpp b/tests/chains/Ravencoin/TWCoinTypeTests.cpp index 2e7e0df55fb..38304931caa 100644 --- a/tests/chains/Ravencoin/TWCoinTypeTests.cpp +++ b/tests/chains/Ravencoin/TWCoinTypeTests.cpp @@ -25,8 +25,8 @@ TEST(TWRavencoinCoinType, TWCoinType) { ASSERT_EQ(0x7a, TWCoinTypeP2shPrefix(TWCoinTypeRavencoin)); ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeRavencoin)); assertStringsEqual(symbol, "RVN"); - assertStringsEqual(txUrl, "https://ravencoin.network/tx/t123"); - assertStringsEqual(accUrl, "https://ravencoin.network/address/a12"); + assertStringsEqual(txUrl, "https://blockbook.ravencoin.org/tx/t123"); + assertStringsEqual(accUrl, "https://blockbook.ravencoin.org/address/a12"); assertStringsEqual(id, "ravencoin"); assertStringsEqual(name, "Ravencoin"); } diff --git a/tests/chains/Ravencoin/TWRavencoinTransactionTests.cpp b/tests/chains/Ravencoin/TWRavencoinTransactionTests.cpp index 161acf0d6c9..7c3c7da223e 100644 --- a/tests/chains/Ravencoin/TWRavencoinTransactionTests.cpp +++ b/tests/chains/Ravencoin/TWRavencoinTransactionTests.cpp @@ -81,7 +81,7 @@ TEST(RavencoinTransaction, LockScripts) { assertHexEqual(scriptData, "76a9149451f4546e09fc2e49ef9b5303924712ec2b038e88ac"); // P2SH - // https://ravencoin.network/api/tx/f600d07814677d1f60545c8f7f71260238595c4928d6fb87caa0f9dd732e9bb5 + // https://blockbook.ravencoin.org/tx/f600d07814677d1f60545c8f7f71260238595c4928d6fb87caa0f9dd732e9bb5 auto script2 = WRAP(TWBitcoinScript, TWBitcoinScriptLockScriptForAddress(STRING("rPWwn5h4QFZNaz1XmY39rc73sdYGGDdmq1").get(), TWCoinTypeRavencoin)); auto scriptData2 = WRAPD(TWBitcoinScriptData(script2.get())); From 7c71119ab797fb339135feab2592e6dd1d66457c Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:08:44 +0100 Subject: [PATCH 055/128] [Bitcoin]: UTXO selection and dust improvements (#3675) * [Bitcoin]: Add Fixed Dust threshold to SigningInput * Filter out Dust UTXOs before actual UTXO selecting * [Bitcoin]: Check the Change output to be at least Dust threshold * [Bitcoin]: Add Fixed Dust threshold to SigningInput * Filter out Dust UTXOs before actual UTXO selecting * [Bitcoin]: Fix iOS test * [Bitcoin]: Fix comments * [Bitcoin]: Update KMP WC * [Bitcoin]: Return Error_not_enough_utxos if max amount requested, but total amount is dust --- rust/tw_coin_entry/src/error.rs | 1 + src/Bitcoin/DustCalculator.cpp | 38 +++ src/Bitcoin/DustCalculator.h | 51 ++++ src/Bitcoin/InputSelector.cpp | 47 ++-- src/Bitcoin/InputSelector.h | 28 +- src/Bitcoin/SigningInput.cpp | 6 + src/Bitcoin/SigningInput.h | 5 +- src/Bitcoin/TransactionBuilder.cpp | 46 ++- src/proto/Bitcoin.proto | 7 + src/proto/Common.proto | 2 + swift/Tests/Blockchains/BitcoinTests.swift | 3 + tests/chains/Bitcoin/InputSelectorTests.cpp | 69 +++-- .../chains/Bitcoin/TWBitcoinSigningTests.cpp | 266 +++++++++++++++++- tests/chains/Bitcoin/TransactionPlanTests.cpp | 52 +++- tests/chains/Bitcoin/TxComparisonHelper.cpp | 1 + 15 files changed, 552 insertions(+), 70 deletions(-) create mode 100644 src/Bitcoin/DustCalculator.cpp create mode 100644 src/Bitcoin/DustCalculator.h diff --git a/rust/tw_coin_entry/src/error.rs b/rust/tw_coin_entry/src/error.rs index e8ceb589066..bd28fddd6c3 100644 --- a/rust/tw_coin_entry/src/error.rs +++ b/rust/tw_coin_entry/src/error.rs @@ -126,6 +126,7 @@ impl fmt::Display for SigningError { SigningErrorType::Error_invalid_params => "Incorrect input parameter", SigningErrorType::Error_invalid_requested_token_amount => "Invalid input token amount", SigningErrorType::Error_not_supported => "Operation not supported for the chain", + SigningErrorType::Error_dust_amount_requested => "Requested amount is too low (less dust)", }; write!(f, "{str}") } diff --git a/src/Bitcoin/DustCalculator.cpp b/src/Bitcoin/DustCalculator.cpp new file mode 100644 index 00000000000..3aeea03cecb --- /dev/null +++ b/src/Bitcoin/DustCalculator.cpp @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#include "DustCalculator.h" + +namespace TW::Bitcoin { + +FixedDustCalculator::FixedDustCalculator(Amount fixed) noexcept + : fixedDustAmount(fixed) { +} + +Amount FixedDustCalculator::dustAmount([[maybe_unused]] Amount byteFee) noexcept { + return fixedDustAmount; +} + +LegacyDustCalculator::LegacyDustCalculator(TWCoinType coinType) noexcept + : feeCalculator(getFeeCalculator(coinType, false)) { +} + +Amount LegacyDustCalculator::dustAmount([[maybe_unused]] Amount byteFee) noexcept { + return feeCalculator.calculateSingleInput(byteFee); +} + +DustCalculatorShared getDustCalculator(const Proto::SigningInput& input) { + if (input.disable_dust_filter()) { + return std::make_shared(0); + } + + if (input.has_fixed_dust_threshold()) { + return std::make_shared(input.fixed_dust_threshold()); + } + + const auto coinType = static_cast(input.coin_type()); + return std::make_shared(coinType); +} + +} // namespace TW::Bitcoin diff --git a/src/Bitcoin/DustCalculator.h b/src/Bitcoin/DustCalculator.h new file mode 100644 index 00000000000..825ea9b2ba6 --- /dev/null +++ b/src/Bitcoin/DustCalculator.h @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#pragma once + +#include "Amount.h" +#include "FeeCalculator.h" +#include "proto/Bitcoin.pb.h" + +#include +#include + +namespace TW::Bitcoin { + +/// Interface for transaction dust amount calculator. +struct DustCalculator { + virtual ~DustCalculator() noexcept = default; + + /// Returns a Dust threshold of a transaction UTXO or output. + virtual Amount dustAmount(Amount byteFee) noexcept = 0; +}; + +/// Always returns a fixed Dust amount specified in the signing request. +class FixedDustCalculator final: public DustCalculator { +public: + explicit FixedDustCalculator(Amount fixed) noexcept; + + Amount dustAmount([[maybe_unused]] Amount byteFee) noexcept override; + +private: + Amount fixedDustAmount {0}; +}; + +/// Legacy Dust filter implementation using [`FeeCalculator::calculateSingleInput`]. +/// Depends on a coin type, sats/Byte fee. +class LegacyDustCalculator final: public DustCalculator { +public: + explicit LegacyDustCalculator(TWCoinType coinType) noexcept; + + Amount dustAmount(Amount byteFee) noexcept override; + +private: + const FeeCalculator& feeCalculator; +}; + +using DustCalculatorShared = std::shared_ptr; + +DustCalculatorShared getDustCalculator(const Proto::SigningInput& input); + +} // namespace TW::Bitcoin diff --git a/src/Bitcoin/InputSelector.cpp b/src/Bitcoin/InputSelector.cpp index 9b2bc7bb175..98a5f4e3358 100644 --- a/src/Bitcoin/InputSelector.cpp +++ b/src/Bitcoin/InputSelector.cpp @@ -25,8 +25,8 @@ template std::vector InputSelector::filterOutDust(const std::vector& inputs, int64_t byteFee) noexcept { - auto inputFeeLimit = static_cast(feeCalculator.calculateSingleInput(byteFee)); - return filterThreshold(inputs, inputFeeLimit); + auto dustThreshold = static_cast(dustCalculator->dustAmount(byteFee)); + return filterThreshold(inputs, dustThreshold); } // Filters utxos that are dust @@ -36,7 +36,7 @@ InputSelector::filterThreshold(const std::vector uint64_t minimumAmount) noexcept { std::vector filtered; for (auto& i : inputs) { - if (static_cast(i.amount) > minimumAmount) { + if (static_cast(i.amount) >= minimumAmount) { filtered.push_back(i); } } @@ -70,22 +70,24 @@ InputSelector::select(uint64_t targetValue, uint64_t byteFee, ui return {}; } + // Get all possible utxo selections up to a maximum size, sort by total amount, increasing + std::vector sorted = filterOutDust(_inputs, byteFee); + std::sort( + sorted.begin(), + sorted.end(), + [](const TypeWithAmount& lhs, const TypeWithAmount& rhs) { + return lhs.amount < rhs.amount; + }); + // total values of utxos should be greater than targetValue - if (_inputs.empty() || sum(_inputs) < targetValue) { + if (sorted.empty() || sum(sorted) < targetValue) { return {}; } - assert(_inputs.size() >= 1); + assert(sorted.size() >= 1); // definitions for the following calculation const auto doubleTargetValue = targetValue * 2; - // Get all possible utxo selections up to a maximum size, sort by total amount, increasing - std::vector sorted = _inputs; - std::sort(sorted.begin(), sorted.end(), - [](const TypeWithAmount& lhs, const TypeWithAmount& rhs) { - return lhs.amount < rhs.amount; - }); - // Precompute maximum amount possible to obtain with given number of inputs const auto n = sorted.size(); std::vector maxWithXInputs = std::vector(); @@ -104,7 +106,7 @@ InputSelector::select(uint64_t targetValue, uint64_t byteFee, ui return doubleTargetValue - val; }; - const int64_t dustThreshold = feeCalculator.calculateSingleInput(byteFee); + const int64_t dustThreshold = dustCalculator->dustAmount(byteFee); // 1. Find a combination of the fewest inputs that is // (1) bigger than what we need @@ -131,7 +133,7 @@ InputSelector::select(uint64_t targetValue, uint64_t byteFee, ui const std::vector& rhs) { return distFrom2x(sum(lhs)) < distFrom2x(sum(rhs)); }); - return filterOutDust(slices.front(), byteFee); + return slices.front(); } } @@ -150,11 +152,14 @@ InputSelector::select(uint64_t targetValue, uint64_t byteFee, ui }), slices.end()); if (!slices.empty()) { - return filterOutDust(slices.front(), byteFee); + return slices.front(); } } - return {}; + // If we couldn't find a combination of inputs to cover estimated transaction fee and the target amount, + // return the whole set of UTXOs. Later, the transaction fee will be calculated more accurately, + // and these UTXOs can be enough. + return sorted; } template @@ -170,13 +175,13 @@ std::vector InputSelector::selectSimple(int64_t } assert(_inputs.size() >= 1); - // target value is larger that original, but not by a factor of 2 (optimized for large UTXO + // target value is larger than original, but not by a factor of 2 (optimized for large UTXO // cases) const auto increasedTargetValue = (uint64_t)((double)targetValue * 1.1 + feeCalculator.calculate(_inputs.size(), numOutputs, byteFee) + 1000); - const int64_t dustThreshold = feeCalculator.calculateSingleInput(byteFee); + const int64_t dustThreshold = dustCalculator->dustAmount(byteFee); // Go through inputs in a single pass, in the order they appear, no optimization uint64_t sum = 0; @@ -193,8 +198,10 @@ std::vector InputSelector::selectSimple(int64_t } } - // not enough - return {}; + // If we couldn't find a combination of inputs to cover estimated transaction fee and the target amount, + // return the whole set of UTXOs. Later, the transaction fee will be calculated more accurately, + // and these UTXOs can be enough. + return selected; } template diff --git a/src/Bitcoin/InputSelector.h b/src/Bitcoin/InputSelector.h index e2941e812d1..848dae390d4 100644 --- a/src/Bitcoin/InputSelector.h +++ b/src/Bitcoin/InputSelector.h @@ -5,6 +5,7 @@ #pragma once #include "FeeCalculator.h" +#include "DustCalculator.h" #include #include @@ -17,16 +18,18 @@ class InputSelector { public: /// Selects unspent transactions to use given a target transaction value, using complete logic. /// - /// \returns the list of indices of selected inputs, or an empty list if there are insufficient - /// funds. + /// \returns the list of indices of selected inputs. May return the entire list of UTXOs + /// even if they aren't enough to cover `targetValue + fee`. + /// That's because `InputSelector` has a rough segwit fee estimation algorithm, and the UTXOs can actually be enough. std::vector select(uint64_t targetValue, uint64_t byteFee, uint64_t numOutputs = 2); /// Selects unspent transactions to use given a target transaction value; /// Simplified version suitable for large number of inputs /// - /// \returns the list of indices of selected inputs, or an empty list if there are insufficient - /// funds. + /// \returns the list of indices of selected inputs. May return the entire list of UTXOs + /// even if they aren't enough to cover `targetValue + fee`. + /// That's because `InputSelector` has a rough segwit fee estimation algorithm, and the UTXOs can actually be enough. std::vector selectSimple(int64_t targetValue, int64_t byteFee, int64_t numOutputs = 2); @@ -34,12 +37,20 @@ class InputSelector { /// Return indices. One output and no change is assumed. std::vector selectMaxAmount(int64_t byteFee) noexcept; - /// Construct, using provided feeCalculator (see getFeeCalculator()). + /// Construct, using provided feeCalculator (see getFeeCalculator()) and dustCalculator (see getDustCalculator()). explicit InputSelector(const std::vector& inputs, - const FeeCalculator& feeCalculator) noexcept - : _inputs(inputs), feeCalculator(feeCalculator) {} + const FeeCalculator& feeCalculator, + DustCalculatorShared dustCalculator) noexcept + : _inputs(inputs), + feeCalculator(feeCalculator), + dustCalculator(std::move(dustCalculator)) { + } + explicit InputSelector(const std::vector& inputs) noexcept - : InputSelector(inputs, getFeeCalculator(TWCoinTypeBitcoin)) {} + : _inputs(inputs), + feeCalculator(getFeeCalculator(TWCoinTypeBitcoin)), + dustCalculator(std::make_shared(TWCoinTypeBitcoin)) { + } /// Sum of input amounts static uint64_t sum(const std::vector& amounts) noexcept; @@ -53,6 +64,7 @@ class InputSelector { private: const std::vector _inputs; const FeeCalculator& feeCalculator; + const DustCalculatorShared dustCalculator; }; } // namespace TW::Bitcoin diff --git a/src/Bitcoin/SigningInput.cpp b/src/Bitcoin/SigningInput.cpp index bcdf4e50f8f..3367652dd4a 100644 --- a/src/Bitcoin/SigningInput.cpp +++ b/src/Bitcoin/SigningInput.cpp @@ -6,6 +6,10 @@ namespace TW::Bitcoin { +SigningInput::SigningInput() + : dustCalculator(std::make_shared(TWCoinTypeBitcoin)) { +} + SigningInput::SigningInput(const Proto::SigningInput& input) { hashType = static_cast(input.hash_type()); amount = input.amount(); @@ -37,6 +41,8 @@ SigningInput::SigningInput(const Proto::SigningInput& input) { extraOutputsAmount += output.amount(); extraOutputs.push_back(std::make_pair(output.to_address(), output.amount())); } + + dustCalculator = getDustCalculator(input); } } // namespace TW::Bitcoin diff --git a/src/Bitcoin/SigningInput.h b/src/Bitcoin/SigningInput.h index 7686df1d1bc..a2b40419323 100644 --- a/src/Bitcoin/SigningInput.h +++ b/src/Bitcoin/SigningInput.h @@ -5,6 +5,7 @@ #pragma once #include "Amount.h" +#include "DustCalculator.h" #include "Transaction.h" #include "UTXO.h" #include @@ -73,8 +74,10 @@ class SigningInput { // Total amount of the `extraOutputs`. Amount extraOutputsAmount = 0; + DustCalculatorShared dustCalculator; + public: - SigningInput() = default; + SigningInput(); SigningInput(const Proto::SigningInput& input); }; diff --git a/src/Bitcoin/TransactionBuilder.cpp b/src/Bitcoin/TransactionBuilder.cpp index b4f167d92d6..b343b2bd924 100644 --- a/src/Bitcoin/TransactionBuilder.cpp +++ b/src/Bitcoin/TransactionBuilder.cpp @@ -88,6 +88,7 @@ TransactionPlan TransactionBuilder::plan(const SigningInput& input) { bool maxAmount = input.useMaxAmount; Amount totalAmount = input.amount + input.extraOutputsAmount; + Amount dustThreshold = input.dustCalculator->dustAmount(input.byteFee); if (totalAmount == 0 && !maxAmount) { plan.error = Common::Proto::Error_zero_amount_requested; @@ -95,7 +96,7 @@ TransactionPlan TransactionBuilder::plan(const SigningInput& input) { plan.error = Common::Proto::Error_missing_input_utxos; } else { const auto& feeCalculator = getFeeCalculator(static_cast(input.coinType), input.disableDustFilter); - auto inputSelector = InputSelector(input.utxos, feeCalculator); + auto inputSelector = InputSelector(input.utxos, feeCalculator, input.dustCalculator); auto inputSum = InputSelector::sum(input.utxos); // select UTXOs @@ -111,6 +112,8 @@ TransactionPlan TransactionBuilder::plan(const SigningInput& input) { auto output_size = 2; UTXOs selectedInputs; if (!maxAmount) { + // Please note that there may not be a "change" output if the "change.amount" is less than "dust", + // but we use a max amount of transaction outputs to simplify the algorithm, so the fee can be slightly bigger in rare cases. output_size = 2 + extraOutputs; // output + change if (input.useMaxUtxo) { selectedInputs = inputSelector.selectMaxAmount(input.byteFee); @@ -145,10 +148,16 @@ TransactionPlan TransactionBuilder::plan(const SigningInput& input) { } else { plan.availableAmount = InputSelector::sum(plan.utxos); + // There can be less UTXOs after Dust filtering. + if (!maxAmount && totalAmount > plan.availableAmount) { + TransactionPlan errorPlan; + errorPlan.error = Common::Proto::Error_not_enough_utxos; + return errorPlan; + } + // Compute fee. // must preliminary set change so that there is a second output if (!maxAmount) { - assert(totalAmount <= plan.availableAmount); plan.amount = input.amount; plan.fee = 0; plan.change = plan.availableAmount - totalAmount; @@ -158,6 +167,16 @@ TransactionPlan TransactionBuilder::plan(const SigningInput& input) { plan.change = 0; } plan.fee = estimateSegwitFee(feeCalculator, plan, output_size, input); + + // `InputSelector` has a rough segwit fee estimation algorithm, + // so the fee could be increased or decreased (see `InputSelector::select`). + // We need to make sure if we have enough UTXOs to cover "requested amount + final fee". + if (!maxAmount && plan.availableAmount < plan.fee + plan.amount) { + TransactionPlan errorPlan; + errorPlan.error = Common::Proto::Error_not_enough_utxos; + return errorPlan; + } + // If fee is larger than availableAmount (can happen in special maxAmount case), we reduce it (and hope it will go through) plan.fee = std::min(plan.availableAmount, plan.fee); assert(plan.fee >= 0 && plan.fee <= plan.availableAmount); @@ -175,13 +194,28 @@ TransactionPlan TransactionBuilder::plan(const SigningInput& input) { // The total amount that will be spent. Amount totalSpendAmount = plan.amount + input.extraOutputsAmount + plan.fee; + // Make sure that the output amount is greater or at least equal to the dust threshold. + if (plan.amount < dustThreshold) { + TransactionPlan errorPlan; + errorPlan.error = maxAmount ? Common::Proto::Error_not_enough_utxos : Common::Proto::Error_dust_amount_requested; + return errorPlan; + } + // Make sure that we have enough available UTXOs to spend `fee`, `amount` and `extraOutputsAmount`. if (plan.availableAmount < totalSpendAmount) { - plan.amount = 0; - plan.error = Common::Proto::Error_not_enough_utxos; + TransactionPlan errorPlan; + errorPlan.error = Common::Proto::Error_not_enough_utxos; + return errorPlan; + } + + auto changeAmount = plan.availableAmount - totalSpendAmount; + // Compute change if it's not dust. + if (changeAmount >= dustThreshold) { + plan.change = changeAmount; } else { - // compute change - plan.change = plan.availableAmount - totalSpendAmount; + // Spend the change as tx fee if it's dust, otherwise the transaction won't be mined. + plan.change = 0; + plan.fee += changeAmount; } } } diff --git a/src/proto/Bitcoin.proto b/src/proto/Bitcoin.proto index 4345c7093cc..0add08247a7 100644 --- a/src/proto/Bitcoin.proto +++ b/src/proto/Bitcoin.proto @@ -164,6 +164,13 @@ message SigningInput { // If set, uses Bitcoin 2.0 Signing protocol. // As a result, `Bitcoin.Proto.SigningOutput.signing_result_v2` is set. BitcoinV2.Proto.SigningInput signing_v2 = 21; + + // One of the "Dust" amount policies. + // Later, we plan to add support for `DynamicDust` policy with a `min_relay_fee` amount. + oneof dust_policy { + // Use a constant "Dust" threshold. + int64 fixed_dust_threshold = 24; + } } // Describes a preliminary transaction plan. diff --git a/src/proto/Common.proto b/src/proto/Common.proto index 0a9944bff49..af13469d47f 100644 --- a/src/proto/Common.proto +++ b/src/proto/Common.proto @@ -69,4 +69,6 @@ enum SigningError { Error_invalid_requested_token_amount = 23; // Operation not supported for the chain. Error_not_supported = 24; + // Requested amount is too low (less dust). + Error_dust_amount_requested = 25; } diff --git a/swift/Tests/Blockchains/BitcoinTests.swift b/swift/Tests/Blockchains/BitcoinTests.swift index 40c9e894a7f..ac1b6dd1206 100644 --- a/swift/Tests/Blockchains/BitcoinTests.swift +++ b/swift/Tests/Blockchains/BitcoinTests.swift @@ -305,6 +305,9 @@ class BitcoinTransactionSignerTests: XCTestCase { $0.byteFee = 1 $0.toAddress = "1Bp9U1ogV3A14FMvKbRJms7ctyso4Z4Tcx" $0.changeAddress = "1FQc5LdgGHMHEN9nwkjmz6tWkxhPpxBvBU" + // Set the very low fixed Dust threshold just to fix the tests. + // Actually, the transaction in this test has change=79 that will lead to Dust error when broadcasting it. + $0.fixedDustThreshold = 79; } input.scripts["593128f9f90e38b706c18623151e37d2da05c229"] = Data(hexString: "2103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc71ac")! diff --git a/tests/chains/Bitcoin/InputSelectorTests.cpp b/tests/chains/Bitcoin/InputSelectorTests.cpp index 999b9d950a6..eef037af45a 100644 --- a/tests/chains/Bitcoin/InputSelectorTests.cpp +++ b/tests/chains/Bitcoin/InputSelectorTests.cpp @@ -125,7 +125,9 @@ TEST(BitcoinInputSelector, SelectOneInsufficientEqual) { auto selector = InputSelector(utxos); auto selected = selector.select(100'000, 1); - EXPECT_TRUE(verifySelectedUTXOs(selected, {})); + // `InputSelector` returns the entire list of UTXOs even if they are not enough. + // That's because `InputSelector` has a rough segwit fee estimation algorithm, and the UTXOs can actually be enough. + EXPECT_TRUE(verifySelectedUTXOs(selected, {100'000})); } TEST(BitcoinInputSelector, SelectOneInsufficientHigher) { @@ -134,14 +136,28 @@ TEST(BitcoinInputSelector, SelectOneInsufficientHigher) { auto selector = InputSelector(utxos); auto selected = selector.select(99'900, 1); - EXPECT_TRUE(verifySelectedUTXOs(selected, {})); + // `InputSelector` returns the entire list of UTXOs even if they are not enough. + // That's because `InputSelector` has a rough segwit fee estimation algorithm, and the UTXOs can actually be enough. + EXPECT_TRUE(verifySelectedUTXOs(selected, {100'000})); +} + +TEST(BitcoinInputSelector, SelectOneInsufficientHigherFilterDust) { + auto utxos = buildTestUTXOs({22, 100'000, 40}); + + auto selector = InputSelector(utxos); + auto selected = selector.select(99'900, 1); + + // `InputSelector` returns the entire list of UTXOs even if they are not enough. + // That's because `InputSelector` has a rough segwit fee estimation algorithm, and the UTXOs can actually be enough. + // However, the list of result UTXOs does not include dust inputs. + EXPECT_TRUE(verifySelectedUTXOs(selected, {100'000})); } TEST(BitcoinInputSelector, SelectOneFitsExactly) { auto utxos = buildTestUTXOs({100'000}); auto& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); - auto selector = InputSelector(utxos, feeCalculator); + auto selector = InputSelector(utxos); auto expectedFee = 174; auto selected = selector.select(100'000 - expectedFee, 1); @@ -150,10 +166,11 @@ TEST(BitcoinInputSelector, SelectOneFitsExactly) { EXPECT_EQ(feeCalculator.calculate(1, 2, 1), expectedFee); EXPECT_EQ(feeCalculator.calculate(1, 1, 1), 143); - // 1 sat more and does not fit any more + // 1 sat more and does not fit any more. + // However, `InputSelector` returns the entire list of UTXOs even if they are not enough. + // That's because `InputSelector` has a rough segwit fee estimation algorithm, and the UTXOs can actually be enough. selected = selector.select(100'000 - expectedFee + 1, 1); - - EXPECT_TRUE(verifySelectedUTXOs(selected, {})); + EXPECT_TRUE(verifySelectedUTXOs(selected, {100'000})); } TEST(BitcoinInputSelector, SelectOneFitsExactlyHighfee) { @@ -161,7 +178,7 @@ TEST(BitcoinInputSelector, SelectOneFitsExactlyHighfee) { const auto byteFee = 10; auto& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); - auto selector = InputSelector(utxos, feeCalculator); + auto selector = InputSelector(utxos); auto expectedFee = 1740; auto selected = selector.select(100'000 - expectedFee, byteFee); @@ -170,17 +187,18 @@ TEST(BitcoinInputSelector, SelectOneFitsExactlyHighfee) { EXPECT_EQ(feeCalculator.calculate(1, 2, byteFee), expectedFee); EXPECT_EQ(feeCalculator.calculate(1, 1, byteFee), 1430); - // 1 sat more and does not fit any more + // 1 sat more and does not fit any more. + // However, `InputSelector` returns the entire list of UTXOs even if they are not enough. + // That's because `InputSelector` has a rough segwit fee estimation algorithm, and the UTXOs can actually be enough. selected = selector.select(100'000 - expectedFee + 1, byteFee); - - EXPECT_TRUE(verifySelectedUTXOs(selected, {})); + EXPECT_TRUE(verifySelectedUTXOs(selected, {100'000})); } TEST(BitcoinInputSelector, SelectThreeNoDust) { auto utxos = buildTestUTXOs({100'000, 70'000, 75'000}); auto& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); - auto selector = InputSelector(utxos, feeCalculator); + auto selector = InputSelector(utxos); auto selected = selector.select(100'000 - 174 - 10, 1); // 100'000 would fit with dust; instead two UTXOs are selected not to leave dust @@ -274,7 +292,7 @@ TEST(BitcoinInputSelector, SelectTenThreeExact) { auto utxos = buildTestUTXOs({1'000, 2'000, 100'000, 3'000, 4'000, 5'000, 125'000, 6'000, 150'000, 7'000}); auto& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); - auto selector = InputSelector(utxos, feeCalculator); + auto selector = InputSelector(utxos); const auto dustLimit = 102; auto selected = selector.select(375'000 - 376 - dustLimit, 1); @@ -292,7 +310,7 @@ TEST(BitcoinInputSelector, SelectMaxAmountOne) { auto utxos = buildTestUTXOs({10189534}); auto& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); - auto selector = InputSelector(utxos, feeCalculator); + auto selector = InputSelector(utxos); auto selected = selector.selectMaxAmount(1); EXPECT_TRUE(verifySelectedUTXOs(selected, {10189534})); @@ -304,7 +322,7 @@ TEST(BitcoinInputSelector, SelectAllAvail) { auto utxos = buildTestUTXOs({10189534}); auto& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); - auto selector = InputSelector(utxos, feeCalculator); + auto selector = InputSelector(utxos); auto selected = selector.select(10189534 - 226, 1); EXPECT_TRUE(verifySelectedUTXOs(selected, {10189534})); @@ -316,7 +334,7 @@ TEST(BitcoinInputSelector, SelectMaxAmount5of5) { auto utxos = buildTestUTXOs({400, 500, 600, 800, 1000}); auto& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); - auto selector = InputSelector(utxos, feeCalculator); + auto selector = InputSelector(utxos); auto byteFee = 1; auto selected = selector.selectMaxAmount(byteFee); @@ -330,7 +348,7 @@ TEST(BitcoinInputSelector, SelectMaxAmount4of5) { auto utxos = buildTestUTXOs({400, 500, 600, 800, 1000}); auto& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); - auto selector = InputSelector(utxos, feeCalculator); + auto selector = InputSelector(utxos); auto byteFee = 4; auto selected = selector.selectMaxAmount(byteFee); @@ -344,7 +362,7 @@ TEST(BitcoinInputSelector, SelectMaxAmount1of5) { auto utxos = buildTestUTXOs({400, 500, 600, 800, 1000}); auto& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); - auto selector = InputSelector(utxos, feeCalculator); + auto selector = InputSelector(utxos); auto byteFee = 8; auto selected = selector.selectMaxAmount(byteFee); @@ -358,7 +376,7 @@ TEST(BitcoinInputSelector, SelectMaxAmountNone) { auto utxos = buildTestUTXOs({400, 500, 600, 800, 1000}); auto& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); - auto selector = InputSelector(utxos, feeCalculator); + auto selector = InputSelector(utxos); auto byteFee = 10; auto selected = selector.selectMaxAmount(byteFee); @@ -370,8 +388,7 @@ TEST(BitcoinInputSelector, SelectMaxAmountNone) { TEST(BitcoinInputSelector, SelectMaxAmountNoUTXOs) { auto utxos = buildTestUTXOs({}); - auto& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); - auto selector = InputSelector(utxos, feeCalculator); + auto selector = InputSelector(utxos); auto selected = selector.selectMaxAmount(1); EXPECT_TRUE(verifySelectedUTXOs(selected, {})); @@ -380,7 +397,7 @@ TEST(BitcoinInputSelector, SelectMaxAmountNoUTXOs) { TEST(BitcoinInputSelector, SelectZcashUnspents) { auto utxos = buildTestUTXOs({100000, 2592, 73774}); - auto selector = InputSelector(utxos, getFeeCalculator(TWCoinTypeZcash)); + auto selector = InputSelector(utxos, getFeeCalculator(TWCoinTypeZcash), std::make_shared(TWCoinTypeZcash)); auto selected = selector.select(10000, 1); EXPECT_TRUE(verifySelectedUTXOs(selected, {73774})); @@ -389,7 +406,7 @@ TEST(BitcoinInputSelector, SelectZcashUnspents) { TEST(BitcoinInputSelector, SelectGroestlUnspents) { auto utxos = buildTestUTXOs({499971976}); - auto selector = InputSelector(utxos, getFeeCalculator(TWCoinTypeZcash)); + auto selector = InputSelector(utxos, getFeeCalculator(TWCoinTypeZcash), std::make_shared(TWCoinTypeZcash)); auto selected = selector.select(499951976, 1, 1); EXPECT_TRUE(verifySelectedUTXOs(selected, {499971976})); @@ -398,7 +415,7 @@ TEST(BitcoinInputSelector, SelectGroestlUnspents) { TEST(BitcoinInputSelector, SelectZcashMaxAmount) { auto utxos = buildTestUTXOs({100000, 2592, 73774}); - auto selector = InputSelector(utxos, getFeeCalculator(TWCoinTypeZcash)); + auto selector = InputSelector(utxos, getFeeCalculator(TWCoinTypeZcash), std::make_shared(TWCoinTypeZcash)); auto selected = selector.selectMaxAmount(1); EXPECT_TRUE(verifySelectedUTXOs(selected, {100000, 2592, 73774})); @@ -407,10 +424,12 @@ TEST(BitcoinInputSelector, SelectZcashMaxAmount) { TEST(BitcoinInputSelector, SelectZcashMaxUnspents2) { auto utxos = buildTestUTXOs({100000, 2592, 73774}); - auto selector = InputSelector(utxos, getFeeCalculator(TWCoinTypeZcash)); + auto selector = InputSelector(utxos, getFeeCalculator(TWCoinTypeZcash), std::make_shared(TWCoinTypeZcash)); auto selected = selector.select(176366 - 6, 1); - EXPECT_TRUE(verifySelectedUTXOs(selected, {})); + // `InputSelector` returns the entire list of UTXOs even if they are not enough. + // That's because `InputSelector` has a rough segwit fee estimation algorithm, and the UTXOs can actually be enough. + EXPECT_TRUE(verifySelectedUTXOs(selected, {2592, 73774, 100000})); } TEST(BitcoinInputSelector, ManyUtxos_900) { diff --git a/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp b/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp index 2306de4eb31..d3e64144553 100644 --- a/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp +++ b/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp @@ -32,8 +32,6 @@ namespace TW::Bitcoin { -constexpr uint64_t ONE_BTC = 100'000'000; - // clang-format off SigningInput buildInputP2PKH(bool omitKey = false) { auto hash0 = parse_hex("fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f"); @@ -85,6 +83,68 @@ SigningInput buildInputP2PKH(bool omitKey = false) { return input; } +TEST(BitcoinSigning, SpendMinimumAmountP2WPKH) { + auto myPrivateKey = PrivateKey(parse_hex("9ea2172511ed73ae0096be8e593c3b75631700edaf729f1abbae607314a20e35")); + + auto myPublicKey = myPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + auto utxoPubkeyHash = Hash::ripemd(Hash::sha256(myPublicKey.bytes)); + auto redeemScript = Script::buildPayToWitnessPublicKeyHash(utxoPubkeyHash); + + // Both two UTXOs came from the same transaction. + auto utxoHash = parse_hex("e8b3c2d0d5851cef139d87dfb5794db8897ce90ce1b6961526f61923baf5b5a3"); + std::reverse(utxoHash.begin(), utxoHash.end()); + + auto segwitDustAmount = 294; + + // Setup input + SigningInput input; + input.hashType = hashTypeForCoin(TWCoinTypeBitcoin); + input.amount = 546; + input.useMaxAmount = false; + input.useMaxUtxo = true; + input.byteFee = 27; + input.toAddress = "bc1qvrt7ukvhvmdny0a3j9k8l8jasx92lrqm30t2u2"; + input.changeAddress = "bc1qvrt7ukvhvmdny0a3j9k8l8jasx92lrqm30t2u2"; + input.coinType = TWCoinTypeBitcoin; + input.dustCalculator = std::make_shared(segwitDustAmount); + + input.privateKeys.push_back(myPrivateKey); + input.scripts[hex(utxoPubkeyHash)] = redeemScript; + + UTXO utxo0; + utxo0.script = redeemScript; + utxo0.amount = segwitDustAmount; + utxo0.outPoint = OutPoint(utxoHash, 0, UINT32_MAX); + input.utxos.push_back(utxo0); + + UTXO utxo1; + utxo1.script = redeemScript; + utxo1.amount = 16776; + utxo1.outPoint = OutPoint(utxoHash, 1, UINT32_MAX); + input.utxos.push_back(utxo1); + + { + // test plan (but do not reuse plan result) + auto plan = TransactionBuilder::plan(input); + EXPECT_TRUE(verifyPlan(plan, {294, 16776}, 546, 5643)); + EXPECT_EQ(plan.change, 10881); + } + + // Signs + auto result = TransactionSigner::sign(input); + + ASSERT_TRUE(result) << std::to_string(result.error()); + const auto signedTx = result.payload(); + + Data serialized; + signedTx.encode(serialized); + + EXPECT_EQ( + hex(serialized), + "01000000000102a3b5f5ba2319f6261596b6e10ce97c89b84d79b5df879d13ef1c85d5d0c2b3e80000000000ffffffffa3b5f5ba2319f6261596b6e10ce97c89b84d79b5df879d13ef1c85d5d0c2b3e80100000000ffffffff02220200000000000016001460d7ee599766db323fb1916c7f9e5d818aaf8c1b812a00000000000016001460d7ee599766db323fb1916c7f9e5d818aaf8c1b02483045022100d7e4d267e94547bd365736229219a85b21f79cf896a65baa444e339215b4b36f022078c0dee3d1d603f77855fee8f23291fe180b50afaa2c9ae9f724b7418d76da75012103a11506993946e20ea82686b157bf08f944759f43d91af8d84650ee73a482431c02483045022100c10cdbe21cedab3b4e7db9422f69c7074764711d552a63545104d71c905b138802204999f3ecb5fdadfd8669a8c14f04643c59bb3e98aaf52c52f829a0f6ef5d6abb012103a11506993946e20ea82686b157bf08f944759f43d91af8d84650ee73a482431c00000000" + ); +} + TEST(BitcoinSigning, ExtraOutputs) { auto privateKey = parse_hex("e253373989199da27c48680e3a3fc0f648d50f9a727ef17a7fe6a4dc3b159129"); auto ownAddress = "1MhdctqCwYMn2DT4mshpwvYtfF98wBojXS"; @@ -202,6 +262,9 @@ TEST(BitcoinSigning, ExtraOutputsRequireExtraInputs) { signingInput.set_to_address(toAddress); signingInput.set_change_address(ownAddress); signingInput.add_private_key(privateKey.data(), privateKey.size()); + // Dust threshold will be 612 (102 * 6) if otherwise is not set. + // So to fix the test, we should set the 313 dust threshold for the change output to be included. + signingInput.set_fixed_dust_threshold(313); auto utxoScript = Script::lockScriptForAddress(ownAddress, TWCoinTypeBitcoin); auto& utxo0 = *signingInput.add_utxo(); @@ -626,6 +689,202 @@ TEST(BitcoinSigning, SignNftInscriptionReveal) { ASSERT_EQ(result.substr(292, result.size() - 292), expectedHex.substr(292, result.size() - 292)); } +TEST(BitcoinSigning, SignPlanTransactionWithDustAmount) { + const auto privateKey = parse_hex("4646464646464646464646464646464646464646464646464646464646464646"); + const auto ownAddress = "bc1qhkfq3zahaqkkzx5mjnamwjsfpq2jk7z00ppggv"; + + const auto revUtxoHash0 = + parse_hex("07c42b969286be06fae38528c85f0a1ce508d4df837eb5ac4cf5f2a7a9d65fa8"); + const auto utxoScript0 = parse_hex("0014bd92088bb7e82d611a9b94fbb74a0908152b784f"); + + const auto dustAmount = 546; + // Use an amount less than dust. + const auto sendAmount = 545; + const auto availableAmount = 10'189'534; + + Proto::SigningInput signingInput; + signingInput.set_coin_type(TWCoinTypeBitcoin); + signingInput.set_hash_type(TWBitcoinSigHashTypeAll); + signingInput.set_amount(sendAmount); + signingInput.set_byte_fee(1); + signingInput.set_to_address("bc1q2dsdlq3343vk29runkgv4yc292hmq53jedfjmp"); + signingInput.set_change_address(ownAddress); + signingInput.set_fixed_dust_threshold(dustAmount); + + *signingInput.add_private_key() = std::string(privateKey.begin(), privateKey.end()); + + // Add UTXO + auto utxo = signingInput.add_utxo(); + utxo->set_script(utxoScript0.data(), utxoScript0.size()); + utxo->set_amount(availableAmount); + utxo->mutable_out_point()->set_hash( + std::string(revUtxoHash0.begin(), revUtxoHash0.end())); + utxo->mutable_out_point()->set_index(0); + utxo->mutable_out_point()->set_sequence(UINT32_MAX); + + Proto::TransactionPlan plan; + ANY_PLAN(signingInput, plan, TWCoinTypeBitcoin); + EXPECT_EQ(plan.error(), Common::Proto::Error_dust_amount_requested); + + // `AnySigner.sign` should return the same error. + Proto::SigningOutput output; + ANY_SIGN(signingInput, TWCoinTypeBitcoin); + EXPECT_EQ(output.error(), Common::Proto::Error_dust_amount_requested); +} + +// If the change amount is less than "dust", there should not be a change output. +TEST(BitcoinSigning, SignPlanTransactionNoChange) { + const auto myPrivateKey = PrivateKey(parse_hex("9ea2172511ed73ae0096be8e593c3b75631700edaf729f1abbae607314a20e35")); + auto myPublicKey = myPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + auto utxoPubkeyHash = Hash::ripemd(Hash::sha256(myPublicKey.bytes)); + auto redeemScript = Script::buildPayToWitnessPublicKeyHash(utxoPubkeyHash); + + const auto ownAddress = "bc1qvrt7ukvhvmdny0a3j9k8l8jasx92lrqm30t2u2"; + + auto utxoHash0 = + parse_hex("b33082a5fad105c1d9712e8d503971fe4d84713065bd323fd1019636ed940e8d"); + std::reverse(utxoHash0.begin(), utxoHash0.end()); + auto utxoAmount0 = 30269; + auto utxoOutputIndex0 = 1; + + auto utxoHash1 = + parse_hex("1f62c18bfc5f8293a2b7b061587c427bf830fb224289f9a806e6ad48de6a4c7d"); + std::reverse(utxoHash1.begin(), utxoHash1.end()); + auto utxoAmount1 = 4863; + auto utxoOutputIndex1 = 1; + + auto utxoHash2 = + parse_hex("71c3343dfca5f1914e1bfc04153517d73650cb9c931e8511d24d1f5290120f6f"); + std::reverse(utxoHash2.begin(), utxoHash2.end()); + // This UTXO will be filtered out as less than dust threshold. + auto utxoAmount2 = 300; + auto utxoOutputIndex2 = 0; + + const auto dustAmount = 546; + // Change amount is too low (less than dust), so we just waste it as the transaction fee. + const auto dustChange = 200; + const auto sendAmount = 28235 - dustChange; + + Proto::SigningInput signingInput; + signingInput.set_coin_type(TWCoinTypeBitcoin); + signingInput.set_hash_type(TWBitcoinSigHashTypeAll); + signingInput.set_byte_fee(33); + signingInput.set_amount(sendAmount); + signingInput.set_to_address("bc1q2dsdlq3343vk29runkgv4yc292hmq53jedfjmp"); + signingInput.set_change_address(ownAddress); + signingInput.set_fixed_dust_threshold(dustAmount); + + signingInput.add_private_key(myPrivateKey.bytes.data(), myPrivateKey.bytes.size()); + + // Add UTXO 0 + auto utxo0 = signingInput.add_utxo(); + utxo0->set_script(redeemScript.bytes.data(), redeemScript.bytes.size()); + utxo0->set_amount(utxoAmount0); + utxo0->mutable_out_point()->set_hash( + std::string(utxoHash0.begin(), utxoHash0.end())); + utxo0->mutable_out_point()->set_index(utxoOutputIndex0); + utxo0->mutable_out_point()->set_sequence(UINT32_MAX); + + // Add UTXO 1 + auto utxo1 = signingInput.add_utxo(); + utxo1->set_script(redeemScript.bytes.data(), redeemScript.bytes.size()); + utxo1->set_amount(utxoAmount1); + utxo1->mutable_out_point()->set_hash( + std::string(utxoHash1.begin(), utxoHash1.end())); + utxo1->mutable_out_point()->set_index(utxoOutputIndex1); + utxo1->mutable_out_point()->set_sequence(UINT32_MAX); + + // Add UTXO 2 + auto utxo2 = signingInput.add_utxo(); + utxo2->set_script(redeemScript.bytes.data(), redeemScript.bytes.size()); + utxo2->set_amount(utxoAmount2); + utxo2->mutable_out_point()->set_hash( + std::string(utxoHash2.begin(), utxoHash2.end())); + utxo2->mutable_out_point()->set_index(utxoOutputIndex2); + utxo2->mutable_out_point()->set_sequence(UINT32_MAX); + + Proto::TransactionPlan plan; + ANY_PLAN(signingInput, plan, TWCoinTypeBitcoin); + EXPECT_EQ(plan.error(), Common::Proto::OK); + + auto fee = 6897 + dustChange; + // UTXO-2 with 300 satoshis should be filtered out as less than dust. + EXPECT_TRUE(verifyPlan(plan, {4863, 30269}, sendAmount, fee)); + + Proto::SigningOutput output; + ANY_SIGN(signingInput, TWCoinTypeBitcoin); + EXPECT_EQ(output.error(), Common::Proto::OK); + // Successfully broadcasted: https://mempool.space/tx/5d6bf53576a54be4d92cd8abf58d28ecc9ea7956eaf970d24d6bfcb9fcfe9855 + EXPECT_EQ(hex(output.encoded()), "010000000001027d4c6ade48ade606a8f9894222fb30f87b427c5861b0b7a293825ffc8bc1621f0100000000ffffffff8d0e94ed369601d13f32bd653071844dfe7139508d2e71d9c105d1faa58230b30100000000ffffffff01836d0000000000001600145360df8231ac5965147c9d90ca930a2aafb0523202483045022100f95f9ac5d39f4b47dcd8c86daaaeac86374258d9960f922333ba0d5fdaa15b7e0220761794672dc9fbd71398d608f72f5d21a0f6c1306c6b700ad0d82f747c221062012103a11506993946e20ea82686b157bf08f944759f43d91af8d84650ee73a482431c02483045022100eb6ba0dcc64af61b2186b7efdab1ff03784d585ee03437f9a53875e93429db080220015a268d308436d3564b83ceaed90bc7272ca164016298ea855d1936568002a7012103a11506993946e20ea82686b157bf08f944759f43d91af8d84650ee73a482431c00000000"); +} + +// Not enough funds to send requested amount after UTXO dust filtering. +TEST(BitcoinSigning, SignPlanTransactionNotSufficientAfterDustFiltering) { + const auto myPrivateKey = PrivateKey(parse_hex("9ea2172511ed73ae0096be8e593c3b75631700edaf729f1abbae607314a20e35")); + auto myPublicKey = myPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + auto utxoPubkeyHash = Hash::ripemd(Hash::sha256(myPublicKey.bytes)); + auto redeemScript = Script::buildPayToWitnessPublicKeyHash(utxoPubkeyHash); + + const auto ownAddress = "bc1qvrt7ukvhvmdny0a3j9k8l8jasx92lrqm30t2u2"; + + auto utxoHash0 = + parse_hex("b33082a5fad105c1d9712e8d503971fe4d84713065bd323fd1019636ed940e8d"); + std::reverse(utxoHash0.begin(), utxoHash0.end()); + auto utxoAmount0 = 30269; + auto utxoOutputIndex0 = 1; + + auto utxoHash1 = + parse_hex("1f62c18bfc5f8293a2b7b061587c427bf830fb224289f9a806e6ad48de6a4c7d"); + std::reverse(utxoHash1.begin(), utxoHash1.end()); + auto utxoAmount1 = 545; + auto utxoOutputIndex1 = 1; + + const auto utxoScript0 = parse_hex("0014bd92088bb7e82d611a9b94fbb74a0908152b784f"); + + const auto dustAmount = 546; + const auto sendAmount = 25620; + + Proto::SigningInput signingInput; + signingInput.set_coin_type(TWCoinTypeBitcoin); + signingInput.set_hash_type(TWBitcoinSigHashTypeAll); + signingInput.set_byte_fee(33); + signingInput.set_amount(sendAmount); + signingInput.set_to_address("bc1q2dsdlq3343vk29runkgv4yc292hmq53jedfjmp"); + signingInput.set_change_address(ownAddress); + signingInput.set_fixed_dust_threshold(dustAmount); + + signingInput.add_private_key(myPrivateKey.bytes.data(), myPrivateKey.bytes.size()); + + // Add UTXO 0 + auto utxo0 = signingInput.add_utxo(); + utxo0->set_script(redeemScript.bytes.data(), redeemScript.bytes.size()); + utxo0->set_amount(utxoAmount0); + utxo0->mutable_out_point()->set_hash( + std::string(utxoHash0.begin(), utxoHash0.end())); + utxo0->mutable_out_point()->set_index(utxoOutputIndex0); + utxo0->mutable_out_point()->set_sequence(UINT32_MAX); + + // Add UTXO 1 + auto utxo1 = signingInput.add_utxo(); + utxo1->set_script(redeemScript.bytes.data(), redeemScript.bytes.size()); + utxo1->set_amount(utxoAmount1); + utxo1->mutable_out_point()->set_hash( + std::string(utxoHash1.begin(), utxoHash1.end())); + utxo1->mutable_out_point()->set_index(utxoOutputIndex1); + utxo1->mutable_out_point()->set_sequence(UINT32_MAX); + + // sum() + + Proto::TransactionPlan plan; + ANY_PLAN(signingInput, plan, TWCoinTypeBitcoin); + EXPECT_EQ(plan.error(), Common::Proto::Error_not_enough_utxos); + + // `AnySigner.sign` should return the same error. + Proto::SigningOutput output; + ANY_SIGN(signingInput, TWCoinTypeBitcoin); + EXPECT_EQ(output.error(), Common::Proto::Error_not_enough_utxos); +} + TEST(BitcoinSigning, SignP2PKH) { auto input = buildInputP2PKH(); @@ -1025,6 +1284,9 @@ SigningInput buildInputP2WSH(enum TWBitcoinSigHashType hashType, bool omitScript input.byteFee = 1; input.toAddress = "1Bp9U1ogV3A14FMvKbRJms7ctyso4Z4Tcx"; input.changeAddress = "1FQc5LdgGHMHEN9nwkjmz6tWkxhPpxBvBU"; + // Set the very low fixed Dust threshold just to fix the tests. + // Actually, transactions in these tests have change=79 and change=52 that will lead to Dust error when broadcasting it. + input.dustCalculator = std::make_shared(50); if (!omitKeys) { auto utxoKey0 = PrivateKey(parse_hex("ed00a0841cd53aedf89b0c616742d1d2a930f8ae2b0fb514765a17bb62c7521a")); diff --git a/tests/chains/Bitcoin/TransactionPlanTests.cpp b/tests/chains/Bitcoin/TransactionPlanTests.cpp index 7953fdd8ef4..5a4d6597f74 100644 --- a/tests/chains/Bitcoin/TransactionPlanTests.cpp +++ b/tests/chains/Bitcoin/TransactionPlanTests.cpp @@ -59,17 +59,29 @@ TEST(TransactionPlan, OneInsufficientLower100) { EXPECT_TRUE(verifyPlan(txPlan, {}, 0, 0, Common::Proto::Error_not_enough_utxos)); } -TEST(TransactionPlan, OneInsufficientLower170) { +TEST(TransactionPlan, OneInsufficient146) { // requested is only slightly lower than avail, not enough for fee, cannot be satisfied auto utxos = buildTestUTXOs({100'000}); - auto sigingInput = buildSigningInput(100'000 - 170, 1, utxos); + auto sigingInput = buildSigningInput(100'000 - 146, 1, utxos); auto txPlan = TransactionBuilder::plan(sigingInput); EXPECT_TRUE(verifyPlan(txPlan, {}, 0, 0, Common::Proto::Error_not_enough_utxos)); } -TEST(TransactionPlan, OneInsufficientLower300) { +TEST(TransactionPlan, OneSufficientLower170) { + // requested is only slightly lower than avail, not enough for fee, cannot be satisfied + auto utxos = buildTestUTXOs({100'000}); + auto sigingInput = buildSigningInput(100'000 - 170, 1, utxos); + + auto txPlan = TransactionBuilder::plan(sigingInput); + + auto dustChange = 23; + auto actualFee = 147 + dustChange; + EXPECT_TRUE(verifyPlan(txPlan, {100'000}, 100'000 - 170, actualFee)); +} + +TEST(TransactionPlan, OneSufficientLower300) { auto utxos = buildTestUTXOs({100'000}); auto sigingInput = buildSigningInput(100'000 - 300, 1, utxos); @@ -92,7 +104,9 @@ TEST(TransactionPlan, OneMoreRequested) { TEST(TransactionPlan, OneFitsExactly) { auto utxos = buildTestUTXOs({100'000}); auto byteFee = 1; - auto expectedFee = 147; + auto dustChange = 27; + // Change amount is too low (less than dust), so we just waste it as the transaction fee. + auto expectedFee = 147 + dustChange; auto sigingInput = buildSigningInput(100'000 - 174, byteFee, utxos); auto txPlan = TransactionBuilder::plan(sigingInput); @@ -106,7 +120,9 @@ TEST(TransactionPlan, OneFitsExactly) { TEST(TransactionPlan, OneFitsExactlyHighFee) { auto utxos = buildTestUTXOs({100'000}); auto byteFee = 10; - auto expectedFee = 1470; + auto dustChange = 270; + // Change amount is too low (less than dust), so we just waste it as the transaction fee. + auto expectedFee = 1470 + dustChange; auto sigingInput = buildSigningInput(100'000 - 1740, byteFee, utxos); auto txPlan = TransactionBuilder::plan(sigingInput); @@ -239,6 +255,26 @@ TEST(TransactionPlan, SelectionSuboptimal_ExtraSmallUtxo) { EXPECT_EQ(firstUtxo, 500); } +TEST(TransactionPlan, SelectionSuboptimal_ExtraSmallUtxoFixedDust) { + // Solution found 4-in-2-out {500, 600, 800, 1000} avail 2900 txamount 1390 fee 702 change 628 + // Better solution: 3-in-2-out {600, 800, 1000} avail 2400 txamount 1390 fee 566 change 444 + // Previously, with with higher fee estimation used in UTXO selection, solution found was 5-in-2-out {400, 500, 600, 800, 1000} avail 3300 txamount 1390 fee 838 change 1072 + auto utxos = buildTestUTXOs({400, 500, 600, 800, 1'000}); + auto byteFee = 2; + auto signingInput = buildSigningInput(1'390, byteFee, utxos); + signingInput.dustCalculator = std::make_shared(450); + + // UTXOs smaller than singleInputFee are not included + auto txPlan = TransactionBuilder::plan(signingInput); + + auto expectedFee = 702; + EXPECT_TRUE(verifyPlan(txPlan, {500, 600, 800, 1'000}, 1'390, expectedFee)); + auto change = 2'900 - 1'390 - expectedFee; + auto firstUtxo = txPlan.utxos[0].amount; + EXPECT_EQ(change, 808); + EXPECT_EQ(firstUtxo, 500); +} + TEST(TransactionPlan, Selection_Satisfied5) { // 5-input case, with a 5-input solution. // Previously, with with higher fee estimation used in UTXO selection, no solution would be found. @@ -484,15 +520,15 @@ TEST(TransactionPlan, MaxAmountDustAllFee10) { } TEST(TransactionPlan, One_MaxAmount_FeeMoreThanAvailable) { - auto utxos = buildTestUTXOs({170}); + auto utxos = buildTestUTXOs({340}); auto byteFee = 1; auto expectedFee = 113; - auto sigingInput = buildSigningInput(300, byteFee, utxos, true); + auto sigingInput = buildSigningInput(340, byteFee, utxos, true); auto txPlan = TransactionBuilder::plan(sigingInput); // Fee is reduced to availableAmount - EXPECT_TRUE(verifyPlan(txPlan, {170}, 170 - expectedFee, expectedFee)); + EXPECT_TRUE(verifyPlan(txPlan, {340}, 340 - expectedFee, expectedFee)); auto& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); EXPECT_EQ(feeCalculator.calculate(1, 1, byteFee), 143); diff --git a/tests/chains/Bitcoin/TxComparisonHelper.cpp b/tests/chains/Bitcoin/TxComparisonHelper.cpp index c003744bd13..f674bcaaff7 100644 --- a/tests/chains/Bitcoin/TxComparisonHelper.cpp +++ b/tests/chains/Bitcoin/TxComparisonHelper.cpp @@ -45,6 +45,7 @@ SigningInput buildSigningInput(Amount amount, int byteFee, const UTXOs& utxos, b input.byteFee = byteFee; input.useMaxAmount = useMaxAmount; input.coinType = coin; + input.dustCalculator = std::make_shared(coin); if (!omitPrivateKey) { auto utxoKey = PrivateKey(parse_hex("619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb9")); From c0cf03fe36c0b3116b7913bcb199cf3017f966b1 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Tue, 5 Mar 2024 13:37:38 +0100 Subject: [PATCH 056/128] [Solana]: Move Solana blockchain to Rust (#3691) * [Solana]: Add `MessageRaw` to `SigningInput` * Replace `Pubkey` with `SolanaAddress` * [Solana]: Add instruction builders * [Solana]: WIP Signer * [Solana]: Move instruction builders to dedicated modules * [Bitcoin]: Fix the order of compiled account keys * [Solana]: Fix delegate staking * [Solana]: Add deactivate staking transaction * [Solana]: Add deactivate all stake transaction * [Solana]: Add withdraw stake transaction * [Solana]: Add withdraw all stake transaction * [Solana]: Add create token account transaction * [Solana]: Add TokenTransfer transaction * Refactor `InstructionBuilder` to allow easily append references, add memo and nonce instructions * [Solana]: Minor changes * [Solana]: Add CreateAndTransferToken transaction * Add support for fee payer * [Solana]: Add CreateNonceAccount and WithdrawNonceAccount transactions * [Solana]: Add AdvanceNonceAccount transaction * [Solana]: Add RawMessage transaction * [Solana]: Add support for preimage hashing and compiling * [Solana]: Replace C++ implementation * [Solana]: Add more compile tests * [Solana]: Add tw_solana_address_default_token_address * [Solana]: Add "WithDurableNonce" tests * [Solana]: Remove C++ implementation * Identify a bug in `Address::findProgramAddress` function * [CI] Trigger CI * [Solana]: Remove extra C++ tests * [Solana]: Fix clippy warnings * [Solana]: Fix `tw_solana_address_default_token_address` * Replace C++'s implementation with `tw_solana_address_default_token_address` * [Solana]: Remove fix Boost path at CMakeLists.txt * [Solana]: Add `TransactionDecoder` module (#3698) * [Solana]: Add external signatures to the `RawMessage` * [Solana]: Add `ProtoBuilder` to convert `VersionedTransaction` to a `Proto::RawMessage` * [Solana]: Add `TransactionDecoder` module * [Solana]: Fix signature overriding, add more tests * [Solana]: Refactor tests * [Solana]: Add and implement `TWTransactionDecoderDecode` FFI in C++ * [Solana]: Add Kotlin test * [Solana]: Remove fix Boost path at CMakeLists.txt * [Solana]: Add Swift test * [WalletConnect/Solana]: Add support for WalletConnect signing requests (#3705) * [Solana]: Add SolanaWalletConnector * [Solana]: Add pubkey signatures to the `Proto::SigningOutput` * [Solana]: Add `TWSolanaWalletConnect` test * [Solana]: Add Kotlin, Swift tests * [Solana]: Fix C++ test * [Solana]: Fix iOS test * [Solana]: Add support for priority fee (#3710) * [Solana]: Add priority fee price and limit * [Solana]: Add priority fee tests * [CI]: Split linux-ci-rust.yml into two jobs * Move `new-blockchain-rust` step into codegen-v2.yml * [CI]: Fix linux-ci-rust.yml --- .github/workflows/codegen-v2.yml | 42 +- .github/workflows/linux-ci-rust.yml | 45 +- .../solana/TestSolanaTransaction.kt | 42 + .../solana/TestSolanaWalletConnectSigning.kt | 52 ++ .../rust/templates/blockchain_crate/entry.rs | 2 + .../TrustWalletCore/TWTransactionDecoder.h | 25 + rust/Cargo.lock | 112 ++- rust/chains/tw_binance/src/entry.rs | 2 + rust/chains/tw_cosmos/src/entry.rs | 4 + rust/chains/tw_greenfield/src/entry.rs | 2 + rust/chains/tw_native_evmos/src/entry.rs | 4 + rust/chains/tw_native_injective/src/entry.rs | 4 + rust/chains/tw_solana/Cargo.toml | 6 +- rust/chains/tw_solana/src/address.rs | 123 ++- rust/chains/tw_solana/src/blockhash.rs | 40 + rust/chains/tw_solana/src/compiler.rs | 84 +- .../chains/tw_solana/src/defined_addresses.rs | 27 + rust/chains/tw_solana/src/entry.rs | 19 +- rust/chains/tw_solana/src/instruction.rs | 102 +++ rust/chains/tw_solana/src/lib.rs | 4 + .../src/modules/compiled_instructions.rs | 90 ++ .../tw_solana/src/modules/compiled_keys.rs | 120 +++ .../compute_budget_instruction.rs | 43 + .../src/modules/instruction_builder/mod.rs | 80 ++ .../instruction_builder/stake_instruction.rs | 402 ++++++++ .../instruction_builder/system_instruction.rs | 337 +++++++ .../instruction_builder/token_instruction.rs | 110 +++ .../tw_solana/src/modules/message_builder.rs | 689 ++++++++++++++ rust/chains/tw_solana/src/modules/mod.rs | 13 + .../tw_solana/src/modules/proto_builder.rs | 98 ++ .../src/modules/transaction_decoder.rs | 38 + .../chains/tw_solana/src/modules/tx_signer.rs | 53 +- rust/chains/tw_solana/src/modules/utils.rs | 22 +- .../src/modules/wallet_connect/connector.rs | 62 ++ .../src/modules/wallet_connect/mod.rs | 6 + .../src/modules/wallet_connect/request.rs | 14 + rust/chains/tw_solana/src/program/mod.rs | 5 + .../tw_solana/src/program/stake_program.rs | 42 + rust/chains/tw_solana/src/signer.rs | 38 +- .../tw_solana/src/transaction/legacy.rs | 14 +- rust/chains/tw_solana/src/transaction/mod.rs | 80 +- rust/chains/tw_solana/src/transaction/v0.rs | 7 +- .../tw_solana/src/transaction/versioned.rs | 49 +- .../tests/get_default_token_address.rs | 50 + rust/chains/tw_thorchain/src/entry.rs | 4 + rust/tw_any_coin/src/ffi/mod.rs | 1 + .../src/ffi/tw_transaction_decoder.rs | 29 + rust/tw_any_coin/src/lib.rs | 1 + rust/tw_any_coin/src/test_utils/mod.rs | 1 + .../test_utils/transaction_decode_utils.rs | 31 + rust/tw_any_coin/src/transaction_decoder.rs | 19 + rust/tw_any_coin/tests/chains/solana/mod.rs | 2 + .../tests/chains/solana/solana_compile.rs | 342 ++++++- .../tests/chains/solana/solana_sign.rs | 860 +++++++++++++++++- .../tests/chains/solana/solana_transaction.rs | 337 +++++++ .../chains/solana/solana_wallet_connect.rs | 109 +++ rust/tw_aptos/src/entry.rs | 2 + rust/tw_bitcoin/src/entry.rs | 2 + rust/tw_coin_entry/src/coin_entry.rs | 12 + rust/tw_coin_entry/src/coin_entry_ext.rs | 13 + rust/tw_coin_entry/src/derivation.rs | 2 + rust/tw_coin_entry/src/error.rs | 1 + rust/tw_coin_entry/src/modules/mod.rs | 1 + .../src/modules/transaction_decoder.rs | 24 + rust/tw_encoding/src/base58.rs | 26 + rust/tw_encoding/src/base64.rs | 16 +- rust/tw_ethereum/src/entry.rs | 2 + rust/tw_hash/src/hash_array.rs | 2 +- rust/tw_internet_computer/src/entry.rs | 10 +- rust/tw_keypair/src/ed25519/signature.rs | 2 +- rust/tw_ronin/src/entry.rs | 2 + rust/wallet_core_rs/Cargo.toml | 3 +- rust/wallet_core_rs/src/ffi/solana/address.rs | 45 + rust/wallet_core_rs/src/ffi/solana/mod.rs | 2 + rust/wallet_core_rs/tests/solana_address.rs | 26 + .../tests/solana_transaction.rs | 2 +- src/Solana/AccountMeta.h | 18 - src/Solana/Address.cpp | 13 - src/Solana/AddressLookupTable.h | 18 - src/Solana/CompiledInstruction.cpp | 20 - src/Solana/CompiledInstruction.h | 37 - src/Solana/Constants.h | 26 - src/Solana/Encoding.h | 29 - src/Solana/Entry.cpp | 53 +- src/Solana/Entry.h | 14 +- src/Solana/Instruction.h | 182 ---- src/Solana/LegacyMessage.cpp | 101 -- src/Solana/LegacyMessage.h | 337 ------- src/Solana/MessageHeader.h | 20 - src/Solana/Program.cpp | 93 -- src/Solana/Program.h | 36 - src/Solana/Signer.cpp | 530 ----------- src/Solana/Signer.h | 40 - src/Solana/Transaction.cpp | 40 - src/Solana/Transaction.h | 43 - src/Solana/V0Message.h | 17 - src/Solana/VersionedMessage.cpp | 65 -- src/Solana/VersionedMessage.h | 19 - src/Solana/VersionedTransaction.cpp | 34 - src/Solana/VersionedTransaction.h | 43 - src/interface/TWSolanaAddress.cpp | 12 +- src/interface/TWTransactionDecoder.cpp | 18 + src/proto/Solana.proto | 106 ++- src/proto/WalletConnect.proto | 4 + src/rust/Wrapper.h | 14 + swift/Tests/Blockchains/SolanaTests.swift | 64 ++ tests/chains/Solana/AddressTests.cpp | 11 - tests/chains/Solana/ProgramTests.cpp | 96 -- tests/chains/Solana/SignerTests.cpp | 656 ------------- tests/chains/Solana/TWAnySignerTests.cpp | 558 ------------ tests/chains/Solana/TWSolanaAddressTests.cpp | 11 + tests/chains/Solana/TWSolanaTransaction.cpp | 55 +- tests/chains/Solana/TWWalletConnectSolana.cpp | 52 ++ .../Solana/TransactionCompilerTests.cpp | 463 ---------- tests/chains/Solana/TransactionTests.cpp | 607 ------------ .../interface/TWTransactionCompilerTests.cpp | 1 - 116 files changed, 5311 insertions(+), 4379 deletions(-) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/solana/TestSolanaWalletConnectSigning.kt create mode 100644 include/TrustWalletCore/TWTransactionDecoder.h create mode 100644 rust/chains/tw_solana/src/blockhash.rs create mode 100644 rust/chains/tw_solana/src/defined_addresses.rs create mode 100644 rust/chains/tw_solana/src/instruction.rs create mode 100644 rust/chains/tw_solana/src/modules/compiled_instructions.rs create mode 100644 rust/chains/tw_solana/src/modules/compiled_keys.rs create mode 100644 rust/chains/tw_solana/src/modules/instruction_builder/compute_budget_instruction.rs create mode 100644 rust/chains/tw_solana/src/modules/instruction_builder/mod.rs create mode 100644 rust/chains/tw_solana/src/modules/instruction_builder/stake_instruction.rs create mode 100644 rust/chains/tw_solana/src/modules/instruction_builder/system_instruction.rs create mode 100644 rust/chains/tw_solana/src/modules/instruction_builder/token_instruction.rs create mode 100644 rust/chains/tw_solana/src/modules/message_builder.rs create mode 100644 rust/chains/tw_solana/src/modules/proto_builder.rs create mode 100644 rust/chains/tw_solana/src/modules/transaction_decoder.rs create mode 100644 rust/chains/tw_solana/src/modules/wallet_connect/connector.rs create mode 100644 rust/chains/tw_solana/src/modules/wallet_connect/mod.rs create mode 100644 rust/chains/tw_solana/src/modules/wallet_connect/request.rs create mode 100644 rust/chains/tw_solana/src/program/mod.rs create mode 100644 rust/chains/tw_solana/src/program/stake_program.rs create mode 100644 rust/chains/tw_solana/tests/get_default_token_address.rs create mode 100644 rust/tw_any_coin/src/ffi/tw_transaction_decoder.rs create mode 100644 rust/tw_any_coin/src/test_utils/transaction_decode_utils.rs create mode 100644 rust/tw_any_coin/src/transaction_decoder.rs create mode 100644 rust/tw_any_coin/tests/chains/solana/solana_transaction.rs create mode 100644 rust/tw_any_coin/tests/chains/solana/solana_wallet_connect.rs create mode 100644 rust/tw_coin_entry/src/modules/transaction_decoder.rs create mode 100644 rust/wallet_core_rs/src/ffi/solana/address.rs create mode 100644 rust/wallet_core_rs/tests/solana_address.rs delete mode 100644 src/Solana/AccountMeta.h delete mode 100644 src/Solana/AddressLookupTable.h delete mode 100644 src/Solana/CompiledInstruction.cpp delete mode 100644 src/Solana/CompiledInstruction.h delete mode 100644 src/Solana/Constants.h delete mode 100644 src/Solana/Encoding.h delete mode 100644 src/Solana/Instruction.h delete mode 100644 src/Solana/LegacyMessage.cpp delete mode 100644 src/Solana/LegacyMessage.h delete mode 100644 src/Solana/MessageHeader.h delete mode 100644 src/Solana/Program.cpp delete mode 100644 src/Solana/Program.h delete mode 100644 src/Solana/Signer.cpp delete mode 100644 src/Solana/Signer.h delete mode 100644 src/Solana/Transaction.cpp delete mode 100644 src/Solana/Transaction.h delete mode 100644 src/Solana/V0Message.h delete mode 100644 src/Solana/VersionedMessage.cpp delete mode 100644 src/Solana/VersionedMessage.h delete mode 100644 src/Solana/VersionedTransaction.cpp delete mode 100644 src/Solana/VersionedTransaction.h create mode 100644 src/interface/TWTransactionDecoder.cpp delete mode 100644 tests/chains/Solana/ProgramTests.cpp delete mode 100644 tests/chains/Solana/SignerTests.cpp create mode 100644 tests/chains/Solana/TWWalletConnectSolana.cpp delete mode 100644 tests/chains/Solana/TransactionTests.cpp diff --git a/.github/workflows/codegen-v2.yml b/.github/workflows/codegen-v2.yml index f8ced68b019..aaa4ca76447 100644 --- a/.github/workflows/codegen-v2.yml +++ b/.github/workflows/codegen-v2.yml @@ -10,16 +10,42 @@ on: paths: - 'codegen-v2/**' +env: + SCCACHE_GHA_ENABLED: "true" + RUSTC_WRAPPER: "sccache" + jobs: test: runs-on: ubuntu-latest if: github.event.pull_request.draft == false steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - - uses: actions-rs/cargo@v1 - with: - command: test - args: --manifest-path codegen-v2/Cargo.toml + - uses: actions/checkout@v3 + - name: Install system dependencies + run: | + tools/install-sys-dependencies-linux + + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.3 + + - name: Install Rust dependencies + run: | + tools/install-rust-dependencies + + - name: Run codegen-v2 tests + run: | + cargo test --all + working-directory: codegen-v2 + + # Generate files for a blockchain. + # Please note the blockchain should not be implemented in Rust at the moment of running this step, + # otherwise consider either generating files for another blockchain or removing this step at all. + - name: Test codegen-v2 new-blockchain-rust + run: | + cargo run -- new-blockchain-rust iotex + working-directory: codegen-v2 + + # Check if `new-blockchain-rust` command has generated files that do not break project compilation. + - name: Check Rust compiles + run: | + cargo check --tests + working-directory: rust diff --git a/.github/workflows/linux-ci-rust.yml b/.github/workflows/linux-ci-rust.yml index e0dcee23cfd..02e1b7955b3 100644 --- a/.github/workflows/linux-ci-rust.yml +++ b/.github/workflows/linux-ci-rust.yml @@ -15,7 +15,8 @@ concurrency: cancel-in-progress: true jobs: - build: + # Check formatting, clippy warnings, run tests and check code coverage. + build-and-test: permissions: contents: read checks: write @@ -33,6 +34,7 @@ jobs: - name: Cache Rust uses: Swatinem/rust-cache@v2 with: + key: "build-and-test" workspaces: | rust @@ -40,9 +42,6 @@ jobs: run: | tools/install-rust-dependencies dev - - name: Install emsdk - run: tools/install-wasm-dependencies - - name: Check code formatting run: | cargo fmt --check @@ -58,9 +57,6 @@ jobs: cargo llvm-cov nextest --profile ci --no-fail-fast --lcov --output-path coverage.info working-directory: rust - - name: Run tests in WASM - run: tools/rust-test wasm - - name: Rust Test Report uses: dorny/test-reporter@v1 if: success() || failure() @@ -75,17 +71,32 @@ jobs: run: | tools/check-coverage rust/coverage.stats rust/coverage.info - # Generate files for a blockchain in the end of the pipeline. - # Please note the blockchain should not be implemented in Rust at the moment of running this step, - # otherwise consider either generate files for another blockchain or remove this step at all. - - name: Test codegen-v2 new-blockchain-rust + # Run Rust tests in WASM. + test-wasm: + runs-on: ubuntu-latest + if: github.event.pull_request.draft == false + steps: + - uses: actions/checkout@v3 + - name: Install system dependencies run: | - cargo run -- new-blockchain-rust iotex - working-directory: codegen-v2 + tools/install-sys-dependencies-linux + + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.3 + + - name: Cache Rust + uses: Swatinem/rust-cache@v2 + with: + key: "test-wasm" + workspaces: | + rust - # Check if `new-blockchain-rust` command has generated files that do not break project compilation. - - name: Check Rust compiles + - name: Install Rust dependencies run: | - cargo check --tests - working-directory: rust + tools/install-rust-dependencies + - name: Install emsdk + run: tools/install-wasm-dependencies + + - name: Run tests in WASM + run: tools/rust-test wasm diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/solana/TestSolanaTransaction.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/solana/TestSolanaTransaction.kt index 5f26065966f..c2f8a3d461c 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/solana/TestSolanaTransaction.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/solana/TestSolanaTransaction.kt @@ -7,11 +7,16 @@ import org.junit.Assert.assertEquals import org.junit.Test import wallet.core.jni.Base58 import wallet.core.java.AnySigner +import wallet.core.jni.Base64 +import wallet.core.jni.CoinType import wallet.core.jni.CoinType.SOLANA import wallet.core.jni.SolanaTransaction import wallet.core.jni.DataVector +import wallet.core.jni.TransactionDecoder import wallet.core.jni.proto.Common.SigningError import wallet.core.jni.proto.Solana +import wallet.core.jni.proto.Solana.DecodingTransactionOutput +import wallet.core.jni.proto.Solana.SigningInput import wallet.core.jni.proto.Solana.SigningOutput class TestSolanaTransaction { @@ -39,4 +44,41 @@ class TestSolanaTransaction { val expectedString = "Ajzc/Tke0CG8Cew5qFa6xZI/7Ya3DN0M8Ige6tKPsGzhg8Bw9DqL18KUrEZZ1F4YqZBo4Rv+FsDT8A7Nss7p4A6BNVZzzGprCJqYQeNg0EVIbmPc6mDitNniHXGeKgPZ6QZbM4FElw9O7IOFTpOBPvQFeqy0vZf/aayncL8EK/UEAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqbHiki6ThNH3auuyZPQpJntnN0mA//56nMpK/6HIuu8xAQUEAgQDAQoMoA8AAAAAAAAG" assertEquals(output.encoded, expectedString) } + + @Test + fun testDecodeUpdateBlockhashAndSign() { + // https://explorer.solana.com/tx/3KbvREZUat76wgWMtnJfWbJL74Vzh4U2eabVJa3Z3bb2fPtW8AREP5pbmRwUrxZCESbTomWpL41PeKDcPGbojsej?cluster=devnet + val encodedTx = Base64.decode("AnQTYwZpkm3fs4SdLxnV6gQj3hSLsyacpxDdLMALYWObm722f79IfYFTbZeFK9xHtMumiDOWAM2hHQP4r/GtbARpncaXgOVFv7OgbRLMbuCEJHO1qwcdCbtH72VzyzU8yw9sqqHIAaCUE8xaQTgT6Z5IyZfeyMe2QGJIfOjz65UPAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8Aqe6sdLXiXSDILEtzckCjkjchiSf6zVGpMYiAE5BE2IqHAQUEAgQDAQoMoA8AAAAAAAAG") + val newBlockhash = "CyPYVsYWrsJNfVpi8aazu7WsrswNFuDd385z6GNoBGUg" + + val senderPrivateKeyData = "7f0932159226ddec9e1a4b0b8fe7cdc135049f9e549a867d722aa720dd64f32e".toHexByteArray() + val feePayerPrivateKeyData = "4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7".toHexByteArray() + + // Step 1: Decode the transaction. + + val decodedData = TransactionDecoder.decode(SOLANA, encodedTx) + val decodedOutput = DecodingTransactionOutput.parseFrom(decodedData) + + assertEquals(decodedOutput.error, SigningError.OK) + assert(decodedOutput.transaction.hasLegacy()) + + // Step 2: Update recent blockhash. + + val rawTx = decodedOutput.transaction.toBuilder().apply { + legacy = decodedOutput.transaction.legacy.toBuilder().setRecentBlockhash(newBlockhash).build() + }.build() + + // Step 3: Re-sign the updated transaction. + + val signingInput = SigningInput.newBuilder().apply { + rawMessage = rawTx + privateKey = ByteString.copyFrom(senderPrivateKeyData) + feePayerPrivateKey = ByteString.copyFrom(feePayerPrivateKeyData) + txEncoding = Solana.Encoding.Base64 + }.build() + + val output = AnySigner.sign(signingInput, SOLANA, SigningOutput.parser()) + val expectedString = "Ajzc/Tke0CG8Cew5qFa6xZI/7Ya3DN0M8Ige6tKPsGzhg8Bw9DqL18KUrEZZ1F4YqZBo4Rv+FsDT8A7Nss7p4A6BNVZzzGprCJqYQeNg0EVIbmPc6mDitNniHXGeKgPZ6QZbM4FElw9O7IOFTpOBPvQFeqy0vZf/aayncL8EK/UEAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqbHiki6ThNH3auuyZPQpJntnN0mA//56nMpK/6HIuu8xAQUEAgQDAQoMoA8AAAAAAAAG" + assertEquals(output.encoded, expectedString) + } } \ No newline at end of file diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/solana/TestSolanaWalletConnectSigning.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/solana/TestSolanaWalletConnectSigning.kt new file mode 100644 index 00000000000..51360e7b5af --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/solana/TestSolanaWalletConnectSigning.kt @@ -0,0 +1,52 @@ +package com.trustwallet.core.app.blockchains.solana + +import com.google.protobuf.ByteString +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.java.AnySigner +import wallet.core.jni.Base58 +import wallet.core.jni.CoinType.SOLANA +import wallet.core.jni.WalletConnectRequest +import wallet.core.jni.proto.Common.SigningError +import wallet.core.jni.proto.Solana.Encoding +import wallet.core.jni.proto.Solana.SigningOutput +import wallet.core.jni.proto.WalletConnect + +class TestSolanaWalletConnectSigning { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testSignSolanaTransactionFromWalletConnectRequest() { + // Step 1: Parse a signing request received through WalletConnect. + + val parsingInput = WalletConnect.ParseRequestInput.newBuilder().apply { + method = WalletConnect.Method.SolanaSignTransaction + payload = "{\"transaction\":\"AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEDZsL1CMnFVcrMn7JtiOiN1U4hC7WovOVof2DX51xM0H/GizyJTHgrBanCf8bGbrFNTn0x3pCGq30hKbywSTr6AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgIAAQwCAAAAKgAAAAAAAAA=\"}" + }.build() + + val parsingOutputBytes = WalletConnectRequest.parse(SOLANA, parsingInput.toByteArray()) + val parsingOutput = WalletConnect.ParseRequestOutput.parseFrom(parsingOutputBytes) + + assertEquals(parsingOutput.error, SigningError.OK) + + // Step 2: Set missing fields. + + val signingInput = parsingOutput.solana.toBuilder().apply { + privateKey = ByteString.copyFrom(Base58.decodeNoCheck("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr")) + txEncoding = Encoding.Base64 + }.build() + + // Step 3: Sign the transaction. + + val output = AnySigner.sign(signingInput, SOLANA, SigningOutput.parser()) + + assertEquals(output.error, SigningError.OK) + assertEquals(output.encoded, "AQPWaOi7dMdmQpXi8HyQQKwiqIftrg1igGQxGtZeT50ksn4wAnyH4DtDrkkuE0fqgx80LTp4LwNN9a440SrmoA8BAAEDZsL1CMnFVcrMn7JtiOiN1U4hC7WovOVof2DX51xM0H/GizyJTHgrBanCf8bGbrFNTn0x3pCGq30hKbywSTr6AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgIAAQwCAAAAKgAAAAAAAAA=") + + assertEquals(output.getSignatures(0).pubkey, "7v91N7iZ9mNicL8WfG6cgSCKyRXydQjLh6UYBWwm6y1Q") + assertEquals(output.getSignatures(0).signature, "5T6uZBHnHFd8uWErDBTFRVkbKuhbcm94K5MJ2beTYDruzqv4FjS7EMKvC94ZfxNAiWUXZ6bZxS3WXUbhJwYNPWn") + } +} \ No newline at end of file diff --git a/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs b/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs index 643e7b053a4..0cb80268185 100644 --- a/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs +++ b/codegen-v2/src/codegen/rust/templates/blockchain_crate/entry.rs @@ -13,6 +13,7 @@ use tw_coin_entry::error::AddressResult; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_coin_entry::prefix::NoPrefix; use tw_keypair::tw::PublicKey; @@ -33,6 +34,7 @@ impl CoinEntry for {BLOCKCHAIN}Entry { type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; type WalletConnector = NoWalletConnector; + type TransactionDecoder = NoTransactionDecoder; #[inline] fn parse_address( diff --git a/include/TrustWalletCore/TWTransactionDecoder.h b/include/TrustWalletCore/TWTransactionDecoder.h new file mode 100644 index 00000000000..2d1a22cffe4 --- /dev/null +++ b/include/TrustWalletCore/TWTransactionDecoder.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#pragma once + +#include "TWBase.h" +#include "TWCoinType.h" +#include "TWData.h" +#include "TWString.h" + +TW_EXTERN_C_BEGIN + +TW_EXPORT_STRUCT +struct TWTransactionDecoder; + +/// Decodes a transaction from a binary representation. +/// +/// \param coin coin type. +/// \param encodedTx encoded transaction data. +/// \return serialized protobuf message specific for the given coin. +TW_EXPORT_STATIC_METHOD +TWData *_Nonnull TWTransactionDecoderDecode(enum TWCoinType coinType, TWData *_Nonnull encodedTx); + +TW_EXTERN_C_END diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 033fa1c04c2..fe167147af0 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -275,6 +275,30 @@ dependencies = [ "generic-array", ] +[[package]] +name = "borsh" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58b559fd6448c6e2fd0adb5720cd98a2506594cafa4737ff98c396f3e82f667" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aadb5b6ccbd078890f6d7003694e33816e6b784358f18e15e7e6d9f065a57cd" +dependencies = [ + "once_cell", + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.37", + "syn_derive", +] + [[package]] name = "bs58" version = "0.4.0" @@ -317,6 +341,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "ciborium" version = "0.2.1" @@ -951,9 +981,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" @@ -1007,7 +1037,7 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 1.0.107", @@ -1019,7 +1049,7 @@ version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 1.0.107", @@ -1098,7 +1128,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.11", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", ] [[package]] @@ -1568,6 +1630,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.37", +] + [[package]] name = "synstructure" version = "0.12.6" @@ -1626,9 +1700,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" [[package]] name = "toml_edit" @@ -1638,7 +1712,18 @@ checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7" dependencies = [ "indexmap", "toml_datetime", - "winnow", + "winnow 0.4.7", +] + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow 0.5.40", ] [[package]] @@ -2010,6 +2095,8 @@ name = "tw_solana" version = "0.1.0" dependencies = [ "bincode", + "borsh", + "lazy_static", "serde", "serde_json", "tw_coin_entry", @@ -2236,6 +2323,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "wyz" version = "0.2.0" diff --git a/rust/chains/tw_binance/src/entry.rs b/rust/chains/tw_binance/src/entry.rs index f2e979c3b1e..f7798bd0f39 100644 --- a/rust/chains/tw_binance/src/entry.rs +++ b/rust/chains/tw_binance/src/entry.rs @@ -15,6 +15,7 @@ use tw_coin_entry::error::AddressResult; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_keypair::tw::PublicKey; use tw_proto::Binance::Proto; use tw_proto::TxCompiler::Proto as CompilerProto; @@ -33,6 +34,7 @@ impl CoinEntry for BinanceEntry { type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; type WalletConnector = BinanceWalletConnector; + type TransactionDecoder = NoTransactionDecoder; #[inline] fn parse_address( diff --git a/rust/chains/tw_cosmos/src/entry.rs b/rust/chains/tw_cosmos/src/entry.rs index 53eb31c218c..1f8650a70cd 100644 --- a/rust/chains/tw_cosmos/src/entry.rs +++ b/rust/chains/tw_cosmos/src/entry.rs @@ -10,6 +10,7 @@ use tw_coin_entry::error::AddressResult; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_cosmos_sdk::address::{Address, Bech32Prefix}; use tw_cosmos_sdk::context::StandardCosmosContext; @@ -27,10 +28,13 @@ impl CoinEntry for CosmosEntry { type SigningInput<'a> = Proto::SigningInput<'a>; type SigningOutput = Proto::SigningOutput<'static>; type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + + // Optional modules: type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; type WalletConnector = NoWalletConnector; + type TransactionDecoder = NoTransactionDecoder; #[inline] fn parse_address( diff --git a/rust/chains/tw_greenfield/src/entry.rs b/rust/chains/tw_greenfield/src/entry.rs index a01e6146ae3..52ea07203f4 100644 --- a/rust/chains/tw_greenfield/src/entry.rs +++ b/rust/chains/tw_greenfield/src/entry.rs @@ -13,6 +13,7 @@ use tw_coin_entry::error::{AddressError, AddressResult}; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_coin_entry::prefix::NoPrefix; use tw_keypair::tw::PublicKey; @@ -33,6 +34,7 @@ impl CoinEntry for GreenfieldEntry { type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; type WalletConnector = NoWalletConnector; + type TransactionDecoder = NoTransactionDecoder; #[inline] fn parse_address( diff --git a/rust/chains/tw_native_evmos/src/entry.rs b/rust/chains/tw_native_evmos/src/entry.rs index 017daede1db..9586259983c 100644 --- a/rust/chains/tw_native_evmos/src/entry.rs +++ b/rust/chains/tw_native_evmos/src/entry.rs @@ -11,6 +11,7 @@ use tw_coin_entry::error::AddressResult; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_cosmos_sdk::address::{Address, Bech32Prefix}; use tw_cosmos_sdk::modules::compiler::tw_compiler::TWTransactionCompiler; @@ -27,10 +28,13 @@ impl CoinEntry for NativeEvmosEntry { type SigningInput<'a> = Proto::SigningInput<'a>; type SigningOutput = Proto::SigningOutput<'static>; type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + + // Optional modules: type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; type WalletConnector = NoWalletConnector; + type TransactionDecoder = NoTransactionDecoder; #[inline] fn parse_address( diff --git a/rust/chains/tw_native_injective/src/entry.rs b/rust/chains/tw_native_injective/src/entry.rs index a93d89c294e..e65ab7fa0a8 100644 --- a/rust/chains/tw_native_injective/src/entry.rs +++ b/rust/chains/tw_native_injective/src/entry.rs @@ -11,6 +11,7 @@ use tw_coin_entry::error::AddressResult; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_cosmos_sdk::address::{Address, Bech32Prefix}; use tw_cosmos_sdk::modules::compiler::tw_compiler::TWTransactionCompiler; @@ -27,10 +28,13 @@ impl CoinEntry for NativeInjectiveEntry { type SigningInput<'a> = Proto::SigningInput<'a>; type SigningOutput = Proto::SigningOutput<'static>; type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + + // Optional modules: type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; type WalletConnector = NoWalletConnector; + type TransactionDecoder = NoTransactionDecoder; #[inline] fn parse_address( diff --git a/rust/chains/tw_solana/Cargo.toml b/rust/chains/tw_solana/Cargo.toml index 1d3e0611345..99c893b081e 100644 --- a/rust/chains/tw_solana/Cargo.toml +++ b/rust/chains/tw_solana/Cargo.toml @@ -5,13 +5,13 @@ edition = "2021" [dependencies] bincode = "1.3.3" +borsh = { version = "1.3.1", features = ["derive"] } +lazy_static = "1.4.0" serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" tw_coin_entry = { path = "../../tw_coin_entry" } tw_encoding = { path = "../../tw_encoding" } tw_hash = { path = "../../tw_hash" } tw_keypair = { path = "../../tw_keypair" } tw_memory = { path = "../../tw_memory" } tw_proto = { path = "../../tw_proto" } - -[dev-dependencies] -serde_json = "1.0" diff --git a/rust/chains/tw_solana/src/address.rs b/rust/chains/tw_solana/src/address.rs index 57c7808892b..f15718964eb 100644 --- a/rust/chains/tw_solana/src/address.rs +++ b/rust/chains/tw_solana/src/address.rs @@ -10,11 +10,15 @@ use std::str::FromStr; use tw_coin_entry::coin_entry::CoinAddress; use tw_coin_entry::error::{AddressError, AddressResult}; use tw_encoding::base58; -use tw_hash::H256; -use tw_keypair::tw; +use tw_hash::{as_byte_sequence, sha2, H256}; +use tw_keypair::{ed25519, tw}; use tw_memory::Data; -#[derive(Clone, Copy)] +pub const MAX_SEEDS: usize = 16; +pub const MAX_SEED_LEN: usize = H256::LEN; +const PDA_MARKER: &[u8; 21] = b"ProgramDerivedAddress"; + +#[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct SolanaAddress { bytes: H256, } @@ -28,6 +32,12 @@ impl SolanaAddress { Ok(SolanaAddress { bytes }) } + pub fn with_public_key_ed25519(public_key: &ed25519::sha512::PublicKey) -> SolanaAddress { + SolanaAddress { + bytes: public_key.to_bytes(), + } + } + pub fn with_public_key_bytes(bytes: H256) -> SolanaAddress { SolanaAddress { bytes } } @@ -35,6 +45,87 @@ impl SolanaAddress { pub fn bytes(&self) -> H256 { self.bytes } + + /// Find a valid [program derived address][pda] and its corresponding bump seed. + /// + /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses + /// + /// Program derived addresses (PDAs) are account keys that only the program, + /// `program_id`, has the authority to sign. The address is of the same form + /// as a Solana `Pubkey`, except they are ensured to not be on the ed25519 + /// curve and thus have no associated private key. When performing + /// cross-program invocations the program can "sign" for the key by calling + /// [`invoke_signed`] and passing the same seeds used to generate the + /// address, along with the calculated _bump seed_, which this function + /// returns as the second tuple element. The runtime will verify that the + /// program associated with this address is the caller and thus authorized + /// to be the signer. + pub fn find_program_address( + seeds: &[&[u8]], + program_id: SolanaAddress, + ) -> Option { + let mut bump_seed = [u8::MAX]; + for _ in 0..u8::MAX { + let mut seeds_with_bump = seeds.to_vec(); + seeds_with_bump.push(&bump_seed); + match Self::create_program_address(&seeds_with_bump, program_id) { + Ok(Some(address)) => return Some(address), + // Try to re-compute the program address with a different seed. + Ok(None) => (), + Err(_) => return None, + } + // Try to re-compute the program address with a different seed. + bump_seed[0] -= 1; + } + None + } + + /// Create a valid [program derived address][pda] without searching for a bump seed. + /// + /// [pda]: https://solana.com/docs/core/cpi#program-derived-addresses + /// + /// Because this function does not create a bump seed, it may unpredictably + /// return an error for any given set of seeds and is not generally suitable + /// for creating program derived addresses. + /// + /// However, it can be used for efficiently verifying that a set of seeds plus + /// bump seed generated by [`find_program_address`] derives a particular + /// address as expected. See the example for details. + /// + /// See the documentation for [`find_program_address`] for a full description + /// of program derived addresses and bump seeds. + /// + /// [`find_program_address`]: Pubkey::find_program_address + pub fn create_program_address( + seeds: &[&[u8]], + program_id: SolanaAddress, + ) -> AddressResult> { + if seeds.len() > MAX_SEEDS { + return Err(AddressError::Internal); + } + if seeds.iter().any(|seed| seed.len() > MAX_SEED_LEN) { + return Err(AddressError::Internal); + } + + let mut data_to_hash = Vec::new(); + + // concatenate seeds + for seed in seeds { + data_to_hash.extend_from_slice(seed); + } + // Append `program_id`. + data_to_hash.extend_from_slice(program_id.bytes.as_slice()); + data_to_hash.extend_from_slice(PDA_MARKER); + + let hash = H256::try_from(sha2::sha256(&data_to_hash).as_slice()) + .expect("sha256 must return 32 bytes"); + + // The given hash (aka new public key) must not be on the ed25519 elliptic curve. + match ed25519::sha512::PublicKey::try_from(hash.as_slice()) { + Ok(_) => Ok(None), + Err(_) => Ok(Some(SolanaAddress::with_public_key_bytes(hash))), + } + } } impl CoinAddress for SolanaAddress { @@ -55,6 +146,18 @@ impl FromStr for SolanaAddress { } } +impl From<&'static str> for SolanaAddress { + fn from(s: &'static str) -> Self { + SolanaAddress::from_str(s).unwrap() + } +} + +impl fmt::Debug for SolanaAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self) + } +} + impl fmt::Display for SolanaAddress { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let encoded = base58::encode(self.bytes.as_slice(), &SOLANA_ALPHABET); @@ -67,7 +170,10 @@ impl Serialize for SolanaAddress { where S: Serializer, { - self.to_string().serialize(serializer) + if serializer.is_human_readable() { + return self.to_string().serialize(serializer); + } + as_byte_sequence::serialize(&self.bytes(), serializer) } } @@ -76,7 +182,12 @@ impl<'de> Deserialize<'de> for SolanaAddress { where D: Deserializer<'de>, { - let addr_str = String::deserialize(deserializer)?; - SolanaAddress::from_str(&addr_str).map_err(|e| DeError::custom(format!("{e:?}"))) + if deserializer.is_human_readable() { + let addr_str = String::deserialize(deserializer)?; + return SolanaAddress::from_str(&addr_str) + .map_err(|e| DeError::custom(format!("{e:?}"))); + } + let bytes = as_byte_sequence::deserialize(deserializer)?; + Ok(SolanaAddress { bytes }) } } diff --git a/rust/chains/tw_solana/src/blockhash.rs b/rust/chains/tw_solana/src/blockhash.rs new file mode 100644 index 00000000000..8c7844dae26 --- /dev/null +++ b/rust/chains/tw_solana/src/blockhash.rs @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::SOLANA_ALPHABET; +use serde::Deserialize; +use std::fmt; +use std::str::FromStr; +use tw_encoding::base58::{self, as_base58_bitcoin}; +use tw_encoding::EncodingError; +use tw_hash::H256; + +#[derive(Clone, Copy, Default, Deserialize)] +pub struct Blockhash(#[serde(with = "as_base58_bitcoin")] H256); + +impl Blockhash { + pub fn with_bytes(bytes: H256) -> Blockhash { + Blockhash(bytes) + } + + pub fn to_bytes(&self) -> H256 { + self.0 + } +} + +impl fmt::Display for Blockhash { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", base58::encode(self.0.as_slice(), &SOLANA_ALPHABET)) + } +} + +impl FromStr for Blockhash { + type Err = EncodingError; + + fn from_str(s: &str) -> Result { + let bytes = base58::decode(s, &SOLANA_ALPHABET)?; + let bytes = H256::try_from(bytes.as_slice()).map_err(|_| EncodingError::InvalidInput)?; + Ok(Blockhash(bytes)) + } +} diff --git a/rust/chains/tw_solana/src/compiler.rs b/rust/chains/tw_solana/src/compiler.rs index 5f2aabe50db..5b3c9f89df8 100644 --- a/rust/chains/tw_solana/src/compiler.rs +++ b/rust/chains/tw_solana/src/compiler.rs @@ -2,12 +2,21 @@ // // Copyright © 2017 Trust Wallet. +use crate::address::SolanaAddress; +use crate::modules::message_builder::MessageBuilder; +use crate::modules::proto_builder::ProtoBuilder; +use crate::modules::tx_signer::TxSigner; +use crate::SOLANA_ALPHABET; +use std::borrow::Cow; +use std::collections::HashMap; use tw_coin_entry::coin_context::CoinContext; use tw_coin_entry::coin_entry::{PublicKeyBytes, SignatureBytes}; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_coin_entry::signing_output_error; +use tw_encoding::{base58, base64}; +use tw_keypair::ed25519; +use tw_keypair::traits::VerifyingKeyTrait; use tw_proto::Solana::Proto; -use tw_proto::TxCompiler::Proto as CompilerProto; pub struct SolanaCompiler; @@ -16,16 +25,31 @@ impl SolanaCompiler { pub fn preimage_hashes( coin: &dyn CoinContext, input: Proto::SigningInput<'_>, - ) -> CompilerProto::PreSigningOutput<'static> { + ) -> Proto::PreSigningOutput<'static> { Self::preimage_hashes_impl(coin, input) - .unwrap_or_else(|e| signing_output_error!(CompilerProto::PreSigningOutput, e)) + .unwrap_or_else(|e| signing_output_error!(Proto::PreSigningOutput, e)) } fn preimage_hashes_impl( _coin: &dyn CoinContext, - _input: Proto::SigningInput<'_>, - ) -> SigningResult> { - todo!() + input: Proto::SigningInput<'_>, + ) -> SigningResult> { + let builder = MessageBuilder::new(input); + let signers = builder.signers()?; + let unsigned_msg = builder.build()?; + + let data_to_sign = TxSigner::preimage_versioned(&unsigned_msg)?; + + let signers: Vec<_> = signers + .iter() + .map(|addr| Cow::from(addr.to_string().into_bytes())) + .collect(); + + Ok(Proto::PreSigningOutput { + signers, + data: Cow::from(data_to_sign), + ..Proto::PreSigningOutput::default() + }) } #[inline] @@ -41,10 +65,48 @@ impl SolanaCompiler { fn compile_impl( _coin: &dyn CoinContext, - _input: Proto::SigningInput<'_>, - _signatures: Vec, - _public_keys: Vec, + input: Proto::SigningInput<'_>, + signatures: Vec, + public_keys: Vec, ) -> SigningResult> { - todo!() + let encode = move |data| match input.tx_encoding { + Proto::Encoding::Base58 => base58::encode(data, &SOLANA_ALPHABET), + Proto::Encoding::Base64 => base64::encode(data, false), + }; + + if signatures.len() != public_keys.len() { + return Err(SigningError(SigningErrorType::Error_signatures_count)); + } + + let builder = MessageBuilder::new(input); + let unsigned_msg = builder.build()?; + let data_to_sign = TxSigner::preimage_versioned(&unsigned_msg)?; + + // Verify the given signatures and collect the key-signature map. + let mut key_signs = HashMap::default(); + for (sign, pubkey) in signatures.iter().zip(public_keys.iter()) { + let signature = ed25519::Signature::try_from(sign.as_slice())?; + let pubkey = ed25519::sha512::PublicKey::try_from(pubkey.as_slice())?; + + if !pubkey.verify(signature.clone(), data_to_sign.clone()) { + return Err(SigningError(SigningErrorType::Error_signing)); + } + + key_signs.insert(SolanaAddress::with_public_key_ed25519(&pubkey), signature); + } + + let signed_tx = TxSigner::compile_versioned(unsigned_msg, key_signs)?; + + let signed_encoded = bincode::serialize(&signed_tx) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let signed_encoded = encode(&signed_encoded); + let unsigned_encoded = encode(&data_to_sign); + + Ok(Proto::SigningOutput { + encoded: Cow::from(signed_encoded), + unsigned_tx: Cow::from(unsigned_encoded), + signatures: ProtoBuilder::build_signatures(&signed_tx), + ..Proto::SigningOutput::default() + }) } } diff --git a/rust/chains/tw_solana/src/defined_addresses.rs b/rust/chains/tw_solana/src/defined_addresses.rs new file mode 100644 index 00000000000..4047605b435 --- /dev/null +++ b/rust/chains/tw_solana/src/defined_addresses.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::address::SolanaAddress; +use lazy_static::lazy_static; + +macro_rules! define { + ($name:ident = $s:literal) => { + lazy_static! { + pub static ref $name: SolanaAddress = SolanaAddress::from($s); + } + }; +} + +define!(SYSTEM_PROGRAM_ID_ADDRESS = "11111111111111111111111111111111"); +define!(STAKE_PROGRAM_ID_ADDRESS = "Stake11111111111111111111111111111111111111"); +define!(TOKEN_PROGRAM_ID_ADDRESS = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); +define!(ASSOCIATED_TOKEN_PROGRAM_ID_ADDRESS = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); +define!(SYSVAR_RENT_ID_ADDRESS = "SysvarRent111111111111111111111111111111111"); +define!(SYSVAR_CLOCK_ID_ADDRESS = "SysvarC1ock11111111111111111111111111111111"); +define!(STAKE_CONFIG_ID_ADDRESS = "StakeConfig11111111111111111111111111111111"); +define!(NULL_ID_ADDRESS = "11111111111111111111111111111111"); +define!(SYSVAR_STAKE_HISTORY_ID_ADDRESS = "SysvarStakeHistory1111111111111111111111111"); +define!(MEMO_PROGRAM_ID_ADDRESS = "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"); +define!(SYSVAR_RECENT_BLOCKHASHS_ADDRESS = "SysvarRecentB1ockHashes11111111111111111111"); +define!(COMPUTE_BUDGET_ADDRESS = "ComputeBudget111111111111111111111111111111"); diff --git a/rust/chains/tw_solana/src/entry.rs b/rust/chains/tw_solana/src/entry.rs index 9d9f0f7b49a..62356a87bf6 100644 --- a/rust/chains/tw_solana/src/entry.rs +++ b/rust/chains/tw_solana/src/entry.rs @@ -4,6 +4,8 @@ use crate::address::SolanaAddress; use crate::compiler::SolanaCompiler; +use crate::modules::transaction_decoder::SolanaTransactionDecoder; +use crate::modules::wallet_connect::connector::SolanaWalletConnector; use crate::signer::SolanaSigner; use std::str::FromStr; use tw_coin_entry::coin_context::CoinContext; @@ -13,11 +15,9 @@ use tw_coin_entry::error::AddressResult; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; -use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_coin_entry::prefix::NoPrefix; use tw_keypair::tw::PublicKey; use tw_proto::Solana::Proto; -use tw_proto::TxCompiler::Proto as CompilerProto; pub struct SolanaEntry; @@ -26,13 +26,14 @@ impl CoinEntry for SolanaEntry { type Address = SolanaAddress; type SigningInput<'a> = Proto::SigningInput<'a>; type SigningOutput = Proto::SigningOutput<'static>; - type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + type PreSigningOutput = Proto::PreSigningOutput<'static>; // Optional modules: type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; - type WalletConnector = NoWalletConnector; + type WalletConnector = SolanaWalletConnector; + type TransactionDecoder = SolanaTransactionDecoder; #[inline] fn parse_address( @@ -88,4 +89,14 @@ impl CoinEntry for SolanaEntry { ) -> Self::SigningOutput { SolanaCompiler::compile(coin, input, signatures, public_keys) } + + #[inline] + fn wallet_connector(&self) -> Option { + Some(SolanaWalletConnector) + } + + #[inline] + fn transaction_decoder(&self) -> Option { + Some(SolanaTransactionDecoder) + } } diff --git a/rust/chains/tw_solana/src/instruction.rs b/rust/chains/tw_solana/src/instruction.rs new file mode 100644 index 00000000000..fee1928163b --- /dev/null +++ b/rust/chains/tw_solana/src/instruction.rs @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::address::SolanaAddress; +use borsh::BorshSerialize; +use serde::{Deserialize, Serialize}; +use tw_memory::Data; + +pub struct Instruction { + /// Pubkey of the program that executes this instruction. + pub program_id: SolanaAddress, + /// Metadata describing accounts that should be passed to the program. + pub accounts: Vec, + /// Opaque data passed to the program for its own interpretation. + pub data: Data, +} + +impl Instruction { + /// Create a new instruction from a value, encoded with [`bincode`]. + /// + /// [`bincode`]: https://docs.rs/bincode/latest/bincode/ + /// + /// `program_id` is the address of the program that will execute the instruction. + /// `accounts` contains a description of all accounts that may be accessed by the program. + pub fn new_with_bincode( + program_id: SolanaAddress, + data: T, + accounts: Vec, + ) -> Self { + let data = bincode::serialize(&data).expect("Error serializing bincode"); + Self { + program_id, + accounts, + data, + } + } + + /// Create a new instruction from a value, encoded with [`borsh`]. + /// + /// [`borsh`]: https://docs.rs/borsh/latest/borsh/ + /// + /// `program_id` is the address of the program that will execute the instruction. + /// `accounts` contains a description of all accounts that may be accessed by the program. + pub fn new_with_borsh( + program_id: SolanaAddress, + data: &T, + accounts: Vec, + ) -> Self { + let data = borsh::to_vec(data).expect("Error serializing borsh"); + Self { + program_id, + accounts, + data, + } + } + + pub fn new(program_id: SolanaAddress, data: Data, accounts: Vec) -> Self { + Self { + program_id, + accounts, + data, + } + } + + pub fn with_references>(mut self, references: I) -> Self { + for reference in references { + self.accounts.push(AccountMeta::readonly(reference, false)); + } + self + } +} + +#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct AccountMeta { + /// An account's public key. + pub pubkey: SolanaAddress, + /// True if an `Instruction` requires a `Transaction` signature matching `pubkey`. + pub is_signer: bool, + /// True if the account data or metadata may be mutated during program execution. + pub is_writable: bool, +} + +impl AccountMeta { + /// Construct metadata for a writable account. + pub fn new(pubkey: SolanaAddress, is_signer: bool) -> Self { + Self { + pubkey, + is_signer, + is_writable: true, + } + } + + /// Construct metadata for a read-only account. + pub fn readonly(pubkey: SolanaAddress, is_signer: bool) -> Self { + Self { + pubkey, + is_signer, + is_writable: false, + } + } +} diff --git a/rust/chains/tw_solana/src/lib.rs b/rust/chains/tw_solana/src/lib.rs index 8283647ad9b..7e945d862f0 100644 --- a/rust/chains/tw_solana/src/lib.rs +++ b/rust/chains/tw_solana/src/lib.rs @@ -5,9 +5,13 @@ use tw_encoding::base58::Alphabet; pub mod address; +pub mod blockhash; pub mod compiler; +pub mod defined_addresses; pub mod entry; +pub mod instruction; pub mod modules; +pub mod program; pub mod signer; pub mod transaction; diff --git a/rust/chains/tw_solana/src/modules/compiled_instructions.rs b/rust/chains/tw_solana/src/modules/compiled_instructions.rs new file mode 100644 index 00000000000..92d16320a16 --- /dev/null +++ b/rust/chains/tw_solana/src/modules/compiled_instructions.rs @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::address::SolanaAddress; +use crate::instruction::Instruction; +use crate::transaction::CompiledInstruction; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; + +pub fn compile_instructions( + ixs: &[Instruction], + keys: &[SolanaAddress], +) -> SigningResult> { + ixs.iter().map(|ix| compile_instruction(ix, keys)).collect() +} + +fn position(keys: &[SolanaAddress], key: &SolanaAddress) -> SigningResult { + keys.iter() + .position(|k| k == key) + .map(|k| k as u8) + .ok_or(SigningError(SigningErrorType::Error_internal)) +} + +/// https://github.com/solana-labs/solana/blob/4b65cc8eef6ef79cb9b9cbc534a99b4900e58cf7/sdk/program/src/message/legacy.rs#L72-L84 +pub(crate) fn compile_instruction( + ix: &Instruction, + keys: &[SolanaAddress], +) -> SigningResult { + let accounts = ix + .accounts + .iter() + .map(|account_meta| position(keys, &account_meta.pubkey)) + .collect::>>()?; + + Ok(CompiledInstruction { + program_id_index: position(keys, &ix.program_id)?, + data: ix.data.clone(), + accounts, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::instruction::AccountMeta; + use crate::SOLANA_ALPHABET; + use std::str::FromStr; + use tw_encoding::base58; + use tw_keypair::ed25519; + + #[test] + fn test_compile_instruction() { + let public_0 = base58::decode( + "GymAh18wHuFTytfSJWi8eYTA9x5S3sNb9CJSGBWoPRE3", + &SOLANA_ALPHABET, + ) + .unwrap(); + let public_0 = ed25519::sha512::PublicKey::try_from(public_0.as_slice()).unwrap(); + let address_0 = SolanaAddress::with_public_key_ed25519(&public_0); + + let public_1 = base58::decode( + "2oKoYSAHgveX91917v4DUEuN8BNKXDg8KJWpaGyEay9V", + &SOLANA_ALPHABET, + ) + .unwrap(); + let public_1 = ed25519::sha512::PublicKey::try_from(public_1.as_slice()).unwrap(); + let address_1 = SolanaAddress::with_public_key_ed25519(&public_1); + + let program_id = SolanaAddress::from_str("11111111111111111111111111111111").unwrap(); + + let addresses = vec![address_0, address_1, program_id]; + let ixs_addresses = vec![ + AccountMeta::new(address_1, false), + AccountMeta::new(address_0, false), + AccountMeta::new(program_id, false), + AccountMeta::new(address_1, false), + AccountMeta::new(address_0, false), + ]; + let data = vec![0_u8, 1, 2, 4]; + let instruction = Instruction::new(program_id, data.clone(), ixs_addresses); + + let compiled_ix = compile_instruction(&instruction, &addresses).unwrap(); + let expected = CompiledInstruction { + program_id_index: 2, + accounts: vec![1, 0, 2, 1, 0], + data, + }; + assert_eq!(compiled_ix, expected); + } +} diff --git a/rust/chains/tw_solana/src/modules/compiled_keys.rs b/rust/chains/tw_solana/src/modules/compiled_keys.rs new file mode 100644 index 00000000000..3272f17b64b --- /dev/null +++ b/rust/chains/tw_solana/src/modules/compiled_keys.rs @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +//! Original source code: https://github.com/solana-labs/solana/blob/4b65cc8eef6ef79cb9b9cbc534a99b4900e58cf7/sdk/program/src/message/compiled_keys.rs + +use crate::address::SolanaAddress; +use crate::instruction::Instruction; +use crate::transaction::MessageHeader; +use std::collections::hash_map::Entry; +use std::collections::HashMap; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; + +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +struct CompiledKeyMeta { + is_signer: bool, + is_writable: bool, +} + +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub(crate) struct CompiledKeys { + ordered_keys: Vec, + key_meta_map: HashMap, +} + +impl CompiledKeys { + pub fn with_fee_payer(fee_payer: SolanaAddress) -> Self { + let mut selfi = Self::default(); + + selfi.key_meta_map.insert( + fee_payer, + CompiledKeyMeta { + is_signer: true, + is_writable: true, + }, + ); + // Fee payer must be the first account in the keys list. + selfi.ordered_keys.push(fee_payer); + + selfi + } + + pub fn compile(mut self, instructions: &[Instruction]) -> Self { + for ix in instructions { + for account_meta in &ix.accounts { + let meta_entry = self.key_meta_map.entry(account_meta.pubkey); + if matches!(meta_entry, Entry::Vacant(_)) { + self.ordered_keys.push(account_meta.pubkey); + } + + let meta = meta_entry.or_default(); + meta.is_signer |= account_meta.is_signer; + meta.is_writable |= account_meta.is_writable; + } + } + + // add programIds (read-only, at end) + for ix in instructions { + let meta_entry = self.key_meta_map.entry(ix.program_id); + if matches!(meta_entry, Entry::Vacant(_)) { + self.ordered_keys.push(ix.program_id); + } + meta_entry.or_default(); + } + + self + } + + pub fn try_into_message_components(self) -> SigningResult<(MessageHeader, Vec)> { + let try_into_u8 = |num: usize| -> SigningResult { + u8::try_from(num).map_err(|_| SigningError(SigningErrorType::Error_tx_too_big)) + }; + + let Self { + ordered_keys, + key_meta_map, + } = self; + + let filter = |account, is_signer: bool, is_writable: bool| -> Option { + let meta = key_meta_map.get(account).copied().unwrap_or_default(); + (meta.is_signer == is_signer && meta.is_writable == is_writable).then_some(*account) + }; + + let writable_signer_keys: Vec = ordered_keys + .iter() + .filter_map(|key| filter(key, true, true)) + .collect(); + let readonly_signer_keys: Vec = ordered_keys + .iter() + .filter_map(|key| filter(key, true, false)) + .collect(); + let writable_non_signer_keys: Vec = ordered_keys + .iter() + .filter_map(|key| filter(key, false, true)) + .collect(); + let readonly_non_signer_keys: Vec = ordered_keys + .iter() + .filter_map(|key| filter(key, false, false)) + .collect(); + + let signers_len = writable_signer_keys + .len() + .saturating_add(readonly_signer_keys.len()); + + let header = MessageHeader { + num_required_signatures: try_into_u8(signers_len)?, + num_readonly_signed_accounts: try_into_u8(readonly_signer_keys.len())?, + num_readonly_unsigned_accounts: try_into_u8(readonly_non_signer_keys.len())?, + }; + + let static_account_keys: Vec<_> = std::iter::empty() + .chain(writable_signer_keys) + .chain(readonly_signer_keys) + .chain(writable_non_signer_keys) + .chain(readonly_non_signer_keys) + .collect(); + + Ok((header, static_account_keys)) + } +} diff --git a/rust/chains/tw_solana/src/modules/instruction_builder/compute_budget_instruction.rs b/rust/chains/tw_solana/src/modules/instruction_builder/compute_budget_instruction.rs new file mode 100644 index 00000000000..a95218e4365 --- /dev/null +++ b/rust/chains/tw_solana/src/modules/instruction_builder/compute_budget_instruction.rs @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::defined_addresses::COMPUTE_BUDGET_ADDRESS; +use crate::instruction::Instruction; +use borsh::{BorshDeserialize, BorshSerialize}; + +pub type UnitLimit = u32; +pub type UnitPrice = u64; + +#[derive(BorshDeserialize, BorshSerialize)] +pub enum ComputeBudgetInstruction { + Unused, + RequestHeapFrame(u32), + SetComputeUnitLimit(UnitLimit), + SetComputeUnitPrice(UnitPrice), + SetLoadedAccountsDataSizeLimit(u32), +} + +pub struct ComputeBudgetInstructionBuilder; + +impl ComputeBudgetInstructionBuilder { + /// Create a `ComputeBudgetInstruction::SetComputeUnitLimit` `Instruction` + pub fn set_compute_unit_limit(units: UnitLimit) -> Instruction { + let account_metas = vec![]; + Instruction::new_with_borsh( + *COMPUTE_BUDGET_ADDRESS, + &ComputeBudgetInstruction::SetComputeUnitLimit(units), + account_metas, + ) + } + + /// Create a `ComputeBudgetInstruction::SetComputeUnitPrice` `Instruction` + pub fn set_compute_unit_price(micro_lamports: UnitPrice) -> Instruction { + let account_metas = vec![]; + Instruction::new_with_borsh( + *COMPUTE_BUDGET_ADDRESS, + &ComputeBudgetInstruction::SetComputeUnitPrice(micro_lamports), + account_metas, + ) + } +} diff --git a/rust/chains/tw_solana/src/modules/instruction_builder/mod.rs b/rust/chains/tw_solana/src/modules/instruction_builder/mod.rs new file mode 100644 index 00000000000..9a4da04d11d --- /dev/null +++ b/rust/chains/tw_solana/src/modules/instruction_builder/mod.rs @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::address::SolanaAddress; +use crate::instruction::Instruction; + +pub mod compute_budget_instruction; +pub mod stake_instruction; +pub mod system_instruction; +pub mod token_instruction; + +use compute_budget_instruction::ComputeBudgetInstructionBuilder; +use system_instruction::SystemInstructionBuilder; + +#[derive(Default)] +pub struct InstructionBuilder { + instructions: Vec, +} + +impl InstructionBuilder { + /// Adds an `advance_nonce` instruction if the nonce account provided. + pub fn maybe_advance_nonce( + &mut self, + nonce_account: Option, + authorized_account: SolanaAddress, + ) -> &mut Self { + if let Some(nonce_account) = nonce_account { + self.instructions + .push(SystemInstructionBuilder::advance_nonce_account( + nonce_account, + authorized_account, + )); + } + self + } + + /// Adds a `memo` instruction if it is not empty. + pub fn maybe_memo(&mut self, memo: &str) -> &mut Self { + if !memo.is_empty() { + self.instructions.push(SystemInstructionBuilder::memo(memo)); + } + self + } + + /// Adds a `priority_fee` instruction if it is provided. + pub fn maybe_priority_fee_price(&mut self, micro_lamports: Option) -> &mut Self { + if let Some(micro_lamports) = micro_lamports { + let ix = ComputeBudgetInstructionBuilder::set_compute_unit_price(micro_lamports); + self.instructions.push(ix); + } + self + } + + /// Adds a `fee_limit` instruction if it is provided. + pub fn maybe_priority_fee_limit(&mut self, units: Option) -> &mut Self { + if let Some(units) = units { + let ix = ComputeBudgetInstructionBuilder::set_compute_unit_limit(units); + self.instructions.push(ix); + } + self + } + + pub fn add_instruction(&mut self, instruction: Instruction) -> &mut Self { + self.instructions.push(instruction); + self + } + + pub fn add_instructions(&mut self, instructions: I) -> &mut Self + where + I: IntoIterator, + { + self.instructions.extend(instructions); + self + } + + pub fn output(self) -> Vec { + self.instructions + } +} diff --git a/rust/chains/tw_solana/src/modules/instruction_builder/stake_instruction.rs b/rust/chains/tw_solana/src/modules/instruction_builder/stake_instruction.rs new file mode 100644 index 00000000000..be6ba463aba --- /dev/null +++ b/rust/chains/tw_solana/src/modules/instruction_builder/stake_instruction.rs @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::address::SolanaAddress; +use crate::blockhash::Blockhash; +use crate::defined_addresses::*; +use crate::instruction::{AccountMeta, Instruction}; +use crate::modules::instruction_builder::system_instruction::SystemInstructionBuilder; +use crate::program::stake_program::StakeProgram; +use serde::{Deserialize, Serialize}; +use tw_coin_entry::error::SigningResult; + +type UnixTimestamp = i64; +type Epoch = u64; + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct Authorized { + pub staker: SolanaAddress, + pub withdrawer: SolanaAddress, +} + +#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq, Clone)] +pub struct Lockup { + /// UnixTimestamp at which this stake will allow withdrawal, unless the + /// transaction is signed by the custodian + pub unix_timestamp: UnixTimestamp, + /// epoch height at which this stake will allow withdrawal, unless the + /// transaction is signed by the custodian + pub epoch: Epoch, + /// custodian signature on a transaction exempts the operation from + /// lockup constraints + pub custodian: SolanaAddress, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +pub enum StakeAuthorize { + Staker, + Withdrawer, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct LockupArgs { + pub unix_timestamp: Option, + pub epoch: Option, + pub custodian: Option, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +pub enum StakeInstruction { + /// Initialize a stake with lockup and authorization information + /// + /// # Account references + /// 0. `[WRITE]` Uninitialized stake account + /// 1. `[]` Rent sysvar + /// + /// Authorized carries pubkeys that must sign staker transactions + /// and withdrawer transactions. + /// Lockup carries information about withdrawal restrictions + Initialize(Authorized, Lockup), + + /// Authorize a key to manage stake or withdrawal + /// + /// # Account references + /// 0. `[WRITE]` Stake account to be updated + /// 1. `[]` Clock sysvar + /// 2. `[SIGNER]` The stake or withdraw authority + /// 3. Optional: `[SIGNER]` Lockup authority, if updating StakeAuthorize::Withdrawer before + /// lockup expiration + Authorize(SolanaAddress, StakeAuthorize), + + /// Delegate a stake to a particular vote account + /// + /// # Account references + /// 0. `[WRITE]` Initialized stake account to be delegated + /// 1. `[]` Vote account to which this stake will be delegated + /// 2. `[]` Clock sysvar + /// 3. `[]` Stake history sysvar that carries stake warmup/cooldown history + /// 4. `[]` Address of config account that carries stake config + /// 5. `[SIGNER]` Stake authority + /// + /// The entire balance of the staking account is staked. DelegateStake + /// can be called multiple times, but re-delegation is delayed + /// by one epoch + DelegateStake, + + /// Split u64 tokens and stake off a stake account into another stake account. + /// + /// # Account references + /// 0. `[WRITE]` Stake account to be split; must be in the Initialized or Stake state + /// 1. `[WRITE]` Uninitialized stake account that will take the split-off amount + /// 2. `[SIGNER]` Stake authority + Split(u64), + + /// Withdraw unstaked lamports from the stake account + /// + /// # Account references + /// 0. `[WRITE]` Stake account from which to withdraw + /// 1. `[WRITE]` Recipient account + /// 2. `[]` Clock sysvar + /// 3. `[]` Stake history sysvar that carries stake warmup/cooldown history + /// 4. `[SIGNER]` Withdraw authority + /// 5. Optional: `[SIGNER]` Lockup authority, if before lockup expiration + /// + /// The u64 is the portion of the stake account balance to be withdrawn, + /// must be `<= StakeAccount.lamports - staked_lamports`. + Withdraw(u64), + + /// Deactivates the stake in the account + /// + /// # Account references + /// 0. `[WRITE]` Delegated stake account + /// 1. `[]` Clock sysvar + /// 2. `[SIGNER]` Stake authority + Deactivate, + + /// Set stake lockup + /// + /// If a lockup is not active, the withdraw authority may set a new lockup + /// If a lockup is active, the lockup custodian may update the lockup parameters + /// + /// # Account references + /// 0. `[WRITE]` Initialized stake account + /// 1. `[SIGNER]` Lockup authority or withdraw authority + SetLockup { + unix_timestamp: Option, + epoch: Option, + custodian: Option, + }, + + /// Merge two stake accounts. + /// + /// Both accounts must have identical lockup and authority keys. A merge + /// is possible between two stakes in the following states with no additional + /// conditions: + /// + /// * two deactivated stakes + /// * an inactive stake into an activating stake during its activation epoch + /// + /// For the following cases, the voter pubkey and vote credits observed must match: + /// + /// * two activated stakes + /// * two activating accounts that share an activation epoch, during the activation epoch + /// + /// All other combinations of stake states will fail to merge, including all + /// "transient" states, where a stake is activating or deactivating with a + /// non-zero effective stake. + /// + /// # Account references + /// 0. `[WRITE]` Destination stake account for the merge + /// 1. `[WRITE]` Source stake account for to merge. This account will be drained + /// 2. `[]` Clock sysvar + /// 3. `[]` Stake history sysvar that carries stake warmup/cooldown history + /// 4. `[SIGNER]` Stake authority + Merge, + + /// Authorize a key to manage stake or withdrawal with a derived key + /// + /// # Account references + /// 0. `[WRITE]` Stake account to be updated + /// 1. `[SIGNER]` Base key of stake or withdraw authority + /// 2. `[]` Clock sysvar + /// 3. Optional: `[SIGNER]` Lockup authority, if updating StakeAuthorize::Withdrawer before + /// lockup expiration + AuthorizeWithSeed { + new_authorized_pubkey: SolanaAddress, + stake_authorize: StakeAuthorize, + authority_seed: String, + authority_owner: SolanaAddress, + }, + + /// Initialize a stake with authorization information + /// + /// This instruction is similar to `Initialize` except that the withdraw authority + /// must be a signer, and no lockup is applied to the account. + /// + /// # Account references + /// 0. `[WRITE]` Uninitialized stake account + /// 1. `[]` Rent sysvar + /// 2. `[]` The stake authority + /// 3. `[SIGNER]` The withdraw authority + /// + InitializeChecked, + + /// Authorize a key to manage stake or withdrawal + /// + /// This instruction behaves like `Authorize` with the additional requirement that the new + /// stake or withdraw authority must also be a signer. + /// + /// # Account references + /// 0. `[WRITE]` Stake account to be updated + /// 1. `[]` Clock sysvar + /// 2. `[SIGNER]` The stake or withdraw authority + /// 3. `[SIGNER]` The new stake or withdraw authority + /// 4. Optional: `[SIGNER]` Lockup authority, if updating StakeAuthorize::Withdrawer before + /// lockup expiration + AuthorizeChecked(StakeAuthorize), + + /// Authorize a key to manage stake or withdrawal with a derived key + /// + /// This instruction behaves like `AuthorizeWithSeed` with the additional requirement that + /// the new stake or withdraw authority must also be a signer. + /// + /// # Account references + /// 0. `[WRITE]` Stake account to be updated + /// 1. `[SIGNER]` Base key of stake or withdraw authority + /// 2. `[]` Clock sysvar + /// 3. `[SIGNER]` The new stake or withdraw authority + /// 4. Optional: `[SIGNER]` Lockup authority, if updating StakeAuthorize::Withdrawer before + /// lockup expiration + AuthorizeCheckedWithSeed { + stake_authorize: StakeAuthorize, + authority_seed: String, + authority_owner: SolanaAddress, + }, + + /// Set stake lockup + /// + /// This instruction behaves like `SetLockup` with the additional requirement that + /// the new lockup authority also be a signer. + /// + /// If a lockup is not active, the withdraw authority may set a new lockup + /// If a lockup is active, the lockup custodian may update the lockup parameters + /// + /// # Account references + /// 0. `[WRITE]` Initialized stake account + /// 1. `[SIGNER]` Lockup authority or withdraw authority + /// 2. Optional: `[SIGNER]` New lockup authority + SetLockupChecked { + unix_timestamp: Option, + epoch: Option, + }, + + /// Get the minimum stake delegation, in lamports + /// + /// # Account references + /// None + /// + /// Returns the minimum delegation as a little-endian encoded u64 value. + /// Programs can use the [`get_minimum_delegation()`] helper function to invoke and + /// retrieve the return value for this instruction. + /// + /// [`get_minimum_delegation()`]: super::tools::get_minimum_delegation + GetMinimumDelegation, + + /// Deactivate stake delegated to a vote account that has been delinquent for at least + /// `MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION` epochs. + /// + /// No signer is required for this instruction as it is a common good to deactivate abandoned + /// stake. + /// + /// # Account references + /// 0. `[WRITE]` Delegated stake account + /// 1. `[]` Delinquent vote account for the delegated stake account + /// 2. `[]` Reference vote account that has voted at least once in the last + /// `MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION` epochs + DeactivateDelinquent, + + /// Redelegate activated stake to another vote account. + /// + /// Upon success: + /// * the balance of the delegated stake account will be reduced to the undelegated amount in + /// the account (rent exempt minimum and any additional lamports not part of the delegation), + /// and scheduled for deactivation. + /// * the provided uninitialized stake account will receive the original balance of the + /// delegated stake account, minus the rent exempt minimum, and scheduled for activation to + /// the provided vote account. Any existing lamports in the uninitialized stake account + /// will also be included in the re-delegation. + /// + /// # Account references + /// 0. `[WRITE]` Delegated stake account to be redelegated. The account must be fully + /// activated and carry a balance greater than or equal to the minimum delegation amount + /// plus rent exempt minimum + /// 1. `[WRITE]` Uninitialized stake account that will hold the redelegated stake + /// 2. `[]` Vote account to which this stake will be re-delegated + /// 3. `[]` Address of config account that carries stake config + /// 4. `[SIGNER]` Stake authority + /// + Redelegate, +} + +pub struct DepositStakeArgs { + pub sender: SolanaAddress, + pub validator: SolanaAddress, + pub stake_account: Option, + pub recent_blockhash: Blockhash, + pub lamports: u64, + pub space: u64, +} + +pub struct StakeInstructionBuilder; + +impl StakeInstructionBuilder { + /// Creates an Initialize Stake instruction. + pub fn stake_initialize( + stake_pubkey: SolanaAddress, + authorized: Authorized, + lockup: Lockup, + ) -> Instruction { + Instruction::new_with_bincode( + *STAKE_PROGRAM_ID_ADDRESS, + StakeInstruction::Initialize(authorized, lockup), + vec![ + AccountMeta::new(stake_pubkey, false), + AccountMeta::readonly(*SYSVAR_RENT_ID_ADDRESS, false), + ], + ) + } + + pub fn withdraw( + stake_pubkey: SolanaAddress, + withdrawer_pubkey: SolanaAddress, + to_pubkey: SolanaAddress, + lamports: u64, + custodian_pubkey: Option, + ) -> Instruction { + let mut account_metas = vec![ + AccountMeta::new(stake_pubkey, false), + AccountMeta::new(to_pubkey, false), + AccountMeta::readonly(*SYSVAR_CLOCK_ID_ADDRESS, false), + AccountMeta::readonly(*SYSVAR_STAKE_HISTORY_ID_ADDRESS, false), + AccountMeta::readonly(withdrawer_pubkey, true), + ]; + + if let Some(custodian_pubkey) = custodian_pubkey { + account_metas.push(AccountMeta::readonly(custodian_pubkey, true)); + } + + Instruction::new_with_bincode( + *STAKE_PROGRAM_ID_ADDRESS, + StakeInstruction::Withdraw(lamports), + account_metas, + ) + } + + pub fn delegate( + stake_pubkey: SolanaAddress, + vote_pubkey: SolanaAddress, + authorized_pubkey: SolanaAddress, + ) -> Instruction { + let account_metas = vec![ + AccountMeta::new(stake_pubkey, false), + AccountMeta::readonly(vote_pubkey, false), + AccountMeta::readonly(*SYSVAR_CLOCK_ID_ADDRESS, false), + AccountMeta::readonly(*SYSVAR_STAKE_HISTORY_ID_ADDRESS, false), + AccountMeta::readonly(*STAKE_CONFIG_ID_ADDRESS, false), + AccountMeta::readonly(authorized_pubkey, true), + ]; + Instruction::new_with_bincode( + *STAKE_PROGRAM_ID_ADDRESS, + StakeInstruction::DelegateStake, + account_metas, + ) + } + + /// 0. `[WRITE]` Delegated stake account + /// 1. `[]` Clock sysvar + /// 2. `[SIGNER]` Stake authority + pub fn deactivate( + stake_pubkey: SolanaAddress, + authorized_pubkey: SolanaAddress, + ) -> Instruction { + let account_metas = vec![ + AccountMeta::new(stake_pubkey, false), + AccountMeta::readonly(*SYSVAR_CLOCK_ID_ADDRESS, false), + AccountMeta::new(authorized_pubkey, true), + ]; + Instruction::new_with_bincode( + *STAKE_PROGRAM_ID_ADDRESS, + StakeInstruction::Deactivate, + account_metas, + ) + } + + /// The function represents "stake delegation" operation that consists of several small instructions. + pub fn deposit_stake(args: DepositStakeArgs) -> SigningResult> { + let stake_addr = args.stake_account.unwrap_or_else(|| { + // no stake address specified, generate a new unique + StakeProgram::address_from_recent_blockhash(args.sender, args.recent_blockhash) + }); + let seed = args.recent_blockhash.to_string(); + + let authorized = Authorized { + staker: args.sender, + withdrawer: args.sender, + }; + let lockup = Lockup::default(); + + Ok(vec![ + SystemInstructionBuilder::create_account_with_seed( + args.sender, + stake_addr, + args.sender, + seed, + args.lamports, + args.space, + ), + StakeInstructionBuilder::stake_initialize(stake_addr, authorized, lockup), + StakeInstructionBuilder::delegate(stake_addr, args.validator, args.sender), + ]) + } +} diff --git a/rust/chains/tw_solana/src/modules/instruction_builder/system_instruction.rs b/rust/chains/tw_solana/src/modules/instruction_builder/system_instruction.rs new file mode 100644 index 00000000000..ee7e0ab3f59 --- /dev/null +++ b/rust/chains/tw_solana/src/modules/instruction_builder/system_instruction.rs @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::address::SolanaAddress; +use crate::defined_addresses::*; +use crate::instruction::{AccountMeta, Instruction}; +use serde::{Deserialize, Serialize}; + +/// An instruction to the system program. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub enum SystemInstruction { + /// Create a new account + /// + /// # Account references + /// 0. `[WRITE, SIGNER]` Funding account + /// 1. `[WRITE, SIGNER]` New account + CreateAccount { + /// Number of lamports to transfer to the new account + lamports: u64, + + /// Number of bytes of memory to allocate + space: u64, + + /// Address of program that will own the new account + owner: SolanaAddress, + }, + + /// Assign account to a program + /// + /// # Account references + /// 0. `[WRITE, SIGNER]` Assigned account public key + Assign { + /// Owner program account + owner: SolanaAddress, + }, + + /// Transfer lamports + /// + /// # Account references + /// 0. `[WRITE, SIGNER]` Funding account + /// 1. `[WRITE]` Recipient account + Transfer { lamports: u64 }, + + /// Create a new account at an address derived from a base pubkey and a seed + /// + /// # Account references + /// 0. `[WRITE, SIGNER]` Funding account + /// 1. `[WRITE]` Created account + /// 2. `[SIGNER]` (optional) Base account; the account matching the base SolanaAddress below must be + /// provided as a signer, but may be the same as the funding account + /// and provided as account 0 + CreateAccountWithSeed { + /// Base public key + base: SolanaAddress, + + /// String of ASCII chars, no longer than `SolanaAddress::MAX_SEED_LEN` + seed: String, + + /// Number of lamports to transfer to the new account + lamports: u64, + + /// Number of bytes of memory to allocate + space: u64, + + /// Owner program account address + owner: SolanaAddress, + }, + + /// Consumes a stored nonce, replacing it with a successor + /// + /// # Account references + /// 0. `[WRITE]` Nonce account + /// 1. `[]` RecentBlockhashes sysvar + /// 2. `[SIGNER]` Nonce authority + AdvanceNonceAccount, + + /// Withdraw funds from a nonce account + /// + /// # Account references + /// 0. `[WRITE]` Nonce account + /// 1. `[WRITE]` Recipient account + /// 2. `[]` RecentBlockhashes sysvar + /// 3. `[]` Rent sysvar + /// 4. `[SIGNER]` Nonce authority + /// + /// The `u64` parameter is the lamports to withdraw, which must leave the + /// account balance above the rent exempt reserve or at zero. + WithdrawNonceAccount(u64), + + /// Drive state of Uninitialized nonce account to Initialized, setting the nonce value + /// + /// # Account references + /// 0. `[WRITE]` Nonce account + /// 1. `[]` RecentBlockhashes sysvar + /// 2. `[]` Rent sysvar + /// + /// The `SolanaAddress` parameter specifies the entity authorized to execute nonce + /// instruction on the account + /// + /// No signatures are required to execute this instruction, enabling derived + /// nonce account addresses + InitializeNonceAccount(SolanaAddress), + + /// Change the entity authorized to execute nonce instructions on the account + /// + /// # Account references + /// 0. `[WRITE]` Nonce account + /// 1. `[SIGNER]` Nonce authority + /// + /// The `SolanaAddress` parameter identifies the entity to authorize + AuthorizeNonceAccount(SolanaAddress), + + /// Allocate space in a (possibly new) account without funding + /// + /// # Account references + /// 0. `[WRITE, SIGNER]` New account + Allocate { + /// Number of bytes of memory to allocate + space: u64, + }, + + /// Allocate space for and assign an account at an address + /// derived from a base public key and a seed + /// + /// # Account references + /// 0. `[WRITE]` Allocated account + /// 1. `[SIGNER]` Base account + AllocateWithSeed { + /// Base public key + base: SolanaAddress, + + /// String of ASCII chars, no longer than `pubkey::MAX_SEED_LEN` + seed: String, + + /// Number of bytes of memory to allocate + space: u64, + + /// Owner program account + owner: SolanaAddress, + }, + + /// Assign account to a program based on a seed + /// + /// # Account references + /// 0. `[WRITE]` Assigned account + /// 1. `[SIGNER]` Base account + AssignWithSeed { + /// Base public key + base: SolanaAddress, + + /// String of ASCII chars, no longer than `pubkey::MAX_SEED_LEN` + seed: String, + + /// Owner program account + owner: SolanaAddress, + }, + + /// Transfer lamports from a derived address + /// + /// # Account references + /// 0. `[WRITE]` Funding account + /// 1. `[SIGNER]` Base for funding account + /// 2. `[WRITE]` Recipient account + TransferWithSeed { + /// Amount to transfer + lamports: u64, + + /// Seed to use to derive the funding account address + from_seed: String, + + /// Owner to use to derive the funding account address + from_owner: SolanaAddress, + }, + + /// One-time idempotent upgrade of legacy nonce versions in order to bump + /// them out of chain blockhash domain. + /// + /// # Account references + /// 0. `[WRITE]` Nonce account + UpgradeNonceAccount, +} + +pub struct SystemInstructionBuilder; + +impl SystemInstructionBuilder { + pub fn memo(memo: &str) -> Instruction { + let account_metas = Vec::default(); + let data = memo.as_bytes().to_vec(); + Instruction::new(*MEMO_PROGRAM_ID_ADDRESS, data, account_metas) + } + + pub fn create_account( + from_pubkey: SolanaAddress, + to_pubkey: SolanaAddress, + lamports: u64, + space: u64, + owner: SolanaAddress, + ) -> Instruction { + let account_metas = vec![ + AccountMeta::new(from_pubkey, true), + AccountMeta::new(to_pubkey, true), + ]; + Instruction::new_with_bincode( + *SYSTEM_PROGRAM_ID_ADDRESS, + SystemInstruction::CreateAccount { + lamports, + space, + owner, + }, + account_metas, + ) + } + + pub fn transfer( + from_pubkey: SolanaAddress, + to_pubkey: SolanaAddress, + lamports: u64, + ) -> Instruction { + let account_metas = vec![ + AccountMeta::new(from_pubkey, true), + AccountMeta::new(to_pubkey, false), + ]; + Instruction::new_with_bincode( + *SYSTEM_PROGRAM_ID_ADDRESS, + SystemInstruction::Transfer { lamports }, + account_metas, + ) + } + + /// Please note `to_pubkey` must match `create_with_seed(base, seed, owner)`. + pub fn create_account_with_seed( + from_pubkey: SolanaAddress, + to_pubkey: SolanaAddress, + base: SolanaAddress, + seed: String, + lamports: u64, + space: u64, + ) -> Instruction { + let account_metas = vec![ + AccountMeta::new(from_pubkey, true), + AccountMeta::new(to_pubkey, false), + AccountMeta::readonly(base, true), + ]; + + Instruction::new_with_bincode( + *SYSTEM_PROGRAM_ID_ADDRESS, + SystemInstruction::CreateAccountWithSeed { + base, + seed, + lamports, + space, + owner: *STAKE_PROGRAM_ID_ADDRESS, + }, + account_metas, + ) + } + + /// Advance the value of a durable transaction nonce. + /// + /// # Required signers + /// + /// The `authorized_pubkey` signer must sign the transaction. + pub fn advance_nonce_account( + nonce_pubkey: SolanaAddress, + authorized_pubkey: SolanaAddress, + ) -> Instruction { + let account_metas = vec![ + AccountMeta::new(nonce_pubkey, false), + AccountMeta::readonly(*SYSVAR_RECENT_BLOCKHASHS_ADDRESS, false), + AccountMeta::readonly(authorized_pubkey, true), + ]; + Instruction::new_with_bincode( + *SYSTEM_PROGRAM_ID_ADDRESS, + SystemInstruction::AdvanceNonceAccount, + account_metas, + ) + } + + /// Create an account containing a durable transaction nonce. + pub fn init_nonce_account( + nonce_pubkey: SolanaAddress, + authority: SolanaAddress, + ) -> Instruction { + let account_metas = vec![ + AccountMeta::new(nonce_pubkey, false), + AccountMeta::readonly(*SYSVAR_RECENT_BLOCKHASHS_ADDRESS, false), + AccountMeta::readonly(*SYSVAR_RENT_ID_ADDRESS, false), + ]; + Instruction::new_with_bincode( + *SYSTEM_PROGRAM_ID_ADDRESS, + SystemInstruction::InitializeNonceAccount(authority), + account_metas, + ) + } + + /// Withdraw lamports from a durable transaction nonce account. + pub fn withdraw_nonce_account( + nonce_pubkey: SolanaAddress, + authorized_pubkey: SolanaAddress, + to_pubkey: SolanaAddress, + lamports: u64, + ) -> Instruction { + let account_metas = vec![ + AccountMeta::new(nonce_pubkey, false), + AccountMeta::new(to_pubkey, false), + AccountMeta::readonly(*SYSVAR_RECENT_BLOCKHASHS_ADDRESS, false), + AccountMeta::readonly(*SYSVAR_RENT_ID_ADDRESS, false), + AccountMeta::readonly(authorized_pubkey, true), + ]; + Instruction::new_with_bincode( + *SYSTEM_PROGRAM_ID_ADDRESS, + SystemInstruction::WithdrawNonceAccount(lamports), + account_metas, + ) + } + + /// The function represents "create nonce account" operation that consists of several small instructions. + pub fn create_nonce_account( + signer: SolanaAddress, + new_nonce_account: SolanaAddress, + rent: u64, + space: u64, + ) -> Vec { + let program_id = *SYSTEM_PROGRAM_ID_ADDRESS; + vec![ + SystemInstructionBuilder::create_account( + signer, + new_nonce_account, + rent, + space, + program_id, + ), + SystemInstructionBuilder::init_nonce_account(new_nonce_account, signer), + ] + } +} diff --git a/rust/chains/tw_solana/src/modules/instruction_builder/token_instruction.rs b/rust/chains/tw_solana/src/modules/instruction_builder/token_instruction.rs new file mode 100644 index 00000000000..8cfb3248bad --- /dev/null +++ b/rust/chains/tw_solana/src/modules/instruction_builder/token_instruction.rs @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::address::SolanaAddress; +use crate::defined_addresses::*; +use crate::instruction::{AccountMeta, Instruction}; +use std::mem::size_of; +use tw_memory::Data; + +/// Instructions supported by the token program. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TokenInstruction { + /// Transfers tokens from one account to another either directly or via a + /// delegate. If this account is associated with the native mint then equal + /// amounts of SOL and Tokens will be transferred to the destination + /// account. + /// + /// This instruction differs from Transfer in that the token mint and + /// decimals value is checked by the caller. This may be useful when + /// creating transactions offline or within a hardware wallet. + /// + /// Accounts expected by this instruction: + /// + /// * Single owner/delegate + /// 0. `[writable]` The source account. + /// 1. `[]` The token mint. + /// 2. `[writable]` The destination account. + /// 3. `[signer]` The source account's owner/delegate. + /// + /// * Multisignature owner/delegate + /// 0. `[writable]` The source account. + /// 1. `[]` The token mint. + /// 2. `[writable]` The destination account. + /// 3. `[]` The source account's multisignature owner/delegate. + /// 4. ..4+M `[signer]` M signer accounts. + TransferChecked { + /// The amount of tokens to transfer. + amount: u64, + /// Expected number of base 10 digits to the right of the decimal place. + decimals: u8, + }, +} + +impl TokenInstruction { + /// Packs a [TokenInstruction](enum.TokenInstruction.html) into a byte + /// buffer. + /// + /// # Important + /// + /// This serialization method differs from [`bincode::serialize`]: + /// It needs to serialize enum `type` as u8 (1 byte), but [`bincode::serialize`] serializes the type as u32 (4 bytes). + pub fn pack(&self) -> Vec { + let mut buf = Vec::with_capacity(size_of::()); + match self { + &Self::TransferChecked { amount, decimals } => { + // https://github.com/solana-labs/solana-program-library/blob/5418cf9b90d5c9ff5bff9f55fd17651f66c98902/token/program-2022/src/instruction.rs#L334-L339 + // https://github.com/trustwallet/wallet-core/blob/cd5a27481d2181e63362cb57e2b2160506cce163/src/Solana/Instruction.h#L37 + buf.push(12); + buf.extend_from_slice(&amount.to_le_bytes()); + buf.push(decimals); + }, + }; + buf + } +} + +pub struct TokenInstructionBuilder; + +impl TokenInstructionBuilder { + // `create_associated_token_account()` solana-program-library/associated-token-account/program/src/lib.rs + pub fn create_account( + funding_pubkey: SolanaAddress, + other_main_pubkey: SolanaAddress, + token_mint_pubkey: SolanaAddress, + token_pubkey: SolanaAddress, + ) -> Instruction { + let account_metas = vec![ + AccountMeta::new(funding_pubkey, true), + AccountMeta::new(token_pubkey, false), + AccountMeta::readonly(other_main_pubkey, false), + AccountMeta::readonly(token_mint_pubkey, false), + AccountMeta::readonly(*SYSTEM_PROGRAM_ID_ADDRESS, false), + AccountMeta::readonly(*TOKEN_PROGRAM_ID_ADDRESS, false), + AccountMeta::readonly(*SYSVAR_RENT_ID_ADDRESS, false), + ]; + let data = Data::default(); + Instruction::new(*ASSOCIATED_TOKEN_PROGRAM_ID_ADDRESS, data, account_metas) + } + + /// transfer_checked() solana-program-library/token/program/src/instruction.rs + pub fn transfer_checked( + sender_token_pubkey: SolanaAddress, + token_mint_pubkey: SolanaAddress, + recipient_token_pubkey: SolanaAddress, + signer: SolanaAddress, + amount: u64, + decimals: u8, + ) -> Instruction { + let account_metas = vec![ + AccountMeta::new(sender_token_pubkey, false), + AccountMeta::readonly(token_mint_pubkey, false), + AccountMeta::new(recipient_token_pubkey, false), + AccountMeta::new(signer, true), + ]; + + let data = TokenInstruction::TransferChecked { amount, decimals }.pack(); + Instruction::new(*TOKEN_PROGRAM_ID_ADDRESS, data, account_metas) + } +} diff --git a/rust/chains/tw_solana/src/modules/message_builder.rs b/rust/chains/tw_solana/src/modules/message_builder.rs new file mode 100644 index 00000000000..b9e43cde4e5 --- /dev/null +++ b/rust/chains/tw_solana/src/modules/message_builder.rs @@ -0,0 +1,689 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::address::SolanaAddress; +use crate::blockhash::Blockhash; +use crate::instruction::Instruction; +use crate::modules::compiled_instructions::compile_instructions; +use crate::modules::compiled_keys::CompiledKeys; +use crate::modules::instruction_builder::compute_budget_instruction::{UnitLimit, UnitPrice}; +use crate::modules::instruction_builder::stake_instruction::{ + DepositStakeArgs, StakeInstructionBuilder, +}; +use crate::modules::instruction_builder::system_instruction::SystemInstructionBuilder; +use crate::modules::instruction_builder::token_instruction::TokenInstructionBuilder; +use crate::modules::instruction_builder::InstructionBuilder; +use crate::modules::PubkeySignatureMap; +use crate::transaction::versioned::VersionedMessage; +use crate::transaction::{legacy, v0, CompiledInstruction, MessageHeader, Signature}; +use std::borrow::Cow; +use std::str::FromStr; +use tw_coin_entry::error::{AddressResult, SigningError, SigningErrorType, SigningResult}; +use tw_keypair::ed25519; +use tw_keypair::traits::KeyPairTrait; +use tw_proto::Solana::Proto; +use Proto::mod_SigningInput::OneOftransaction_type as ProtoTransactionType; + +const DEFAULT_SPACE: u64 = 200; +const DEFAULT_CREATE_NONCE_SPACE: u64 = 80; + +pub struct MessageBuilder<'a> { + input: Proto::SigningInput<'a>, +} + +impl<'a> MessageBuilder<'a> { + pub fn new(input: Proto::SigningInput<'a>) -> Self { + MessageBuilder { input } + } + + pub fn signing_keys(&self) -> SigningResult> { + let mut signing_keys = Vec::default(); + if !self.input.fee_payer_private_key.is_empty() { + let fee_payer_private_key = + ed25519::sha512::KeyPair::try_from(self.input.fee_payer_private_key.as_ref())?; + signing_keys.push(fee_payer_private_key); + } + + signing_keys.push(self.signer_key()?); + + // Consider matching other transaction types if they may contain other private keys. + if let ProtoTransactionType::create_nonce_account(ref nonce) = self.input.transaction_type { + let nonce_private_key = + ed25519::sha512::KeyPair::try_from(nonce.nonce_account_private_key.as_ref())?; + signing_keys.push(nonce_private_key); + } + + Ok(signing_keys) + } + + pub fn signers(&self) -> SigningResult> { + let mut signers = Vec::default(); + if !self.input.fee_payer.is_empty() { + signers.push(SolanaAddress::from_str(self.input.fee_payer.as_ref())?); + } + + signers.push(self.signer_address()?); + + // Consider matching other transaction types if they may contain other private keys. + if let ProtoTransactionType::create_nonce_account(ref nonce) = self.input.transaction_type { + signers.push(SolanaAddress::from_str(nonce.nonce_account.as_ref())?); + } + + Ok(signers) + } + + pub fn external_signatures(&self) -> SigningResult { + match self.input.raw_message { + Some(ref raw_message) => RawMessageBuilder::external_signatures(raw_message), + None => Ok(PubkeySignatureMap::default()), + } + } + + pub fn build(self) -> SigningResult { + if let Some(ref raw_message) = self.input.raw_message { + return RawMessageBuilder::build(raw_message); + } + + let instructions = self.build_instructions()?; + + // Please note the fee payer can be different from the actual signer. + let (message_header, account_keys) = CompiledKeys::with_fee_payer(self.fee_payer()?) + .compile(&instructions) + .try_into_message_components()?; + + let compiled_instructions = compile_instructions(&instructions, &account_keys)?; + + if self.input.v0_msg { + Ok(VersionedMessage::V0(v0::Message { + header: message_header, + account_keys, + recent_blockhash: self.recent_blockhash()?.to_bytes(), + instructions: compiled_instructions, + address_table_lookups: Vec::default(), + })) + } else { + Ok(VersionedMessage::Legacy(legacy::Message { + header: message_header, + account_keys, + recent_blockhash: self.recent_blockhash()?.to_bytes(), + instructions: compiled_instructions, + })) + } + } + + /// Please note that this method can add [`MessageBuilder::signer_keys`] if necessary. + fn build_instructions(&self) -> SigningResult> { + match self.input.transaction_type { + ProtoTransactionType::transfer_transaction(ref transfer) => { + self.transfer_from_proto(transfer) + }, + ProtoTransactionType::delegate_stake_transaction(ref delegate) => { + self.delegate_stake_from_proto(delegate) + }, + ProtoTransactionType::deactivate_stake_transaction(ref deactivate) => { + self.deactivate_stake_from_proto(deactivate) + }, + ProtoTransactionType::deactivate_all_stake_transaction(ref deactivate_all) => { + self.deactivate_all_stake_from_proto(deactivate_all) + }, + ProtoTransactionType::withdraw_transaction(ref withdraw) => { + self.withdraw_from_proto(withdraw) + }, + ProtoTransactionType::withdraw_all_transaction(ref withdraw_all) => { + self.withdraw_all_from_proto(withdraw_all) + }, + ProtoTransactionType::create_token_account_transaction(ref create_token_acc) => { + self.create_token_account_from_proto(create_token_acc) + }, + ProtoTransactionType::token_transfer_transaction(ref token_transfer) => { + self.token_transfer_from_proto(token_transfer) + }, + ProtoTransactionType::create_and_transfer_token_transaction( + ref create_and_transfer, + ) => self.create_and_transfer_token_from_proto(create_and_transfer), + ProtoTransactionType::create_nonce_account(ref create_nonce) => { + self.create_nonce_account_from_proto(create_nonce) + }, + ProtoTransactionType::withdraw_nonce_account(ref withdraw_nonce) => { + self.withdraw_nonce_from_proto(withdraw_nonce) + }, + ProtoTransactionType::advance_nonce_account(ref advance_nonce) => { + self.advance_nonce_from_proto(advance_nonce) + }, + ProtoTransactionType::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + } + } + + fn transfer_from_proto( + &self, + transfer: &Proto::Transfer<'_>, + ) -> SigningResult> { + let from = self.signer_address()?; + let to = SolanaAddress::from_str(&transfer.recipient)?; + let references = Self::parse_references(&transfer.references)?; + + let transfer_ix = SystemInstructionBuilder::transfer(from, to, transfer.value) + .with_references(references); + + let mut builder = InstructionBuilder::default(); + builder + .maybe_priority_fee_price(self.priority_fee_price()) + .maybe_priority_fee_limit(self.priority_fee_limit()) + .maybe_advance_nonce(self.nonce_account()?, from) + .maybe_memo(transfer.memo.as_ref()) + .add_instruction(transfer_ix); + Ok(builder.output()) + } + + fn delegate_stake_from_proto( + &self, + delegate: &Proto::DelegateStake, + ) -> SigningResult> { + let sender = self.signer_address()?; + let validator = SolanaAddress::from_str(delegate.validator_pubkey.as_ref())?; + + let stake_account = if delegate.stake_account.is_empty() { + None + } else { + let stake_account = SolanaAddress::from_str(&delegate.stake_account)?; + Some(stake_account) + }; + + let deposit_ixs = StakeInstructionBuilder::deposit_stake(DepositStakeArgs { + sender, + validator, + stake_account, + recent_blockhash: self.recent_blockhash()?, + lamports: delegate.value, + space: DEFAULT_SPACE, + })?; + + let mut builder = InstructionBuilder::default(); + builder + .maybe_priority_fee_price(self.priority_fee_price()) + .maybe_priority_fee_limit(self.priority_fee_limit()) + .maybe_advance_nonce(self.nonce_account()?, sender) + .add_instructions(deposit_ixs); + Ok(builder.output()) + } + + fn deactivate_stake_from_proto( + &self, + deactivate: &Proto::DeactivateStake, + ) -> SigningResult> { + let sender = self.signer_address()?; + let stake_account = SolanaAddress::from_str(&deactivate.stake_account)?; + let deactivate_ix = StakeInstructionBuilder::deactivate(stake_account, sender); + + let mut builder = InstructionBuilder::default(); + builder + .maybe_priority_fee_price(self.priority_fee_price()) + .maybe_priority_fee_limit(self.priority_fee_limit()) + .maybe_advance_nonce(self.nonce_account()?, sender) + .add_instruction(deactivate_ix); + Ok(builder.output()) + } + + fn deactivate_all_stake_from_proto( + &self, + deactivate_all: &Proto::DeactivateAllStake, + ) -> SigningResult> { + let sender = self.signer_address()?; + let deactivate_ixs = deactivate_all + .stake_accounts + .iter() + .map(|stake_account| { + let stake_account = SolanaAddress::from_str(stake_account.as_ref())?; + Ok(StakeInstructionBuilder::deactivate(stake_account, sender)) + }) + .collect::>>()?; + + let mut builder = InstructionBuilder::default(); + builder + .maybe_priority_fee_price(self.priority_fee_price()) + .maybe_priority_fee_limit(self.priority_fee_limit()) + .maybe_advance_nonce(self.nonce_account()?, sender) + .add_instructions(deactivate_ixs); + Ok(builder.output()) + } + + fn withdraw_from_proto( + &self, + withdraw: &Proto::WithdrawStake, + ) -> SigningResult> { + let sender = self.signer_address()?; + let stake_account = SolanaAddress::from_str(withdraw.stake_account.as_ref())?; + let custodian_account = None; + + let withdraw_ix = StakeInstructionBuilder::withdraw( + stake_account, + sender, + sender, + withdraw.value, + custodian_account, + ); + + let mut builder = InstructionBuilder::default(); + builder + .maybe_priority_fee_price(self.priority_fee_price()) + .maybe_priority_fee_limit(self.priority_fee_limit()) + .maybe_advance_nonce(self.nonce_account()?, sender) + .add_instruction(withdraw_ix); + Ok(builder.output()) + } + + fn withdraw_all_from_proto( + &self, + withdraw_all: &Proto::WithdrawAllStake, + ) -> SigningResult> { + let sender = self.signer_address()?; + + let withdraw_ixs = withdraw_all + .stake_accounts + .iter() + .map(|withdraw| { + let stake_account = SolanaAddress::from_str(withdraw.stake_account.as_ref())?; + let custodian_account = None; + Ok(StakeInstructionBuilder::withdraw( + stake_account, + sender, + sender, + withdraw.value, + custodian_account, + )) + }) + .collect::>>()?; + + let mut builder = InstructionBuilder::default(); + builder + .maybe_priority_fee_price(self.priority_fee_price()) + .maybe_priority_fee_limit(self.priority_fee_limit()) + .maybe_advance_nonce(self.nonce_account()?, sender) + .add_instructions(withdraw_ixs); + Ok(builder.output()) + } + + fn create_token_account_from_proto( + &self, + create_token_acc: &Proto::CreateTokenAccount, + ) -> SigningResult> { + let funding_account = self.signer_address()?; + let other_main_address = SolanaAddress::from_str(create_token_acc.main_address.as_ref())?; + let token_mint_address = + SolanaAddress::from_str(create_token_acc.token_mint_address.as_ref())?; + let token_address = SolanaAddress::from_str(create_token_acc.token_address.as_ref())?; + + let instruction = TokenInstructionBuilder::create_account( + funding_account, + other_main_address, + token_mint_address, + token_address, + ); + let mut builder = InstructionBuilder::default(); + builder + .maybe_priority_fee_price(self.priority_fee_price()) + .maybe_priority_fee_limit(self.priority_fee_limit()) + .maybe_advance_nonce(self.nonce_account()?, funding_account) + .add_instruction(instruction); + Ok(builder.output()) + } + + fn token_transfer_from_proto( + &self, + token_transfer: &Proto::TokenTransfer, + ) -> SigningResult> { + let signer = self.signer_address()?; + let sender_token_address = + SolanaAddress::from_str(token_transfer.sender_token_address.as_ref())?; + let token_mint_address = + SolanaAddress::from_str(token_transfer.token_mint_address.as_ref())?; + let recipient_token_address = + SolanaAddress::from_str(token_transfer.recipient_token_address.as_ref())?; + + let decimals = token_transfer + .decimals + .try_into() + .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?; + let references = Self::parse_references(&token_transfer.references)?; + + let transfer_instruction = TokenInstructionBuilder::transfer_checked( + sender_token_address, + token_mint_address, + recipient_token_address, + signer, + token_transfer.amount, + decimals, + ) + .with_references(references); + + let mut builder = InstructionBuilder::default(); + builder + .maybe_priority_fee_price(self.priority_fee_price()) + .maybe_priority_fee_limit(self.priority_fee_limit()) + .maybe_advance_nonce(self.nonce_account()?, signer) + .maybe_memo(token_transfer.memo.as_ref()) + .add_instruction(transfer_instruction); + Ok(builder.output()) + } + + fn create_and_transfer_token_from_proto( + &self, + create_and_transfer: &Proto::CreateAndTransferToken, + ) -> SigningResult> { + let signer = self.signer_address()?; + let fee_payer = self.fee_payer()?; + let sender_token_address = + SolanaAddress::from_str(create_and_transfer.sender_token_address.as_ref())?; + let token_mint_address = + SolanaAddress::from_str(create_and_transfer.token_mint_address.as_ref())?; + let recipient_main_address = + SolanaAddress::from_str(create_and_transfer.recipient_main_address.as_ref())?; + let recipient_token_address = + SolanaAddress::from_str(create_and_transfer.recipient_token_address.as_ref())?; + let references = Self::parse_references(&create_and_transfer.references)?; + + let decimals = create_and_transfer + .decimals + .try_into() + .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?; + + let create_account_instruction = TokenInstructionBuilder::create_account( + // Can be different from the actual signer. + fee_payer, + recipient_main_address, + token_mint_address, + recipient_token_address, + ); + let transfer_instruction = TokenInstructionBuilder::transfer_checked( + sender_token_address, + token_mint_address, + recipient_token_address, + signer, + create_and_transfer.amount, + decimals, + ) + .with_references(references); + + let mut builder = InstructionBuilder::default(); + builder + .maybe_priority_fee_price(self.priority_fee_price()) + .maybe_priority_fee_limit(self.priority_fee_limit()) + .maybe_advance_nonce(self.nonce_account()?, signer) + .add_instruction(create_account_instruction) + // Optional memo. Order: before transfer, as per documentation. + .maybe_memo(create_and_transfer.memo.as_ref()) + .add_instruction(transfer_instruction); + Ok(builder.output()) + } + + fn create_nonce_account_from_proto( + &self, + create_nonce: &Proto::CreateNonceAccount, + ) -> SigningResult> { + let signer = self.signer_address()?; + let prev_nonce_account = self.nonce_account()?; + + let new_nonce_account = if create_nonce.nonce_account.is_empty() { + let nonce_key = ed25519::sha512::KeyPair::try_from( + create_nonce.nonce_account_private_key.as_ref(), + )?; + SolanaAddress::with_public_key_ed25519(nonce_key.public()) + } else { + SolanaAddress::from_str(create_nonce.nonce_account.as_ref())? + }; + + let mut builder = InstructionBuilder::default(); + builder + .maybe_priority_fee_price(self.priority_fee_price()) + .maybe_priority_fee_limit(self.priority_fee_limit()) + .maybe_advance_nonce(prev_nonce_account, signer) + .add_instructions(SystemInstructionBuilder::create_nonce_account( + signer, + new_nonce_account, + create_nonce.rent, + DEFAULT_CREATE_NONCE_SPACE, + )); + Ok(builder.output()) + } + + fn withdraw_nonce_from_proto( + &self, + withdraw_nonce: &Proto::WithdrawNonceAccount, + ) -> SigningResult> { + let signer = self.signer_address()?; + let withdraw_from_nonce = SolanaAddress::from_str(withdraw_nonce.nonce_account.as_ref())?; + let recipient = SolanaAddress::from_str(withdraw_nonce.recipient.as_ref())?; + + let mut builder = InstructionBuilder::default(); + builder + .maybe_priority_fee_price(self.priority_fee_price()) + .maybe_priority_fee_limit(self.priority_fee_limit()) + .maybe_advance_nonce(self.nonce_account()?, signer) + .add_instruction(SystemInstructionBuilder::withdraw_nonce_account( + withdraw_from_nonce, + signer, + recipient, + withdraw_nonce.value, + )); + Ok(builder.output()) + } + + fn advance_nonce_from_proto( + &self, + advance_nonce: &Proto::AdvanceNonceAccount, + ) -> SigningResult> { + let signer = self.signer_address()?; + let nonce_account = SolanaAddress::from_str(advance_nonce.nonce_account.as_ref())?; + + let mut builder = InstructionBuilder::default(); + builder + .maybe_priority_fee_price(self.priority_fee_price()) + .maybe_priority_fee_limit(self.priority_fee_limit()) + .maybe_advance_nonce(Some(nonce_account), signer); + Ok(builder.output()) + } + + fn nonce_account(&self) -> SigningResult> { + if self.input.nonce_account.is_empty() { + Ok(None) + } else { + SolanaAddress::from_str(&self.input.nonce_account) + .map(Some) + .map_err(SigningError::from) + } + } + + fn signer_address(&self) -> SigningResult { + if self.input.private_key.is_empty() { + SolanaAddress::from_str(&self.input.sender).map_err(SigningError::from) + } else { + Ok(SolanaAddress::with_public_key_ed25519( + self.signer_key()?.public(), + )) + } + } + + /// Returns a fee payer of the transaction. + /// Please note it can be different the transaction signer if [`Proto::SigningInput::fee_payer`] is set. + pub fn fee_payer(&self) -> SigningResult { + if !self.input.fee_payer_private_key.is_empty() { + let fee_payer_key = + ed25519::sha512::KeyPair::try_from(self.input.fee_payer_private_key.as_ref())?; + return Ok(SolanaAddress::with_public_key_ed25519( + fee_payer_key.public(), + )); + } + if !self.input.fee_payer.is_empty() { + return SolanaAddress::from_str(self.input.fee_payer.as_ref()) + .map_err(SigningError::from); + } + self.signer_address() + } + + fn recent_blockhash(&self) -> SigningResult { + Ok(Blockhash::from_str(&self.input.recent_blockhash)?) + } + + fn signer_key(&self) -> SigningResult { + ed25519::sha512::KeyPair::try_from(self.input.private_key.as_ref()) + .map_err(SigningError::from) + } + + fn priority_fee_price(&self) -> Option { + self.input + .priority_fee_price + .as_ref() + .map(|proto| proto.price) + } + + fn priority_fee_limit(&self) -> Option { + self.input + .priority_fee_limit + .as_ref() + .map(|proto| proto.limit) + } + + fn parse_references(refs: &[Cow<'_, str>]) -> SigningResult> { + refs.iter() + .map(|addr| SolanaAddress::from_str(addr).map_err(SigningError::from)) + .collect() + } +} + +pub struct RawMessageBuilder; + +impl RawMessageBuilder { + pub fn build(raw_message: &Proto::RawMessage) -> SigningResult { + use Proto::mod_RawMessage::OneOfmessage as RawMessageType; + + match raw_message.message { + RawMessageType::legacy(ref legacy) => Self::build_legacy(legacy), + RawMessageType::v0(ref v0) => Self::build_v0(v0), + RawMessageType::None => Err(SigningError(SigningErrorType::Error_invalid_params)), + } + } + + pub fn external_signatures( + raw_message: &Proto::RawMessage, + ) -> SigningResult { + let mut key_signs = PubkeySignatureMap::with_capacity(raw_message.signatures.len()); + for entry in raw_message.signatures.iter() { + let pubkey = SolanaAddress::from_str(entry.pubkey.as_ref())?; + let signature = Signature::from_str(entry.signature.as_ref())?; + let ed25519_signature = ed25519::Signature::try_from(signature.0.as_slice())?; + key_signs.insert(pubkey, ed25519_signature); + } + Ok(key_signs) + } + + fn build_legacy( + legacy: &Proto::mod_RawMessage::MessageLegacy, + ) -> SigningResult { + let header = Self::build_message_header(&legacy.header)?; + let account_keys = Self::build_account_keys(&legacy.account_keys)?; + let recent_blockhash = Blockhash::from_str(legacy.recent_blockhash.as_ref())?; + let instructions: Vec<_> = Self::build_instructions(&legacy.instructions)?; + + Ok(VersionedMessage::Legacy(legacy::Message { + header, + account_keys, + recent_blockhash: recent_blockhash.to_bytes(), + instructions, + })) + } + + fn build_v0(v0: &Proto::mod_RawMessage::MessageV0) -> SigningResult { + let header = Self::build_message_header(&v0.header)?; + let account_keys = Self::build_account_keys(&v0.account_keys)?; + let recent_blockhash = Blockhash::from_str(v0.recent_blockhash.as_ref())?; + let instructions: Vec<_> = Self::build_instructions(&v0.instructions)?; + let address_table_lookups = v0 + .address_table_lookups + .iter() + .map(Self::build_address_lookup_table) + .collect::>>()?; + + Ok(VersionedMessage::V0(v0::Message { + header, + account_keys, + recent_blockhash: recent_blockhash.to_bytes(), + instructions, + address_table_lookups, + })) + } + + fn build_message_header( + raw_header: &Option, + ) -> SigningResult { + let raw_header = raw_header + .as_ref() + .ok_or(SigningError(SigningErrorType::Error_invalid_params))?; + Ok(MessageHeader { + num_required_signatures: try_into_u8(raw_header.num_required_signatures)?, + num_readonly_signed_accounts: try_into_u8(raw_header.num_readonly_signed_accounts)?, + num_readonly_unsigned_accounts: try_into_u8(raw_header.num_readonly_unsigned_accounts)?, + }) + } + + fn build_account_keys(raw_keys: &[Cow<'_, str>]) -> SigningResult> { + raw_keys + .iter() + .map(|s| SolanaAddress::from_str(s.as_ref())) + .collect::>>() + .map_err(SigningError::from) + } + + fn build_instructions( + ixs: &[Proto::mod_RawMessage::Instruction], + ) -> SigningResult> { + ixs.iter().map(Self::build_instruction).collect() + } + + fn build_instruction( + ix: &Proto::mod_RawMessage::Instruction, + ) -> SigningResult { + let accounts = ix + .accounts + .iter() + .map(|idx| try_into_u8(*idx)) + .collect::>>()?; + + Ok(CompiledInstruction { + program_id_index: try_into_u8(ix.program_id)?, + accounts, + data: ix.program_data.to_vec(), + }) + } + + fn build_address_lookup_table( + lookup: &Proto::mod_RawMessage::MessageAddressTableLookup, + ) -> SigningResult { + let account_key = SolanaAddress::from_str(lookup.account_key.as_ref())?; + let writable_indexes = lookup + .writable_indexes + .iter() + .copied() + .map(try_into_u8) + .collect::>>()?; + let readonly_indexes = lookup + .readonly_indexes + .iter() + .copied() + .map(try_into_u8) + .collect::>>()?; + + Ok(v0::MessageAddressTableLookup { + account_key, + writable_indexes, + readonly_indexes, + }) + } +} + +fn try_into_u8(num: T) -> SigningResult +where + u8: TryFrom, +{ + u8::try_from(num).map_err(|_| SigningError(SigningErrorType::Error_invalid_params)) +} diff --git a/rust/chains/tw_solana/src/modules/mod.rs b/rust/chains/tw_solana/src/modules/mod.rs index 576e5ccc106..253aec84a3d 100644 --- a/rust/chains/tw_solana/src/modules/mod.rs +++ b/rust/chains/tw_solana/src/modules/mod.rs @@ -2,5 +2,18 @@ // // Copyright © 2017 Trust Wallet. +use crate::address::SolanaAddress; +use std::collections::HashMap; +use tw_keypair::ed25519; + +pub mod compiled_instructions; +pub mod compiled_keys; +pub mod instruction_builder; +pub mod message_builder; +pub mod proto_builder; +pub mod transaction_decoder; pub mod tx_signer; pub mod utils; +pub mod wallet_connect; + +pub type PubkeySignatureMap = HashMap; diff --git a/rust/chains/tw_solana/src/modules/proto_builder.rs b/rust/chains/tw_solana/src/modules/proto_builder.rs new file mode 100644 index 00000000000..d5394ab972b --- /dev/null +++ b/rust/chains/tw_solana/src/modules/proto_builder.rs @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::transaction::v0; +use crate::transaction::versioned::{VersionedMessage, VersionedTransaction}; +use std::borrow::Cow; +use tw_coin_entry::error::SigningResult; +use tw_proto::Solana::Proto::{self, mod_RawMessage::OneOfmessage as ProtoMessageType}; + +pub struct ProtoBuilder; + +impl ProtoBuilder { + pub fn build_from_tx(tx: &VersionedTransaction) -> SigningResult> { + let message_header = Proto::mod_RawMessage::MessageHeader { + num_required_signatures: tx.message.header().num_required_signatures as u32, + num_readonly_signed_accounts: tx.message.header().num_readonly_signed_accounts as u32, + num_readonly_unsigned_accounts: tx.message.header().num_readonly_unsigned_accounts + as u32, + }; + + let account_keys: Vec<_> = tx + .message + .account_keys() + .iter() + .map(|acc| Cow::from(acc.to_string())) + .collect(); + + let recent_blockhash = Cow::from(tx.message.recent_blockhash().to_string()); + + let instructions = tx + .message + .instructions() + .iter() + .map(|ix| Proto::mod_RawMessage::Instruction { + program_id: ix.program_id_index as u32, + accounts: vec_u8_to_u32(&ix.accounts), + program_data: Cow::from(ix.data.clone()), + }) + .collect(); + + let message = match tx.message { + VersionedMessage::Legacy(_) => { + ProtoMessageType::legacy(Proto::mod_RawMessage::MessageLegacy { + header: Some(message_header), + account_keys, + recent_blockhash, + instructions, + }) + }, + VersionedMessage::V0(ref v0) => { + let address_table_lookups = + Self::build_address_table_lookups(&v0.address_table_lookups); + ProtoMessageType::v0(Proto::mod_RawMessage::MessageV0 { + header: Some(message_header), + account_keys, + recent_blockhash, + instructions, + address_table_lookups, + }) + }, + }; + + Ok(Proto::RawMessage { + signatures: Self::build_signatures(tx), + message, + }) + } + + fn build_address_table_lookups( + lookups: &[v0::MessageAddressTableLookup], + ) -> Vec> { + lookups + .iter() + .map(|lookup| Proto::mod_RawMessage::MessageAddressTableLookup { + account_key: Cow::from(lookup.account_key.to_string()), + writable_indexes: vec_u8_to_u32(&lookup.writable_indexes), + readonly_indexes: vec_u8_to_u32(&lookup.readonly_indexes), + }) + .collect() + } + + pub fn build_signatures(tx: &VersionedTransaction) -> Vec> { + tx.message + .account_keys() + .iter() + .zip(tx.signatures.iter()) + .map(|(pubkey, signature)| Proto::PubkeySignature { + pubkey: Cow::from(pubkey.to_string()), + signature: Cow::from(signature.to_string()), + }) + .collect() + } +} + +fn vec_u8_to_u32(v: &[u8]) -> Vec { + v.iter().map(|num| *num as u32).collect() +} diff --git a/rust/chains/tw_solana/src/modules/transaction_decoder.rs b/rust/chains/tw_solana/src/modules/transaction_decoder.rs new file mode 100644 index 00000000000..00bea7705b2 --- /dev/null +++ b/rust/chains/tw_solana/src/modules/transaction_decoder.rs @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::modules::proto_builder::ProtoBuilder; +use crate::transaction::versioned::VersionedTransaction; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::modules::transaction_decoder::TransactionDecoder; +use tw_coin_entry::signing_output_error; +use tw_proto::Solana::Proto; + +pub struct SolanaTransactionDecoder; + +impl TransactionDecoder for SolanaTransactionDecoder { + type Output = Proto::DecodingTransactionOutput<'static>; + + fn decode_transaction(&self, coin: &dyn CoinContext, tx: &[u8]) -> Self::Output { + Self::decode_transaction_impl(coin, tx) + .unwrap_or_else(|e| signing_output_error!(Proto::DecodingTransactionOutput, e)) + } +} + +impl SolanaTransactionDecoder { + fn decode_transaction_impl( + _coin: &dyn CoinContext, + tx: &[u8], + ) -> SigningResult> { + let decoded_tx: VersionedTransaction = bincode::deserialize(tx) + .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; + let transaction = ProtoBuilder::build_from_tx(&decoded_tx)?; + + Ok(Proto::DecodingTransactionOutput { + transaction: Some(transaction), + ..Proto::DecodingTransactionOutput::default() + }) + } +} diff --git a/rust/chains/tw_solana/src/modules/tx_signer.rs b/rust/chains/tw_solana/src/modules/tx_signer.rs index 763f037149f..ee190af6063 100644 --- a/rust/chains/tw_solana/src/modules/tx_signer.rs +++ b/rust/chains/tw_solana/src/modules/tx_signer.rs @@ -2,29 +2,53 @@ // // Copyright © 2017 Trust Wallet. -use crate::transaction::{versioned, Pubkey, Signature}; +use crate::address::SolanaAddress; +use crate::modules::PubkeySignatureMap; +use crate::transaction::{versioned, Signature}; +use std::collections::HashMap; use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_keypair::ed25519; -use tw_keypair::traits::SigningKeyTrait; +use tw_keypair::traits::{KeyPairTrait, SigningKeyTrait}; use tw_memory::Data; pub struct TxSigner; impl TxSigner { pub fn sign_versioned( - mut tx: versioned::VersionedTransaction, - keys: &[ed25519::sha512::PrivateKey], + unsigned_msg: versioned::VersionedMessage, + keys: &[ed25519::sha512::KeyPair], + external_signatures: &PubkeySignatureMap, ) -> SigningResult { - if keys.len() != tx.message.num_required_signatures() { - return Err(SigningError(SigningErrorType::Error_signatures_count)); - } + let mut key_signs = HashMap::default(); - tx.zeroize_signatures(); - let message_data = bincode::serialize(&tx.message) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?; + let message_encoded = Self::preimage_versioned(&unsigned_msg)?; + // Add external signatures first, so they can be overriden if corresponding private keys are specified. + key_signs.extend(external_signatures.clone().into_iter()); + + // Sign the message with all given private keys. for private_key in keys { - let signing_pubkey = Pubkey(private_key.public().to_bytes()); + let signing_pubkey = + SolanaAddress::with_public_key_bytes(private_key.public().to_bytes()); + let ed25519_signature = private_key.sign(message_encoded.clone())?; + + key_signs.insert(signing_pubkey, ed25519_signature); + } + + Self::compile_versioned(unsigned_msg, key_signs) + } + + pub fn compile_versioned( + unsigned_msg: versioned::VersionedMessage, + key_signs: HashMap, + ) -> SigningResult { + let mut tx = versioned::VersionedTransaction::unsigned(unsigned_msg); + + if key_signs.len() != tx.message.num_required_signatures() { + return Err(SigningError(SigningErrorType::Error_signatures_count)); + } + + for (signing_pubkey, ed25519_signature) in key_signs { // Find an index of the corresponding account. let account_index = tx .message @@ -34,16 +58,13 @@ impl TxSigner { .signatures .get_mut(account_index) .ok_or(SigningError(SigningErrorType::Error_signatures_count))?; - - let ed25519_signature = private_key.sign(message_data.clone())?; *signature_to_reassign = Signature(ed25519_signature.to_bytes()); } Ok(tx) } - pub fn preimage_versioned(tx: &versioned::VersionedTransaction) -> SigningResult { - bincode::serialize(&tx.message) - .map_err(|_| SigningError(SigningErrorType::Error_invalid_params)) + pub fn preimage_versioned(msg: &versioned::VersionedMessage) -> SigningResult { + bincode::serialize(&msg).map_err(|_| SigningError(SigningErrorType::Error_internal)) } } diff --git a/rust/chains/tw_solana/src/modules/utils.rs b/rust/chains/tw_solana/src/modules/utils.rs index 99f9f69806c..c6b3d713935 100644 --- a/rust/chains/tw_solana/src/modules/utils.rs +++ b/rust/chains/tw_solana/src/modules/utils.rs @@ -2,7 +2,9 @@ // // Copyright © 2017 Trust Wallet. +use crate::modules::proto_builder::ProtoBuilder; use crate::modules::tx_signer::TxSigner; +use crate::modules::PubkeySignatureMap; use crate::transaction::versioned::VersionedTransaction; use crate::SOLANA_ALPHABET; use std::borrow::Cow; @@ -34,44 +36,42 @@ impl SolanaTransaction { let is_url = false; let tx_bytes = base64::decode(encoded_tx, is_url)?; - let mut tx_to_sign: VersionedTransaction = bincode::deserialize(&tx_bytes) + let tx_to_sign: VersionedTransaction = bincode::deserialize(&tx_bytes) .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; + let mut msg_to_sign = tx_to_sign.message; let new_blockchain_hash = base58::decode(recent_blockhash, &SOLANA_ALPHABET)?; let new_blockchain_hash = H256::try_from(new_blockchain_hash.as_slice()) .map_err(|_| SigningError(SigningErrorType::Error_invalid_params))?; // Update the transaction's blockhash and re-sign it. - tx_to_sign.message.set_recent_blockhash(new_blockchain_hash); + msg_to_sign.set_recent_blockhash(new_blockchain_hash); - let unsigned_encoded = TxSigner::preimage_versioned(&tx_to_sign)?; + let unsigned_encoded = TxSigner::preimage_versioned(&msg_to_sign)?; // Do not sign the transaction if there is no private keys, but set zeroed signatures. // It's needed to estimate the transaction fee with an updated blockhash without using real private keys. let signed_tx = if private_keys.is_empty() { - tx_to_sign.zeroize_signatures(); - tx_to_sign + VersionedTransaction::unsigned(msg_to_sign) } else { let private_keys = private_keys .iter() - .map(|pk| ed25519::sha512::PrivateKey::try_from(pk.as_slice())) + .map(|pk| ed25519::sha512::KeyPair::try_from(pk.as_slice())) .collect::>>()?; - TxSigner::sign_versioned(tx_to_sign, &private_keys)? + let external_signatures = PubkeySignatureMap::default(); + TxSigner::sign_versioned(msg_to_sign, &private_keys, &external_signatures)? }; let unsigned_encoded = base64::encode(&unsigned_encoded, is_url); let signed_encoded = bincode::serialize(&signed_tx) .map_err(|_| SigningError(SigningErrorType::Error_internal))?; let signed_encoded = base64::encode(&signed_encoded, is_url); - let message_encoded = bincode::serialize(&signed_tx.message) - .map_err(|_| SigningError(SigningErrorType::Error_internal))?; - let message_encoded = base64::encode(&message_encoded, is_url); Ok(Proto::SigningOutput { encoded: Cow::from(signed_encoded), unsigned_tx: Cow::from(unsigned_encoded), - message_encoded: Cow::from(message_encoded), + signatures: ProtoBuilder::build_signatures(&signed_tx), ..Proto::SigningOutput::default() }) } diff --git a/rust/chains/tw_solana/src/modules/wallet_connect/connector.rs b/rust/chains/tw_solana/src/modules/wallet_connect/connector.rs new file mode 100644 index 00000000000..31d45860f84 --- /dev/null +++ b/rust/chains/tw_solana/src/modules/wallet_connect/connector.rs @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::modules::proto_builder::ProtoBuilder; +use crate::modules::wallet_connect::request::SignTransactionRequest; +use crate::transaction::versioned::VersionedTransaction; +use tw_coin_entry::coin_context::CoinContext; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; +use tw_coin_entry::modules::wallet_connector::WalletConnector; +use tw_coin_entry::signing_output_error; +use tw_proto::Solana::Proto; +use tw_proto::WalletConnect::Proto::{ + self as WCProto, mod_ParseRequestOutput::OneOfsigning_input_oneof as SigningInputEnum, +}; + +pub struct SolanaWalletConnector; + +impl WalletConnector for SolanaWalletConnector { + fn parse_request( + &self, + coin: &dyn CoinContext, + request: WCProto::ParseRequestInput<'_>, + ) -> WCProto::ParseRequestOutput<'static> { + Self::parse_request_impl(coin, request) + .unwrap_or_else(|e| signing_output_error!(WCProto::ParseRequestOutput, e)) + } +} + +impl SolanaWalletConnector { + fn parse_request_impl( + coin: &dyn CoinContext, + request: WCProto::ParseRequestInput<'_>, + ) -> SigningResult> { + match request.method { + WCProto::Method::SolanaSignTransaction => { + Self::parse_sign_transaction_request(coin, request) + }, + _ => Err(SigningError(SigningErrorType::Error_not_supported)), + } + } + + pub fn parse_sign_transaction_request( + _coin: &dyn CoinContext, + request: WCProto::ParseRequestInput<'_>, + ) -> SigningResult> { + let sign_req: SignTransactionRequest = serde_json::from_str(&request.payload) + .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; + let transaction: VersionedTransaction = bincode::deserialize(&sign_req.transaction.0) + .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; + + let signing_input = Proto::SigningInput { + raw_message: Some(ProtoBuilder::build_from_tx(&transaction)?), + ..Proto::SigningInput::default() + }; + + Ok(WCProto::ParseRequestOutput { + signing_input_oneof: SigningInputEnum::solana(signing_input), + ..WCProto::ParseRequestOutput::default() + }) + } +} diff --git a/rust/chains/tw_solana/src/modules/wallet_connect/mod.rs b/rust/chains/tw_solana/src/modules/wallet_connect/mod.rs new file mode 100644 index 00000000000..6a72d9528b8 --- /dev/null +++ b/rust/chains/tw_solana/src/modules/wallet_connect/mod.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +pub mod connector; +pub mod request; diff --git a/rust/chains/tw_solana/src/modules/wallet_connect/request.rs b/rust/chains/tw_solana/src/modules/wallet_connect/request.rs new file mode 100644 index 00000000000..603de8e1045 --- /dev/null +++ b/rust/chains/tw_solana/src/modules/wallet_connect/request.rs @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use serde::Deserialize; +use tw_encoding::base64::Base64Encoded; + +/// `solana_signTransaction` request payload without legacy fields. +/// https://docs.walletconnect.com/advanced/rpc-reference/solana-rpc#solana_signtransaction +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SignTransactionRequest { + pub transaction: Base64Encoded, +} diff --git a/rust/chains/tw_solana/src/program/mod.rs b/rust/chains/tw_solana/src/program/mod.rs new file mode 100644 index 00000000000..bd0451b533b --- /dev/null +++ b/rust/chains/tw_solana/src/program/mod.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +pub mod stake_program; diff --git a/rust/chains/tw_solana/src/program/stake_program.rs b/rust/chains/tw_solana/src/program/stake_program.rs new file mode 100644 index 00000000000..5f2ca1886dc --- /dev/null +++ b/rust/chains/tw_solana/src/program/stake_program.rs @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::address::SolanaAddress; +use crate::blockhash::Blockhash; +use crate::defined_addresses::*; +use tw_coin_entry::error::{AddressError, AddressResult}; +use tw_hash::sha2::sha256; +use tw_hash::H256; + +pub struct StakeProgram; + +impl StakeProgram { + pub fn address_from_recent_blockhash( + from: SolanaAddress, + recent_blockhash: Blockhash, + ) -> SolanaAddress { + let mut seed = from.bytes().to_vec(); + seed.extend_from_slice(recent_blockhash.to_string().as_bytes()); + seed.extend_from_slice(STAKE_PROGRAM_ID_ADDRESS.bytes().as_slice()); + let bytes = + H256::try_from(sha256(&seed).as_slice()).expect("sha256 expected to return 32 bytes"); + SolanaAddress::with_public_key_bytes(bytes) + } + + /// https://github.com/solana-labs/solana-program-library/blob/master/associated-token-account/program/src/lib.rs#L35 + pub fn get_associated_token_address( + main_address: SolanaAddress, + token_mint_address: SolanaAddress, + ) -> AddressResult { + SolanaAddress::find_program_address( + &[ + main_address.bytes().as_slice(), + TOKEN_PROGRAM_ID_ADDRESS.bytes().as_slice(), + token_mint_address.bytes().as_slice(), + ], + *ASSOCIATED_TOKEN_PROGRAM_ID_ADDRESS, + ) + .ok_or(AddressError::InvalidInput) + } +} diff --git a/rust/chains/tw_solana/src/signer.rs b/rust/chains/tw_solana/src/signer.rs index 27633d26f26..b03ccd345d0 100644 --- a/rust/chains/tw_solana/src/signer.rs +++ b/rust/chains/tw_solana/src/signer.rs @@ -2,9 +2,15 @@ // // Copyright © 2017 Trust Wallet. +use crate::modules::message_builder::MessageBuilder; +use crate::modules::proto_builder::ProtoBuilder; +use crate::modules::tx_signer::TxSigner; +use crate::SOLANA_ALPHABET; +use std::borrow::Cow; use tw_coin_entry::coin_context::CoinContext; -use tw_coin_entry::error::SigningResult; +use tw_coin_entry::error::{SigningError, SigningErrorType, SigningResult}; use tw_coin_entry::signing_output_error; +use tw_encoding::{base58, base64}; use tw_proto::Solana::Proto; pub struct SolanaSigner; @@ -20,8 +26,34 @@ impl SolanaSigner { fn sign_impl( _coin: &dyn CoinContext, - _input: Proto::SigningInput<'_>, + input: Proto::SigningInput<'_>, ) -> SigningResult> { - todo!() + let encode = move |data| match input.tx_encoding { + Proto::Encoding::Base58 => base58::encode(data, &SOLANA_ALPHABET), + Proto::Encoding::Base64 => base64::encode(data, false), + }; + + let builder = MessageBuilder::new(input); + let signing_keys = builder.signing_keys()?; + let external_signatures = builder.external_signatures()?; + let unsigned_msg = builder.build()?; + + let encoded_unsigned = bincode::serialize(&unsigned_msg) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let encoded_unsigned = encode(&encoded_unsigned); + + let signed_tx = + TxSigner::sign_versioned(unsigned_msg, &signing_keys, &external_signatures)?; + + let encoded_tx = bincode::serialize(&signed_tx) + .map_err(|_| SigningError(SigningErrorType::Error_internal))?; + let encoded_tx = encode(&encoded_tx); + + Ok(Proto::SigningOutput { + encoded: Cow::from(encoded_tx), + unsigned_tx: Cow::from(encoded_unsigned), + signatures: ProtoBuilder::build_signatures(&signed_tx), + ..Proto::SigningOutput::default() + }) } } diff --git a/rust/chains/tw_solana/src/transaction/legacy.rs b/rust/chains/tw_solana/src/transaction/legacy.rs index 94be08e1bc7..dae3bf13949 100644 --- a/rust/chains/tw_solana/src/transaction/legacy.rs +++ b/rust/chains/tw_solana/src/transaction/legacy.rs @@ -2,7 +2,8 @@ // // Copyright © 2017 Trust Wallet. -use crate::transaction::{short_vec, CompiledInstruction, MessageHeader, Pubkey, Signature}; +use crate::address::SolanaAddress; +use crate::transaction::{short_vec, CompiledInstruction, MessageHeader, Signature}; use serde::{Deserialize, Serialize}; use tw_hash::{as_byte_sequence, H256}; @@ -15,7 +16,7 @@ pub struct Message { /// All the account keys used by this transaction. #[serde(with = "short_vec")] - pub account_keys: Vec, + pub account_keys: Vec, /// The id of a recent ledger entry. #[serde(with = "as_byte_sequence")] @@ -44,12 +45,3 @@ pub struct Transaction { /// The message to sign. pub message: Message, } - -impl Transaction { - /// Zeroize signatures before re-signing... - pub fn zeroize_signatures(&mut self) { - self.signatures - .iter_mut() - .for_each(|signature| *signature = Signature::default()); - } -} diff --git a/rust/chains/tw_solana/src/transaction/mod.rs b/rust/chains/tw_solana/src/transaction/mod.rs index 5da19f1c730..b5848ba006e 100644 --- a/rust/chains/tw_solana/src/transaction/mod.rs +++ b/rust/chains/tw_solana/src/transaction/mod.rs @@ -2,17 +2,19 @@ // // Copyright © 2017 Trust Wallet. +use crate::SOLANA_ALPHABET; use serde::{Deserialize, Serialize}; -use tw_hash::{as_byte_sequence, H256, H512}; +use std::fmt; +use std::str::FromStr; +use tw_coin_entry::error::{SigningError, SigningErrorType}; +use tw_encoding::base58; +use tw_hash::{as_byte_sequence, H512}; pub mod legacy; pub mod short_vec; pub mod v0; pub mod versioned; -#[derive(Clone, Copy, Default, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub struct Pubkey(#[serde(with = "as_byte_sequence")] pub(crate) H256); - #[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone, Copy)] #[serde(rename_all = "camelCase")] pub struct MessageHeader { @@ -45,7 +47,25 @@ pub struct CompiledInstruction { } #[derive(Serialize, Deserialize, Debug, Default, Clone, Copy, Eq, PartialEq, Hash)] -pub struct Signature(#[serde(with = "as_byte_sequence")] pub(crate) H512); +pub struct Signature(#[serde(with = "as_byte_sequence")] pub H512); + +impl FromStr for Signature { + type Err = SigningError; + + fn from_str(s: &str) -> Result { + let data = base58::decode(s, &SOLANA_ALPHABET) + .map_err(|_| SigningError(SigningErrorType::Error_input_parse))?; + H512::try_from(data.as_slice()) + .map(Signature) + .map_err(|_| SigningError(SigningErrorType::Error_input_parse)) + } +} + +impl fmt::Display for Signature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", base58::encode(self.0.as_slice(), &SOLANA_ALPHABET)) + } +} #[cfg(test)] mod tests { @@ -54,15 +74,11 @@ mod tests { use crate::transaction::v0::MessageAddressTableLookup; use crate::transaction::versioned::{VersionedMessage, VersionedTransaction}; use crate::SOLANA_ALPHABET; - use std::str::FromStr; use tw_encoding::hex::ToHex; use tw_encoding::{base58, base64}; + use tw_hash::H256; use tw_memory::Data; - fn address_pubkey(addr: &'static str) -> Pubkey { - Pubkey(SolanaAddress::from_str(addr).unwrap().bytes()) - } - fn base58_decode(s: &'static str) -> Data { base58::decode(s, &SOLANA_ALPHABET).unwrap() } @@ -86,22 +102,22 @@ mod tests { num_readonly_unsigned_accounts: 7, }, account_keys: vec![ - address_pubkey("AHy6YZA8BsHgQfVkk7MbwpAN94iyN7Nf1zN4nPqUN32Q"), - address_pubkey("g7dD1FHSemkUQrX1Eak37wzvDjscgBW2pFCENwjLdMX"), - address_pubkey("7m57LBTxtzhWn6WdFxKtnoJLBQXyNERLYebebXLVaKy3"), - address_pubkey("AEBCPtV8FFkWFAKxrz7mbYvobpkZuWaRWQCyJVRaheUD"), - address_pubkey("BND2ehwWVeHVA5EtMm2b7Vu51AT8f2PNWusS9KQX5moy"), - address_pubkey("DVCeozFGbe6ew3eWTnZByjHeYqTq1cvbrB7JJhkLxaRJ"), - address_pubkey("GvgWmk8iPACw1AEMt47WzkuTkKoSGbn4Xk3aLM8vdbJD"), - address_pubkey("HkphEpUqnFBxBuCPEq5j1HA9L8EwmsmRT6UcFKziptM1"), - address_pubkey("Hzxx6b5a7dmmJeDXLQzr4dTrc2HGK9ar5YRakZgr3ZZ7"), - address_pubkey("11111111111111111111111111111111"), - address_pubkey("ComputeBudget111111111111111111111111111111"), - address_pubkey("JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"), - address_pubkey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), - address_pubkey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"), - address_pubkey("D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"), - address_pubkey("GGztQqQ6pCPaJQnNpXBgELr5cs3WwDakRbh1iEMzjgSJ"), + SolanaAddress::from("AHy6YZA8BsHgQfVkk7MbwpAN94iyN7Nf1zN4nPqUN32Q"), + SolanaAddress::from("g7dD1FHSemkUQrX1Eak37wzvDjscgBW2pFCENwjLdMX"), + SolanaAddress::from("7m57LBTxtzhWn6WdFxKtnoJLBQXyNERLYebebXLVaKy3"), + SolanaAddress::from("AEBCPtV8FFkWFAKxrz7mbYvobpkZuWaRWQCyJVRaheUD"), + SolanaAddress::from("BND2ehwWVeHVA5EtMm2b7Vu51AT8f2PNWusS9KQX5moy"), + SolanaAddress::from("DVCeozFGbe6ew3eWTnZByjHeYqTq1cvbrB7JJhkLxaRJ"), + SolanaAddress::from("GvgWmk8iPACw1AEMt47WzkuTkKoSGbn4Xk3aLM8vdbJD"), + SolanaAddress::from("HkphEpUqnFBxBuCPEq5j1HA9L8EwmsmRT6UcFKziptM1"), + SolanaAddress::from("Hzxx6b5a7dmmJeDXLQzr4dTrc2HGK9ar5YRakZgr3ZZ7"), + SolanaAddress::from("11111111111111111111111111111111"), + SolanaAddress::from("ComputeBudget111111111111111111111111111111"), + SolanaAddress::from("JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"), + SolanaAddress::from("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), + SolanaAddress::from("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"), + SolanaAddress::from("D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"), + SolanaAddress::from("GGztQqQ6pCPaJQnNpXBgELr5cs3WwDakRbh1iEMzjgSJ"), ], recent_blockhash: base58_decode_h256( "DiSimxK2z1cRa6yD4goqte3rDMmghJAD8WDUZEab2CzD", @@ -151,17 +167,23 @@ mod tests { ], address_table_lookups: vec![ MessageAddressTableLookup { - account_key: address_pubkey("FeXRmSWmwChZbB2EC7Qjw9XKk28yBrPj3k3nzT1DKfak"), + account_key: SolanaAddress::from( + "FeXRmSWmwChZbB2EC7Qjw9XKk28yBrPj3k3nzT1DKfak", + ), writable_indexes: vec![202, 200, 201], readonly_indexes: vec![196, 197, 36, 199], }, MessageAddressTableLookup { - account_key: address_pubkey("5cFsmTCEfmvpBUBHqsWZnf9n5vTWLYH2LT8X7HdShwxP"), + account_key: SolanaAddress::from( + "5cFsmTCEfmvpBUBHqsWZnf9n5vTWLYH2LT8X7HdShwxP", + ), writable_indexes: vec![160, 245, 248, 159, 157], readonly_indexes: vec![156, 244, 246, 247], }, MessageAddressTableLookup { - account_key: address_pubkey("HJ5StCvsDU4JsvK39VcsHjaoTRTtQU749MQ9qUsJaG1m"), + account_key: SolanaAddress::from( + "HJ5StCvsDU4JsvK39VcsHjaoTRTtQU749MQ9qUsJaG1m", + ), writable_indexes: vec![122, 121, 125], readonly_indexes: vec![110, 126], }, diff --git a/rust/chains/tw_solana/src/transaction/v0.rs b/rust/chains/tw_solana/src/transaction/v0.rs index 18f70a40ef4..8b1a67d0cc9 100644 --- a/rust/chains/tw_solana/src/transaction/v0.rs +++ b/rust/chains/tw_solana/src/transaction/v0.rs @@ -2,7 +2,8 @@ // // Copyright © 2017 Trust Wallet. -use crate::transaction::{short_vec, CompiledInstruction, MessageHeader, Pubkey}; +use crate::address::SolanaAddress; +use crate::transaction::{short_vec, CompiledInstruction, MessageHeader}; use serde::{Deserialize, Serialize}; use tw_hash::{as_byte_sequence, H256}; @@ -10,7 +11,7 @@ use tw_hash::{as_byte_sequence, H256}; #[serde(rename_all = "camelCase")] pub struct MessageAddressTableLookup { /// Address lookup table account key - pub account_key: Pubkey, + pub account_key: SolanaAddress, /// List of indexes used to load writable account addresses #[serde(with = "short_vec")] pub writable_indexes: Vec, @@ -29,7 +30,7 @@ pub struct Message { /// List of accounts loaded by this transaction. #[serde(with = "short_vec")] - pub account_keys: Vec, + pub account_keys: Vec, /// The blockhash of a recent block. #[serde(with = "as_byte_sequence")] diff --git a/rust/chains/tw_solana/src/transaction/versioned.rs b/rust/chains/tw_solana/src/transaction/versioned.rs index ffe94c9d76d..71282340222 100644 --- a/rust/chains/tw_solana/src/transaction/versioned.rs +++ b/rust/chains/tw_solana/src/transaction/versioned.rs @@ -4,9 +4,9 @@ //! Source code: https://github.com/solana-labs/solana/blob/a16f982169eb197fad0eb8c58c307fb069f69d8f/sdk/program/src/message/versions/mod.rs -use crate::transaction::{ - legacy, short_vec, v0, CompiledInstruction, MessageHeader, Pubkey, Signature, -}; +use crate::address::SolanaAddress; +use crate::blockhash::Blockhash; +use crate::transaction::{legacy, short_vec, v0, CompiledInstruction, MessageHeader, Signature}; use serde::de::{SeqAccess, Unexpected, Visitor}; use serde::ser::SerializeTuple; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; @@ -28,10 +28,11 @@ pub struct VersionedTransaction { } impl VersionedTransaction { - /// Fill the signatures up with zeroed signatures - /// (same number of signatures as [`VersionedTransaction::num_required_signatures`]). - pub fn zeroize_signatures(&mut self) { - self.signatures = vec![Signature::default(); self.message.num_required_signatures()]; + pub fn unsigned(message: VersionedMessage) -> VersionedTransaction { + VersionedTransaction { + signatures: vec![Signature::default(); message.num_required_signatures()], + message, + } } } @@ -50,6 +51,34 @@ pub enum VersionedMessage { } impl VersionedMessage { + pub fn header(&self) -> &MessageHeader { + match self { + VersionedMessage::Legacy(legacy) => &legacy.header, + VersionedMessage::V0(v0) => &v0.header, + } + } + + pub fn account_keys(&self) -> &[SolanaAddress] { + match self { + VersionedMessage::Legacy(legacy) => &legacy.account_keys, + VersionedMessage::V0(v0) => &v0.account_keys, + } + } + + pub fn recent_blockhash(&self) -> Blockhash { + match self { + VersionedMessage::Legacy(legacy) => Blockhash::with_bytes(legacy.recent_blockhash), + VersionedMessage::V0(v0) => Blockhash::with_bytes(v0.recent_blockhash), + } + } + + pub fn instructions(&self) -> &[CompiledInstruction] { + match self { + VersionedMessage::Legacy(legacy) => &legacy.instructions, + VersionedMessage::V0(v0) => &v0.instructions, + } + } + pub fn num_required_signatures(&self) -> usize { match self { VersionedMessage::Legacy(legacy) => legacy.header.num_required_signatures as usize, @@ -57,12 +86,12 @@ impl VersionedMessage { } } - pub fn get_account_index(&self, account_pubkey: Pubkey) -> Option { + pub fn get_account_index(&self, account: SolanaAddress) -> Option { let account_keys = match self { VersionedMessage::Legacy(legacy) => &legacy.account_keys, VersionedMessage::V0(v0) => &v0.account_keys, }; - account_keys.iter().position(|pk| *pk == account_pubkey) + account_keys.iter().position(|pk| *pk == account) } pub fn set_recent_blockhash(&mut self, recent_blockhash: H256) { @@ -165,7 +194,7 @@ impl<'de> Deserialize<'de> for VersionedMessage { pub num_readonly_signed_accounts: u8, pub num_readonly_unsigned_accounts: u8, #[serde(with = "short_vec")] - pub account_keys: Vec, + pub account_keys: Vec, #[serde(with = "as_byte_sequence")] pub recent_blockhash: H256, #[serde(with = "short_vec")] diff --git a/rust/chains/tw_solana/tests/get_default_token_address.rs b/rust/chains/tw_solana/tests/get_default_token_address.rs new file mode 100644 index 00000000000..94c3358ed5a --- /dev/null +++ b/rust/chains/tw_solana/tests/get_default_token_address.rs @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use std::str::FromStr; +use tw_solana::address::SolanaAddress; +use tw_solana::program::stake_program::StakeProgram; + +fn test_get_default_token_address_impl( + main_address: &str, + token_mint_address: &str, + expected: &str, +) { + let main_address = SolanaAddress::from_str(main_address).unwrap(); + let token_mint_address = SolanaAddress::from_str(token_mint_address).unwrap(); + let expected = SolanaAddress::from_str(expected).unwrap(); + + let actual = StakeProgram::get_associated_token_address(main_address, token_mint_address) + .expect("!get_associated_token_address"); + assert_eq!(actual, expected); +} + +#[test] +fn test_get_default_token_address() { + test_get_default_token_address_impl( + "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V", + "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt", + "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP", + ); + test_get_default_token_address_impl( + "FtsZbVDYGBUw4R5Rcy8p58RAdYRFUJcAiBRdQAona7t1", + "FQYWEccPpAVR5Q16FKoTFUa6z8BWzdK5eh3D586fdQbM", + "2XGUJePx3CJSYQvAQcSgLf7xpjCdMUgx8LNaSCUhE2LS", + ); + test_get_default_token_address_impl( + "74nsHsFivzUPLEygULuZLs193MnDNZfnKrSEkgA4qkY7", + "FQYWEccPpAVR5Q16FKoTFUa6z8BWzdK5eh3D586fdQbM", + "823YRj6GozU23rwSyd1f78vPopVoQPCeK79ppWCu5SYQ", + ); + test_get_default_token_address_impl( + "HBYC51YrGFAZ8rM7Sj8e9uqKggpSrDYrinQDZzvMtqQp", + "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt", + "6X4X1Ae24mkoWeCEpktevySVG9jzeCufut5vtUW3wFrD", + ); + test_get_default_token_address_impl( + "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ", + "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt", + "ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf", + ); +} diff --git a/rust/chains/tw_thorchain/src/entry.rs b/rust/chains/tw_thorchain/src/entry.rs index 838c10bd526..25f0316984a 100644 --- a/rust/chains/tw_thorchain/src/entry.rs +++ b/rust/chains/tw_thorchain/src/entry.rs @@ -12,6 +12,7 @@ use tw_coin_entry::error::AddressResult; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_cosmos_sdk::address::{Address, Bech32Prefix}; use tw_keypair::tw; @@ -26,10 +27,13 @@ impl CoinEntry for ThorchainEntry { type SigningInput<'a> = Proto::SigningInput<'a>; type SigningOutput = Proto::SigningOutput<'static>; type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + + // Optional modules: type JsonSigner = NoJsonSigner; type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; type WalletConnector = NoWalletConnector; + type TransactionDecoder = NoTransactionDecoder; #[inline] fn parse_address( diff --git a/rust/tw_any_coin/src/ffi/mod.rs b/rust/tw_any_coin/src/ffi/mod.rs index 8db000e6d80..8d422f37de4 100644 --- a/rust/tw_any_coin/src/ffi/mod.rs +++ b/rust/tw_any_coin/src/ffi/mod.rs @@ -6,4 +6,5 @@ pub mod tw_any_address; pub mod tw_any_signer; pub mod tw_message_signer; pub mod tw_transaction_compiler; +pub mod tw_transaction_decoder; pub mod tw_wallet_connect_request; diff --git a/rust/tw_any_coin/src/ffi/tw_transaction_decoder.rs b/rust/tw_any_coin/src/ffi/tw_transaction_decoder.rs new file mode 100644 index 00000000000..d5beb6d6a64 --- /dev/null +++ b/rust/tw_any_coin/src/ffi/tw_transaction_decoder.rs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#![allow(clippy::missing_safety_doc)] + +use crate::transaction_decoder::TransactionDecoder; +use tw_coin_registry::coin_type::CoinType; +use tw_memory::ffi::tw_data::TWData; +use tw_memory::ffi::RawPtrTrait; +use tw_misc::try_or_else; + +/// Decodes a transaction from a binary representation. +/// +/// \param coin coin type. +/// \param tx encoded transaction data. +/// \return serialized protobuf message specific for the given coin. +#[no_mangle] +pub unsafe extern "C" fn tw_transaction_decoder_decode( + coin: u32, + tx: *const TWData, +) -> *mut TWData { + let tx = try_or_else!(TWData::from_ptr_as_ref(tx), std::ptr::null_mut); + let coin = try_or_else!(CoinType::try_from(coin), std::ptr::null_mut); + + TransactionDecoder::decode_transaction(coin, tx.as_slice()) + .map(|output| TWData::from(output).into_ptr()) + .unwrap_or_else(|_| std::ptr::null_mut()) +} diff --git a/rust/tw_any_coin/src/lib.rs b/rust/tw_any_coin/src/lib.rs index 84ca053fa3b..412873c0095 100644 --- a/rust/tw_any_coin/src/lib.rs +++ b/rust/tw_any_coin/src/lib.rs @@ -7,6 +7,7 @@ pub mod any_signer; pub mod ffi; pub mod message_signer; pub mod transaction_compiler; +pub mod transaction_decoder; pub mod wallet_connect_request; #[cfg(feature = "test-utils")] diff --git a/rust/tw_any_coin/src/test_utils/mod.rs b/rust/tw_any_coin/src/test_utils/mod.rs index 7c86db82edc..837298bd147 100644 --- a/rust/tw_any_coin/src/test_utils/mod.rs +++ b/rust/tw_any_coin/src/test_utils/mod.rs @@ -4,4 +4,5 @@ pub mod address_utils; pub mod sign_utils; +pub mod transaction_decode_utils; pub mod wallet_connect_utils; diff --git a/rust/tw_any_coin/src/test_utils/transaction_decode_utils.rs b/rust/tw_any_coin/src/test_utils/transaction_decode_utils.rs new file mode 100644 index 00000000000..7e232ce14f9 --- /dev/null +++ b/rust/tw_any_coin/src/test_utils/transaction_decode_utils.rs @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::ffi::tw_transaction_decoder::tw_transaction_decoder_decode; +use std::marker::PhantomData; +use tw_coin_registry::coin_type::CoinType; +use tw_memory::test_utils::tw_data_helper::TWDataHelper; +use tw_memory::Data; +use tw_proto::{deserialize, MessageRead}; + +#[derive(Default)] +pub struct TransactionDecoderHelper<'a, Output: MessageRead<'a>> { + output_data: Data, + _output_type: PhantomData<&'a Output>, +} + +impl<'a, Output: MessageRead<'a>> TransactionDecoderHelper<'a, Output> { + pub fn decode(&'a mut self, coin_type: CoinType, tx: Data) -> Output { + let tx_data = TWDataHelper::create(tx); + + self.output_data = TWDataHelper::wrap(unsafe { + tw_transaction_decoder_decode(coin_type as u32, tx_data.ptr()) + }) + .to_vec() + .expect("!tw_transaction_decoder_decode returned nullptr"); + + let output: Output = deserialize(&self.output_data).unwrap(); + output + } +} diff --git a/rust/tw_any_coin/src/transaction_decoder.rs b/rust/tw_any_coin/src/transaction_decoder.rs new file mode 100644 index 00000000000..fcc10c5dec4 --- /dev/null +++ b/rust/tw_any_coin/src/transaction_decoder.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_coin_entry::error::SigningResult; +use tw_coin_registry::coin_type::CoinType; +use tw_coin_registry::dispatcher::coin_dispatcher; +use tw_memory::Data; + +pub struct TransactionDecoder; + +impl TransactionDecoder { + /// Decodes a transaction from a binary representation. + #[inline] + pub fn decode_transaction(coin: CoinType, tx: &[u8]) -> SigningResult { + let (ctx, entry) = coin_dispatcher(coin)?; + entry.decode_transaction(&ctx, tx) + } +} diff --git a/rust/tw_any_coin/tests/chains/solana/mod.rs b/rust/tw_any_coin/tests/chains/solana/mod.rs index 2b199e6a807..069e9709509 100644 --- a/rust/tw_any_coin/tests/chains/solana/mod.rs +++ b/rust/tw_any_coin/tests/chains/solana/mod.rs @@ -5,3 +5,5 @@ mod solana_address; mod solana_compile; mod solana_sign; +mod solana_transaction; +mod solana_wallet_connect; diff --git a/rust/tw_any_coin/tests/chains/solana/solana_compile.rs b/rust/tw_any_coin/tests/chains/solana/solana_compile.rs index 84b9a412183..a2f6b0cfadf 100644 --- a/rust/tw_any_coin/tests/chains/solana/solana_compile.rs +++ b/rust/tw_any_coin/tests/chains/solana/solana_compile.rs @@ -2,8 +2,344 @@ // // Copyright © 2017 Trust Wallet. +use tw_any_coin::test_utils::sign_utils::{CompilerHelper, PreImageHelper}; +use tw_coin_registry::coin_type::CoinType; +use tw_encoding::base58::{self, Alphabet}; +use tw_encoding::hex::{DecodeHex, ToHex}; +use tw_proto::Common::Proto::SigningError; +use tw_proto::Solana::Proto::{self, mod_SigningInput::OneOftransaction_type as TransactionType}; + +#[test] +fn test_solana_compile_transfer() { + let transfer = Proto::Transfer { + recipient: "3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe".into(), + value: 1000, + ..Proto::Transfer::default() + }; + let input = Proto::SigningInput { + sender: "sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH".into(), + recent_blockhash: "TPJFTN4CjBn12HiBfAbGUhpD9zGvRSm2RcheFRA4Fyv".into(), + transaction_type: TransactionType::transfer_transaction(transfer), + ..Proto::SigningInput::default() + }; + + // Step 2: Obtain preimage hash + let mut pre_imager = PreImageHelper::::default(); + let preimage_output = pre_imager.pre_image_hashes(CoinType::Solana, &input); + + assert_eq!(preimage_output.error, SigningError::OK); + assert_eq!( + preimage_output.data.to_hex(), + "010001030d044a62d0a4dfe5a037a15b59fa4d4d0d3ab81103a2c10a6da08a4d058611c024c255a8bc3e8496217a2cd2a1894b9b9dcace04fcd9c0d599acdaaea40a1b61000000000000000000000000000000000000000000000000000000000000000006c25012cc11a599a45b3b2f7f8a7c65b0547fa0bb67170d7a0cd1eda4e2c9e501020200010c02000000e803000000000000" + ); + + // Step 3: Compile transaction info + + // Simulate signature, normally obtained from signature server. + let signature = "a8c610697087eaf8a34b3facbe06f8e9bb9603bb03270dad021ffcd2fc37b6e9efcdcb78b227401f000eb9231c67685240890962e44a17fd27fc2ff7b971df03".decode_hex().unwrap(); + let public_key = "0d044a62d0a4dfe5a037a15b59fa4d4d0d3ab81103a2c10a6da08a4d058611c0" + .decode_hex() + .unwrap(); + + let mut compiler = CompilerHelper::::default(); + let output = compiler.compile(CoinType::Solana, &input, vec![signature], vec![public_key]); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "5bWxVCP5fuzkKSGby9hnsLranszQJR2evJGTfBrpDQ4rJceW1WxKNrWqVPBsN2QCAGmE6W7VaYkyWjv39HhGrr1Ne2QSUuHZdyyn7hK4pxzLPMgPG8fY1XvXdppWMaKMLmhriLkckzGKJMaE3pWBRFBKzigXY28714uUNndb7S9hVakxa59hrLph39CMgAkcj6b8KYvJEkb1YdYytHSZNGi4kVVTNqiicNgPdf1gmG6qz9zVtnqj9JtaD2efdS8qxsKnvNWSgb8XxbT6dwyp7msUUi7d27cYaPTpK"); + assert_eq!(output.unsigned_tx, "87PYr2vKPjNPfwNmqTvhgkThhohTqFNKYJgCHcrUCeayX6daQs9AFvMA698MG9TknbSnUxNXaNaReatkevLDgiTG5FqcBgVHG5PLPrq3PCdKPLjAN9RMQJXM5i6KaVMDzGJGMfgSFMS4ecEjqumZX4nux9rhG4jpYaQbe5sgyYUetwMmemoNiCgW2qCFsGnTYR9rWSU7S9zF"); +} + +#[test] +fn test_solana_compile_create_nonce_account() { + let nonce_account = "6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ"; + let sender = "sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH"; + + let create_nonce_account = Proto::CreateNonceAccount { + nonce_account: nonce_account.into(), + rent: 10000000, + ..Proto::CreateNonceAccount::default() + }; + let input = Proto::SigningInput { + sender: sender.into(), + recent_blockhash: "mFmK2xFMhzJJaUN5cctfdCizE9dtgcSASSEDh1Yzmat".into(), + transaction_type: TransactionType::create_nonce_account(create_nonce_account), + tx_encoding: Proto::Encoding::Base64, + ..Proto::SigningInput::default() + }; + + // Step 2: Obtain preimage hash + let mut pre_imager = PreImageHelper::::default(); + let preimage_output = pre_imager.pre_image_hashes(CoinType::Solana, &input); + + assert_eq!(preimage_output.error, SigningError::OK); + assert_eq!( + preimage_output.data.to_hex(), + "020003050d044a62d0a4dfe5a037a15b59fa4d4d0d3ab81103a2c10a6da08a4d058611c057f6ed937bb447a6700c9684d2e182b1a6661838a86cca7d0aac18be2e098b2106a7d517192c568ee08a845f73d29788cf035c3145b21ab344d8062ea940000006a7d517192c5c51218cc94c3d4af17f58daee089ba1fd44e3dbd98a0000000000000000000000000000000000000000000000000000000000000000000000000b563fd13b46e844f12f54fa8a0e78c44d95dbae4953368b7135f1e0de111cb502040200013400000000809698000000000050000000000000000000000000000000000000000000000000000000000000000000000000000000040301020324060000000d044a62d0a4dfe5a037a15b59fa4d4d0d3ab81103a2c10a6da08a4d058611c0" + ); + + // Step 3: Compile transaction info + + // Simulate signature, normally obtained from signature server. + let sender_signature = "83985a7cb1ba60a11c361a49476f10316d70032d5e2b4d882250d4de1a841f3dbaf9e68b036b067ec4f56c04032672229777ad101e9d3d51d6d917994b767e0e".decode_hex().unwrap(); + let nonce_signature = "24708efdba7e33259607597396b665baea3fc51d71e19a963cccad4bbdd2770e42e4831d1cc037afd02065a09e8c2b5e04f63da20f200e6aff689ad3b2bf7f0a".decode_hex().unwrap(); + let sender_pubkey = base58::decode(sender, Alphabet::BITCOIN).unwrap(); + let nonce_pubkey = base58::decode(nonce_account, Alphabet::BITCOIN).unwrap(); + + let mut compiler = CompilerHelper::::default(); + let output = compiler.compile( + CoinType::Solana, + &input, + vec![sender_signature, nonce_signature], + vec![sender_pubkey, nonce_pubkey], + ); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "AoOYWnyxumChHDYaSUdvEDFtcAMtXitNiCJQ1N4ahB89uvnmiwNrBn7E9WwEAyZyIpd3rRAenT1R1tkXmUt2fg4kcI79un4zJZYHWXOWtmW66j/FHXHhmpY8zK1LvdJ3DkLkgx0cwDev0CBloJ6MK14E9j2iDyAOav9omtOyv38KAgADBQ0ESmLQpN/loDehW1n6TU0NOrgRA6LBCm2gik0FhhHAV/btk3u0R6ZwDJaE0uGCsaZmGDiobMp9CqwYvi4JiyEGp9UXGSxWjuCKhF9z0peIzwNcMUWyGrNE2AYuqUAAAAan1RcZLFxRIYzJTD1K8X9Y2u4Im6H9ROPb2YoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALVj/RO0boRPEvVPqKDnjETZXbrklTNotxNfHg3hEctQIEAgABNAAAAACAlpgAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAwECAyQGAAAADQRKYtCk3+WgN6FbWfpNTQ06uBEDosEKbaCKTQWGEcA="); + assert_eq!(output.unsigned_tx, "AgADBQ0ESmLQpN/loDehW1n6TU0NOrgRA6LBCm2gik0FhhHAV/btk3u0R6ZwDJaE0uGCsaZmGDiobMp9CqwYvi4JiyEGp9UXGSxWjuCKhF9z0peIzwNcMUWyGrNE2AYuqUAAAAan1RcZLFxRIYzJTD1K8X9Y2u4Im6H9ROPb2YoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALVj/RO0boRPEvVPqKDnjETZXbrklTNotxNfHg3hEctQIEAgABNAAAAACAlpgAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAwECAyQGAAAADQRKYtCk3+WgN6FbWfpNTQ06uBEDosEKbaCKTQWGEcA="); +} + #[test] -#[ignore] -fn test_solana_compile() { - todo!() +fn test_solana_compile_withdraw_nonce_account() { + let sender = "sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH"; + + let withdraw_nonce = Proto::WithdrawNonceAccount { + nonce_account: "6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ".into(), + recipient: "3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe".into(), + value: 10000000, + }; + let input = Proto::SigningInput { + sender: sender.into(), + recent_blockhash: "5ccb7sRth3CP8fghmarFycr6VQX3NcfyDJsMFtmdkdU8".into(), + transaction_type: TransactionType::withdraw_nonce_account(withdraw_nonce), + ..Proto::SigningInput::default() + }; + + // Step 2: Obtain preimage hash + let mut pre_imager = PreImageHelper::::default(); + let preimage_output = pre_imager.pre_image_hashes(CoinType::Solana, &input); + + assert_eq!(preimage_output.error, SigningError::OK); + assert_eq!( + preimage_output.data.to_hex(), + "010003060d044a62d0a4dfe5a037a15b59fa4d4d0d3ab81103a2c10a6da08a4d058611c057f6ed937bb447a6700c9684d2e182b1a6661838a86cca7d0aac18be2e098b2124c255a8bc3e8496217a2cd2a1894b9b9dcace04fcd9c0d599acdaaea40a1b6106a7d517192c568ee08a845f73d29788cf035c3145b21ab344d8062ea940000006a7d517192c5c51218cc94c3d4af17f58daee089ba1fd44e3dbd98a000000000000000000000000000000000000000000000000000000000000000000000000448e50d73f42e3163f5926922aadd2bca6bdd91f97b3eb7b750e2cecfd810f6d01050501020304000c050000008096980000000000" + ); + + // Step 3: Compile transaction info + + // Simulate signature, normally obtained from signature server. + let signature = "1212e67f30fc9f17a440ef446c3e8638676b8253d3dc4517e297a777c011f820509c5d102e5648827e2a6a4b6274943c9209f44d2dee31277eaa6c54afd0410c".decode_hex().unwrap(); + let pubkey = base58::decode(sender, Alphabet::BITCOIN).unwrap(); + + let mut compiler = CompilerHelper::::default(); + let output = compiler.compile(CoinType::Solana, &input, vec![signature], vec![pubkey]); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "7gdEdDymvtfPfVgVvCTPzafmZc1Z8Zu4uXgJDLm8KGpLyPHysxFGjtFzimZDmGtNhQCh22Ygv3ZtPZmSbANbafikR3S1tvujatHW9gMo35jveq7TxwcGoNSqc7tnH85hkEZwnDryVaiKRvtCeH3dgFE9YqPHxiBuZT5eChHJvVNb9iTTdMsJXMusRtzeRV45CvrLKUvsAH7SSWHYW6bGow5TbEJie4buuz2rnbeVG5cxaZ6vyG2nJWHNuDPWZJTRi1MFEwHoxst3a5jQPv9UrG9rNZFCw4uZizVcG6HEqHWgQBu8gVpYpzFCX5SrhjGPZpbK3YmHhUEMEpJx3Fn7jX7Kt4t3hhhrieXppoqKNuqjeNVjfEf3Q8dJRfuVMLdXYbmitCVTPQzYKWBR6ERqWLYoAVqjoAS2pRUw1nrqi1HR"); +} + +#[test] +fn test_solana_compile_create_and_transfer_token() { + let sender = "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"; + + let create_transfer_token = Proto::CreateAndTransferToken { + recipient_main_address: "3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei".into(), + token_mint_address: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU".into(), + recipient_token_address: "BwTAyrCEdkjNyGSGVNSSGh6phn8KQaLN638Evj7PVQfJ".into(), + sender_token_address: "5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf".into(), + amount: 4000, + decimals: 6, + ..Proto::CreateAndTransferToken::default() + }; + let input = Proto::SigningInput { + sender: sender.into(), + recent_blockhash: "AfzzEC8NVXoxKoHdjXLDVzqwqvvZmgPuqyJqjuHiPY1D".into(), + transaction_type: TransactionType::create_and_transfer_token_transaction( + create_transfer_token, + ), + ..Proto::SigningInput::default() + }; + + // Step 2: Obtain preimage hash + let mut pre_imager = PreImageHelper::::default(); + let preimage_output = pre_imager.pre_image_hashes(CoinType::Solana, &input); + + assert_eq!(preimage_output.error, SigningError::OK); + assert_eq!( + preimage_output.data.to_hex(), + "0100060994c3890fa8d4bc04ab2a676d2cafea5cdc899ecd95a9cbe593e9df258759685aa287d36838b4ef65c2c460d1a52498453c259cd6a35ca91064aaead28187ca69485a24ffb4070461bb6d7f1c8b758c6b2dc90029d551b5fd4eacd82d65e302202544558bb05c2698f88852a7925c5c0ee5ea8711ddb3fe1262150283eee811633b442cb3912157f13a933d0134282d032b5ffecd01a2dbf1b7790608df002ea7000000000000000000000000000000000000000000000000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a906a7d517192c5c51218cc94c3d4af17f58daee089ba1fd44e3dbd98a000000008c97258f4e2489f1bb3d1029148e0d830b5a1399daff1084048e7bd8dbe9f8598fb6d19edbbae20ebbc767fba1da4d4b40a4b97479fe526a82325cba7cee506802080700010304050607000604020401000a0ca00f00000000000006" + ); + + // Step 3: Compile transaction info + + // Simulate signature, normally obtained from signature server. + let signature = "28d04f30288e51c257648b8c955c661228d1e13632ee2fa366d3932e359a0d6044c66074e89d8542d2a60675679bd28a1b6eb80c273ed60a8f1cfe4fdc736b02".decode_hex().unwrap(); + let pubkey = base58::decode(sender, Alphabet::BITCOIN).unwrap(); + + let mut compiler = CompilerHelper::::default(); + let output = compiler.compile(CoinType::Solana, &input, vec![signature], vec![pubkey]); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "2qkvFTcMk9kPaHtd7idJ1gJc4zTkuYDUJsC67kXvHjv3zwEyUx92QyhhSeBjL6h3Zaisj2nvTWid2UD1N9hbg9Ty7vSHLc7mcFVvy3yJmN9tz99iLKsf15rEeKUk3crXWLtKZEpcXJurN7vrxKwjQJnVob2RjyxwVfho1oNZ72BHvqToRM1W2KbcYhxK4d9zB4QY5tR2dzgCHWqAjf9Yov3y9mPBYCQBtw2GewrVMDbg5TK81E9BaWer3BcEafc3NCnRfcFEp7ZUXsGAgJYx32uUoJPP8ByTqBsp2JWgHyZhoz1WUUYRqWKZthzotErVetjSik4h5GcXk9Nb6kzqEf4nKEZ22eyrP5dv3eZMuGUUpMYUT9uF16T72s4TTwqiWDPFkidD33tACx74JKGoDraHEvEeAPrv6iUmC675kMuAV4EtVspVc5SnKXgRWRxb4dcH3k7K4ckjSxYZwg8UhTXUgPxA936jBr2HeQuPLmNVn2muA1HfL2DnyrobUP9vHpbL3HHgM2fckeXy8LAcjnoE9TTaAKX32wo5xoMj9wJmmtcU6YbXN4KgZ"); +} + +#[test] +fn test_solana_compile_advance_nonce_account() { + let sender = "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"; + + let advance_nonce = Proto::AdvanceNonceAccount { + nonce_account: "6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ".into(), + }; + let input = Proto::SigningInput { + sender: sender.into(), + recent_blockhash: "4KQLRUfd7GEVXxAeDqwtuGTdwKd9zMfPycyAG3wJsdck".into(), + transaction_type: TransactionType::advance_nonce_account(advance_nonce), + ..Proto::SigningInput::default() + }; + + // Step 2: Obtain preimage hash + let mut pre_imager = PreImageHelper::::default(); + let preimage_output = pre_imager.pre_image_hashes(CoinType::Solana, &input); + + assert_eq!(preimage_output.error, SigningError::OK); + assert_eq!( + preimage_output.data.to_hex(), + "0100020494c3890fa8d4bc04ab2a676d2cafea5cdc899ecd95a9cbe593e9df258759685a57f6ed937bb447a6700c9684d2e182b1a6661838a86cca7d0aac18be2e098b2106a7d517192c568ee08a845f73d29788cf035c3145b21ab344d8062ea940000000000000000000000000000000000000000000000000000000000000000000003149e670959884ea98bb33bca21c9505f1fc17b1d51ca59555a5d58c93f0f9c90103030102000404000000" + ); + + // Step 3: Compile transaction info + + // Simulate signature, normally obtained from signature server. + let signature = "54772ff2be7cce175e58dde0152437382ca71f932b62497ccac624f202bbf8fb8247a8c80ba7dd5412d8316c8ab25caf48739cc677c15b97cbbbc005b3a56006".decode_hex().unwrap(); + let pubkey = base58::decode(sender, Alphabet::BITCOIN).unwrap(); + + let mut compiler = CompilerHelper::::default(); + let output = compiler.compile(CoinType::Solana, &input, vec![signature], vec![pubkey]); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "7YPgNzjCnUd2zBb6ZC6bf1YaoLjhJPHixLUdTjqMjq1YdzADJCx2wsTTBFFrqDKSHXEL6ntRq8NVJTQMGzGH5AQRKwtKtutehxesmtzkZCPY9ADZ4ijFyveLmTt7kjZXX7ZWVoUmKAqiaYsPTex728uMBSRJpV4zRw2yKGdQRHTKy2QFEb9acwLjmrbEgoyzPCarxjPhw21QZnNcy8RiYJB2mzZ9nvhrD5d2jB5TtdiroQPgTSdKFzkNEd7hJUKpqUppjDFcNHGK73FE9pCP2dKxCLH8Wfaez8bLtopjmWun9cbikxo7LZsarYzMXvxwZmerRd1"); +} + +#[test] +fn test_solana_compile_create_and_transfer_token_with_external_fee_payer() { + let sender = "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"; + let fee_payer = "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"; + + let create_transfer_token = Proto::CreateAndTransferToken { + recipient_main_address: "E54BymfdhXQtzDSGtgiJayauEMdB2gJjPqoDjzfbCXwj".into(), + token_mint_address: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU".into(), + recipient_token_address: "Ay7G7Yb7ZCebCQdG39FxD1nFNDtqFWJCBgfd5Ek7DDcS".into(), + sender_token_address: "5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf".into(), + // 0.004 + amount: 4000, + decimals: 6, + ..Proto::CreateAndTransferToken::default() + }; + let input = Proto::SigningInput { + sender: sender.into(), + recent_blockhash: "EsnN3ksLV6RLBW7qqgrvm2diwVJNTzL9QCuzm6tqWsU8".into(), + transaction_type: TransactionType::create_and_transfer_token_transaction( + create_transfer_token, + ), + fee_payer: fee_payer.into(), + ..Proto::SigningInput::default() + }; + + // Step 2: Obtain preimage hash + let mut pre_imager = PreImageHelper::::default(); + let preimage_output = pre_imager.pre_image_hashes(CoinType::Solana, &input); + + assert_eq!(preimage_output.error, SigningError::OK); + assert_eq!( + preimage_output.data.to_hex(), + "0200060acb2af089b56a557737bc1718e0cbf232cf5b02e14ee0aa7c6675233f5f6f9b5794c3890fa8d4bc04ab2a676d2cafea5cdc899ecd95a9cbe593e9df258759685a9418c9576a9c00c6bd8fc223f471573f7172488de10aa84dbf63c53a20bae717485a24ffb4070461bb6d7f1c8b758c6b2dc90029d551b5fd4eacd82d65e30220c231dc02f482980f7d9915c1ecf53374091d38c060b49487f9c5d932e077ed763b442cb3912157f13a933d0134282d032b5ffecd01a2dbf1b7790608df002ea7000000000000000000000000000000000000000000000000000000000000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a906a7d517192c5c51218cc94c3d4af17f58daee089ba1fd44e3dbd98a000000008c97258f4e2489f1bb3d1029148e0d830b5a1399daff1084048e7bd8dbe9f859ce2a4331bce3670e6ea8bedff5908c6d91f833a31a7fdeac16978c261a1801d502090700020405060708000704030502010a0ca00f00000000000006" + ); + let actual_signers: Vec<_> = preimage_output + .signers + .iter() + .map(|signer| String::from_utf8(signer.to_vec()).unwrap()) + .collect(); + assert_eq!( + actual_signers, + vec![fee_payer.to_string(), sender.to_string()] + ); + + // Step 3: Compile transaction info + + // Simulate signature, normally obtained from signature server. + let signature1 = "0567ccb81d1c52d87e6491d26a7f9e93f346a69a6f0318ab2f1d8fba210d0b034343730c7ed5d3a96a37e2eb4e97cb813eec9b1fab82b538f9cf7dd71383cd03".decode_hex().unwrap(); + let signature2 = "8aef8b94f8448e583e51b54c4935b1911bb98ebc3053a46859865451584b11e83fe1a50734afc933b973142b73a9b4892509798b2a3a6dcd851c0809704d4b0e".decode_hex().unwrap(); + let fee_payer_pubkey = base58::decode(fee_payer, Alphabet::BITCOIN).unwrap(); + let sender_pubkey = base58::decode(sender, Alphabet::BITCOIN).unwrap(); + + let mut compiler = CompilerHelper::::default(); + let output = compiler.compile( + CoinType::Solana, + &input, + vec![signature1, signature2], + vec![fee_payer_pubkey, sender_pubkey], + ); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "5sxFkQYd2FvqRU64N79A6xjJKNkgUsEEg2wKgai2NiK7A7hF3q5GYEbjQsYBG9S2MejwTENbwHzvypaa3D3cEkxvVTg19aJFWdCtXQiz42QF5fN2MuAb6eJR4KHFnzCtxxnYGtN9swZ5B5cMSPCffCRZeUTe3kooRmbTYPvSaemU6reVSM7X2beoFKPd2svrLFa8XnvhBwL9EiFWQ9WhHB2cDV7KozCnJAW9kdNDR4RbfFQxboANGo3ZGE5ddcZ6YdomATKze1TtHj2qzJEJRwxsRr3iM3iNFb4Eav5Q2n71KUriRf73mo44GQUPbQ2LvpZKf4V6M2PzxJwzBo7FiFZurPmsanT3U5efEsKnnueddbiLHedc8JXc1d3Z53sFxVGJpsGA8RR6thse9wUvaEWqXVtPbNA6NMao9DFGD6Dudza9pJXSobPc7mDHZmVmookf5vi6Lb9Y1Q4EgcEPQmbaDnKGGB6uGfZe629i3iKXRzAd2dB7mKfffhDadZ8S1eYGT3dhddV3ExRxcqDP9BAGQT3rkRw1JpeSSi7ziYMQ3vn4t3okdgQSq6rrpbPDUNG8tLSHFMAq3ydnh4Cb4ECKkYoz9SFAnXACUu4mWETxijuKMK9kHrTqPGk9weHTzobzCC8q8fcPWV3TcyUyMxsbVxh5q1p5h5tWfD9td5TZJ2HEUbTop2dA53ZF"); +} + +#[test] +fn test_solana_compile_token_transfer_with_external_fee_payer() { + let sender = "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"; + let fee_payer = "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"; + + let token_transfer = Proto::TokenTransfer { + token_mint_address: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU".into(), + sender_token_address: "5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf".into(), + recipient_token_address: "AZapcpAZtEL1gQuC87F2L1hSfAZnAvNy1hHtJ8DJzySN".into(), + // 0.004 + amount: 4000, + decimals: 6, + ..Proto::TokenTransfer::default() + }; + let input = Proto::SigningInput { + sender: sender.into(), + recent_blockhash: "GwB5uixiTQUG2Pvo6fWAaNQmz41Jt4WMEPD7b83wvHkX".into(), + transaction_type: TransactionType::token_transfer_transaction(token_transfer), + fee_payer: fee_payer.into(), + ..Proto::SigningInput::default() + }; + + // Step 2: Obtain preimage hash + let mut pre_imager = PreImageHelper::::default(); + let preimage_output = pre_imager.pre_image_hashes(CoinType::Solana, &input); + + assert_eq!(preimage_output.error, SigningError::OK); + assert_eq!( + preimage_output.data.to_hex(), + "02000206cb2af089b56a557737bc1718e0cbf232cf5b02e14ee0aa7c6675233f5f6f9b5794c3890fa8d4bc04ab2a676d2cafea5cdc899ecd95a9cbe593e9df258759685a485a24ffb4070461bb6d7f1c8b758c6b2dc90029d551b5fd4eacd82d65e302208e12027e9261a6a276b5ff00ddecfda567ff3ae510a5b47045086ad1d50cab573b442cb3912157f13a933d0134282d032b5ffecd01a2dbf1b7790608df002ea706ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a9ecc01200a43c87ad04ab8b382c0934a54e585e7dcd9cdef6d1cacd52718981c4010504020403010a0ca00f00000000000006" + ); + let actual_signers: Vec<_> = preimage_output + .signers + .iter() + .map(|signer| String::from_utf8(signer.to_vec()).unwrap()) + .collect(); + assert_eq!( + actual_signers, + vec![fee_payer.to_string(), sender.to_string()] + ); + + // Step 3: Compile transaction info + + // Simulate signature, normally obtained from signature server. + let signature1 = "42eb5f9c2203aaaa1f58c38161da982fd5a855562fe75f12808fdec3bb742f3a688cf806f55588b4c533cd7e19bacc468497a8f19ad86fe06fe4dbb67a526c08".decode_hex().unwrap(); + let signature2 = "54eac822f984d4a23620546059890652d0540d7a73e6ca450d34ae046e7d0fd25d9bd6fa041a82f5573469196b2c46088997b6de97a8d20e28ba46b07333ec02".decode_hex().unwrap(); + let fee_payer_pubkey = base58::decode(fee_payer, Alphabet::BITCOIN).unwrap(); + let sender_pubkey = base58::decode(sender, Alphabet::BITCOIN).unwrap(); + + let mut compiler = CompilerHelper::::default(); + let output = compiler.compile( + CoinType::Solana, + &input, + vec![signature1, signature2], + vec![fee_payer_pubkey, sender_pubkey], + ); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "qjgNVBmoPDHNTN2ENQfxNVE57jWXpqdmu5GQX4msA7iK8ZRAnKpvbusQagv8CZGyNYti23p9jBsjTSx75ZU26UW5vgC8D88pusW8W5dp1ERo5DSfurMSYJ6afgQHdcuzn7exb8znSm6uV4y1cWgBRcuAGdg3wRpVhP8HEB1EeKgzjYVWvMSy6yR7qVrSL6BxHG6eiAMyahLFbEt4qBqLEdxxY7Dt4DyydVYmG2ZVtheaMHD3ACwCjpyPLXj399wxSgGXQQFGtzEJQw9awVezmJ4wZk6W4dDpXQvdKYaqUvwTwRZsQB5o2iekPWZXR9xvHiMLjMVBPzYgcU14ZSaCbqSNVv2pAJxP1sMvxZMNMzZPttPxCsDDGq9biC7exXwzesXSnZ3rsgEYeZtkUiBHAxR4rYqBpA6VzLs1bPx8MPTvr9mhNi2ezMBbg2nEfHV6Fz7H7rEY2g3jDtRz35Vmgits8s9RKi3kb73WtGUieRiXjiqkNhpvKkST1oEYRQ9"); } diff --git a/rust/tw_any_coin/tests/chains/solana/solana_sign.rs b/rust/tw_any_coin/tests/chains/solana/solana_sign.rs index 194cad133fe..3f53846e94b 100644 --- a/rust/tw_any_coin/tests/chains/solana/solana_sign.rs +++ b/rust/tw_any_coin/tests/chains/solana/solana_sign.rs @@ -2,8 +2,862 @@ // // Copyright © 2017 Trust Wallet. +use std::borrow::Cow; +use tw_any_coin::test_utils::sign_utils::AnySignerHelper; +use tw_coin_registry::coin_type::CoinType; +use tw_encoding::base58::{self, Alphabet}; +use tw_encoding::hex::DecodeHex; +use tw_proto::Common::Proto::SigningError; +use tw_proto::Solana::Proto; + +use tw_proto::Solana::Proto::mod_SigningInput::OneOftransaction_type as TransactionType; + +fn b58(s: &str) -> Cow<'static, [u8]> { + base58::decode(s, Alphabet::BITCOIN).unwrap().into() +} + +#[test] +fn test_solana_sign_transfer() { + let transfer = Proto::Transfer { + recipient: "EN2sCsJ1WDV8UFqsiTXHcUPUxQ4juE71eCknHYYMifkd".into(), + value: 42, + ..Proto::Transfer::default() + }; + let input = Proto::SigningInput { + private_key: b58("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr"), + recent_blockhash: "11111111111111111111111111111111".into(), + transaction_type: TransactionType::transfer_transaction(transfer), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "3p2kzZ1DvquqC6LApPuxpTg5CCDVPqJFokGSnGhnBHrta4uq7S2EyehV1XNUVXp51D69GxGzQZUjikfDzbWBG2aFtG3gHT1QfLzyFKHM4HQtMQMNXqay1NAeiiYZjNhx9UvMX4uAQZ4Q6rx6m2AYfQ7aoMUrejq298q1wBFdtS9XVB5QTiStnzC7zs97FUEK2T4XapjF1519EyFBViTfHpGpnf5bfizDzsW9kYUtRDW1UC2LgHr7npgq5W9TBmHf9hSmRgM9XXucjXLqubNWE7HUMhbKjuBqkirRM"); + assert_eq!(output.unsigned_tx, "87PYsiS4MUU1UqXrsDoCBmD5FcKsXhwEBD8hc4zbq78yePu7bLENmbnmjmVbsj4VvaxnZhy4bERndPFzjSRH5WpwKwMLSCKvn9eSDmPESNcdkqne2UdMfWiFoq8ZeQBnF9h98dP8GM9kfzWPjvLmhjwuwA1E2k5WCtfii7LKQ34v6AtmFQGZqgdKiNqygP7ZKusHWGT8ZkTZ"); +} + +#[test] +fn test_solana_sign_transfer_v0() { + let transfer = Proto::Transfer { + recipient: "6pEfiZjMycJY4VA2FtAbKgYvRwzXDpxY58Xp4b7FQCz9".into(), + value: 5000, + ..Proto::Transfer::default() + }; + let input = Proto::SigningInput { + private_key: "833a053c59e78138a3ed090459bc6743cca6a9cbc2809a7bf5dbc7939b8775c8" + .decode_hex() + .unwrap() + .into(), + recent_blockhash: "HxKwWFTHixCu8aw35J1uxAX6yUhLHkFCdJJdK4y98Gyj".into(), + v0_msg: true, + transaction_type: TransactionType::transfer_transaction(transfer), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + // Successfully broadcasted: https://explorer.solana.com/tx/4ffBzXxLPYEEdCYpQGETkCTCCsH6iTdmKzwUZXZZgFemdhRpxQwboguFFoKCeGF3SsZPzuwwE7LbRwLgJbsyRqyP?cluster=testnet + assert_eq!(output.encoded, "6NijVxwQoDjqt6A41HXCK9kXwNDp48uLgvRyE8uz6NY5dEzaEDLzjzuMnc5TGatHZZUXehKrzUGzbg9jPSdn6pVsMc9TXNH6JGe5RJLmHwWey3MC1p8Hs2zhjw5P439P57NToatraDX9ZwvBtK4EzZzRjWbyGdicheTPjeYKCzvPCLxDkTFtPCM9VZGGXSN2Bne92NLDvf6ntNm5pxsPkZGxPe4w9Eq26gkE83hZyrYXKaiDh8TbqbHatSkw"); +} + +#[test] +fn test_solana_sign_transfer_to_self() { + let transfer = Proto::Transfer { + recipient: "zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu".into(), + value: 42, + ..Proto::Transfer::default() + }; + let input = Proto::SigningInput { + private_key: b58("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"), + recent_blockhash: "11111111111111111111111111111111".into(), + transaction_type: TransactionType::transfer_transaction(transfer), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + // Successfully broadcasted: https://explorer.solana.com/tx/4ffBzXxLPYEEdCYpQGETkCTCCsH6iTdmKzwUZXZZgFemdhRpxQwboguFFoKCeGF3SsZPzuwwE7LbRwLgJbsyRqyP?cluster=testnet + assert_eq!(output.encoded, "EKUmihvvUPKVN4GSCFwZRtz8WiyAuPvthW69Smo19SCjcPLQ6T7EVZd1HU71WAoe1bfgmPNS5JhU7ZLA9XKG3qbZqeEFJ1xmRwW9ZKw8SKMAL6VRWxp87oLu7PSmf5b8R34vCaww3XLKtZkoP49a7TUK31DqPN5xJCceMB3BZJyaojQaKU8nUkzSGf89LY6abZXp9krKAebvc6bSMzTP8SHSvbmZbf3VtejmpQeN9X6e7WVDn6oDa2bGT"); +} + +#[test] +fn test_solana_sign_transfer_with_memo_and_references() { + let transfer = Proto::Transfer { + recipient: "71e8mDsh3PR6gN64zL1HjwuxyKpgRXrPDUJT7XXojsVd".into(), + value: 10000000, + memo: "HelloSolanaMemo".into(), + references: vec![ + "CuieVDEDtLo7FypA9SbLM9saXFdb1dsshEkyErMqkRQq".into(), + "tFpP7tZUt6zb7YZPpQ11kXNmsc5YzpMXmahGMvCHhqS".into(), + ], + ..Proto::Transfer::default() + }; + let input = Proto::SigningInput { + private_key: b58("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"), + recent_blockhash: "11111111111111111111111111111111".into(), + transaction_type: TransactionType::transfer_transaction(transfer), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + // Successfully broadcasted: https://explorer.solana.com/tx/4ffBzXxLPYEEdCYpQGETkCTCCsH6iTdmKzwUZXZZgFemdhRpxQwboguFFoKCeGF3SsZPzuwwE7LbRwLgJbsyRqyP?cluster=testnet + assert_eq!(output.encoded, "NfNH76sST3nJ4FmFGTZJBUpJou7DRuHM3YNprT1HeEau699CQF65xNf21Hoi491bbtVKUXfqCJyeZhfTCEnABuXNC1JrhGBeCv2AbQdaS9gpp9j4xHHomhCYdwYaBWFMcKkdMXrx9xHqL9Vkny4HezkwQfb3wGqcaE9XVRdkkNxsoJnVKddRnrQbjhsZGTcKdfmbTghoUeRECNPTm6nZTA1owWF1Dq6mfr6M3GZRh4ucqEquxKsQC2HQwNRrGZahsfyUvwspPWwMt78q5Jpjd9kHqkFDspZL6Pepv4dAA4uHhYDCHeP2bbDiFMBYxxWCVDDtRKSh3H92xUgh1GCSgNcjGdbVfQUhSDPX3k9xuuszPTsVZ2GnsavAsRp6Vf6fFEikBX6pVV9zjW1cx94EepQ2aGEBSsVu4RzX7rJjCLCq87h8cxxf1XnF8mvYGEK7wzF"); +} + +#[test] +fn test_solana_sign_transfer_with_durable_nonce() { + let transfer = Proto::Transfer { + recipient: "3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe".into(), + value: 1000, + ..Proto::Transfer::default() + }; + let input = Proto::SigningInput { + private_key: "044014463e2ee3cc9c67a6f191dbac82288eb1d5c1111d21245bdc6a855082a1" + .decode_hex() + .unwrap() + .into(), + recent_blockhash: "5ycoKxPRpW2GdD4byZuMptHU3VU5MgUCh6NLGQ2U8VE5".into(), + transaction_type: TransactionType::transfer_transaction(transfer), + nonce_account: "ALAaqqt4Cc8hWH22GT2L16xKNAn6gv7XCTF7JkbfWsc".into(), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "6zRqmNP5waeyartbf8GuQrWxdSy4SCYBTEmGhiXfYNxQTuUrvrBjia18YoCM367AQZWZ5yTjcN6FaXuaPWju7aVZNFjyqpuMZLNEbpm8ZNmKP4Na2VzR59iAdSPEZGTPuesZEniNMAD7ZSux6fayxgwrEwMWjeiskFQEwdvFzKNHfNLbjoVpdSTxhKiqfbwxnFBpBxNE4nqMj3bUR37cYJAFoDFokxy23HGpV93V9mbGG89aLBNQnd9LKTjpYFv49VMd48mptUd7uyrRwZLMneew2Bxq3PLsj9SaJyCWbsnqYj6bBahhsErz67PJTJepx4BEhqRxHGUSbpeNiL7qyERri1GZsXhN8fgU3nPiYr7tMMxuLAoUFRMJ79HCex7vxhf7SapvcP"); +} + +/// Transfer SOL with `priority_fee_limit` and `priority_fee_price`. +#[test] +fn test_solana_sign_transfer_with_priority_fee() { + // Corresponding Solana address: 7HP8cENhoDxS7yT6UHyT3mVwvN5rv6gMrzwRXXWFJvDU. + let private_key = "1eb5264f82747294e7481ecae700bd1d21e50db620bcf0d2af7582f712c71b40" + .decode_hex() + .unwrap(); + + let transfer = Proto::Transfer { + recipient: "HwHDGHYeYfXdzwvCZu7zm21SzQ7ZLBGTLNkuscyFdpvZ".into(), + value: 1001, + ..Proto::Transfer::default() + }; + + let input = Proto::SigningInput { + private_key: private_key.into(), + recent_blockhash: "ABdaGDKfoBe2SJAYkD31sojYVwTM8ainsdBD7ZM9dut1".into(), + transaction_type: TransactionType::transfer_transaction(transfer), + priority_fee_limit: Some(Proto::PriorityFeeLimit { limit: 2000 }), + priority_fee_price: Some(Proto::PriorityFeePrice { price: 2 }), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "4WReC2rmup9nZjtFQbBbha5uADkVb54CRd6a3vbAoptbuzYvdud2Xau2tkzCejZsfsUC2ppqhMqbXXzaoiSpYH8XEDo6QtUV8EGXcE3GFxVjjtBT2U8abtC2qcBMDGAghtzpsmKWfWMxmQj8wWKjpmzamiU6PjfMbuAyAX5VPhQxZQ84Ay2HtQJBK8LA9CJ9qva9ehmu7g41wKBXLPq22NGjeSHq6ubjcNLQrFx34sKGtPJ68Z2oZgrju7XjKFhtnKzqUCWcgVUjUsyHyyBJ3awaExYHEw2uKq9NVoKmf9JDkjf2LBjNpUJ3EGzFHrfwnVEQ7B5XCX4nQRPir7eLuunLwVKhrdATKDbxin3ANe5D"); +} + +/// Transfer SOL with `priority_fee_limit` set only. +#[test] +fn test_solana_sign_transfer_with_priority_fee_limit() { + // Corresponding Solana address: 7HP8cENhoDxS7yT6UHyT3mVwvN5rv6gMrzwRXXWFJvDU. + let private_key = "1eb5264f82747294e7481ecae700bd1d21e50db620bcf0d2af7582f712c71b40" + .decode_hex() + .unwrap(); + + let transfer = Proto::Transfer { + recipient: "HwHDGHYeYfXdzwvCZu7zm21SzQ7ZLBGTLNkuscyFdpvZ".into(), + value: 160000, + ..Proto::Transfer::default() + }; + + let input = Proto::SigningInput { + private_key: private_key.into(), + recent_blockhash: "8mhXYg8vpezXr7y7eP5q3oztzY9FRwTPSvyB3izzF88m".into(), + transaction_type: TransactionType::transfer_transaction(transfer), + priority_fee_limit: Some(Proto::PriorityFeeLimit { limit: 1000 }), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + // https://solscan.io/tx/4ei1iejYd1Yr11hdi7ziFjc6GyUAtNnUQqURqssBXB7H6kYJ4CXxYiSks3PjoPdJfZFMXTSAYTp7wqYrjCKgKxcV + assert_eq!(output.encoded, "23MBXQ2GgD4CWLXG5MrhmXa6YDf4ghZdxEAzdrG9U457PMBAeg3WbSy7WA7H7a8VEFZ5eYR8TSvSAJ4ZBqhS4bLT7e8VSTszLDu159RmeKcfXtn7Ae4hQybb6uALWSkdBjSHyURYZoto6HWzS57htjUQkmAimaWFG5nwfkox9wqxAZqDNVAFYEAzM3eNWwWeN2rCnNy4vLfyKSZeNTRdvPg8PjUFyXrWcKkd3NFTqSchQmZsccaxpN32sNjg2Br2thCrcazon3rBY9T1BwuaWTtZsvm653dhhKENNajaD3TyA79K1m4rP6wYVaEs6UWr6qcwsdR6WD5TCkwmmmEjVYeQp2Py"); +} + #[test] -#[ignore] -fn test_solana_sign() { - todo!() +fn test_solana_sign_delegate_stake_no_stake_account() { + let delegate = Proto::DelegateStake { + validator_pubkey: "4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC".into(), + value: 42, + ..Proto::DelegateStake::default() + }; + let input = Proto::SigningInput { + private_key: b58("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"), + recent_blockhash: "11111111111111111111111111111111".into(), + transaction_type: TransactionType::delegate_stake_transaction(delegate), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "j24mVM9Zgu5vDZhPLGGuCRXQnP9djNtxdHh4txN3S7dwJsNNL5fbhzGpPgSUAcLGoMVCfF9TuqTYfpfJnb4sJFe1ahM8yPL5HwuKL6py5AZJFi8SWx9fvaVB699dCPo1GT3JoEBLPCZ9o2jQtnwzLkzTYJnKv2axqhKWFE2sz6TBA5J39eZcjMFUYgyxz6Q5S4MWqYQCb8UET2NAEZoKcfy7j8N25WXL6Gj4j3hBZjpHQQNaGaNEprEqyma3ZuVhpGiCALSsuzVLX3wZVo4icXwe952deMFA4tH3BK1jcSQCgfmcKDJ9nd7bdrnUUs4BoMdF1uDZB5LxE2UH8QiqtYvaUcorF4SJ3gPxM5ykbyPsNK1cSYZF9NMpW2GofyC17eELwnHQTQB2kqphxJZu7BahvkwiDPPeeydiXAkBspJ3nc3PCBujv6WJw22ZHw5j6zAP8ZGnCW44pqtWD5qifF9tTKhySKdANNiWifs3tSCCPQqjfJXu14drNinR6VG8rJxS1qgmRYiRQUa7m1vtoaZFRN5qKUeAfoFKkAVaNnMdwgsNqNH4dqBodTCJFs1LkYwhgRZdZGbwXTn1j7vpR3DSnv4g72i2H556srzK53jdUmdv6yfxt516XDSshqZtHnKZ1tudxKjBXwsqT3imDiZFVka9wKWUAYMCi4XZ79CY6Xpsd9c18U2e9TCngQmgkTATFgrqysfraokNffgqWxvsPMugksbvbPjJs3iCzByvphkC9p7hCf6LwbeF8XnVB91EAgRDA4VLE1f9wkcq5zjy879YWJ4r516h3PQszTz1EaJXNAXdbk5Em7eyuuabGP1Q3nijFTL2yhMDsXpgrjAuEAABNxFMd4J1JRMaic615mHrhwociksrsfQK"); +} + +#[test] +fn test_solana_sign_delegate_stake_with_account() { + let delegate = Proto::DelegateStake { + validator_pubkey: "4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC".into(), + value: 42, + stake_account: "6u9vJH9pRj66N5oJFCBADEbpMTrLxQATcL6q5p5MXwYv".into(), + }; + let input = Proto::SigningInput { + private_key: b58("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"), + recent_blockhash: "11111111111111111111111111111111".into(), + transaction_type: TransactionType::delegate_stake_transaction(delegate), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "TKPiN35HzeD3zdwxDFvnkgoqud7CZsda15JkBwM4nDpr623rM7MZsH6QvMMyKpiz7MeRNTrfyHkRLQSBT9Tbg2mgTdfrbhhqeF3Suu5ECphqn8DFYPoMnFzeg5u9gaqevfjhuizzeo2YDJF8aVGy1pez8gMbp5vHz1SuvQUgfcvFctggUMwNiJorSmmp3N6TzQSd38CZrA8ZLhaJjuwDwVMjmj18rGTV1gkX19L7byTFrus2vNvPeUa2AawwUnFpYMPgvCKkHTrpnjvypjoLof9yMUFQ5M1S3Ntv53KJyXwXq6ejJnBDtisnDcdMDNSZp3VeKz6XCr8XVM5xNVh3LX12V4kc3ueqkokYJLP1JmuhA3nNZA1G5KTNno93HUoBkEa1x5h3haoCSgmQC97LoJbJM6B6C2NbaDj2J6iiTaVQdin4He4Jpj575WDhNTqsLjzFUHPUHQF1CRnuss8UpVyMsa4kdVqCDQGeh5DKbkikgcB8GKPBuC91DRxGEqgoygNsu5nnQy4o3YAJnBBK6HsKxpdjbYD8wCUdLw8muhjpEqeBTPShEaogm9zfehidiCcnxbeoX3gmW8oH9gpWoX7GrkJgF6Wn7iWohmrzqzAjoBz8hpeY5nkkhHrf9iswVGMpakdLGy3YxkGJVpsW8KJACwEKXGLq8SVLtXSUHG8EP16zfYHxKjkCSs8PkdFsA5esxsxppPTVZivuEPqJ5og55aNmugdNDrAFYWdcH1Q4rm7BXN6oHECdz2yY4HFVWh9u592oqozt2gQKu3vmhcNFzzQe1xgs6zKSv38kSGTnipd7Hx2VL3qNAR6XBRiwAi226qSTzxi6R82p7cMB7TMy6fk5AZ3sXDSXFNJ9S5SSU1V63ruw75QMtVio"); +} + +#[test] +fn test_solana_sign_deactivate_stake() { + let deactivate = Proto::DeactivateStake { + stake_account: "6XMLCn47d5kPi3g4YcjqFvDuxWnpVADpN2tXpeRc4XUB".into(), + }; + let input = Proto::SigningInput { + private_key: b58("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"), + recent_blockhash: "11111111111111111111111111111111".into(), + transaction_type: TransactionType::deactivate_stake_transaction(deactivate), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "6x3fSstNz4GpPxmT5jHXwyD62uyJMKaPWeBDNNcwXZA9NJ3E7KavCXPNUd8ZYTX5VpkfHKGszkwzM6AdAp4giLD29jvWdNYjkV1Nvb42xFwGD6ryMPZzXkJijaRTrA7SvPTDSRU2haGVmorqkywAXLQUCw47NmBUfLTb5gDcKoBeaAsahckv1eCE746thJVTg2dQNvUTULKF6xckUg7kwFkcUuRe4HCcRgrKcNAUKLR2rEM3brVQkUyAaAtMMtc3gVDXxxpbtW5Fa9wGaEnh31FdRo4z5YBzAUaz7vcrvzF2j81KCPTVnYyTmeJzCzJafzCVCtw"); +} + +#[test] +fn test_solana_sign_deactivate_all_stake() { + let deactivate_all = Proto::DeactivateAllStake { + stake_accounts: vec![ + "CJQStmfyoHbosX1GfVn64yWrNJAo214q2aqxwS6FGh4k".into(), + "6XMLCn47d5kPi3g4YcjqFvDuxWnpVADpN2tXpeRc4XUB".into(), + ], + }; + let input = Proto::SigningInput { + private_key: b58("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"), + recent_blockhash: "11111111111111111111111111111111".into(), + transaction_type: TransactionType::deactivate_all_stake_transaction(deactivate_all), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "U9azMJWRfDhypoDeQLYWyBYFZCwRNZy8sbrVX9awKK84zNGbSQfYTTJ3ZyzjNUVbU5npbw2MsWfmZGHZRvpfN7G7o3sVePyFRXrmLxrGZzGycFv25Zff4zPxDarbsugbCBgzVGpgwu8x7MdkwBAVHVtNsgMcHgArEAjEmk7YEGpZ15rjo39bCRvmuprWLqSv2SK1RyTZPpTPXVevAbA4i9vvcY8eUbwW29SZCoyGaagLU5EBV9vckMjzGa7gq2yMR6rbq8tDdWaXapYs8RavU49WN94yg4wdE4fzYq8DjqXHq3MuUBLxeYDKJnvj84ioeM4eR1EwjBNrGyz5GHTRuhbNg1nc57SpKsSMVSZW5Ra3tUk84YZXYFHxzeQ9Tv4o"); +} + +#[test] +fn test_solana_sign_withdraw_stake() { + let withdraw = Proto::WithdrawStake { + stake_account: "6XMLCn47d5kPi3g4YcjqFvDuxWnpVADpN2tXpeRc4XUB".into(), + value: 42, + }; + let input = Proto::SigningInput { + private_key: b58("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"), + recent_blockhash: "11111111111111111111111111111111".into(), + transaction_type: TransactionType::withdraw_transaction(withdraw), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "gxr4o1trVP8DGG8UC21AA964YqAPFA3rBCF9MwmBQpn5fDtcujM9wp1gzT466MxWGR8wMciS6dSL771q29eURrEEuvhJzRaFDGPLgVB3UL4gd4T2amPQkR4Dzq5drKEtPJRBR86KVVc2kjDsbWNpdL8S7pZqW3VUijAbm9TS8ezG8NExSCkhxExKhUjXWWguEL4qXra7s2JZfhtmvuJneWnEY3isUVfC9knWtGNwpNFvRvzbH2sgHzwtSsD7mkYrBJoazLCwT8r9yypxycHL41XcGtH425MA16kVSunvvBfzG9PzBTS65YJBs64tzttasCU9uEphkwgmfrmoEC8iKt8xD47Ra79RyXd95yURsaxvpb1tVAH8kMNtj8iV1Pfm"); +} + +#[test] +fn test_solana_sign_withdraw_all_stake() { + let withdraw_all = Proto::WithdrawAllStake { + stake_accounts: vec![ + Proto::StakeAccountValue { + stake_account: "CJQStmfyoHbosX1GfVn64yWrNJAo214q2aqxwS6FGh4k".into(), + value: 42, + }, + Proto::StakeAccountValue { + stake_account: "6XMLCn47d5kPi3g4YcjqFvDuxWnpVADpN2tXpeRc4XUB".into(), + value: 67, + }, + ], + }; + let input = Proto::SigningInput { + private_key: b58("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"), + recent_blockhash: "11111111111111111111111111111111".into(), + transaction_type: TransactionType::withdraw_all_transaction(withdraw_all), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "cvBNusjtHkR74EfWsvFPEe2Mydcr7eoLeY2wJw2ZMZYViotbb63Adai7UD1PW9uLusoVHGLeJC5cPgVBC4F693P9tPAxLs9yiZj1ZJQ4DgnYbeXafqzjdWje1Ly5FgpDUJaaU2RnLCG51CcrmiTJ4KB5fwai6egZaNjbiqo1DEC1wJz4FgKug2aKQWLdeCiH9WhCuvqfhNV6mEE4qRCkU8uS2gfSqBd1AdrczvoDEbKQszosrwmawxqmvTE5EWaFzMb48x9nLqxvpQCvGQu1nX6FxZJjv2swekA7wGLEAA4uSdFLTHNrYSi8pn8hVYGwESEzth9oiPkJCvW7Y2KvGALeERUZn8knHiz2eqaaT72Ajp9UogMdZtiuFHufveLXpBLWUERchhB7eU1magYcPNHcZuEE4uQv5kZJhHAqYCGU6dyUFLVA9Edus7o6fTktYVCjoGb"); +} + +#[test] +fn test_solana_sign_create_token_account() { + let create_token_acc = Proto::CreateTokenAccount { + main_address: "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V".into(), + token_mint_address: "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt".into(), + token_address: "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP".into(), + }; + let input = Proto::SigningInput { + private_key: b58("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"), + recent_blockhash: "9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K".into(), + transaction_type: TransactionType::create_token_account_transaction(create_token_acc), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "CKzRLx3AQeVeLQ7T4hss2rdbUpuAHdbwXDazxtRnSKBuncCk3WnYgy7XTrEiya19MJviYHYdTxi9gmWJY8qnR2vHVnH2DbPiKA8g72rD3VvMnjosGUBBvCwbBLge6FeQdgczMyRo9n5PcHvg9yJBTJaEEvuewyBVHwCGyGQci7eYd26xtZtCjAjwcTq4gGr3NZbeRW6jZp6j6APuew7jys4MKYRV4xPodua1TZFCkyWZr1XKzmPh7KTavtN5VzPDA8rbsvoEjHnKzjB2Bszs6pDjcBFSHyQqGsHoF8XPD35BLfjDghNtBmf9cFqo5axa6oSjANAuYg6cMSP4Hy28waSj8isr6gQjE315hWi3W1swwwPcn322gYZx6aMAcmjczaxX9aktpHYgZxixF7cYWEHxJs5QUK9mJePu9Xc6yW75UB4Ynx6dUgaSTEUzoQthF2TN3xXwu1"); +} + +#[test] +fn test_solana_sign_create_token_account_5ktpn1() { + let create_token_acc = Proto::CreateTokenAccount { + main_address: "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ".into(), + token_mint_address: "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt".into(), + token_address: "ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf".into(), + }; + let input = Proto::SigningInput { + private_key: "4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7" + .decode_hex() + .unwrap() + .into(), + recent_blockhash: "HxaCmxrXgzkzXYvDFTToENtf9rVKk7cbiuSUqnqNheHq".into(), + transaction_type: TransactionType::create_token_account_transaction(create_token_acc), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + // https://explorer.solana.com/tx/5KtPn1LGuxhFiwjxErkxTb7XxtLVYUBe6Cn33ej7ATNVyorrkk3UAFJWDBUmzP8CZjmkocCxiMAdYnvrKoGpMsJx + assert_eq!(output.encoded, "EoJGDRFZdnjmx7rgwYSuDGTMTUdxCBeh8RggrQDzGht9bwzLPpCWkCrN4iQJqg3R6JxP7z2QZuf7dGCZcjMVBmmisYE8waRsohcvygRwmGr6nefbaujR5avm2x3EUvoTGyy8cMZJxX7URx45qQJyCgqFLNFCQzD1Kej3xCEPAJqCdGZgmqkryw2E2nkpGKXgRmbyEg2rFgd5kpvjG6jSLLYzGomxVnaKK2XyMQbcedkTMYJ8Ara71iWPRFUziWfgivZcA1qsQp92Fpao3FSsRprhoQz9u1VyAnh8zEM9jCKiE5s4dwCknqCJYeYsbMLn1be2vNP9bMQfu1jjGSHmbb9WR3E2vakTUEUByASXqSAJZuXYE5scopEzB28rC8nrC31ArLMZng5wWym3QbqEv2Syd6RHoEeoXR6vA5LPqvJKyvtH82p4hc4XbD18128aNrFG3GTD2P"); +} + +#[test] +fn test_solana_sign_create_token_account_for_other_3e6ufv() { + let create_token_acc = Proto::CreateTokenAccount { + main_address: "3xJ3MoUVFPNFEHfWdtNFa8ajXUHsJPzXcBSWMKLd76ft".into(), + token_mint_address: "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt".into(), + token_address: "67BrwFYt7qUnbAcYBVx7sQ4jeD2KWN1ohP6bMikmmQV3".into(), + }; + let input = Proto::SigningInput { + private_key: "4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7" + .decode_hex() + .unwrap() + .into(), + recent_blockhash: "HmWyvrif3QfZJnDiRyrojmH9iLr7eMxxqiC9RJWFeunr".into(), + transaction_type: TransactionType::create_token_account_transaction(create_token_acc), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + // https://explorer.solana.com/tx/3E6UFVamHCm6Bgk8gXdZex7R7tJAVxqJm6t9ephAKu1PjcfZrD7CJqMwKu6RrvWSUESbZFqzdUyLXuxAFaawPHvJ + assert_eq!(output.encoded, "4BsrHedHuForcKDhLdnLYDXgtQgQEj3EQVDtEhqa7o6ukFjW3shpTWv6PeKQdMp6af4ASjD4xQeZvXxLK5WUjguVMUf3xdJn7RnFeM7hdDJ56RDBM5PRJbRJVHjz6FJ7SVNTvr9y3gVYQtWx7NfKRxiyEAfq9JG7nqxSWaW6raMr9t35aVcdAVuXE9iXj3rzhVfCS69vVzy5KcFEK3mvDYG6L12V2CfviCydmeCvPw5r3zBUrZSQv7Ti4XFNBrPbk28gcqQwsBknBqasHxHqD9VUyPmBTuUyXq75QN8rhqN55NjxKBUw37tEUS1jKVpWnTeLFq1eRAMdXvjftNuQ5Bmm8Zc12PGWj9vdorBaYyvZXexJST5xNjR4SCkXvXZoRScETck95chv3VBn54jP8DpB4GGUmATFKSxpdtnNV64i1SQXW13KJwswthJvAaDiqevQLKLkvrTEAdb4BxEfPkFjDVti6P58rTZCMg5CTVLqdmWwpTSW5V"); +} + +#[test] +fn test_solana_sign_create_token_account_with_priority_fee_price() { + // Corresponding Solana address: 7HP8cENhoDxS7yT6UHyT3mVwvN5rv6gMrzwRXXWFJvDU. + let private_key = "1eb5264f82747294e7481ecae700bd1d21e50db620bcf0d2af7582f712c71b40" + .decode_hex() + .unwrap(); + + let create_token_acc = Proto::CreateTokenAccount { + main_address: "7HP8cENhoDxS7yT6UHyT3mVwvN5rv6gMrzwRXXWFJvDU".into(), + // WBTC + token_mint_address: "3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh".into(), + token_address: "94JfTH8qCeBQDBatvULREG43msjSQscT7oHnqx7jppX".into(), + }; + + let input = Proto::SigningInput { + private_key: private_key.into(), + recent_blockhash: "FgDiTexKSzxGudn6pupKZZRm6J2GFvM1vGf7hPH2WX6r".into(), + transaction_type: TransactionType::create_token_account_transaction(create_token_acc), + priority_fee_price: Some(Proto::PriorityFeePrice { price: 20 }), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + // https://solscan.io/tx/5B2Zxu14wvr7CJJa4L3934cU9GEi82JHF1B9pDcHgtTeTPqBckyL12JSuZN224EAMCBGiCXEezT8fReQu4PVmHS + assert_eq!(output.encoded, "BtCPMKt9QUhF9cLrawa51GPZyY3GicQpaf1iX73sswSQ4t6gNNcoKM2kVkXne3Xcyzo3fRbuBxh6PsDEcBSi5TviDq7YZsS8usLchrGwCM41U1KqjsyABZSnwiyKNf4bUg77B3SiUqs5gWrH7s6QXJ4LDZvUMu3W9Hqo7uHbBkGGBPw4Pqjys1D963LnbBrSRHBRKuYLJauePJcQ9RoCNhWgy63RfuQMXyMLdVanUtiBCwY9BWxyFiwJTLvz2XbyY8CRruxQFvmDcyCSghMGvim8AKxah7EHgBzn9PTqfdRqYcdT3quHf6mfKCpxv13DuwbYUHAhQe4acCKQsV2ty3w6QtVaL9gND1CYp3rYpDcRXg8KyXrARcvyRmBU2oBsxyHyvwqxWarre8RZJxMKEHytYDo2Wkx36zgm8JZcruUajrZi4Z2RQNNhsKYSqU9AGzV31opPpa8YLa9hRFy3YmhrDGtBv897GqDV9eD5FiDkQsDAzH9JcSibaffPe3aK3E46fh"); +} + +#[test] +fn test_solana_sign_token_transfer() { + let token_transfer = Proto::TokenTransfer { + token_mint_address: "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt".into(), + sender_token_address: "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP".into(), + recipient_token_address: "3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei".into(), + // 0.004 + amount: 4000, + decimals: 6, + ..Proto::TokenTransfer::default() + }; + let input = Proto::SigningInput { + private_key: b58("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"), + recent_blockhash: "CNaHfvqePgGYMvtYi9RuUdVxDYttr1zs4TWrTXYabxZi".into(), + transaction_type: TransactionType::token_transfer_transaction(token_transfer), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + // https://explorer.solana.com/tx/3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg + assert_eq!(output.encoded, "PGfKqEaH2zZXDMZLcU6LUKdBSzU1GJWJ1CJXtRYCxaCH7k8uok38WSadZfrZw3TGejiau7nSpan2GvbK26hQim24jRe2AupmcYJFrgsdaCt1Aqs5kpGjPqzgj9krgxTZwwob3xgC1NdHK5BcNwhxwRtrCphGEH7zUFpGFrFrHzgpf2KY8FvPiPELQyxzTBuyNtjLjMMreehSKShEjD9Xzp1QeC1pEF8JL6vUKzxMXuveoEYem8q8JiWszYzmTMfDk13JPgv7pXFGMqDV3yNGCLsWccBeSFKN4UKECre6x2QbUEiKGkHkMc4zQwwyD8tGmEMBAGm339qdANssEMNpDeJp2LxLDStSoWShHnotcrH7pUa94xCVvCPPaomF"); +} + +#[test] +fn test_solana_sign_token_transfer_2pmvzp() { + let token_transfer = Proto::TokenTransfer { + token_mint_address: "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt".into(), + sender_token_address: "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP".into(), + recipient_token_address: "ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf".into(), + // 0.0061 + amount: 6100, + decimals: 6, + ..Proto::TokenTransfer::default() + }; + let input = Proto::SigningInput { + private_key: b58("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"), + recent_blockhash: "zMEbroNLJ4vfDTdQyA72rk35c7nPo4K38efHLujbSuz".into(), + transaction_type: TransactionType::token_transfer_transaction(token_transfer), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + // https://explorer.solana.com/tx/2pMvzparE16evNgNhiexBfj15eurQgqFJXemYkuGasWV8RfT5tQseadqXA2VXbgGZPM1MpLcGwfkKKqvYvrKTmnR + assert_eq!(output.encoded, "LCtawaKHmvh9WEjYPFFMDQXsdKMQbVyK4Q3aRRfLCouqw6GE4p31PRPFoQqtazTziEj3ex3iLgnCspz1MN4SUE9d33g3HiiA6oCS6wGMvB2i3ojtmJzndCiLoDmuZgiuGouVSeS2MAEUoS3CRjdnbNKbRwgKn8YsDe1bZ57ueipfBLJfiE7xr8ji678uAv8FcMgo8Mq88SBGxVCUhjMS2VGQZhRUHHzDmvnzxhbbUzsLDfApzjHExkUm7ws3cQ2i1cSpQNCQWJd6rcDv1sYwDAavPS571Ny3CUq4cZxABh45Gj88LkRpzBMRdoebrh9hPy8ZRnu7PocBVjZytCgdF4CuhzdYNsmdcuU2WN5CEmv5zQ7pBrFdLZ8bBifP"); +} + +#[test] +fn test_solana_sign_create_and_transfer_token() { + let create_transfer_token = Proto::CreateAndTransferToken { + recipient_main_address: "71e8mDsh3PR6gN64zL1HjwuxyKpgRXrPDUJT7XXojsVd".into(), + token_mint_address: "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt".into(), + recipient_token_address: "EF6L8yJT1SoRoDCkAZfSVmaweqMzfhxZiptKi7Tgj5XY".into(), + sender_token_address: "ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf".into(), + // 0.0029 + amount: 2900, + decimals: 6, + ..Proto::CreateAndTransferToken::default() + }; + let input = Proto::SigningInput { + private_key: b58("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"), + recent_blockhash: "DMmDdJP41M9mw8Z4586VSvxqGCrqPy5uciF6HsKUVDja".into(), + transaction_type: TransactionType::create_and_transfer_token_transaction( + create_transfer_token, + ), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + // https://explorer.solana.com/tx/449VaYo48LrkMJF6XVKt9sJwVQN6Seqrmh9erDCLtiuj6BgFG3wpF5TwjNkxgJ7qzNa6NTj3TFsU3h9hKszfkA7w + assert_eq!(output.encoded, "3Y2MVz2VVi7aEyC9q1awwdk1ModDBPHRSacKmTYnSgkmbbJeZ62Fub1bVPSHaTy4LUcQpzCQYhHAKtTKXUDYijEeLsMAUqPBEMAq1w8zCdqDpdXy6M4PuwNtYVV1WgqeiEsiMWpPp4BGWKfcziwFbmYueUGituacJq4wTnt92fho8mFi49XW64gEG4iNGScDtJkY7Geq8PKiLh1E9JMJoceiHxKbmxzCmmLTxEHdhySYHcDUSXnXWogZskeZNBMtR9dNjEMkCzEjrxRpBtJPtUNshciY45mDPNmw4j3xyLCBTRikyfFLc5g11r3UgyVD4YokoPRvrEXsgt6W3yjBshropBm6mY2eJYvfY2eZz4Yq8kLcUatCHVKtjcb1mP9Ww57KisJ9bRhipC8sodFaMYhZARMEa4a1u9eH4MyNUATRGNXarwQSBY46PWS3nKP6QBK7Dw7Ppp9MmYkdPcXKaLScbyLF3jKu6dHWMkHw3WdXSsM1wwXjXnWF9LxdwaEVcDmySWybj6aKD9QCWTU5kdncqJU56f7SYNRTN289WdUFGNDmSh56tj2v1"); +} + +#[test] +fn test_solana_sign_create_and_transfer_token_2() { + let create_transfer_token = Proto::CreateAndTransferToken { + recipient_main_address: "3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei".into(), + token_mint_address: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU".into(), + recipient_token_address: "BwTAyrCEdkjNyGSGVNSSGh6phn8KQaLN638Evj7PVQfJ".into(), + sender_token_address: "5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf".into(), + // 0.004 + amount: 4000, + decimals: 6, + ..Proto::CreateAndTransferToken::default() + }; + let input = Proto::SigningInput { + private_key: b58("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"), + recent_blockhash: "AfzzEC8NVXoxKoHdjXLDVzqwqvvZmgPuqyJqjuHiPY1D".into(), + transaction_type: TransactionType::create_and_transfer_token_transaction( + create_transfer_token, + ), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "2qkvFTcMk9kPaHtd7idJ1gJc4zTkuYDUJsC67kXvHjv3zwEyUx92QyhhSeBjL6h3Zaisj2nvTWid2UD1N9hbg9Ty7vSHLc7mcFVvy3yJmN9tz99iLKsf15rEeKUk3crXWLtKZEpcXJurN7vrxKwjQJnVob2RjyxwVfho1oNZ72BHvqToRM1W2KbcYhxK4d9zB4QY5tR2dzgCHWqAjf9Yov3y9mPBYCQBtw2GewrVMDbg5TK81E9BaWer3BcEafc3NCnRfcFEp7ZUXsGAgJYx32uUoJPP8ByTqBsp2JWgHyZhoz1WUUYRqWKZthzotErVetjSik4h5GcXk9Nb6kzqEf4nKEZ22eyrP5dv3eZMuGUUpMYUT9uF16T72s4TTwqiWDPFkidD33tACx74JKGoDraHEvEeAPrv6iUmC675kMuAV4EtVspVc5SnKXgRWRxb4dcH3k7K4ckjSxYZwg8UhTXUgPxA936jBr2HeQuPLmNVn2muA1HfL2DnyrobUP9vHpbL3HHgM2fckeXy8LAcjnoE9TTaAKX32wo5xoMj9wJmmtcU6YbXN4KgZ"); +} + +#[test] +fn test_solana_sign_create_and_transfer_token_with_memo_and_references() { + let create_transfer_token = Proto::CreateAndTransferToken { + recipient_main_address: "71e8mDsh3PR6gN64zL1HjwuxyKpgRXrPDUJT7XXojsVd".into(), + token_mint_address: "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt".into(), + recipient_token_address: "EF6L8yJT1SoRoDCkAZfSVmaweqMzfhxZiptKi7Tgj5XY".into(), + sender_token_address: "ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf".into(), + // 0.0029 + amount: 2900, + decimals: 6, + memo: "HelloSolanaMemo370".into(), + references: vec![ + "CuieVDEDtLo7FypA9SbLM9saXFdb1dsshEkyErMqkRQq".into(), + "tFpP7tZUt6zb7YZPpQ11kXNmsc5YzpMXmahGMvCHhqS".into(), + ], + }; + let input = Proto::SigningInput { + private_key: b58("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"), + recent_blockhash: "DMmDdJP41M9mw8Z4586VSvxqGCrqPy5uciF6HsKUVDja".into(), + transaction_type: TransactionType::create_and_transfer_token_transaction( + create_transfer_token, + ), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "FuUw2MoEGPATE38roXAw9mGQhCfdsdpVDdhuf5h8LKc8iWj2HzNS3SteXqyUoZtQ7L1ufLvu7cTMwNzxT8snnVimcknsA52CeN7bgMz1Ad1hRTAr77zE5efzAi8B124kaQ1cBEb6nFMr5Zq4wwDRoJgBaiUaM1U9ZY6GofCKHGMQN7ZNqEFG4fFvPaMXB59dFtiqrtApBGzvDho3nGshyQWZVWfMY44hvVk45FqiGrXuqUwkiJqeRaDhooZdXiFR9ubwJLXo3Ux23ZyijWKXYNsx1Lm5zMFEgRz3kXhzxzb8uzHVSrFYNieXXCQEv1GtErMKeQWuAHcwS3zxC6avTnTWJhTz3kVSXfSTYEg4MF2MBWeGrzKZ7id88ZfbpG4ZwzsDsdUCSMV6YYRNmx9P3B6oC4DL7cbi2g8hwtBdeKojY4G6JMPeg629V9sPyg2KKeYxD3cjhMKAYtrsJEbixep4LZENtdQxmgZFouJVvGy9MVhiTzGEFVwm4G25p5FhWhiS9HxHWVRXpUFHi2K9K2ttoo4Ug39V9f8s9cG1Xb5A4bHhGSuKLeCCBcrBqPWEsuLdVhjxsKJrRBJhyrZ6mpxtDhUWivZa6skmEawTts9rN2aP3dXW3cNch3s3LTXZWXG9QPUARJJPy5QAYsBoR8GunF5FFgHVuEHVpjXAd8ku9f7aoF8RNiMnXAqQHxiM3ug6HZpLHLX8aGoUbJ7vVAnEDLH"); +} + +#[test] +fn test_solana_sign_create_and_transfer_token_with_durable_nonce() { + let create_transfer_token = Proto::CreateAndTransferToken { + recipient_main_address: "3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe".into(), + token_mint_address: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU".into(), + recipient_token_address: "93hbN3brRjZqRQTT9Xx6rAHVDFZFWD9ragFDXvDbTEjr".into(), + sender_token_address: "5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf".into(), + // 0.004 + amount: 4000, + decimals: 6, + ..Proto::CreateAndTransferToken::default() + }; + let input = Proto::SigningInput { + private_key: b58("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"), + recent_blockhash: "AaYfEmGQpfJWypZ8MNmBHTep1dwCHVYDRHuZ3gVFiJpY".into(), + transaction_type: TransactionType::create_and_transfer_token_transaction( + create_transfer_token, + ), + nonce_account: "6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ".into(), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "388uZws6GfA9aiH1LPsYBijGBEfLEgqe6q5NWVYhsmjXjrgZB4cScGuvja6nBL3i6qg6HA4a8ptW6aHsNKVdcBWKhjZjaTPH5heEThzwEsMDfnH2PWAUbqfiFgMZQRCkhyCj57hGUR7hBFPELfz3DBw5qMz1tnP9gU6KTqHUomu5UaadLHb2v5mbgTRcsMm3yDp2tzMwrp53VqvFNmHSau4ot4kdNL1jqEJC68Fj4ku6fMQaFSPyAeLQRF45ofYsFCa65fmtb4gBpqWUdqWLv5Dy6xQUQUDsin8qpEVds6unXw5f63UjZeD7XQdC6Vz5aq3e6P9ug8L41xc1rbuRU3Kp4arUKyqTsHMQ2dxMhPwEJLkHd4mFqqUWpYFTdfLFaNGU22hEkvP1esHUzaaGDmzAozbS96oaFw2jbHRRJtL8VjoA1aokGFFThM6M6mExuy8GhUXdGjxDFU83Dan1URmHMGBRC4J9RMZip9s1sktJw9Rj5Std9KVT8T7m4MxTVTx4QoBw6KAf6PgNHyHPtZSc7kzoCxDYNo2Myxvy8D95zk9YMp1MxeZXTDQ2aJuhWvfHhhrwgcQasAxRzbnJ9oehebVUNEcZEFsfnCgYuUmxWUemoKZnE1bNMCvERVkT5fKQ36e1rt5vTC2iES9jzr3hDC1Pk1"); +} + +#[test] +fn test_solana_sign_create_nonce_account() { + let create_nonce_account = Proto::CreateNonceAccount { + nonce_account_private_key: + "2a9737aca3cde2dc0b4f3ae3487e3a90000490cb39fbc979da32b974ff5d7490" + .decode_hex() + .unwrap() + .into(), + rent: 10000000, + ..Proto::CreateNonceAccount::default() + }; + let input = Proto::SigningInput { + private_key: "044014463e2ee3cc9c67a6f191dbac82288eb1d5c1111d21245bdc6a855082a1" + .decode_hex() + .unwrap() + .into(), + recent_blockhash: "mFmK2xFMhzJJaUN5cctfdCizE9dtgcSASSEDh1Yzmat".into(), + transaction_type: TransactionType::create_nonce_account(create_nonce_account), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "3wu6xJSbb2NysVgi7pdfMgwVBT1knAdeCr9NR8EktJLoByzM4s9SMto2PPmrnbRqPtHwnpAKxXkC4vqyWY2dRBgdGGCC1bep6qN5nSLVzpPYAWUSq5cd4gfYMAVriFYRRNHmYUnEq8vMn4vjiECmZoHrpabBj8HpXGqYBo87sbZa8ZPCxUcB71hxXiHWZHj2rovx2kr75Uuv1buWXyW6M8uR4UNvQcPPvzVbwBG82RjDYTuancMSAxmrVNR8GLBQNhrCCYrZyte3EWgEyMQxxfW8T3xNXqnbgdfvFJ3UjRBxXj3hrmv17xEivTjfs81aG2AAi24yiYrk8ep7eQqwDHVSArsrynnwVKVNUcCQCnSy7fuiuS7FweFX8DEN1K9BrfecHyWrF15fYzhkmWSs64aH6ZTYHWPv5znhFKYmAuopGwbsBEb2j5p8NS3iJZ2skb2wi47n1rpLZfoCHWKxNiikkDUJTGQNcSDrGUMfeW5aGubJrCfecPKEo9Wo9kd36iSsxYPYSWNKrz2HTooa1rCRhqjXD8dyX3bXGV8TK6W2sEgf4JkcDnNoWQLbindcP8XR"); +} + +#[test] +fn test_solana_sign_create_nonce_account_with_durable_nonce() { + let create_nonce_account = Proto::CreateNonceAccount { + nonce_account_private_key: + "2a9737aca3cde2dc0b4f3ae3487e3a90000490cb39fbc979da32b974ff5d7490" + .decode_hex() + .unwrap() + .into(), + rent: 10000000, + ..Proto::CreateNonceAccount::default() + }; + let input = Proto::SigningInput { + private_key: "044014463e2ee3cc9c67a6f191dbac82288eb1d5c1111d21245bdc6a855082a1" + .decode_hex() + .unwrap() + .into(), + recent_blockhash: "E6hvnoXU9QmfWaibMk9NuT6QRZdfzbs96WGc2hhttqXQ".into(), + transaction_type: TransactionType::create_nonce_account(create_nonce_account), + nonce_account: "ALAaqqt4Cc8hWH22GT2L16xKNAn6gv7XCTF7JkbfWsc".into(), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "Fr8FzXoH7h6Xo2La6SE49BEPzRX6f93Qn1cFA5E8n6z2GJtZdTU2BfyYGr1zv21Zkq7h68Z3Q96VnFyUVVd1hTWeq6tHDamF1JK5L23yEeUXpEWv1KziWvG9XbxfseHUyWETQck7SY2HbsT4KSjRX9suDaBh68Bu8c96CVN7KtgYPhUrKP62dAMHsf5qo7MESFN8wKJto94ANNCbQMzPmhig9nfiAfvfz9CqV4nbnSiqBGwo2XoyPknDK8RJ1UmA5ptfZ6w6Fy4UmJbQZWuZwpUrkEkfgLMNJ36McHkGAnjpyzq9gMtzb33xSjx1BqnbWXkKJdi8HyQAHTtvtqPz7DMsW9qx5fu3TNz6iC8YHG2HiynFCRjTtc2aH1rpJ9TLdFQEK8WrhdMFr3yW27cg6NB3JUFopUkDg2k5FwtzFyCdfifwebD7eswVNnqjoZxW59fHgY3BrBH8uNst8YAQWvRH77y5L6imVmFhezU5JUb5sF58gR1D8eAQhUcHueakZb5FkFCaMeioTpKrVGgcSNe9zkBMuquoUR3t4MVTiUSLa815qKoBCRmdexQDBt5RQbdQhYyVWn3ovjdhkwDGBU2zywRvottGCcEStQrUrSQDg1tMVKxX5G3sBtxYf"); +} + +#[test] +fn test_solana_sign_withdraw_nonce_account() { + let withdraw_nonce = Proto::WithdrawNonceAccount { + nonce_account: "6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ".into(), + recipient: "3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe".into(), + value: 10000000, + }; + let input = Proto::SigningInput { + private_key: "044014463e2ee3cc9c67a6f191dbac82288eb1d5c1111d21245bdc6a855082a1" + .decode_hex() + .unwrap() + .into(), + recent_blockhash: "5ccb7sRth3CP8fghmarFycr6VQX3NcfyDJsMFtmdkdU8".into(), + transaction_type: TransactionType::withdraw_nonce_account(withdraw_nonce), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "7gdEdDymvtfPfVgVvCTPzafmZc1Z8Zu4uXgJDLm8KGpLyPHysxFGjtFzimZDmGtNhQCh22Ygv3ZtPZmSbANbafikR3S1tvujatHW9gMo35jveq7TxwcGoNSqc7tnH85hkEZwnDryVaiKRvtCeH3dgFE9YqPHxiBuZT5eChHJvVNb9iTTdMsJXMusRtzeRV45CvrLKUvsAH7SSWHYW6bGow5TbEJie4buuz2rnbeVG5cxaZ6vyG2nJWHNuDPWZJTRi1MFEwHoxst3a5jQPv9UrG9rNZFCw4uZizVcG6HEqHWgQBu8gVpYpzFCX5SrhjGPZpbK3YmHhUEMEpJx3Fn7jX7Kt4t3hhhrieXppoqKNuqjeNVjfEf3Q8dJRfuVMLdXYbmitCVTPQzYKWBR6ERqWLYoAVqjoAS2pRUw1nrqi1HR"); +} + +#[test] +fn test_solana_sign_withdraw_nonce_account_to_self_with_durable_nonce() { + let withdraw_nonce = Proto::WithdrawNonceAccount { + nonce_account: "6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ".into(), + recipient: "sp6VUqq1nDEuU83bU2hstmEYrJNipJYpwS7gZ7Jv7ZH".into(), + value: 10000000, + }; + let input = Proto::SigningInput { + private_key: "044014463e2ee3cc9c67a6f191dbac82288eb1d5c1111d21245bdc6a855082a1" + .decode_hex() + .unwrap() + .into(), + recent_blockhash: "5EtRPR4sTWRSwNUE5a5SnKB46ZqTJH8vgF1qZFTKGHvw".into(), + transaction_type: TransactionType::withdraw_nonce_account(withdraw_nonce), + nonce_account: "ALAaqqt4Cc8hWH22GT2L16xKNAn6gv7XCTF7JkbfWsc".into(), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "3rxbwm6dSX4SbFa7yitVQnoUGWkmRuQtg3V13a2jEAPfZZACCXZX2UFgWFpPqE7KfZSYhd5QE9TLzyikCwcmSBHhKXjMp4oktQXwRT66YaCK8rJdNzBUuS1D9tgHMkLUWKAR7ZRyWd3XvtQhe7nWD6YF6TRGoKPSuwsZAArBxogA7YddmEUKPsr2qjSKbjg7X5BbNceFwjEFAiafuizdSt7eGJHB5m9zJeYct8LCanTwJwyEVu1T9HTsgjW9hqHehqhCiHP46KGo63o7WAoappZvM4EJZemu4GfM6F6H48bPXF2z1QJz17wE6BYeMXfXuGkCRt5jYxrjdKuqvTDYV34X1HjZYUdrkW6mQotWDY3bS6zyAt784Vwzk2uiA8ytmWMbC24coUVwPSPGwZ92WJ6BpVCCtGDxLzp4CkahRu78UNWzdcEwPG6AUf"); +} + +#[test] +fn test_solana_sign_advance_nonce_account() { + let advance_nonce = Proto::AdvanceNonceAccount { + nonce_account: "6vNrYDm6EHcvBALY7HywuDWpTSc6uGt3y2nf5MuG1TmJ".into(), + }; + let input = Proto::SigningInput { + private_key: b58("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"), + recent_blockhash: "4KQLRUfd7GEVXxAeDqwtuGTdwKd9zMfPycyAG3wJsdck".into(), + transaction_type: TransactionType::advance_nonce_account(advance_nonce), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "7YPgNzjCnUd2zBb6ZC6bf1YaoLjhJPHixLUdTjqMjq1YdzADJCx2wsTTBFFrqDKSHXEL6ntRq8NVJTQMGzGH5AQRKwtKtutehxesmtzkZCPY9ADZ4ijFyveLmTt7kjZXX7ZWVoUmKAqiaYsPTex728uMBSRJpV4zRw2yKGdQRHTKy2QFEb9acwLjmrbEgoyzPCarxjPhw21QZnNcy8RiYJB2mzZ9nvhrD5d2jB5TtdiroQPgTSdKFzkNEd7hJUKpqUppjDFcNHGK73FE9pCP2dKxCLH8Wfaez8bLtopjmWun9cbikxo7LZsarYzMXvxwZmerRd1"); +} + +#[test] +fn test_solana_sign_token_transfer_with_external_fee_payer() { + let token_transfer = Proto::TokenTransfer { + token_mint_address: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU".into(), + sender_token_address: "5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf".into(), + recipient_token_address: "AZapcpAZtEL1gQuC87F2L1hSfAZnAvNy1hHtJ8DJzySN".into(), + // 0.004 + amount: 4000, + decimals: 6, + ..Proto::TokenTransfer::default() + }; + let input = Proto::SigningInput { + private_key: b58("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"), + recent_blockhash: "H4gZ56AdmHfZj1F36oWrxDJMUJ8ph7XdTHtmsbtHZshG".into(), + transaction_type: TransactionType::token_transfer_transaction(token_transfer), + fee_payer_private_key: b58("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + // https://explorer.solana.com/tx/3KbvREZUat76wgWMtnJfWbJL74Vzh4U2eabVJa3Z3bb2fPtW8AREP5pbmRwUrxZCESbTomWpL41PeKDcPGbojsej?cluster=devnet + assert_eq!(output.encoded, "ushDP6dNZWq32FASGqdnw7E8x14zFDAEZBViTyevUptV9yb4WSgpYzEGCxt9sXkrEtHVyww4F4GdcGZbPEaQTjAeyX5KGvHHDhWoPeFNnECzjUTuPj35dpF7zJ75Jx3ADfLQtUyzu5w7812fQvhwBwP8XDm3btqSG4VLWeQU5XuVqpg33Mq1L9zkGHQ8PZ4WkgNuSrC584EVnDcFE4rZsUtAv2jFTMjinQJB1qQEGTCHbjgdtJt8PzmXGXeczNyisPsEDrhZUw3g7RFYsgBDB1RFe1TxspzbWmxwr6CNPkGVsopmS6cbvSG9ejXY8xRYaswP7knAoPXwYk26yetoA824mzdv9vJ2RYpyK72EyCqFfFidm8MrJjFR49KwV3HRQKxZzYcvhYuJhR15GKBUWAQvYJTQQWArTi7pr7m84wNsV1mCUjrYsCVK47QtMAYvWU4toTxfgThngfF47awnpcSxfy8ggbwamq7qcaSH6cQVk1LPGo1iB5YxitSbaXP"); +} + +#[test] +fn test_solana_sign_create_and_transfer_token_with_external_fee_payer() { + let create_transfer_token = Proto::CreateAndTransferToken { + recipient_main_address: "E54BymfdhXQtzDSGtgiJayauEMdB2gJjPqoDjzfbCXwj".into(), + token_mint_address: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU".into(), + recipient_token_address: "Ay7G7Yb7ZCebCQdG39FxD1nFNDtqFWJCBgfd5Ek7DDcS".into(), + sender_token_address: "5sS5Z8GAdVHqZKRqEvpDauHvvLgbDveiyfi81uh25mrf".into(), + // 0.004 + amount: 4000, + decimals: 6, + ..Proto::CreateAndTransferToken::default() + }; + let input = Proto::SigningInput { + private_key: b58("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"), + recent_blockhash: "EsnN3ksLV6RLBW7qqgrvm2diwVJNTzL9QCuzm6tqWsU8".into(), + transaction_type: TransactionType::create_and_transfer_token_transaction( + create_transfer_token, + ), + fee_payer_private_key: b58("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + // https://explorer.solana.com/tx/7GZGFT2VA4dpJBBwjiMqj1o8yChDvoCsqnQ7xz4GxY513W3efxRqbB8y7g4tH2GEGQRx4vCkKipG1sMaDEen1A2?cluster=devnet + assert_eq!(output.encoded, "5sxFkQYd2FvqRU64N79A6xjJKNkgUsEEg2wKgai2NiK7A7hF3q5GYEbjQsYBG9S2MejwTENbwHzvypaa3D3cEkxvVTg19aJFWdCtXQiz42QF5fN2MuAb6eJR4KHFnzCtxxnYGtN9swZ5B5cMSPCffCRZeUTe3kooRmbTYPvSaemU6reVSM7X2beoFKPd2svrLFa8XnvhBwL9EiFWQ9WhHB2cDV7KozCnJAW9kdNDR4RbfFQxboANGo3ZGE5ddcZ6YdomATKze1TtHj2qzJEJRwxsRr3iM3iNFb4Eav5Q2n71KUriRf73mo44GQUPbQ2LvpZKf4V6M2PzxJwzBo7FiFZurPmsanT3U5efEsKnnueddbiLHedc8JXc1d3Z53sFxVGJpsGA8RR6thse9wUvaEWqXVtPbNA6NMao9DFGD6Dudza9pJXSobPc7mDHZmVmookf5vi6Lb9Y1Q4EgcEPQmbaDnKGGB6uGfZe629i3iKXRzAd2dB7mKfffhDadZ8S1eYGT3dhddV3ExRxcqDP9BAGQT3rkRw1JpeSSi7ziYMQ3vn4t3okdgQSq6rrpbPDUNG8tLSHFMAq3ydnh4Cb4ECKkYoz9SFAnXACUu4mWETxijuKMK9kHrTqPGk9weHTzobzCC8q8fcPWV3TcyUyMxsbVxh5q1p5h5tWfD9td5TZJ2HEUbTop2dA53ZF"); +} + +#[test] +fn test_solana_sign_raw_message_legacy() { + let legacy = Proto::mod_RawMessage::MessageLegacy { + header: Some(Proto::mod_RawMessage::MessageHeader { + num_required_signatures: 1, + num_readonly_signed_accounts: 0, + num_readonly_unsigned_accounts: 1, + }), + account_keys: vec![ + "7v91N7iZ9mNicL8WfG6cgSCKyRXydQjLh6UYBWwm6y1Q".into(), + "EN2sCsJ1WDV8UFqsiTXHcUPUxQ4juE71eCknHYYMifkd".into(), + "11111111111111111111111111111111".into(), + ], + recent_blockhash: "11111111111111111111111111111111".into(), + instructions: vec![Proto::mod_RawMessage::Instruction { + program_id: 2, + accounts: vec![0, 1], + program_data: "020000002a00000000000000".decode_hex().unwrap().into(), + }], + }; + + let raw_message = Proto::RawMessage { + signatures: Vec::default(), + message: Proto::mod_RawMessage::OneOfmessage::legacy(legacy), + }; + let input = Proto::SigningInput { + private_key: b58("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr"), + raw_message: Some(raw_message), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "3p2kzZ1DvquqC6LApPuxpTg5CCDVPqJFokGSnGhnBHrta4uq7S2EyehV1XNUVXp51D69GxGzQZUjikfDzbWBG2aFtG3gHT1QfLzyFKHM4HQtMQMNXqay1NAeiiYZjNhx9UvMX4uAQZ4Q6rx6m2AYfQ7aoMUrejq298q1wBFdtS9XVB5QTiStnzC7zs97FUEK2T4XapjF1519EyFBViTfHpGpnf5bfizDzsW9kYUtRDW1UC2LgHr7npgq5W9TBmHf9hSmRgM9XXucjXLqubNWE7HUMhbKjuBqkirRM"); +} + +#[test] +fn test_solana_sign_raw_message_v0() { + let v0 = Proto::mod_RawMessage::MessageV0 { + header: Some(Proto::mod_RawMessage::MessageHeader { + num_required_signatures: 1, + num_readonly_signed_accounts: 0, + num_readonly_unsigned_accounts: 1, + }), + account_keys: vec![ + "6pEfiZjMycJY4VA2FtAbKgYvRwzXDpxY58Xp4b7FQCz9".into(), + "11111111111111111111111111111111".into(), + ], + recent_blockhash: "HxKwWFTHixCu8aw35J1uxAX6yUhLHkFCdJJdK4y98Gyj".into(), + instructions: vec![Proto::mod_RawMessage::Instruction { + program_id: 1, + accounts: vec![0, 0], + program_data: "020000008813000000000000".decode_hex().unwrap().into(), + }], + address_table_lookups: Vec::default(), + }; + + let raw_message = Proto::RawMessage { + signatures: Vec::default(), + message: Proto::mod_RawMessage::OneOfmessage::v0(v0), + }; + let input = Proto::SigningInput { + private_key: "833a053c59e78138a3ed090459bc6743cca6a9cbc2809a7bf5dbc7939b8775c8" + .decode_hex() + .unwrap() + .into(), + raw_message: Some(raw_message), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + // Successfully broadcasted: https://explorer.solana.com/tx/4ffBzXxLPYEEdCYpQGETkCTCCsH6iTdmKzwUZXZZgFemdhRpxQwboguFFoKCeGF3SsZPzuwwE7LbRwLgJbsyRqyP?cluster=testnet + assert_eq!(output.encoded, "6NijVxwQoDjqt6A41HXCK9kXwNDp48uLgvRyE8uz6NY5dEzaEDLzjzuMnc5TGatHZZUXehKrzUGzbg9jPSdn6pVsMc9TXNH6JGe5RJLmHwWey3MC1p8Hs2zhjw5P439P57NToatraDX9ZwvBtK4EzZzRjWbyGdicheTPjeYKCzvPCLxDkTFtPCM9VZGGXSN2Bne92NLDvf6ntNm5pxsPkZGxPe4w9Eq26gkE83hZyrYXKaiDh8TbqbHatSkw"); } diff --git a/rust/tw_any_coin/tests/chains/solana/solana_transaction.rs b/rust/tw_any_coin/tests/chains/solana/solana_transaction.rs new file mode 100644 index 00000000000..6d00329d02f --- /dev/null +++ b/rust/tw_any_coin/tests/chains/solana/solana_transaction.rs @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use std::borrow::Cow; +use tw_any_coin::test_utils::sign_utils::{AnySignerHelper, CompilerHelper, PreImageHelper}; +use tw_any_coin::test_utils::transaction_decode_utils::TransactionDecoderHelper; +use tw_coin_registry::coin_type::CoinType; +use tw_encoding::base58::Alphabet; +use tw_encoding::{base58, base64}; +use tw_proto::Common::Proto::SigningError; +use tw_proto::Solana::Proto; +use tw_proto::Solana::Proto::mod_RawMessage as raw_message; +use tw_proto::Solana::Proto::mod_RawMessage::OneOfmessage as MessageType; + +fn b58(s: &str) -> Cow<'static, [u8]> { + base58::decode(s, Alphabet::BITCOIN).unwrap().into() +} + +fn check_and_update_recent_blockhash( + tx: &mut Proto::RawMessage, + expected_prev: &str, + updated: &str, +) { + let recent_blockhash = match tx.message { + MessageType::legacy(ref mut legacy) => &mut legacy.recent_blockhash, + MessageType::v0(ref mut v0) => &mut v0.recent_blockhash, + MessageType::None => unreachable!(), + }; + assert_eq!(recent_blockhash, expected_prev); + *recent_blockhash = updated.to_string().into(); +} + +const PREV_RECENT_BLOCKHASH: &str = "H4gZ56AdmHfZj1F36oWrxDJMUJ8ph7XdTHtmsbtHZshG"; +const NEW_RECENT_BLOCKHASH: &str = "CyPYVsYWrsJNfVpi8aazu7WsrswNFuDd385z6GNoBGUg"; + +const SENDER_PRIVATE_KEY: &str = "9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"; +const SENDER_PUBLIC_KEY: &str = "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"; +const FEE_PAYER_PRIVATE_KEY: &str = "66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"; +const FEE_PAYER_PUBLIC_KEY: &str = "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"; + +const PREV_SENDER_SIGNATURE: &str = + "37UT8U6DdqaWqV6yQuHcRN3JpMefDgVna8LJJPmmDNKYMwmefEgvcreSg9AqSsT7cU2rMBNyDktEtDU19ZqqZvML"; +const NEW_SENDER_SIGNATURE: &str = + "3aqBe5B9uj9PBdhLwbdDkH5X9Wa2E49bDvjaMJyZWj68TeQvAavw9dizTuUkRLLqucgA4NP9SDtT261bPXoHAHEj"; +const PREV_FEE_PAYER_SIGNATURE: &str = + "3KbvREZUat76wgWMtnJfWbJL74Vzh4U2eabVJa3Z3bb2fPtW8AREP5pbmRwUrxZCESbTomWpL41PeKDcPGbojsej"; +const NEW_FEE_PAYER_SIGNATURE: &str = + "2DaVFTx5i3fuvBvyt3s3ti4FajmYa8DCcur5Bjf9cgoFA9CnQRM6RNLTrMQ537WmRkQw9Qw3iojfxVrzQy7jWxT7"; + +/// https://explorer.solana.com/tx/3KbvREZUat76wgWMtnJfWbJL74Vzh4U2eabVJa3Z3bb2fPtW8AREP5pbmRwUrxZCESbTomWpL41PeKDcPGbojsej?cluster=devnet +const PREV_ENCODED_TX: &str = "AnQTYwZpkm3fs4SdLxnV6gQj3hSLsyacpxDdLMALYWObm722f79IfYFTbZeFK9xHtMumiDOWAM2hHQP4r/GtbARpncaXgOVFv7OgbRLMbuCEJHO1qwcdCbtH72VzyzU8yw9sqqHIAaCUE8xaQTgT6Z5IyZfeyMe2QGJIfOjz65UPAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8Aqe6sdLXiXSDILEtzckCjkjchiSf6zVGpMYiAE5BE2IqHAQUEAgQDAQoMoA8AAAAAAAAG"; +const NEW_ENCODED_TX: &str = "Ajzc/Tke0CG8Cew5qFa6xZI/7Ya3DN0M8Ige6tKPsGzhg8Bw9DqL18KUrEZZ1F4YqZBo4Rv+FsDT8A7Nss7p4A6BNVZzzGprCJqYQeNg0EVIbmPc6mDitNniHXGeKgPZ6QZbM4FElw9O7IOFTpOBPvQFeqy0vZf/aayncL8EK/UEAgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqbHiki6ThNH3auuyZPQpJntnN0mA//56nMpK/6HIuu8xAQUEAgQDAQoMoA8AAAAAAAAG"; +const ENCODED_UNSIGNED_MSG: &str = "AgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqbHiki6ThNH3auuyZPQpJntnN0mA//56nMpK/6HIuu8xAQUEAgQDAQoMoA8AAAAAAAAG"; + +#[test] +fn test_solana_decode_transaction() { + let encoded_tx = base64::decode("AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAHEIoR5xuWyrvjIW4xU7CWlPOfyFAiy8B295hGo6tNjBmRCgUkQaFYTleMcAX2p74eBXQZd1dwDyQZAPJfSv2KGc5kcFLJj5qd2BVMaSNGVPfVBm74GbLwUq5/U1Ccdqc2gokZQxRDpMq7aeToP3nRaWIP4RXMxN+LJetccXMPq/QumgOqt7kkqk07cyPCKgYoQ4fQtOqqZn5sEqjWHYj3CDS5ha48uggePWu090s1ff4yoCjAvULeZ+cqYFn+Adk5Teyfw71W3u/F6VTnLQEPW96gJr5Kcm3bGi08n224JyF++PTko52VL0CIM2xtl0WkvNslD6Wawxr7yd9HYllN4Lz8lFwXilWGgyJdOq1qqBuZbE49glHeCO/sJHNnIHC0BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwZGb+UhFzL/7K26csOb57yM5bvF9xJrLEObOkAAAAAEedVb8jHAbu50xW7OaBUH/bGy3qP0jlECsc2iVrwTjwbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCpjJclj04kifG7PRApFI4NgwtaE5na/xCEBI572Nvp+Fm0P/on9df2SnTAmx8pWHneSwmrNt/J3VFLMhqns4zl6OL4d+g9rsaIj0Orta57MRu3jDSWCJf85ae4LBbiD/GXvOojZjsHekJrpRUuPggLJr943hDVD5UareeEucjCvaoHCgAFAsBcFQAKAAkDBBcBAAAAAAANBgAGACMJDAEBCQIABgwCAAAAAMqaOwAAAAAMAQYBEQs1DA8ABgEFAiMhCwsOCx0MDxoBGQcYBAgDJBscDB4PBwUQEhEfFR8UFwcFISITHw8MDCAfFgstwSCbM0HWnIEAAwAAABEBZAABCh0BAyZHAQMAypo7AAAAAJaWFAYAAAAAMgAADAMGAAABCQPZoILFk7gfE2y5bt3AC+g/4OwNzdiHKBhIbdeYvYFEjQPKyMkExMUkx0R25UNa/g5KsG0vfUwdUJ8e8HecK/Jkd3qm9XefBOB0BaD1+J+dBJz09vfyGuRYZH09HfdE/kL8v6Ql+H03+tO+9lMmmVg8O1c6gAN6eX0Cbn4=", false).unwrap(); + + // Step 1: Decode the transaction. + + let mut decoder = TransactionDecoderHelper::::default(); + let output = decoder.decode(CoinType::Solana, encoded_tx); + + assert_eq!(output.error, SigningError::OK); + + let expected_msg = raw_message::MessageV0 { + header: Some(raw_message::MessageHeader { + num_required_signatures: 1, + num_readonly_signed_accounts: 0, + num_readonly_unsigned_accounts: 7, + }), + account_keys: vec![ + "AHy6YZA8BsHgQfVkk7MbwpAN94iyN7Nf1zN4nPqUN32Q".into(), + "g7dD1FHSemkUQrX1Eak37wzvDjscgBW2pFCENwjLdMX".into(), + "7m57LBTxtzhWn6WdFxKtnoJLBQXyNERLYebebXLVaKy3".into(), + "AEBCPtV8FFkWFAKxrz7mbYvobpkZuWaRWQCyJVRaheUD".into(), + "BND2ehwWVeHVA5EtMm2b7Vu51AT8f2PNWusS9KQX5moy".into(), + "DVCeozFGbe6ew3eWTnZByjHeYqTq1cvbrB7JJhkLxaRJ".into(), + "GvgWmk8iPACw1AEMt47WzkuTkKoSGbn4Xk3aLM8vdbJD".into(), + "HkphEpUqnFBxBuCPEq5j1HA9L8EwmsmRT6UcFKziptM1".into(), + "Hzxx6b5a7dmmJeDXLQzr4dTrc2HGK9ar5YRakZgr3ZZ7".into(), + "11111111111111111111111111111111".into(), + "ComputeBudget111111111111111111111111111111".into(), + "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4".into(), + "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA".into(), + "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL".into(), + "D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf".into(), + "GGztQqQ6pCPaJQnNpXBgELr5cs3WwDakRbh1iEMzjgSJ".into(), + ], + recent_blockhash: "DiSimxK2z1cRa6yD4goqte3rDMmghJAD8WDUZEab2CzD".into(), + instructions: vec![ + raw_message::Instruction { + program_id: 10, + accounts: vec![], + program_data: b58("K1FDJ7"), + }, + raw_message::Instruction { + program_id: 10, + accounts: vec![], + program_data: b58("3E9ErJ5MrzbZ"), + }, + raw_message::Instruction { + program_id: 13, + accounts: vec![0, 6, 0, 35, 9, 12], + program_data: b58("2"), + }, + raw_message::Instruction { + program_id: 9, + accounts: vec![0, 6], + program_data: b58("3Bxs3zzLZLuLQEYX"), + }, + raw_message::Instruction { + program_id: 12, + accounts: vec![6], + program_data: b58("J"), + }, + raw_message::Instruction { + program_id: 11, + accounts: vec![ + 12, 15, 0, 6, 1, 5, 2, 35, 33, 11, 11, 14, 11, 29, 12, 15, 26, 1, 25, 7, 24, 4, + 8, 3, 36, 27, 28, 12, 30, 15, 7, 5, 16, 18, 17, 31, 21, 31, 20, 23, 7, 5, 33, + 34, 19, 31, 15, 12, 12, 32, 31, 22, 11, + ], + program_data: b58("5n9zLuyvSGkuf4iDD6PfDvzvzehUkDghmApUkZSXSx57jF9RGSH5Y23tzFJDG3"), + }, + raw_message::Instruction { + program_id: 12, + accounts: vec![6, 0, 0], + program_data: b58("A"), + }, + ], + address_table_lookups: vec![ + raw_message::MessageAddressTableLookup { + account_key: "FeXRmSWmwChZbB2EC7Qjw9XKk28yBrPj3k3nzT1DKfak".into(), + writable_indexes: vec![202, 200, 201], + readonly_indexes: vec![196, 197, 36, 199], + }, + raw_message::MessageAddressTableLookup { + account_key: "5cFsmTCEfmvpBUBHqsWZnf9n5vTWLYH2LT8X7HdShwxP".into(), + writable_indexes: vec![160, 245, 248, 159, 157], + readonly_indexes: vec![156, 244, 246, 247], + }, + raw_message::MessageAddressTableLookup { + account_key: "HJ5StCvsDU4JsvK39VcsHjaoTRTtQU749MQ9qUsJaG1m".into(), + writable_indexes: vec![122, 121, 125], + readonly_indexes: vec![110, 126], + }, + ], + }; + let expected = Proto::RawMessage { + signatures: vec![Proto::PubkeySignature { + pubkey: "AHy6YZA8BsHgQfVkk7MbwpAN94iyN7Nf1zN4nPqUN32Q".into(), + signature: "1111111111111111111111111111111111111111111111111111111111111111".into(), + }], + message: MessageType::v0(expected_msg), + }; + + assert_eq!(output.transaction, Some(expected)); +} + +#[test] +fn test_solana_decode_transaction_update_blockhash_and_sign_with_external_fee_payer() { + // https://explorer.solana.com/tx/3KbvREZUat76wgWMtnJfWbJL74Vzh4U2eabVJa3Z3bb2fPtW8AREP5pbmRwUrxZCESbTomWpL41PeKDcPGbojsej?cluster=devnet + let encoded_tx = base64::decode(PREV_ENCODED_TX, false).unwrap(); + + // Step 1: Decode the transaction. + let mut decoder = TransactionDecoderHelper::::default(); + let output = decoder.decode(CoinType::Solana, encoded_tx); + + assert_eq!(output.error, SigningError::OK); + let mut decoded_tx = output.transaction.unwrap(); + + let expected_signatures = vec![ + Proto::PubkeySignature { + pubkey: FEE_PAYER_PUBLIC_KEY.into(), + signature: PREV_FEE_PAYER_SIGNATURE.into(), + }, + Proto::PubkeySignature { + pubkey: SENDER_PUBLIC_KEY.into(), + signature: PREV_SENDER_SIGNATURE.into(), + }, + ]; + assert_eq!(decoded_tx.signatures, expected_signatures); + + // Step 2. Update the recent-blockhash. + + check_and_update_recent_blockhash(&mut decoded_tx, PREV_RECENT_BLOCKHASH, NEW_RECENT_BLOCKHASH); + + // Step 3. Re-sign the updated transaction. + + let input = Proto::SigningInput { + private_key: b58(SENDER_PRIVATE_KEY), + fee_payer_private_key: b58(FEE_PAYER_PRIVATE_KEY), + raw_message: Some(decoded_tx), + tx_encoding: Proto::Encoding::Base64, + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, NEW_ENCODED_TX); +} + +/// The test is similar to `test_solana_decode_transaction_update_blockhash_and_resign_with_external_fee_payer`, +/// but with only difference that the external fee payer private key is not specified, +/// and the corresponding signature is set at [`RawMessage::signatures`]. +#[test] +fn test_solana_decode_transaction_update_blockhash_and_sign_with_external_fee_payer_signature() { + // https://explorer.solana.com/tx/3KbvREZUat76wgWMtnJfWbJL74Vzh4U2eabVJa3Z3bb2fPtW8AREP5pbmRwUrxZCESbTomWpL41PeKDcPGbojsej?cluster=devnet + let encoded_tx = base64::decode(PREV_ENCODED_TX, false).unwrap(); + + // Step 1: Decode the transaction. + let mut decoder = TransactionDecoderHelper::::default(); + let output = decoder.decode(CoinType::Solana, encoded_tx); + + assert_eq!(output.error, SigningError::OK); + let mut decoded_tx = output.transaction.unwrap(); + + let expected_signatures = vec![ + Proto::PubkeySignature { + pubkey: FEE_PAYER_PUBLIC_KEY.into(), + signature: PREV_FEE_PAYER_SIGNATURE.into(), + }, + Proto::PubkeySignature { + pubkey: SENDER_PUBLIC_KEY.into(), + signature: PREV_SENDER_SIGNATURE.into(), + }, + ]; + assert_eq!(decoded_tx.signatures, expected_signatures); + + // Step 2. Update the recent-blockhash and external fee payer signature. + + decoded_tx.signatures[0].signature = NEW_FEE_PAYER_SIGNATURE.into(); + + check_and_update_recent_blockhash(&mut decoded_tx, PREV_RECENT_BLOCKHASH, NEW_RECENT_BLOCKHASH); + + // Step 3. Re-sign the updated transaction. + + let input = Proto::SigningInput { + private_key: b58(SENDER_PRIVATE_KEY), + // Do not set the fee payer private key to test if the [`RawMessage::signatures`] is used instead. + // fee_payer_private_key: b58(FEE_PAYER_PRIVATE_KEY), + raw_message: Some(decoded_tx), + tx_encoding: Proto::Encoding::Base64, + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, NEW_ENCODED_TX); +} + +#[test] +fn test_solana_decode_transaction_update_blockhash_and_sign_no_matching_pubkey() { + let encoded_tx = base64::decode("AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAHEIoR5xuWyrvjIW4xU7CWlPOfyFAiy8B295hGo6tNjBmRCgUkQaFYTleMcAX2p74eBXQZd1dwDyQZAPJfSv2KGc5kcFLJj5qd2BVMaSNGVPfVBm74GbLwUq5/U1Ccdqc2gokZQxRDpMq7aeToP3nRaWIP4RXMxN+LJetccXMPq/QumgOqt7kkqk07cyPCKgYoQ4fQtOqqZn5sEqjWHYj3CDS5ha48uggePWu090s1ff4yoCjAvULeZ+cqYFn+Adk5Teyfw71W3u/F6VTnLQEPW96gJr5Kcm3bGi08n224JyF++PTko52VL0CIM2xtl0WkvNslD6Wawxr7yd9HYllN4Lz8lFwXilWGgyJdOq1qqBuZbE49glHeCO/sJHNnIHC0BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwZGb+UhFzL/7K26csOb57yM5bvF9xJrLEObOkAAAAAEedVb8jHAbu50xW7OaBUH/bGy3qP0jlECsc2iVrwTjwbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCpjJclj04kifG7PRApFI4NgwtaE5na/xCEBI572Nvp+Fm0P/on9df2SnTAmx8pWHneSwmrNt/J3VFLMhqns4zl6OL4d+g9rsaIj0Orta57MRu3jDSWCJf85ae4LBbiD/GXvOojZjsHekJrpRUuPggLJr943hDVD5UareeEucjCvaoHCgAFAsBcFQAKAAkDBBcBAAAAAAANBgAGACMJDAEBCQIABgwCAAAAAMqaOwAAAAAMAQYBEQs1DA8ABgEFAiMhCwsOCx0MDxoBGQcYBAgDJBscDB4PBwUQEhEfFR8UFwcFISITHw8MDCAfFgstwSCbM0HWnIEAAwAAABEBZAABCh0BAyZHAQMAypo7AAAAAJaWFAYAAAAAMgAADAMGAAABCQPZoILFk7gfE2y5bt3AC+g/4OwNzdiHKBhIbdeYvYFEjQPKyMkExMUkx0R25UNa/g5KsG0vfUwdUJ8e8HecK/Jkd3qm9XefBOB0BaD1+J+dBJz09vfyGuRYZH09HfdE/kL8v6Ql+H03+tO+9lMmmVg8O1c6gAN6eX0Cbn4=", false).unwrap(); + + // Step 1: Decode the transaction. + let mut decoder = TransactionDecoderHelper::::default(); + let output = decoder.decode(CoinType::Solana, encoded_tx); + + assert_eq!(output.error, SigningError::OK); + let mut decoded_tx = output.transaction.unwrap(); + + // Step 2. Clear previous signatures. + decoded_tx.signatures.clear(); + + // Step 3. Re-sign the updated transaction. + + let input = Proto::SigningInput { + // There is no matching pubkey in the transaction account keys. + private_key: b58("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr"), + raw_message: Some(decoded_tx), + ..Proto::SigningInput::default() + }; + + let mut signer = AnySignerHelper::::default(); + let output = signer.sign(CoinType::Solana, input); + + assert_eq!(output.error, SigningError::Error_missing_private_key); +} + +#[test] +fn test_solana_decode_transaction_update_blockhash_preimage_hash_and_compile() { + // https://explorer.solana.com/tx/3KbvREZUat76wgWMtnJfWbJL74Vzh4U2eabVJa3Z3bb2fPtW8AREP5pbmRwUrxZCESbTomWpL41PeKDcPGbojsej?cluster=devnet + let encoded_tx = base64::decode(PREV_ENCODED_TX, false).unwrap(); + + // Step 1: Decode the transaction. + let mut decoder = TransactionDecoderHelper::::default(); + let decode_output = decoder.decode(CoinType::Solana, encoded_tx); + + assert_eq!(decode_output.error, SigningError::OK); + let mut decoded_tx = decode_output.transaction.unwrap(); + + // Step 2. Update recent blockhash. + check_and_update_recent_blockhash(&mut decoded_tx, PREV_RECENT_BLOCKHASH, NEW_RECENT_BLOCKHASH); + + // Step 3. Pre-image hash the transaction. + + let input = Proto::SigningInput { + // There is no matching pubkey in the transaction account keys. + sender: SENDER_PUBLIC_KEY.into(), + raw_message: Some(decoded_tx), + tx_encoding: Proto::Encoding::Base64, + ..Proto::SigningInput::default() + }; + + let mut preimager = PreImageHelper::::default(); + let preimage_output = preimager.pre_image_hashes(CoinType::Solana, &input); + + assert_eq!(preimage_output.error, SigningError::OK); + let expected_unsigned_msg = base64::decode(ENCODED_UNSIGNED_MSG, false).unwrap(); + assert_eq!(preimage_output.data, expected_unsigned_msg); + + // Step 4. Compile the updated transaction with signatures. + + let fee_payer_pubkey = b58(FEE_PAYER_PUBLIC_KEY).to_vec(); + let sender_pubkey = b58(SENDER_PUBLIC_KEY).to_vec(); + + let fee_payer_signature = b58(NEW_FEE_PAYER_SIGNATURE).to_vec(); + let sender_signature = b58(NEW_SENDER_SIGNATURE).to_vec(); + + let mut compiler = CompilerHelper::::default(); + let compile_output = compiler.compile( + CoinType::Solana, + &input, + vec![fee_payer_signature, sender_signature], + vec![fee_payer_pubkey, sender_pubkey], + ); + + assert_eq!(compile_output.error, SigningError::OK); + assert_eq!(compile_output.encoded, NEW_ENCODED_TX); +} diff --git a/rust/tw_any_coin/tests/chains/solana/solana_wallet_connect.rs b/rust/tw_any_coin/tests/chains/solana/solana_wallet_connect.rs new file mode 100644 index 00000000000..49c4270b27d --- /dev/null +++ b/rust/tw_any_coin/tests/chains/solana/solana_wallet_connect.rs @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use serde_json::json; +use std::borrow::Cow; +use tw_any_coin::test_utils::sign_utils::AnySignerHelper; +use tw_any_coin::test_utils::wallet_connect_utils::WalletConnectRequestHelper; +use tw_coin_registry::coin_type::CoinType; +use tw_encoding::base58; +use tw_encoding::base58::Alphabet; +use tw_proto::Common::Proto::SigningError; +use tw_proto::Solana::Proto; +use tw_proto::WalletConnect::Proto as WCProto; + +fn b58(s: &str) -> Cow<'static, [u8]> { + base58::decode(s, Alphabet::BITCOIN).unwrap().into() +} + +fn pubkey_signature(pubkey: &str, signature: &str) -> Proto::PubkeySignature<'static> { + Proto::PubkeySignature { + pubkey: pubkey.to_string().into(), + signature: signature.to_string().into(), + } +} + +#[test] +fn test_solana_sign_wallet_connect_case_1() { + let tx_encoded_to_sign = "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEDZsL1CMnFVcrMn7JtiOiN1U4hC7WovOVof2DX51xM0H/GizyJTHgrBanCf8bGbrFNTn0x3pCGq30hKbywSTr6AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgIAAQwCAAAAKgAAAAAAAAA="; + let tx_encoded_signed = "AQPWaOi7dMdmQpXi8HyQQKwiqIftrg1igGQxGtZeT50ksn4wAnyH4DtDrkkuE0fqgx80LTp4LwNN9a440SrmoA8BAAEDZsL1CMnFVcrMn7JtiOiN1U4hC7WovOVof2DX51xM0H/GizyJTHgrBanCf8bGbrFNTn0x3pCGq30hKbywSTr6AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgIAAQwCAAAAKgAAAAAAAAA="; + + let request_params = json!({ + "transaction": tx_encoded_to_sign + }); + let input = WCProto::ParseRequestInput { + protocol: WCProto::Protocol::V2, + method: WCProto::Method::SolanaSignTransaction, + payload: request_params.to_string().into(), + }; + + let mut parser = WalletConnectRequestHelper::default(); + let parsing_output = parser.parse(CoinType::Solana, &input); + + let mut signing_input = match parsing_output.signing_input_oneof { + WCProto::mod_ParseRequestOutput::OneOfsigning_input_oneof::solana(input) => input, + _ => unreachable!(), + }; + + // Set missing private key. + signing_input.private_key = b58("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr"); + signing_input.tx_encoding = Proto::Encoding::Base64; + + let mut signer = AnySignerHelper::::default(); + let signing_output = signer.sign(CoinType::Solana, signing_input); + + assert_eq!(signing_output.error, SigningError::OK); + assert_eq!(signing_output.encoded, tx_encoded_signed); + let expected_signatures = vec![pubkey_signature( + "7v91N7iZ9mNicL8WfG6cgSCKyRXydQjLh6UYBWwm6y1Q", + "5T6uZBHnHFd8uWErDBTFRVkbKuhbcm94K5MJ2beTYDruzqv4FjS7EMKvC94ZfxNAiWUXZ6bZxS3WXUbhJwYNPWn", + )]; + assert_eq!(signing_output.signatures, expected_signatures); +} + +#[test] +fn test_solana_sign_wallet_connect_with_external_fee_payer() { + let tx_encoded_to_sign = "AgVnzLgdHFLYfmSR0mp/npPzRqaabwMYqy8dj7ohDQsDQ0NzDH7V06lqN+LrTpfLgT7smx+rgrU4+c991xODzQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAGCssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFqUGMlXapwAxr2PwiP0cVc/cXJIjeEKqE2/Y8U6ILrnF0haJP+0BwRhu21/HIt1jGstyQAp1VG1/U6s2C1l4wIgwjHcAvSCmA99mRXB7PUzdAkdOMBgtJSH+cXZMuB37XY7RCyzkSFX8TqTPQE0KC0DK1/+zQGi2/G3eQYI3wAupwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABt324ddloZPZy+FGzut5rBy0he1fWzeROoz1hX7/AKkGp9UXGSxcUSGMyUw9SvF/WNruCJuh/UTj29mKAAAAAIyXJY9OJInxuz0QKRSODYMLWhOZ2v8QhASOe9jb6fhZzipDMbzjZw5uqL7f9ZCMbZH4M6Maf96sFpeMJhoYAdUCCQcAAgQFBgcIAAcEAwUCAQoMoA8AAAAAAAAG"; + // https://explorer.solana.com/tx/7GZGFT2VA4dpJBBwjiMqj1o8yChDvoCsqnQ7xz4GxY513W3efxRqbB8y7g4tH2GEGQRx4vCkKipG1sMaDEen1A2?cluster=devnet + let tx_encoded_signed = "AgVnzLgdHFLYfmSR0mp/npPzRqaabwMYqy8dj7ohDQsDQ0NzDH7V06lqN+LrTpfLgT7smx+rgrU4+c991xODzQOK74uU+ESOWD5RtUxJNbGRG7mOvDBTpGhZhlRRWEsR6D/hpQc0r8kzuXMUK3OptIklCXmLKjptzYUcCAlwTUsOAgAGCssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFqUGMlXapwAxr2PwiP0cVc/cXJIjeEKqE2/Y8U6ILrnF0haJP+0BwRhu21/HIt1jGstyQAp1VG1/U6s2C1l4wIgwjHcAvSCmA99mRXB7PUzdAkdOMBgtJSH+cXZMuB37XY7RCyzkSFX8TqTPQE0KC0DK1/+zQGi2/G3eQYI3wAupwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABt324ddloZPZy+FGzut5rBy0he1fWzeROoz1hX7/AKkGp9UXGSxcUSGMyUw9SvF/WNruCJuh/UTj29mKAAAAAIyXJY9OJInxuz0QKRSODYMLWhOZ2v8QhASOe9jb6fhZzipDMbzjZw5uqL7f9ZCMbZH4M6Maf96sFpeMJhoYAdUCCQcAAgQFBgcIAAcEAwUCAQoMoA8AAAAAAAAG"; + + let request_params = json!({ + "transaction": tx_encoded_to_sign + }); + let input = WCProto::ParseRequestInput { + protocol: WCProto::Protocol::V2, + method: WCProto::Method::SolanaSignTransaction, + payload: request_params.to_string().into(), + }; + + let mut parser = WalletConnectRequestHelper::default(); + let parsing_output = parser.parse(CoinType::Solana, &input); + + let mut signing_input = match parsing_output.signing_input_oneof { + WCProto::mod_ParseRequestOutput::OneOfsigning_input_oneof::solana(input) => input, + _ => unreachable!(), + }; + + // Set missing private key. + signing_input.private_key = b58("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); + signing_input.tx_encoding = Proto::Encoding::Base64; + + let mut signer = AnySignerHelper::::default(); + let signing_output = signer.sign(CoinType::Solana, signing_input); + + assert_eq!(signing_output.error, SigningError::OK); + assert_eq!(signing_output.encoded, tx_encoded_signed); + + let expected_signatures = vec![ + pubkey_signature( + "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ", + "7GZGFT2VA4dpJBBwjiMqj1o8yChDvoCsqnQ7xz4GxY513W3efxRqbB8y7g4tH2GEGQRx4vCkKipG1sMaDEen1A2", + ), + pubkey_signature( + "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V", + "3n7RHTCBAtnFVuDn5eRbyQB24h6AqajJi5nGMPrfnUVFUDh2Cb8AoaJ7mVtjnv73V4HaJCzSwCLAj3zcGEaFftWZ" + ) + ]; + assert_eq!(signing_output.signatures, expected_signatures); +} diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs index cb8dba03f75..741502bdf93 100644 --- a/rust/tw_aptos/src/entry.rs +++ b/rust/tw_aptos/src/entry.rs @@ -13,6 +13,7 @@ use tw_coin_entry::error::{AddressError, AddressResult}; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_coin_entry::prefix::NoPrefix; use tw_keypair::tw::PublicKey; @@ -33,6 +34,7 @@ impl CoinEntry for AptosEntry { type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; type WalletConnector = NoWalletConnector; + type TransactionDecoder = NoTransactionDecoder; #[inline] fn parse_address( diff --git a/rust/tw_bitcoin/src/entry.rs b/rust/tw_bitcoin/src/entry.rs index 18bab2ce741..a2b60b3fbb5 100644 --- a/rust/tw_bitcoin/src/entry.rs +++ b/rust/tw_bitcoin/src/entry.rs @@ -11,6 +11,7 @@ use tw_coin_entry::error::{AddressError, AddressResult}; use tw_coin_entry::modules::json_signer::NoJsonSigner; use tw_coin_entry::modules::message_signer::NoMessageSigner; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_coin_entry::prefix::NoPrefix; use tw_coin_entry::signing_output_error; @@ -47,6 +48,7 @@ impl CoinEntry for BitcoinEntry { type PlanBuilder = NoPlanBuilder; type MessageSigner = NoMessageSigner; type WalletConnector = NoWalletConnector; + type TransactionDecoder = NoTransactionDecoder; #[inline] fn parse_address( diff --git a/rust/tw_coin_entry/src/coin_entry.rs b/rust/tw_coin_entry/src/coin_entry.rs index 62133a7e61f..b6a4cd10e6b 100644 --- a/rust/tw_coin_entry/src/coin_entry.rs +++ b/rust/tw_coin_entry/src/coin_entry.rs @@ -14,6 +14,7 @@ use tw_memory::Data; use tw_proto::{MessageRead, MessageWrite}; use crate::modules::message_signer::MessageSigner; +use crate::modules::transaction_decoder::TransactionDecoder; use crate::modules::wallet_connector::WalletConnector; pub use tw_proto::{ProtoError, ProtoResult}; @@ -63,6 +64,10 @@ pub trait CoinEntry { /// /// **Optional**. Use `NoWalletConnector` if the blockchain does not support WalletConnect transactions. type WalletConnector: WalletConnector; + /// TransactionDecoder - the module allows to decode transactions from binary representation. + /// + /// **Optional**. Use `NoTransactionDecoder` if the blockchain does not support transaction decoding yet. + type TransactionDecoder: TransactionDecoder; /// Tries to parse `Self::Address` from the given `address` string by `coin` type and address `prefix`. fn parse_address( @@ -135,4 +140,11 @@ pub trait CoinEntry { fn wallet_connector(&self) -> Option { None } + + /// It is optional, Transaction decoding. + /// Returns `Ok(None)` if the blockchain does not support transaction decoding yet. + #[inline] + fn transaction_decoder(&self) -> Option { + None + } } diff --git a/rust/tw_coin_entry/src/coin_entry_ext.rs b/rust/tw_coin_entry/src/coin_entry_ext.rs index 93e4243aa4a..4bf882a08e0 100644 --- a/rust/tw_coin_entry/src/coin_entry_ext.rs +++ b/rust/tw_coin_entry/src/coin_entry_ext.rs @@ -10,6 +10,7 @@ use crate::error::{AddressResult, SigningError, SigningErrorType}; use crate::modules::json_signer::JsonSigner; use crate::modules::message_signer::MessageSigner; use crate::modules::plan_builder::PlanBuilder; +use crate::modules::transaction_decoder::TransactionDecoder; use crate::modules::wallet_connector::WalletConnector; use crate::prefix::AddressPrefix; use tw_keypair::tw::{PrivateKey, PublicKey}; @@ -91,6 +92,9 @@ pub trait CoinEntryExt { coin: &dyn CoinContext, input: &[u8], ) -> SigningResult; + + /// Decodes a transaction from binary representation. + fn decode_transaction(&self, coin: &dyn CoinContext, tx: &[u8]) -> SigningResult; } impl CoinEntryExt for T @@ -230,4 +234,13 @@ where let output = wc_connector.parse_request(coin, input); serialize(&output).map_err(SigningError::from) } + + fn decode_transaction(&self, coin: &dyn CoinContext, tx: &[u8]) -> SigningResult { + let Some(tx_decoder) = self.transaction_decoder() else { + return Err(SigningError(SigningErrorType::Error_not_supported)); + }; + + let output = tx_decoder.decode_transaction(coin, tx); + serialize(&output).map_err(SigningError::from) + } } diff --git a/rust/tw_coin_entry/src/derivation.rs b/rust/tw_coin_entry/src/derivation.rs index 91ed87b0e2c..b718b013f70 100644 --- a/rust/tw_coin_entry/src/derivation.rs +++ b/rust/tw_coin_entry/src/derivation.rs @@ -9,6 +9,7 @@ pub enum Derivation { /// Default derivation. #[default] Default = 0, + Solana = 6, } impl Derivation { @@ -16,6 +17,7 @@ impl Derivation { pub fn from_raw(derivation: u32) -> Option { match derivation { 0 => Some(Derivation::Default), + 6 => Some(Derivation::Solana), _ => None, } } diff --git a/rust/tw_coin_entry/src/error.rs b/rust/tw_coin_entry/src/error.rs index bd28fddd6c3..a8a75e9a2e5 100644 --- a/rust/tw_coin_entry/src/error.rs +++ b/rust/tw_coin_entry/src/error.rs @@ -40,6 +40,7 @@ pub enum AddressError { InvalidRegistry, InvalidInput, InvalidChecksum, + Internal, } pub type SigningResult = Result; diff --git a/rust/tw_coin_entry/src/modules/mod.rs b/rust/tw_coin_entry/src/modules/mod.rs index 88cfc996f91..bf9d37ccd6e 100644 --- a/rust/tw_coin_entry/src/modules/mod.rs +++ b/rust/tw_coin_entry/src/modules/mod.rs @@ -7,4 +7,5 @@ pub mod json_signer; pub mod message_signer; pub mod plan_builder; +pub mod transaction_decoder; pub mod wallet_connector; diff --git a/rust/tw_coin_entry/src/modules/transaction_decoder.rs b/rust/tw_coin_entry/src/modules/transaction_decoder.rs new file mode 100644 index 00000000000..238afd006bc --- /dev/null +++ b/rust/tw_coin_entry/src/modules/transaction_decoder.rs @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::coin_context::CoinContext; +use tw_proto::{MessageWrite, NoMessage}; + +pub trait TransactionDecoder { + type Output: MessageWrite; + + /// Parses a signing request received through Wallet Connect. + fn decode_transaction(&self, coin: &dyn CoinContext, tx: &[u8]) -> Self::Output; +} + +/// `NoTransactionDecoder` can't be created since there are no enum variants. +pub enum NoTransactionDecoder {} + +impl TransactionDecoder for NoTransactionDecoder { + type Output = NoMessage; + + fn decode_transaction(&self, _coin: &dyn CoinContext, _tx: &[u8]) -> Self::Output { + panic!("`NoTransactionDecoder` should never be constructed and used") + } +} diff --git a/rust/tw_encoding/src/base58.rs b/rust/tw_encoding/src/base58.rs index c383a69b00e..988b94596bc 100644 --- a/rust/tw_encoding/src/base58.rs +++ b/rust/tw_encoding/src/base58.rs @@ -23,6 +23,32 @@ pub fn decode(input: &str, alphabet: &Alphabet) -> EncodingResult> { .map_err(EncodingError::from) } +pub mod as_base58_bitcoin { + use super::*; + use serde::de::Error; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + /// Serializes the `value` as base58. + pub fn serialize(value: &T, serializer: S) -> Result + where + T: AsRef<[u8]>, + S: Serializer, + { + encode(value.as_ref(), Alphabet::BITCOIN).serialize(serializer) + } + + /// Serializes the `value` as base58. + pub fn deserialize<'de, D, T>(deserializer: D) -> Result + where + D: Deserializer<'de>, + T: for<'a> TryFrom<&'a [u8]>, + { + let s = String::deserialize(deserializer)?; + let data = decode(&s, Alphabet::BITCOIN).map_err(|e| Error::custom(format!("{e:?}")))?; + T::try_from(&data).map_err(|_| Error::custom("Unexpected bytes length")) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/rust/tw_encoding/src/base64.rs b/rust/tw_encoding/src/base64.rs index 66fbbee03bf..827613b036b 100644 --- a/rust/tw_encoding/src/base64.rs +++ b/rust/tw_encoding/src/base64.rs @@ -3,7 +3,8 @@ // Copyright © 2017 Trust Wallet. use crate::{EncodingError, EncodingResult}; -use serde::{Serialize, Serializer}; +use serde::de::Error as DeError; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use tw_memory::Data; pub fn encode(data: &[u8], is_url: bool) -> String { @@ -35,3 +36,16 @@ impl Serialize for Base64Encoded { serializer.serialize_str(&encode(&self.0, is_url)) } } + +impl<'de> Deserialize<'de> for Base64Encoded { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + let is_url = false; + decode(&s, is_url) + .map(Base64Encoded) + .map_err(|e| DeError::custom(format!("{e:?}"))) + } +} diff --git a/rust/tw_ethereum/src/entry.rs b/rust/tw_ethereum/src/entry.rs index 15ad5fbee87..86b351aad25 100644 --- a/rust/tw_ethereum/src/entry.rs +++ b/rust/tw_ethereum/src/entry.rs @@ -8,6 +8,7 @@ use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; use tw_coin_entry::error::{AddressError, AddressResult}; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_coin_entry::prefix::NoPrefix; use tw_evm::address::Address; @@ -35,6 +36,7 @@ impl CoinEntry for EthereumEntry { type PlanBuilder = NoPlanBuilder; type MessageSigner = EthMessageSigner; type WalletConnector = NoWalletConnector; + type TransactionDecoder = NoTransactionDecoder; #[inline] fn parse_address( diff --git a/rust/tw_hash/src/hash_array.rs b/rust/tw_hash/src/hash_array.rs index f76893b5349..b740b2b9735 100644 --- a/rust/tw_hash/src/hash_array.rs +++ b/rust/tw_hash/src/hash_array.rs @@ -36,7 +36,7 @@ pub fn concat( } /// Represents a fixed-length byte array. -#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] pub struct Hash([u8; N]); diff --git a/rust/tw_internet_computer/src/entry.rs b/rust/tw_internet_computer/src/entry.rs index bad58a82a04..446ed2367fd 100644 --- a/rust/tw_internet_computer/src/entry.rs +++ b/rust/tw_internet_computer/src/entry.rs @@ -4,6 +4,7 @@ use std::str::FromStr; +use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_coin_entry::{ coin_context::CoinContext, coin_entry::CoinEntry, @@ -27,22 +28,17 @@ pub struct InternetComputerEntry; impl CoinEntry for InternetComputerEntry { type AddressPrefix = NoPrefix; - type Address = AccountIdentifier; - type SigningInput<'a> = Proto::SigningInput<'a>; - type SigningOutput = Proto::SigningOutput<'static>; - type PreSigningOutput = CompilerProto::PreSigningOutput<'static>; + // Optional modules: type JsonSigner = NoJsonSigner; - type PlanBuilder = NoPlanBuilder; - type MessageSigner = NoMessageSigner; - type WalletConnector = NoWalletConnector; + type TransactionDecoder = NoTransactionDecoder; #[inline] fn parse_address( diff --git a/rust/tw_keypair/src/ed25519/signature.rs b/rust/tw_keypair/src/ed25519/signature.rs index f0f957938a7..85374bb2ff6 100644 --- a/rust/tw_keypair/src/ed25519/signature.rs +++ b/rust/tw_keypair/src/ed25519/signature.rs @@ -11,7 +11,7 @@ use tw_misc::traits::ToBytesVec; /// Represents an `ed25519` signature. /// Source: https://github.com/dalek-cryptography/ed25519-dalek/blob/1.0.1/src/signature.rs#L22-L53 #[allow(non_snake_case)] -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Signature { /// `R` is an `EdwardsPoint`, formed by using an hash function with /// 512-bits output to produce the digest of: diff --git a/rust/tw_ronin/src/entry.rs b/rust/tw_ronin/src/entry.rs index 07e963c6c67..2bf767d2af6 100644 --- a/rust/tw_ronin/src/entry.rs +++ b/rust/tw_ronin/src/entry.rs @@ -10,6 +10,7 @@ use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes}; use tw_coin_entry::derivation::Derivation; use tw_coin_entry::error::{AddressError, AddressResult}; use tw_coin_entry::modules::plan_builder::NoPlanBuilder; +use tw_coin_entry::modules::transaction_decoder::NoTransactionDecoder; use tw_coin_entry::modules::wallet_connector::NoWalletConnector; use tw_coin_entry::prefix::NoPrefix; use tw_evm::evm_entry::EvmEntry; @@ -35,6 +36,7 @@ impl CoinEntry for RoninEntry { type PlanBuilder = NoPlanBuilder; type MessageSigner = EthMessageSigner; type WalletConnector = NoWalletConnector; + type TransactionDecoder = NoTransactionDecoder; #[inline] fn parse_address( diff --git a/rust/wallet_core_rs/Cargo.toml b/rust/wallet_core_rs/Cargo.toml index 9f9fe5c9ea4..effc5c0ad81 100644 --- a/rust/wallet_core_rs/Cargo.toml +++ b/rust/wallet_core_rs/Cargo.toml @@ -8,10 +8,11 @@ name = "wallet_core_rs" crate-type = ["staticlib", "rlib"] # Creates static lib [features] -default = ["bitcoin-legacy", "ethereum-abi", "ethereum-rlp", "solana-transaction"] +default = ["bitcoin-legacy", "ethereum-abi", "ethereum-rlp", "solana-address", "solana-transaction"] bitcoin-legacy = [] ethereum-abi = [] ethereum-rlp = [] +solana-address = [] solana-transaction = [] [dependencies] diff --git a/rust/wallet_core_rs/src/ffi/solana/address.rs b/rust/wallet_core_rs/src/ffi/solana/address.rs new file mode 100644 index 00000000000..9429a8f2836 --- /dev/null +++ b/rust/wallet_core_rs/src/ffi/solana/address.rs @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#![allow(clippy::missing_safety_doc)] + +use std::str::FromStr; +use tw_memory::ffi::tw_string::TWString; +use tw_memory::ffi::RawPtrTrait; +use tw_misc::try_or_else; +use tw_solana::address::SolanaAddress; +use tw_solana::program::stake_program::StakeProgram; + +/// Derive default token address for token +/// +/// \param address Non-null pointer to a Solana Address +/// \param token_mint_address Non-null pointer to a token mint address as a string +/// \return Null pointer if the Default token address for a token is not found, valid pointer otherwise +#[no_mangle] +pub unsafe extern "C" fn tw_solana_address_default_token_address( + address: *const TWString, + token_mint_address: *const TWString, +) -> *mut TWString { + let main_address = try_or_else!(TWString::from_ptr_as_ref(address), std::ptr::null_mut); + let main_address = try_or_else!(main_address.as_str(), std::ptr::null_mut); + + let token_mint_address = try_or_else!( + TWString::from_ptr_as_ref(token_mint_address), + std::ptr::null_mut + ); + let token_mint_address = try_or_else!(token_mint_address.as_str(), std::ptr::null_mut); + + let main_address = try_or_else!(SolanaAddress::from_str(main_address), std::ptr::null_mut); + let token_mint_address = try_or_else!( + SolanaAddress::from_str(token_mint_address), + std::ptr::null_mut + ); + + let token_address = try_or_else!( + StakeProgram::get_associated_token_address(main_address, token_mint_address), + std::ptr::null_mut + ); + + TWString::from(token_address.to_string()).into_ptr() +} diff --git a/rust/wallet_core_rs/src/ffi/solana/mod.rs b/rust/wallet_core_rs/src/ffi/solana/mod.rs index 073788657fa..19e39015399 100644 --- a/rust/wallet_core_rs/src/ffi/solana/mod.rs +++ b/rust/wallet_core_rs/src/ffi/solana/mod.rs @@ -2,5 +2,7 @@ // // Copyright © 2017 Trust Wallet. +#[cfg(feature = "solana-address")] +pub mod address; #[cfg(feature = "solana-transaction")] pub mod transaction; diff --git a/rust/wallet_core_rs/tests/solana_address.rs b/rust/wallet_core_rs/tests/solana_address.rs new file mode 100644 index 00000000000..7bbe4ed7b39 --- /dev/null +++ b/rust/wallet_core_rs/tests/solana_address.rs @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_memory::test_utils::tw_string_helper::TWStringHelper; +use wallet_core_rs::ffi::solana::address::tw_solana_address_default_token_address; + +#[test] +fn test_solana_address_default_token_address() { + let main_address = "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"; + let main_address = TWStringHelper::create(main_address); + + let token_mint_address = "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"; + let token_mint_address = TWStringHelper::create(token_mint_address); + + let actual = unsafe { + TWStringHelper::wrap(tw_solana_address_default_token_address( + main_address.ptr(), + token_mint_address.ptr(), + )) + } + .to_string() + .expect("!tw_solana_address_default_token_address returned a nullptr"); + + assert_eq!(actual, "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); +} diff --git a/rust/wallet_core_rs/tests/solana_transaction.rs b/rust/wallet_core_rs/tests/solana_transaction.rs index 66b10315eb3..773f2e73a23 100644 --- a/rust/wallet_core_rs/tests/solana_transaction.rs +++ b/rust/wallet_core_rs/tests/solana_transaction.rs @@ -111,5 +111,5 @@ fn test_solana_transaction_update_blockhash_and_sign_empty_private_keys() { assert_eq!(output.encoded, expected); let expected_message = "AgACBssq8Im1alV3N7wXGODL8jLPWwLhTuCqfGZ1Iz9fb5tXlMOJD6jUvASrKmdtLK/qXNyJns2Vqcvlk+nfJYdZaFpIWiT/tAcEYbttfxyLdYxrLckAKdVRtf1OrNgtZeMCII4SAn6SYaaidrX/AN3s/aVn/zrlEKW0cEUIatHVDKtXO0Qss5EhV/E6kz0BNCgtAytf/s0Botvxt3kGCN8ALqcG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqbHiki6ThNH3auuyZPQpJntnN0mA//56nMpK/6HIuu8xAQUEAgQDAQoMoA8AAAAAAAAG"; - assert_eq!(output.message_encoded, expected_message); + assert_eq!(output.unsigned_tx, expected_message); } diff --git a/src/Solana/AccountMeta.h b/src/Solana/AccountMeta.h deleted file mode 100644 index 3f748d88686..00000000000 --- a/src/Solana/AccountMeta.h +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "Solana/Address.h" - -namespace TW::Solana { - -struct AccountMeta { - Address account; - bool isSigner; - bool isReadOnly; - AccountMeta(const Address& address, bool isSigner, bool isReadOnly) noexcept : account(address), isSigner(isSigner), isReadOnly(isReadOnly) {} -}; - -} diff --git a/src/Solana/Address.cpp b/src/Solana/Address.cpp index 1cd08d6443d..e642a59f94a 100644 --- a/src/Solana/Address.cpp +++ b/src/Solana/Address.cpp @@ -3,15 +3,6 @@ // Copyright © 2017 Trust Wallet. #include "Address.h" -#include "Program.h" -#include "Transaction.h" -#include "../Base58.h" -#include "../Base58Address.h" -#include "../Hash.h" - -#include - -#include using namespace TW; @@ -53,8 +44,4 @@ Data Address::vector() const { return Data(begin(bytes), end(bytes)); } -Address Address::defaultTokenAddress(const Address& tokenMintAddress) { - return TokenProgram::defaultTokenAddress(*this, tokenMintAddress); -} - } // namespace TW::Solana diff --git a/src/Solana/AddressLookupTable.h b/src/Solana/AddressLookupTable.h deleted file mode 100644 index ae759dc1cda..00000000000 --- a/src/Solana/AddressLookupTable.h +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include - -namespace TW::Solana { - -struct AddressLookupTable { - Solana::Address key; - std::vector addresses; -}; - -using MessageAddressTableLookup = std::vector; - -} diff --git a/src/Solana/CompiledInstruction.cpp b/src/Solana/CompiledInstruction.cpp deleted file mode 100644 index ee71633e525..00000000000 --- a/src/Solana/CompiledInstruction.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#include "Solana/CompiledInstruction.h" - -namespace TW::Solana { - -uint8_t CompiledInstruction::findAccount(const Address& address) { - auto it = std::find(addresses.begin(), addresses.end(), address); - if (it == addresses.end()) { - throw std::invalid_argument("address not found"); - } - assert(it != addresses.end()); - auto dist = std::distance(addresses.begin(), it); - assert(dist < 256); - return (uint8_t)dist; -} - -} diff --git a/src/Solana/CompiledInstruction.h b/src/Solana/CompiledInstruction.h deleted file mode 100644 index 63100c4e980..00000000000 --- a/src/Solana/CompiledInstruction.h +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright © 2017 Trust Wallet. - -#pragma once - -#include "Solana/Instruction.h" - -namespace TW::Solana { - -// A compiled instruction -struct CompiledInstruction { - // Index into the transaction keys array indicating the program account that executes this instruction - uint8_t programIdIndex; - // Ordered indices into the transaction keys array indicating which accounts - // to pass to the program - std::vector accounts; - // The program input data - Data data; - - // Reference to the address vector - const std::vector